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  }