The Pedigree Project 0.1
AcpiPciRouting.h
1/* Copyright (c) 2026, Pedigree Developers. SPDX-License-Identifier: ISC */
2#ifndef PEDIGREE_PC_ACPI_PCI_ROUTING_H
3#define PEDIGREE_PC_ACPI_PCI_ROUTING_H
4
5#include <stddef.h>
6#include <stdint.h>
7
10 public:
11 struct Route {
12 uint32_t gsi = 0;
13 bool activeLow = false;
14 };
15
16 static bool parse(const uint8_t* aml, size_t size, Route routes[32][4]) {
17 if (!aml || !size || !routes) {
18 return false;
19 }
20 for (size_t slot = 0; slot < 32; ++slot) {
21 for (size_t pin = 0; pin < 4; ++pin) {
22 routes[slot][pin] = {};
23 }
24 }
25 // Q35 exposes its APIC-mode table as PRTA, selected by a _PRT method.
26 // Other firmware can expose a static _PRT package directly.
27 return parsePackage(aml, size, "_PRT", routes) || parsePackage(aml, size, "PRTA", routes);
28 }
29
30 private:
31 static bool equal(const uint8_t* bytes, const char* name) {
32 for (size_t i = 0; i < 4; ++i) {
33 if (bytes[i] != static_cast<uint8_t>(name[i])) {
34 return false;
35 }
36 }
37 return true;
38 }
39
40 static bool package(const uint8_t* bytes, size_t size, size_t& cursor, size_t& end) {
41 if (cursor >= size) {
42 return false;
43 }
44 const uint8_t lead = bytes[cursor];
45 const size_t follow = lead >> 6;
46 if (follow >= size - cursor) {
47 return false;
48 }
49 uint32_t length = follow ? lead & 0x0f : lead & 0x3f;
50 for (size_t i = 0; i < follow; ++i) {
51 length |= uint32_t(bytes[cursor + 1 + i]) << (4 + i * 8);
52 }
53 if (length < follow + 1 || length > size - cursor) {
54 return false;
55 }
56 end = cursor + length;
57 cursor += follow + 1;
58 return true;
59 }
60
61 static bool integer(const uint8_t* bytes, size_t end, size_t& cursor, uint64_t& value) {
62 if (cursor >= end) {
63 return false;
64 }
65 const uint8_t op = bytes[cursor++];
66 if (op <= 1) {
67 value = op;
68 return true;
69 }
70 const size_t width = op == 0x0a ? 1 : op == 0x0b ? 2 : op == 0x0c ? 4 : op == 0x0e ? 8 : 0;
71 if (!width || width > end - cursor) {
72 return false;
73 }
74 value = 0;
75 for (size_t i = 0; i < width; ++i) {
76 value |= uint64_t(bytes[cursor++]) << (i * 8);
77 }
78 return true;
79 }
80
81 static bool link(const uint8_t* aml, size_t size, const char source[4], Route& route) {
82 for (size_t i = 0; i + 8 < size; ++i) {
83 if (aml[i] != 0x5b || aml[i + 1] != 0x82) {
84 continue;
85 }
86 size_t body = i + 2, deviceEnd = 0;
87 if (!package(aml, size, body, deviceEnd) || deviceEnd - body < 4 ||
88 !equal(aml + body, source)) {
89 continue;
90 }
91 body += 4;
92 for (size_t j = body; j + 7 < deviceEnd; ++j) {
93 if (aml[j] != 0x08 || !equal(aml + j + 1, "_CRS") || aml[j + 5] != 0x11) {
94 continue;
95 }
96 size_t data = j + 6, bufferEnd = 0;
97 uint64_t declared = 0;
98 if (!package(aml, deviceEnd, data, bufferEnd) || !integer(aml, bufferEnd, data, declared) ||
99 declared > bufferEnd - data) {
100 continue;
101 }
102 const size_t end = data + static_cast<size_t>(declared);
103 while (data < end) {
104 const uint8_t tag = aml[data++];
105 size_t length = 0;
106 if (tag & 0x80) {
107 if (end - data < 2) {
108 return false;
109 }
110 length = size_t(aml[data]) | (size_t(aml[data + 1]) << 8);
111 data += 2;
112 } else {
113 length = tag & 7;
114 }
115 if (length > end - data) {
116 return false;
117 }
118 if (tag == 0x89 && length == 6 && aml[data + 1] == 1 && !(aml[data] & 2)) {
119 route.gsi = uint32_t(aml[data + 2]) | (uint32_t(aml[data + 3]) << 8) |
120 (uint32_t(aml[data + 4]) << 16) | (uint32_t(aml[data + 5]) << 24);
121 route.activeLow = (aml[data] & 4) != 0;
122 return route.gsi >= 16;
123 }
124 data += length;
125 }
126 }
127 }
128 return false;
129 }
130
131 static bool parsePackage(const uint8_t* aml, size_t size, const char* name, Route routes[32][4]) {
132 for (size_t i = 0; i + 7 < size; ++i) {
133 if (aml[i] != 0x08 || !equal(aml + i + 1, name) || aml[i + 5] != 0x12) {
134 continue;
135 }
136 size_t body = i + 6, end = 0;
137 if (!package(aml, size, body, end) || body >= end) {
138 continue;
139 }
140 const uint8_t count = aml[body++];
141 Route parsed[32][4] = {};
142 bool valid = true, found = false;
143 for (size_t entry = 0; entry < count; ++entry) {
144 if (body >= end || aml[body++] != 0x12) {
145 valid = false;
146 break;
147 }
148 size_t itemEnd = 0;
149 if (!package(aml, end, body, itemEnd) || body >= itemEnd || aml[body++] != 4) {
150 valid = false;
151 break;
152 }
153 uint64_t address = 0, pin = 0, sourceIndex = 0;
154 if (!integer(aml, itemEnd, body, address) || !integer(aml, itemEnd, body, pin) ||
155 body >= itemEnd) {
156 valid = false;
157 break;
158 }
159 Route route = {};
160 char source[4] = {};
161 const bool direct = aml[body] == 0;
162 if (direct) {
163 ++body;
164 } else if (itemEnd - body >= 4) {
165 for (size_t c = 0; c < 4; ++c) {
166 source[c] = aml[body++];
167 }
168 } else {
169 valid = false;
170 break;
171 }
172 if (!integer(aml, itemEnd, body, sourceIndex) || (address & 0xffff) != 0xffff || pin > 3 ||
173 sourceIndex > UINT32_MAX) {
174 valid = false;
175 break;
176 }
177 if (direct) {
178 route.gsi = static_cast<uint32_t>(sourceIndex);
179 route.activeLow = true;
180 } else if (sourceIndex == 0) {
181 link(aml, size, source, route);
182 }
183 const uint32_t slot = (address >> 16) & 0xffff;
184 if (slot < 32 && route.gsi >= 16) {
185 if (parsed[slot][pin].gsi && (parsed[slot][pin].gsi != route.gsi ||
186 parsed[slot][pin].activeLow != route.activeLow)) {
187 valid = false;
188 break;
189 }
190 parsed[slot][pin] = route;
191 found = true;
192 }
193 body = itemEnd;
194 }
195 if (valid && found && body == end) {
196 for (size_t slot = 0; slot < 32; ++slot) {
197 for (size_t pin = 0; pin < 4; ++pin) {
198 routes[slot][pin] = parsed[slot][pin];
199 }
200 }
201 return true;
202 }
203 }
204 return false;
205 }
206};
207
208#endif