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  }