github.com/u-root/u-root@v7.0.1-0.20200915234505-ad7babab0a8e+incompatible/pkg/boot/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/boot/ibft" 24 "github.com/u-root/u-root/pkg/boot/kexec" 25 "github.com/u-root/u-root/pkg/boot/multiboot/internal/trampoline" 26 "github.com/u-root/u-root/pkg/boot/util" 27 "github.com/u-root/u-root/pkg/ubinary" 28 "github.com/u-root/u-root/pkg/uio" 29 ) 30 31 const bootloader = "u-root kexec" 32 33 // Module describe a module by a ReaderAt and a `Cmdline` 34 type Module struct { 35 Module io.ReaderAt 36 Cmdline string 37 } 38 39 // Name returns the first field of the cmdline, if there is one. 40 func (m Module) Name() string { 41 f := strings.Fields(m.Cmdline) 42 if len(f) > 0 { 43 return f[0] 44 } 45 return "" 46 } 47 48 // Modules is a range of module with a Closer interface 49 type Modules []Module 50 51 // multiboot defines parameters for working with multiboot kernels. 52 type multiboot struct { 53 mem kexec.Memory 54 55 kernel io.ReaderAt 56 modules []Module 57 58 cmdLine string 59 bootloader string 60 61 // trampoline is a path to an executable blob, which contains a trampoline segment. 62 // Trampoline sets machine to a specific state defined by multiboot v1 spec. 63 // https://www.gnu.org/software/grub/manual/multiboot/multiboot.html#Machine-state. 64 trampoline string 65 66 // EntryPoint is a pointer to trampoline. 67 entryPoint uintptr 68 69 info info 70 loadedModules modules 71 } 72 73 var ( 74 rangeTypes = map[kexec.RangeType]uint32{ 75 kexec.RangeRAM: 1, 76 kexec.RangeDefault: 2, 77 kexec.RangeACPI: 3, 78 kexec.RangeNVS: 4, 79 kexec.RangeReserved: 2, 80 } 81 ) 82 83 var sizeofMemoryMap = uint(binary.Size(MemoryMap{})) 84 85 // MemoryMap represents a reserved range of memory passed via the multiboot Info header. 86 type MemoryMap struct { 87 // Size is the size of the associated structure in bytes. 88 Size uint32 89 // BaseAddr is the starting address. 90 BaseAddr uint64 91 // Length is the size of the memory region in bytes. 92 Length uint64 93 // Type is the variety of address range represented. 94 Type uint32 95 } 96 97 // String returns a readable representation of a MemoryMap entry. 98 func (m MemoryMap) String() string { 99 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) 100 } 101 102 type memoryMaps []MemoryMap 103 104 // marshal writes out the exact bytes expected by the multiboot info header 105 // specified in 106 // https://www.gnu.org/software/grub/manual/multiboot/multiboot.html#Boot-information-format. 107 func (m memoryMaps) marshal() ([]byte, error) { 108 buf := bytes.Buffer{} 109 err := binary.Write(&buf, ubinary.NativeEndian, m) 110 return buf.Bytes(), err 111 } 112 113 // elems adds mutiboot info elements describing the memory map of the system. 114 func (m memoryMaps) elems() []elem { 115 var e []elem 116 for _, mm := range m { 117 e = append(e, &mutibootMemRange{ 118 startAddr: mm.BaseAddr, 119 length: mm.Length, 120 memType: mm.Type, 121 }) 122 } 123 return e 124 } 125 126 // String returns a new-line-separated representation of the entire memory map. 127 func (m memoryMaps) String() string { 128 var s []string 129 for _, mm := range m { 130 s = append(s, mm.String()) 131 } 132 return strings.Join(s, "\n") 133 } 134 135 // Probe checks if `kernel` is multiboot v1 or mutiboot kernel. 136 func Probe(kernel io.ReaderAt) error { 137 r := util.TryGzipFilter(kernel) 138 _, err := parseHeader(uio.Reader(r)) 139 if err == ErrHeaderNotFound { 140 _, err = parseMutiHeader(uio.Reader(r)) 141 } 142 return err 143 } 144 145 // newMB returns a new multiboot instance. 146 func newMB(kernel io.ReaderAt, cmdLine string, modules []Module) (*multiboot, error) { 147 // Trampoline should be a part of current binary. 148 p, err := os.Executable() 149 if err != nil { 150 return nil, fmt.Errorf("cannot find current executable path: %v", err) 151 } 152 trampoline, err := filepath.EvalSymlinks(p) 153 if err != nil { 154 return nil, fmt.Errorf("cannot eval symlinks for %v: %v", p, err) 155 } 156 157 return &multiboot{ 158 kernel: kernel, 159 modules: modules, 160 cmdLine: cmdLine, 161 trampoline: trampoline, 162 bootloader: bootloader, 163 mem: kexec.Memory{}, 164 }, nil 165 } 166 167 // Load parses and loads a multiboot `kernel` using kexec_load. 168 // 169 // debug turns on debug logging. 170 // 171 // Load can set up an arbitrary number of modules, and takes care of the 172 // multiboot info structure, including the memory map. 173 // 174 // After Load is called, kexec.Reboot() is ready to be called any time to stop 175 // Linux and execute the loaded kernel. 176 func Load(debug bool, kernel io.ReaderAt, cmdline string, modules []Module, ibft *ibft.IBFT) error { 177 kernel = util.TryGzipFilter(kernel) 178 for i, mod := range modules { 179 modules[i].Module = util.TryGzipFilter(mod.Module) 180 } 181 182 m, err := newMB(kernel, cmdline, modules) 183 if err != nil { 184 return err 185 } 186 if err := m.load(debug, ibft); err != nil { 187 return err 188 } 189 if err := kexec.Load(m.entryPoint, m.mem.Segments, 0); err != nil { 190 return fmt.Errorf("kexec.Load() error: %v", err) 191 } 192 return nil 193 } 194 195 // OpenModules open modules as files and fill a range of `Module` struct 196 // 197 // Each module is a path followed by optional command-line arguments, e.g. 198 // []string{"./module arg1 arg2", "./module2 arg3 arg4"}. 199 func OpenModules(cmds []string) (Modules, error) { 200 modules := make([]Module, len(cmds)) 201 for i, cmd := range cmds { 202 modules[i].Cmdline = cmd 203 name := strings.Fields(cmd)[0] 204 f, err := os.Open(name) 205 if err != nil { 206 // TODO close already open files 207 return nil, fmt.Errorf("error opening module %v: %v", name, err) 208 } 209 modules[i].Module = f 210 } 211 return modules, nil 212 } 213 214 // LazyOpenModules assigns modules to be opened as files. 215 // 216 // Each module is a path followed by optional command-line arguments, e.g. 217 // []string{"./module arg1 arg2", "./module2 arg3 arg4"}. 218 func LazyOpenModules(cmds []string) Modules { 219 modules := make([]Module, 0, len(cmds)) 220 for _, cmd := range cmds { 221 name := strings.Fields(cmd)[0] 222 modules = append(modules, Module{ 223 Cmdline: cmd, 224 Module: uio.NewLazyFile(name), 225 }) 226 } 227 return modules 228 } 229 230 // Close closes all Modules ReaderAt implementing the io.Closer interface 231 func (m Modules) Close() error { 232 // poor error handling inspired from uio.multiCloser 233 var allErr error 234 for _, mod := range m { 235 if c, ok := mod.Module.(io.Closer); ok { 236 if err := c.Close(); err != nil { 237 allErr = err 238 } 239 } 240 } 241 return allErr 242 } 243 244 // load loads and parses multiboot information from m.kernel. 245 func (m *multiboot) load(debug bool, ibft *ibft.IBFT) error { 246 var err error 247 log.Println("Parsing multiboot header") 248 // TODO: the kernel is opened like 4 separate times here. Just open it 249 // once and pass it around. 250 251 var header imageType 252 multibootHeader, err := parseHeader(uio.Reader(m.kernel)) 253 if err == nil { 254 header = multibootHeader 255 } else if err == ErrHeaderNotFound { 256 var mutibootHeader *mutibootHeader 257 // We don't even need the header at the moment. Just need to 258 // know it's there. Everything that matters is in the ELF. 259 mutibootHeader, err = parseMutiHeader(uio.Reader(m.kernel)) 260 header = mutibootHeader 261 } 262 if err != nil { 263 return fmt.Errorf("error parsing headers: %v", err) 264 } 265 log.Printf("Found %s image", header.name()) 266 267 log.Printf("Getting kernel entry point") 268 kernelEntry, err := getEntryPoint(m.kernel) 269 if err != nil { 270 return fmt.Errorf("error getting kernel entry point: %v", err) 271 } 272 log.Printf("Kernel entry point at %#x", kernelEntry) 273 274 log.Printf("Parsing ELF segments") 275 if err := m.mem.LoadElfSegments(m.kernel); err != nil { 276 return fmt.Errorf("error loading ELF segments: %v", err) 277 } 278 279 log.Printf("Parsing memory map") 280 if err := m.mem.ParseMemoryMap(); err != nil { 281 return fmt.Errorf("error parsing memory map: %v", err) 282 } 283 284 // Insert the iBFT now, since nothing else has been allocated and this 285 // is the most restricted allocation we're gonna have to make. 286 if ibft != nil { 287 ibuf := ibft.Marshal() 288 289 // The iBFT may sit between 512K and 1M in physical memory. The 290 // loaded OS finds it by scanning this region. 291 allowedRange := kexec.Range{ 292 Start: 0x80000, 293 Size: 0x80000, 294 } 295 r, err := m.mem.ReservePhys(uint(len(ibuf)), allowedRange) 296 if err != nil { 297 return fmt.Errorf("reserving space for the iBFT in %s failed: %v", allowedRange, err) 298 } 299 log.Printf("iBFT was allocated at %s: %#v", r, ibft) 300 m.mem.Segments.Insert(kexec.NewSegment(ibuf, r)) 301 } 302 303 log.Printf("Preparing %s info", header.name()) 304 infoAddr, err := header.addInfo(m) 305 if err != nil { 306 return fmt.Errorf("error preparing %s info: %v", header.name(), err) 307 } 308 309 log.Printf("Adding trampoline") 310 if m.entryPoint, err = m.addTrampoline(header.bootMagic(), infoAddr, kernelEntry); err != nil { 311 return fmt.Errorf("error adding trampoline: %v", err) 312 } 313 log.Printf("Trampoline entry point at %#x", m.entryPoint) 314 315 if debug { 316 info, err := m.description() 317 if err != nil { 318 log.Printf("%v cannot create debug info: %v", DebugPrefix, err) 319 } 320 log.Printf("%v %v", DebugPrefix, info) 321 } 322 323 return nil 324 } 325 326 func getEntryPoint(r io.ReaderAt) (uintptr, error) { 327 f, err := elf.NewFile(r) 328 if err != nil { 329 return 0, err 330 } 331 return uintptr(f.Entry), err 332 } 333 334 // addInfo collects and adds multiboot info into the relocations/segments. 335 // 336 // addInfo marshals out everything required for 337 // https://www.gnu.org/software/grub/manual/multiboot/multiboot.html#Boot-information-format 338 // which is a memory map; a list of module structures, pointed to by mods_addr 339 // and mods_count; and the multiboot info structure itself. 340 func (h *header) addInfo(m *multiboot) (addr uintptr, err error) { 341 iw, err := h.newMultibootInfo(m) 342 if err != nil { 343 return 0, err 344 } 345 infoSize, err := iw.size() 346 if err != nil { 347 return 0, err 348 } 349 350 r, err := m.mem.FindSpace(infoSize) 351 if err != nil { 352 return 0, err 353 } 354 355 d, err := iw.marshal(r.Start) 356 if err != nil { 357 return 0, err 358 } 359 m.info = iw.info 360 361 m.mem.Segments.Insert(kexec.NewSegment(d, r)) 362 return r.Start, nil 363 } 364 365 // addInfo collects and adds mutiboot (without L!) into the segments. 366 // 367 // The format is described in the structs in 368 // https://github.com/vmware/esx-boot/blob/master/include/mutiboot.h 369 // 370 // It includes a memory map and a list of modules. 371 func (*mutibootHeader) addInfo(m *multiboot) (addr uintptr, err error) { 372 var mi mutibootInfo 373 374 mi.elems = append(mi.elems, m.memoryMap().elems()...) 375 mods, err := m.loadModules() 376 if err != nil { 377 return 0, err 378 } 379 mi.elems = append(mi.elems, mods.elems()...) 380 381 // This marshals the mutiboot info with cmdline = 0. We're gonna append 382 // the cmdline, so we must know the size of the marshaled stuff first 383 // to be able to point to it. 384 // 385 // TODO: find a better place to put the cmdline so we don't do this 386 // bullshit. 387 b := mi.marshal() 388 389 // string + null-terminator 390 cmdlineLen := len(m.cmdLine) + 1 391 392 memRange, err := m.mem.FindSpace(uint(len(b) + cmdlineLen)) 393 if err != nil { 394 return 0, err 395 } 396 mi.cmdline = uint64(memRange.Start + uintptr(len(b))) 397 398 // Re-marshal, now that the cmdline is set. 399 b = mi.marshal() 400 b = append(b, []byte(m.cmdLine)...) 401 b = append(b, 0) 402 m.mem.Segments.Insert(kexec.NewSegment(b, memRange)) 403 return memRange.Start, nil 404 } 405 406 func (m multiboot) memoryMap() memoryMaps { 407 var ret memoryMaps 408 for _, r := range m.mem.Phys { 409 typ, ok := rangeTypes[r.Type] 410 if !ok { 411 typ = rangeTypes[kexec.RangeDefault] 412 } 413 v := MemoryMap{ 414 // Size is really used for skipping to the next pair. 415 Size: uint32(sizeofMemoryMap) - 4, 416 BaseAddr: uint64(r.Start), 417 Length: uint64(r.Size), 418 Type: typ, 419 } 420 ret = append(ret, v) 421 } 422 log.Printf("Memory map: %v", ret) 423 return ret 424 } 425 426 // addMmap adds a multiboot-marshaled memory map in memory. 427 func (m *multiboot) addMmap() (addr uintptr, size uint, err error) { 428 mmap := m.memoryMap() 429 d, err := mmap.marshal() 430 if err != nil { 431 return 0, 0, err 432 } 433 r, err := m.mem.AddKexecSegment(d) 434 if err != nil { 435 return 0, 0, err 436 } 437 return r.Start, uint(len(mmap)) * sizeofMemoryMap, nil 438 } 439 440 func (m multiboot) memoryBoundaries() (lower, upper uint32) { 441 const M1 = 1048576 442 const K640 = 640 * 1024 443 for _, r := range m.mem.Phys { 444 if r.Type != kexec.RangeRAM { 445 continue 446 } 447 end := uint32(r.Start) + uint32(r.Size) 448 // Lower memory starts at address 0, and upper memory starts at address 1 megabyte. 449 // The maximum possible value for lower memory is 640 kilobytes. 450 // The value returned for upper memory is maximally the address of the first upper memory hole minus 1 megabyte. 451 // It is not guaranteed to be this value. 452 if r.Start <= K640 && end > lower { 453 lower = end 454 } 455 if r.Start <= M1 && end > upper+M1 { 456 upper = end - M1 457 } 458 } 459 return 460 } 461 462 func min(a, b uint32) uint32 { 463 if a < b { 464 return a 465 } 466 return b 467 } 468 469 func (h *header) newMultibootInfo(m *multiboot) (*infoWrapper, error) { 470 mmapAddr, mmapSize, err := m.addMmap() 471 if err != nil { 472 return nil, err 473 } 474 var inf info 475 if h.Flags&flagHeaderMemoryInfo != 0 { 476 lower, upper := m.memoryBoundaries() 477 inf = info{ 478 Flags: flagInfoMemMap | flagInfoMemory, 479 MemLower: min(uint32(lower>>10), 0xFFFFFFFF), 480 MemUpper: min(uint32(upper>>10), 0xFFFFFFFF), 481 MmapLength: uint32(mmapSize), 482 MmapAddr: uint32(mmapAddr), 483 } 484 } 485 486 if len(m.modules) > 0 { 487 modAddr, err := m.addMultibootModules() 488 if err != nil { 489 return nil, err 490 } 491 inf.Flags |= flagInfoMods 492 inf.ModsAddr = uint32(modAddr) 493 inf.ModsCount = uint32(len(m.modules)) 494 } 495 496 return &infoWrapper{ 497 info: inf, 498 Cmdline: m.cmdLine, 499 BootLoaderName: m.bootloader, 500 }, nil 501 } 502 503 func (m *multiboot) addTrampoline(magic, infoAddr, kernelEntry uintptr) (entry uintptr, err error) { 504 // Trampoline setups the machine registers to desired state 505 // and executes the loaded kernel. 506 d, err := trampoline.Setup(m.trampoline, magic, infoAddr, kernelEntry) 507 if err != nil { 508 return 0, err 509 } 510 511 r, err := m.mem.AddKexecSegment(d) 512 if err != nil { 513 return 0, err 514 } 515 return r.Start, nil 516 }