github.com/jlowellwofford/u-root@v1.0.0/pkg/bzimage/bzImage.go (about) 1 // Copyright 2015-2018 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 bzImage implements encoding.UnmarshalBinary for bzImage files. 6 // The bzImage struct contains all the information about the file and can 7 // be used to create a new bzImage. 8 package bzimage 9 10 import ( 11 "bytes" 12 "encoding/binary" 13 "fmt" 14 "io/ioutil" 15 "reflect" 16 "strings" 17 ) 18 19 var Debug = func(string, ...interface{}) {} 20 21 // UnmarshalBinary implements the encoding.BinaryUnmarshaler interface. 22 // For now, it hardwires the KernelBase to 0x100000. 23 func (b *BzImage) UnmarshalBinary(d []byte) error { 24 Debug("Processing %d byte image", len(d)) 25 r := bytes.NewReader(d) 26 if err := binary.Read(r, binary.LittleEndian, &b.Header); err != nil { 27 return err 28 } 29 Debug("Header was %d bytes", len(d)-r.Len()) 30 Debug("bzImage header %v", b.Header) 31 Debug("magic %x switch %v", b.Header.HeaderMagic, b.Header.RealModeSwitch) 32 if b.Header.HeaderMagic != HeaderMagic { 33 return fmt.Errorf("Not a bzImage: magic should be %02x, and is %02x", HeaderMagic, b.Header.HeaderMagic) 34 } 35 Debug("RamDisk image %x size %x", b.Header.RamDiskImage, b.Header.RamDiskSize) 36 Debug("StartSys %x", b.Header.StartSys) 37 Debug("Boot type: %s(%x)", LoaderType[boottype(b.Header.TypeOfLoader)], b.Header.TypeOfLoader) 38 Debug("SetupSects %d", b.Header.SetupSects) 39 40 b.KernelOffset = (uintptr(b.Header.SetupSects) + 1) * 512 41 Debug("Kernel offset is %d bytes", b.KernelOffset) 42 if b.KernelOffset > uintptr(len(d)) { 43 return fmt.Errorf("len(b) is %d, b.Header.SetupSects+1 * 512 is %d: too small?", len(d), b.KernelOffset) 44 } 45 b.BootCode = d[len(d)-r.Len() : b.KernelOffset] 46 Debug("%d bytes of BootCode", len(b.BootCode)) 47 b.Kernel = d[b.KernelOffset:] 48 Debug("Kernel at %d, %d bytes", b.KernelOffset, len(b.Kernel)) 49 b.KernelBase = uintptr(0x100000) 50 if b.Header.RamDiskImage == 0 { 51 return nil 52 } 53 i := b.Header.RamDiskImage - uint32(b.KernelBase) 54 s := b.Header.RamDiskSize 55 Debug("initrd in file at %d, %d bytes", i, s) 56 b.InitRAMFS = d[i : i+s] 57 Debug("Ramdisk at %d, %d bytes", b.Header.RamDiskImage, b.Header.RamDiskSize) 58 return nil 59 } 60 61 // MarshalBinary implements the encoding.BinaryMarshaler interface. 62 func (b *BzImage) MarshalBinary() ([]byte, error) { 63 var w bytes.Buffer 64 w.Grow(int(b.KernelOffset) + len(b.Kernel) + len(b.InitRAMFS)) 65 if len(b.InitRAMFS) > 0 { 66 b.Header.RamDiskImage = uint32(b.KernelBase) + uint32(len(b.Kernel)) 67 b.Header.RamDiskSize = uint32(len(b.InitRAMFS)) 68 } 69 70 Debug("Grew output buffer to %d bytes", w.Len()) 71 if err := binary.Write(&w, binary.LittleEndian, &b.Header); err != nil { 72 return nil, err 73 } 74 Debug("Wrote %d bytes of header", w.Len()) 75 if _, err := w.Write(b.BootCode); err != nil { 76 return nil, err 77 } 78 if _, err := w.Write(b.Kernel); err != nil { 79 return nil, err 80 } 81 if _, err := w.Write(b.InitRAMFS); err != nil { 82 return nil, err 83 } 84 Debug("Finished writing, len is now %d bytes", w.Len()) 85 return w.Bytes(), nil 86 } 87 88 func Equal(a, b []byte) error { 89 if len(a) != len(b) { 90 return fmt.Errorf("Images differ in len: %d bytes and %d bytes", len(a), len(b)) 91 } 92 var ba BzImage 93 if err := ba.UnmarshalBinary(a); err != nil { 94 return err 95 } 96 var bb BzImage 97 if err := bb.UnmarshalBinary(b); err != nil { 98 return err 99 } 100 if !reflect.DeepEqual(ba.Header, bb.Header) { 101 return fmt.Errorf("Headers do not match: %s", ba.Header.Diff(&bb.Header)) 102 } 103 // this is overkill, I can't see any way it can happen. 104 if len(ba.Kernel) != len(bb.Kernel) { 105 return fmt.Errorf("Kernel lengths differ: %d vs %d bytes", len(ba.Kernel), len(bb.Kernel)) 106 } 107 if len(ba.BootCode) != len(bb.BootCode) { 108 return fmt.Errorf("BootCode lengths differ: %d vs %d bytes", len(ba.Kernel), len(bb.Kernel)) 109 } 110 111 if !reflect.DeepEqual(ba.BootCode, bb.BootCode) { 112 return fmt.Errorf("BootCode does not match") 113 } 114 if !reflect.DeepEqual(ba.Kernel, bb.Kernel) { 115 return fmt.Errorf("Kernels do not match") 116 } 117 if !reflect.DeepEqual(ba.InitRAMFS, bb.InitRAMFS) { 118 return fmt.Errorf("Kernels do not match") 119 } 120 121 return nil 122 } 123 124 func (b *BzImage) AddInitRAMFS(s string) error { 125 d, err := ioutil.ReadFile(s) 126 if err != nil { 127 return err 128 } 129 b.InitRAMFS = d 130 return nil 131 } 132 133 // MakeLinuxHeader marshals a LinuxHeader into a []byte. 134 func MakeLinuxHeader(h *LinuxHeader) ([]byte, error) { 135 buf := new(bytes.Buffer) 136 err := binary.Write(buf, binary.LittleEndian, h) 137 return buf.Bytes(), err 138 } 139 140 // Show stringifies a LinuxHeader into a []string 141 func (h *LinuxHeader) Show() []string { 142 var s []string 143 144 val := reflect.ValueOf(*h) 145 for i := 0; i < val.NumField(); i++ { 146 v := val.Field(i) 147 k := reflect.ValueOf(v).Kind() 148 n := fmt.Sprintf("%s", val.Type().Field(i).Name) 149 switch k { 150 case reflect.Slice: 151 s = append(s, fmt.Sprintf("%s:%#02x", n, v)) 152 case reflect.Bool: 153 s = append(s, fmt.Sprintf("%s:%v", n, v)) 154 default: 155 s = append(s, fmt.Sprintf("%s:%#02x", n, v)) 156 } 157 } 158 return s 159 } 160 161 // Diff is a convenience function that returns a string showing 162 // differents between a header and another header. 163 func (h *LinuxHeader) Diff(i *LinuxHeader) string { 164 var s string 165 hs := h.Show() 166 is := i.Show() 167 for i := range hs { 168 if hs[i] != is[i] { 169 s += fmt.Sprintf("%s != %s", hs[i], is[i]) 170 } 171 } 172 return s 173 } 174 175 // String stringifies a LinuxHeader into comma-separated parts 176 func (h *LinuxHeader) String() string { 177 return strings.Join(h.Show(), ",") 178 }