github.com/mvdan/u-root-coreutils@v0.0.0-20230122170626-c2eef2898555/pkg/boot/zimage/zimage.go (about) 1 // Copyright 2019 the u-root Authors. All rights reserved 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Package zimage contains a Parser for the arm zImage Linux format. It assumes 6 // little endian arm. 7 package zimage 8 9 import ( 10 "encoding/binary" 11 "fmt" 12 "io" 13 ) 14 15 // Magic values used in the zImage header and table. 16 const ( 17 Magic = 0x016f2818 18 Endianess = 0x04030201 19 TableMagic = 0x45454545 20 ) 21 22 // Tags used by TableEntry (at the time of writing, there is only one tag). 23 const ( 24 TagKernelSize Tag = 0x5a534c4b 25 ) 26 27 // Tag is used to identify a TableEntry. 28 type Tag uint32 29 30 // ZImage is one of the major formats used by Linux on ARM. This struct 31 // is only for storing the metadata. 32 type ZImage struct { 33 Header Header 34 Table []TableEntry 35 } 36 37 // Header appears near the beginning of the zImage. 38 // 39 // The layout is defined in Linux: 40 // 41 // arch/arm/boot/compressed/head.S 42 type Header struct { 43 Magic uint32 44 Start uint32 45 End uint32 46 Endianess uint32 47 TableMagic uint32 48 TableAddr uint32 49 } 50 51 // TableEntry is an extension to Header. A zImage may have 0 or more entries. 52 // 53 // The layout is defined in Linux: 54 // 55 // arch/arm/boot/compressed/vmlinux.lds.S 56 type TableEntry struct { 57 Tag Tag 58 Data []uint32 59 } 60 61 // Parse a ZImage from a file. 62 func Parse(f io.ReadSeeker) (*ZImage, error) { 63 // Parse the header. 64 if _, err := f.Seek(0x24, io.SeekStart); err != nil { 65 return nil, err 66 } 67 z := &ZImage{} 68 if err := binary.Read(f, binary.LittleEndian, &z.Header); err != nil { 69 return nil, err 70 } 71 if z.Header.Magic != Magic { 72 return z, fmt.Errorf("invalid zImage magic, got %#08x, expected %#08x", 73 z.Header.Magic, Magic) 74 } 75 if z.Header.Endianess != Endianess { 76 return z, fmt.Errorf("unsupported zImage endianess, expected little") 77 } 78 if z.Header.End < z.Header.Start { 79 return z, fmt.Errorf("invalid zImage, end is less than start, %d < %d", 80 z.Header.End, z.Header.Start) 81 } 82 83 if z.Header.TableMagic != TableMagic { 84 // No table. 85 return z, nil 86 } 87 88 // Parse the table. 89 addr := z.Header.TableAddr 90 for addr != 0 { 91 if _, err := f.Seek(int64(addr), io.SeekStart); err != nil { 92 return nil, err 93 } 94 var size uint32 95 if err := binary.Read(f, binary.LittleEndian, &size); err != nil { 96 return nil, err 97 } 98 entry := TableEntry{Data: make([]uint32, size)} 99 if err := binary.Read(f, binary.LittleEndian, &entry.Tag); err != nil { 100 return nil, err 101 } 102 if err := binary.Read(f, binary.LittleEndian, &entry.Data); err != nil { 103 return nil, err 104 } 105 z.Table = append(z.Table, entry) 106 107 // In its current form, the Linux source code does not make it 108 // super clear how multiple entries are specified in the table. Is 109 // it a zero-terminated array? Is it a linked-list? Is it similar 110 // to atags? See Linux commit c77256. Regardless, the kernel 111 // currently only has one entry, so we exit after one iteration. 112 addr = 0 113 } 114 return z, nil 115 } 116 117 // GetEntry searches through the zImage table for the given tag. 118 func (z *ZImage) GetEntry(t Tag) (*TableEntry, error) { 119 for i := range z.Table { 120 if z.Table[i].Tag == t { 121 return &z.Table[i], nil 122 } 123 } 124 return nil, fmt.Errorf("zImage table does not contain the %#08x tag", t) 125 } 126 127 // GetKernelSizes returns two kernel sizes relevant for kexec. 128 func (z *ZImage) GetKernelSizes() (piggySizeAddr uint32, kernelBSSSize uint32, err error) { 129 e, err := z.GetEntry(TagKernelSize) 130 if err != nil { 131 return 0, 0, err 132 } 133 if len(e.Data) != 2 { 134 return 0, 0, fmt.Errorf("zImage tag %#08x has incorrect size %d, expected 2", 135 TagKernelSize, len(e.Data)) 136 } 137 return e.Data[0], e.Data[1], nil 138 }