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