github.com/xyproto/u-root@v6.0.1-0.20200302025726-5528e0c77a3c+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) addModules() (uintptr, error) { 37 loaded, data, err := loadModules(m.modules) 38 if err != nil { 39 return 0, err 40 } 41 42 cmdlineRange, err := m.mem.AddKexecSegment(data) 43 if err != nil { 44 return 0, err 45 } 46 47 loaded.fix(uint32(cmdlineRange.Start)) 48 49 m.loadedModules = loaded 50 51 b, err := loaded.marshal() 52 if err != nil { 53 return 0, err 54 } 55 modRange, err := m.mem.AddKexecSegment(b) 56 if err != nil { 57 return 0, err 58 } 59 return modRange.Start, nil 60 } 61 62 // loadModules loads module files. 63 // Returns loaded modules description and buffer storing loaded modules. 64 // Memory layout of the loaded modules is following: 65 // cmdLine_1 66 // cmdLine_2 67 // ... 68 // cmdLine_n 69 // <padding> 70 // modules_1 71 // <padding> 72 // modules_2 73 // ... 74 // <padding> 75 // modules_n 76 // 77 // <padding> aligns the start of each module to a page beginning. 78 func loadModules(rmods []Module) (loaded modules, data []byte, err error) { 79 loaded = make(modules, len(rmods)) 80 buf := bytes.Buffer{} 81 82 for i, rmod := range rmods { 83 if err := loaded[i].setCmdLine(&buf, rmod.CmdLine); err != nil { 84 return nil, nil, err 85 } 86 } 87 88 for i, rmod := range rmods { 89 if err := loaded[i].loadModule(&buf, rmod.Module, rmod.Name); err != nil { 90 return nil, nil, fmt.Errorf("error adding module %v: %v", rmod.Name, err) 91 } 92 } 93 94 return loaded, buf.Bytes(), nil 95 } 96 97 // alignUp pads buf to a page boundary. 98 func alignUp(buf *bytes.Buffer) error { 99 mask := (os.Getpagesize() - 1) 100 size := (buf.Len() + mask) &^ mask 101 _, err := buf.Write(bytes.Repeat([]byte{0}, size-buf.Len())) 102 return err 103 } 104 105 func (m *module) loadModule(buf *bytes.Buffer, r io.ReaderAt, name string) error { 106 log.Printf("Adding module %v", name) 107 108 // place start of each module to a beginning of a page. 109 if err := alignUp(buf); err != nil { 110 return err 111 } 112 113 m.Start = uint32(buf.Len()) 114 115 if _, err := io.Copy(buf, uio.Reader(r)); err != nil { 116 return err 117 } 118 119 m.End = uint32(buf.Len()) 120 121 return nil 122 } 123 124 func (m *module) setCmdLine(buf *bytes.Buffer, cmdLine string) error { 125 m.CmdLine = uint32(buf.Len()) 126 if _, err := buf.WriteString(cmdLine); err != nil { 127 return err 128 } 129 return buf.WriteByte(0) 130 } 131 132 // fix fixes pointers converting relative values to absolute values. 133 func (m modules) fix(base uint32) { 134 for i := range m { 135 m[i].Start += base 136 m[i].End += base 137 m[i].CmdLine += base 138 } 139 } 140 141 // marshal writes out the exact bytes of modules to be loaded 142 // along with the kernel. 143 func (m modules) marshal() ([]byte, error) { 144 buf := bytes.Buffer{} 145 err := binary.Write(&buf, ubinary.NativeEndian, m) 146 return buf.Bytes(), err 147 }