The Pedigree Project 0.1
Resources.cc
1/* Copyright (c) 2026, Pedigree Developers. SPDX-License-Identifier: ISC */
2#include "Resources.h"
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"
10
11namespace {
12enum class Kind : uint8_t { Bar, MemoryWindow, PrefetchWindow, Assigned };
13
14struct Interval {
15 const PciFirmware::Root* root;
16 uint32_t route;
17 uint64_t base;
18 uint64_t last;
19 Kind kind;
20 uint8_t bar;
21 bool prefetchable;
22};
23
24struct Staged {
25 Interval interval;
26 Device* owner;
27};
28
29struct Function {
30 const PciFirmware::Root* root;
31 uint32_t route;
32};
33
34Mutex g_Lock;
35Vector<Interval> g_Intervals;
36Vector<Function> g_Functions;
37bool g_Ready = false;
38
39uint32_t routeId(Device* device) {
40 return (device->getPciBusPosition() << 8) | (device->getPciDevicePosition() << 3) |
41 device->getPciFunctionNumber();
42}
43
44bool isPciFunction(Device* device) {
45 Device* parent = device->getParent();
46 if (!parent || parent->getSpecificType() != "pci") {
47 return false;
48 }
49 const auto config = device->getPciConfigHeader();
50 return config.vendor && config.vendor != 0xffff;
51}
52
53bool isBridge(Device* device) {
54 return device->getPciClassCode() == 6 && device->getPciSubclassCode() == 4 &&
55 (device->getPciConfigHeader().header_type & 0x7f) == 1;
56}
57
58bool ancestor(uint32_t route, Device* device) {
59 for (Device* parent = device->getParent(); parent; parent = parent->getParent()) {
60 if (isPciFunction(parent) && routeId(parent) == route) {
61 return true;
62 }
63 }
64 return false;
65}
66
67bool lastAddress(uint64_t base, uint64_t size, uint64_t& last) {
68 if (!size || size - 1 > ~uint64_t{0} - base) {
69 return false;
70 }
71 last = base + size - 1;
72 return true;
73}
74
75bool overlap(const Interval& a, const Interval& b) {
76 return a.root == b.root && a.base <= b.last && b.base <= a.last;
77}
78
79bool bridgeWindow(const Interval& entry) {
80 return entry.kind == Kind::MemoryWindow || entry.kind == Kind::PrefetchWindow;
81}
82
83bool usableWindow(const PciFirmware::Window& window, bool prefetchable) {
84 if (window.io || window.prefetchable != prefetchable) {
85 return false;
86 }
87#if X86_COMMON
88 if (window.pciBase != window.cpuBase) {
89 WARNING("PCI resources: translated host window unsupported");
90 return false;
91 }
92#endif
93 return true;
94}
95
96template <typename T>
97bool append(Vector<T>& entries, const T& entry) {
98 if (entries.count() == entries.size() && !entries.tryReserve(entries.count() + 1)) {
99 return false;
100 }
101 entries.pushBack(entry);
102 return true;
103}
104
105bool addWindow(Device* owner, const PciFirmware::Root* root, uint32_t registerValue,
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;
110 if (prefetchable) {
111 if (type != (limitRegister & 0xf) || type > 1) {
112 return false;
113 }
114 } else if (type || (limitRegister & 0xf)) {
115 return false;
116 }
117
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;
123 }
124 if (base > last) {
125 return true; // Disabled window.
126 }
127 return append(staged,
128 {{root, routeId(owner), base, last,
129 prefetchable ? Kind::PrefetchWindow : Kind::MemoryWindow, 0, prefetchable},
130 owner});
131}
132
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");
139 return false;
140 }
141 if (discovery == PciExtendedCapabilities::FindResult::Found) {
142 struct Config {
143 PciBus& pci;
144 Device* device;
145 bool read16(uint16_t offset, uint16_t& value) {
146 return pci.readConfig16(device, offset, value);
147 }
148 bool read32(uint16_t offset, uint32_t& value) {
149 return pci.readConfig32(device, offset, value);
150 }
151 } access{pci, device};
152 PciSriov::State state;
153 if (!PciSriov::read(access, capability.offset, capability.next, state) ||
154 (state.control & PciSriov::VfControl)) {
155 // Active VF BARs cannot be sized without disrupting inherited users.
156 WARNING("PCI resources: SR-IOV apertures are active or unavailable");
157 return false;
158 }
159 }
160 return true;
161}
162
163bool stoppedLeaf(Device* device) {
164 if (!device || !isPciFunction(device) || device->getPhysicalFunction() ||
165 device->getNumChildren() || device->getPciBusPosition() > 255 ||
166 device->getPciDevicePosition() > 31 || device->getPciFunctionNumber() > 7) {
167 return false;
168 }
169 const auto config = device->getPciConfigHeader();
170 auto& pci = PciBus::instance();
171 uint32_t identity = 0;
172 uint32_t rom = 0;
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);
181}
182
183bool collect(Device* device, Vector<Staged>& staged, Vector<Function>& functions) {
184 if (!isPciFunction(device)) {
185 return true;
186 }
187 if (device->getPciBusPosition() > 255 || device->getPciDevicePosition() > 31 ||
188 device->getPciFunctionNumber() > 7 || !sriovDisabled(device)) {
189 return false;
190 }
191 const auto* root = PciFirmware::rootForBus(device->getPciBusPosition());
192 if (!root || root->segment != 0 || root->windowCount > PciFirmware::MaxWindows ||
193 !append(functions, {root, routeId(device)})) {
194 return false;
195 }
196
197 const auto config = device->getPciConfigHeader();
198 const uint8_t header = config.header_type & 0x7f;
199 if (header > 1) {
200 return false;
201 }
202 // Expansion ROM sizing is not available in the enumerated BAR inventory.
203 if ((header == 0 && (config.rom_base_address & 1U)) || (header == 1 && (config.reserved1 & 1U))) {
204 return false;
205 }
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];
209 if (low & 1U) {
210 continue; // This manager owns MMIO only.
211 }
212 const unsigned type = (low >> 1) & 3U;
213 if (type == 3 || (type == 2 && bar + 1 == barCount)) {
214 return false;
215 }
216 const bool wide = type == 2;
217 uint64_t base = low & (type == 1 ? 0xffff0U : ~uint32_t{15});
218 if (wide) {
219 base |= uint64_t(config.bar[bar + 1]) << 32;
220 }
221 if (base) {
222 char name[] = {'b', 'a', 'r', static_cast<char>('0' + bar), 0};
223 Device::Address* address = nullptr;
224 for (Device::Address* candidate : device->addresses()) {
225 if (candidate && candidate->m_Name == name) {
226 address = candidate;
227 break;
228 }
229 }
230 uint64_t last = 0;
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),
233 bool(low & 8U)},
234 device})) {
235 return false;
236 }
237 }
238 if (wide) {
239 ++bar;
240 }
241 }
242
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,
247 staged)) {
248 return false;
249 }
250 }
251 return true;
252}
253
254bool scoped(Device* owner, const PciFirmware::Root* root, bool prefetchable, uint64_t& base,
255 uint64_t& last) {
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)) {
260 behindBridge = true;
261 break;
262 }
263 }
264 if (!behindBridge) {
265 return false; // Unlinked secondary bus: forwarding path is ambiguous.
266 }
267 }
268 for (Device* parent = owner->getParent(); parent; parent = parent->getParent()) {
269 if (!isPciFunction(parent) || !isBridge(parent)) {
270 continue;
271 }
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) {
276 window = &entry;
277 break;
278 }
279 }
280 if (!window) {
281 return false;
282 }
283 if (base < window->base) {
284 base = window->base;
285 }
286 if (last > window->last) {
287 last = window->last;
288 }
289 if (base > last) {
290 return false;
291 }
292 }
293 return true;
294}
295
296bool ownerRoot(Device* owner, const PciFirmware::Root*& root) {
297 if (!g_Ready || !owner || !isPciFunction(owner) || owner->getPciBusPosition() > 255) {
298 return false;
299 }
300 root = PciFirmware::rootForBus(owner->getPciBusPosition());
301 if (!root) {
302 return false;
303 }
304 for (const Function& function : g_Functions) {
305 if (function.root == root && function.route == routeId(owner)) {
306 return true;
307 }
308 }
309 return false;
310}
311
312const Interval* occupied(Device* owner, const Interval& candidate) {
313 for (const Interval& entry : g_Intervals) {
314 if (!overlap(entry, candidate)) {
315 continue;
316 }
317 if (bridgeWindow(entry) && ancestor(entry.route, owner)) {
318 continue;
319 }
320 return &entry;
321 }
322 return nullptr;
323}
324
325bool alignUp(uint64_t value, uint64_t alignment, uint64_t& aligned) {
326 if (!alignment || (alignment & (alignment - 1)) || value > ~uint64_t{0} - (alignment - 1)) {
327 return false;
328 }
329 aligned = (value + alignment - 1) & ~(alignment - 1);
330 return true;
331}
332} // namespace
333
334namespace PciResources {
335bool initialize(Device* root) {
336 LockGuard<Mutex> guard(g_Lock);
337 if (g_Ready) {
338 return true;
339 }
340 Vector<Staged> staged;
341 Vector<Function> functions;
342 bool valid = !root || collect(root, staged, functions);
343 auto visit = [&](Device* device) -> Device* {
344 if (valid) {
345 valid = collect(device, staged, functions);
346 }
347 return device;
348 };
349 auto callback = pedigree_std::make_callable(visit);
350 Device::foreach (callback, root);
351 if (!valid || !functions.count()) {
352 return false;
353 }
354
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)) {
360 continue;
361 }
362 if ((bridgeWindow(a.interval) && ancestor(a.interval.route, b.owner)) ||
363 (bridgeWindow(b.interval) && ancestor(b.interval.route, a.owner))) {
364 continue;
365 }
366 return false;
367 }
368 }
369
370 Vector<Interval> intervals;
371 for (const Staged& entry : staged) {
372 if (!append(intervals, entry.interval)) {
373 return false;
374 }
375 }
376 g_Intervals.swap(intervals);
377 g_Functions.swap(functions);
378 g_Ready = true;
379 return true;
380}
381
382bool addFunction(Device* device) {
383 LockGuard<Mutex> guard(g_Lock);
384 if (!g_Ready || !stoppedLeaf(device)) {
385 return false;
386 }
387 const auto* root = PciFirmware::rootForBus(device->getPciBusPosition());
388 if (!root || root->segment != 0 || root->windowCount > PciFirmware::MaxWindows) {
389 return false;
390 }
391 const uint32_t route = routeId(device);
392 for (const Function& function : g_Functions) {
393 if (function.root == root && function.route == route) {
394 return false;
395 }
396 }
397
398 const auto config = device->getPciConfigHeader();
399 auto& pci = PciBus::instance();
400 for (size_t bar = 0; bar < 6; ++bar) {
401 uint32_t low = 0;
402 if (!pci.readConfig32(device, 0x10 + bar * 4, low) || low != config.bar[bar]) {
403 return false;
404 }
405 if (low & 1U) {
406 if (low & ~uint32_t{3}) {
407 return false;
408 }
409 continue;
410 }
411 const unsigned type = (low >> 1) & 3U;
412 if (type == 3 || (low & ~uint32_t{15})) {
413 return false;
414 }
415 if (type == 2) {
416 uint32_t high = 0;
417 if (++bar == 6 || !pci.readConfig32(device, 0x10 + bar * 4, high) || high ||
418 high != config.bar[bar]) {
419 return false;
420 }
421 }
422 }
423 return append(g_Functions, {root, route});
424}
425
426bool removeFunction(Device* device) {
427 LockGuard<Mutex> guard(g_Lock);
428 const PciFirmware::Root* root = nullptr;
429 if (!ownerRoot(device, root) || !stoppedLeaf(device)) {
430 return false;
431 }
432 const uint32_t route = routeId(device);
433 for (const Interval& entry : g_Intervals) {
434 if (entry.root == root && entry.route == route && bridgeWindow(entry)) {
435 return false;
436 }
437 }
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);
442 }
443 }
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);
447 return true;
448 }
449 }
450 return false;
451}
452
453bool reserve(Device* owner, uint64_t base, uint64_t size, bool prefetchable) {
454 LockGuard<Mutex> guard(g_Lock);
455 const PciFirmware::Root* root = nullptr;
456 uint64_t last = 0;
457 if (!ownerRoot(owner, root) || !base || !lastAddress(base, size, last)) {
458 return false;
459 }
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 ||
466 last > windowLast) {
467 continue;
468 }
469 uint64_t allowedBase = base;
470 uint64_t allowedLast = last;
471 if (scoped(owner, root, prefetchable, allowedBase, allowedLast) && allowedBase == base &&
472 allowedLast == last) {
473 forwarded = true;
474 break;
475 }
476 }
477 if (!forwarded) {
478 return false;
479 }
480 Interval candidate{root, routeId(owner), base, last, Kind::Assigned, 0, prefetchable};
481 return !occupied(owner, candidate) && append(g_Intervals, candidate);
482}
483
484bool allocate(Device* owner, uint64_t size, uint64_t alignment, bool prefetchable,
485 uint64_t maxAddress, uint64_t& outBase) {
486 LockGuard<Mutex> guard(g_Lock);
487 const PciFirmware::Root* root = nullptr;
488 if (!ownerRoot(owner, root) || !size || !alignment || (alignment & (alignment - 1)) ||
489 size - 1 > maxAddress) {
490 return false;
491 }
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)) {
497 continue;
498 }
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)) {
502 continue;
503 }
504 uint64_t base = 0;
505 if (!alignUp(low, alignment, base)) {
506 continue;
507 }
508 while (base <= high && size - 1 <= high - base) {
509 Interval candidate{root, routeId(owner), base, base + size - 1, Kind::Assigned,
510 0, prefetchable};
511 const Interval* conflict = occupied(owner, candidate);
512 if (!conflict) {
513 if (!append(g_Intervals, candidate)) {
514 return false;
515 }
516 outBase = base;
517 return true;
518 }
519 if (conflict->last == ~uint64_t{0} || !alignUp(conflict->last + 1, alignment, base)) {
520 break;
521 }
522 }
523 }
524 return false;
525}
526
527bool release(Device* owner, uint64_t base, uint64_t size) {
528 LockGuard<Mutex> guard(g_Lock);
529 const PciFirmware::Root* root = nullptr;
530 uint64_t last = 0;
531 if (!ownerRoot(owner, root) || !lastAddress(base, size, last)) {
532 return false;
533 }
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);
539 return true;
540 }
541 }
542 return false;
543}
544} // namespace PciResources
static void foreach(Callback callback, Device *root=0)
Definition Device.cc:107
virtual const String & getSpecificType()
Definition Device.h:183
Device * getParent() const
Definition Device.h:166
Definition Mutex.h:56
Definition Pci.h:31
A vector / dynamic array.
Definition Vector.h:33
void erase(size_t index)
Definition Vector.h:389
void swap(Iterator a, Iterator b)
Definition Vector.h:481
size_t count() const
Definition Vector.h:270