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 }