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"
9#include <uacpi/resources.h>
10#include <uacpi/uacpi.h>
11#include <uacpi/utilities.h>
14#include "system/kernel/machine/mach_virt/DeviceTree.h"
18constexpr size_t MaxRoots = 8;
19constexpr uint32_t OscSupport = (1U << 0) | (1U << 4);
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};
28 uacpi_namespace_node* node =
nullptr;
30 bool oscFailed =
false;
33struct ResourceContext {
41RootState g_Roots[MaxRoots];
42size_t g_RootCount = 0;
46bool overlaps(uint64_t first, uint64_t firstSize, uint64_t second, uint64_t secondSize) {
47 return first <= second + secondSize - 1 && second <= first + firstSize - 1;
50bool translate(uint64_t address, uint64_t length, uint64_t offset,
unsigned width,
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);
55 const uint64_t magnitude = ((~offset) & mask) + 1;
56 if (address < magnitude) {
59 result = address - magnitude;
61 if (address > ~uint64_t(0) - offset) {
64 result = address + offset;
66 return result <= ~uint64_t(0) - (length - 1);
69bool addAddress(ResourceContext& context,
const uacpi_resource_address_common& common,
70 uint64_t minimum, uint64_t maximum, uint64_t offset, uint64_t length,
72 if (common.direction != UACPI_PRODUCER) {
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) {
81 if (common.type == UACPI_RANGE_BUS) {
82 if (context.sawBus || offset || minimum != context.baseBus || maximum > 255) {
85 context.root->firstBus =
static_cast<uint8_t
>(minimum);
86 context.root->lastBus =
static_cast<uint8_t
>(maximum);
87 context.sawBus =
true;
90 if (common.type != UACPI_RANGE_IO && common.type != UACPI_RANGE_MEMORY) {
93 if (common.type == UACPI_RANGE_MEMORY &&
94 common.attribute.memory.range_type != UACPI_RANGE_TYPE_MEMORY) {
97 if (common.type == UACPI_RANGE_IO &&
98 common.attribute.io.translation_type != UACPI_TRANSLATION_DENSE) {
102 uint64_t cpuBase = 0;
103 if (!translate(minimum, length, offset, width, cpuBase)) {
106 const bool io = common.type == UACPI_RANGE_IO;
107 if (io && (maximum > 0xffff || cpuBase + length - 1 > 0xffff)) {
111 if (root.windowCount == PciFirmware::MaxWindows) {
114 for (
size_t i = 0; i < root.windowCount; ++i) {
116 if (old.io == io && (overlaps(minimum, length, old.pciBase, old.size) ||
117 overlaps(cpuBase, length, old.cpuBase, old.size))) {
122 window.pciBase = minimum;
123 window.cpuBase = cpuBase;
124 window.size = length;
126 window.prefetchable = !io && common.attribute.memory.caching == UACPI_PREFETCHABLE;
130uacpi_iteration_decision readResource(
void* user, uacpi_resource* resource) {
131 auto& context = *
static_cast<ResourceContext*
>(user);
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);
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);
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);
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);
161 context.valid = valid;
162 return valid ? UACPI_ITERATION_DECISION_CONTINUE : UACPI_ITERATION_DECISION_BREAK;
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) {
171 if (segment > 0xffff) {
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) {
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) {
189 uacpi_free_resources(resources);
193 status = uacpi_for_each_resource(resources, readResource, &context);
194 uacpi_free_resources(resources);
195 if (status != UACPI_STATUS_OK || !context.valid || !context.sawBus) {
202struct DiscoveryContext {
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;
212 if (uacpi_eval_sta(node, &flags) != UACPI_STATUS_OK) {
213 context.failed =
true;
214 return UACPI_ITERATION_DECISION_BREAK;
216 if (!(flags & (ACPI_STA_RESULT_DEVICE_PRESENT | ACPI_STA_RESULT_DEVICE_FUNCTIONING))) {
217 return UACPI_ITERATION_DECISION_NEXT_PEER;
220 if (!readRoot(node, candidate)) {
221 context.failed =
true;
222 return UACPI_ITERATION_DECISION_BREAK;
224 if (!candidate.node) {
225 return UACPI_ITERATION_DECISION_CONTINUE;
227 if (g_RootCount == MaxRoots) {
228 context.failed =
true;
229 return UACPI_ITERATION_DECISION_BREAK;
231 for (
size_t i = 0; i < g_RootCount; ++i) {
234 if (current.firstBus <= old.lastBus && old.firstBus <= current.lastBus) {
235 context.failed =
true;
236 return UACPI_ITERATION_DECISION_BREAK;
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;
250 g_Roots[g_RootCount++] = candidate;
251 return UACPI_ITERATION_DECISION_CONTINUE;
254RootState* findRoot(uint8_t bus) {
255 for (
size_t i = 0; i < g_RootCount; ++i) {
257 if (root.firstBus <= bus && bus <= root.lastBus) {
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));
270uint32_t read32(
const uint8_t* bytes) {
272 for (
unsigned i = 0; i < 4; ++i) {
273 value |= uint32_t(bytes[i]) << (i * 8);
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) {
297 uacpi_object* output =
nullptr;
299 uacpi_object_array array{args, 4};
300 complete = uacpi_eval_buffer(node,
"_OSC", &array, &output) == UACPI_STATUS_OK && output;
302 for (uacpi_object* arg : args) {
304 uacpi_object_unref(arg);
309 uacpi_object_unref(output);
313 uacpi_data_view response{};
314 complete = uacpi_object_get_buffer(output, &response) == UACPI_STATUS_OK &&
315 response.length ==
sizeof(capabilities) && response.const_bytes;
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;
323 uacpi_object_unref(output);
328namespace PciFirmware {
337 if (uacpi_get_current_init_level() < UACPI_INIT_LEVEL_NAMESPACE_INITIALIZED) {
340 if (!VirtDeviceTree::pciHost(host) || host.firstBus > host.lastBus || host.lastBus > 255) {
343 Root& root = g_Roots[0].root;
344 root.firstBus = host.firstBus;
345 root.lastBus = host.lastBus;
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;
353 root.windows[root.windowCount++] = {window.pciBase, window.cpuBase, window.size,
354 window.space == 0x01000000, window.prefetchable};
358 NOTICE(
"PCI firmware: using device-tree root windows");
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) {
371 WARNING(
"PCI firmware: root discovery failed");
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);
383const Root* rootForBus(uint8_t bus) {
385 RootState* root = g_Ready ? findRoot(bus) : nullptr;
386 return root ? &root->root :
nullptr;
389bool requestNativeControl(uint8_t bus, uint32_t bits) {
390 if (!bits || (bits & ~NativeMask)) {
394 RootState* root = g_Ready ? findRoot(bus) : nullptr;
395 if (!root || !root->node || root->oscFailed) {
398 const uint32_t requested = root->control | bits;
399 if (requested == root->control) {
402 if (!evaluateOsc(root->node, requested,
true)) {
405 if (!evaluateOsc(root->node, requested,
false)) {
406 root->oscFailed =
true;
409 root->control = requested;
413uint32_t nativeControl(uint8_t bus) {
415 RootState* root = g_Ready ? findRoot(bus) : nullptr;
416 return root ? root->control : 0;