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