The Pedigree Project 0.1
Boot.cc
1#include "pedigree/kernel/BootstrapInfo.h"
2
3#include "../../../machine/mach_virt/DeviceTree.h"
4
5extern "C" void _main(BootstrapStruct_t& bootstrap);
6extern "C" char kernel_physical_start, kernel_physical_end;
7
8namespace {
9constexpr size_t MaximumMemoryRegions = 128;
10constexpr uint64_t DirectMapBase = 0xffff000000000000ULL;
11BootstrapStruct_t::MemoryMapEntry memoryMap[MaximumMemoryRegions] = {};
12BootstrapStruct_t::Module initrdModule = {};
13char uefiCommandLine[4096] = {};
14
15struct ReservedRegion {
16 uint64_t start;
17 uint64_t end;
18};
19
20uint64_t alignDown(uint64_t value) {
21 return value & ~uint64_t(PAGE_SIZE - 1);
22}
23
24uint64_t alignUp(uint64_t value) {
25 return (value + PAGE_SIZE - 1) & ~uint64_t(PAGE_SIZE - 1);
26}
27
28bool readHex(const char*& text, uint64_t& value) {
29 value = 0;
30 bool found = false;
31 if (text[0] == '0' && (text[1] == 'x' || text[1] == 'X')) {
32 text += 2;
33 }
34 for (;;) {
35 const char c = *text;
36 uint64_t digit = 0;
37 if (c >= '0' && c <= '9') {
38 digit = c - '0';
39 } else if (c >= 'a' && c <= 'f') {
40 digit = c - 'a' + 10;
41 } else if (c >= 'A' && c <= 'F') {
42 digit = c - 'A' + 10;
43 } else {
44 break;
45 }
46 if (value > (UINT64_MAX - digit) / 16) {
47 return false;
48 }
49 value = value * 16 + digit;
50 ++text;
51 found = true;
52 }
53 return found;
54}
55
56bool initrdFromBootargs(const char* bootargs, uint64_t& start, uint64_t& end) {
57 static const char prefix[] = "pedigree.initrd=";
58 if (!bootargs) {
59 return false;
60 }
61 while (*bootargs) {
62 while (*bootargs == ' ') {
63 ++bootargs;
64 }
65 const char* token = bootargs;
66 while (*bootargs && *bootargs != ' ') {
67 ++bootargs;
68 }
69 const char* cursor = token;
70 for (size_t i = 0; i < sizeof(prefix) - 1 && cursor < bootargs; ++i) {
71 if (*cursor++ != prefix[i]) {
72 break;
73 }
74 if (i == sizeof(prefix) - 2) {
75 return readHex(cursor, start) && cursor < bootargs && *cursor++ == ':' &&
76 readHex(cursor, end) && cursor == bootargs && start < end;
77 }
78 }
79 }
80 return false;
81}
82
83bool initrdInMemory(uint64_t start, uint64_t end, const void* deviceTree,
84 const BootstrapStruct_t::MemoryMapEntry* uefiMap, size_t uefiCount) {
85 if (!start || start >= end) {
86 return false;
87 }
88 const uint64_t kernelStart = reinterpret_cast<uintptr_t>(&kernel_physical_start);
89 const uint64_t kernelEnd = reinterpret_cast<uintptr_t>(&kernel_physical_end);
90 const uint64_t dtbStart = reinterpret_cast<uintptr_t>(deviceTree);
91 const uint64_t dtbEnd = dtbStart + VirtDeviceTree::blobSize();
92 if (!(end <= kernelStart || start >= kernelEnd) ||
93 (deviceTree && !(end <= dtbStart || start >= dtbEnd))) {
94 return false;
95 }
96 if (uefiMap) {
97 for (size_t i = 0; i < uefiCount; ++i) {
98 const auto& entry = uefiMap[i];
99 if (entry.type == 2 && entry.address <= start && entry.length &&
100 entry.address <= UINT64_MAX - entry.length && end <= entry.address + entry.length) {
101 return true;
102 }
103 }
104 return false;
105 }
106 for (size_t i = 0; i < MaximumMemoryRegions; ++i) {
107 uint64_t base = 0, size = 0;
108 if (!virtGetMemoryRegion(i, &base, &size)) {
109 break;
110 }
111 if (base <= start && size && base <= UINT64_MAX - size && end <= base + size) {
112 return true;
113 }
114 }
115 return false;
116}
117
118void addUsable(uint64_t start, uint64_t end, size_t& count) {
119 start = alignUp(start);
120 end = alignDown(end);
121 if (start >= end || count == MaximumMemoryRegions) {
122 return;
123 }
124 memoryMap[count++] = {sizeof(BootstrapStruct_t::MemoryMapEntry), start, end - start, 1};
125}
126} // namespace
127
128extern "C" void arm64BootMain(const void* deviceTree, uint64_t uefiInitrdStart,
129 uint64_t uefiInitrdEnd, uint64_t uefiCommandLineAddress,
130 uint64_t uefiMemoryMapAddress, uint64_t uefiMemoryMapBytes,
131 uint64_t uefiAcpiRsdp) {
132 const bool uefi = uefiCommandLineAddress != 0;
133 if (uefi && (!uefiMemoryMapAddress || !uefiMemoryMapBytes ||
134 uefiMemoryMapBytes % sizeof(BootstrapStruct_t::MemoryMapEntry) ||
135 uefiMemoryMapBytes > 16 * PAGE_SIZE)) {
136 while (true) {
137 asm volatile("wfe");
138 }
139 }
140 const auto* uefiMemoryMap = uefi ? reinterpret_cast<const BootstrapStruct_t::MemoryMapEntry*>(
141 DirectMapBase + uefiMemoryMapAddress)
142 : nullptr;
143 const size_t uefiCount =
144 uefi ? uefiMemoryMapBytes / sizeof(BootstrapStruct_t::MemoryMapEntry) : 0;
145 if (deviceTree) {
146 virtSetDeviceTree(deviceTree);
147 } else if (uefi && uefiAcpiRsdp) {
148 VirtDeviceTree::initialiseAcpi(uefiAcpiRsdp, uefiMemoryMap, uefiCount);
149 }
150 if (!VirtDeviceTree::valid()) {
151 while (true) {
152 asm volatile("wfe");
153 }
154 }
155
156 const char* bootargs = nullptr;
157 virtGetBootargs(&bootargs);
158 if (uefi) {
159 const char* supplied = reinterpret_cast<const char*>(DirectMapBase + uefiCommandLineAddress);
160 size_t length = 0;
161 while (length < sizeof(uefiCommandLine) - 1 && supplied[length]) {
162 uefiCommandLine[length] = supplied[length];
163 ++length;
164 }
165 uefiCommandLine[length] = 0;
166 bootargs = uefiCommandLine;
167 }
168 uint64_t initrdStart = 0, initrdEnd = 0;
169 if (uefiInitrdStart && uefiInitrdEnd) {
170 initrdStart = uefiInitrdStart;
171 initrdEnd = uefiInitrdEnd;
172 }
173 const bool hasInitrd =
174 ((initrdStart && initrdEnd) || virtGetInitrd(&initrdStart, &initrdEnd) ||
175 initrdFromBootargs(bootargs, initrdStart, initrdEnd)) &&
176 initrdInMemory(initrdStart, initrdEnd, deviceTree, uefiMemoryMap, uefiCount);
177 ReservedRegion reserved[3] = {
178 {alignDown(reinterpret_cast<uintptr_t>(&kernel_physical_start)),
179 alignUp(reinterpret_cast<uintptr_t>(&kernel_physical_end))},
180 {alignDown(reinterpret_cast<uintptr_t>(deviceTree)),
181 alignUp(reinterpret_cast<uintptr_t>(deviceTree) + VirtDeviceTree::blobSize())},
182 {hasInitrd ? alignDown(initrdStart) : 0, hasInitrd ? alignUp(initrdEnd) : 0}};
183 for (size_t i = 1; i < 3; ++i) {
184 for (size_t j = i; j && reserved[j].start < reserved[j - 1].start; --j) {
185 const ReservedRegion previous = reserved[j - 1];
186 reserved[j - 1] = reserved[j];
187 reserved[j] = previous;
188 }
189 }
190
191 size_t count = 0;
192 const size_t sourceCount = uefi ? uefiCount : MaximumMemoryRegions / 4;
193 for (size_t i = 0; i < sourceCount; ++i) {
194 uint64_t base = 0, size = 0;
195 if (uefi) {
196 if (uefiMemoryMap[i].type != 1) {
197 continue;
198 }
199 base = uefiMemoryMap[i].address;
200 size = uefiMemoryMap[i].length;
201 } else {
202 if (!virtGetMemoryRegion(i, &base, &size)) {
203 break;
204 }
205 }
206 if (!size || base + size < base) {
207 continue;
208 }
209
210 uint64_t cursor = base;
211 const uint64_t end = base + size;
212 for (size_t r = 0; r < 3; ++r) {
213 if (!reserved[r].end || reserved[r].start >= end) {
214 continue;
215 }
216 if (reserved[r].start > cursor) {
217 addUsable(cursor, reserved[r].start < end ? reserved[r].start : end, count);
218 }
219 if (reserved[r].end > cursor) {
220 cursor = reserved[r].end;
221 }
222 }
223 addUsable(cursor, end, count);
224 }
225
226 BootstrapStruct_t bootstrap;
227 bootstrap.setMemoryMap(memoryMap, count);
228 if (uefi) {
229 bootstrap.setUefi();
230 }
231 if (uefiAcpiRsdp) {
232 bootstrap.setAcpiRsdp(DirectMapBase + uefiAcpiRsdp);
233 }
234 if (hasInitrd) {
235 static const char name[] = "rootfs.img";
236 initrdModule = {DirectMapBase + initrdStart, DirectMapBase + initrdEnd,
237 reinterpret_cast<uintptr_t>(name), 0};
238 bootstrap.setModules(&initrdModule, 1);
239 }
240 if (bootargs) {
241 bootstrap.setCommandLine(uefi ? bootargs
242 : reinterpret_cast<const char*>(
243 DirectMapBase + reinterpret_cast<uintptr_t>(bootargs)));
244 }
245 _main(bootstrap);
246 while (true) {
247 asm volatile("wfe");
248 }
249}