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