The Pedigree Project 0.1
AcpiDmar.h
1/* Copyright (c) 2026, Pedigree Developers. SPDX-License-Identifier: ISC */
2#ifndef PEDIGREE_PC_ACPI_DMAR_H
3#define PEDIGREE_PC_ACPI_DMAR_H
4
5#include <stddef.h>
6#include <stdint.h>
7
8namespace AcpiDmar {
9struct Info {
10 static constexpr size_t MaxDirectEndpoints = 128;
11 static constexpr size_t MaxDirectBridges = 128;
12
13 uint8_t hostAddressWidth = 0;
14 bool interruptRemapping = false;
15 bool x2ApicOptOut = false;
16 bool dmaControlPlatformOptIn = false;
17 bool reservedMemoryRegions = false;
18 size_t hardwareUnitCount = 0;
19 size_t segmentZeroUnitCount = 0;
20 size_t segmentZeroIncludeAllCount = 0;
21 uint64_t firstSegmentZeroUnitAddress = 0;
22 uint8_t firstSegmentZeroUnitRegisterPagesLog2 = 0;
23 uint64_t firstSegmentZeroIncludeAllAddress = 0;
24 uint8_t firstSegmentZeroIncludeAllRegisterPagesLog2 = 0;
25 uint16_t directEndpoints[MaxDirectEndpoints] = {};
26 size_t directEndpointCount = 0;
27 uint16_t directBridges[MaxDirectBridges] = {};
28 size_t directBridgeCount = 0;
29
30 bool includesDirectEndpoint(uint8_t bus, uint8_t device, uint8_t function) const {
31 const uint16_t sourceId = (uint16_t(bus) << 8) | (uint16_t(device) << 3) | function;
32 for (size_t index = 0; index < directEndpointCount; ++index) {
33 if (directEndpoints[index] == sourceId) {
34 return true;
35 }
36 }
37 return false;
38 }
39};
40
41inline uint16_t read16(const uint8_t* bytes) {
42 return uint16_t(bytes[0]) | (uint16_t(bytes[1]) << 8);
43}
44
45inline uint32_t read32(const uint8_t* bytes) {
46 return uint32_t(read16(bytes)) | (uint32_t(read16(bytes + 2)) << 16);
47}
48
49inline uint64_t read64(const uint8_t* bytes) {
50 return uint64_t(read32(bytes)) | (uint64_t(read32(bytes + 4)) << 32);
51}
52
54inline bool parse(const uint8_t* table, size_t available, Info& result) {
55 constexpr size_t HeaderBytes = 48;
56 if (!table || available < HeaderBytes || read32(table) != 0x52414d44U) {
57 return false;
58 }
59 const size_t length = read32(table + 4);
60 if (length < HeaderBytes || length > available || table[36] < 31 || table[36] > 63 ||
61 (table[37] & ~7U)) {
62 return false;
63 }
64
65 uint8_t checksum = 0;
66 for (size_t i = 0; i < length; ++i) {
67 checksum += table[i];
68 }
69 if (checksum) {
70 return false;
71 }
72
73 Info found;
74 found.hostAddressWidth = table[36] + 1;
75 found.interruptRemapping = table[37] & 1U;
76 found.x2ApicOptOut = table[37] & 2U;
77 found.dmaControlPlatformOptIn = table[37] & 4U;
78 for (size_t offset = HeaderBytes; offset < length;) {
79 if (length - offset < 4) {
80 return false;
81 }
82 const uint16_t type = read16(table + offset);
83 const size_t bytes = read16(table + offset + 2);
84 if (bytes < 4 || bytes > length - offset) {
85 return false;
86 }
87 if (type == 0) {
88 if (bytes < 16) {
89 return false;
90 }
91 const uint8_t registerPagesLog2 = table[offset + 5] & 0x0fU;
92 if ((table[offset + 4] & ~1U) || (table[offset + 5] & ~0x0fU) ||
93 !read64(table + offset + 8) ||
94 (read64(table + offset + 8) & ((uint64_t(1) << (12 + registerPagesLog2)) - 1))) {
95 return false;
96 }
97 for (size_t scope = offset + 16; scope < offset + bytes;) {
98 if (offset + bytes - scope < 8) {
99 return false;
100 }
101 const size_t scopeBytes = table[scope + 1];
102 if (!table[scope] || scopeBytes < 8 || (scopeBytes & 1U) ||
103 scopeBytes > offset + bytes - scope) {
104 return false;
105 }
106 if (!read16(table + offset + 6) && (table[scope] == 1 || table[scope] == 2) &&
107 scopeBytes == 8 && !table[scope + 2] && !table[scope + 3] && !table[scope + 4] &&
108 table[scope + 6] < 32 && table[scope + 7] < 8) {
109 const uint16_t sourceId = (uint16_t(table[scope + 5]) << 8) |
110 (uint16_t(table[scope + 6]) << 3) | table[scope + 7];
111 if (table[scope] == 1) {
112 if (found.directEndpointCount == Info::MaxDirectEndpoints) {
113 return false;
114 }
115 found.directEndpoints[found.directEndpointCount++] = sourceId;
116 } else {
117 if (found.directBridgeCount == Info::MaxDirectBridges) {
118 return false;
119 }
120 found.directBridges[found.directBridgeCount++] = sourceId;
121 }
122 }
123 scope += scopeBytes;
124 }
125 ++found.hardwareUnitCount;
126 if (!read16(table + offset + 6)) {
127 ++found.segmentZeroUnitCount;
128 if (!found.firstSegmentZeroUnitAddress) {
129 found.firstSegmentZeroUnitAddress = read64(table + offset + 8);
130 found.firstSegmentZeroUnitRegisterPagesLog2 = registerPagesLog2;
131 }
132 if (table[offset + 4] & 1U) {
133 ++found.segmentZeroIncludeAllCount;
134 if (!found.firstSegmentZeroIncludeAllAddress) {
135 found.firstSegmentZeroIncludeAllAddress = read64(table + offset + 8);
136 found.firstSegmentZeroIncludeAllRegisterPagesLog2 = registerPagesLog2;
137 }
138 }
139 }
140 } else if (type == 1) {
141 if (bytes < 24) {
142 return false;
143 }
144 found.reservedMemoryRegions = true;
145 }
146 offset += bytes;
147 }
148 if (!found.hardwareUnitCount) {
149 return false;
150 }
151 result = found;
152 return true;
153}
154} // namespace AcpiDmar
155
156#endif