github.com/mvdan/u-root-coreutils@v0.0.0-20230122170626-c2eef2898555/pkg/boot/fit/fit.go (about) 1 // Copyright 2020 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 fit provides tools to read and verify FIT kernel images 6 // See https://doc.coreboot.org/lib/payloads/fit.html 7 package fit 8 9 import ( 10 "bytes" 11 "fmt" 12 "io" 13 "os" 14 15 "github.com/ProtonMail/go-crypto/openpgp" 16 "github.com/mvdan/u-root-coreutils/pkg/boot" 17 "github.com/mvdan/u-root-coreutils/pkg/dt" 18 ) 19 20 // Image is a Flattened Image Tree implementation for OSImage. 21 type Image struct { 22 name string 23 // Cmdline is the command line for the new kernel. 24 Cmdline string 25 // Root is the FDT. 26 Root *dt.FDT 27 // Kernel is the name of the kernel node. 28 Kernel string 29 // InitRAMFS is the name of the initramfs node. 30 InitRAMFS string 31 // ConfigOverride is the optional FIT config to use instead of default 32 ConfigOverride string 33 // SkipInitRAMFS skips the search for an ramdisk entry in the config 34 SkipInitRAMFS bool 35 // BootRank ranks the priority of the images in boot menu 36 BootRank int 37 // KeyRing is the optional set of public keys used to validate images at Load 38 KeyRing openpgp.KeyRing 39 } 40 41 var _ = boot.OSImage(&Image{}) 42 43 // New returns a new image initialized with a file containing an FDT. 44 func New(n string) (*Image, error) { 45 f, err := os.Open(n) 46 if err != nil { 47 return nil, err 48 } 49 defer f.Close() 50 fdt, err := dt.ReadFDT(f) 51 if err != nil { 52 return nil, err 53 } 54 return &Image{name: n, Root: fdt}, nil 55 } 56 57 // ParseConfig reads r for a FIT image and returns a OSImage for each 58 // configuration parsed. 59 func ParseConfig(r io.ReadSeeker) ([]Image, error) { 60 fdt, err := dt.ReadFDT(r) 61 if err != nil { 62 return nil, err 63 } 64 65 var images []Image 66 configs := fdt.Root().Walk("configurations") 67 cn, _ := configs.ListChildNodes() 68 69 for _, n := range cn { 70 i := Image{name: n, Root: fdt, ConfigOverride: n} 71 72 kn, in, err := i.LoadConfig() 73 74 if err == nil { 75 i.Kernel, i.InitRAMFS = kn, in 76 images = append(images, i) 77 } 78 } 79 80 if len(images) == 0 { 81 return nil, fmt.Errorf("failed to find valid usable config") 82 } 83 84 return images, nil 85 } 86 87 // String is a Stringer for Image. 88 func (i *Image) String() string { 89 return fmt.Sprintf("FDT %s, kernel %q, initrd %q", i.name, i.Kernel, i.InitRAMFS) 90 } 91 92 // Label returns an Image Label. 93 func (i *Image) Label() string { 94 if i.Kernel == "" { 95 return i.name 96 } 97 return fmt.Sprintf("%s (kernel: %s)", i.name, i.Kernel) 98 } 99 100 // Rank returns an Image Rank. 101 func (i *Image) Rank() int { 102 return i.BootRank 103 } 104 105 // Edit edits the Image cmdline using a func. 106 func (i *Image) Edit(f func(s string) string) { 107 i.Cmdline = f(i.Cmdline) 108 } 109 110 // Load LinuxImage to memory 111 func loadLinuxImage(i *boot.LinuxImage, verbose bool) error { 112 return i.Load(verbose) 113 } 114 115 // provide chance to mock in test 116 var loadImage = loadLinuxImage 117 118 // Load loads an image and reboots 119 func (i *Image) Load(verbose bool) error { 120 image := &boot.LinuxImage{ 121 Cmdline: i.Cmdline, 122 } 123 124 if i.KeyRing != nil { 125 kr, err := i.ReadSignedImage(i.Kernel, i.KeyRing) 126 if err != nil { 127 return err 128 } 129 image.Kernel = kr 130 } else { 131 kr, err := i.ReadImage(i.Kernel) 132 if err != nil { 133 return err 134 } 135 image.Kernel = kr 136 } 137 138 if len(i.InitRAMFS) != 0 { 139 if i.KeyRing != nil { 140 ir, err := i.ReadSignedImage(i.InitRAMFS, i.KeyRing) 141 if err != nil { 142 return err 143 } 144 image.Initrd = ir 145 } else { 146 ir, err := i.ReadImage(i.InitRAMFS) 147 if err != nil { 148 return err 149 } 150 image.Initrd = ir 151 } 152 } 153 154 if err := loadImage(image, verbose); err != nil { 155 return err 156 } 157 158 return nil 159 } 160 161 // ReadImage reads an image node from an FDT and returns the `data` contents. 162 func (i *Image) ReadImage(image string) (*bytes.Reader, error) { 163 root := i.Root.Root().Walk("images").Walk(image) 164 b, err := root.Property("data").AsBytes() 165 if err != nil { 166 return nil, err 167 } 168 return bytes.NewReader(b), nil 169 } 170 171 // GetConfigName finds the name of the default configuration or returns the 172 // override config if available 173 func (i *Image) GetConfigName() (string, error) { 174 if len(i.ConfigOverride) != 0 { 175 return i.ConfigOverride, nil 176 } 177 178 configs := i.Root.Root().Walk("configurations") 179 dc, err := configs.Property("default").AsString() 180 if err != nil { 181 return "", err 182 } 183 184 return dc, nil 185 } 186 187 // LoadConfig loads a configuration from a FIT image 188 // Returns <kernel_name>, <ramdisk_name>, error 189 func (i *Image) LoadConfig() (string, string, error) { 190 tc, err := i.GetConfigName() 191 if err != nil { 192 return "", "", err 193 } 194 195 configs := i.Root.Root().Walk("configurations") 196 config := configs.Walk(tc) 197 _, err = config.AsString() 198 199 if err != nil { 200 return "", "", err 201 } 202 203 var kn, rn string 204 205 kn, err = config.Property("kernel").AsString() 206 if err != nil { 207 return "", "", err 208 } 209 210 // Allow missing initram nodes 211 rn, _ = config.Property("ramdisk").AsString() 212 213 return kn, rn, nil 214 }