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 }