3#include "pedigree/kernel/LockGuard.h"
4#include "pedigree/kernel/Log.h"
5#include "pedigree/kernel/machine/Device.h"
6#include "pedigree/kernel/machine/IrqHandler.h"
7#include "pedigree/kernel/machine/IrqManager.h"
8#include "pedigree/kernel/machine/Machine.h"
9#include "pedigree/kernel/machine/Pci.h"
10#include "pedigree/kernel/machine/PciAer.h"
11#include "pedigree/kernel/machine/PciExpress.h"
12#include "pedigree/kernel/machine/PciFirmware.h"
13#include "pedigree/kernel/panic.h"
14#include "pedigree/kernel/process/Mutex.h"
15#include "pedigree/kernel/time/Time.h"
16#include "pedigree/kernel/utilities/RequestQueue.h"
18#include "Enumeration.h"
21constexpr size_t MaxPorts = 16;
22constexpr uint32_t RootReporting = 7;
23constexpr uint32_t RootStatusBits = 0x7f;
24constexpr uint16_t SlotInterrupts = 0x103f;
25constexpr uint16_t SlotEvents = 0x10f;
27void waitFor(Time::Timestamp duration) {
28 const auto deadline = Time::getTicks() + duration;
29 for (
auto now = Time::getTicks(); now < deadline; now = Time::getTicks()) {
30 Time::delay(deadline - now);
35bool slotWorkAvailable();
36void scheduleSlot(Port* port);
40 bool read8(uint16_t offset, uint8_t& value) {
41 return PciBus::instance().readConfig8(device, offset, value);
43 bool read16(uint16_t offset, uint16_t& value) {
44 return PciBus::instance().readConfig16(device, offset, value);
46 bool read32(uint16_t offset, uint32_t& value) {
47 return PciBus::instance().readConfig32(device, offset, value);
49 bool write16(uint16_t offset, uint16_t value) {
50 return PciBus::instance().writeConfig16(device, offset, value);
52 bool write32(uint16_t offset, uint32_t value) {
53 return PciBus::instance().writeConfig32(device, offset, value);
57uint16_t requester(
Device* device) {
58 return (device->getPciBusPosition() << 8) | (device->getPciDevicePosition() << 3) |
59 device->getPciFunctionNumber();
62bool pciFunction(
Device* device) {
63 return device->getPciVendorId() && device->getPciVendorId() != 0xffff &&
64 device->getPciBusPosition() <= 255 && device->getPciDevicePosition() < 32 &&
65 device->getPciFunctionNumber() < 8;
68bool enableReporting(
Device* device) {
69 Config config{device};
71 const auto found = PciExpress::find(config, express);
72 if (found == PciExpress::FindResult::Absent) {
75 if (found != PciExpress::FindResult::Found ||
76 !PciFunctionState::writeVerified16(config, express.offset + 8,
77 express.deviceControl | 0xfU)) {
81 if (!config.read8(0x0e, header)) {
84 if ((header & 0x7fU) == 1) {
85 uint16_t control = 0, actual = 0;
88 if (!config.read16(0x3e, control) || !config.write16(0x3e, (control | 2U) & ~0x400U) ||
89 !config.read16(0x3e, actual) || ((actual ^ (control | 2U)) & ~0x400U)) {
98 void initialize(
Device* device) {
99 Config config{device};
101 uint32_t status = 0, buses = 0, reporting = 0;
102 uint16_t rootControl = 0;
103 auto& pci = PciBus::instance();
104 if (PciExpress::find(config, m_Express) != PciExpress::FindResult::Found ||
105 m_Express.type != PciExpress::Type::RootPort) {
109 const auto aer = pci.findExtendedCapability(device, PciAer::CapabilityId, m_Aer);
110 if (aer != PciExtendedCapabilities::FindResult::Found &&
111 aer != PciExtendedCapabilities::FindResult::Absent) {
114 bool aerUsable = aer == PciExtendedCapabilities::FindResult::Found;
115 if (aerUsable && (m_Aer.offset > 0x1000 - 0x38 ||
116 (m_Aer.next >= m_Aer.offset && m_Aer.next < m_Aer.offset + 0x38) ||
117 !config.read32(m_Aer.offset + 0x30, status) ||
118 !config.read32(m_Aer.offset + 0x2c, reporting))) {
121 if (!config.read32(0x18, buses) || !config.read16(m_Express.offset + 0x1c, rootControl) ||
122 !pci.inspectFunction(device, function,
false) || (rootControl & 8U)) {
123 WARNING(
"PCIe: root " <<
Hex << requester(device)
124 <<
" has unusable capabilities or PME users");
127 m_FirstBus = buses >> 8;
128 m_LastBus = buses >> 16;
129 const auto* root = PciFirmware::rootForBus(device->getPciBusPosition());
130 if (!root || uint8_t(buses) != device->getPciBusPosition() ||
131 m_FirstBus <= device->getPciBusPosition() || m_LastBus < m_FirstBus ||
132 m_LastBus > root->lastBus || (!function.msi && !function.msix)) {
136 aerUsable = aerUsable && !((status >> 27) & 0x1fU) &&
137 PciFirmware::requestNativeControl(
138 device->getPciBusPosition(),
139 PciFirmware::NativeAer | PciFirmware::NativePcieCapability);
140 bool slotUsable = m_Express.slotImplemented && !m_Express.interruptMessage &&
141 (m_Express.slotCapabilities & (1U << 6)) && slotWorkAvailable() &&
142 PciFirmware::requestNativeControl(
143 device->getPciBusPosition(),
144 PciFirmware::NativeHotplug | PciFirmware::NativePcieCapability);
145 if ((!aerUsable && !slotUsable) || (!aerUsable && (reporting & RootReporting)) ||
146 (!slotUsable && (m_Express.slotControl & SlotInterrupts))) {
147 WARNING(
"PCIe: root " <<
Hex << requester(device) <<
" lacks exclusive native ownership");
150 if (aerUsable && !setReporting(0)) {
151 panic(
"PCIe AER could not quiesce root reporting");
153 if (slotUsable && !slotCommand(SlotInterrupts, 0)) {
156 if (!pci.disableMessageInterrupts(device, function) || !pci.updateCommand(device, 0, 4U)) {
159 auto* irqManager = Machine::instance().getIrqManager();
162 uint16_t msiControl = 0, msixControl = 0;
163 const bool messageEnabled =
164 (function.msi && config.read16(function.msi + 2, msiControl) && (msiControl & 1U)) ||
165 (function.msix && config.read16(function.msix + 2, msixControl) && (msixControl & 0x8000U));
166 const bool selectedMessage =
167 !aerUsable || (config.read32(m_Aer.offset + 0x30, status) && !((status >> 27) & 0x1fU));
168 if (
id && messageEnabled && selectedMessage) {
169 if (aerUsable && enableReporting(device)) {
171 if (pciFunction(child) && !enableReporting(child)) {
172 WARNING(
"PCIe AER: could not enable reporting at " <<
Hex << requester(child));
176 auto callback = pedigree_std::make_callable(report);
179 m_AerReady = setReporting(RootReporting);
185 uint16_t slotStatus = 0;
186 if (!config.read16(m_Express.offset + 0x1a, slotStatus) ||
187 !config.write16(m_Express.offset + 0x1a, slotStatus & SlotEvents)) {
188 panic(
"PCIe: could not acknowledge inherited slot events");
190 uint16_t enables = (1U << 3) | (1U << 5);
191 if (m_Express.slotCapabilities & 1U) {
194 if (m_Express.slotCapabilities & 2U) {
197 if (m_Express.slotCapabilities & 4U) {
200 if (m_Express.linkCapabilities & (1U << 20)) {
206 if (slotCommand(SlotInterrupts, enables, enables & ((1U << 12) | 2U))) {
207 uint16_t enabled = 0;
208 if ((enables & (1U << 12)) && config.read16(m_Express.offset + 0x18, enabled) &&
209 !(enabled & (1U << 12))) {
210 WARNING(
"PCIe hotplug: root " <<
Hex << requester(device)
211 <<
" link-change notifications unavailable");
215 }
else if (!slotCommand(SlotInterrupts, 0)) {
216 panic(
"PCIe: could not disable an unavailable slot service");
220 if (!m_AerReady && !m_SlotReady) {
221 if (
id && !irqManager->unregisterHandler(
id,
this)) {
222 panic(
"PCIe: could not remove an inactive port interrupt handler");
227 NOTICE(
"PCIe AER: root " <<
Hex << requester(device) <<
" native reporting on IRQ " <<
id);
230 NOTICE(
"PCIe hotplug: root " <<
Hex << requester(device) <<
" native slot service on IRQ "
238 bool handled = slotInterrupt();
240 return handled ? IrqDisposition::Handled : IrqDisposition::NotHandled;
242 Config config{m_Device};
243 for (
unsigned pass = 0; pass < 8; ++pass) {
244 uint32_t status = 0, source = 0;
245 if (!config.read32(m_Aer.offset + 0x30, status) ||
246 !config.read32(m_Aer.offset + 0x34, source)) {
248 return IrqDisposition::Handled;
250 if (!(status & RootStatusBits)) {
251 return handled ? IrqDisposition::Handled : IrqDisposition::NotHandled;
256 const uint16_t
id = source >> 16;
257 const bool contained = contain(
id);
258 ERROR(
"PCIe AER: " << ((status & 0x40U) ?
"fatal" :
"nonfatal") <<
" source " <<
Dec
259 << (id >> 8) <<
":" << ((id >> 3) & 31U) <<
":" << (id & 7U) <<
Hex
260 <<
" root status=" << status <<
" bus master stopped=" << contained
261 <<
"; driver recovery required");
262 if (!contained || (status & 8U) ||
id == requester(m_Device)) {
265 acknowledge(
id,
false);
267 acknowledgeBranch(
false);
271 const uint16_t
id = source;
272 NOTICE(
"PCIe AER: correctable source " <<
Dec << (
id >> 8) <<
":" << ((
id >> 3) & 31U)
273 <<
":" << (
id & 7U) <<
Hex
274 <<
" root status=" << status);
275 acknowledge(
id,
true);
277 acknowledgeBranch(
true);
281 if (!config.write32(m_Aer.offset + 0x30, status & RootStatusBits)) {
283 return IrqDisposition::Handled;
286 WARNING(
"PCIe AER: root " <<
Hex << requester(m_Device) <<
" reporting did not drain");
288 return IrqDisposition::Handled;
296 events = m_SlotEvents;
298 if (!events || m_SlotFault) {
299 m_SlotQueued =
false;
308 bool slotInterrupt() {
312 Config config{m_Device};
314 if (!config.read16(m_Express.offset + 0x1a, status)) {
315 panic(
"PCIe: slot status became unavailable");
317 const uint16_t events = status & SlotEvents;
321 if (!config.write16(m_Express.offset + 0x1a, events)) {
322 panic(
"PCIe: could not acknowledge slot events");
325 m_SlotEvents |= events;
334 bool slotCommand(uint16_t clear, uint16_t set, uint16_t optional = 0) {
335 Config config{m_Device};
336 bool verified =
false;
339 uint16_t control = 0, status = 0;
340 if (!config.read16(m_Express.offset + 0x18, control) ||
341 !config.read16(m_Express.offset + 0x1a, status)) {
345 const uint16_t desired = (control & ~(clear | 0x800U)) | set;
346 if (!(set & 0x800U) && desired == control) {
349 if ((status & 0x10U) && !config.write16(m_Express.offset + 0x1a, 0x10U)) {
352 const bool written = config.write16(m_Express.offset + 0x18, desired);
354 verified = config.read16(m_Express.offset + 0x18, actual) && written &&
355 !((actual ^ desired) & ~(0x800U | optional));
357 if (m_Express.slotCapabilities & (1U << 18)) {
362 const auto deadline = Time::getTicks() + Time::Multiplier::Second;
367 if (!config.read16(m_Express.offset + 0x1a, status)) {
369 }
else if (status & 0x10U) {
370 return config.write16(m_Express.offset + 0x1a, 0x10U) && verified;
373 Time::delay(10 * Time::Multiplier::Millisecond);
374 }
while (Time::getTicks() < deadline);
375 WARNING(
"PCIe hotplug: root " <<
Hex << requester(m_Device) <<
" command completion timeout");
379 bool powerIndicator(uint16_t state) {
380 return !(m_Express.slotCapabilities & (1U << 4)) || slotCommand(3U << 8, state << 8);
383 bool interlock(
bool engaged) {
384 if (!(m_Express.slotCapabilities & (1U << 17))) {
387 uint16_t status = 0, link = 0;
388 if (!slotState(status, link)) {
391 if (
bool(status & (1U << 7)) == engaged) {
394 if (!slotCommand(0, 1U << 11)) {
397 const auto deadline = Time::getTicks() + Time::Multiplier::Second;
399 if (!slotState(status, link)) {
402 if (
bool(status & (1U << 7)) == engaged) {
405 Time::delay(10 * Time::Multiplier::Millisecond);
406 }
while (Time::getTicks() < deadline);
410 bool slotState(uint16_t& status, uint16_t& link) {
411 Config config{m_Device};
412 return config.read16(m_Express.offset + 0x1a, status) &&
413 config.read16(m_Express.offset + 0x12, link);
416 bool occupied(uint16_t status)
const {
417 return (status & (1U << 6)) &&
418 (!(m_Express.slotCapabilities & (1U << 2)) || !(status & (1U << 5)));
421 bool linkActive(uint16_t link)
const {
422 return (m_Express.linkCapabilities & (1U << 20)) ? bool(link & (1U << 13))
423 : !(link & (1U << 11)) && (link & 0x3f0U);
428 return m_SlotFault || (m_SlotEvents & 2U);
437 if (!PciBus::instance().updateCommand(m_Device, 4U, 0)) {
438 panic(
"PCIe: could not contain a lost slot");
440 ERROR(
"PCIe hotplug: root "
441 <<
Hex << requester(m_Device)
442 <<
" lost its device or power; retaining resources, recovery required");
446 uint16_t status = 0, link = 0;
447 const auto deadline = Time::getTicks() + Time::Multiplier::Second;
449 if (!slotState(status, link) || !occupied(status) || slotFault()) {
452 if (linkActive(link)) {
455 Time::delay(10 * Time::Multiplier::Millisecond);
456 }
while (Time::getTicks() < deadline);
457 if (!linkActive(link)) {
461 waitFor(100 * Time::Multiplier::Millisecond);
464 const auto configDeadline = Time::getTicks() + Time::Multiplier::Second;
467 if (!slotState(status, link) || !occupied(status) || !linkActive(link) || slotFault()) {
470 if (PciBus::instance().readConfig32(&endpoint, 0,
identity) && (
identity & 0xffffU) > 1 &&
474 Time::delay(10 * Time::Multiplier::Millisecond);
475 }
while (Time::getTicks() < configDeadline);
479 bool attentionDelay() {
480 if (!powerIndicator(2)) {
483 const auto deadline = Time::getTicks() + 5 * Time::Multiplier::Second;
487 if (m_SlotEvents & 1U) {
489 NOTICE(
"PCIe hotplug: root " <<
Hex << requester(m_Device) <<
" eject cancelled");
492 if (m_SlotFault || (m_SlotEvents & 2U)) {
496 uint16_t status = 0, link = 0;
497 if (!slotState(status, link) || !occupied(status) || !linkActive(link)) {
501 Time::delay(50 * Time::Multiplier::Millisecond);
502 }
while (Time::getTicks() < deadline);
506 bool populated()
const {
508 for (
size_t i = 0; i < m_Device->getNumChildren(); ++i) {
517 void processSlot(uint16_t events) {
518 uint16_t status = 0, link = 0;
519 if (!slotState(status, link)) {
523 if ((events & 2U) || (populated() && (!occupied(status) || !linkActive(link)))) {
528 if (!(status & (1U << 6))) {
533 if ((events & 1U) && populated()) {
534 if (!(m_Express.slotCapabilities & 2U)) {
535 WARNING(
"PCIe hotplug: root " <<
Hex << requester(m_Device) <<
" has no power controller");
538 if (!attentionDelay()) {
540 (void)powerIndicator(1);
544 if (!PciEnumeration::prepareRemoveSlot(m_Device)) {
545 PciEnumeration::cancelRemoveSlot(m_Device);
546 (void)powerIndicator(1);
547 NOTICE(
"PCIe hotplug: root " <<
Hex << requester(m_Device) <<
" eject denied: device busy");
550 if (slotFault() || !slotState(status, link) || !occupied(status) || !linkActive(link)) {
551 PciEnumeration::cancelRemoveSlot(m_Device);
555 if (!PciEnumeration::removeSlot(m_Device)) {
556 PciEnumeration::cancelRemoveSlot(m_Device);
557 (void)powerIndicator(1);
558 WARNING(
"PCIe hotplug: root " <<
Hex << requester(m_Device) <<
" driver detach failed");
562 if (!slotCommand(1U << 10, 1U << 10)) {
566 waitFor(Time::Multiplier::Second);
567 if (!interlock(
false)) {
571 (void)powerIndicator(3);
572 NOTICE(
"PCIe hotplug: root " <<
Hex << requester(m_Device) <<
" orderly eject complete");
575 if (!occupied(status) || populated()) {
578 waitFor(100 * Time::Multiplier::Millisecond);
579 if (!slotState(status, link) || !occupied(status) || slotFault()) {
582 if ((m_Express.slotCapabilities & 2U) && !slotCommand(1U << 10, 0)) {
585 if (!settleSlot() || !PciEnumeration::probeSlot(m_Device)) {
586 WARNING(
"PCIe hotplug: root " <<
Hex << requester(m_Device) <<
" insertion unavailable");
593 bool aerReady =
false;
596 aerReady = m_AerReady;
600 if (pciFunction(child) && !enableReporting(child)) {
601 WARNING(
"PCIe AER: could not enable reporting at " <<
Hex << requester(child));
605 auto callback = pedigree_std::make_callable(report);
608 (void)powerIndicator(1);
609 NOTICE(
"PCIe hotplug: root " <<
Hex << requester(m_Device) <<
" insertion complete");
612 bool inBranch(uint16_t
id)
const {
613 return id == requester(m_Device) || ((
id >> 8) >= m_FirstBus && (
id >> 8) <= m_LastBus);
616 bool setReporting(uint32_t bits) {
617 Config config{m_Device};
618 uint32_t command = 0;
619 return config.read32(m_Aer.offset + 0x2c, command) &&
620 PciFunctionState::writeVerified32(config, m_Aer.offset + 0x2c,
621 (command & ~RootReporting) | bits);
624 void stopReporting() {
625 if (!setReporting(0)) {
626 panic(
"PCIe AER could not disable an unserviceable interrupt source");
631 bool contain(uint16_t
id) {
637 Config config{&source};
640 PciBus::instance().updateCommand(&source, 4U, 0);
643 void containBranch() {
644 uint16_t slotStatus = 0, link = 0;
645 if (m_SlotReady && slotState(slotStatus, link) &&
646 (!occupied(slotStatus) || !linkActive(link))) {
647 if (!contain(requester(m_Device))) {
648 panic(
"PCIe AER could not contain a lost slot");
650 ERROR(
"PCIe AER: root " <<
Hex << requester(m_Device)
651 <<
" lost its link; retaining downstream resources");
654 bool stopped = contain(requester(m_Device));
656 if (pciFunction(child) && !contain(requester(child))) {
661 auto callback = pedigree_std::make_callable(containChild);
664 panic(
"PCIe AER could not contain an uncorrectable error");
666 ERROR(
"PCIe AER: root " <<
Hex << requester(m_Device)
667 <<
" stopped bus mastering throughout its branch");
670 void acknowledgeBranch(
bool correctable) {
671 acknowledge(requester(m_Device), correctable);
673 if (pciFunction(child)) {
674 acknowledge(requester(child), correctable);
678 auto callback = pedigree_std::make_callable(clearChild);
682 void acknowledge(uint16_t
id,
bool correctable) {
684 WARNING(
"PCIe AER: source outside root bus range " <<
Hex <<
id);
689 Config config{&source};
692 if (PciBus::instance().findExtendedCapability(&source, PciAer::CapabilityId, aer) ==
693 PciExtendedCapabilities::FindResult::Found &&
694 PciAer::read(config, aer.offset, aer.next, status)) {
695 const uint32_t bits = correctable ? status.correctable : status.uncorrectable;
697 NOTICE(
"PCIe AER: requester "
698 <<
Hex <<
id <<
" status=" << bits
699 <<
" mask=" << (correctable ? status.correctableMask : status.uncorrectableMask));
700 if (!config.write32(aer.offset + (correctable ? 0x10 : 4), bits)) {
701 WARNING(
"PCIe AER: requester status could not be acknowledged " <<
Hex <<
id);
706 if (PciExpress::find(config, express) == PciExpress::FindResult::Found) {
707 const uint16_t bits = express.deviceStatus & (correctable ? 1U : 0xeU);
708 if (bits && !config.write16(express.offset + 0x0a, bits)) {
709 WARNING(
"PCIe AER: device status could not be acknowledged " <<
Hex <<
id);
714 Device* m_Device =
nullptr;
717 uint8_t m_FirstBus = 0, m_LastBus = 0;
718 bool m_AerReady =
false;
719 bool m_SlotReady =
false;
720 bool m_SlotFault =
false;
721 bool m_SlotQueued =
false;
722 bool m_Ejected =
false;
723 uint16_t m_SlotEvents = 0;
729 SlotQueue() :
RequestQueue(MakeConstantString(
"PCIe slots")) {}
730 ~SlotQueue()
override {
735 uint64_t
executeRequest(uint64_t port, uint64_t, uint64_t, uint64_t, uint64_t, uint64_t, uint64_t,
737 reinterpret_cast<Port*
>(
static_cast<uintptr_t
>(port))->serviceSlot();
743bool slotWorkAvailable() {
744 return slotWork.getLifecycleState() == RequestQueue::LifecycleState::Accepting;
746void scheduleSlot(Port* port) {
747 if (!slotWork.publishAsyncRequest(0,
reinterpret_cast<uintptr_t
>(port))) {
748 panic(
"PCIe: slot work queue unavailable");
753bool initialized =
false;
756void PciePorts::initialize() {
761 slotWork.initialise();
762 Device* candidates[MaxPorts] = {};
765 if (pciFunction(device) && device->getPciClassCode() == 6 &&
766 device->getPciSubclassCode() == 4) {
767 Config config{device};
769 if (PciExpress::find(config, express) == PciExpress::FindResult::Found &&
770 express.type == PciExpress::Type::RootPort) {
771 if (count < MaxPorts) {
772 candidates[count++] = device;
774 WARNING(
"PCIe: root service capacity exhausted");
780 auto callback = pedigree_std::make_callable(collect);
782 for (
size_t i = 0; i < count; ++i) {
783 ports[i].initialize(candidates[i]);
Device * getChild(size_t n)
static void foreach(Callback callback, Device *root=0)
virtual const String & getSpecificType()
void setPciPosition(uint32_t bus, uint32_t device, uint32_t func)
virtual IrqDisposition irq(irq_id_t number)=0
virtual irq_id_t registerPciMessageIrqHandler(IrqHandler *handler, Device *pDevice, const IrqPolicy &intxFallbackPolicy)
virtual uint64_t executeRequest(uint64_t p1, uint64_t p2, uint64_t p3, uint64_t p4, uint64_t p5, uint64_t p6, uint64_t p7, uint64_t p8)=0
void EXPORTED_PUBLIC panic(const char *msg) NORETURN