13constexpr uint32_t FdtMagic = 0xd00dfeed;
14constexpr uint32_t BeginNode = 1;
15constexpr uint32_t EndNode = 2;
16constexpr uint32_t Property = 3;
17constexpr uint32_t Nop = 4;
18constexpr uint32_t End = 9;
19constexpr size_t MaxDtbSize = 2 * 1024 * 1024;
20constexpr size_t MaxDepth = 16;
21constexpr size_t MaxMemoryRegions = 8;
22constexpr size_t MaxVirtioMmio = 32;
23constexpr size_t MaxPciWindows = 8;
25constexpr uintptr_t DirectMapBase = 0x80000000;
27constexpr uintptr_t DirectMapBase = 0xffff000000000000ULL;
34 size_t virtioMmioCount;
37 size_t pciWindowCount;
38 const uint8_t* pciInterruptMap;
39 size_t pciInterruptMapSize;
40 const uint8_t* pciMsiMap;
42 uint32_t pciMsiMapMask;
43 uint32_t pciInterruptMask[4];
44 uint32_t acpiPciIrq[32][4];
46 uint32_t gicAddressCells;
51 uintptr_t gicDistributor;
53 uintptr_t gicRedistributor;
56 uint32_t physicalTimerIrq;
57 uint32_t virtualTimerIrq;
70 uint32_t addressCells;
74 const uint8_t* interrupts;
75 size_t interruptsSize;
76 const uint8_t* ranges;
78 const uint8_t* interruptMap;
79 size_t interruptMapSize;
80 const uint8_t* interruptMapMask;
81 size_t interruptMapMaskSize;
82 const uint8_t* msiMap;
102 uint64_t initrdStart;
104 const char* bootargs;
107PlatformInfo g_Platform;
109uint32_t read32(
const uint8_t* p) {
110 return (uint32_t(p[0]) << 24) | (uint32_t(p[1]) << 16) | (uint32_t(p[2]) << 8) | uint32_t(p[3]);
113bool equal(
const char* a,
const char* b) {
114 while (*a && *b && *a == *b) {
121bool hasString(
const uint8_t* data,
size_t size,
const char* wanted) {
123 while (offset < size) {
125 while (offset + length < size && data[offset + length]) {
128 if (offset + length == size) {
131 if (equal(
reinterpret_cast<const char*
>(data + offset), wanted)) {
134 offset += length + 1;
139bool readCells(
const uint8_t* data,
size_t size, uint32_t count, uint64_t& value) {
140 if (!count || count > 2 || size < count * 4) {
143 value = count == 2 ? read32(data) : 0;
144 value = (value << 32) | read32(data + (count - 1) * 4);
148bool firstReg(
const Node& node,
const Node& parent, uint64_t& base, uint64_t& size) {
149 const uint32_t addressBytes = parent.addressCells * 4;
150 const uint32_t sizeBytes = parent.sizeCells * 4;
151 if (!node.reg || node.regSize < addressBytes + sizeBytes ||
152 !readCells(node.reg, node.regSize, parent.addressCells, base) ||
153 !readCells(node.reg + addressBytes, node.regSize - addressBytes, parent.sizeCells, size)) {
156 return size && base <= UINT64_MAX - size;
159uint32_t gicIrq(
const uint8_t* data,
size_t size) {
160 if (!data || size < 12) {
163 const uint32_t type = read32(data);
164 const uint32_t number = read32(data + 4);
165 if (type == 0 && number < 988) {
168 if (type == 1 && number < 16) {
174void finishNode(
const Node& node,
const Node& parent, PlatformInfo& out) {
177 if (node.memory && node.reg && parent.addressCells && parent.sizeCells &&
178 parent.addressCells <= 2 && parent.sizeCells <= 2) {
179 const size_t stride = (parent.addressCells + parent.sizeCells) * 4;
180 for (
size_t offset = 0; offset + stride <= node.regSize && out.memoryCount < MaxMemoryRegions;
183 entry.reg = node.reg + offset;
184 entry.regSize = stride;
185 if (firstReg(entry, parent, base, size)) {
186 out.memory[out.memoryCount++] = {base, size};
191 if (node.uart && firstReg(node, parent, base, size)) {
192 out.uart =
static_cast<uintptr_t
>(base);
193 out.uartIrq = gicIrq(node.interrupts, node.interruptsSize);
195 if (node.rtc && firstReg(node, parent, base, size)) {
196 out.rtc =
static_cast<uintptr_t
>(base);
199 if (node.gic && firstReg(node, parent, base, size)) {
200 out.gicDistributor =
static_cast<uintptr_t
>(base);
201 out.gicVersion = node.gicV3 ? 3 : 2;
202 out.gicPhandle = node.phandle;
203 out.gicAddressCells = node.addressCells;
204 const size_t stride = (parent.addressCells + parent.sizeCells) * 4;
205 if (node.regSize >= stride * 2) {
208 cpu.regSize -= stride;
209 if (firstReg(cpu, parent, base, size)) {
211 out.gicRedistributor =
static_cast<uintptr_t
>(base);
213 out.gicCpu =
static_cast<uintptr_t
>(base);
219 if ((node.gicV2m || node.gicIts) && node.phandle &&
220 out.msiController.type == VirtMsiController::Type::None &&
221 firstReg(node, parent, base, size)) {
222 out.msiController = {
223 node.gicV2m ? VirtMsiController::Type::GicV2m : VirtMsiController::Type::GicV3Its, base,
225 out.msiPhandle = node.phandle;
228 if (node.timer && node.interruptsSize >= 36) {
229 out.physicalTimerIrq = gicIrq(node.interrupts + 12, node.interruptsSize - 12);
230 out.virtualTimerIrq = gicIrq(node.interrupts + 24, node.interruptsSize - 24);
232 if (node.virtioMmio && out.virtioMmioCount < MaxVirtioMmio &&
233 firstReg(node, parent, base, size)) {
234 out.virtioMmio[out.virtioMmioCount++] = {base, size,
235 gicIrq(node.interrupts, node.interruptsSize)};
237 if (node.pciHost && !out.pciHost.size && firstReg(node, parent, base, size) &&
238 node.addressCells == 3 && node.sizeCells == 2) {
239 const uint64_t buses = size >> 20;
240 const uint32_t firstBus = node.busRange ? node.firstBus : 0;
241 const uint32_t lastBus = node.busRange ? node.lastBus : 255;
242 if (!(base & 0xfffff) && !(size & 0xfffff) && buses && firstBus <= lastBus && lastBus < 256 &&
243 uint64_t(lastBus - firstBus) < buses) {
244 out.pciHost = {base, size, firstBus, lastBus};
245 if (node.interruptMap && node.interruptMapMask && node.interruptMapMaskSize == 16 &&
246 !(node.interruptMapSize % 4)) {
247 out.pciInterruptMap = node.interruptMap;
248 out.pciInterruptMapSize = node.interruptMapSize;
249 for (
size_t i = 0; i < 4; ++i) {
250 out.pciInterruptMask[i] = read32(node.interruptMapMask + i * 4);
253 if (node.msiMap && node.msiMapSize && !(node.msiMapSize % 16)) {
254 out.pciMsiMap = node.msiMap;
255 out.pciMsiMapSize = node.msiMapSize;
256 out.pciMsiMapMask = node.msiMapMask;
258 if (node.ranges && parent.addressCells <= 2) {
259 const size_t stride = (node.addressCells + parent.addressCells + node.sizeCells) * 4;
260 for (
size_t offset = 0;
261 offset + stride <= node.rangesSize && out.pciWindowCount < MaxPciWindows;
263 const uint8_t* entry = node.ranges + offset;
264 const uint32_t space = read32(entry) & 0x03000000;
265 const uint64_t pciBase = (uint64_t(read32(entry + 4)) << 32) | read32(entry + 8);
266 uint64_t cpuBase = 0;
268 if ((space == 0x01000000 || space == 0x02000000 || space == 0x03000000) &&
269 readCells(entry + 12, stride - 12, parent.addressCells, cpuBase) &&
270 readCells(entry + 12 + parent.addressCells * 4, stride - 12 - parent.addressCells * 4,
271 node.sizeCells, length) &&
272 length && pciBase <= UINT64_MAX - length && cpuBase <= UINT64_MAX - length) {
273 out.pciWindows[out.pciWindowCount++] = {space, pciBase, cpuBase, length,
274 bool(read32(entry) & 0x40000000U)};
281 out.psciAvailable =
true;
282 out.psciHvc = node.psciHvc;
285 out.initrdStart = node.initrdStart;
286 out.initrdEnd = node.initrdEnd;
287 out.bootargs = node.bootargs;
291bool parse(
const uint8_t* dtb, PlatformInfo& out) {
292 if (!dtb || read32(dtb) != FdtMagic) {
296 const size_t total = read32(dtb + 4);
297 const size_t structOffset = read32(dtb + 8);
298 const size_t stringsOffset = read32(dtb + 12);
299 const size_t stringsSize = read32(dtb + 32);
300 const size_t structSize = read32(dtb + 36);
301 if (total < 40 || total > MaxDtbSize || structOffset > total ||
302 structSize > total - structOffset || stringsOffset > total ||
303 stringsSize > total - stringsOffset) {
307 const uint8_t* structure = dtb + structOffset;
308 const uint8_t* strings = dtb + stringsOffset;
310 Node stack[MaxDepth] = {};
313 while (cursor + 4 <= structSize) {
314 const uint32_t token = read32(structure + cursor);
316 if (token == BeginNode) {
317 if (depth == MaxDepth) {
320 const size_t start = cursor;
321 while (cursor < structSize && structure[cursor]) {
324 if (cursor == structSize) {
327 const size_t nameLength = cursor - start;
328 cursor = (cursor + 4) & ~
size_t(3);
329 if (cursor > structSize) {
332 Node& node = stack[depth];
334 node.addressCells = depth ? stack[depth - 1].addressCells : 2;
335 node.sizeCells = depth ? stack[depth - 1].sizeCells : 1;
336 node.msiMapMask = 0xffff;
337 node.memory = depth == 1 && nameLength >= 6 && structure[start] ==
'm' &&
338 structure[start + 1] ==
'e' && structure[start + 2] ==
'm' &&
339 structure[start + 3] ==
'o' && structure[start + 4] ==
'r' &&
340 structure[start + 5] ==
'y';
341 node.chosen = depth == 1 && equal(
reinterpret_cast<const char*
>(structure + start),
"chosen");
343 }
else if (token == Property) {
344 if (!depth || cursor + 8 > structSize) {
347 const size_t length = read32(structure + cursor);
348 const size_t nameOffset = read32(structure + cursor + 4);
350 if (nameOffset >= stringsSize || length > structSize - cursor) {
353 size_t nameEnd = nameOffset;
354 while (nameEnd < stringsSize && strings[nameEnd]) {
357 if (nameEnd == stringsSize) {
360 const char* name =
reinterpret_cast<const char*
>(strings + nameOffset);
361 const uint8_t* data = structure + cursor;
362 Node& node = stack[depth - 1];
363 if (equal(name,
"#address-cells") && length == 4) {
364 node.addressCells = read32(data);
365 }
else if (equal(name,
"#size-cells") && length == 4) {
366 node.sizeCells = read32(data);
367 }
else if (equal(name,
"reg")) {
369 node.regSize = length;
370 }
else if (equal(name,
"interrupts")) {
371 node.interrupts = data;
372 node.interruptsSize = length;
373 }
else if (equal(name,
"ranges")) {
375 node.rangesSize = length;
376 }
else if (equal(name,
"interrupt-map")) {
377 node.interruptMap = data;
378 node.interruptMapSize = length;
379 }
else if (equal(name,
"interrupt-map-mask")) {
380 node.interruptMapMask = data;
381 node.interruptMapMaskSize = length;
382 }
else if (equal(name,
"msi-map")) {
384 node.msiMapSize = length;
385 }
else if (equal(name,
"msi-map-mask") && length == 4) {
386 node.msiMapMask = read32(data);
387 }
else if (equal(name,
"bus-range") && length == 8) {
388 node.firstBus = read32(data);
389 node.lastBus = read32(data + 4);
390 node.busRange =
true;
391 }
else if (equal(name,
"phandle") && length == 4) {
392 node.phandle = read32(data);
393 }
else if (equal(name,
"device_type") && hasString(data, length,
"memory")) {
395 }
else if (equal(name,
"compatible")) {
396 node.uart = hasString(data, length,
"arm,pl011");
397 node.rtc = hasString(data, length,
"arm,pl031");
398 node.gic = hasString(data, length,
"arm,cortex-a15-gic") ||
399 hasString(data, length,
"arm,gic-400") || hasString(data, length,
"arm,gic-v3");
400 node.gicV3 = hasString(data, length,
"arm,gic-v3");
401 node.gicV2m = hasString(data, length,
"arm,gic-v2m-frame");
402 node.gicIts = hasString(data, length,
"arm,gic-v3-its");
403 node.timer = hasString(data, length,
"arm,armv8-timer") ||
404 hasString(data, length,
"arm,armv7-timer");
406 hasString(data, length,
"arm,psci-1.0") || hasString(data, length,
"arm,psci-0.2");
407 node.virtioMmio = hasString(data, length,
"virtio,mmio");
408 node.pciHost = hasString(data, length,
"pci-host-ecam-generic");
409 }
else if (equal(name,
"method")) {
410 node.psciHvc = hasString(data, length,
"hvc");
411 }
else if (node.chosen && equal(name,
"linux,initrd-start")) {
412 if (length == 4 || length == 8) {
413 readCells(data, length, length / 4, node.initrdStart);
415 }
else if (node.chosen && equal(name,
"linux,initrd-end")) {
416 if (length == 4 || length == 8) {
417 readCells(data, length, length / 4, node.initrdEnd);
419 }
else if (node.chosen && equal(name,
"bootargs") && length && data[length - 1] == 0) {
420 node.bootargs =
reinterpret_cast<const char*
>(data);
422 cursor = (cursor + length + 3) & ~
size_t(3);
423 if (cursor > structSize) {
426 }
else if (token == EndNode) {
431 finishNode(stack[depth - 1], stack[depth - 2], out);
434 }
else if (token == End) {
435 return depth == 0 && out.memoryCount && out.uart && out.gicDistributor &&
436 ((out.gicVersion == 2 && out.gicCpu) ||
437 (out.gicVersion == 3 && out.gicRedistributor)) &&
438 out.physicalTimerIrq && out.virtualTimerIrq;
439 }
else if (token != Nop) {
447bool VirtDeviceTree::initialise(
const void* dtb) {
448 PlatformInfo parsed = {};
449 if (!parse(
static_cast<const uint8_t*
>(dtb), parsed)) {
453 parsed.blobSize = read32(
static_cast<const uint8_t*
>(dtb) + 4);
459bool VirtDeviceTree::initialiseAcpi(uint64_t rsdpPhysical,
461 size_t memoryMapCount) {
463 if (!virtParseAcpi(rsdpPhysical, memoryMap, memoryMapCount, acpi)) {
467 PlatformInfo platform = {};
468 platform.pciHost = acpi.pciHost;
469 platform.pciWindowCount = acpi.pciWindowCount;
470 for (
size_t i = 0; i < acpi.pciWindowCount; ++i) {
471 platform.pciWindows[i] = acpi.pciWindows[i];
473 for (
size_t slot = 0; slot < 32; ++slot) {
474 for (
size_t pin = 0; pin < 4; ++pin) {
475 platform.acpiPciIrq[slot][pin] = acpi.pciIrq[slot][pin];
478 platform.uart = acpi.uart;
479 platform.uartIrq = acpi.uartIrq;
480 platform.rtc = acpi.rtc;
481 platform.gicDistributor = acpi.gicDistributor;
482 platform.gicCpu = acpi.gicCpu;
483 platform.gicRedistributor = acpi.gicRedistributor;
484 platform.gicVersion = acpi.gicVersion;
485 platform.msiController = acpi.msiController;
486 platform.pciMsiIdentity = acpi.pciMsiIdentity;
487 platform.physicalTimerIrq = acpi.physicalTimerIrq;
488 platform.virtualTimerIrq = acpi.virtualTimerIrq;
489 platform.psciAvailable = acpi.psciAvailable;
490 platform.psciHvc = acpi.psciHvc;
491 platform.acpi =
true;
492 platform.valid =
true;
493 g_Platform = platform;
497bool VirtDeviceTree::valid() {
498 return g_Platform.valid;
502 if (!g_Platform.valid || index >= g_Platform.memoryCount) {
505 region = g_Platform.memory[index];
509bool VirtDeviceTree::virtioMmio(
size_t index,
VirtMmioDevice& device) {
510 if (!g_Platform.valid || index >= g_Platform.virtioMmioCount) {
513 device = g_Platform.virtioMmio[index];
518 if (!g_Platform.valid || !g_Platform.pciHost.size) {
521 host = g_Platform.pciHost;
525bool VirtDeviceTree::pciWindow(
size_t index,
VirtPciWindow& window) {
526 if (!g_Platform.valid || index >= g_Platform.pciWindowCount) {
529 window = g_Platform.pciWindows[index];
533bool VirtDeviceTree::pciTranslate(uint64_t address, uint64_t size,
bool io, uint64_t& physical) {
534 if (!g_Platform.valid || !size) {
537 for (
size_t i = 0; i < g_Platform.pciWindowCount; ++i) {
539 if ((window.space == 0x01000000) != io || address < window.pciBase ||
540 address - window.pciBase >= window.size ||
541 size > window.size - (address - window.pciBase)) {
544 physical = window.cpuBase + (address - window.pciBase);
550uint32_t VirtDeviceTree::pciInterrupt(uint8_t bus, uint8_t device, uint8_t function, uint8_t pin) {
551 if (g_Platform.acpi) {
552 return bus == g_Platform.pciHost.firstBus && device < 32 && function < 8 && pin >= 1 && pin <= 4
553 ? g_Platform.acpiPciIrq[device][pin - 1]
556 if (!g_Platform.valid || !g_Platform.gicPhandle || !g_Platform.pciInterruptMap ||
557 g_Platform.gicAddressCells > 2 || device >= 32 || function >= 8 || pin < 1 || pin > 4) {
560 const size_t stride = (8 + g_Platform.gicAddressCells) * 4;
561 if (g_Platform.pciInterruptMapSize % stride) {
564 const uint32_t address =
565 (uint32_t(bus) << 16) | (uint32_t(device) << 11) | (uint32_t(function) << 8);
566 for (
size_t offset = 0; offset < g_Platform.pciInterruptMapSize; offset += stride) {
567 const uint8_t* entry = g_Platform.pciInterruptMap + offset;
568 if ((read32(entry) & g_Platform.pciInterruptMask[0]) !=
569 (address & g_Platform.pciInterruptMask[0]) ||
570 (read32(entry + 4) & g_Platform.pciInterruptMask[1]) ||
571 (read32(entry + 8) & g_Platform.pciInterruptMask[2]) ||
572 (read32(entry + 12) & g_Platform.pciInterruptMask[3]) !=
573 (pin & g_Platform.pciInterruptMask[3]) ||
574 read32(entry + 16) != g_Platform.gicPhandle) {
577 return gicIrq(entry + 20 + g_Platform.gicAddressCells * 4, 12);
583 if (!g_Platform.valid || !g_Platform.pciHost.size || !g_Platform.msiController.base ||
584 (g_Platform.gicVersion == 2 &&
585 g_Platform.msiController.type != VirtMsiController::Type::GicV2m) ||
586 (g_Platform.gicVersion == 3 &&
587 g_Platform.msiController.type != VirtMsiController::Type::GicV2m &&
588 g_Platform.msiController.type != VirtMsiController::Type::GicV3Its) ||
589 (g_Platform.acpi && g_Platform.msiController.type == VirtMsiController::Type::GicV3Its &&
590 !g_Platform.pciMsiIdentity)) {
593 if (!g_Platform.acpi && (!g_Platform.pciMsiMap || !g_Platform.msiPhandle)) {
596 controller = g_Platform.msiController;
600bool VirtDeviceTree::pciMsiDeviceId(uint8_t bus, uint8_t device, uint8_t function, uint32_t&
id) {
601 if (!g_Platform.valid || bus < g_Platform.pciHost.firstBus || bus > g_Platform.pciHost.lastBus ||
602 device >= 32 || function >= 8) {
605 const uint32_t rid = (uint32_t(bus) << 8) | (uint32_t(device) << 3) | function;
606 if (g_Platform.acpi) {
607 if (g_Platform.msiController.type == VirtMsiController::Type::GicV3Its &&
608 !g_Platform.pciMsiIdentity) {
614 if (!g_Platform.pciMsiMap || !g_Platform.msiPhandle) {
617 const uint32_t masked = rid & g_Platform.pciMsiMapMask;
618 for (
size_t offset = 0; offset < g_Platform.pciMsiMapSize; offset += 16) {
619 const uint8_t* entry = g_Platform.pciMsiMap + offset;
620 const uint32_t start = read32(entry);
621 const uint32_t phandle = read32(entry + 4);
622 const uint32_t base = read32(entry + 8);
623 const uint32_t count = read32(entry + 12);
624 if (phandle != g_Platform.msiPhandle || !count || masked < start || masked - start >= count ||
625 base > UINT32_MAX - (masked - start)) {
628 id = base + (masked - start);
635 return g_Platform.uart ? DirectMapBase + g_Platform.uart : 0;
638uint32_t VirtDeviceTree::uartIrq() {
639 return g_Platform.uartIrq;
642uintptr_t VirtDeviceTree::rtcBase() {
643 return g_Platform.rtc ? DirectMapBase + g_Platform.rtc : 0;
646uint32_t VirtDeviceTree::gicVersion() {
647 return g_Platform.gicVersion;
650uintptr_t VirtDeviceTree::gicDistributorBase() {
651 return g_Platform.gicDistributor ? DirectMapBase + g_Platform.gicDistributor : 0;
654uintptr_t VirtDeviceTree::gicCpuBase() {
655 return g_Platform.gicCpu ? DirectMapBase + g_Platform.gicCpu : 0;
658uintptr_t VirtDeviceTree::gicRedistributorBase() {
659 return g_Platform.gicRedistributor ? DirectMapBase + g_Platform.gicRedistributor : 0;
662uint64_t VirtDeviceTree::gicRedistributorPhysical() {
663 return g_Platform.gicRedistributor;
666uint32_t VirtDeviceTree::physicalTimerIrq() {
667 return g_Platform.physicalTimerIrq;
670uint32_t VirtDeviceTree::virtualTimerIrq() {
671 return g_Platform.virtualTimerIrq;
674bool VirtDeviceTree::psciUsesHvc() {
675 return g_Platform.psciHvc;
678bool VirtDeviceTree::psciAvailable() {
679 return g_Platform.psciAvailable;
682bool VirtDeviceTree::initrd(uint64_t& start, uint64_t& end) {
683 if (!g_Platform.valid || !g_Platform.initrdStart ||
684 g_Platform.initrdEnd <= g_Platform.initrdStart) {
687 start = g_Platform.initrdStart;
688 end = g_Platform.initrdEnd;
692const char* VirtDeviceTree::bootargs() {
693 return g_Platform.valid ? g_Platform.bootargs :
nullptr;
696size_t VirtDeviceTree::blobSize() {
697 return g_Platform.valid ? g_Platform.blobSize : 0;
700extern "C" void virtSetDeviceTree(
const void* dtb) {
701 VirtDeviceTree::initialise(dtb);
704extern "C" bool virtGetMemoryRegion(
size_t index, uint64_t* base, uint64_t* size) {
706 if (!base || !size || !VirtDeviceTree::memoryRegion(index, region)) {
714extern "C" bool virtGetVirtioMmio(
size_t index, uint64_t* base, uint64_t* size, uint32_t* irq) {
716 if (!base || !size || !irq || !VirtDeviceTree::virtioMmio(index, device)) {
725extern "C" bool virtGetInitrd(uint64_t* start, uint64_t* end) {
726 return start && end && VirtDeviceTree::initrd(*start, *end);
729extern "C" bool virtGetBootargs(
const char** text) {
733 *text = VirtDeviceTree::bootargs();
734 return *text !=
nullptr;
static uintptr_t uartBase()