2#include "pedigree/kernel/BootstrapInfo.h"
3#include "pedigree/kernel/processor/armv7/UefiHandoff.h"
8extern "C" char kernel_physical_start, kernel_physical_end;
10#ifndef ARMV7_BOOTSTRAP
11extern "C" void virtSetDeviceTree(
const void* tree);
15enum { FdtBeginNode = 1, FdtEndNode = 2, FdtProperty = 3, FdtNop = 4, FdtEnd = 9 };
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];
31bool matches(
const unsigned char* p,
size_t length,
const char* wanted) {
33 while (i < length && wanted[i] && p[i] == (
unsigned char)wanted[i]) {
36 return i < length && !p[i] && !wanted[i];
39bool hasCompatible(
const unsigned char* p,
size_t length) {
41 while (offset < length) {
43 while (end < length && p[end]) {
46 if (matches(p + offset, end - offset + (end < length),
"arm,pl011")) {
54bool readPhysical(
const unsigned char* p,
unsigned int cells, uint32_t& value) {
59 if (cells == 2 && readBe32(p) == 0) {
60 value = readBe32(p + 4);
66bool readHex(
const char*& text,
const char* end, uint32_t& value) {
69 if (end - text >= 2 && text[0] ==
'0' && (text[1] ==
'x' || text[1] ==
'X')) {
75 if (c >=
'0' && c <=
'9') {
77 }
else if (c >=
'a' && c <=
'f') {
79 }
else if (c >=
'A' && c <=
'F') {
84 if (value > (UINT32_MAX - digit) / 16) {
87 value = value * 16 + digit;
94bool initrdFromBootargs(
const char* bootargs, uint32_t& start, uint32_t& end) {
95 static const char prefix[] =
"pedigree.initrd=";
100 while (*bootargs ==
' ') {
103 const char* token = bootargs;
104 while (*bootargs && *bootargs !=
' ') {
107 const char* cursor = token;
108 if (
size_t(bootargs - token) <
sizeof(prefix) - 1) {
111 size_t prefixLength = 0;
112 while (prefixLength <
sizeof(prefix) - 1 && cursor[prefixLength] == prefix[prefixLength]) {
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;
124bool inspectTree(
const void* tree, EarlyPlatform& found) {
125 const auto* blob =
static_cast<const unsigned char*
>(tree);
126 if (readBe32(blob) != 0xd00dfeed) {
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) {
135 found.dtbSize = total;
140 unsigned int addressCells;
141 unsigned int sizeCells;
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);
152 if (token == FdtBeginNode) {
156 const unsigned char* name = p;
157 while (p < end && *p) {
163 auto& node = nodes[depth];
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;
172 uintptr_t next = (uintptr_t(p) + 4) & ~uintptr_t(3);
173 if (next > uintptr_t(end)) {
176 p =
reinterpret_cast<const unsigned char*
>(next);
177 }
else if (token == FdtEndNode) {
181 const auto& node = nodes[--depth];
182 if (node.serial && node.address) {
183 found.serial = node.address;
185 if (node.memory && node.address && node.size && !found.memorySize) {
186 found.memoryBase = node.address;
187 found.memorySize = node.size;
189 }
else if (token == FdtProperty) {
190 if (!depth || (
size_t)(end - p) < 8) {
193 uint32_t length = readBe32(p);
194 uint32_t nameOffset = readBe32(p + 4);
196 size_t remaining = (size_t)(end - p);
197 if (length > remaining || nameOffset >= total - strings) {
200 size_t padded = ((size_t)length + 3u) & ~(size_t)3;
201 if (padded > remaining) {
204 const unsigned char* name = blob + strings + nameOffset;
205 const unsigned char* value = p;
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);
235 }
else if (token == FdtEnd) {
236 return found.serial && found.memorySize;
237 }
else if (token != FdtNop) {
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);
248 while (*flags & (1u << 5)) {
250 *data =
static_cast<unsigned char>(*
message++);
254void writeHex(uintptr_t base, uint32_t value) {
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);
261 writeSerial(base, digits);
267 if (uefi->magic != ARMV7_UEFI_HANDOFF_MAGIC || uefi->fdt < 0x40000000U ||
268 uefi->fdt >= 0x80000000U) {
273 tree =
reinterpret_cast<const void*
>(uefi->fdt + 0x80000000U);
275 EarlyPlatform platform = {0, 0, 0, 0, 0, 0,
nullptr};
276 if (!inspectTree(tree, platform)) {
281 if (platform.serial >= 0x10000000) {
286 platform.serial += 0x80000000;
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);
294 if (!platform.initrdStart && !platform.initrdEnd) {
295 initrdFromBootargs(platform.bootargs, platform.initrdStart, platform.initrdEnd);
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");
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");
325 struct ReservedRegion {
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;
339 size_t memoryCount = 0;
340 uint32_t cursor = platform.memoryBase;
341 for (
const ReservedRegion& region : reserved) {
342 if (!region.end || region.start >= ramEnd) {
345 if (region.start > cursor) {
346 memoryMap[memoryCount++] = {
sizeof(memoryMap[0]), cursor, region.start - cursor, 1};
348 if (region.end > cursor) {
352 if (cursor < ramEnd) {
353 memoryMap[memoryCount++] = {
sizeof(memoryMap[0]), cursor, ramEnd - cursor, 1};
355 if (!memoryCount && !uefi) {
356 writeSerial(platform.serial,
"Pedigree ARMv7: no usable RAM\r\n");
363 if (uefi->memory_map < 0x40000000U || uefi->memory_map >= 0x80000000U ||
364 !uefi->memory_map_bytes || uefi->memory_map_bytes > 16 * PAGE_SIZE ||
366 writeSerial(platform.serial,
"Pedigree ARMv7: invalid UEFI memory map\r\n");
371 bootstrap.setMemoryMap(
376 bootstrap.setMemoryMap(memoryMap, memoryCount);
378 BootstrapStruct_t::Module
module;
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");
390 if (platform.bootargs) {
391 bootstrap.setCommandLine(platform.bootargs);
393#ifndef ARMV7_BOOTSTRAP
395 virtSetDeviceTree(tree);
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");
407 if (bootstrap.getCommandLine()) {
408 writeSerial(platform.serial,
"bootargs: ");
409 writeSerial(platform.serial, bootstrap.getCommandLine());
410 writeSerial(platform.serial,
"\r\n");