github.com/system-transparency/u-root@v6.0.1-0.20190919065413-ed07a650de4c+incompatible/pkg/bootconfig/bootconfig.go (about)

     1  // Copyright 2017-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 bootconfig
     6  
     7  import (
     8  	"encoding/json"
     9  	"errors"
    10  	"fmt"
    11  	"log"
    12  	"os"
    13  
    14  	"github.com/u-root/u-root/pkg/crypto"
    15  	"github.com/u-root/u-root/pkg/kexec"
    16  	"github.com/u-root/u-root/pkg/multiboot"
    17  )
    18  
    19  // BootConfig is a general-purpose boot configuration. It draws some
    20  // characteristics from FIT but it's not compatible with it. It uses
    21  // JSON for interoperability.
    22  type BootConfig struct {
    23  	Name          string   `json:"name,omitempty"`
    24  	Kernel        string   `json:"kernel"`
    25  	Initramfs     string   `json:"initramfs,omitempty"`
    26  	KernelArgs    string   `json:"kernel_args,omitempty"`
    27  	DeviceTree    string   `json:"devicetree,omitempty"`
    28  	Multiboot     string   `json:"multiboot_kernel,omitempty"`
    29  	MultibootArgs string   `json:"multiboot_args,omitempty"`
    30  	Modules       []string `json:"multiboot_modules,omitempty"`
    31  }
    32  
    33  // IsValid returns true if a BootConfig object has valid content, and false
    34  // otherwise
    35  func (bc *BootConfig) IsValid() bool {
    36  	return (bc.Kernel != "" && bc.Multiboot == "") || (bc.Kernel == "" && bc.Multiboot != "")
    37  }
    38  
    39  // FileNames returns a slice of all filenames in the bootconfig.
    40  func (bc *BootConfig) fileNames() []string {
    41  	str := make([]string, 0)
    42  	str = append(str, bc.Kernel)
    43  	str = append(str, bc.Initramfs)
    44  	for _, module := range bc.Modules {
    45  		str = append(str, module)
    46  	}
    47  	return str
    48  }
    49  
    50  func (bc *BootConfig) bytestream() []byte {
    51  	b := bc.Name + bc.Kernel + bc.Initramfs + bc.KernelArgs + bc.DeviceTree + bc.Multiboot + bc.MultibootArgs
    52  	for _, module := range bc.Modules {
    53  		b = b + module
    54  	}
    55  	return []byte(b)
    56  }
    57  
    58  // Boot tries to boot the kernel with optional initramfs and command line
    59  // options. If a device-tree is specified, that will be used too
    60  func (bc *BootConfig) Boot() error {
    61  	crypto.TryMeasureData(crypto.BootConfigPCR, bc.bytestream(), "bootconfig")
    62  	crypto.TryMeasureFiles(bc.fileNames()...)
    63  	if bc.Kernel != "" {
    64  		kernel, err := os.Open(bc.Kernel)
    65  		if err != nil {
    66  			return err
    67  		}
    68  		var initramfs *os.File
    69  		if bc.Initramfs != "" {
    70  			initramfs, err = os.Open(bc.Initramfs)
    71  			if err != nil {
    72  				return err
    73  			}
    74  		}
    75  		defer func() {
    76  			// clean up
    77  			if kernel != nil {
    78  				if err := kernel.Close(); err != nil {
    79  					log.Printf("Error closing kernel file descriptor: %v", err)
    80  				}
    81  			}
    82  			if initramfs != nil {
    83  				if err := initramfs.Close(); err != nil {
    84  					log.Printf("Error closing initramfs file descriptor: %v", err)
    85  				}
    86  			}
    87  		}()
    88  		if err := kexec.FileLoad(kernel, initramfs, bc.KernelArgs); err != nil {
    89  			return err
    90  		}
    91  	} else if bc.Multiboot != "" {
    92  		// check multiboot header
    93  		if err := multiboot.Probe(bc.Multiboot); err != nil {
    94  			log.Printf("Error parsing multiboot header: %v", err)
    95  			return err
    96  		}
    97  		if err := multiboot.Load(true, bc.Multiboot, bc.MultibootArgs, bc.Modules, nil); err != nil {
    98  			return fmt.Errorf("kexec.Load() error: %v", err)
    99  		}
   100  	}
   101  	err := kexec.Reboot()
   102  	if err == nil {
   103  		return errors.New("Unexpectedly returned from Reboot() without error. The system did not reboot")
   104  	}
   105  	return err
   106  }
   107  
   108  // NewBootConfig parses a boot configuration in JSON format and returns a
   109  // BootConfig object.
   110  func NewBootConfig(data []byte) (*BootConfig, error) {
   111  	var bootconfig BootConfig
   112  	if err := json.Unmarshal(data, &bootconfig); err != nil {
   113  		return nil, err
   114  	}
   115  	return &bootconfig, nil
   116  }