github.com/u-root/u-root@v7.0.1-0.20200915234505-ad7babab0a8e+incompatible/pkg/boot/multiboot/module.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 6 7 import ( 8 "bytes" 9 "encoding/binary" 10 "fmt" 11 "io" 12 "log" 13 "os" 14 15 "github.com/u-root/u-root/pkg/ubinary" 16 "github.com/u-root/u-root/pkg/uio" 17 ) 18 19 // A module represents a module to be loaded along with the kernel. 20 type module struct { 21 // Start is the inclusive start of the Module memory location 22 Start uint32 23 24 // End is the exclusive end of the Module memory location. 25 End uint32 26 27 // Cmdline is a pointer to a null-terminated ASCII string. 28 Cmdline uint32 29 30 // Reserved is always zero. 31 Reserved uint32 32 } 33 34 type modules []module 35 36 func (m *multiboot) loadModules() (modules, error) { 37 loaded, data, err := loadModules(m.modules) 38 if err != nil { 39 return nil, err 40 } 41 42 cmdlineRange, err := m.mem.AddKexecSegment(data) 43 if err != nil { 44 return nil, err 45 } 46 47 loaded.fix(uint32(cmdlineRange.Start)) 48 m.loadedModules = loaded 49 50 for i, mod := range loaded { 51 log.Printf("Added module %s at [%#x, %#x)", m.modules[i].Name(), mod.Start, mod.End) 52 } 53 54 return loaded, nil 55 } 56 57 func (m *multiboot) addMultibootModules() (uintptr, error) { 58 loaded, err := m.loadModules() 59 if err != nil { 60 return 0, err 61 } 62 b, err := loaded.marshal() 63 if err != nil { 64 return 0, err 65 } 66 modRange, err := m.mem.AddKexecSegment(b) 67 if err != nil { 68 return 0, err 69 } 70 return modRange.Start, nil 71 } 72 73 // loadModules loads module files. 74 // Returns loaded modules description and buffer storing loaded modules. 75 // Memory layout of the loaded modules is following: 76 // cmdLine_1 77 // cmdLine_2 78 // ... 79 // cmdLine_n 80 // <padding> 81 // modules_1 82 // <padding> 83 // modules_2 84 // ... 85 // <padding> 86 // modules_n 87 // 88 // <padding> aligns the start of each module to a page beginning. 89 func loadModules(rmods []Module) (loaded modules, data []byte, err error) { 90 loaded = make(modules, len(rmods)) 91 var buf bytes.Buffer 92 93 for i, rmod := range rmods { 94 if err := loaded[i].setCmdline(&buf, rmod.Cmdline); err != nil { 95 return nil, nil, err 96 } 97 } 98 99 for i, rmod := range rmods { 100 if err := loaded[i].loadModule(&buf, rmod.Module); err != nil { 101 return nil, nil, fmt.Errorf("error adding module %v: %v", rmod.Name(), err) 102 } 103 } 104 return loaded, buf.Bytes(), nil 105 } 106 107 func pageAlign(val uint32) uint32 { 108 mask := uint32(os.Getpagesize() - 1) 109 return (val + mask) &^ mask 110 } 111 112 // pageAlignBuf pads buf to a page boundary. 113 func pageAlignBuf(buf *bytes.Buffer) error { 114 mask := (os.Getpagesize() - 1) 115 size := (buf.Len() + mask) &^ mask 116 _, err := buf.Write(bytes.Repeat([]byte{0}, size-buf.Len())) 117 return err 118 } 119 120 func (m *module) loadModule(buf *bytes.Buffer, r io.ReaderAt) error { 121 // place start of each module to a beginning of a page. 122 if err := pageAlignBuf(buf); err != nil { 123 return err 124 } 125 126 m.Start = uint32(buf.Len()) 127 128 if _, err := io.Copy(buf, uio.Reader(r)); err != nil { 129 return err 130 } 131 132 m.End = uint32(buf.Len()) 133 return nil 134 } 135 136 func (m *module) setCmdline(buf *bytes.Buffer, cmdLine string) error { 137 m.Cmdline = uint32(buf.Len()) 138 if _, err := buf.WriteString(cmdLine); err != nil { 139 return err 140 } 141 return buf.WriteByte(0) 142 } 143 144 // fix fixes pointers converting relative values to absolute values. 145 func (m modules) fix(base uint32) { 146 for i := range m { 147 m[i].Start += base 148 m[i].End += base 149 m[i].Cmdline += base 150 } 151 } 152 153 // marshal writes out the module list in multiboot info format, as described in 154 // https://www.gnu.org/software/grub/manual/multiboot/multiboot.html#Boot-information-format 155 func (m modules) marshal() ([]byte, error) { 156 buf := bytes.Buffer{} 157 err := binary.Write(&buf, ubinary.NativeEndian, m) 158 return buf.Bytes(), err 159 } 160 161 // elems adds mutiboot info elements describing where to find each module and 162 // its cmdline. 163 func (m modules) elems() []elem { 164 var e []elem 165 for _, mm := range m { 166 e = append(e, &mutibootModule{ 167 cmdline: uint64(mm.Cmdline), 168 moduleSize: uint64(mm.End - mm.Start), 169 ranges: []mutibootModuleRange{ 170 { 171 startPageNum: uint64(mm.Start / uint32(os.Getpagesize())), 172 numPages: pageAlign(mm.End-mm.Start) / uint32(os.Getpagesize()), 173 }, 174 }, 175 }) 176 } 177 return e 178 }