3#include "pedigree/kernel/LockGuard.h"
4#include "pedigree/kernel/Log.h"
5#include "pedigree/kernel/machine/Device.h"
6#include "pedigree/kernel/machine/PciFirmware.h"
7#include "pedigree/kernel/machine/PciSriov.h"
8#include "pedigree/kernel/process/Mutex.h"
9#include "pedigree/kernel/utilities/Vector.h"
12enum class Kind : uint8_t { Bar, MemoryWindow, PrefetchWindow, Assigned };
39uint32_t routeId(
Device* device) {
40 return (device->getPciBusPosition() << 8) | (device->getPciDevicePosition() << 3) |
41 device->getPciFunctionNumber();
44bool isPciFunction(
Device* device) {
45 Device* parent = device->getParent();
49 const auto config = device->getPciConfigHeader();
50 return config.vendor && config.vendor != 0xffff;
53bool isBridge(
Device* device) {
54 return device->getPciClassCode() == 6 && device->getPciSubclassCode() == 4 &&
55 (device->getPciConfigHeader().header_type & 0x7f) == 1;
58bool ancestor(uint32_t route,
Device* device) {
59 for (
Device* parent = device->getParent(); parent; parent = parent->
getParent()) {
60 if (isPciFunction(parent) && routeId(parent) == route) {
67bool lastAddress(uint64_t base, uint64_t size, uint64_t& last) {
68 if (!size || size - 1 > ~uint64_t{0} - base) {
71 last = base + size - 1;
75bool overlap(
const Interval& a,
const Interval& b) {
76 return a.root == b.root && a.base <= b.last && b.base <= a.last;
79bool bridgeWindow(
const Interval& entry) {
80 return entry.kind == Kind::MemoryWindow || entry.kind == Kind::PrefetchWindow;
84 if (window.io || window.prefetchable != prefetchable) {
88 if (window.pciBase != window.cpuBase) {
89 WARNING(
"PCI resources: translated host window unsupported");
97bool append(
Vector<T>& entries,
const T& entry) {
98 if (entries.count() == entries.size() && !entries.tryReserve(entries.count() + 1)) {
101 entries.pushBack(entry);
106 uint32_t highBase, uint32_t highLimit,
bool prefetchable,
Vector<Staged>& staged) {
107 const uint16_t baseRegister = registerValue;
108 const uint16_t limitRegister = registerValue >> 16;
109 const uint16_t type = baseRegister & 0xf;
111 if (type != (limitRegister & 0xf) || type > 1) {
114 }
else if (type || (limitRegister & 0xf)) {
118 uint64_t base = uint64_t(baseRegister & 0xfff0U) << 16;
119 uint64_t last = (uint64_t(limitRegister & 0xfff0U) << 16) | 0xfffffU;
120 if (prefetchable && type == 1) {
121 base |= uint64_t(highBase) << 32;
122 last |= uint64_t(highLimit) << 32;
127 return append(staged,
128 {{root, routeId(owner), base, last,
129 prefetchable ? Kind::PrefetchWindow : Kind::MemoryWindow, 0, prefetchable},
133bool sriovDisabled(
Device* device) {
134 auto& pci = PciBus::instance();
136 const auto discovery = pci.findExtendedCapability(device, PciSriov::CapabilityId, capability);
137 if (discovery == PciExtendedCapabilities::FindResult::Malformed) {
138 WARNING(
"PCI resources: malformed extended capabilities");
141 if (discovery == PciExtendedCapabilities::FindResult::Found) {
145 bool read16(uint16_t offset, uint16_t& value) {
146 return pci.readConfig16(device, offset, value);
148 bool read32(uint16_t offset, uint32_t& value) {
149 return pci.readConfig32(device, offset, value);
151 } access{pci, device};
153 if (!PciSriov::read(access, capability.offset, capability.next, state) ||
154 (state.control & PciSriov::VfControl)) {
156 WARNING(
"PCI resources: SR-IOV apertures are active or unavailable");
163bool stoppedLeaf(
Device* device) {
164 if (!device || !isPciFunction(device) || device->getPhysicalFunction() ||
165 device->getNumChildren() || device->getPciBusPosition() > 255 ||
166 device->getPciDevicePosition() > 31 || device->getPciFunctionNumber() > 7) {
169 const auto config = device->getPciConfigHeader();
170 auto& pci = PciBus::instance();
173 uint16_t command = 0;
174 uint8_t header = 0xff;
175 return !(config.header_type & 0x7f) && pci.readConfig32(device, 0,
identity) &&
176 identity == ((uint32_t(config.device) << 16) | config.vendor) &&
177 pci.readConfig8(device, 0xe, header) && !(header & 0x7f) &&
178 pci.readConfig16(device, 4, command) && !(command & 7U) &&
179 pci.readConfig32(device, 0x30, rom) && !(rom & 1U) && !pci.hasDmaRemapping(device) &&
180 sriovDisabled(device);
184 if (!isPciFunction(device)) {
187 if (device->getPciBusPosition() > 255 || device->getPciDevicePosition() > 31 ||
188 device->getPciFunctionNumber() > 7 || !sriovDisabled(device)) {
191 const auto* root = PciFirmware::rootForBus(device->getPciBusPosition());
192 if (!root || root->segment != 0 || root->windowCount > PciFirmware::MaxWindows ||
193 !append(functions, {root, routeId(device)})) {
197 const auto config = device->getPciConfigHeader();
198 const uint8_t header = config.header_type & 0x7f;
203 if ((header == 0 && (config.rom_base_address & 1U)) || (header == 1 && (config.reserved1 & 1U))) {
206 const size_t barCount = header == 0 ? 6 : 2;
207 for (
size_t bar = 0; bar < barCount; ++bar) {
208 const uint32_t low = config.bar[bar];
212 const unsigned type = (low >> 1) & 3U;
213 if (type == 3 || (type == 2 && bar + 1 == barCount)) {
216 const bool wide = type == 2;
217 uint64_t base = low & (type == 1 ? 0xffff0U : ~uint32_t{15});
219 base |= uint64_t(config.bar[bar + 1]) << 32;
222 char name[] = {
'b',
'a',
'r',
static_cast<char>(
'0' + bar), 0};
225 if (candidate && candidate->m_Name == name) {
231 if (!address || address->m_IsIoSpace || !lastAddress(base, address->m_Size, last) ||
232 !append(staged, {{root, routeId(device), base, last, Kind::Bar,
static_cast<uint8_t
>(bar),
243 if (isBridge(device) && (config.command & 2U)) {
244 const uint32_t prefetchLimitHigh = (uint32_t(config.subsys_id) << 16) | config.subsys_vendor;
245 if (!addWindow(device, root, config.bar[4], 0, 0,
false, staged) ||
246 !addWindow(device, root, config.bar[5], config.cardbus_pointer, prefetchLimitHigh,
true,
256 if (owner->getPciBusPosition() != root->firstBus) {
257 bool behindBridge =
false;
258 for (
Device* parent = owner->getParent(); parent; parent = parent->
getParent()) {
259 if (isPciFunction(parent) && isBridge(parent)) {
268 for (
Device* parent = owner->getParent(); parent; parent = parent->
getParent()) {
269 if (!isPciFunction(parent) || !isBridge(parent)) {
272 const Kind kind = prefetchable ? Kind::PrefetchWindow : Kind::MemoryWindow;
273 const Interval* window =
nullptr;
274 for (
const Interval& entry : g_Intervals) {
275 if (entry.root == root && entry.route == routeId(parent) && entry.kind == kind) {
283 if (base < window->base) {
286 if (last > window->last) {
297 if (!g_Ready || !owner || !isPciFunction(owner) || owner->getPciBusPosition() > 255) {
300 root = PciFirmware::rootForBus(owner->getPciBusPosition());
304 for (
const Function& function : g_Functions) {
305 if (function.root == root && function.route == routeId(owner)) {
312const Interval* occupied(
Device* owner,
const Interval& candidate) {
313 for (
const Interval& entry : g_Intervals) {
314 if (!overlap(entry, candidate)) {
317 if (bridgeWindow(entry) && ancestor(entry.route, owner)) {
325bool alignUp(uint64_t value, uint64_t alignment, uint64_t& aligned) {
326 if (!alignment || (alignment & (alignment - 1)) || value > ~uint64_t{0} - (alignment - 1)) {
329 aligned = (value + alignment - 1) & ~(alignment - 1);
334namespace PciResources {
335bool initialize(
Device* root) {
342 bool valid = !root || collect(root, staged, functions);
345 valid = collect(device, staged, functions);
349 auto callback = pedigree_std::make_callable(visit);
351 if (!valid || !functions.
count()) {
355 for (
size_t i = 0; i < staged.
count(); ++i) {
356 const Staged& a = staged[i];
357 for (
size_t j = 0; j < i; ++j) {
358 const Staged& b = staged[j];
359 if (!overlap(a.interval, b.interval)) {
362 if ((bridgeWindow(a.interval) && ancestor(a.interval.route, b.owner)) ||
363 (bridgeWindow(b.interval) && ancestor(b.interval.route, a.owner))) {
371 for (
const Staged& entry : staged) {
372 if (!append(intervals, entry.interval)) {
376 g_Intervals.
swap(intervals);
377 g_Functions.
swap(functions);
382bool addFunction(
Device* device) {
384 if (!g_Ready || !stoppedLeaf(device)) {
387 const auto* root = PciFirmware::rootForBus(device->getPciBusPosition());
388 if (!root || root->segment != 0 || root->windowCount > PciFirmware::MaxWindows) {
391 const uint32_t route = routeId(device);
392 for (
const Function& function : g_Functions) {
393 if (function.root == root && function.route == route) {
398 const auto config = device->getPciConfigHeader();
399 auto& pci = PciBus::instance();
400 for (
size_t bar = 0; bar < 6; ++bar) {
402 if (!pci.readConfig32(device, 0x10 + bar * 4, low) || low != config.bar[bar]) {
406 if (low & ~uint32_t{3}) {
411 const unsigned type = (low >> 1) & 3U;
412 if (type == 3 || (low & ~uint32_t{15})) {
417 if (++bar == 6 || !pci.readConfig32(device, 0x10 + bar * 4, high) || high ||
418 high != config.bar[bar]) {
423 return append(g_Functions, {root, route});
426bool removeFunction(
Device* device) {
429 if (!ownerRoot(device, root) || !stoppedLeaf(device)) {
432 const uint32_t route = routeId(device);
433 for (
const Interval& entry : g_Intervals) {
434 if (entry.root == root && entry.route == route && bridgeWindow(entry)) {
438 for (
size_t i = g_Intervals.
count(); i; --i) {
439 const Interval& entry = g_Intervals[i - 1];
440 if (entry.root == root && entry.route == route) {
441 g_Intervals.
erase(i - 1);
444 for (
size_t i = 0; i < g_Functions.
count(); ++i) {
445 if (g_Functions[i].root == root && g_Functions[i].route == route) {
446 g_Functions.
erase(i);
453bool reserve(
Device* owner, uint64_t base, uint64_t size,
bool prefetchable) {
457 if (!ownerRoot(owner, root) || !base || !lastAddress(base, size, last)) {
460 bool forwarded =
false;
461 for (
size_t i = 0; i < root->windowCount; ++i) {
462 const auto& window = root->windows[i];
463 uint64_t windowLast = 0;
464 if (!usableWindow(window, prefetchable) ||
465 !lastAddress(window.pciBase, window.size, windowLast) || base < window.pciBase ||
469 uint64_t allowedBase = base;
470 uint64_t allowedLast = last;
471 if (scoped(owner, root, prefetchable, allowedBase, allowedLast) && allowedBase == base &&
472 allowedLast == last) {
480 Interval candidate{root, routeId(owner), base, last, Kind::Assigned, 0, prefetchable};
481 return !occupied(owner, candidate) && append(g_Intervals, candidate);
484bool allocate(
Device* owner, uint64_t size, uint64_t alignment,
bool prefetchable,
485 uint64_t maxAddress, uint64_t& outBase) {
488 if (!ownerRoot(owner, root) || !size || !alignment || (alignment & (alignment - 1)) ||
489 size - 1 > maxAddress) {
492 for (
size_t i = 0; i < root->windowCount; ++i) {
493 const auto& window = root->windows[i];
494 uint64_t windowLast = 0;
495 if (!usableWindow(window, prefetchable) ||
496 !lastAddress(window.pciBase, window.size, windowLast)) {
499 uint64_t low = window.pciBase > 0 ? window.pciBase : 1;
500 uint64_t high = windowLast < maxAddress ? windowLast : maxAddress;
501 if (low > high || !scoped(owner, root, prefetchable, low, high)) {
505 if (!alignUp(low, alignment, base)) {
508 while (base <= high && size - 1 <= high - base) {
509 Interval candidate{root, routeId(owner), base, base + size - 1, Kind::Assigned,
511 const Interval* conflict = occupied(owner, candidate);
513 if (!append(g_Intervals, candidate)) {
519 if (conflict->last == ~uint64_t{0} || !alignUp(conflict->last + 1, alignment, base)) {
527bool release(
Device* owner, uint64_t base, uint64_t size) {
531 if (!ownerRoot(owner, root) || !lastAddress(base, size, last)) {
534 for (
size_t i = 0; i < g_Intervals.
count(); ++i) {
535 const Interval& entry = g_Intervals[i];
536 if (entry.root == root && entry.route == routeId(owner) && entry.kind == Kind::Assigned &&
537 entry.base == base && entry.last == last) {
538 g_Intervals.
erase(i);
static void foreach(Callback callback, Device *root=0)
virtual const String & getSpecificType()
Device * getParent() const
A vector / dynamic array.
void swap(Iterator a, Iterator b)