github.com/u-root/u-root@v7.0.1-0.20200915234505-ad7babab0a8e+incompatible/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  //     arch/arm/boot/compressed/head.S
    41  type Header struct {
    42  	Magic      uint32
    43  	Start      uint32
    44  	End        uint32
    45  	Endianess  uint32
    46  	TableMagic uint32
    47  	TableAddr  uint32
    48  }
    49  
    50  // TableEntry is an extension to Header. A zImage may have 0 or more entries.
    51  //
    52  // The layout is defined in Linux:
    53  //     arch/arm/boot/compressed/vmlinux.lds.S
    54  type TableEntry struct {
    55  	Tag  Tag
    56  	Data []uint32
    57  }
    58  
    59  // Parse a ZImage from a file.
    60  func Parse(f io.ReadSeeker) (*ZImage, error) {
    61  	// Parse the header.
    62  	if _, err := f.Seek(0x24, io.SeekStart); err != nil {
    63  		return nil, err
    64  	}
    65  	z := &ZImage{}
    66  	if err := binary.Read(f, binary.LittleEndian, &z.Header); err != nil {
    67  		return nil, err
    68  	}
    69  	if z.Header.Magic != Magic {
    70  		return z, fmt.Errorf("invalid zImage magic, got %#08x, expected %#08x",
    71  			z.Header.Magic, Magic)
    72  	}
    73  	if z.Header.Endianess != Endianess {
    74  		return z, fmt.Errorf("unsupported zImage endianess, expected little")
    75  	}
    76  	if z.Header.End < z.Header.Start {
    77  		return z, fmt.Errorf("invalid zImage, end is less than start, %d < %d",
    78  			z.Header.End, z.Header.Start)
    79  	}
    80  
    81  	if z.Header.TableMagic != TableMagic {
    82  		// No table.
    83  		return z, nil
    84  	}
    85  
    86  	// Parse the table.
    87  	addr := z.Header.TableAddr
    88  	for addr != 0 {
    89  		if _, err := f.Seek(int64(addr), io.SeekStart); err != nil {
    90  			return nil, err
    91  		}
    92  		var size uint32
    93  		if err := binary.Read(f, binary.LittleEndian, &size); err != nil {
    94  			return nil, err
    95  		}
    96  		entry := TableEntry{Data: make([]uint32, size)}
    97  		if err := binary.Read(f, binary.LittleEndian, &entry.Tag); err != nil {
    98  			return nil, err
    99  		}
   100  		if err := binary.Read(f, binary.LittleEndian, &entry.Data); err != nil {
   101  			return nil, err
   102  		}
   103  		z.Table = append(z.Table, entry)
   104  
   105  		// In its current form, the Linux source code does not make it
   106  		// super clear how multiple entries are specified in the table. Is
   107  		// it a zero-terminated array? Is it a linked-list? Is it similar
   108  		// to atags? See Linux commit c77256. Regardless, the kernel
   109  		// currently only has one entry, so we exit after one iteration.
   110  		addr = 0
   111  	}
   112  	return z, nil
   113  }
   114  
   115  // GetEntry searches through the zImage table for the given tag.
   116  func (z *ZImage) GetEntry(t Tag) (*TableEntry, error) {
   117  	for i := range z.Table {
   118  		if z.Table[i].Tag == t {
   119  			return &z.Table[i], nil
   120  		}
   121  	}
   122  	return nil, fmt.Errorf("zImage table does not contain the %#08x tag", t)
   123  }
   124  
   125  // GetKernelSizes returns two kernel sizes relevant for kexec.
   126  func (z *ZImage) GetKernelSizes() (piggySizeAddr uint32, kernelBSSSize uint32, err error) {
   127  	e, err := z.GetEntry(TagKernelSize)
   128  	if err != nil {
   129  		return 0, 0, err
   130  	}
   131  	if len(e.Data) != 2 {
   132  		return 0, 0, fmt.Errorf("zImage tag %#08x has incorrect size %d, expected 2",
   133  			TagKernelSize, len(e.Data))
   134  	}
   135  	return e.Data[0], e.Data[1], nil
   136  }