The Pedigree Project 0.1
BootMain.cc
1/* Early QEMU virt discovery runs before the machine layer is available. */
2#include "pedigree/kernel/BootstrapInfo.h"
3#include "pedigree/kernel/processor/armv7/UefiHandoff.h"
4
5#include <stddef.h>
6#include <stdint.h>
7
8extern "C" char kernel_physical_start, kernel_physical_end;
9extern "C" void _main(BootstrapStruct_t& bootstrap);
10#ifndef ARMV7_BOOTSTRAP
11extern "C" void virtSetDeviceTree(const void* tree);
12#endif
13
14namespace {
15enum { FdtBeginNode = 1, FdtEndNode = 2, FdtProperty = 3, FdtNop = 4, FdtEnd = 9 };
16
17struct EarlyPlatform {
18 uintptr_t serial;
19 uint32_t memoryBase;
20 uint32_t memorySize;
21 uint32_t dtbSize;
22 uint32_t initrdStart;
23 uint32_t initrdEnd;
24 const char* bootargs;
25};
26
27uint32_t readBe32(const unsigned char* p) {
28 return ((uint32_t)p[0] << 24) | ((uint32_t)p[1] << 16) | ((uint32_t)p[2] << 8) | p[3];
29}
30
31bool matches(const unsigned char* p, size_t length, const char* wanted) {
32 size_t i = 0;
33 while (i < length && wanted[i] && p[i] == (unsigned char)wanted[i]) {
34 ++i;
35 }
36 return i < length && !p[i] && !wanted[i];
37}
38
39bool hasCompatible(const unsigned char* p, size_t length) {
40 size_t offset = 0;
41 while (offset < length) {
42 size_t end = offset;
43 while (end < length && p[end]) {
44 ++end;
45 }
46 if (matches(p + offset, end - offset + (end < length), "arm,pl011")) {
47 return true;
48 }
49 offset = end + 1;
50 }
51 return false;
52}
53
54bool readPhysical(const unsigned char* p, unsigned int cells, uint32_t& value) {
55 if (cells == 1) {
56 value = readBe32(p);
57 return true;
58 }
59 if (cells == 2 && readBe32(p) == 0) {
60 value = readBe32(p + 4);
61 return true;
62 }
63 return false;
64}
65
66bool readHex(const char*& text, const char* end, uint32_t& value) {
67 value = 0;
68 bool found = false;
69 if (end - text >= 2 && text[0] == '0' && (text[1] == 'x' || text[1] == 'X')) {
70 text += 2;
71 }
72 while (text < end) {
73 const char c = *text;
74 uint32_t digit;
75 if (c >= '0' && c <= '9') {
76 digit = c - '0';
77 } else if (c >= 'a' && c <= 'f') {
78 digit = c - 'a' + 10;
79 } else if (c >= 'A' && c <= 'F') {
80 digit = c - 'A' + 10;
81 } else {
82 break;
83 }
84 if (value > (UINT32_MAX - digit) / 16) {
85 return false;
86 }
87 value = value * 16 + digit;
88 ++text;
89 found = true;
90 }
91 return found;
92}
93
94bool initrdFromBootargs(const char* bootargs, uint32_t& start, uint32_t& end) {
95 static const char prefix[] = "pedigree.initrd=";
96 if (!bootargs) {
97 return false;
98 }
99 while (*bootargs) {
100 while (*bootargs == ' ') {
101 ++bootargs;
102 }
103 const char* token = bootargs;
104 while (*bootargs && *bootargs != ' ') {
105 ++bootargs;
106 }
107 const char* cursor = token;
108 if (size_t(bootargs - token) < sizeof(prefix) - 1) {
109 continue;
110 }
111 size_t prefixLength = 0;
112 while (prefixLength < sizeof(prefix) - 1 && cursor[prefixLength] == prefix[prefixLength]) {
113 ++prefixLength;
114 }
115 if (prefixLength == sizeof(prefix) - 1) {
116 cursor += prefixLength;
117 return readHex(cursor, bootargs, start) && cursor < bootargs && *cursor++ == ':' &&
118 readHex(cursor, bootargs, end) && cursor == bootargs && start < end;
119 }
120 }
121 return false;
122}
123
124bool inspectTree(const void* tree, EarlyPlatform& found) {
125 const auto* blob = static_cast<const unsigned char*>(tree);
126 if (readBe32(blob) != 0xd00dfeed) {
127 return false;
128 }
129 const uint32_t total = readBe32(blob + 4);
130 const uint32_t structure = readBe32(blob + 8);
131 const uint32_t strings = readBe32(blob + 12);
132 if (total < 40 || total > 2 * 1024 * 1024 || structure >= total || strings >= total) {
133 return false;
134 }
135 found.dtbSize = total;
136
137 struct Node {
138 uint32_t address;
139 uint32_t size;
140 unsigned int addressCells;
141 unsigned int sizeCells;
142 bool serial;
143 bool memory;
144 bool chosen;
145 } nodes[16];
146 unsigned int depth = 0;
147 const unsigned char* p = blob + structure;
148 const unsigned char* end = blob + total;
149 while ((size_t)(end - p) >= 4) {
150 uint32_t token = readBe32(p);
151 p += 4;
152 if (token == FdtBeginNode) {
153 if (depth == 16) {
154 return false;
155 }
156 const unsigned char* name = p;
157 while (p < end && *p) {
158 ++p;
159 }
160 if (p == end) {
161 return false;
162 }
163 auto& node = nodes[depth];
164 node.address = 0;
165 node.size = 0;
166 node.serial = false;
167 node.memory = false;
168 node.chosen = matches(name, size_t(p - name) + 1, "chosen");
169 node.addressCells = depth ? nodes[depth - 1].addressCells : 2;
170 node.sizeCells = depth ? nodes[depth - 1].sizeCells : 2;
171 ++depth;
172 uintptr_t next = (uintptr_t(p) + 4) & ~uintptr_t(3);
173 if (next > uintptr_t(end)) {
174 return false;
175 }
176 p = reinterpret_cast<const unsigned char*>(next);
177 } else if (token == FdtEndNode) {
178 if (!depth) {
179 return false;
180 }
181 const auto& node = nodes[--depth];
182 if (node.serial && node.address) {
183 found.serial = node.address;
184 }
185 if (node.memory && node.address && node.size && !found.memorySize) {
186 found.memoryBase = node.address;
187 found.memorySize = node.size;
188 }
189 } else if (token == FdtProperty) {
190 if (!depth || (size_t)(end - p) < 8) {
191 return false;
192 }
193 uint32_t length = readBe32(p);
194 uint32_t nameOffset = readBe32(p + 4);
195 p += 8;
196 size_t remaining = (size_t)(end - p);
197 if (length > remaining || nameOffset >= total - strings) {
198 return false;
199 }
200 size_t padded = ((size_t)length + 3u) & ~(size_t)3;
201 if (padded > remaining) {
202 return false;
203 }
204 const unsigned char* name = blob + strings + nameOffset;
205 const unsigned char* value = p;
206 p += padded;
207 auto& node = nodes[depth - 1];
208 size_t nameLength = size_t(end - name);
209 if (matches(name, nameLength, "#address-cells") && length >= 4) {
210 node.addressCells = readBe32(value);
211 } else if (matches(name, nameLength, "#size-cells") && length >= 4) {
212 node.sizeCells = readBe32(value);
213 } else if (matches(name, nameLength, "compatible")) {
214 node.serial = hasCompatible(value, length);
215 } else if (matches(name, nameLength, "device_type")) {
216 node.memory = matches(value, length, "memory");
217 } else if (matches(name, nameLength, "bootargs") && node.chosen && length &&
218 value[length - 1] == 0) {
219 found.bootargs = reinterpret_cast<const char*>(value);
220 } else if (matches(name, nameLength, "linux,initrd-start") && node.chosen &&
221 (length == 4 || length == 8)) {
222 readPhysical(value, length / 4, found.initrdStart);
223 } else if (matches(name, nameLength, "linux,initrd-end") && node.chosen &&
224 (length == 4 || length == 8)) {
225 readPhysical(value, length / 4, found.initrdEnd);
226 } else if (matches(name, nameLength, "reg") && depth > 1) {
227 unsigned int addressCells = nodes[depth - 2].addressCells;
228 unsigned int sizeCells = nodes[depth - 2].sizeCells;
229 if (addressCells >= 1 && addressCells <= 2 && sizeCells >= 1 && sizeCells <= 2 &&
230 length >= (addressCells + sizeCells) * 4) {
231 readPhysical(value, addressCells, node.address);
232 readPhysical(value + addressCells * 4, sizeCells, node.size);
233 }
234 }
235 } else if (token == FdtEnd) {
236 return found.serial && found.memorySize;
237 } else if (token != FdtNop) {
238 return false;
239 }
240 }
241 return false;
242}
243
244void writeSerial(uintptr_t base, const char* message) {
245 auto* data = reinterpret_cast<volatile uint32_t*>(base);
246 auto* flags = reinterpret_cast<volatile uint32_t*>(base + 0x18);
247 while (*message) {
248 while (*flags & (1u << 5)) {
249 }
250 *data = static_cast<unsigned char>(*message++);
251 }
252}
253
254void writeHex(uintptr_t base, uint32_t value) {
255 char digits[9];
256 for (unsigned int i = 0; i < 8; ++i) {
257 const uint32_t nibble = (value >> ((7 - i) * 4)) & 15;
258 digits[i] = nibble < 10 ? char('0' + nibble) : char('a' + nibble - 10);
259 }
260 digits[8] = 0;
261 writeSerial(base, digits);
262}
263} // namespace
264
265extern "C" void armv7BootMain(const void* tree, const armv7_uefi_handoff_t* uefi) {
266 if (uefi) {
267 if (uefi->magic != ARMV7_UEFI_HANDOFF_MAGIC || uefi->fdt < 0x40000000U ||
268 uefi->fdt >= 0x80000000U) {
269 for (;;) {
270 asm volatile("wfi");
271 }
272 }
273 tree = reinterpret_cast<const void*>(uefi->fdt + 0x80000000U);
274 }
275 EarlyPlatform platform = {0, 0, 0, 0, 0, 0, nullptr};
276 if (!inspectTree(tree, platform)) {
277 for (;;) {
278 asm volatile("wfi");
279 }
280 }
281 if (platform.serial >= 0x10000000) {
282 for (;;) {
283 asm volatile("wfi");
284 }
285 }
286 platform.serial += 0x80000000;
287 if (uefi) {
288 platform.initrdStart = uefi->initrd_start;
289 platform.initrdEnd = uefi->initrd_end;
290 if (uefi->command_line >= 0x40000000U && uefi->command_line < 0x80000000U) {
291 platform.bootargs = reinterpret_cast<const char*>(uefi->command_line + 0x80000000U);
292 }
293 }
294 if (!platform.initrdStart && !platform.initrdEnd) {
295 initrdFromBootargs(platform.bootargs, platform.initrdStart, platform.initrdEnd);
296 }
297
298 const uint64_t declaredEnd = uint64_t(platform.memoryBase) + platform.memorySize;
299 const uint32_t ramEnd = declaredEnd > 0x80000000ULL ? 0x80000000U : uint32_t(declaredEnd);
300 const uint32_t dtbStart = uefi ? uefi->fdt : 0x40000000U;
301 const uint32_t dtbEnd = (dtbStart + platform.dtbSize + 4095) & ~uint32_t(4095);
302 const uint32_t kernelStart = uintptr_t(&kernel_physical_start);
303 const uint32_t kernelEnd = (uintptr_t(&kernel_physical_end) + 4095) & ~uint32_t(4095);
304 if (platform.memoryBase != 0x40000000 || kernelEnd > ramEnd || dtbEnd < dtbStart ||
305 (kernelStart < dtbEnd && kernelEnd > dtbStart)) {
306 writeSerial(platform.serial, "Pedigree ARMv7: invalid memory map\r\n");
307 for (;;) {
308 asm volatile("wfi");
309 }
310 }
311
312 const bool hasInitrd = platform.initrdStart && platform.initrdEnd > platform.initrdStart;
313 const uint32_t initrdStart = platform.initrdStart & ~uint32_t(PAGE_SIZE - 1);
314 const uint32_t initrdEnd = (platform.initrdEnd + PAGE_SIZE - 1) & ~uint32_t(PAGE_SIZE - 1);
315 if (hasInitrd && (platform.initrdStart < platform.memoryBase || platform.initrdEnd >= ramEnd ||
316 platform.initrdEnd > UINT32_MAX - (PAGE_SIZE - 1) ||
317 (initrdStart < dtbEnd && initrdEnd > dtbStart) ||
318 (initrdStart < kernelEnd && initrdEnd > kernelStart))) {
319 writeSerial(platform.serial, "Pedigree ARMv7: invalid initrd region\r\n");
320 for (;;) {
321 asm volatile("wfi");
322 }
323 }
324
325 struct ReservedRegion {
326 uint32_t start, end;
327 } reserved[3] = {{dtbStart, dtbEnd},
328 {kernelStart, kernelEnd},
329 {hasInitrd ? initrdStart : 0, hasInitrd ? initrdEnd : 0}};
330 for (size_t i = 1; i < 3; ++i) {
331 for (size_t j = i; j && reserved[j].start < reserved[j - 1].start; --j) {
332 ReservedRegion previous = reserved[j - 1];
333 reserved[j - 1] = reserved[j];
334 reserved[j] = previous;
335 }
336 }
337
339 size_t memoryCount = 0;
340 uint32_t cursor = platform.memoryBase;
341 for (const ReservedRegion& region : reserved) {
342 if (!region.end || region.start >= ramEnd) {
343 continue;
344 }
345 if (region.start > cursor) {
346 memoryMap[memoryCount++] = {sizeof(memoryMap[0]), cursor, region.start - cursor, 1};
347 }
348 if (region.end > cursor) {
349 cursor = region.end;
350 }
351 }
352 if (cursor < ramEnd) {
353 memoryMap[memoryCount++] = {sizeof(memoryMap[0]), cursor, ramEnd - cursor, 1};
354 }
355 if (!memoryCount && !uefi) {
356 writeSerial(platform.serial, "Pedigree ARMv7: no usable RAM\r\n");
357 for (;;) {
358 asm volatile("wfi");
359 }
360 }
361 BootstrapStruct_t bootstrap;
362 if (uefi) {
363 if (uefi->memory_map < 0x40000000U || uefi->memory_map >= 0x80000000U ||
364 !uefi->memory_map_bytes || uefi->memory_map_bytes > 16 * PAGE_SIZE ||
365 uefi->memory_map_bytes % sizeof(BootstrapStruct_t::MemoryMapEntry)) {
366 writeSerial(platform.serial, "Pedigree ARMv7: invalid UEFI memory map\r\n");
367 for (;;) {
368 asm volatile("wfi");
369 }
370 }
371 bootstrap.setMemoryMap(
372 reinterpret_cast<const BootstrapStruct_t::MemoryMapEntry*>(uefi->memory_map + 0x80000000U),
373 uefi->memory_map_bytes / sizeof(BootstrapStruct_t::MemoryMapEntry));
374 bootstrap.setUefi();
375 } else {
376 bootstrap.setMemoryMap(memoryMap, memoryCount);
377 }
378 BootstrapStruct_t::Module module;
379 if (hasInitrd) {
380 static const char name[] = "rootfs.img";
381 module = {uint64_t(platform.initrdStart) + 0x80000000ULL,
382 uint64_t(platform.initrdEnd) + 0x80000000ULL, reinterpret_cast<uintptr_t>(name), 0};
383 bootstrap.setModules(&module, 1);
384 writeSerial(platform.serial, "initrd 0x");
385 writeHex(platform.serial, platform.initrdStart);
386 writeSerial(platform.serial, " + 0x");
387 writeHex(platform.serial, bootstrap.getInitrdSize());
388 writeSerial(platform.serial, "\r\n");
389 }
390 if (platform.bootargs) {
391 bootstrap.setCommandLine(platform.bootargs);
392 }
393#ifndef ARMV7_BOOTSTRAP
394 if (uefi) {
395 virtSetDeviceTree(tree);
396 }
397#endif
398
399 writeSerial(platform.serial, "Pedigree ARMv7 virt bootstrap: MMU and FDT serial ready\r\n");
400 for (void* entry = bootstrap.getMemoryMap(); entry; entry = bootstrap.nextMemoryMapEntry(entry)) {
401 writeSerial(platform.serial, "usable RAM 0x");
402 writeHex(platform.serial, uint32_t(bootstrap.getMemoryMapEntryAddress(entry)));
403 writeSerial(platform.serial, " + 0x");
404 writeHex(platform.serial, uint32_t(bootstrap.getMemoryMapEntryLength(entry)));
405 writeSerial(platform.serial, "\r\n");
406 }
407 if (bootstrap.getCommandLine()) {
408 writeSerial(platform.serial, "bootargs: ");
409 writeSerial(platform.serial, bootstrap.getCommandLine());
410 writeSerial(platform.serial, "\r\n");
411 }
412 _main(bootstrap);
413 for (;;) {
414 asm volatile("wfi");
415 }
416}