The Pedigree Project 0.1
mach_pc/Pci.cc
1/*
2 * Copyright (c) 2008-2014, Pedigree Developers
3 *
4 * Please see the CONTRIB file in the root of the source tree for a full
5 * list of contributors.
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
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"
33
34#if ACPI
35#include "Acpi.h"
36#endif
37#if X64 && ACPI
38#include "IntelIommu.h"
39#endif
40#include "Pic.h"
41
42namespace {
43IoPort configSpace("PCI config space");
44Spinlock configLock(false);
45Spinlock ecamLock(false);
46bool configAvailable = false;
47MemoryMappedIo* ecamBuses[256] = {};
48PciConfigAccess<IoPort, Spinlock> config(configSpace, configLock);
49
50MemoryMappedIo* ecamBus(uint8_t bus) {
51#if ACPI
52 MemoryMappedIo* mapped = __atomic_load_n(&ecamBuses[bus], __ATOMIC_ACQUIRE);
53 if (mapped) {
54 return mapped;
55 }
56 LockGuard<Spinlock> guard(ecamLock);
57 mapped = __atomic_load_n(&ecamBuses[bus], __ATOMIC_ACQUIRE);
58 if (mapped) {
59 return mapped;
60 }
61 uint64_t physical = 0;
62 if (!Acpi::instance().pciConfigurationAddress(bus, physical)) {
63 return nullptr;
64 }
65 auto* candidate = new MemoryMappedIo("PCI ECAM");
66 constexpr size_t BusBytes = 1U << 20;
67 if (!PhysicalMemoryManager::instance().allocateRegion(
68 *candidate, BusBytes / PhysicalMemoryManager::getPageSize(),
73 physical)) {
74 WARNING("PCI: could not map ECAM bus " << Dec << bus << " at " << Hex << physical);
75 delete candidate;
76 return nullptr;
77 }
78 __atomic_store_n(&ecamBuses[bus], candidate, __ATOMIC_RELEASE);
79 return candidate;
80#else
81 return nullptr;
82#endif
83}
84
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));
88}
89
90bool readEcam(uint8_t bus, uint8_t device, uint8_t function, uint16_t offset, uint8_t width,
91 uint32_t& value) {
92 if (!ecamAccessValid(device, function, offset, width)) {
93 return false;
94 }
95 MemoryMappedIo* io = ecamBus(bus);
96 if (!io) {
97 return false;
98 }
99 const size_t position = (size_t(device) << 15) | (size_t(function) << 12) | offset;
100 if (width == 1) {
101 value = io->read8(position);
102 } else if (width == 2) {
103 value = io->read16(position);
104 } else {
105 value = io->read32(position);
106 }
107 return true;
108}
109
110bool writeEcam(uint8_t bus, uint8_t device, uint8_t function, uint16_t offset, uint8_t width,
111 uint32_t value) {
112 if (!ecamAccessValid(device, function, offset, width)) {
113 return false;
114 }
115 MemoryMappedIo* io = ecamBus(bus);
116 if (!io) {
117 return false;
118 }
119 const size_t position = (size_t(device) << 15) | (size_t(function) << 12) | offset;
120 if (width == 1) {
121 io->write8(value, position);
122 } else if (width == 2) {
123 io->write16(value, position);
124 } else {
125 io->write32(value, position);
126 }
127 return true;
128}
129
130struct FunctionConfig {
131 Device* device;
132 bool read8(uint16_t offset, uint8_t& value) {
133 return PciBus::instance().readConfig8(device, offset, value);
134 }
135 bool read16(uint16_t offset, uint16_t& value) {
136 return PciBus::instance().readConfig16(device, offset, value);
137 }
138 bool read32(uint16_t offset, uint32_t& value) {
139 return PciBus::instance().readConfig32(device, offset, value);
140 }
141 bool write16(uint16_t offset, uint16_t value) {
142 return PciBus::instance().writeConfig16(device, offset, value);
143 }
144 bool write32(uint16_t offset, uint32_t value) {
145 return PciBus::instance().writeConfig32(device, offset, value);
146 }
147};
148
149bool readFunction(Device* device, uint16_t offset, uint8_t width, uint32_t& value) {
150 if (!device) {
151 return false;
152 }
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);
158 }
159 return readEcam(bus, slot, function, offset, width, value);
160}
161bool writeFunction(Device* device, uint16_t offset, uint8_t width, uint32_t value) {
162 if (!device) {
163 return false;
164 }
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);
170 }
171 return writeEcam(bus, slot, function, offset, width, value);
172}
173} // namespace
174
175PciBus PciBus::m_Instance;
176PciBus::PciBus() {}
177PciBus::~PciBus() {}
178
180 if (configSpace.allocate(0xCF8, 8)) {
181 LockGuard<Spinlock> guard(configLock);
182 configSpace.write32(0x80000000, 0);
183 configAvailable = configSpace.read32(0) == 0x80000000;
184 }
185 // ACPI tables may be published after the early device-tree setup.
186 if (!configAvailable) {
187 NOTICE("PCI: legacy configuration ports unavailable; waiting for ACPI ECAM");
188 }
189}
190
191uint32_t PciBus::readConfigSpace(Device* device, uint8_t offset) {
192 uint32_t value = 0xffffffffU;
193 readConfig32(device, uint16_t{offset} * 4, value);
194 return value;
195}
196uint32_t PciBus::readConfigSpace(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset) {
197 uint32_t value = 0xffffffffU;
198 if (configAvailable && offset < 64) {
199 config.read(bus, device, function, uint16_t{offset} * 4, 4, value);
200 } else {
201 readEcam(bus, device, function, uint16_t{offset} * 4, 4, value);
202 }
203 return value;
204}
205void PciBus::writeConfigSpace(Device* device, uint8_t offset, uint32_t value) {
206 writeConfig32(device, uint16_t{offset} * 4, value);
207}
208void PciBus::writeConfigSpace(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset,
209 uint32_t value) {
210 if (configAvailable && offset < 64) {
211 config.write(bus, device, function, uint16_t{offset} * 4, 4, value);
212 } else {
213 writeEcam(bus, device, function, uint16_t{offset} * 4, 4, value);
214 }
215}
216bool PciBus::readConfig8(Device* device, uint16_t offset, uint8_t& value) {
217 uint32_t data = 0;
218 if (!readFunction(device, offset, 1, data))
219 return false;
220 value = data;
221 return true;
222}
223bool PciBus::readConfig16(Device* device, uint16_t offset, uint16_t& value) {
224 uint32_t data = 0;
225 if (!readFunction(device, offset, 2, data))
226 return false;
227 value = data;
228 return true;
229}
230bool PciBus::readConfig32(Device* device, uint16_t offset, uint32_t& value) {
231 return readFunction(device, offset, 4, value);
232}
233bool PciBus::writeConfig8(Device* device, uint16_t offset, uint8_t value) {
234 return writeFunction(device, offset, 1, value);
235}
236bool PciBus::writeConfig16(Device* device, uint16_t offset, uint16_t value) {
237 return writeFunction(device, offset, 2, value);
238}
239bool PciBus::writeConfig32(Device* device, uint16_t offset, uint32_t value) {
240 return writeFunction(device, offset, 4, value);
241}
242bool PciBus::updateCommand(Device* device, uint16_t clearBits, uint16_t setBits) {
243 if (!device) {
244 return false;
245 }
246 if (configAvailable) {
247 return config.updateCommand(device->getPciBusPosition(), device->getPciDevicePosition(),
248 device->getPciFunctionNumber(), clearBits, setBits);
249 }
250 uint16_t command = 0;
251 if (!readConfig16(device, 4, command)) {
252 return false;
253 }
254 const uint16_t desired = (command & ~clearBits) | setBits;
255 return writeConfig16(device, 4, desired) && readConfig16(device, 4, command) &&
256 command == desired;
257}
258bool PciBus::inspectFunction(Device* device, PciFunctionState::State& state,
259 bool requireLegacyInterrupt) {
260 FunctionConfig function{device};
261 return device &&
262 PciFunctionState::inspect(function, state, requireLegacyInterrupt,
263 device->getPhysicalFunction()) &&
264 (!requireLegacyInterrupt || state.interruptLine == device->getInterruptNumber());
265}
266bool PciBus::disableMessageInterrupts(Device* device, const PciFunctionState::State& state) {
267 FunctionConfig function{device};
268 return PciFunctionState::disableMessageInterrupts(function, state);
269}
270bool PciBus::enableMsi(Device* device, uint64_t address, uint16_t data) {
271 FunctionConfig function{device};
273 return device &&
274 PciFunctionState::inspect(function, state, false, device->getPhysicalFunction()) &&
275 PciFunctionState::enableMsi(function, state, address, data);
276}
277bool PciBus::disableMsi(Device* device) {
278 FunctionConfig function{device};
280 return device &&
281 PciFunctionState::inspect(function, state, false, device->getPhysicalFunction()) &&
282 PciFunctionState::disableMsi(function, state);
283}
284bool PciBus::enableMsix(Device* device, uint64_t address, uint32_t data) {
285 return enableMsixVectors(device, address, &data, 1);
286}
287bool PciBus::enableMsixVectors(Device* device, uint64_t address, const uint32_t* data, size_t count,
288 bool* touched, const uint64_t* addresses) {
289 if (touched) {
290 *touched = false;
291 }
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) {
298 return false;
299 }
300 IoBase* io = PciFunctionState::msixTableIo(device, state, table, true);
301 if (touched && io) {
302 *touched = true;
303 }
304 return io && PciFunctionState::enableMsixVectors(function, state, *io, table.offset, address,
305 data, count, addresses);
306}
307bool PciBus::setMsixVectorMask(Device* device, size_t index, bool masked) {
308 FunctionConfig function{device};
311 if (!device ||
312 !PciFunctionState::inspect(function, state, false, device->getPhysicalFunction()) ||
313 !PciFunctionState::msixTable(function, state, table) || index >= table.vectors) {
314 return false;
315 }
316 IoBase* io = PciFunctionState::msixTableIo(device, state, table, false);
317 return io &&
318 PciFunctionState::setMsixVectorMask(function, state, *io, table.offset, index, masked);
319}
320bool PciBus::disableMsix(Device* device) {
321 FunctionConfig function{device};
324 if (!device ||
325 !PciFunctionState::inspect(function, state, false, device->getPhysicalFunction()) ||
326 !PciFunctionState::msixTable(function, state, table)) {
327 return false;
328 }
329 IoBase* io = PciFunctionState::msixTableIo(device, state, table, false);
330 return io && PciFunctionState::disableMsix(function, state, *io, table.offset);
331}
332bool PciBus::resourcesUnchanged(Device* device, const PciFunctionState::State& state) {
333 FunctionConfig function{device};
334 return device &&
335 PciFunctionState::resourcesUnchanged(function, state, device->getPhysicalFunction());
336}
337
338bool PciBus::translateAddress(uint64_t pciAddress, uint64_t, bool, uint64_t& cpuPhysical) {
339 cpuPhysical = pciAddress;
340 return true;
341}
342
343bool PciBus::busRange(uint8_t& first, uint8_t& last) {
344 if (configAvailable) {
345 first = 0;
346 last = 255;
347 return true;
348 }
349#if ACPI
350 return Acpi::instance().pciBusRange(first, last);
351#else
352 return false;
353#endif
354}
355
356uint32_t PciBus::interruptRoute(uint8_t, uint8_t, uint8_t, uint8_t) {
357 return 0;
358}
359
360bool PciBus::assignBar(Device*, uint8_t, uint32_t, uint32_t, uint32_t, uint32_t) {
361 return true;
362}
363
365#if X64 && ACPI
366 return device && IntelIommu::instance().attach(device);
367#else
368 (void)device;
369 return false;
370#endif
371}
372
373bool PciBus::hasDmaRemapping(Device* device) const {
374#if X64 && ACPI
375 return device && IntelIommu::instance().attached(device);
376#else
377 (void)device;
378 return false;
379#endif
380}
381
383#if X64 && ACPI
384 return device && IntelIommu::instance().attach(device, true);
385#else
386 (void)device;
387 return false;
388#endif
389}
390
392#if X64 && ACPI
393 return device && IntelIommu::instance().detachIsolated(device);
394#else
395 (void)device;
396 return false;
397#endif
398}
399
400bool PciBus::isolatedDmaIdle(Device* device) const {
401#if X64 && ACPI
402 return device && IntelIommu::instance().isolatedIdle(device);
403#else
404 (void)device;
405 return false;
406#endif
407}
408
410#if X64 && ACPI
411 Device* pf = device ? device->getPhysicalFunction() : nullptr;
413 uint16_t control = 0;
414 if (!pf ||
415 findExtendedCapability(pf, 0x10, capability) != PciExtendedCapabilities::FindResult::Found ||
416 !readConfig16(pf, capability.offset + 8, control) || (control & 9)) {
417 return false;
418 }
419 return IntelIommu::instance().detachIsolated(device, true);
420#else
421 (void)device;
422 return false;
423#endif
424}
425
426bool PciBus::hasDmaIsolation(Device* device) const {
427#if X64 && ACPI
428 return device && IntelIommu::instance().isolated(device);
429#else
430 (void)device;
431 return false;
432#endif
433}
434
435bool PciBus::mapDmaPage(Device* device, physical_uintptr_t physical, size_t bytes,
436 DmaMapping& mapping) {
437 constexpr uint64_t HighestDmaAddress = 0xffffffffULL;
438 if (!device || mapping.m_Device || !physical || !bytes || bytes > TargetInfo::getPageSize() ||
439 (physical & (TargetInfo::getPageSize() - 1))) {
440 return false;
441 }
442
443 uint32_t address = 0;
444 uint16_t token = 0;
445 bool isolated = false;
446#if X64 && ACPI
447 isolated = IntelIommu::instance().isolated(device);
448#endif
449 if (!isolated && uint64_t{physical} <= HighestDmaAddress - (bytes - 1)) {
450 address = static_cast<uint32_t>(physical);
451 } else {
452#if X64 && ACPI
453 if (!IntelIommu::instance().mapPage(device, physical, address, token)) {
454 return false;
455 }
456#else
457 return false;
458#endif
459 }
460 mapping.m_Device = device;
461 mapping.m_Address = address;
462 mapping.m_Token = token;
463 return true;
464}
465
466void PciBus::unmapDmaPage(Device* device, uint16_t token) {
467#if X64 && ACPI
468 if (device && token) {
469 IntelIommu::instance().unmapPage(device, token);
470 }
471#else
472 (void)device;
473 (void)token;
474#endif
475}
476
477bool PciBus::reserveLegacyInterrupt(uint8_t irq) {
478 return Machine::instance().getIrqManager() == &Pic::instance() &&
480}
Abstrace base class for hardware I/O capabilities.
Definition IoBase.h:32
I/O port range.
Definition IoPort.h:33
Memory mapped I/O range.
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)
Definition Pci.h:31
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)
void initialise()
bool detachIsolatedDma(Device *device)
bool mapDmaPage(Device *device, physical_uintptr_t physical, size_t bytes, DmaMapping &mapping)
static PhysicalMemoryManager & instance()
static Pic & instance()
Definition Pic.h:56
bool reservePciRoute(uint8_t irq)
Definition Pic.cc:232
static constexpr size_t getPageSize() noexcept
Definition TargetInfo.h:40
@ Dec
Definition Log.h:126
@ Hex
Definition Log.h:124