The Pedigree Project 0.1
PciFirmware.cc
1/* Copyright (c) 2026, Pedigree Developers. SPDX-License-Identifier: ISC */
2#include "pedigree/kernel/LockGuard.h"
3#include "pedigree/kernel/Log.h"
4#include "pedigree/kernel/machine/PciFirmware.h"
5#include "pedigree/kernel/process/Mutex.h"
6
7#include "PciRouting.h"
8#include <uacpi/acpi.h>
9#include <uacpi/resources.h>
10#include <uacpi/uacpi.h>
11#include <uacpi/utilities.h>
12
13#if ARM64 || ARMV7
14#include "system/kernel/machine/mach_virt/DeviceTree.h"
15#endif
16
17namespace {
18constexpr size_t MaxRoots = 8;
19constexpr uint32_t OscSupport = (1U << 0) | (1U << 4); // Extended config and MSI.
20constexpr uint32_t NativeMask =
21 PciFirmware::NativeHotplug | PciFirmware::NativeAer | PciFirmware::NativePcieCapability;
22constexpr const char* RootIds[] = {"PNP0A03", "PNP0A08", nullptr};
23constexpr uint8_t OscUuid[16] = {0x5b, 0x4d, 0xdb, 0x33, 0xf7, 0x1f, 0x1c, 0x40,
24 0x96, 0x57, 0x74, 0x41, 0xc0, 0x3d, 0xd7, 0x66};
25
26struct RootState {
28 uacpi_namespace_node* node = nullptr;
29 uint32_t control = 0;
30 bool oscFailed = false;
31};
32
33struct ResourceContext {
35 uint8_t baseBus;
36 bool sawBus = false;
37 bool valid = true;
38};
39
40Mutex g_Lock;
41RootState g_Roots[MaxRoots];
42size_t g_RootCount = 0;
43bool g_Ready = false;
44bool g_Failed = false;
45
46bool overlaps(uint64_t first, uint64_t firstSize, uint64_t second, uint64_t secondSize) {
47 return first <= second + secondSize - 1 && second <= first + firstSize - 1;
48}
49
50bool translate(uint64_t address, uint64_t length, uint64_t offset, unsigned width,
51 uint64_t& result) {
52 const uint64_t mask = width == 64 ? ~uint64_t(0) : (uint64_t(1) << width) - 1;
53 const uint64_t sign = uint64_t(1) << (width - 1);
54 if (offset & sign) {
55 const uint64_t magnitude = ((~offset) & mask) + 1;
56 if (address < magnitude) {
57 return false;
58 }
59 result = address - magnitude;
60 } else {
61 if (address > ~uint64_t(0) - offset) {
62 return false;
63 }
64 result = address + offset;
65 }
66 return result <= ~uint64_t(0) - (length - 1);
67}
68
69bool addAddress(ResourceContext& context, const uacpi_resource_address_common& common,
70 uint64_t minimum, uint64_t maximum, uint64_t offset, uint64_t length,
71 unsigned width) {
72 if (common.direction != UACPI_PRODUCER) {
73 return true;
74 }
75 if (!length || minimum > maximum || length - 1 != maximum - minimum ||
76 common.fixed_min_address != UACPI_ADDRESS_FIXED ||
77 common.fixed_max_address != UACPI_ADDRESS_FIXED ||
78 common.decode_type != UACPI_POSITIVE_DECODE) {
79 return false;
80 }
81 if (common.type == UACPI_RANGE_BUS) {
82 if (context.sawBus || offset || minimum != context.baseBus || maximum > 255) {
83 return false;
84 }
85 context.root->firstBus = static_cast<uint8_t>(minimum);
86 context.root->lastBus = static_cast<uint8_t>(maximum);
87 context.sawBus = true;
88 return true;
89 }
90 if (common.type != UACPI_RANGE_IO && common.type != UACPI_RANGE_MEMORY) {
91 return false;
92 }
93 if (common.type == UACPI_RANGE_MEMORY &&
94 common.attribute.memory.range_type != UACPI_RANGE_TYPE_MEMORY) {
95 return true;
96 }
97 if (common.type == UACPI_RANGE_IO &&
98 common.attribute.io.translation_type != UACPI_TRANSLATION_DENSE) {
99 return false;
100 }
101
102 uint64_t cpuBase = 0;
103 if (!translate(minimum, length, offset, width, cpuBase)) {
104 return false;
105 }
106 const bool io = common.type == UACPI_RANGE_IO;
107 if (io && (maximum > 0xffff || cpuBase + length - 1 > 0xffff)) {
108 return false;
109 }
110 PciFirmware::Root& root = *context.root;
111 if (root.windowCount == PciFirmware::MaxWindows) {
112 return false;
113 }
114 for (size_t i = 0; i < root.windowCount; ++i) {
115 const PciFirmware::Window& old = root.windows[i];
116 if (old.io == io && (overlaps(minimum, length, old.pciBase, old.size) ||
117 overlaps(cpuBase, length, old.cpuBase, old.size))) {
118 return false;
119 }
120 }
121 PciFirmware::Window& window = root.windows[root.windowCount++];
122 window.pciBase = minimum;
123 window.cpuBase = cpuBase;
124 window.size = length;
125 window.io = io;
126 window.prefetchable = !io && common.attribute.memory.caching == UACPI_PREFETCHABLE;
127 return true;
128}
129
130uacpi_iteration_decision readResource(void* user, uacpi_resource* resource) {
131 auto& context = *static_cast<ResourceContext*>(user);
132 bool valid = true;
133 switch (resource->type) {
134 case UACPI_RESOURCE_TYPE_ADDRESS16: {
135 const auto& address = resource->address16;
136 valid = addAddress(context, address.common, address.minimum, address.maximum,
137 address.translation_offset, address.address_length, 16);
138 break;
139 }
140 case UACPI_RESOURCE_TYPE_ADDRESS32: {
141 const auto& address = resource->address32;
142 valid = addAddress(context, address.common, address.minimum, address.maximum,
143 address.translation_offset, address.address_length, 32);
144 break;
145 }
146 case UACPI_RESOURCE_TYPE_ADDRESS64: {
147 const auto& address = resource->address64;
148 valid = addAddress(context, address.common, address.minimum, address.maximum,
149 address.translation_offset, address.address_length, 64);
150 break;
151 }
152 case UACPI_RESOURCE_TYPE_ADDRESS64_EXTENDED: {
153 const auto& address = resource->address64_extended;
154 valid = addAddress(context, address.common, address.minimum, address.maximum,
155 address.translation_offset, address.address_length, 64);
156 break;
157 }
158 default:
159 break;
160 }
161 context.valid = valid;
162 return valid ? UACPI_ITERATION_DECISION_CONTINUE : UACPI_ITERATION_DECISION_BREAK;
163}
164
165bool readRoot(uacpi_namespace_node* node, RootState& result) {
166 uint64_t segment = 0;
167 uacpi_status status = uacpi_eval_simple_integer(node, "_SEG", &segment);
168 if (status != UACPI_STATUS_OK && status != UACPI_STATUS_NOT_FOUND) {
169 return false;
170 }
171 if (segment > 0xffff) {
172 return false;
173 }
174 if (segment) {
175 return true;
176 }
177
178 uint64_t baseBus = 0;
179 status = uacpi_eval_simple_integer(node, "_BBN", &baseBus);
180 if ((status != UACPI_STATUS_OK && status != UACPI_STATUS_NOT_FOUND) || baseBus > 255) {
181 return false;
182 }
183 result.root.segment = 0;
184 ResourceContext context{&result.root, static_cast<uint8_t>(baseBus)};
185 uacpi_resources* resources = nullptr;
186 status = uacpi_get_current_resources(node, &resources);
187 if (status != UACPI_STATUS_OK || !resources) {
188 if (resources) {
189 uacpi_free_resources(resources);
190 }
191 return false;
192 }
193 status = uacpi_for_each_resource(resources, readResource, &context);
194 uacpi_free_resources(resources);
195 if (status != UACPI_STATUS_OK || !context.valid || !context.sawBus) {
196 return false;
197 }
198 result.node = node;
199 return true;
200}
201
202struct DiscoveryContext {
203 bool failed = false;
204};
205
206uacpi_iteration_decision readDevice(void* user, uacpi_namespace_node* node, uacpi_u32) {
207 auto& context = *static_cast<DiscoveryContext*>(user);
208 if (!uacpi_device_matches_pnp_id(node, RootIds)) {
209 return UACPI_ITERATION_DECISION_CONTINUE;
210 }
211 uacpi_u32 flags = 0;
212 if (uacpi_eval_sta(node, &flags) != UACPI_STATUS_OK) {
213 context.failed = true;
214 return UACPI_ITERATION_DECISION_BREAK;
215 }
216 if (!(flags & (ACPI_STA_RESULT_DEVICE_PRESENT | ACPI_STA_RESULT_DEVICE_FUNCTIONING))) {
217 return UACPI_ITERATION_DECISION_NEXT_PEER;
218 }
219 RootState candidate;
220 if (!readRoot(node, candidate)) {
221 context.failed = true;
222 return UACPI_ITERATION_DECISION_BREAK;
223 }
224 if (!candidate.node) {
225 return UACPI_ITERATION_DECISION_CONTINUE;
226 }
227 if (g_RootCount == MaxRoots) {
228 context.failed = true;
229 return UACPI_ITERATION_DECISION_BREAK;
230 }
231 for (size_t i = 0; i < g_RootCount; ++i) {
232 const PciFirmware::Root& old = g_Roots[i].root;
233 const PciFirmware::Root& current = candidate.root;
234 if (current.firstBus <= old.lastBus && old.firstBus <= current.lastBus) {
235 context.failed = true;
236 return UACPI_ITERATION_DECISION_BREAK;
237 }
238 for (size_t a = 0; a < current.windowCount; ++a) {
239 for (size_t b = 0; b < old.windowCount; ++b) {
240 const auto& first = current.windows[a];
241 const auto& second = old.windows[b];
242 if (first.io == second.io &&
243 overlaps(first.cpuBase, first.size, second.cpuBase, second.size)) {
244 context.failed = true;
245 return UACPI_ITERATION_DECISION_BREAK;
246 }
247 }
248 }
249 }
250 g_Roots[g_RootCount++] = candidate;
251 return UACPI_ITERATION_DECISION_CONTINUE;
252}
253
254RootState* findRoot(uint8_t bus) {
255 for (size_t i = 0; i < g_RootCount; ++i) {
256 PciFirmware::Root& root = g_Roots[i].root;
257 if (root.firstBus <= bus && bus <= root.lastBus) {
258 return &g_Roots[i];
259 }
260 }
261 return nullptr;
262}
263
264void write32(uint8_t* bytes, uint32_t value) {
265 for (unsigned i = 0; i < 4; ++i) {
266 bytes[i] = static_cast<uint8_t>(value >> (i * 8));
267 }
268}
269
270uint32_t read32(const uint8_t* bytes) {
271 uint32_t value = 0;
272 for (unsigned i = 0; i < 4; ++i) {
273 value |= uint32_t(bytes[i]) << (i * 8);
274 }
275 return value;
276}
277
278bool evaluateOsc(uacpi_namespace_node* node, uint32_t control, bool query) {
279 uint8_t capabilities[12] = {};
280 write32(capabilities, query ? 1U : 0U);
281 write32(capabilities + 4, OscSupport);
282 write32(capabilities + 8, control);
283 uacpi_data_view uuid{};
284 uuid.const_bytes = OscUuid;
285 uuid.length = sizeof(OscUuid);
286 uacpi_data_view caps{};
287 caps.const_bytes = capabilities;
288 caps.length = sizeof(capabilities);
289 uacpi_object* args[4] = {uacpi_object_create_buffer(uuid), uacpi_object_create_integer(1),
290 uacpi_object_create_integer(3), uacpi_object_create_buffer(caps)};
291 bool complete = true;
292 for (uacpi_object* arg : args) {
293 if (!arg) {
294 complete = false;
295 }
296 }
297 uacpi_object* output = nullptr;
298 if (complete) {
299 uacpi_object_array array{args, 4};
300 complete = uacpi_eval_buffer(node, "_OSC", &array, &output) == UACPI_STATUS_OK && output;
301 }
302 for (uacpi_object* arg : args) {
303 if (arg) {
304 uacpi_object_unref(arg);
305 }
306 }
307 if (!complete) {
308 if (output) {
309 uacpi_object_unref(output);
310 }
311 return false;
312 }
313 uacpi_data_view response{};
314 complete = uacpi_object_get_buffer(output, &response) == UACPI_STATUS_OK &&
315 response.length == sizeof(capabilities) && response.const_bytes;
316 if (complete) {
317 const uint32_t status = read32(response.const_bytes);
318 const uint32_t support = read32(response.const_bytes + 4);
319 const uint32_t granted = read32(response.const_bytes + 8);
320 complete = (status & ~1U) == 0 && bool(status & 1U) == query &&
321 (support & OscSupport) == OscSupport && (granted & control) == control;
322 }
323 uacpi_object_unref(output);
324 return complete;
325}
326} // namespace
327
328namespace PciFirmware {
329bool discover() {
330 LockGuard<Mutex> guard(g_Lock);
331 if (g_Ready) {
332 return true;
333 }
334 if (g_Failed) {
335 return false;
336 }
337 if (uacpi_get_current_init_level() < UACPI_INIT_LEVEL_NAMESPACE_INITIALIZED) {
338#if ARM64 || ARMV7
339 VirtPciHost host{};
340 if (!VirtDeviceTree::pciHost(host) || host.firstBus > host.lastBus || host.lastBus > 255) {
341 return false;
342 }
343 Root& root = g_Roots[0].root;
344 root.firstBus = host.firstBus;
345 root.lastBus = host.lastBus;
346 VirtPciWindow window{};
347 for (size_t i = 0; VirtDeviceTree::pciWindow(i, window); ++i) {
348 if (i == MaxWindows || !window.size || window.pciBase > ~uint64_t(0) - (window.size - 1) ||
349 window.cpuBase > ~uint64_t(0) - (window.size - 1)) {
350 root.windowCount = 0;
351 return false;
352 }
353 root.windows[root.windowCount++] = {window.pciBase, window.cpuBase, window.size,
354 window.space == 0x01000000, window.prefetchable};
355 }
356 g_RootCount = 1;
357 g_Ready = true;
358 NOTICE("PCI firmware: using device-tree root windows");
359 return true;
360#else
361 return false;
362#endif
363 }
364 DiscoveryContext context;
365 const uacpi_status status =
366 uacpi_namespace_for_each_child(uacpi_namespace_root(), readDevice, nullptr,
367 UACPI_OBJECT_DEVICE_BIT, UACPI_MAX_DEPTH_ANY, &context);
368 if (status != UACPI_STATUS_OK || context.failed || !g_RootCount) {
369 g_RootCount = 0;
370 g_Failed = true;
371 WARNING("PCI firmware: root discovery failed");
372 return false;
373 }
374 g_Ready = true;
375 NOTICE("PCI firmware: discovered " << Dec << g_RootCount << " segment-zero root bridges");
376 for (size_t i = 0; i < g_RootCount; ++i) {
377 const RootState& root = g_Roots[i];
378 addPciRoutingRoot(root.node, root.root.firstBus, root.root.lastBus);
379 }
380 return true;
381}
382
383const Root* rootForBus(uint8_t bus) {
384 LockGuard<Mutex> guard(g_Lock);
385 RootState* root = g_Ready ? findRoot(bus) : nullptr;
386 return root ? &root->root : nullptr;
387}
388
389bool requestNativeControl(uint8_t bus, uint32_t bits) {
390 if (!bits || (bits & ~NativeMask)) {
391 return false;
392 }
393 LockGuard<Mutex> guard(g_Lock);
394 RootState* root = g_Ready ? findRoot(bus) : nullptr;
395 if (!root || !root->node || root->oscFailed) {
396 return false;
397 }
398 const uint32_t requested = root->control | bits;
399 if (requested == root->control) {
400 return true;
401 }
402 if (!evaluateOsc(root->node, requested, true)) {
403 return false;
404 }
405 if (!evaluateOsc(root->node, requested, false)) {
406 root->oscFailed = true;
407 return false;
408 }
409 root->control = requested;
410 return true;
411}
412
413uint32_t nativeControl(uint8_t bus) {
414 LockGuard<Mutex> guard(g_Lock);
415 RootState* root = g_Ready ? findRoot(bus) : nullptr;
416 return root ? root->control : 0;
417}
418} // namespace PciFirmware
Definition Mutex.h:56
@ Dec
Definition Log.h:126