20#include "pedigree/kernel/LockGuard.h"
21#include "pedigree/kernel/Log.h"
22#include "pedigree/kernel/Spinlock.h"
23#include "pedigree/kernel/TargetInfo.h"
24#include "pedigree/kernel/machine/Device.h"
25#include "pedigree/kernel/machine/Machine.h"
26#include "pedigree/kernel/machine/Pci.h"
27#include "pedigree/kernel/machine/PciConfigAccess.h"
28#include "pedigree/kernel/machine/PciMessageBar.h"
29#include "pedigree/kernel/processor/IoPort.h"
30#include "pedigree/kernel/processor/MemoryMappedIo.h"
31#include "pedigree/kernel/processor/PhysicalMemoryManager.h"
32#include "pedigree/kernel/processor/VirtualAddressSpace.h"
38#include "IntelIommu.h"
43IoPort configSpace(
"PCI config space");
46bool configAvailable =
false;
52 MemoryMappedIo* mapped = __atomic_load_n(&ecamBuses[bus], __ATOMIC_ACQUIRE);
57 mapped = __atomic_load_n(&ecamBuses[bus], __ATOMIC_ACQUIRE);
61 uint64_t physical = 0;
62 if (!Acpi::instance().pciConfigurationAddress(bus, physical)) {
66 constexpr size_t BusBytes = 1U << 20;
74 WARNING(
"PCI: could not map ECAM bus " <<
Dec << bus <<
" at " <<
Hex << physical);
78 __atomic_store_n(&ecamBuses[bus], candidate, __ATOMIC_RELEASE);
85bool ecamAccessValid(uint8_t device, uint8_t function, uint16_t offset, uint8_t width) {
86 return device < 32 && function < 8 && (width == 1 || width == 2 || width == 4) &&
87 offset <= 4096 - width && !(offset & (width - 1));
90bool readEcam(uint8_t bus, uint8_t device, uint8_t function, uint16_t offset, uint8_t width,
92 if (!ecamAccessValid(device, function, offset, width)) {
99 const size_t position = (size_t(device) << 15) | (
size_t(function) << 12) | offset;
101 value = io->
read8(position);
102 }
else if (width == 2) {
103 value = io->
read16(position);
105 value = io->
read32(position);
110bool writeEcam(uint8_t bus, uint8_t device, uint8_t function, uint16_t offset, uint8_t width,
112 if (!ecamAccessValid(device, function, offset, width)) {
119 const size_t position = (size_t(device) << 15) | (
size_t(function) << 12) | offset;
121 io->
write8(value, position);
122 }
else if (width == 2) {
130struct FunctionConfig {
132 bool read8(uint16_t offset, uint8_t& value) {
133 return PciBus::instance().readConfig8(device, offset, value);
135 bool read16(uint16_t offset, uint16_t& value) {
136 return PciBus::instance().readConfig16(device, offset, value);
138 bool read32(uint16_t offset, uint32_t& value) {
139 return PciBus::instance().readConfig32(device, offset, value);
141 bool write16(uint16_t offset, uint16_t value) {
142 return PciBus::instance().writeConfig16(device, offset, value);
144 bool write32(uint16_t offset, uint32_t value) {
145 return PciBus::instance().writeConfig32(device, offset, value);
149bool readFunction(
Device* device, uint16_t offset, uint8_t width, uint32_t& value) {
153 const uint8_t bus = device->getPciBusPosition();
154 const uint8_t slot = device->getPciDevicePosition();
155 const uint8_t function = device->getPciFunctionNumber();
156 if (configAvailable && offset <= 256 - width) {
157 return config.read(bus, slot, function, offset, width, value);
159 return readEcam(bus, slot, function, offset, width, value);
161bool writeFunction(
Device* device, uint16_t offset, uint8_t width, uint32_t value) {
165 const uint8_t bus = device->getPciBusPosition();
166 const uint8_t slot = device->getPciDevicePosition();
167 const uint8_t function = device->getPciFunctionNumber();
168 if (configAvailable && offset <= 256 - width) {
169 return config.write(bus, slot, function, offset, width, value);
171 return writeEcam(bus, slot, function, offset, width, value);
180 if (configSpace.allocate(0xCF8, 8)) {
182 configSpace.write32(0x80000000, 0);
183 configAvailable = configSpace.read32(0) == 0x80000000;
186 if (!configAvailable) {
187 NOTICE(
"PCI: legacy configuration ports unavailable; waiting for ACPI ECAM");
192 uint32_t value = 0xffffffffU;
193 readConfig32(device, uint16_t{offset} * 4, value);
197 uint32_t value = 0xffffffffU;
198 if (configAvailable && offset < 64) {
199 config.read(bus, device, function, uint16_t{offset} * 4, 4, value);
201 readEcam(bus, device, function, uint16_t{offset} * 4, 4, value);
206 writeConfig32(device, uint16_t{offset} * 4, value);
210 if (configAvailable && offset < 64) {
211 config.write(bus, device, function, uint16_t{offset} * 4, 4, value);
213 writeEcam(bus, device, function, uint16_t{offset} * 4, 4, value);
216bool PciBus::readConfig8(
Device* device, uint16_t offset, uint8_t& value) {
218 if (!readFunction(device, offset, 1, data))
223bool PciBus::readConfig16(
Device* device, uint16_t offset, uint16_t& value) {
225 if (!readFunction(device, offset, 2, data))
230bool PciBus::readConfig32(
Device* device, uint16_t offset, uint32_t& value) {
231 return readFunction(device, offset, 4, value);
233bool PciBus::writeConfig8(
Device* device, uint16_t offset, uint8_t value) {
234 return writeFunction(device, offset, 1, value);
236bool PciBus::writeConfig16(
Device* device, uint16_t offset, uint16_t value) {
237 return writeFunction(device, offset, 2, value);
239bool PciBus::writeConfig32(
Device* device, uint16_t offset, uint32_t value) {
240 return writeFunction(device, offset, 4, value);
242bool PciBus::updateCommand(
Device* device, uint16_t clearBits, uint16_t setBits) {
246 if (configAvailable) {
247 return config.updateCommand(device->getPciBusPosition(), device->getPciDevicePosition(),
248 device->getPciFunctionNumber(), clearBits, setBits);
250 uint16_t command = 0;
251 if (!readConfig16(device, 4, command)) {
254 const uint16_t desired = (command & ~clearBits) | setBits;
255 return writeConfig16(device, 4, desired) && readConfig16(device, 4, command) &&
259 bool requireLegacyInterrupt) {
260 FunctionConfig function{device};
262 PciFunctionState::inspect(function, state, requireLegacyInterrupt,
263 device->getPhysicalFunction()) &&
264 (!requireLegacyInterrupt || state.interruptLine == device->getInterruptNumber());
267 FunctionConfig function{device};
268 return PciFunctionState::disableMessageInterrupts(function, state);
270bool PciBus::enableMsi(
Device* device, uint64_t address, uint16_t data) {
271 FunctionConfig function{device};
274 PciFunctionState::inspect(function, state,
false, device->getPhysicalFunction()) &&
275 PciFunctionState::enableMsi(function, state, address, data);
277bool PciBus::disableMsi(
Device* device) {
278 FunctionConfig function{device};
281 PciFunctionState::inspect(function, state,
false, device->getPhysicalFunction()) &&
282 PciFunctionState::disableMsi(function, state);
284bool PciBus::enableMsix(
Device* device, uint64_t address, uint32_t data) {
285 return enableMsixVectors(device, address, &data, 1);
287bool PciBus::enableMsixVectors(
Device* device, uint64_t address,
const uint32_t* data,
size_t count,
288 bool* touched,
const uint64_t* addresses) {
292 FunctionConfig function{device};
295 if (!device || !data || !count ||
296 !PciFunctionState::inspect(function, state,
false, device->getPhysicalFunction()) ||
297 !PciFunctionState::msixTable(function, state, table) || count > table.vectors) {
300 IoBase* io = PciFunctionState::msixTableIo(device, state, table,
true);
304 return io && PciFunctionState::enableMsixVectors(function, state, *io, table.offset, address,
305 data, count, addresses);
307bool PciBus::setMsixVectorMask(
Device* device,
size_t index,
bool masked) {
308 FunctionConfig function{device};
312 !PciFunctionState::inspect(function, state,
false, device->getPhysicalFunction()) ||
313 !PciFunctionState::msixTable(function, state, table) || index >= table.vectors) {
316 IoBase* io = PciFunctionState::msixTableIo(device, state, table,
false);
318 PciFunctionState::setMsixVectorMask(function, state, *io, table.offset, index, masked);
320bool PciBus::disableMsix(
Device* device) {
321 FunctionConfig function{device};
325 !PciFunctionState::inspect(function, state,
false, device->getPhysicalFunction()) ||
326 !PciFunctionState::msixTable(function, state, table)) {
329 IoBase* io = PciFunctionState::msixTableIo(device, state, table,
false);
330 return io && PciFunctionState::disableMsix(function, state, *io, table.offset);
333 FunctionConfig function{device};
335 PciFunctionState::resourcesUnchanged(function, state, device->getPhysicalFunction());
339 cpuPhysical = pciAddress;
344 if (configAvailable) {
350 return Acpi::instance().pciBusRange(first, last);
356uint32_t PciBus::interruptRoute(uint8_t, uint8_t, uint8_t, uint8_t) {
366 return device && IntelIommu::instance().attach(device);
373bool PciBus::hasDmaRemapping(
Device* device)
const {
375 return device && IntelIommu::instance().attached(device);
384 return device && IntelIommu::instance().attach(device,
true);
393 return device && IntelIommu::instance().detachIsolated(device);
402 return device && IntelIommu::instance().isolatedIdle(device);
411 Device* pf = device ? device->getPhysicalFunction() :
nullptr;
413 uint16_t control = 0;
415 findExtendedCapability(pf, 0x10, capability) != PciExtendedCapabilities::FindResult::Found ||
416 !readConfig16(pf, capability.offset + 8, control) || (control & 9)) {
419 return IntelIommu::instance().detachIsolated(device,
true);
426bool PciBus::hasDmaIsolation(
Device* device)
const {
428 return device && IntelIommu::instance().isolated(device);
437 constexpr uint64_t HighestDmaAddress = 0xffffffffULL;
443 uint32_t address = 0;
445 bool isolated =
false;
447 isolated = IntelIommu::instance().isolated(device);
449 if (!isolated && uint64_t{physical} <= HighestDmaAddress - (bytes - 1)) {
450 address =
static_cast<uint32_t
>(physical);
453 if (!IntelIommu::instance().mapPage(device, physical, address, token)) {
460 mapping.m_Device = device;
461 mapping.m_Address = address;
462 mapping.m_Token = token;
466void PciBus::unmapDmaPage(
Device* device, uint16_t token) {
468 if (device && token) {
469 IntelIommu::instance().unmapPage(device, token);
477bool PciBus::reserveLegacyInterrupt(uint8_t irq) {
478 return Machine::instance().getIrqManager() == &
Pic::instance() &&
Abstrace base class for hardware I/O capabilities.
virtual void write8(uint8_t value, size_t offset=0)
virtual uint32_t read32(size_t offset=0)
virtual uint16_t read16(size_t offset=0)
virtual uint8_t read8(size_t offset=0)
virtual void write16(uint16_t value, size_t offset=0)
virtual void write32(uint32_t value, size_t offset=0)
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 const size_t continuous
static PhysicalMemoryManager & instance()
static constexpr size_t getPageSize() PURE
static const size_t force
static const size_t nonRamMemory
bool reservePciRoute(uint8_t irq)
static constexpr size_t getPageSize() noexcept
static const size_t CacheDisable
static const size_t KernelMode
static const size_t Write