github.com/system-transparency/u-root@v6.0.1-0.20190919065413-ed07a650de4c+incompatible/pkg/multiboot/multiboot.go (about) 1 // Copyright 2018-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 multiboot implements bootloading multiboot kernels as defined by 6 // https://www.gnu.org/software/grub/manual/multiboot/multiboot.html. 7 // 8 // Package multiboot crafts kexec segments that can be used with the kexec_load 9 // system call. 10 package multiboot 11 12 import ( 13 "bytes" 14 "debug/elf" 15 "encoding/binary" 16 "fmt" 17 "io" 18 "log" 19 "os" 20 "path/filepath" 21 "strings" 22 23 "github.com/u-root/u-root/pkg/ibft" 24 "github.com/u-root/u-root/pkg/kexec" 25 "github.com/u-root/u-root/pkg/multiboot/internal/trampoline" 26 "github.com/u-root/u-root/pkg/ubinary" 27 ) 28 29 const bootloader = "u-root kexec" 30 31 // multiboot defines parameters for working with multiboot kernels. 32 type multiboot struct { 33 mem kexec.Memory 34 35 file string 36 modules []string 37 38 cmdLine string 39 bootloader string 40 41 // trampoline is a path to an executable blob, which contains a trampoline segment. 42 // Trampoline sets machine to a specific state defined by multiboot v1 spec. 43 // https://www.gnu.org/software/grub/manual/multiboot/multiboot.html#Machine-state. 44 trampoline string 45 46 header header 47 48 // infoAddr is a pointer to multiboot info. 49 infoAddr uintptr 50 // kernelEntry is a pointer to entry point of kernel. 51 kernelEntry uintptr 52 // EntryPoint is a pointer to trampoline. 53 entryPoint uintptr 54 55 info info 56 loadedModules []module 57 } 58 59 var ( 60 rangeTypes = map[kexec.RangeType]uint32{ 61 kexec.RangeRAM: 1, 62 kexec.RangeDefault: 2, 63 kexec.RangeACPI: 3, 64 kexec.RangeNVS: 4, 65 kexec.RangeReserved: 2, 66 } 67 PageSize = os.Getpagesize() 68 ) 69 70 var sizeofMemoryMap = uint(binary.Size(MemoryMap{})) 71 72 // MemoryMap represents a reserved range of memory passed via the multiboot Info header. 73 type MemoryMap struct { 74 // Size is the size of the associated structure in bytes. 75 Size uint32 76 // BaseAddr is the starting address. 77 BaseAddr uint64 78 // Length is the size of the memory region in bytes. 79 Length uint64 80 // Type is the variety of address range represented. 81 Type uint32 82 } 83 84 // String returns a readable representation of a MemoryMap entry. 85 func (m MemoryMap) String() string { 86 return fmt.Sprintf("[0x%x, 0x%x) (len: 0x%x, size: 0x%x, type: %d)", m.BaseAddr, m.BaseAddr+m.Length, m.Length, m.Size, m.Type) 87 } 88 89 type memoryMaps []MemoryMap 90 91 // String returns a new-line-separated representation of the entire memory map. 92 func (m memoryMaps) String() string { 93 var s []string 94 for _, mm := range m { 95 s = append(s, mm.String()) 96 } 97 return strings.Join(s, "\n") 98 } 99 100 // Probe checks if file is multiboot v1 kernel. 101 func Probe(file string) error { 102 b, err := readFile(file) 103 if err != nil { 104 return err 105 } 106 kernel := &kernelReader{buf: b} 107 _, err = parseHeader(kernel) 108 return err 109 } 110 111 // newMB returns a new multiboot instance. 112 func newMB(file, cmdLine string, modules []string) (*multiboot, error) { 113 // Trampoline should be a part of current binary. 114 p, err := os.Executable() 115 if err != nil { 116 return nil, fmt.Errorf("Cannot find current executable path: %v", err) 117 } 118 trampoline, err := filepath.EvalSymlinks(p) 119 if err != nil { 120 return nil, fmt.Errorf("Cannot eval symlinks for %v: %v", p, err) 121 } 122 123 return &multiboot{ 124 file: file, 125 modules: modules, 126 cmdLine: cmdLine, 127 trampoline: trampoline, 128 bootloader: bootloader, 129 mem: kexec.Memory{}, 130 }, nil 131 } 132 133 // Load parses and loads a multiboot kernel `file` using kexec_load. 134 // 135 // Each module is a path followed by optional command-line arguments, e.g. 136 // []string{"./module arg1 arg2", "./module2 arg3 arg4"}. 137 // 138 // debug turns on debug logging. 139 // 140 // Load can set up an arbitrary number of modules, and takes care of the 141 // multiboot info structure, including the memory map. 142 // 143 // After Load is called, kexec.Reboot() is ready to be called any time to stop 144 // Linux and execute the loaded kernel. 145 func Load(debug bool, file, cmdline string, modules []string, ibft *ibft.IBFT) error { 146 m, err := newMB(file, cmdline, modules) 147 if err != nil { 148 return err 149 } 150 if err := m.load(debug, ibft); err != nil { 151 return err 152 } 153 if err := kexec.Load(m.entryPoint, m.mem.Segments, 0); err != nil { 154 return fmt.Errorf("kexec.Load() error: %v", err) 155 } 156 return nil 157 } 158 159 // load loads and parses multiboot information from m.file. 160 func (m *multiboot) load(debug bool, ibft *ibft.IBFT) error { 161 log.Printf("Parsing file %v", m.file) 162 b, err := readFile(m.file) 163 if err != nil { 164 return err 165 } 166 kernel := kernelReader{buf: b} 167 log.Println("Parsing multiboot header") 168 if m.header, err = parseHeader(&kernel); err != nil { 169 return fmt.Errorf("error parsing headers: %v", err) 170 } 171 172 log.Printf("Getting kernel entry point") 173 if m.kernelEntry, err = getEntryPoint(kernel); err != nil { 174 return fmt.Errorf("error getting kernel entry point: %v", err) 175 } 176 log.Printf("Kernel entry point at %#x", m.kernelEntry) 177 178 log.Printf("Parsing ELF segments") 179 if err := m.mem.LoadElfSegments(kernel); err != nil { 180 return fmt.Errorf("error loading ELF segments: %v", err) 181 } 182 183 log.Printf("Parsing memory map") 184 if err := m.mem.ParseMemoryMap(); err != nil { 185 return fmt.Errorf("error parsing memory map: %v", err) 186 } 187 188 // Insert the iBFT now, since nothing else has been allocated and this 189 // is the most restricted allocation we're gonna have to make. 190 if ibft != nil { 191 ibuf := ibft.Marshal() 192 193 // The iBFT may sit between 512K and 1M in physical memory. The 194 // loaded OS finds it by scanning this region. 195 allowedRange := kexec.Range{ 196 Start: 0x80000, 197 Size: 0x80000, 198 } 199 r, err := m.mem.ReservePhys(uint(len(ibuf)), allowedRange) 200 if err != nil { 201 return fmt.Errorf("reserving space for the iBFT in %s failed: %v", allowedRange, err) 202 } 203 log.Printf("iBFT was allocated at %s: %#v", r, ibft) 204 m.mem.Segments.Insert(kexec.NewSegment(ibuf, r)) 205 } 206 207 log.Printf("Preparing multiboot info") 208 if m.infoAddr, err = m.addInfo(); err != nil { 209 return fmt.Errorf("error preparing multiboot info: %v", err) 210 } 211 212 log.Printf("Adding trampoline") 213 if m.entryPoint, err = m.addTrampoline(); err != nil { 214 return fmt.Errorf("error adding trampoline: %v", err) 215 } 216 log.Printf("Trampoline entry point at %#x", m.entryPoint) 217 218 if debug { 219 info, err := m.description() 220 if err != nil { 221 log.Printf("%v cannot create debug info: %v", DebugPrefix, err) 222 } 223 log.Printf("%v %v", DebugPrefix, info) 224 } 225 226 return nil 227 } 228 229 func getEntryPoint(r io.ReaderAt) (uintptr, error) { 230 f, err := elf.NewFile(r) 231 if err != nil { 232 return 0, err 233 } 234 return uintptr(f.Entry), err 235 } 236 237 func (m *multiboot) addInfo() (addr uintptr, err error) { 238 iw, err := m.newMultibootInfo() 239 if err != nil { 240 return 0, err 241 } 242 infoSize, err := iw.size() 243 if err != nil { 244 return 0, err 245 } 246 247 r, err := m.mem.FindSpace(infoSize) 248 if err != nil { 249 return 0, err 250 } 251 252 d, err := iw.marshal(r.Start) 253 if err != nil { 254 return 0, err 255 } 256 m.info = iw.info 257 258 m.mem.Segments.Insert(kexec.NewSegment(d, r)) 259 return r.Start, nil 260 } 261 262 func (m multiboot) memoryMap() memoryMaps { 263 var ret memoryMaps 264 for _, r := range m.mem.Phys { 265 typ, ok := rangeTypes[r.Type] 266 if !ok { 267 typ = rangeTypes[kexec.RangeDefault] 268 } 269 v := MemoryMap{ 270 // Size is really used for skipping to the next pair. 271 Size: uint32(sizeofMemoryMap) - 4, 272 BaseAddr: uint64(r.Start), 273 Length: uint64(r.Size), 274 Type: typ, 275 } 276 ret = append(ret, v) 277 } 278 return ret 279 } 280 281 func (m *multiboot) addMmap() (addr uintptr, size uint, err error) { 282 mmap := m.memoryMap() 283 log.Printf("Memory map:\n%s", mmap) 284 d, err := mmap.marshal() 285 if err != nil { 286 return 0, 0, err 287 } 288 r, err := m.mem.AddKexecSegment(d) 289 if err != nil { 290 return 0, 0, err 291 } 292 return r.Start, uint(len(mmap)) * sizeofMemoryMap, nil 293 } 294 295 func (m multiboot) memoryBoundaries() (lower, upper uint32) { 296 const M1 = 1048576 297 const K640 = 640 * 1024 298 for _, r := range m.mem.Phys { 299 if r.Type != kexec.RangeRAM { 300 continue 301 } 302 end := uint32(r.Start) + uint32(r.Size) 303 // Lower memory starts at address 0, and upper memory starts at address 1 megabyte. 304 // The maximum possible value for lower memory is 640 kilobytes. 305 // The value returned for upper memory is maximally the address of the first upper memory hole minus 1 megabyte. 306 // It is not guaranteed to be this value. 307 if r.Start <= K640 && end > lower { 308 lower = end 309 } 310 if r.Start <= M1 && end > upper+M1 { 311 upper = end - M1 312 } 313 } 314 return 315 } 316 317 func min(a, b uint32) uint32 { 318 if a < b { 319 return a 320 } 321 return b 322 } 323 324 func (m *multiboot) newMultibootInfo() (*infoWrapper, error) { 325 mmapAddr, mmapSize, err := m.addMmap() 326 if err != nil { 327 return nil, err 328 } 329 var inf info 330 if m.header.Flags&flagHeaderMemoryInfo != 0 { 331 lower, upper := m.memoryBoundaries() 332 inf = info{ 333 Flags: flagInfoMemMap | flagInfoMemory, 334 MemLower: min(uint32(lower>>10), 0xFFFFFFFF), 335 MemUpper: min(uint32(upper>>10), 0xFFFFFFFF), 336 MmapLength: uint32(mmapSize), 337 MmapAddr: uint32(mmapAddr), 338 } 339 } 340 341 if len(m.modules) > 0 { 342 modAddr, err := m.addModules() 343 if err != nil { 344 return nil, err 345 } 346 inf.Flags |= flagInfoMods 347 inf.ModsAddr = uint32(modAddr) 348 inf.ModsCount = uint32(len(m.modules)) 349 } 350 351 inf.CmdLine = sizeofInfo 352 inf.BootLoaderName = sizeofInfo + uint32(len(m.cmdLine)) + 1 353 inf.Flags |= flagInfoCmdLine | flagInfoBootLoaderName 354 return &infoWrapper{ 355 info: inf, 356 CmdLine: m.cmdLine, 357 BootLoaderName: m.bootloader, 358 }, nil 359 } 360 361 // marshal writes out the exact bytes expected by the multiboot info header 362 // specified in 363 // https://www.gnu.org/software/grub/manual/multiboot/multiboot.html#Boot-information-format. 364 func (m memoryMaps) marshal() ([]byte, error) { 365 buf := bytes.Buffer{} 366 err := binary.Write(&buf, ubinary.NativeEndian, m) 367 return buf.Bytes(), err 368 } 369 370 func (m *multiboot) addTrampoline() (entry uintptr, err error) { 371 // Trampoline setups the machine registers to desired state 372 // and executes the loaded kernel. 373 d, err := trampoline.Setup(m.trampoline, m.infoAddr, m.kernelEntry) 374 if err != nil { 375 return 0, err 376 } 377 378 r, err := m.mem.AddKexecSegment(d) 379 if err != nil { 380 return 0, err 381 } 382 return r.Start, nil 383 }