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  }