github.com/hugelgupf/u-root@v0.0.0-20191023214958-4807c632154c/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  	str = append(str, bc.Modules...)
    45  	return str
    46  }
    47  
    48  func (bc *BootConfig) bytestream() []byte {
    49  	b := bc.Name + bc.Kernel + bc.Initramfs + bc.KernelArgs + bc.DeviceTree + bc.Multiboot + bc.MultibootArgs
    50  	for _, module := range bc.Modules {
    51  		b = b + module
    52  	}
    53  	return []byte(b)
    54  }
    55  
    56  // Boot tries to boot the kernel with optional initramfs and command line
    57  // options. If a device-tree is specified, that will be used too
    58  func (bc *BootConfig) Boot() error {
    59  	crypto.TryMeasureData(crypto.BootConfigPCR, bc.bytestream(), "bootconfig")
    60  	crypto.TryMeasureFiles(bc.fileNames()...)
    61  	if bc.Kernel != "" {
    62  		kernel, err := os.Open(bc.Kernel)
    63  		if err != nil {
    64  			return err
    65  		}
    66  		var initramfs *os.File
    67  		if bc.Initramfs != "" {
    68  			initramfs, err = os.Open(bc.Initramfs)
    69  			if err != nil {
    70  				return err
    71  			}
    72  		}
    73  		defer func() {
    74  			// clean up
    75  			if kernel != nil {
    76  				if err := kernel.Close(); err != nil {
    77  					log.Printf("Error closing kernel file descriptor: %v", err)
    78  				}
    79  			}
    80  			if initramfs != nil {
    81  				if err := initramfs.Close(); err != nil {
    82  					log.Printf("Error closing initramfs file descriptor: %v", err)
    83  				}
    84  			}
    85  		}()
    86  		if err := kexec.FileLoad(kernel, initramfs, bc.KernelArgs); err != nil {
    87  			return err
    88  		}
    89  	} else if bc.Multiboot != "" {
    90  		// check multiboot header
    91  		if err := multiboot.Probe(bc.Multiboot); err != nil {
    92  			log.Printf("Error parsing multiboot header: %v", err)
    93  			return err
    94  		}
    95  		if err := multiboot.Load(true, bc.Multiboot, bc.MultibootArgs, bc.Modules, nil); err != nil {
    96  			return fmt.Errorf("kexec.Load() error: %v", err)
    97  		}
    98  	}
    99  	err := kexec.Reboot()
   100  	if err == nil {
   101  		return errors.New("unexpectedly returned from Reboot() without error: system did not reboot")
   102  	}
   103  	return err
   104  }
   105  
   106  // NewBootConfig parses a boot configuration in JSON format and returns a
   107  // BootConfig object.
   108  func NewBootConfig(data []byte) (*BootConfig, error) {
   109  	var bootconfig BootConfig
   110  	if err := json.Unmarshal(data, &bootconfig); err != nil {
   111  		return nil, err
   112  	}
   113  	return &bootconfig, nil
   114  }