github.com/usbarmory/armory-boot@v0.0.0-20240307133412-208c66a380b9/exec/linux.go (about)

     1  // https://github.com/usbarmory/armory-boot
     2  //
     3  // Copyright (c) WithSecure Corporation
     4  // https://foundry.withsecure.com
     5  //
     6  // Use of this source code is governed by the license
     7  // that can be found in the LICENSE file.
     8  
     9  package exec
    10  
    11  import (
    12  	"bytes"
    13  	"encoding/binary"
    14  	"errors"
    15  	"fmt"
    16  
    17  	"github.com/usbarmory/tamago/dma"
    18  
    19  	"github.com/u-root/u-root/pkg/dt"
    20  )
    21  
    22  // LinuxImage represents a bootable Linux kernel image.
    23  type LinuxImage struct {
    24  	// Region is the memory area for image loading.
    25  	Region *dma.Region
    26  
    27  	// Kernel is the Linux kernel image.
    28  	Kernel []byte
    29  	// KernelOffset is the Linux kernel offset from RAM start address.
    30  	KernelOffset int
    31  
    32  	// DeviceTreeBlob is the Linux kernel dtb file.
    33  	DeviceTreeBlob []byte
    34  	// DeviceTreeBlobOffset is the dtb offset from RAM start address.
    35  	DeviceTreeBlobOffset int
    36  
    37  	// InitialRamDisk is the Linux kernel initrd file.
    38  	InitialRamDisk []byte
    39  	// InitialRamDiskOffset is the initrd offset from RAM start address.
    40  	InitialRamDiskOffset int
    41  
    42  	// CmdLine is the Linux kernel command line arguments.
    43  	CmdLine string
    44  
    45  	entry  uint
    46  	dtb    uint
    47  	loaded bool
    48  }
    49  
    50  func (image *LinuxImage) fdt() (fdt *dt.FDT, err error) {
    51  	return dt.ReadFDT(bytes.NewReader(image.DeviceTreeBlob))
    52  }
    53  
    54  func (image *LinuxImage) updateDTB(fdt *dt.FDT) (err error) {
    55  	dtbBuf := new(bytes.Buffer)
    56  	_, err = fdt.Write(dtbBuf)
    57  
    58  	if err != nil {
    59  		return
    60  	}
    61  
    62  	image.DeviceTreeBlob = dtbBuf.Bytes()
    63  
    64  	return
    65  }
    66  
    67  func (image *LinuxImage) fixupBootArgs() (err error) {
    68  	fdt, err := image.fdt()
    69  
    70  	if err != nil {
    71  		return
    72  	}
    73  
    74  	for _, node := range fdt.RootNode.Children {
    75  		if node.Name == "chosen" {
    76  			bootargs := dt.Property{
    77  				Name:  "bootargs",
    78  				Value: []byte(image.CmdLine + "\x00"),
    79  			}
    80  
    81  			node.Properties = append(node.Properties, bootargs)
    82  		}
    83  	}
    84  
    85  	return image.updateDTB(fdt)
    86  }
    87  
    88  func (image *LinuxImage) fixupInitrd(addr uint) (err error) {
    89  	fdt, err := image.fdt()
    90  
    91  	if err != nil {
    92  		return
    93  	}
    94  
    95  	start := addr + uint(image.InitialRamDiskOffset)
    96  	end := start + uint(len(image.InitialRamDisk))
    97  
    98  	for _, node := range fdt.RootNode.Children {
    99  		if node.Name == "chosen" {
   100  			initrdStart := dt.Property{
   101  				Name:  "linux,initrd-start",
   102  				Value: make([]byte, 8),
   103  			}
   104  
   105  			initrdEnd := dt.Property{
   106  				Name:  "linux,initrd-end",
   107  				Value: make([]byte, 8),
   108  			}
   109  
   110  			binary.BigEndian.PutUint64(initrdStart.Value, uint64(start))
   111  			binary.BigEndian.PutUint64(initrdEnd.Value, uint64(end))
   112  
   113  			node.Properties = append(node.Properties, initrdStart)
   114  			node.Properties = append(node.Properties, initrdEnd)
   115  		}
   116  	}
   117  
   118  	return image.updateDTB(fdt)
   119  }
   120  
   121  // Load loads a Linux kernel image in memory.
   122  func (image *LinuxImage) Load() (err error) {
   123  	if image.Region == nil {
   124  		return errors.New("image memory Region must be assigned")
   125  	}
   126  
   127  	if len(image.CmdLine) > 0 {
   128  		if len(image.DeviceTreeBlob) == 0 {
   129  			return errors.New("cmdline requires dtb")
   130  		}
   131  
   132  		if err = image.fixupBootArgs(); err != nil {
   133  			return fmt.Errorf("cmdline dtb fixup error, %v", err)
   134  		}
   135  	}
   136  
   137  	if len(image.InitialRamDisk) > 0 {
   138  		if len(image.DeviceTreeBlob) == 0 {
   139  			return errors.New("initrd requires dtb")
   140  		}
   141  
   142  		if err = image.fixupInitrd(image.Region.Start()); err != nil {
   143  			return fmt.Errorf("initrd dtb fixup error, %v", err)
   144  		}
   145  
   146  		image.Region.Write(image.Region.Start(), image.InitialRamDiskOffset, image.InitialRamDisk)
   147  	}
   148  
   149  	image.Region.Write(image.Region.Start(), image.KernelOffset, image.Kernel)
   150  	image.Region.Write(image.Region.Start(), image.DeviceTreeBlobOffset, image.DeviceTreeBlob)
   151  
   152  	image.entry = image.Region.Start() + uint(image.KernelOffset)
   153  	image.dtb = image.Region.Start() + uint(image.DeviceTreeBlobOffset)
   154  	image.loaded = true
   155  
   156  	return
   157  }
   158  
   159  // Entry returns the image entry address.
   160  func (image *LinuxImage) Entry() uint {
   161  	return image.entry
   162  }
   163  
   164  // DTB returns the image DTB address.
   165  func (image *LinuxImage) DTB() uint {
   166  	return image.dtb
   167  }
   168  
   169  // Boot calls a loaded Linux kernel image.
   170  func (image *LinuxImage) Boot(cleanup func()) (err error) {
   171  	if !image.loaded {
   172  		return errors.New("Load() kernel before Boot()")
   173  	}
   174  
   175  	return boot(image.entry, image.dtb, cleanup, false)
   176  }