22#include "LocalApicLint0Policy.h"
27#include "PciMessageInterrupts.h"
30#include "PciIoApicInterrupts.h"
33#include "pedigree/kernel/Log.h"
34#include "pedigree/kernel/compiler.h"
35#include "pedigree/kernel/machine/Device.h"
36#include "pedigree/kernel/machine/IrqHandler.h"
37#include "pedigree/kernel/machine/Pci.h"
38#include "pedigree/kernel/machine/SchedulerIrqHandler.h"
39#include "pedigree/kernel/panic.h"
40#include "pedigree/kernel/process/TerminationDeferral.h"
41#include "pedigree/kernel/processor/InterruptManager.h"
42#include "pedigree/kernel/processor/Processor.h"
43#include "pedigree/kernel/utilities/Iterator.h"
44#include "pedigree/kernel/utilities/utility.h"
46#define BASE_INTERRUPT_VECTOR 0x20
51#define DEFAULT_IRQ_MITIGATE_THRESHOLD 10
57 explicit StateGuard(
Pic& pic) : m_Pic(pic), m_Owned(
false), m_ThreadOwned(
false) {
65 m_Pic.drainPendingControllerActionsLocked();
80 m_Pic.drainPendingControllerActionsLocked();
85 m_Pic.releaseControllerState();
106 if (m_Owned && lifetime != PicControllerStateGate::TransitionLifetime &&
113 m_Pic.releaseControllerStateFromInterrupt();
118 m_Pic.releaseControllerStateFromInterrupt();
132 m_Pic.releaseControllerStateFromInterrupt();
153 case MitigationThreshold:
169 !policy.validForThreaded() || policy.trigger() == IrqTrigger::Synthetic))
176 if (!
m_IrqState.canRegister(irq, policy, IrqDelivery::Threaded)) {
177 ERROR(
"PIC: IRQ " <<
Dec << irq <<
" was registered with incompatible trigger modes");
180 beginLineTransitionLocked(irq);
181 const bool firstHandler = !
m_IrqState.handlerCount(irq);
183 finishLineTransitionLocked(irq);
188 advanceThreadedCookieLocked(irq);
190 m_ThreadedHardVetoRecovery[irq] =
false;
191 m_ThreadedHardVetoRecoveryGenerations[irq] = 0;
193 m_IrqState.handlerRegistered(irq, policy, IrqDelivery::Threaded);
194 finishLineTransitionLocked(irq);
195 publishDiagnosticLineLocked(irq);
197 return irq + BASE_INTERRUPT_VECTOR;
202 if (
UNLIKELY(irq >= PicIrqState::LineCount || !handler || !policy.validForHard() ||
203 policy.trigger() == IrqTrigger::Synthetic))
209 if (!
m_IrqState.canRegister(irq, policy, IrqDelivery::Hard)) {
210 ERROR(
"PIC: IRQ " <<
Dec << irq <<
" was registered with incompatible trigger modes");
213 beginLineTransitionLocked(irq);
214 const bool firstHandler = !
m_IrqState.handlerCount(irq);
216 finishLineTransitionLocked(irq);
221 advanceThreadedCookieLocked(irq);
223 m_ThreadedHardVetoRecovery[irq] =
false;
224 m_ThreadedHardVetoRecoveryGenerations[irq] = 0;
226 m_IrqState.handlerRegistered(irq, policy, IrqDelivery::Hard);
227 finishLineTransitionLocked(irq);
228 publishDiagnosticLineLocked(irq);
230 return irq + BASE_INTERRUPT_VECTOR;
233 if (irq >= PicIrqState::LineCount)
242 beginLineTransitionLocked(irq);
243 const uint8_t previous = m_ElcrPort.
read8(irq / 8);
244 if (!claimPciTriggerLocked(irq)) {
245 if (m_ElcrPort.
read8(irq / 8) != previous)
246 panic(
"PIC: cannot roll back failed PCI route reservation");
247 finishLineTransitionLocked(irq);
251 finishLineTransitionLocked(irq);
252 publishDiagnosticLineLocked(irq);
256bool Pic::hasIoApic()
const {
257#if MULTIPROCESSOR && ACPI
258 return m_Intx !=
nullptr;
264bool Pic::reservePciGsi(uint32_t gsi,
bool activeLow) {
265#if MULTIPROCESSOR && ACPI
266 if (!m_Intx || !m_Intx->containsGsi(gsi)) {
270 if (!Acpi::instance().isaIrqsForGsi(gsi, isa)) {
274 StateGuard guard(*
this);
278 for (
size_t irq = 0; irq < PicIrqState::LineCount; ++irq) {
279 if ((isa & (1U << irq)) &&
284 for (
size_t irq = 0; irq < PicIrqState::LineCount; ++irq) {
285 if (isa & (1U << irq)) {
286 beginLineTransitionLocked(irq);
288 finishLineTransitionLocked(irq);
289 publishDiagnosticLineLocked(irq);
293 return m_Intx->reserveGsi(gsi, activeLow);
299bool Pic::claimPciTriggerLocked(uint8_t irq) {
302 uint8_t previous = 0;
304 irq,
true, [
this](
size_t bank) {
return m_ElcrPort.
read8(bank); },
305 [
this](
size_t bank, uint8_t value) { m_ElcrPort.
write8(value, bank); }, previous)) {
306 ERROR(
"PIC: cannot configure PCI IRQ " <<
Dec << irq <<
" as level triggered");
309 const uint16_t bit = uint16_t{1} << irq;
310 if (!(m_OwnedElcr & bit)) {
311 m_OriginalElcr = (m_OriginalElcr & ~bit) | ((uint16_t(previous) << ((irq / 8) * 8)) & bit);
317void Pic::restorePciTriggerLocked(uint8_t irq) {
318 const uint16_t bit = uint16_t{1} << irq;
319 if (!(m_OwnedElcr & bit) ||
m_IrqState.pciRouteReserved(irq))
321 uint8_t previous = 0;
323 irq, m_OriginalElcr & bit, [
this](
size_t bank) {
return m_ElcrPort.
read8(bank); },
324 [
this](
size_t bank, uint8_t value) { m_ElcrPort.
write8(value, bank); }, previous))
325 panic(
"PIC: cannot restore retired PCI trigger mode");
332#if MULTIPROCESSOR && ACPI
335 const irq_id_t
id = m_Intx->registerThreaded(pDevice, handler, policy, routed);
343 !policy.validForThreaded() || policy.trigger() != IrqTrigger::Level))
350 if (!
m_IrqState.canRegister(irq, policy, IrqDelivery::Threaded)) {
351 ERROR(
"PIC: PCI IRQ " <<
Dec << irq <<
" conflicts with an edge-triggered handler");
354 beginLineTransitionLocked(irq);
355 const bool triggerOwned = m_OwnedElcr & (uint16_t{1} << irq);
356 if (!claimPciTriggerLocked(irq)) {
357 finishLineTransitionLocked(irq);
360 const bool firstHandler = !
m_IrqState.handlerCount(irq);
363 restorePciTriggerLocked(irq);
364 finishLineTransitionLocked(irq);
369 advanceThreadedCookieLocked(irq);
371 m_ThreadedHardVetoRecovery[irq] =
false;
372 m_ThreadedHardVetoRecoveryGenerations[irq] = 0;
374 m_IrqState.handlerRegistered(irq, policy, IrqDelivery::Threaded);
375 finishLineTransitionLocked(irq);
376 publishDiagnosticLineLocked(irq);
378 return irq + BASE_INTERRUPT_VECTOR;
385#if MULTIPROCESSOR && ACPI
388 const irq_id_t
id = m_Intx->registerHard(pDevice, handler, policy, routed);
395 if (
UNLIKELY(irq >= PicIrqState::LineCount || !handler || !policy.validForHard() ||
396 policy.trigger() != IrqTrigger::Level))
402 if (!
m_IrqState.canRegister(irq, policy, IrqDelivery::Hard)) {
403 ERROR(
"PIC: PCI IRQ " <<
Dec << irq <<
" conflicts with an edge-triggered handler");
406 beginLineTransitionLocked(irq);
407 const bool triggerOwned = m_OwnedElcr & (uint16_t{1} << irq);
408 if (!claimPciTriggerLocked(irq)) {
409 finishLineTransitionLocked(irq);
412 const bool firstHandler = !
m_IrqState.handlerCount(irq);
415 restorePciTriggerLocked(irq);
416 finishLineTransitionLocked(irq);
421 advanceThreadedCookieLocked(irq);
423 m_ThreadedHardVetoRecovery[irq] =
false;
424 m_ThreadedHardVetoRecoveryGenerations[irq] = 0;
426 m_IrqState.handlerRegistered(irq, policy, IrqDelivery::Hard);
427 finishLineTransitionLocked(irq);
428 publishDiagnosticLineLocked(irq);
430 return irq + BASE_INTERRUPT_VECTOR;
437 bool fallbackSafe =
true;
438 const irq_id_t
id = m_Messages->registerThreaded(device, handler, fallbackSafe);
448 if (!PciBus::instance().inspectFunction(device, state) ||
449 !PciBus::instance().disableMessageInterrupts(device, state)) {
456 irq_id_t* ids,
bool& fallbackSafe,
const size_t* processors) {
460 return m_Messages->registerThreadedVectors(device, handlers, count, ids, fallbackSafe,
471 bool fallbackSafe =
true;
472 const irq_id_t
id = m_Messages->registerHard(device, handler, fallbackSafe);
482 if (!PciBus::instance().inspectFunction(device, state) ||
483 !PciBus::instance().disableMessageInterrupts(device, state)) {
491 if (irq >= PicIrqState::LineCount || !handler)
496 !
m_IrqState.canRegisterScheduler(irq, policy))
499 beginLineTransitionLocked(irq);
502 finishLineTransitionLocked(irq);
503 publishDiagnosticLineLocked(irq);
504 return irq + BASE_INTERRUPT_VECTOR;
508 if (Id != BASE_INTERRUPT_VECTOR || !handler)
515 beginLineTransitionLocked(0);
518 finishLineTransitionLocked(0);
519 publishDiagnosticLineLocked(0);
523void Pic::finishHandlerUnregisterLocked(uint8_t irq, IrqHandlerRegistry::UnregisterResult result,
524 IrqHandlerRegistry::LineMode removedDelivery) {
525 if (
UNLIKELY(irq >= PicIrqState::LineCount)) {
526 FATAL_NOLOCK(
"PIC handler unregister has an invalid IRQ.");
531 if (result == IrqHandlerRegistry::UnregisterResult::Completed ||
532 result == IrqHandlerRegistry::UnregisterResult::Deferred) {
533 assert(removedDelivery == IrqHandlerRegistry::LineMode::Threaded ||
534 removedDelivery == IrqHandlerRegistry::LineMode::HardOnly);
536 const IrqDelivery delivery = removedDelivery == IrqHandlerRegistry::LineMode::Threaded
537 ? IrqDelivery::Threaded
539 m_IrqState.handlerUnregistered(irq, delivery);
541 if (delivery == IrqDelivery::Hard && !
m_IrqState.hardHandlerCount(irq)) {
544 const bool hardStageEnded =
545 previousDelivery == IrqDelivery::Mixed && currentDelivery == IrqDelivery::Threaded;
546 const bool hardQuarantineEnded =
548 if (hardQuarantineEnded) {
551 if (hardStageEnded || hardQuarantineEnded) {
552 if (currentDelivery == IrqDelivery::Threaded || currentDelivery == IrqDelivery::Mixed) {
553 m_ThreadedHardAdmitted[irq] =
true;
554 m_ThreadedHardDisposition[irq] = HardIrqDisposition::Handled;
562 m_ThreadedHardVetoRecovery[irq] =
false;
563 m_ThreadedHardVetoRecoveryGenerations[irq] = 0;
566 if (currentDelivery == IrqDelivery::None ||
567 (previousDelivery == IrqDelivery::Mixed && currentDelivery == IrqDelivery::Hard)) {
568 if (currentDelivery == IrqDelivery::Hard) {
574 const size_t boundary = advanceThreadedCookieLocked(irq);
578 m_ThreadedHardAdmitted[irq] =
false;
579 m_ThreadedHardDisposition[irq] = HardIrqDisposition::NotHandled;
580 m_ThreadedHardVetoRecovery[irq] =
false;
581 m_ThreadedHardVetoRecoveryGenerations[irq] = 0;
583 if (currentDelivery == IrqDelivery::None) {
584 restorePciTriggerLocked(irq);
588 finishLineTransitionLocked(irq);
589 publishDiagnosticLineLocked(irq);
594 if (PciMessageInterrupts::contains(Id)) {
595 return m_Messages && m_Messages->unregisterHandler(Id, handler);
598 if (PciIoApicInterrupts::contains(Id)) {
599 return m_Intx && m_Intx->unregisterHandler(Id, handler);
603 if (Id < BASE_INTERRUPT_VECTOR || Id >= BASE_INTERRUPT_VECTOR + PicIrqState::LineCount ||
607 const uint8_t irq = Id - BASE_INTERRUPT_VECTOR;
608 IrqHandlerRegistry::LineMode removedDelivery = IrqHandlerRegistry::LineMode::Empty;
609 IrqHandlerRegistry::UnregisterResult result = IrqHandlerRegistry::UnregisterResult::Rejected;
618 beginLineTransitionLocked(irq);
620 finishHandlerUnregisterLocked(irq, result, removedDelivery);
628 beginLineTransitionLocked(irq);
634 if (!guard.owned()) {
635 FATAL(
"PIC unregister lost its reserved controller boundary.");
638 finishHandlerUnregisterLocked(irq, result, removedDelivery);
642 if (result == IrqHandlerRegistry::UnregisterResult::Rejected) {
643 __atomic_add_fetch(&m_RemovalRejections[irq],
static_cast<size_t>(1), __ATOMIC_RELAXED);
645 return result == IrqHandlerRegistry::UnregisterResult::Completed;
658 WARNING(
"PIC: PCI level-trigger routing is unavailable");
676 for (
size_t i = 0; i < 16; i++)
680 for (
size_t i = 0; i < 16; i++) {
681 __atomic_store_n(&
m_IrqCount[i],
static_cast<size_t>(0), __ATOMIC_RELAXED);
693 if (Pc::instance().localApicAvailable()) {
694 m_Messages =
new PciMessageInterrupts();
695 if (!m_Messages->initialise()) {
696 WARNING(
"PCI message interrupts unavailable; using PIC INTx");
698 m_Messages =
nullptr;
701 m_Intx =
new PciIoApicInterrupts();
702 if (!m_Intx->initialise()) {
706 NOTICE(
"PCI INTx: IOAPIC routing available");
716 const uint64_t apicBase =
718 if (!LocalApicLint0Policy::isBootstrapProcessor(apicBase)) {
719 ERROR(
"PIC: threaded IRQ workers must be initialised on the BSP");
740 if (m_Messages && !m_Messages->initialiseThreaded()) {
741 WARNING(
"PCI message IRQ workers unavailable; using PIC INTx");
744 if (m_Intx && !m_Intx->initialiseThreaded()) {
745 WARNING(
"PCI IOAPIC INTx workers unavailable; using PIC INTx");
754 ERROR(
"PIC: only the BSP may join PIC threaded IRQ workers");
761 if (m_Messages && !m_Messages->shutdownThreaded()) {
765 if (m_Intx && !m_Intx->shutdownThreaded()) {
779 for (
size_t irq = 0; irq < PicIrqState::LineCount; ++irq) {
780 const uint8_t line =
static_cast<uint8_t
>(irq);
781 beginLineTransitionLocked(line);
782 const size_t boundary = advanceThreadedCookieLocked(line);
787 m_ThreadedHardAdmitted[irq] =
false;
788 m_ThreadedHardDisposition[irq] = HardIrqDisposition::NotHandled;
789 m_ThreadedHardVetoRecovery[irq] =
false;
790 m_ThreadedHardVetoRecoveryGenerations[irq] = 0;
795 publishAllDiagnosticLinesLocked();
801 : m_SlavePort(
"PIC #2"),
802 m_MasterPort(
"PIC #1"),
803 m_ElcrPort(
"PIC trigger modes"),
813 m_SchedulerIrqHandler(nullptr),
815 m_ControllerStateGate(),
817 m_ControllerThreadMutex(),
818 m_ThreadedDispatcher(MakeConstantString(
"PIC IRQ bottom half"), ControllerWorkLine + 1,
819 dispatchThreadedLine, this),
821 m_ThreadedDispatchGenerations(),
822 m_HardStageGenerations(),
823 m_ThreadedHadHardStage(),
824 m_ThreadedHardAdmitted(),
825 m_ThreadedHardDisposition(),
826 m_ThreadedHardVetoRecovery(),
827 m_ThreadedHardVetoRecoveryGenerations(),
828 m_FailClosedReasons(),
829 m_ThreadedPublicationFailures(),
830 m_RemovalRejections(),
831 m_ControllerContentions(),
832 m_ControllerPromptAttempts(0),
833 m_ControllerPromptFailures(0),
834 m_ControllerPromptDestination(0),
836 m_ControllerTemporaryMask(0),
837 m_AppliedControllerMask(0xFFFF),
838 m_DeferredLineTransitions(0),
839 m_DeliveryProcessor(0),
842 m_UnregisterReservations(),
843 m_ShuttingDown(false),
845 m_SpuriousIrqCount(),
846 m_UnhandledIrqCount(),
848 m_MitigationThreshold() {
849 publishAllDiagnosticLinesLocked();
852void Pic::publishDiagnosticLineLocked(uint8_t irq) {
853 if (irq >= PicIrqState::LineCount) {
857 size_t targetBank = 0;
865 line.handlerCount =
m_IrqState.handlerCount(irq);
866 line.configured = line.handlerCount != 0;
868 line.effectiveMasked =
870 line.requestedEnabled =
m_IrqState.requestedEnabled(irq);
871 line.acknowledgementPending =
m_IrqState.acknowledgementPending(irq);
872 line.threadedPending =
m_IrqState.threadedPending(irq);
873 line.dispatchGeneration =
m_IrqState.dispatchGeneration(irq);
874 line.acknowledgedGeneration =
m_IrqState.acknowledgedGeneration(irq);
876 line.interruptCount = __atomic_load_n(&
m_IrqCount[irq], __ATOMIC_RELAXED);
880 line.removalRejections = __atomic_load_n(&m_RemovalRejections[irq], __ATOMIC_RELAXED);
883 line.controllerPromptFailures = __atomic_load_n(&m_ControllerPromptFailures, __ATOMIC_RELAXED);
884 line.controllerPromptDestination =
885 __atomic_load_n(&m_ControllerPromptDestination, __ATOMIC_RELAXED);
887 __atomic_load_n(&m_ControllerPromptState, __ATOMIC_RELAXED));
889 if (!line.configured) {
890 line.maskReasons |= IrqMaskNoHandler;
893 line.controllerAck =
m_IrqState.controllerAck(irq);
894 line.lineRelease =
m_IrqState.lineRelease(irq);
896 if (!line.requestedEnabled) {
897 line.maskReasons |= IrqMaskAdministrativelyDisabled;
899 if (line.acknowledgementPending) {
900 line.maskReasons |= IrqMaskAwaitingAcknowledgement;
902 if (line.threadedPending) {
903 line.maskReasons |= IrqMaskAwaitingThreadedCompletion;
906 line.maskReasons |= IrqMaskMitigated;
909 line.maskReasons |= IrqMaskShuttingDown;
912 line.maskReasons |= IrqMaskControllerContention;
915 line.maskReasons |= IrqMaskControllerContention;
922void Pic::publishAllDiagnosticLinesLocked() {
923 for (
size_t irq = 0; irq < PicIrqState::LineCount; ++irq) {
924 publishDiagnosticLineLocked(
static_cast<uint8_t
>(irq));
929 if (!out || !capacity) {
933 const size_t count = capacity < PicIrqState::LineCount ? capacity : PicIrqState::LineCount;
934 for (
size_t irq = 0; irq < count; ++irq) {
937 out[irq].line =
static_cast<uint8_t
>(irq);
942 const size_t controllerPromptAttempts =
944 const size_t controllerPromptFailures =
945 __atomic_load_n(&m_ControllerPromptFailures, __ATOMIC_RELAXED);
946 const size_t controllerPromptDestination =
947 __atomic_load_n(&m_ControllerPromptDestination, __ATOMIC_RELAXED);
949 __atomic_load_n(&m_ControllerPromptState, __ATOMIC_RELAXED));
950 for (
size_t irq = 0; irq < count; ++irq) {
953 static_cast<uint8_t
>(irq), out[irq].activeHardDispatchGeneration);
954 out[irq].hardStageActive = out[irq].activeHardDispatchCount != 0;
956 static_cast<uint8_t
>(irq), out[irq].activeThreadedHandlerIdentity);
960 out[irq].interruptCount = __atomic_load_n(&
m_IrqCount[irq], __ATOMIC_RELAXED);
963 out[irq].publicationFailures =
965 out[irq].removalRejections = __atomic_load_n(&m_RemovalRejections[irq], __ATOMIC_RELAXED);
966 out[irq].controllerContentions =
968 out[irq].controllerPromptAttempts = controllerPromptAttempts;
969 out[irq].controllerPromptFailures = controllerPromptFailures;
970 out[irq].controllerPromptDestination = controllerPromptDestination;
971 out[irq].controllerPromptState = controllerPromptState;
972 out[irq].diagnosticPublicationFailures =
m_Diagnostics.missedPublications(irq);
974 out[irq].dispatcherInitialised = dispatcherInitialised;
982size_t Pic::advanceThreadedCookieLocked(uint8_t irq) {
983 assert(irq < PicIrqState::LineCount);
991void Pic::beginLineTransitionLocked(uint8_t irq) {
992 assert(irq < PicIrqState::LineCount);
999void Pic::finishLineTransitionLocked(uint8_t irq) {
1000 assert(irq < PicIrqState::LineCount);
1001 const uint16_t lineMask =
static_cast<uint16_t
>(1U << irq);
1023 uint8_t mask = 1 << (irq - 8);
1027 return (isr & mask) == 0;
1030 uint8_t mask = 1 << irq;
1034 return (isr & mask) == 0;
1038bool Pic::drainOnePendingControllerBatchLocked() {
1046 size_t realEntries[PicIrqState::LineCount] = {};
1047 size_t staleRealEntries[PicIrqState::LineCount] = {};
1048 size_t nonMutatingSpuriousEntries[PicIrqState::LineCount] = {};
1049 size_t spuriousCascadeEois = 0;
1050 size_t staleSpuriousCascadeEois = 0;
1051 bool maskChanged =
false;
1053 for (
size_t irq = 0; irq < PicIrqState::LineCount; ++irq) {
1056 finishHardDispatchLocked(tail);
1060 for (
size_t irq = 0; irq < PicIrqState::LineCount; ++irq) {
1061 currentActions.entry[irq] = pending.entry[irq];
1062 currentActions.tail[irq] = pending.tail[irq];
1063 currentActions.tailEoi[irq] = pending.tailEoi[irq];
1064 staleActions.entry[irq] = pending.staleEntry[irq];
1065 staleActions.tail[irq] = pending.staleTail[irq];
1066 staleActions.tailEoi[irq] = pending.staleTailEoi[irq];
1068 for (
size_t occurrence = 0; occurrence < pending.entry[irq]; ++occurrence) {
1070 __atomic_add_fetch(&
m_SpuriousIrqCount[irq],
static_cast<size_t>(1), __ATOMIC_RELAXED);
1071 ++nonMutatingSpuriousEntries[irq];
1073 ++spuriousCascadeEois;
1080 for (
size_t occurrence = 0; occurrence < pending.staleEntry[irq]; ++occurrence) {
1082 __atomic_add_fetch(&
m_SpuriousIrqCount[irq],
static_cast<size_t>(1), __ATOMIC_RELAXED);
1083 ++nonMutatingSpuriousEntries[irq];
1085 ++staleSpuriousCascadeEois;
1088 ++staleRealEntries[irq];
1092 const size_t contentions =
1093 pending.entry[irq] + pending.tail[irq] + pending.staleEntry[irq] + pending.staleTail[irq];
1099 m_IrqState, irq, realEntries[irq], pending.tail[irq], pending.tailEoi[irq]);
1100 assert(result.threadedOccurrences <= realEntries[irq]);
1101 assert(result.threadedOccurrences <= currentActions.entry[irq]);
1102 for (
size_t occurrence = 0; occurrence < result.threadedOccurrences; ++occurrence) {
1103 admitThreadedOccurrenceLocked(
static_cast<uint8_t
>(irq));
1105 realEntries[irq] -= result.threadedOccurrences;
1106 currentActions.entry[irq] -= result.threadedOccurrences;
1107 if (result.terminalWork) {
1108 __atomic_add_fetch(&
m_UnhandledIrqCount[irq], result.unhandledOccurrences, __ATOMIC_RELAXED);
1111 if (result.schedulerDrop) {
1118 }
else if (result.quarantine) {
1122 if (result.invalidateThreaded) {
1123 const size_t boundary = advanceThreadedCookieLocked(
static_cast<uint8_t
>(irq));
1127 m_ThreadedHardAdmitted[irq] =
false;
1128 m_ThreadedHardDisposition[irq] = HardIrqDisposition::NotHandled;
1129 m_ThreadedHardVetoRecovery[irq] =
false;
1130 m_ThreadedHardVetoRecoveryGenerations[irq] = 0;
1132 maskChanged |= result.maskChanged;
1134 const size_t staleUnhandled = staleRealEntries[irq] + pending.staleTail[irq];
1135 if (staleUnhandled) {
1140 const uint16_t temporaryMask = temporaryPicMaskForDeferredWork(
1149 auto writeController = [
this](PicControllerWriteTarget target, uint8_t value) {
1151 case PicControllerWriteTarget::MasterCommand:
1154 case PicControllerWriteTarget::MasterMask:
1157 case PicControllerWriteTarget::SlaveCommand:
1160 case PicControllerWriteTarget::SlaveMask:
1165 emitPicContentionWrites(
m_IrqState,
false, realEntries, currentActions, spuriousCascadeEois,
1167 emitPicContentionWrites(
m_IrqState,
false, staleRealEntries, staleActions,
1168 staleSpuriousCascadeEois, writeController);
1170 for (
size_t irq = 0; irq < PicIrqState::LineCount; ++irq) {
1171 if (pending.entry[irq] || pending.tail[irq] || pending.staleEntry[irq] ||
1172 pending.staleTail[irq]) {
1173 publishDiagnosticLineLocked(
static_cast<uint8_t
>(irq));
1179void Pic::drainPendingControllerActionsLocked() {
1180 while (drainOnePendingControllerBatchLocked()) {
1184bool Pic::handControllerStateToWorker() {
1192void Pic::releaseControllerState() {
1194 drainPendingControllerActionsLocked();
1196 finishDeferredLineTransitionsLocked();
1200 if (!deliveryProcessor) {
1204 const uint16_t restorable =
1207 clearControllerTemporaryMaskLocked(restorable);
1210 const uint16_t pendingUnmask =
1213 if (handControllerStateToWorker()) {
1218 "PIC controller restore work could not reach its BSP "
1225 clearControllerTemporaryMaskLocked();
1230 if (interruptsWereEnabled) {
1236 drainPendingControllerActionsLocked();
1238 finishDeferredLineTransitionsLocked();
1241 clearControllerTemporaryMaskLocked();
1244 if (interruptsWereEnabled) {
1259void Pic::releaseControllerStateFromInterrupt() {
1261 handControllerStateToWorker()) {
1267 drainOnePendingControllerBatchLocked();
1270 drainOnePendingControllerBatchLocked();
1274 if (handControllerStateToWorker()) {
1280 drainOnePendingControllerBatchLocked();
1286 finishDeferredLineTransitionsLocked();
1289 clearControllerTemporaryMaskLocked();
1297void Pic::admitThreadedOccurrenceLocked(uint8_t irq) {
1298 if (
UNLIKELY(irq >= PicIrqState::LineCount)) {
1299 FATAL_NOLOCK(
"PIC threaded occurrence has an invalid IRQ.");
1302 assert(
m_IrqState.delivery(irq) == IrqDelivery::Threaded);
1306 const size_t dispatchGeneration =
m_IrqState.beginDispatch(irq);
1309 const bool wasEnabled =
m_IrqState.enabled(irq);
1315 if (controllerAck != IrqControllerAck::None) {
1320 publishDiagnosticLineLocked(irq);
1327 if (lineRelease == IrqLineRelease::AfterThreadedCompletion) {
1330 if (controllerAck == IrqControllerAck::BeforeHardStage) {
1334 const size_t threadedCookie = advanceThreadedCookieLocked(irq);
1337 m_ThreadedHardAdmitted[irq] =
false;
1338 m_ThreadedHardDisposition[irq] = HardIrqDisposition::NotHandled;
1339 m_ThreadedHardVetoRecovery[irq] =
false;
1340 m_ThreadedHardVetoRecoveryGenerations[irq] = 0;
1350 m_ThreadedHardAdmitted[irq] =
false;
1351 m_ThreadedHardDisposition[irq] = HardIrqDisposition::NotHandled;
1352 const bool wasEnabled =
m_IrqState.enabled(irq);
1361 if (controllerAck == IrqControllerAck::AfterHardStage) {
1364 publishDiagnosticLineLocked(irq);
1368 size_t irq = (interruptNumber - BASE_INTERRUPT_VECTOR);
1369 if (irq >= PicIrqState::LineCount) {
1374 "Legacy PIC interrupt reached a processor whose LINT0 must be "
1379 __atomic_add_fetch(&
m_IrqCount[irq],
static_cast<size_t>(1), __ATOMIC_RELAXED);
1381 HardStateGuard entry(*
this,
static_cast<uint8_t
>(irq), controllerLifetime);
1382 if (!entry.owned()) {
1388 if (schedulerHandler) {
1390 __atomic_add_fetch(&
m_SpuriousIrqCount[irq],
static_cast<size_t>(1), __ATOMIC_RELAXED);
1391 publishDiagnosticLineLocked(
static_cast<uint8_t
>(irq));
1395 const size_t generation =
m_IrqState.beginDispatch(irq);
1399 eoiLocked(
static_cast<uint8_t
>(irq));
1402 publishDiagnosticLineLocked(
static_cast<uint8_t
>(irq));
1404 schedulerHandler->schedulerIrq(
static_cast<irq_id_t
>(irq), state);
1411 bool hasThreadedStage =
false;
1412 size_t dispatchGeneration = 0;
1413 size_t hardStageGeneration = 0;
1414 size_t threadedCookie = 0;
1415 bool threadedPublished =
false;
1427 __atomic_add_fetch(&
m_SpuriousIrqCount[irq],
static_cast<size_t>(1), __ATOMIC_RELAXED);
1428 publishDiagnosticLineLocked(
static_cast<uint8_t
>(irq));
1433 if (delivery == IrqDelivery::Threaded) {
1434 admitThreadedOccurrenceLocked(
static_cast<uint8_t
>(irq));
1438 controllerAck =
m_IrqState.controllerAck(irq);
1440 dispatchGeneration =
m_IrqState.beginDispatch(irq);
1442 hasThreadedStage = delivery == IrqDelivery::Mixed;
1445 bool cutoffCaptured =
false;
1446 if (delivery == IrqDelivery::Mixed) {
1449 hardAdmissionCutoff = cutoffs.hard;
1450 threadedAdmissionCutoff = cutoffs.threaded;
1458 if (!cutoffCaptured) {
1459 const bool wasEnabled =
m_IrqState.enabled(irq);
1461 hasThreadedStage ? ThreadedPublicationQuarantine : UnhandledQuarantine;
1466 if (controllerAck != IrqControllerAck::None) {
1471 static_cast<size_t>(1), __ATOMIC_RELAXED);
1472 publishDiagnosticLineLocked(
static_cast<uint8_t
>(irq));
1476 if (hasThreadedStage) {
1480 if (lineRelease == IrqLineRelease::AfterThreadedCompletion) {
1483 if (controllerAck == IrqControllerAck::BeforeHardStage) {
1486 threadedCookie = advanceThreadedCookieLocked(irq);
1489 m_ThreadedHardAdmitted[irq] =
false;
1490 m_ThreadedHardDisposition[irq] = HardIrqDisposition::NotHandled;
1491 m_ThreadedHardVetoRecovery[irq] =
false;
1492 m_ThreadedHardVetoRecoveryGenerations[irq] = 0;
1495 if (!threadedPublished) {
1498 m_ThreadedHardAdmitted[irq] =
false;
1499 m_ThreadedHardDisposition[irq] = HardIrqDisposition::NotHandled;
1507 }
else if (controllerAck == IrqControllerAck::BeforeHardStage) {
1510 publishDiagnosticLineLocked(
static_cast<uint8_t
>(irq));
1517 dispatchGeneration, hardAdmissionCutoff);
1520 tail.irq =
static_cast<uint8_t
>(irq);
1521 tail.controllerLifetime = controllerLifetime;
1522 tail.dispatchGeneration = dispatchGeneration;
1523 tail.hardStageGeneration = hardStageGeneration;
1524 tail.threadedCookie = threadedCookie;
1525 tail.controllerAck = controllerAck;
1526 tail.hardDisposition = hardDisposition;
1527 tail.hasThreadedStage = hasThreadedStage;
1528 tail.threadedPublished = threadedPublished;
1529 tail.admitted = admitted;
1530 finishHardDispatchFromInterrupt(tail);
1534 const uint8_t irq = record.irq;
1535 if (
UNLIKELY(irq >= PicIrqState::LineCount)) {
1536 FATAL_NOLOCK(
"PIC hard dispatch completion has an invalid IRQ.");
1539 const size_t dispatchGeneration = record.dispatchGeneration;
1540 const size_t threadedCookie = record.threadedCookie;
1542 const bool hasThreadedStage = record.hasThreadedStage;
1543 bool threadedPublished = record.threadedPublished;
1544 const bool admitted = record.admitted;
1548 current.dispatchGeneration =
m_IrqState.dispatchGeneration(irq);
1552 current.hardHandlerCount =
m_IrqState.hardHandlerCount(irq);
1553 current.delivery = hasThreadedStage ?
m_IrqState.delivery(irq) : IrqDelivery::None;
1557 terminal.applyTemporaryMask([
this, irq]() {
1562 const IrqDelivery currentDelivery = current.delivery;
1563 const bool hardStageLifetimeCurrent = plan.hardStageLifetimeCurrent;
1564 const bool threadedLifetimeCurrent = plan.threadedLifetimeCurrent;
1567 if (hasThreadedStage && (hardHandoffFailed || (threadedLifetimeCurrent && !threadedPublished))) {
1568 const bool wasEnabled =
m_IrqState.enabled(irq);
1569 if (hardHandoffFailed) {
1576 if (hardHandoffFailed) {
1582 if (hasThreadedStage) {
1583 if (threadedLifetimeCurrent) {
1584 if (!threadedPublished) {
1586 m_ThreadedHardAdmitted[irq] =
false;
1587 m_ThreadedHardDisposition[irq] = HardIrqDisposition::NotHandled;
1588 const bool wasEnabled =
m_IrqState.enabled(irq);
1593 terminal.acknowledge([
this, irq]() { eoiLocked(irq); });
1594 publishDiagnosticLineLocked(
static_cast<uint8_t
>(irq));
1597 const bool hardStageQuiesced =
1598 !hardStageLifetimeCurrent || currentDelivery == IrqDelivery::Threaded;
1599 m_ThreadedHardAdmitted[irq] = admitted || hardStageQuiesced;
1600 m_ThreadedHardDisposition[irq] = effectiveHardDisposition;
1603 if (doorbell.invalidateStagedDispatch) {
1605 if (doorbell.quarantine) {
1609 m_ThreadedHardAdmitted[irq] =
false;
1610 m_ThreadedHardDisposition[irq] = HardIrqDisposition::NotHandled;
1614 threadedPublished = doorbell.published;
1615 if (doorbell.completeDispatch) {
1616 const bool wasEnabled =
m_IrqState.enabled(irq);
1622 }
else if (plan.threadedAction == PicHardTailThreadedAction::Quiesced) {
1627 const bool threadedStageQuiesced =
true;
1628 const bool aggregateAdmitted = admitted || threadedStageQuiesced;
1629 const bool aggregateAllowRearm =
1630 (effectiveHardDisposition == HardIrqDisposition::Handled || threadedStageQuiesced) &&
1632 const bool wasEnabled =
m_IrqState.enabled(irq);
1634 aggregateAdmitted && !aggregateAllowRearm);
1635 if (!aggregateAdmitted || (!aggregateAllowRearm && !hardHandoffFailed)) {
1643 terminal.acknowledge([
this, irq]() { eoiLocked(irq); });
1644 publishDiagnosticLineLocked(
static_cast<uint8_t
>(irq));
1648 const bool wasEnabled =
m_IrqState.enabled(irq);
1649 const bool needsAcknowledgement =
1651 (admitted && effectiveHardDisposition == HardIrqDisposition::NotHandled);
1652 if (needsAcknowledgement) {
1654 ? HardHandoffQuarantine
1655 : UnhandledQuarantine;
1661 }
else if (hardStageLifetimeCurrent &&
1662 (!admitted || effectiveHardDisposition == HardIrqDisposition::NotHandled)) {
1668 terminal.acknowledge([
this, irq]() { eoiLocked(irq); });
1669 publishDiagnosticLineLocked(
static_cast<uint8_t
>(irq));
1674 finishHardDispatchLocked(record);
1675 releaseControllerStateFromInterrupt();
1681 releaseControllerStateFromInterrupt();
1690 record.controllerAck == IrqControllerAck::AfterHardStage)) {
1691 releaseControllerStateFromInterrupt();
1695void Pic::dispatchThreadedLine(
void* context, uint8_t irq,
size_t cookie) {
1696 Pic* pic =
reinterpret_cast<Pic*
>(context);
1697 if (irq == ControllerWorkLine) {
1698 StateGuard guard(*pic);
1700 FATAL(
"PIC controller continuation could not claim thread state.");
1704 size_t dispatchGeneration = 0;
1706 StateGuard guard(*pic);
1707 if (!guard.owned()) {
1708 FATAL(
"PIC threaded dispatch could not snapshot line state.");
1711 if (irq >= PicIrqState::LineCount) {
1716 (delivery != IrqDelivery::Threaded && delivery != IrqDelivery::Mixed)) {
1726 StateGuard guard(*pic);
1727 if (!guard.owned()) {
1728 FATAL(
"PIC threaded completion could not claim line state.");
1734 (delivery != IrqDelivery::Threaded && delivery != IrqDelivery::Mixed)) {
1739 const bool aggregateAdmitted = pic->m_ThreadedHardAdmitted[irq] || admitted;
1741 const bool aggregateAllowRearm =
1742 (hardDisposition == HardIrqDisposition::Handled || result.allowRearm) &&
1744 const bool hardVetoRecovery =
1746 result.allowRearm &&
1747 pic->
m_IrqState.lineRelease(irq) == IrqLineRelease::AfterThreadedCompletion;
1748 pic->m_ThreadedHardVetoRecovery[irq] = hardVetoRecovery;
1749 pic->m_ThreadedHardVetoRecoveryGenerations[irq] = hardVetoRecovery ? dispatchGeneration : 0;
1750 const bool wasEnabled = pic->
m_IrqState.enabled(irq);
1751 if (!aggregateAdmitted ||
1754 __atomic_add_fetch(&pic->
m_UnhandledIrqCount[irq],
static_cast<size_t>(1), __ATOMIC_RELAXED);
1756 if (hadHardStage && pic->
m_IrqState.lineRelease(irq) == IrqLineRelease::AfterHardStage) {
1758 aggregateAdmitted && !aggregateAllowRearm);
1761 aggregateAdmitted && aggregateAllowRearm);
1763 pic->m_ThreadedHardAdmitted[irq] =
false;
1764 pic->m_ThreadedHardDisposition[irq] = HardIrqDisposition::NotHandled;
1765 if (wasEnabled != pic->
m_IrqState.enabled(irq)) {
1766 pic->applyMaskLocked();
1768 pic->publishDiagnosticLineLocked(irq);
1773bool Pic::promptThreadedWorker(
void* context, uint8_t line,
size_t workerProcessor) {
1774 Pic* pic =
reinterpret_cast<Pic*
>(context);
1780 __atomic_store_n(&pic->m_ControllerPromptDestination,
static_cast<size_t>(pic->
m_DeliveryApicId),
1783 __atomic_add_fetch(&pic->m_ControllerPromptFailures,
static_cast<size_t>(1), __ATOMIC_RELAXED);
1784 __atomic_store_n(&pic->m_ControllerPromptState,
1785 static_cast<size_t>(IrqControllerPromptState::Failed), __ATOMIC_RELEASE);
1792 const bool submitted = Pc::instance().getLocalApic().interProcessorInterrupt(
1793 pic->
m_DeliveryApicId, IPI_RESCHEDULE_VECTOR, LocalApic::deliveryModeFixed,
true,
false);
1795 __atomic_add_fetch(&pic->m_ControllerPromptFailures,
static_cast<size_t>(1), __ATOMIC_RELAXED);
1797 __atomic_store_n(&pic->m_ControllerPromptState,
1798 static_cast<size_t>(submitted ? IrqControllerPromptState::Submitted
1805void Pic::eoiLocked(uint8_t irq) {
1816void Pic::applyMaskLocked() {
1817 const uint16_t canonical =
m_IrqState.mask();
1818 const bool canUnmaskBeforeRelease =
1823 const uint16_t mask = effectiveMaskLocked();
1824 emitPicMaskWrites(mask, [
this](PicControllerWriteTarget target, uint8_t value) {
1825 if (target == PicControllerWriteTarget::MasterMask) {
1834uint16_t Pic::effectiveMaskLocked()
const {
1838void Pic::finishDeferredLineTransitionsLocked() {
1842 for (
size_t irq = 0; irq < PicIrqState::LineCount; ++irq) {
1843 if ((pending &
static_cast<uint16_t
>(1U << irq)) && !
m_IrqState.lineTransitionPending(irq)) {
1853void Pic::clearControllerTemporaryMaskLocked(uint16_t restoreMask) {
1860 finishDeferredLineTransitionsLocked();
1864 for (
size_t irq = 0; irq < PicIrqState::LineCount; ++irq) {
1865 if (restored &
static_cast<uint16_t
>(1U << irq)) {
1866 publishDiagnosticLineLocked(
static_cast<uint8_t
>(irq));
1871void Pic::setEnabledLocked(uint8_t irq,
bool enable) {
1876void Pic::enable(uint8_t irq,
bool enable) {
1878 if (PciMessageInterrupts::contains(irq)) {
1880 m_Messages->enable(irq, enable);
1885 if (PciIoApicInterrupts::contains(irq)) {
1887 m_Intx->enable(irq, enable);
1893 if (irq >= PicIrqState::LineCount) {
1897 StateGuard guard(*
this);
1903 setEnabledLocked(irq, enable);
1904 publishDiagnosticLineLocked(irq);
1906void Pic::enableAll(
bool enable) {
1907 StateGuard guard(*
this);
1908 if (!guard.owned()) {
1909 FATAL(
"PIC could not claim controller state for a global mask.");
1914 publishAllDiagnosticLinesLocked();
virtual uintptr_t getInterruptNumber()
Handles interrupts and interrupt registrations from kernel components.
static EXPORTED_PUBLIC InterruptManager & instance()
virtual bool registerInterruptHandler(size_t nInterruptNumber, InterruptHandler *pHandler)=0
bool allocate(io_port_t ioPort, size_t size)
virtual void write8(uint8_t value, size_t offset=0)
virtual uint8_t read8(size_t offset=0)
IrqLineDiagnosticSnapshot * beginPublication(size_t line, size_t &targetBank)
void finishPublication(size_t line, size_t targetBank)
bool snapshot(size_t line, IrqLineDiagnosticSnapshot &out) const
void invalidateThreadedGenerationFromInterrupt(uint8_t irq, size_t throughGeneration)
bool captureMixedAdmissionCutoffs(uint8_t irq, MixedAdmissionCutoffs &cutoffs)
size_t threadedDispatchState(uint8_t irq, uintptr_t &exactHandlerIdentity) const
bool publishThreadedDispatch(uint8_t irq, size_t dispatchGeneration)
bool captureAdmissionCutoff(uint8_t irq, AdmissionCutoff &cutoff)
bool registerThreadedHandler(uint8_t irq, IrqHandler *handler)
UnregisterResult unregisterHandler(uint8_t irq, IrqHandlerBase *handler)
bool dispatchHard(uint8_t irq, InterruptState &state, HardIrqDisposition &disposition, HardIrqHandler *onlyHandler=nullptr, size_t dispatchGeneration=0, irq_id_t callbackId=0)
void invalidateThreadedLine(uint8_t irq, size_t throughGeneration)
bool dispatchThreaded(uint8_t irq, size_t dispatchGeneration, ThreadedDispatchResult &result, IrqHandler *onlyHandler=nullptr, irq_id_t callbackId=0)
bool hardLineQuarantined(uint8_t irq) const
size_t hardDispatchState(uint8_t irq, size_t &exactGeneration) const
bool registerHardHandler(uint8_t irq, HardIrqHandler *handler)
void requestContinuation()
bool relinquishOwnerForContinuation()
bool queueEntry(size_t irq, size_t lifetime)
size_t finishLineTransition(size_t irq)
bool takePending(PendingActions &actions)
void beginLineTransition(size_t irq)
bool queueTail(size_t irq, size_t lifetime, bool owesEoi)
bool queueTailRecord(size_t irq)
bool consume(uint8_t irq, Finalizer &&finalizer)
void beginThreadedDispatch(size_t irq)
bool completeThreadedDispatch(size_t irq, size_t dispatchGeneration, bool allowRearm)
void reserveIoApic(size_t irq)
void reservePciRoute(size_t irq)
void completeDispatch(size_t irq, size_t dispatchGeneration, bool needsAcknowledgement)
size_t m_UnregisterReservations[PicIrqState::LineCount]
size_t m_ThreadedPublicationFailures[PicIrqState::LineCount]
size_t m_ThreadedDispatchGenerations[PicIrqState::LineCount]
static EXPORTED_PUBLIC Pic m_Instance
virtual bool unregisterHandler(irq_id_t Id, IrqHandlerBase *handler)
virtual bool unregisterSchedulerIrqHandler(irq_id_t Id, SchedulerIrqHandler *handler)
size_t m_HardStageGenerations[PicIrqState::LineCount]
IrqDiagnosticSnapshotStore< PicIrqState::LineCount > m_Diagnostics
virtual bool control(uint8_t irq, ControlCode code, size_t argument)
virtual irq_id_t registerIsaIrqHandler(uint8_t irq, IrqHandler *handler, const IrqPolicy &policy)
virtual irq_id_t registerHardIsaIrqHandler(uint8_t irq, HardIrqHandler *handler, const IrqPolicy &policy)
virtual irq_id_t registerPciIrqHandler(IrqHandler *handler, Device *pDevice, const IrqPolicy &policy)
Mutex m_ControllerThreadMutex
size_t m_ThreadedCookies[PicIrqState::LineCount]
PicControllerStateGate m_ControllerStateGate
uint16_t m_ControllerTemporaryMask
PicHardTailQueue m_HardTailQueue
virtual irq_id_t registerSchedulerIrqHandler(uint8_t irq, SchedulerIrqHandler *handler, const IrqPolicy &policy)
uint8_t m_FailClosedReasons[PicIrqState::LineCount]
size_t m_UnhandledIrqCount[16]
bool m_ThreadedHadHardStage[PicIrqState::LineCount]
size_t m_DeliveryProcessor
virtual void interrupt(size_t interruptNumber, InterruptState &state)
size_t m_ControllerPromptAttempts
ThreadedIrqDispatcher m_ThreadedDispatcher
virtual irq_id_t registerHardPciIrqHandler(HardIrqHandler *handler, Device *pDevice, const IrqPolicy &policy)
SchedulerIrqHandler * m_SchedulerIrqHandler
bool registerPciMsixIrqHandlers(Device *device, IrqHandler *const *handlers, size_t count, irq_id_t *ids, bool &fallbackSafe, const size_t *processors=nullptr) override
uint16_t m_DeferredLineTransitions
bool initialiseThreaded()
bool reservePciRoute(uint8_t irq)
size_t m_SpuriousIrqCount[16]
irq_id_t registerHardPciMessageIrqHandler(HardIrqHandler *handler, Device *device, const IrqPolicy &intxFallbackPolicy) override
irq_id_t registerPciMessageIrqHandler(IrqHandler *handler, Device *device, const IrqPolicy &intxFallbackPolicy) override
size_t m_MitigationThreshold[16]
IrqHandlerRegistry m_Handlers
size_t m_ControllerContentions[PicIrqState::LineCount]
Pic() INITIALISATION_ONLY
bool spuriousLocked(size_t irq)
virtual size_t snapshotIrqLines(IrqLineDiagnosticSnapshot *out, size_t capacity) const
bool initialise() INITIALISATION_ONLY
uint16_t m_AppliedControllerMask
static bool getInterrupts()
static ExecutionContext executionContext()
static void setInterrupts(bool bEnable)
bool acquire(size_t n=1, size_t timeoutSecs=0, size_t timeoutUsecs=0)
size_t pendingCookie(uint8_t line) const
bool initialise(bool lazy=false)
bool publishFromInterrupt(uint8_t line, size_t cookie)
MUST_USE_RESULT bool setRemoteWakeCallback(RemoteWakeCallback callback, void *callbackContext)
void snapshotDiagnostics(uint8_t line, IrqLineDiagnosticSnapshot &snapshot) const
static uint64_t readMachineSpecificRegister(uint32_t index)
void EXPORTED_PUBLIC panic(const char *msg) NORETURN