The Pedigree Project 0.1
DeviceTree.cc
1/*
2 * Copyright (c) 2026, Pedigree Developers
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted.
6 */
7
8#include "DeviceTree.h"
9
10#include "Acpi.h"
11
12namespace {
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;
24#if ARMV7
25constexpr uintptr_t DirectMapBase = 0x80000000;
26#else
27constexpr uintptr_t DirectMapBase = 0xffff000000000000ULL;
28#endif
29
30struct PlatformInfo {
31 VirtMemoryRegion memory[MaxMemoryRegions];
32 size_t memoryCount;
33 VirtMmioDevice virtioMmio[MaxVirtioMmio];
34 size_t virtioMmioCount;
35 VirtPciHost pciHost;
36 VirtPciWindow pciWindows[MaxPciWindows];
37 size_t pciWindowCount;
38 const uint8_t* pciInterruptMap;
39 size_t pciInterruptMapSize;
40 const uint8_t* pciMsiMap;
41 size_t pciMsiMapSize;
42 uint32_t pciMsiMapMask;
43 uint32_t pciInterruptMask[4];
44 uint32_t acpiPciIrq[32][4];
45 uint32_t gicPhandle;
46 uint32_t gicAddressCells;
47 uint32_t msiPhandle;
48 VirtMsiController msiController;
49 uintptr_t uart;
50 uintptr_t rtc;
51 uintptr_t gicDistributor;
52 uintptr_t gicCpu;
53 uintptr_t gicRedistributor;
54 uint32_t gicVersion;
55 uint32_t uartIrq;
56 uint32_t physicalTimerIrq;
57 uint32_t virtualTimerIrq;
58 uint64_t initrdStart;
59 uint64_t initrdEnd;
60 const char* bootargs;
61 size_t blobSize;
62 bool psciAvailable;
63 bool psciHvc;
64 bool acpi;
65 bool pciMsiIdentity;
66 bool valid;
67};
68
69struct Node {
70 uint32_t addressCells;
71 uint32_t sizeCells;
72 const uint8_t* reg;
73 size_t regSize;
74 const uint8_t* interrupts;
75 size_t interruptsSize;
76 const uint8_t* ranges;
77 size_t rangesSize;
78 const uint8_t* interruptMap;
79 size_t interruptMapSize;
80 const uint8_t* interruptMapMask;
81 size_t interruptMapMaskSize;
82 const uint8_t* msiMap;
83 size_t msiMapSize;
84 uint32_t msiMapMask;
85 uint32_t firstBus;
86 uint32_t lastBus;
87 uint32_t phandle;
88 bool memory;
89 bool uart;
90 bool rtc;
91 bool gic;
92 bool gicV3;
93 bool gicV2m;
94 bool gicIts;
95 bool timer;
96 bool psci;
97 bool psciHvc;
98 bool virtioMmio;
99 bool chosen;
100 bool pciHost;
101 bool busRange;
102 uint64_t initrdStart;
103 uint64_t initrdEnd;
104 const char* bootargs;
105};
106
107PlatformInfo g_Platform;
108
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]);
111}
112
113bool equal(const char* a, const char* b) {
114 while (*a && *b && *a == *b) {
115 ++a;
116 ++b;
117 }
118 return *a == *b;
119}
120
121bool hasString(const uint8_t* data, size_t size, const char* wanted) {
122 size_t offset = 0;
123 while (offset < size) {
124 size_t length = 0;
125 while (offset + length < size && data[offset + length]) {
126 ++length;
127 }
128 if (offset + length == size) {
129 return false;
130 }
131 if (equal(reinterpret_cast<const char*>(data + offset), wanted)) {
132 return true;
133 }
134 offset += length + 1;
135 }
136 return false;
137}
138
139bool readCells(const uint8_t* data, size_t size, uint32_t count, uint64_t& value) {
140 if (!count || count > 2 || size < count * 4) {
141 return false;
142 }
143 value = count == 2 ? read32(data) : 0;
144 value = (value << 32) | read32(data + (count - 1) * 4);
145 return true;
146}
147
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)) {
154 return false;
155 }
156 return size && base <= UINT64_MAX - size;
157}
158
159uint32_t gicIrq(const uint8_t* data, size_t size) {
160 if (!data || size < 12) {
161 return 0;
162 }
163 const uint32_t type = read32(data);
164 const uint32_t number = read32(data + 4);
165 if (type == 0 && number < 988) {
166 return number + 32;
167 }
168 if (type == 1 && number < 16) {
169 return number + 16;
170 }
171 return 0;
172}
173
174void finishNode(const Node& node, const Node& parent, PlatformInfo& out) {
175 uint64_t base = 0;
176 uint64_t size = 0;
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;
181 offset += stride) {
182 Node entry = node;
183 entry.reg = node.reg + offset;
184 entry.regSize = stride;
185 if (firstReg(entry, parent, base, size)) {
186 out.memory[out.memoryCount++] = {base, size};
187 }
188 }
189 }
190
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);
194 }
195 if (node.rtc && firstReg(node, parent, base, size)) {
196 out.rtc = static_cast<uintptr_t>(base);
197 }
198
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) {
206 Node cpu = node;
207 cpu.reg += stride;
208 cpu.regSize -= stride;
209 if (firstReg(cpu, parent, base, size)) {
210 if (node.gicV3) {
211 out.gicRedistributor = static_cast<uintptr_t>(base);
212 } else {
213 out.gicCpu = static_cast<uintptr_t>(base);
214 }
215 }
216 }
217 }
218
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,
224 size, 0, 0};
225 out.msiPhandle = node.phandle;
226 }
227
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);
231 }
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)};
236 }
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);
251 }
252 }
253 if (node.msiMap && node.msiMapSize && !(node.msiMapSize % 16)) {
254 out.pciMsiMap = node.msiMap;
255 out.pciMsiMapSize = node.msiMapSize;
256 out.pciMsiMapMask = node.msiMapMask;
257 }
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;
262 offset += stride) {
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;
267 uint64_t length = 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)};
275 }
276 }
277 }
278 }
279 }
280 if (node.psci) {
281 out.psciAvailable = true;
282 out.psciHvc = node.psciHvc;
283 }
284 if (node.chosen) {
285 out.initrdStart = node.initrdStart;
286 out.initrdEnd = node.initrdEnd;
287 out.bootargs = node.bootargs;
288 }
289}
290
291bool parse(const uint8_t* dtb, PlatformInfo& out) {
292 if (!dtb || read32(dtb) != FdtMagic) {
293 return false;
294 }
295
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) {
304 return false;
305 }
306
307 const uint8_t* structure = dtb + structOffset;
308 const uint8_t* strings = dtb + stringsOffset;
309 size_t cursor = 0;
310 Node stack[MaxDepth] = {};
311 size_t depth = 0;
312
313 while (cursor + 4 <= structSize) {
314 const uint32_t token = read32(structure + cursor);
315 cursor += 4;
316 if (token == BeginNode) {
317 if (depth == MaxDepth) {
318 return false;
319 }
320 const size_t start = cursor;
321 while (cursor < structSize && structure[cursor]) {
322 ++cursor;
323 }
324 if (cursor == structSize) {
325 return false;
326 }
327 const size_t nameLength = cursor - start;
328 cursor = (cursor + 4) & ~size_t(3);
329 if (cursor > structSize) {
330 return false;
331 }
332 Node& node = stack[depth];
333 node = {};
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");
342 ++depth;
343 } else if (token == Property) {
344 if (!depth || cursor + 8 > structSize) {
345 return false;
346 }
347 const size_t length = read32(structure + cursor);
348 const size_t nameOffset = read32(structure + cursor + 4);
349 cursor += 8;
350 if (nameOffset >= stringsSize || length > structSize - cursor) {
351 return false;
352 }
353 size_t nameEnd = nameOffset;
354 while (nameEnd < stringsSize && strings[nameEnd]) {
355 ++nameEnd;
356 }
357 if (nameEnd == stringsSize) {
358 return false;
359 }
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")) {
368 node.reg = data;
369 node.regSize = length;
370 } else if (equal(name, "interrupts")) {
371 node.interrupts = data;
372 node.interruptsSize = length;
373 } else if (equal(name, "ranges")) {
374 node.ranges = data;
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")) {
383 node.msiMap = data;
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")) {
394 node.memory = true;
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");
405 node.psci =
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);
414 }
415 } else if (node.chosen && equal(name, "linux,initrd-end")) {
416 if (length == 4 || length == 8) {
417 readCells(data, length, length / 4, node.initrdEnd);
418 }
419 } else if (node.chosen && equal(name, "bootargs") && length && data[length - 1] == 0) {
420 node.bootargs = reinterpret_cast<const char*>(data);
421 }
422 cursor = (cursor + length + 3) & ~size_t(3);
423 if (cursor > structSize) {
424 return false;
425 }
426 } else if (token == EndNode) {
427 if (!depth) {
428 return false;
429 }
430 if (depth > 1) {
431 finishNode(stack[depth - 1], stack[depth - 2], out);
432 }
433 --depth;
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) {
440 return false;
441 }
442 }
443 return false;
444}
445} // namespace
446
447bool VirtDeviceTree::initialise(const void* dtb) {
448 PlatformInfo parsed = {};
449 if (!parse(static_cast<const uint8_t*>(dtb), parsed)) {
450 g_Platform = {};
451 return false;
452 }
453 parsed.blobSize = read32(static_cast<const uint8_t*>(dtb) + 4);
454 parsed.valid = true;
455 g_Platform = parsed;
456 return true;
457}
458
459bool VirtDeviceTree::initialiseAcpi(uint64_t rsdpPhysical,
460 const BootstrapStruct_t::MemoryMapEntry* memoryMap,
461 size_t memoryMapCount) {
462 VirtAcpiInfo acpi = {};
463 if (!virtParseAcpi(rsdpPhysical, memoryMap, memoryMapCount, acpi)) {
464 g_Platform = {};
465 return false;
466 }
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];
472 }
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];
476 }
477 }
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;
494 return true;
495}
496
497bool VirtDeviceTree::valid() {
498 return g_Platform.valid;
499}
500
501bool VirtDeviceTree::memoryRegion(size_t index, VirtMemoryRegion& region) {
502 if (!g_Platform.valid || index >= g_Platform.memoryCount) {
503 return false;
504 }
505 region = g_Platform.memory[index];
506 return true;
507}
508
509bool VirtDeviceTree::virtioMmio(size_t index, VirtMmioDevice& device) {
510 if (!g_Platform.valid || index >= g_Platform.virtioMmioCount) {
511 return false;
512 }
513 device = g_Platform.virtioMmio[index];
514 return true;
515}
516
517bool VirtDeviceTree::pciHost(VirtPciHost& host) {
518 if (!g_Platform.valid || !g_Platform.pciHost.size) {
519 return false;
520 }
521 host = g_Platform.pciHost;
522 return true;
523}
524
525bool VirtDeviceTree::pciWindow(size_t index, VirtPciWindow& window) {
526 if (!g_Platform.valid || index >= g_Platform.pciWindowCount) {
527 return false;
528 }
529 window = g_Platform.pciWindows[index];
530 return true;
531}
532
533bool VirtDeviceTree::pciTranslate(uint64_t address, uint64_t size, bool io, uint64_t& physical) {
534 if (!g_Platform.valid || !size) {
535 return false;
536 }
537 for (size_t i = 0; i < g_Platform.pciWindowCount; ++i) {
538 const VirtPciWindow& window = g_Platform.pciWindows[i];
539 if ((window.space == 0x01000000) != io || address < window.pciBase ||
540 address - window.pciBase >= window.size ||
541 size > window.size - (address - window.pciBase)) {
542 continue;
543 }
544 physical = window.cpuBase + (address - window.pciBase);
545 return true;
546 }
547 return false;
548}
549
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]
554 : 0;
555 }
556 if (!g_Platform.valid || !g_Platform.gicPhandle || !g_Platform.pciInterruptMap ||
557 g_Platform.gicAddressCells > 2 || device >= 32 || function >= 8 || pin < 1 || pin > 4) {
558 return 0;
559 }
560 const size_t stride = (8 + g_Platform.gicAddressCells) * 4;
561 if (g_Platform.pciInterruptMapSize % stride) {
562 return 0;
563 }
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) {
575 continue;
576 }
577 return gicIrq(entry + 20 + g_Platform.gicAddressCells * 4, 12);
578 }
579 return 0;
580}
581
582bool VirtDeviceTree::pciMsiController(VirtMsiController& controller) {
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)) {
591 return false;
592 }
593 if (!g_Platform.acpi && (!g_Platform.pciMsiMap || !g_Platform.msiPhandle)) {
594 return false;
595 }
596 controller = g_Platform.msiController;
597 return true;
598}
599
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) {
603 return false;
604 }
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) {
609 return false;
610 }
611 id = rid;
612 return true;
613 }
614 if (!g_Platform.pciMsiMap || !g_Platform.msiPhandle) {
615 return false;
616 }
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)) {
626 continue;
627 }
628 id = base + (masked - start);
629 return true;
630 }
631 return false;
632}
633
635 return g_Platform.uart ? DirectMapBase + g_Platform.uart : 0;
636}
637
638uint32_t VirtDeviceTree::uartIrq() {
639 return g_Platform.uartIrq;
640}
641
642uintptr_t VirtDeviceTree::rtcBase() {
643 return g_Platform.rtc ? DirectMapBase + g_Platform.rtc : 0;
644}
645
646uint32_t VirtDeviceTree::gicVersion() {
647 return g_Platform.gicVersion;
648}
649
650uintptr_t VirtDeviceTree::gicDistributorBase() {
651 return g_Platform.gicDistributor ? DirectMapBase + g_Platform.gicDistributor : 0;
652}
653
654uintptr_t VirtDeviceTree::gicCpuBase() {
655 return g_Platform.gicCpu ? DirectMapBase + g_Platform.gicCpu : 0;
656}
657
658uintptr_t VirtDeviceTree::gicRedistributorBase() {
659 return g_Platform.gicRedistributor ? DirectMapBase + g_Platform.gicRedistributor : 0;
660}
661
662uint64_t VirtDeviceTree::gicRedistributorPhysical() {
663 return g_Platform.gicRedistributor;
664}
665
666uint32_t VirtDeviceTree::physicalTimerIrq() {
667 return g_Platform.physicalTimerIrq;
668}
669
670uint32_t VirtDeviceTree::virtualTimerIrq() {
671 return g_Platform.virtualTimerIrq;
672}
673
674bool VirtDeviceTree::psciUsesHvc() {
675 return g_Platform.psciHvc;
676}
677
678bool VirtDeviceTree::psciAvailable() {
679 return g_Platform.psciAvailable;
680}
681
682bool VirtDeviceTree::initrd(uint64_t& start, uint64_t& end) {
683 if (!g_Platform.valid || !g_Platform.initrdStart ||
684 g_Platform.initrdEnd <= g_Platform.initrdStart) {
685 return false;
686 }
687 start = g_Platform.initrdStart;
688 end = g_Platform.initrdEnd;
689 return true;
690}
691
692const char* VirtDeviceTree::bootargs() {
693 return g_Platform.valid ? g_Platform.bootargs : nullptr;
694}
695
696size_t VirtDeviceTree::blobSize() {
697 return g_Platform.valid ? g_Platform.blobSize : 0;
698}
699
700extern "C" void virtSetDeviceTree(const void* dtb) {
701 VirtDeviceTree::initialise(dtb);
702}
703
704extern "C" bool virtGetMemoryRegion(size_t index, uint64_t* base, uint64_t* size) {
705 VirtMemoryRegion region;
706 if (!base || !size || !VirtDeviceTree::memoryRegion(index, region)) {
707 return false;
708 }
709 *base = region.base;
710 *size = region.size;
711 return true;
712}
713
714extern "C" bool virtGetVirtioMmio(size_t index, uint64_t* base, uint64_t* size, uint32_t* irq) {
715 VirtMmioDevice device;
716 if (!base || !size || !irq || !VirtDeviceTree::virtioMmio(index, device)) {
717 return false;
718 }
719 *base = device.base;
720 *size = device.size;
721 *irq = device.irq;
722 return true;
723}
724
725extern "C" bool virtGetInitrd(uint64_t* start, uint64_t* end) {
726 return start && end && VirtDeviceTree::initrd(*start, *end);
727}
728
729extern "C" bool virtGetBootargs(const char** text) {
730 if (!text) {
731 return false;
732 }
733 *text = VirtDeviceTree::bootargs();
734 return *text != nullptr;
735}
static uintptr_t uartBase()