11constexpr uint64_t DirectMapBase = 0xffff000000000000ULL;
12constexpr uint64_t DirectMapLimit = 64ULL << 30;
13constexpr uint32_t MaxTableSize = 1024 * 1024;
14constexpr uint32_t XsdtSignature = 0x54445358;
15constexpr uint32_t MadtSignature = 0x43495041;
16constexpr uint32_t GtdtSignature = 0x54445447;
17constexpr uint32_t SpcrSignature = 0x52435053;
18constexpr uint32_t McfgSignature = 0x4746434d;
19constexpr uint32_t IortSignature = 0x54524f49;
20constexpr uint32_t FadtSignature = 0x50434146;
21constexpr uint32_t HeaderSize = 36;
24size_t g_MemoryMapCount = 0;
26uint16_t read16(
const uint8_t* p) {
27 return uint16_t(p[0]) | (uint16_t(p[1]) << 8);
30uint32_t read32(
const uint8_t* p) {
31 return uint32_t(p[0]) | (uint32_t(p[1]) << 8) | (uint32_t(p[2]) << 16) | (uint32_t(p[3]) << 24);
34uint64_t read64(
const uint8_t* p) {
35 return uint64_t(read32(p)) | (uint64_t(read32(p + 4)) << 32);
38bool checksum(
const uint8_t* bytes,
size_t size) {
40 for (
size_t i = 0; i < size; ++i) {
46const uint8_t* physical(uint64_t address,
size_t bytes) {
47 if (!address || !bytes || address >= DirectMapLimit || bytes > DirectMapLimit - address) {
50 const uint64_t end = address + bytes;
51 for (uint64_t cursor = address; cursor < end;) {
52 uint64_t next = cursor;
53 for (
size_t i = 0; i < g_MemoryMapCount; ++i) {
54 const auto& region = g_MemoryMap[i];
55 if ((region.type != 3 && region.type != 4) || region.address > cursor || !region.length ||
56 region.address > UINT64_MAX - region.length || cursor >= region.address + region.length) {
59 const uint64_t regionEnd = region.address + region.length;
60 if (regionEnd > next) {
61 next = regionEnd < end ? regionEnd : end;
69 return reinterpret_cast<const uint8_t*
>(DirectMapBase + address);
72const uint8_t* tableAt(uint64_t address) {
73 const uint8_t* table = physical(address, HeaderSize);
77 const uint32_t length = read32(table + 4);
78 if (length < HeaderSize || length > MaxTableSize || !physical(address, length) ||
79 !checksum(table, length)) {
85bool package(
const uint8_t* bytes,
size_t size,
size_t& cursor,
size_t& end) {
89 const uint8_t lead = bytes[cursor];
90 const size_t follow = lead >> 6;
91 if (cursor + follow >= size) {
94 uint32_t length = follow ? lead & 0x0f : lead & 0x3f;
95 for (
size_t i = 0; i < follow; ++i) {
96 length |= uint32_t(bytes[cursor + 1 + i]) << (4 + 8 * i);
98 if (length < follow + 1 || length > size - cursor) {
101 end = cursor + length;
102 cursor += follow + 1;
106bool integer(
const uint8_t* bytes,
size_t end,
size_t& cursor, uint64_t& result) {
110 const uint8_t op = bytes[cursor++];
115 const size_t width = op == 0x0a ? 1 : op == 0x0b ? 2 : op == 0x0c ? 4 : op == 0x0e ? 8 : 0;
116 if (!width || width > end - cursor) {
120 for (
size_t i = 0; i < width; ++i) {
121 result |= uint64_t(bytes[cursor++]) << (i * 8);
126bool name(
const uint8_t* p,
const char* wanted) {
127 for (
size_t i = 0; i < 4; ++i) {
128 if (p[i] !=
static_cast<uint8_t
>(wanted[i])) {
137bool device(
const uint8_t* aml,
size_t size,
const char* wanted,
size_t& start,
size_t& end) {
138 for (
size_t i = 0; i + 8 < size; ++i) {
139 if (aml[i] != 0x5b || aml[i + 1] != 0x82) {
144 if (package(aml, size, body, limit) && body + 4 <= limit && name(aml + body, wanted)) {
153bool namedObject(
const uint8_t* aml,
size_t start,
size_t end,
const char* wanted, uint8_t op,
154 size_t& body,
size_t& limit) {
155 for (
size_t i = start; i + 7 < end; ++i) {
156 if (aml[i] != 0x08 || !name(aml + i + 1, wanted) || aml[i + 5] != op) {
160 return package(aml, end, body, limit);
165bool resources(
const uint8_t* aml,
size_t start,
size_t end,
const uint8_t*& data,
size_t& size) {
166 size_t body = 0, limit = 0;
167 if (!namedObject(aml, start, end,
"_CRS", 0x11, body, limit)) {
170 uint64_t declared = 0;
171 if (!integer(aml, limit, body, declared) || declared > limit - body) {
175 size =
static_cast<size_t>(declared);
179bool resource(
const uint8_t* data,
size_t size,
size_t& cursor, uint8_t& tag,
180 const uint8_t*& payload,
size_t& length) {
181 if (cursor >= size) {
184 const uint8_t lead = data[cursor++];
186 if (size - cursor < 2) {
190 length = read16(data + cursor);
196 if (length > size - cursor) {
199 payload = data + cursor;
204bool addPciWindow(
VirtAcpiInfo& out, uint8_t tag,
const uint8_t* payload,
size_t length) {
205 const size_t width = tag == 0x88 ? 2 : tag == 0x87 ? 4 : tag == 0x8a ? 8 : 0;
206 if (!width || length < 3 + width * 5 || out.pciWindowCount == 8) {
209 const uint8_t type = payload[0];
213 const uint8_t* fields = payload + 3;
214 uint64_t minimum = 0, maximum = 0, translation = 0, bytes = 0;
215 for (
size_t i = 0; i < width; ++i) {
216 minimum |= uint64_t(fields[width + i]) << (i * 8);
217 maximum |= uint64_t(fields[2 * width + i]) << (i * 8);
218 translation |= uint64_t(fields[3 * width + i]) << (i * 8);
219 bytes |= uint64_t(fields[4 * width + i]) << (i * 8);
221 if (!bytes || maximum < minimum || bytes - 1 > maximum - minimum ||
222 minimum > UINT64_MAX - translation || minimum + translation > UINT64_MAX - bytes) {
225 out.pciWindows[out.pciWindowCount++] = {type == 1 ? 0x01000000U
226 : tag == 0x8a ? 0x03000000U
228 minimum, minimum + translation, bytes};
232uint32_t linkIrq(
const uint8_t* aml,
size_t size,
const char* link) {
233 size_t start = 0, end = 0;
234 if (!device(aml, size, link, start, end)) {
237 const uint8_t* data =
nullptr;
239 if (!resources(aml, start, end, data, length)) {
244 const uint8_t* payload =
nullptr;
246 while (resource(data, length, cursor, tag, payload, bytes)) {
247 if (tag == 0x89 && bytes >= 6 && (payload[0] == 1 || payload[0] == 9) && payload[1] == 1) {
248 const uint32_t irq = read32(payload + 2);
249 return irq >= 32 && irq < 256 ? irq : 0;
255bool pciResources(
const uint8_t* aml,
size_t size,
VirtAcpiInfo& out) {
256 size_t start = 0, end = 0;
257 if (!device(aml, size,
"PCI0", start, end)) {
260 const uint8_t* data =
nullptr;
262 if (!resources(aml, start, end, data, length)) {
267 const uint8_t* payload =
nullptr;
269 while (resource(data, length, cursor, tag, payload, bytes)) {
270 addPciWindow(out, tag, payload, bytes);
272 if (!out.pciWindowCount) {
276 size_t body = 0, limit = 0;
277 if (!namedObject(aml, start, end,
"_PRT", 0x13, body, limit)) {
280 uint64_t entries = 0;
281 if (!integer(aml, limit, body, entries) || entries > 1024) {
284 for (uint64_t i = 0; i < entries; ++i) {
285 if (body >= limit || aml[body++] != 0x12) {
289 if (!package(aml, limit, body, itemEnd) || body >= itemEnd || aml[body++] != 4) {
292 uint64_t address = 0, pin = 0, sourceIndex = 0;
293 if (!integer(aml, itemEnd, body, address) || !integer(aml, itemEnd, body, pin) ||
294 itemEnd - body < 4) {
297 const char* link =
reinterpret_cast<const char*
>(aml + body);
299 if (!integer(aml, itemEnd, body, sourceIndex) || sourceIndex || (address & 0xffff) != 0xffff ||
303 const uint32_t slot = (address >> 16) & 0xffff;
305 out.pciIrq[slot][pin] = linkIrq(aml, size, link);
309 for (
size_t slot = 0; slot < 32; ++slot) {
310 for (
size_t pin = 0; pin < 4; ++pin) {
311 if (out.pciIrq[slot][pin]) {
319void rtcResource(
const uint8_t* aml,
size_t size,
VirtAcpiInfo& out) {
320 size_t start = 0, end = 0;
321 if (!device(aml, size,
"RTC0", start, end)) {
324 const uint8_t* data =
nullptr;
326 if (!resources(aml, start, end, data, length)) {
331 const uint8_t* payload =
nullptr;
333 while (resource(data, length, cursor, tag, payload, bytes)) {
334 if (tag == 0x86 && bytes >= 9 && read32(payload + 5) >= 4) {
335 out.rtc = read32(payload + 1);
341void parseMadt(
const uint8_t* table,
VirtAcpiInfo& out) {
342 const size_t length = read32(table + 4);
347 while (cursor + 2 <= length) {
348 const uint8_t type = table[cursor];
349 const uint8_t bytes = table[cursor + 1];
350 if (bytes < 2 || bytes > length - cursor) {
353 const uint8_t* entry = table + cursor;
354 if (type == 12 && bytes >= 24) {
355 out.gicDistributor = read64(entry + 8);
356 out.gicVersion = entry[20];
357 }
else if (type == 13 && bytes >= 24 &&
358 out.msiController.type == VirtMsiController::Type::None) {
359 out.msiController = {
360 VirtMsiController::Type::GicV2m, read64(entry + 8), 0x1000,
361 static_cast<uint16_t
>((read32(entry + 16) & 1) ? read16(entry + 22) : 0),
362 static_cast<uint16_t
>((read32(entry + 16) & 1) ? read16(entry + 20) : 0)};
363 }
else if (type == 14 && bytes >= 16) {
364 out.gicRedistributor = read64(entry + 4);
365 }
else if (type == 15 && bytes >= 20 &&
366 out.msiController.type == VirtMsiController::Type::None) {
367 out.msiController = {VirtMsiController::Type::GicV3Its, read64(entry + 8), 0x20000, 0, 0};
368 out.msiItsId = read32(entry + 4);
369 }
else if (type == 11 && bytes >= 40 && (read32(entry + 12) & 1)) {
370 out.gicCpu = read64(entry + 32);
376void parseGtdt(
const uint8_t* table,
VirtAcpiInfo& out) {
377 if (read32(table + 4) >= 68) {
378 out.physicalTimerIrq = read32(table + 56);
379 out.virtualTimerIrq = read32(table + 64);
383void parseSpcr(
const uint8_t* table,
VirtAcpiInfo& out) {
384 if (read32(table + 4) >= 58 && table[36] == 3 && table[40] == 0) {
385 out.uart = read64(table + 44);
386 out.uartIrq = read32(table + 54);
390void parseMcfg(
const uint8_t* table,
VirtAcpiInfo& out) {
391 const size_t length = read32(table + 4);
392 if (length < 60 || (length - 44) % 16) {
395 for (
size_t cursor = 44; cursor + 16 <= length; cursor += 16) {
396 if (read16(table + cursor + 8) != 0) {
399 const uint8_t first = table[cursor + 10];
400 const uint8_t last = table[cursor + 11];
401 const uint64_t base = read64(table + cursor);
402 if (last >= first && !(base & 0xfffff)) {
403 out.pciHost = {base, uint64_t(last - first + 1) << 20, first, last};
409bool pciMsiIdentity(
const uint8_t* table,
const VirtAcpiInfo& info) {
410 if (!table || info.msiController.type != VirtMsiController::Type::GicV3Its ||
411 !info.pciHost.size) {
414 const size_t length = read32(table + 4);
418 const uint32_t count = read32(table + 36);
419 size_t offset = read32(table + 40);
420 if (count > 1024 || offset < 48 || offset >= length) {
423 const uint32_t firstId = info.pciHost.firstBus << 8;
424 const uint32_t lastId = (info.pciHost.lastBus << 8) | 0xff;
425 for (uint32_t index = 0; index < count; ++index) {
426 if (offset > length - 16) {
429 const uint8_t* node = table + offset;
430 const size_t size = read16(node + 1);
431 if (size < 16 || size > length - offset) {
434 if (node[0] == 2 && size >= 36 && !read32(node + 28)) {
435 const uint32_t mappings = read32(node + 8);
436 const size_t mappingOffset = read32(node + 12);
437 if (mappingOffset >= 36 && mappingOffset <= size && mappings <= (size - mappingOffset) / 20) {
438 for (uint32_t i = 0; i < mappings; ++i) {
439 const uint8_t* map = node + mappingOffset + i * 20;
440 const uint32_t input = read32(map);
441 const uint32_t output = read32(map + 8);
442 const uint32_t target = read32(map + 12);
443 if (read32(map + 16) || input > firstId || output != input ||
444 uint64_t(input) + read32(map + 4) < lastId || target > length - 24) {
447 const uint8_t* its = table + target;
448 if (its[0] == 0 && read16(its + 1) >= 24 && read16(its + 1) <= length - target &&
449 read32(its + 16) && read32(its + 20) == info.msiItsId) {
460uint64_t parseFadt(
const uint8_t* table,
VirtAcpiInfo& out) {
461 const size_t length = read32(table + 4);
463 const uint16_t flags = read16(table + 129);
464 out.psciAvailable = flags & 1;
465 out.psciHvc = flags & 2;
467 if (length >= 148 && read64(table + 140)) {
468 return read64(table + 140);
470 return length >= 44 ? read32(table + 40) : 0;
476 g_MemoryMap = memoryMap;
477 g_MemoryMapCount = memoryMapCount;
478 const uint8_t* rsdp = physical(rsdpPhysical, 36);
479 if (!rsdp || read64(rsdp) != 0x2052545020445352ULL || !checksum(rsdp, 20) || rsdp[15] < 2 ||
480 read32(rsdp + 20) < 36 || read32(rsdp + 20) > 4096 ||
481 !physical(rsdpPhysical, read32(rsdp + 20)) || !checksum(rsdp, read32(rsdp + 20))) {
484 const uint8_t* xsdt = tableAt(read64(rsdp + 24));
485 if (!xsdt || read32(xsdt) != XsdtSignature || (read32(xsdt + 4) - HeaderSize) % 8) {
489 uint64_t dsdtPhysical = 0;
490 const uint8_t* iort =
nullptr;
491 const size_t count = (read32(xsdt + 4) - HeaderSize) / 8;
492 for (
size_t i = 0; i < count; ++i) {
493 const uint8_t* table = tableAt(read64(xsdt + HeaderSize + i * 8));
497 switch (read32(table)) {
499 parseMadt(table, info);
502 parseGtdt(table, info);
505 parseSpcr(table, info);
508 parseMcfg(table, info);
514 dsdtPhysical = parseFadt(table, info);
520 if (info.pciHost.size) {
521 const uint8_t* dsdt = tableAt(dsdtPhysical);
522 if (!dsdt || read32(dsdt) != 0x54445344 ||
523 !pciResources(dsdt + HeaderSize, read32(dsdt + 4) - HeaderSize, info)) {
525 info.pciWindowCount = 0;
527 rtcResource(dsdt + HeaderSize, read32(dsdt + 4) - HeaderSize, info);
530 info.pciMsiIdentity = pciMsiIdentity(iort, info);
531 return info.uart && info.gicDistributor &&
532 ((info.gicVersion == 2 && info.gicCpu) ||
533 (info.gicVersion == 3 && info.gicRedistributor)) &&
534 info.physicalTimerIrq && info.virtualTimerIrq;