github.com/xyproto/u-root@v6.0.1-0.20200302025726-5528e0c77a3c+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 header header 58 59 // infoAddr is a pointer to multiboot info. 60 infoAddr uintptr 61 // kernelEntry is a pointer to entry point of kernel. 62 kernelEntry uintptr 63 // EntryPoint is a pointer to trampoline. 64 entryPoint uintptr 65 66 info info 67 loadedModules []module 68 } 69 70 var ( 71 rangeTypes = map[kexec.RangeType]uint32{ 72 kexec.RangeRAM: 1, 73 kexec.RangeDefault: 2, 74 kexec.RangeACPI: 3, 75 kexec.RangeNVS: 4, 76 kexec.RangeReserved: 2, 77 } 78 ) 79 80 var sizeofMemoryMap = uint(binary.Size(MemoryMap{})) 81 82 // MemoryMap represents a reserved range of memory passed via the multiboot Info header. 83 type MemoryMap struct { 84 // Size is the size of the associated structure in bytes. 85 Size uint32 86 // BaseAddr is the starting address. 87 BaseAddr uint64 88 // Length is the size of the memory region in bytes. 89 Length uint64 90 // Type is the variety of address range represented. 91 Type uint32 92 } 93 94 // String returns a readable representation of a MemoryMap entry. 95 func (m MemoryMap) String() string { 96 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) 97 } 98 99 type memoryMaps []MemoryMap 100 101 // String returns a new-line-separated representation of the entire memory map. 102 func (m memoryMaps) String() string { 103 var s []string 104 for _, mm := range m { 105 s = append(s, mm.String()) 106 } 107 return strings.Join(s, "\n") 108 } 109 110 // Probe checks if `kernel` is multiboot v1 kernel. 111 func Probe(kernel io.ReaderAt) error { 112 r := tryGzipFilter(kernel) 113 _, err := parseHeader(uio.Reader(r)) 114 return err 115 } 116 117 // newMB returns a new multiboot instance. 118 func newMB(kernel io.ReaderAt, cmdLine string, modules []Module) (*multiboot, error) { 119 // Trampoline should be a part of current binary. 120 p, err := os.Executable() 121 if err != nil { 122 return nil, fmt.Errorf("cannot find current executable path: %v", err) 123 } 124 trampoline, err := filepath.EvalSymlinks(p) 125 if err != nil { 126 return nil, fmt.Errorf("cannot eval symlinks for %v: %v", p, err) 127 } 128 129 return &multiboot{ 130 kernel: kernel, 131 modules: modules, 132 cmdLine: cmdLine, 133 trampoline: trampoline, 134 bootloader: bootloader, 135 mem: kexec.Memory{}, 136 }, nil 137 } 138 139 // Load parses and loads a multiboot `kernel` using kexec_load. 140 // 141 // debug turns on debug logging. 142 // 143 // Load can set up an arbitrary number of modules, and takes care of the 144 // multiboot info structure, including the memory map. 145 // 146 // After Load is called, kexec.Reboot() is ready to be called any time to stop 147 // Linux and execute the loaded kernel. 148 func Load(debug bool, kernel io.ReaderAt, cmdline string, modules []Module, ibft *ibft.IBFT) error { 149 kernel = tryGzipFilter(kernel) 150 for i, mod := range modules { 151 modules[i].Module = tryGzipFilter(mod.Module) 152 } 153 154 m, err := newMB(kernel, cmdline, modules) 155 if err != nil { 156 return err 157 } 158 if err := m.load(debug, ibft); err != nil { 159 return err 160 } 161 if err := kexec.Load(m.entryPoint, m.mem.Segments, 0); err != nil { 162 return fmt.Errorf("kexec.Load() error: %v", err) 163 } 164 return nil 165 } 166 167 // OpenModules open modules as files and fill a range of `Module` struct 168 // 169 // Each module is a path followed by optional command-line arguments, e.g. 170 // []string{"./module arg1 arg2", "./module2 arg3 arg4"}. 171 func OpenModules(cmds []string) (Modules, error) { 172 modules := make([]Module, len(cmds)) 173 for i, cmd := range cmds { 174 modules[i].CmdLine = cmd 175 name := strings.Fields(cmd)[0] 176 modules[i].Name = name 177 f, err := os.Open(name) 178 if err != nil { 179 // TODO close already open files 180 return nil, fmt.Errorf("error opening module %v: %v", name, err) 181 } 182 modules[i].Module = f 183 } 184 return modules, nil 185 } 186 187 // LazyOpenModules assigns modules to be opened as files. 188 // 189 // Each module is a path followed by optional command-line arguments, e.g. 190 // []string{"./module arg1 arg2", "./module2 arg3 arg4"}. 191 func LazyOpenModules(cmds []string) Modules { 192 modules := make([]Module, 0, len(cmds)) 193 for _, cmd := range cmds { 194 name := strings.Fields(cmd)[0] 195 modules = append(modules, Module{ 196 CmdLine: cmd, 197 Name: name, 198 Module: uio.NewLazyFile(name), 199 }) 200 } 201 return modules 202 } 203 204 // Close closes all Modules ReaderAt implementing the io.Closer interface 205 func (m Modules) Close() error { 206 // poor error handling inspired from uio.multiCloser 207 var allErr error 208 for _, mod := range m { 209 if c, ok := mod.Module.(io.Closer); ok { 210 if err := c.Close(); err != nil { 211 allErr = err 212 } 213 } 214 } 215 return allErr 216 } 217 218 // load loads and parses multiboot information from m.kernel. 219 func (m *multiboot) load(debug bool, ibft *ibft.IBFT) error { 220 var err error 221 log.Println("Parsing multiboot header") 222 if m.header, err = parseHeader(uio.Reader(m.kernel)); err != nil { 223 return fmt.Errorf("error parsing headers: %v", err) 224 } 225 226 log.Printf("Getting kernel entry point") 227 if m.kernelEntry, err = getEntryPoint(m.kernel); err != nil { 228 return fmt.Errorf("error getting kernel entry point: %v", err) 229 } 230 log.Printf("Kernel entry point at %#x", m.kernelEntry) 231 232 log.Printf("Parsing ELF segments") 233 if err := m.mem.LoadElfSegments(m.kernel); err != nil { 234 return fmt.Errorf("error loading ELF segments: %v", err) 235 } 236 237 log.Printf("Parsing memory map") 238 if err := m.mem.ParseMemoryMap(); err != nil { 239 return fmt.Errorf("error parsing memory map: %v", err) 240 } 241 242 // Insert the iBFT now, since nothing else has been allocated and this 243 // is the most restricted allocation we're gonna have to make. 244 if ibft != nil { 245 ibuf := ibft.Marshal() 246 247 // The iBFT may sit between 512K and 1M in physical memory. The 248 // loaded OS finds it by scanning this region. 249 allowedRange := kexec.Range{ 250 Start: 0x80000, 251 Size: 0x80000, 252 } 253 r, err := m.mem.ReservePhys(uint(len(ibuf)), allowedRange) 254 if err != nil { 255 return fmt.Errorf("reserving space for the iBFT in %s failed: %v", allowedRange, err) 256 } 257 log.Printf("iBFT was allocated at %s: %#v", r, ibft) 258 m.mem.Segments.Insert(kexec.NewSegment(ibuf, r)) 259 } 260 261 log.Printf("Preparing multiboot info") 262 if m.infoAddr, err = m.addInfo(); err != nil { 263 return fmt.Errorf("error preparing multiboot info: %v", err) 264 } 265 266 log.Printf("Adding trampoline") 267 if m.entryPoint, err = m.addTrampoline(); err != nil { 268 return fmt.Errorf("error adding trampoline: %v", err) 269 } 270 log.Printf("Trampoline entry point at %#x", m.entryPoint) 271 272 if debug { 273 info, err := m.description() 274 if err != nil { 275 log.Printf("%v cannot create debug info: %v", DebugPrefix, err) 276 } 277 log.Printf("%v %v", DebugPrefix, info) 278 } 279 280 return nil 281 } 282 283 func getEntryPoint(r io.ReaderAt) (uintptr, error) { 284 f, err := elf.NewFile(r) 285 if err != nil { 286 return 0, err 287 } 288 return uintptr(f.Entry), err 289 } 290 291 func (m *multiboot) addInfo() (addr uintptr, err error) { 292 iw, err := m.newMultibootInfo() 293 if err != nil { 294 return 0, err 295 } 296 infoSize, err := iw.size() 297 if err != nil { 298 return 0, err 299 } 300 301 r, err := m.mem.FindSpace(infoSize) 302 if err != nil { 303 return 0, err 304 } 305 306 d, err := iw.marshal(r.Start) 307 if err != nil { 308 return 0, err 309 } 310 m.info = iw.info 311 312 m.mem.Segments.Insert(kexec.NewSegment(d, r)) 313 return r.Start, nil 314 } 315 316 func (m multiboot) memoryMap() memoryMaps { 317 var ret memoryMaps 318 for _, r := range m.mem.Phys { 319 typ, ok := rangeTypes[r.Type] 320 if !ok { 321 typ = rangeTypes[kexec.RangeDefault] 322 } 323 v := MemoryMap{ 324 // Size is really used for skipping to the next pair. 325 Size: uint32(sizeofMemoryMap) - 4, 326 BaseAddr: uint64(r.Start), 327 Length: uint64(r.Size), 328 Type: typ, 329 } 330 ret = append(ret, v) 331 } 332 return ret 333 } 334 335 func (m *multiboot) addMmap() (addr uintptr, size uint, err error) { 336 mmap := m.memoryMap() 337 log.Printf("Memory map:\n%s", mmap) 338 d, err := mmap.marshal() 339 if err != nil { 340 return 0, 0, err 341 } 342 r, err := m.mem.AddKexecSegment(d) 343 if err != nil { 344 return 0, 0, err 345 } 346 return r.Start, uint(len(mmap)) * sizeofMemoryMap, nil 347 } 348 349 func (m multiboot) memoryBoundaries() (lower, upper uint32) { 350 const M1 = 1048576 351 const K640 = 640 * 1024 352 for _, r := range m.mem.Phys { 353 if r.Type != kexec.RangeRAM { 354 continue 355 } 356 end := uint32(r.Start) + uint32(r.Size) 357 // Lower memory starts at address 0, and upper memory starts at address 1 megabyte. 358 // The maximum possible value for lower memory is 640 kilobytes. 359 // The value returned for upper memory is maximally the address of the first upper memory hole minus 1 megabyte. 360 // It is not guaranteed to be this value. 361 if r.Start <= K640 && end > lower { 362 lower = end 363 } 364 if r.Start <= M1 && end > upper+M1 { 365 upper = end - M1 366 } 367 } 368 return 369 } 370 371 func min(a, b uint32) uint32 { 372 if a < b { 373 return a 374 } 375 return b 376 } 377 378 func (m *multiboot) newMultibootInfo() (*infoWrapper, error) { 379 mmapAddr, mmapSize, err := m.addMmap() 380 if err != nil { 381 return nil, err 382 } 383 var inf info 384 if m.header.Flags&flagHeaderMemoryInfo != 0 { 385 lower, upper := m.memoryBoundaries() 386 inf = info{ 387 Flags: flagInfoMemMap | flagInfoMemory, 388 MemLower: min(uint32(lower>>10), 0xFFFFFFFF), 389 MemUpper: min(uint32(upper>>10), 0xFFFFFFFF), 390 MmapLength: uint32(mmapSize), 391 MmapAddr: uint32(mmapAddr), 392 } 393 } 394 395 if len(m.modules) > 0 { 396 modAddr, err := m.addModules() 397 if err != nil { 398 return nil, err 399 } 400 inf.Flags |= flagInfoMods 401 inf.ModsAddr = uint32(modAddr) 402 inf.ModsCount = uint32(len(m.modules)) 403 } 404 405 inf.CmdLine = sizeofInfo 406 inf.BootLoaderName = sizeofInfo + uint32(len(m.cmdLine)) + 1 407 inf.Flags |= flagInfoCmdLine | flagInfoBootLoaderName 408 return &infoWrapper{ 409 info: inf, 410 CmdLine: m.cmdLine, 411 BootLoaderName: m.bootloader, 412 }, nil 413 } 414 415 // marshal writes out the exact bytes expected by the multiboot info header 416 // specified in 417 // https://www.gnu.org/software/grub/manual/multiboot/multiboot.html#Boot-information-format. 418 func (m memoryMaps) marshal() ([]byte, error) { 419 buf := bytes.Buffer{} 420 err := binary.Write(&buf, ubinary.NativeEndian, m) 421 return buf.Bytes(), err 422 } 423 424 func (m *multiboot) addTrampoline() (entry uintptr, err error) { 425 // Trampoline setups the machine registers to desired state 426 // and executes the loaded kernel. 427 d, err := trampoline.Setup(m.trampoline, m.infoAddr, m.kernelEntry) 428 if err != nil { 429 return 0, err 430 } 431 432 r, err := m.mem.AddKexecSegment(d) 433 if err != nil { 434 return 0, err 435 } 436 return r.Start, nil 437 }