8#include "pedigree/kernel/LockGuard.h"
9#include "pedigree/kernel/Log.h"
10#include "pedigree/kernel/Spinlock.h"
11#include "pedigree/kernel/TargetInfo.h"
12#include "pedigree/kernel/machine/Device.h"
13#include "pedigree/kernel/machine/Pci.h"
14#include "pedigree/kernel/machine/PciMessageBar.h"
15#include "pedigree/kernel/processor/MemoryMappedIo.h"
16#include "pedigree/kernel/processor/PhysicalMemoryManager.h"
17#include "pedigree/kernel/processor/VirtualAddressSpace.h"
19#include "DeviceTree.h"
25bool configAvailable =
false;
27uint64_t nextAddress[8] = {};
28size_t windowCount = 0;
30struct FunctionConfig {
32 bool read8(uint16_t offset, uint8_t& value) {
33 return PciBus::instance().readConfig8(device, offset, value);
35 bool read16(uint16_t offset, uint16_t& value) {
36 return PciBus::instance().readConfig16(device, offset, value);
38 bool read32(uint16_t offset, uint32_t& value) {
39 return PciBus::instance().readConfig32(device, offset, value);
41 bool write16(uint16_t offset, uint16_t value) {
42 return PciBus::instance().writeConfig16(device, offset, value);
44 bool write32(uint16_t offset, uint32_t value) {
45 return PciBus::instance().writeConfig32(device, offset, value);
49bool valid(uint8_t bus, uint8_t device, uint8_t function, uint16_t offset, uint8_t width) {
50 return configAvailable && bus >= host.firstBus && bus <= host.lastBus && device < 32 &&
51 function < 8 && (width == 1 || width == 2 || width == 4) && offset <= 4096 - width &&
52 !(offset & (width - 1));
55size_t configOffset(uint8_t bus, uint8_t device, uint8_t function, uint16_t offset) {
56 return (
size_t(bus - host.firstBus) << 20) | (size_t(device) << 15) | (
size_t(function) << 12) |
60bool read(uint8_t bus, uint8_t device, uint8_t function, uint16_t offset, uint8_t width,
62 if (!valid(bus, device, function, offset, width)) {
65 const size_t position = configOffset(bus, device, function, offset);
66 if (position > ecam.size() - width) {
70 value = ecam.read8(position);
71 }
else if (width == 2) {
72 value = ecam.read16(position);
74 value = ecam.read32(position);
79bool write(uint8_t bus, uint8_t device, uint8_t function, uint16_t offset, uint8_t width,
81 if (!valid(bus, device, function, offset, width)) {
84 const size_t position = configOffset(bus, device, function, offset);
85 if (position > ecam.size() - width) {
89 ecam.write8(
static_cast<uint8_t
>(value), position);
90 }
else if (width == 2) {
91 ecam.write16(
static_cast<uint16_t
>(value), position);
93 ecam.write32(value, position);
98bool readFunction(
Device* device, uint16_t offset, uint8_t width, uint32_t& value) {
99 return device && read(device->getPciBusPosition(), device->getPciDevicePosition(),
100 device->getPciFunctionNumber(), offset, width, value);
103bool writeFunction(
Device* device, uint16_t offset, uint8_t width, uint32_t value) {
104 return device && write(device->getPciBusPosition(), device->getPciDevicePosition(),
105 device->getPciFunctionNumber(), offset, width, value);
108struct UpstreamBridge {
119 bool contains(uint64_t address, uint64_t bytes)
const {
120 return open && address >= base && address <= limit && bytes - 1 <= limit - address;
124struct BridgeWindows {
127 BridgeWindow prefetch;
130bool findUpstreamBridge(uint8_t childBus, UpstreamBridge& upstream) {
132 for (uint32_t bus = host.firstBus; bus < childBus; ++bus) {
133 for (uint8_t device = 0; device < 32; ++device) {
135 if (!read(bus, device, 0, 0, 4,
identity) || (
identity & 0xffffU) == 0xffffU ||
140 if (!read(bus, device, 0, 0x0c, 4, header)) {
143 const uint8_t functions = (header & 0x00800000U) ? 8 : 1;
144 for (uint8_t function = 0; function < functions; ++function) {
145 if (function && (!read(bus, device, function, 0, 4,
identity) ||
149 uint32_t classCode = 0;
150 uint32_t busNumbers = 0;
151 if (!read(bus, device, function, 0x08, 4, classCode) || (classCode >> 16) != 0x0604U ||
152 !read(bus, device, function, 0x18, 4, busNumbers) || (busNumbers & 0xffU) != bus ||
153 ((busNumbers >> 8) & 0xffU) != childBus || ((busNumbers >> 16) & 0xffU) < childBus) {
159 upstream = {
static_cast<uint8_t
>(bus), device, function};
167bool readBridgeWindows(
const UpstreamBridge& bridge, BridgeWindows& windows) {
168 uint32_t header = 0, command = 0, io = 0, memory = 0, prefetch = 0;
169 uint32_t ioUpper = 0, prefetchBaseUpper = 0, prefetchLimitUpper = 0;
170 if (!read(bridge.bus, bridge.device, bridge.function, 0x0e, 1, header) || (header & 0x7f) != 1 ||
171 !read(bridge.bus, bridge.device, bridge.function, 0x04, 2, command) ||
172 !read(bridge.bus, bridge.device, bridge.function, 0x1c, 4, io) ||
173 !read(bridge.bus, bridge.device, bridge.function, 0x20, 4, memory) ||
174 !read(bridge.bus, bridge.device, bridge.function, 0x24, 4, prefetch) ||
175 !read(bridge.bus, bridge.device, bridge.function, 0x28, 4, prefetchBaseUpper) ||
176 !read(bridge.bus, bridge.device, bridge.function, 0x2c, 4, prefetchLimitUpper) ||
177 !read(bridge.bus, bridge.device, bridge.function, 0x30, 4, ioUpper)) {
181 const uint8_t ioBase = io & 0xff;
182 const uint8_t ioLimit = (io >> 8) & 0xff;
183 const uint8_t ioType = ioBase & 0x0f;
184 if ((command & 1U) && ioType == (ioLimit & 0x0f) && ioType <= 1) {
185 windows.io.base = uint64_t(ioBase & 0xf0) << 8;
186 windows.io.limit = (uint64_t(ioLimit & 0xf0) << 8) | 0xfff;
188 windows.io.base |= uint64_t(ioUpper & 0xffff) << 16;
189 windows.io.limit |= uint64_t(ioUpper >> 16) << 16;
191 windows.io.open = windows.io.base <= windows.io.limit;
195 windows.memory.base = uint64_t(memory & 0xfff0) << 16;
196 windows.memory.limit = (uint64_t((memory >> 16) & 0xfff0) << 16) | 0xfffff;
197 windows.memory.open = windows.memory.base <= windows.memory.limit;
199 const uint16_t prefetchBase = prefetch & 0xffff;
200 const uint16_t prefetchLimit = prefetch >> 16;
201 const uint8_t prefetchType = prefetchBase & 0x0f;
202 if (prefetchType == (prefetchLimit & 0x0f) && prefetchType <= 1) {
203 windows.prefetch.base = uint64_t(prefetchBase & 0xfff0) << 16;
204 windows.prefetch.limit = (uint64_t(prefetchLimit & 0xfff0) << 16) | 0xfffff;
205 if (prefetchType == 1) {
206 windows.prefetch.base |= uint64_t(prefetchBaseUpper) << 32;
207 windows.prefetch.limit |= uint64_t(prefetchLimitUpper) << 32;
209 windows.prefetch.open = windows.prefetch.base <= windows.prefetch.limit;
215bool bridgesForward(uint8_t bus, uint64_t address, uint64_t bytes,
bool io,
bool prefetch) {
216 while (bus != host.firstBus) {
217 UpstreamBridge bridge = {};
218 BridgeWindows windows;
219 if (!findUpstreamBridge(bus, bridge) || !readBridgeWindows(bridge, windows)) {
222 bool forwarded =
false;
224 forwarded = windows.io.contains(address, bytes);
225 }
else if (prefetch) {
227 windows.prefetch.contains(address, bytes) || windows.memory.contains(address, bytes);
229 forwarded = windows.memory.contains(address, bytes);
247 if (!VirtDeviceTree::pciHost(host)) {
256 ERROR(
"PCI: could not map ECAM host at " <<
Hex << host.base);
259 configAvailable =
true;
261 while (windowCount < 8 && VirtDeviceTree::pciWindow(windowCount, window)) {
262 windows[windowCount] = window;
263 nextAddress[windowCount] = window.pciBase;
266 NOTICE(
"PCI: ECAM host " <<
Hex << host.base <<
".." << host.base + host.size <<
Dec <<
" buses "
267 << host.firstBus <<
".." << host.lastBus);
271 uint32_t value = 0xffffffffU;
272 readConfig32(device, uint16_t(offset) * 4, value);
277 uint32_t value = 0xffffffffU;
278 read(bus, device, function, uint16_t(offset) * 4, 4, value);
283 writeConfig32(device, uint16_t(offset) * 4, value);
288 write(bus, device, function, uint16_t(offset) * 4, 4, value);
291bool PciBus::readConfig8(
Device* device, uint16_t offset, uint8_t& value) {
293 if (!readFunction(device, offset, 1, data)) {
300bool PciBus::readConfig16(
Device* device, uint16_t offset, uint16_t& value) {
302 if (!readFunction(device, offset, 2, data)) {
309bool PciBus::readConfig32(
Device* device, uint16_t offset, uint32_t& value) {
310 return readFunction(device, offset, 4, value);
313bool PciBus::writeConfig8(
Device* device, uint16_t offset, uint8_t value) {
314 return writeFunction(device, offset, 1, value);
317bool PciBus::writeConfig16(
Device* device, uint16_t offset, uint16_t value) {
318 return writeFunction(device, offset, 2, value);
321bool PciBus::writeConfig32(
Device* device, uint16_t offset, uint32_t value) {
322 return writeFunction(device, offset, 4, value);
329bool PciBus::hasDmaRemapping(
Device*)
const {
349bool PciBus::hasDmaIsolation(
Device*)
const {
354 DmaMapping& mapping) {
357 uint64_t{physical} > 0xffffffffULL - (bytes - 1)) {
360 mapping.m_Device = device;
361 mapping.m_Address =
static_cast<uint32_t
>(physical);
365void PciBus::unmapDmaPage(
Device*, uint16_t) {}
367bool PciBus::reserveLegacyInterrupt(uint8_t) {
371bool PciBus::updateCommand(
Device* device, uint16_t clearBits, uint16_t setBits) {
376 uint16_t command = 0;
377 if (!readConfig16(device, 4, command)) {
380 const uint16_t desired = (command & ~clearBits) | setBits;
381 return writeConfig16(device, 4, desired) && readConfig16(device, 4, command) &&
386 bool requireLegacyInterrupt) {
387 FunctionConfig function{device};
389 !PciFunctionState::inspect(function, state,
false, device->getPhysicalFunction())) {
392 return !requireLegacyInterrupt || state.interruptPin;
396 FunctionConfig function{device};
397 return PciFunctionState::disableMessageInterrupts(function, state);
399bool PciBus::enableMsi(
Device* device, uint64_t address, uint16_t data) {
400 FunctionConfig function{device};
403 PciFunctionState::inspect(function, state,
false, device->getPhysicalFunction()) &&
404 PciFunctionState::enableMsi(function, state, address, data);
406bool PciBus::disableMsi(
Device* device) {
407 FunctionConfig function{device};
410 PciFunctionState::inspect(function, state,
false, device->getPhysicalFunction()) &&
411 PciFunctionState::disableMsi(function, state);
413bool PciBus::enableMsix(
Device* device, uint64_t address, uint32_t data) {
414 return enableMsixVectors(device, address, &data, 1);
416bool PciBus::enableMsixVectors(
Device* device, uint64_t address,
const uint32_t* data,
size_t count,
417 bool* touched,
const uint64_t* addresses) {
421 FunctionConfig function{device};
424 if (!device || !data || !count ||
425 !PciFunctionState::inspect(function, state,
false, device->getPhysicalFunction()) ||
426 !PciFunctionState::msixTable(function, state, table) || count > table.vectors) {
429 IoBase* io = PciFunctionState::msixTableIo(device, state, table,
true);
433 return io && PciFunctionState::enableMsixVectors(function, state, *io, table.offset, address,
434 data, count, addresses);
436bool PciBus::setMsixVectorMask(
Device* device,
size_t index,
bool masked) {
437 FunctionConfig function{device};
441 !PciFunctionState::inspect(function, state,
false, device->getPhysicalFunction()) ||
442 !PciFunctionState::msixTable(function, state, table) || index >= table.vectors) {
445 IoBase* io = PciFunctionState::msixTableIo(device, state, table,
false);
447 PciFunctionState::setMsixVectorMask(function, state, *io, table.offset, index, masked);
449bool PciBus::disableMsix(
Device* device) {
450 FunctionConfig function{device};
454 !PciFunctionState::inspect(function, state,
false, device->getPhysicalFunction()) ||
455 !PciFunctionState::msixTable(function, state, table)) {
458 IoBase* io = PciFunctionState::msixTableIo(device, state, table,
false);
459 return io && PciFunctionState::disableMsix(function, state, *io, table.offset);
463 FunctionConfig function{device};
465 PciFunctionState::resourcesUnchanged(function, state, device->getPhysicalFunction());
469 return VirtDeviceTree::pciTranslate(pciAddress, bytes, io, cpuPhysical);
473 if (!configAvailable) {
476 first =
static_cast<uint8_t
>(host.firstBus);
477 last =
static_cast<uint8_t
>(host.lastBus);
481uint32_t PciBus::interruptRoute(uint8_t bus, uint8_t device, uint8_t function, uint8_t pin) {
482 if (!configAvailable || bus < host.firstBus || bus > host.lastBus || device >= 32 ||
483 function >= 8 || pin < 1 || pin > 4) {
486 while (bus != host.firstBus) {
487 UpstreamBridge bridge = {};
488 if (!findUpstreamBridge(bus, bridge)) {
491 pin = ((pin - 1 + device) & 3U) + 1;
493 device = bridge.device;
494 function = bridge.function;
496 return VirtDeviceTree::pciInterrupt(bus, device, function, pin);
501 if (!device || index >= 6) {
504 const bool io = low & 1U;
505 const uint32_t type = (low >> 1) & 3U;
509 if (!io && type == 3) {
512 const bool wide = !io && type == 2;
513 if (wide && index == 5) {
516 const uint64_t width = io ? 0xffffULL
517 : type == 1 ? 0xfffffULL
520 const uint64_t addressMask = width & (io ? ~uint64_t{3} : ~uint64_t{15});
521 const uint64_t original = ((wide ? uint64_t(high) << 32 : 0) | low) & addressMask;
522 const uint64_t mask = ((wide ? uint64_t(maskHigh) << 32 : 0) | maskLow) & addressMask;
523 const uint64_t bytes = ((~mask) & width) + 1;
524 if (!bytes || (bytes & (bytes - 1))) {
527 const uint8_t bus = device->getPciBusPosition();
528 const bool behindBridge = bus != host.firstBus;
529 const bool prefetchable = !io && (low & 8U);
530 if (behindBridge && !original) {
536 for (
size_t i = 0; i < windowCount; ++i) {
538 if ((window.space == 0x01000000) != io || original < window.pciBase ||
539 original - window.pciBase >= window.size ||
540 bytes > window.size - (original - window.pciBase) ||
541 (behindBridge && !io && !prefetchable && window.space == 0x03000000) ||
542 (behindBridge && original && !bridgesForward(bus, original, bytes, io, prefetchable))) {
546 const uint64_t end = original + bytes;
547 if (nextAddress[i] < end) {
548 nextAddress[i] = end;
557 for (
size_t i = 0; i < windowCount; ++i) {
559 if ((window.space == 0x01000000) != io) {
562 const uint64_t cursor = nextAddress[i] ? nextAddress[i] : bytes;
563 const uint64_t candidate = (cursor + bytes - 1) & ~(bytes - 1);
564 if (candidate < cursor || candidate < window.pciBase ||
565 candidate - window.pciBase >= window.size ||
566 bytes > window.size - (candidate - window.pciBase) || candidate > width ||
567 bytes - 1 > width - candidate) {
570 uint16_t command = 0;
571 if (!readConfig16(device, 4, command) || !writeConfig16(device, 4, command & ~3U)) {
574 const uint32_t assignedLow =
575 static_cast<uint32_t
>((candidate & addressMask) | (low & ~uint32_t(addressMask)));
576 const uint16_t offset = 0x10 + index * 4;
577 uint32_t actualLow = 0;
578 uint32_t actualHigh = 0;
579 const bool assigned =
580 writeConfig32(device, offset, assignedLow) &&
581 (!wide || writeConfig32(device, offset + 4, candidate >> 32)) &&
582 readConfig32(device, offset, actualLow) &&
583 (!wide || readConfig32(device, offset + 4, actualHigh)) &&
584 (actualLow & uint32_t(addressMask)) == (assignedLow & uint32_t(addressMask)) &&
585 (!wide || actualHigh ==
static_cast<uint32_t
>(candidate >> 32));
587 writeConfig32(device, offset, low);
589 writeConfig32(device, offset + 4, high);
592 if (!writeConfig16(device, 4, command)) {
598 nextAddress[i] = candidate + bytes;
Abstrace base class for hardware I/O capabilities.
uint32_t readConfigSpace(Device *pDev, uint8_t offset)
bool isolatedDmaIdle(Device *device) const
bool translateAddress(uint64_t pciAddress, uint64_t bytes, bool io, uint64_t &cpuPhysical)
bool attachIsolatedDma(Device *device)
bool busRange(uint8_t &first, uint8_t &last)
void writeConfigSpace(Device *pDev, uint8_t offset, uint32_t data)
bool detachDisabledIsolatedDma(Device *device)
bool attachDmaRemapping(Device *device)
bool assignBar(Device *device, uint8_t index, uint32_t low, uint32_t high, uint32_t maskLow, uint32_t maskHigh)
bool detachIsolatedDma(Device *device)
bool mapDmaPage(Device *device, physical_uintptr_t physical, size_t bytes, DmaMapping &mapping)
static PhysicalMemoryManager & instance()
static constexpr size_t getPageSize() PURE
static const size_t force
static const size_t nonRamMemory
static constexpr size_t getPageSize() noexcept
static const size_t CacheDisable
static const size_t KernelMode
static const size_t Write