github.com/u-root/u-root@v7.0.1-0.20200915234505-ad7babab0a8e+incompatible/pkg/boot/jsonboot/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 jsonboot 6 7 import ( 8 "encoding/json" 9 "errors" 10 "fmt" 11 "hash/crc32" 12 "log" 13 "os" 14 "path/filepath" 15 "strings" 16 17 "github.com/u-root/u-root/pkg/boot/kexec" 18 "github.com/u-root/u-root/pkg/boot/multiboot" 19 "github.com/u-root/u-root/pkg/crypto" 20 ) 21 22 // BootConfig is a general-purpose boot configuration. It draws some 23 // characteristics from FIT but it's not compatible with it. It uses 24 // JSON for interoperability. 25 type BootConfig struct { 26 Name string `json:"name,omitempty"` 27 Kernel string `json:"kernel"` 28 Initramfs string `json:"initramfs,omitempty"` 29 KernelArgs string `json:"kernel_args,omitempty"` 30 DeviceTree string `json:"devicetree,omitempty"` 31 Multiboot string `json:"multiboot_kernel,omitempty"` 32 MultibootArgs string `json:"multiboot_args,omitempty"` 33 Modules []string `json:"multiboot_modules,omitempty"` 34 } 35 36 // IsValid returns true if a BootConfig object has valid content, and false 37 // otherwise 38 func (bc *BootConfig) IsValid() bool { 39 return (bc.Kernel != "" && bc.Multiboot == "") || (bc.Kernel == "" && bc.Multiboot != "") 40 } 41 42 // ID retrurns an identifyer composed of bc's name and crc32 hash of bc. 43 // The ID is suitable to be used as part of a filepath. 44 func (bc *BootConfig) ID() string { 45 id := strings.Title(strings.ToLower(bc.Name)) 46 id = strings.ReplaceAll(id, " ", "") 47 id = strings.ReplaceAll(id, "/", "") 48 id = strings.ReplaceAll(id, "\\", "") 49 50 buf := []byte(filepath.Base(bc.Kernel)) 51 buf = append(buf, []byte(bc.KernelArgs)...) 52 buf = append(buf, []byte(filepath.Base(bc.Initramfs))...) 53 buf = append(buf, []byte(filepath.Base(bc.DeviceTree))...) 54 buf = append(buf, []byte(filepath.Base(bc.Multiboot))...) 55 buf = append(buf, []byte(bc.MultibootArgs)...) 56 for _, mod := range bc.Modules { 57 buf = append(buf, []byte(filepath.Base(mod))...) 58 } 59 h := crc32.ChecksumIEEE(buf) 60 x := fmt.Sprintf("%x", h) 61 62 return "BC_" + id + x 63 } 64 65 // FileNames returns a slice of all filenames in the bootconfig. 66 func (bc *BootConfig) FileNames() []string { 67 var files []string 68 if bc.Kernel != "" { 69 files = append(files, bc.Kernel) 70 } 71 if bc.Initramfs != "" { 72 files = append(files, bc.Initramfs) 73 } 74 if bc.DeviceTree != "" { 75 files = append(files, bc.DeviceTree) 76 } 77 if bc.Multiboot != "" { 78 files = append(files, bc.Multiboot) 79 } 80 for _, mod := range bc.Modules { 81 if mod != "" { 82 files = append(files, mod) 83 } 84 } 85 return files 86 } 87 88 // ChangeFilePaths modifies the filepaths inside BootConfig. It replaces 89 // the current paths with new path leaving the last element of the path 90 // unchanged. 91 func (bc *BootConfig) ChangeFilePaths(newPath string) { 92 if bc.Kernel != "" { 93 bc.Kernel = filepath.Join(newPath, filepath.Base(bc.Kernel)) 94 } 95 if bc.Initramfs != "" { 96 bc.Initramfs = filepath.Join(newPath, filepath.Base(bc.Initramfs)) 97 } 98 if bc.DeviceTree != "" { 99 bc.DeviceTree = filepath.Join(newPath, filepath.Base(bc.DeviceTree)) 100 } 101 if bc.Multiboot != "" { 102 bc.Multiboot = filepath.Join(newPath, filepath.Base(bc.Multiboot)) 103 } 104 for j, mod := range bc.Modules { 105 if mod != "" { 106 bc.Modules[j] = filepath.Join(newPath, filepath.Base(mod)) 107 } 108 } 109 } 110 111 // SetFilePathsPrefix modifies the filepaths inside BootConfig. It appends 112 // prefix at the beginning of the current paths 113 func (bc *BootConfig) SetFilePathsPrefix(prefix string) { 114 if bc.Kernel != "" { 115 bc.Kernel = filepath.Join(prefix, bc.Kernel) 116 } 117 if bc.Initramfs != "" { 118 bc.Initramfs = filepath.Join(prefix, bc.Initramfs) 119 } 120 if bc.DeviceTree != "" { 121 bc.DeviceTree = filepath.Join(prefix, bc.DeviceTree) 122 } 123 if bc.Multiboot != "" { 124 bc.Multiboot = filepath.Join(prefix, bc.Multiboot) 125 } 126 for j, mod := range bc.Modules { 127 if mod != "" { 128 bc.Modules[j] = filepath.Join(prefix, mod) 129 } 130 } 131 } 132 133 func (bc *BootConfig) bytestream() []byte { 134 b := bc.Name + bc.Kernel + bc.Initramfs + bc.KernelArgs + bc.DeviceTree + bc.Multiboot + bc.MultibootArgs 135 for _, module := range bc.Modules { 136 b = b + module 137 } 138 return []byte(b) 139 } 140 141 // Boot tries to boot the kernel with optional initramfs and command line 142 // options. If a device-tree is specified, that will be used too 143 func (bc *BootConfig) Boot() error { 144 crypto.TryMeasureData(crypto.BootConfigPCR, bc.bytestream(), "bootconfig") 145 crypto.TryMeasureFiles(bc.FileNames()...) 146 if bc.Kernel != "" { 147 kernel, err := os.Open(bc.Kernel) 148 if err != nil { 149 return fmt.Errorf("can't open kernel file for measurement: %v", err) 150 } 151 var initramfs *os.File 152 if bc.Initramfs != "" { 153 initramfs, err = os.Open(bc.Initramfs) 154 if err != nil { 155 return fmt.Errorf("can't open initramfs file for measurement: %v", err) 156 } 157 } 158 defer func() { 159 // clean up 160 if kernel != nil { 161 if err := kernel.Close(); err != nil { 162 log.Printf("Error closing kernel file descriptor: %v", err) 163 } 164 } 165 if initramfs != nil { 166 if err := initramfs.Close(); err != nil { 167 log.Printf("Error closing initramfs file descriptor: %v", err) 168 } 169 } 170 }() 171 if err := kexec.FileLoad(kernel, initramfs, bc.KernelArgs); err != nil { 172 return fmt.Errorf("kexec.FileLoad() failed: %v", err) 173 } 174 } else if bc.Multiboot != "" { 175 mbkernel, err := os.Open(bc.Multiboot) 176 if err != nil { 177 log.Printf("Error opening multiboot kernel file: %v", err) 178 return err 179 } 180 defer mbkernel.Close() 181 182 // check multiboot header 183 if err := multiboot.Probe(mbkernel); err != nil { 184 log.Printf("Error parsing multiboot header: %v", err) 185 return err 186 } 187 modules, err := multiboot.OpenModules(bc.Modules) 188 if err != nil { 189 return err 190 } 191 defer modules.Close() 192 if err := multiboot.Load(true, mbkernel, bc.MultibootArgs, modules, nil); err != nil { 193 return fmt.Errorf("kexec.Load() error: %v", err) 194 } 195 } 196 err := kexec.Reboot() 197 if err == nil { 198 return errors.New("unexpectedly returned from Reboot() without error: system did not reboot") 199 } 200 return err 201 } 202 203 // NewBootConfig parses a boot configuration in JSON format and returns a 204 // BootConfig object. 205 func NewBootConfig(data []byte) (*BootConfig, error) { 206 var bootconfig BootConfig 207 if err := json.Unmarshal(data, &bootconfig); err != nil { 208 return nil, err 209 } 210 return &bootconfig, nil 211 }