github.com/shaardie/u-root@v4.0.1-0.20190127173353-f24a1c26aa2e+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 basic primitives 6 // to load multiboot kernels as defined in 7 // https://www.gnu.org/software/grub/manual/multiboot/multiboot.html. 8 package multiboot 9 10 import ( 11 "bytes" 12 "debug/elf" 13 "encoding/binary" 14 "fmt" 15 "io" 16 "log" 17 18 "github.com/u-root/u-root/pkg/kexec" 19 "github.com/u-root/u-root/pkg/multiboot/internal/trampoline" 20 "github.com/u-root/u-root/pkg/ubinary" 21 ) 22 23 const bootloader = "u-root kexec" 24 25 // Multiboot defines parameters for working with multiboot kernels. 26 type Multiboot struct { 27 mem kexec.Memory 28 29 file string 30 modules []string 31 32 cmdLine string 33 bootloader string 34 35 // trampoline is a path to an executable blob, which contains a trampoline segment. 36 // Trampoline sets machine to a specific state defined by multiboot v1 spec. 37 // https://www.gnu.org/software/grub/manual/multiboot/multiboot.html#Machine-state. 38 trampoline string 39 40 header Header 41 42 // infoAddr is a pointer to multiboot info. 43 infoAddr uintptr 44 // kernelEntry is a pointer to entry point of kernel. 45 kernelEntry uintptr 46 // EntryPoint is a pointer to trampoline. 47 EntryPoint uintptr 48 49 info Info 50 loadedModules []Module 51 } 52 53 var rangeTypes = map[kexec.RangeType]uint32{ 54 kexec.RangeRAM: 1, 55 kexec.RangeDefault: 2, 56 kexec.RangeNVACPI: 3, 57 kexec.RangeACPI: 3, 58 kexec.RangeNVS: 4, 59 } 60 61 var sizeofMemoryMap = uint(binary.Size(MemoryMap{})) 62 63 // MemoryMap represents a reserved range of memory passed via the Multiboot Info header. 64 type MemoryMap struct { 65 // Size is the size of the associated structure in bytes. 66 Size uint32 67 // BaseAddr is the starting address. 68 BaseAddr uint64 69 // Length is the size of the memory region in bytes. 70 Length uint64 71 // Type is the variety of address range represented. 72 Type uint32 73 } 74 75 type memoryMaps []MemoryMap 76 77 // Probe checks if file is multiboot v1 kernel. 78 func Probe(file string) error { 79 b, err := readFile(file) 80 if err != nil { 81 return err 82 } 83 kernel := &kernelReader{buf: b} 84 _, err = parseHeader(kernel) 85 return err 86 } 87 88 // New returns a new Multiboot instance. 89 func New(file, cmdLine, trampoline string, modules []string) *Multiboot { 90 return &Multiboot{ 91 file: file, 92 modules: modules, 93 cmdLine: cmdLine, 94 trampoline: trampoline, 95 bootloader: bootloader, 96 mem: kexec.Memory{}, 97 } 98 } 99 100 // Load loads and parses multiboot information from m.file. 101 func (m *Multiboot) Load(debug bool) error { 102 log.Printf("Parsing file %v", m.file) 103 b, err := readFile(m.file) 104 if err != nil { 105 return err 106 } 107 kernel := kernelReader{buf: b} 108 log.Println("Parsing Multiboot Header") 109 if m.header, err = parseHeader(&kernel); err != nil { 110 return fmt.Errorf("Error parsing headers: %v", err) 111 } 112 113 log.Printf("Getting kernel entry point") 114 if m.kernelEntry, err = getEntryPoint(kernel); err != nil { 115 return fmt.Errorf("Error getting kernel entry point: %v", err) 116 } 117 118 log.Printf("Parsing ELF segments") 119 if err := m.mem.LoadElfSegments(kernel); err != nil { 120 return fmt.Errorf("Error loading ELF segments: %v", err) 121 } 122 123 log.Printf("Parsing memory map") 124 if err := m.mem.ParseMemoryMap(); err != nil { 125 return fmt.Errorf("Error parsing memory map: %v", err) 126 } 127 128 log.Printf("Preparing Multiboot Info") 129 if m.infoAddr, err = m.addInfo(); err != nil { 130 return fmt.Errorf("Error preparing Multiboot Info: %v", err) 131 } 132 133 log.Printf("Adding trampoline") 134 if m.EntryPoint, err = m.addTrampoline(); err != nil { 135 return fmt.Errorf("Error adding trampoline: %v", err) 136 } 137 138 if debug { 139 info, err := m.Description() 140 if err != nil { 141 log.Printf("%v cannot create debug info: %v", DebugPrefix, err) 142 } 143 log.Printf("%v %v", DebugPrefix, info) 144 } 145 146 return nil 147 } 148 149 func getEntryPoint(r io.ReaderAt) (uintptr, error) { 150 f, err := elf.NewFile(r) 151 if err != nil { 152 return 0, err 153 } 154 return uintptr(f.Entry), err 155 } 156 157 func (m *Multiboot) addInfo() (addr uintptr, err error) { 158 iw, err := m.newMultibootInfo() 159 if err != nil { 160 return 0, err 161 } 162 infoSize, err := iw.size() 163 if err != nil { 164 return 0, err 165 } 166 167 addr, err = m.mem.FindSpace(infoSize) 168 if err != nil { 169 return 0, err 170 } 171 172 d, err := iw.marshal(addr) 173 if err != nil { 174 return 0, err 175 } 176 m.info = iw.Info 177 178 addr, err = m.mem.AddKexecSegment(d) 179 if err != nil { 180 return 0, err 181 } 182 return addr, nil 183 } 184 185 func (m Multiboot) memoryMap() memoryMaps { 186 var ret memoryMaps 187 for _, r := range m.mem.Phys { 188 typ, ok := rangeTypes[r.Type] 189 if !ok { 190 typ = rangeTypes[kexec.RangeDefault] 191 } 192 v := MemoryMap{ 193 // Size is really used for skipping to the next pair. 194 Size: uint32(sizeofMemoryMap) - 4, 195 BaseAddr: uint64(r.Start), 196 Length: uint64(r.Size) + 1, 197 Type: typ, 198 } 199 ret = append(ret, v) 200 } 201 return ret 202 } 203 204 func (m *Multiboot) addMmap() (addr uintptr, size uint, err error) { 205 mmap := m.memoryMap() 206 d, err := mmap.marshal() 207 if err != nil { 208 return 0, 0, err 209 } 210 addr, err = m.mem.AddKexecSegment(d) 211 if err != nil { 212 return 0, 0, err 213 } 214 return addr, uint(len(mmap)) * sizeofMemoryMap, nil 215 } 216 217 func (m Multiboot) memoryBoundaries() (lower, upper uint32) { 218 const M1 = 1048576 219 const K640 = 640 * 1024 220 for _, r := range m.mem.Phys { 221 if r.Type != kexec.RangeRAM { 222 continue 223 } 224 end := uint32(r.Start) + uint32(r.Size) 225 // Lower memory starts at address 0, and upper memory starts at address 1 megabyte. 226 // The maximum possible value for lower memory is 640 kilobytes. 227 // The value returned for upper memory is maximally the address of the first upper memory hole minus 1 megabyte. 228 // It is not guaranteed to be this value. 229 if r.Start <= K640 && end > lower { 230 lower = end 231 } 232 if r.Start <= M1 && end > upper+M1 { 233 upper = end - M1 234 } 235 } 236 return 237 } 238 239 func min(a, b uint32) uint32 { 240 if a < b { 241 return a 242 } 243 return b 244 } 245 246 func (m *Multiboot) newMultibootInfo() (*infoWrapper, error) { 247 mmapAddr, mmapSize, err := m.addMmap() 248 if err != nil { 249 return nil, err 250 } 251 var info Info 252 if m.header.Flags&flagHeaderMemoryInfo != 0 { 253 lower, upper := m.memoryBoundaries() 254 info = Info{ 255 Flags: flagInfoMemMap | flagInfoMemory, 256 MemLower: min(uint32(lower>>10), 0xFFFFFFFF), 257 MemUpper: min(uint32(upper>>10), 0xFFFFFFFF), 258 MmapLength: uint32(mmapSize), 259 MmapAddr: uint32(mmapAddr), 260 } 261 } 262 263 if len(m.modules) > 0 { 264 modAddr, err := m.addModules() 265 if err != nil { 266 return nil, err 267 } 268 info.Flags |= flagInfoMods 269 info.ModsAddr = uint32(modAddr) 270 info.ModsCount = uint32(len(m.modules)) 271 } 272 273 info.CmdLine = sizeofInfo 274 info.BootLoaderName = sizeofInfo + uint32(len(m.cmdLine)) + 1 275 info.Flags |= flagInfoCmdLine | flagInfoBootLoaderName 276 return &infoWrapper{ 277 Info: info, 278 CmdLine: m.cmdLine, 279 BootLoaderName: m.bootloader, 280 }, nil 281 } 282 283 // Segments returns kexec.Segments, where all the multiboot related 284 // information is stored. 285 func (m Multiboot) Segments() []kexec.Segment { 286 return m.mem.Segments 287 } 288 289 // marshal writes out the exact bytes expected by the multiboot info header 290 // specified in 291 // https://www.gnu.org/software/grub/manual/multiboot/multiboot.html#Boot-information-format. 292 func (m memoryMaps) marshal() ([]byte, error) { 293 buf := bytes.Buffer{} 294 err := binary.Write(&buf, ubinary.NativeEndian, m) 295 return buf.Bytes(), err 296 } 297 298 func (m *Multiboot) addTrampoline() (entry uintptr, err error) { 299 // Trampoline setups the machine registers to desired state 300 // and executes the loaded kernel. 301 d, err := trampoline.Setup(m.trampoline, m.infoAddr, m.kernelEntry) 302 if err != nil { 303 return 0, err 304 } 305 306 addr, err := m.mem.AddKexecSegment(d) 307 if err != nil { 308 return 0, err 309 } 310 311 return addr, nil 312 }