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