22#include "LocalApicLint0Policy.h"
28#include "pedigree/kernel/Log.h"
29#include "pedigree/kernel/compiler.h"
30#include "pedigree/kernel/machine/Device.h"
31#include "pedigree/kernel/machine/IrqHandler.h"
32#include "pedigree/kernel/machine/SchedulerIrqHandler.h"
33#include "pedigree/kernel/panic.h"
34#include "pedigree/kernel/process/TerminationDeferral.h"
35#include "pedigree/kernel/processor/InterruptManager.h"
36#include "pedigree/kernel/processor/Processor.h"
37#include "pedigree/kernel/utilities/Iterator.h"
38#include "pedigree/kernel/utilities/utility.h"
40#define BASE_INTERRUPT_VECTOR 0x20
45#define DEFAULT_IRQ_MITIGATE_THRESHOLD 10
51 explicit StateGuard(
Pic& pic) : m_Pic(pic), m_Owned(
false), m_ThreadOwned(
false) {
59 m_Pic.drainPendingControllerActionsLocked();
74 m_Pic.drainPendingControllerActionsLocked();
79 m_Pic.releaseControllerState();
100 if (m_Owned && lifetime != PicControllerStateGate::TransitionLifetime &&
107 m_Pic.releaseControllerStateFromInterrupt();
112 m_Pic.releaseControllerStateFromInterrupt();
126 m_Pic.releaseControllerStateFromInterrupt();
147 case MitigationThreshold:
163 !policy.validForThreaded() || policy.trigger() == IrqTrigger::Synthetic))
170 if (!
m_IrqState.canRegister(irq, policy, IrqDelivery::Threaded)) {
171 ERROR(
"PIC: IRQ " <<
Dec << irq <<
" was registered with incompatible trigger modes");
174 beginLineTransitionLocked(irq);
175 const bool firstHandler = !
m_IrqState.handlerCount(irq);
177 finishLineTransitionLocked(irq);
182 advanceThreadedCookieLocked(irq);
184 m_ThreadedHardVetoRecovery[irq] =
false;
185 m_ThreadedHardVetoRecoveryGenerations[irq] = 0;
187 m_IrqState.handlerRegistered(irq, policy, IrqDelivery::Threaded);
188 finishLineTransitionLocked(irq);
189 publishDiagnosticLineLocked(irq);
191 return irq + BASE_INTERRUPT_VECTOR;
196 if (
UNLIKELY(irq >= PicIrqState::LineCount || !handler || !policy.validForHard() ||
197 policy.trigger() == IrqTrigger::Synthetic))
203 if (!
m_IrqState.canRegister(irq, policy, IrqDelivery::Hard)) {
204 ERROR(
"PIC: IRQ " <<
Dec << irq <<
" was registered with incompatible trigger modes");
207 beginLineTransitionLocked(irq);
208 const bool firstHandler = !
m_IrqState.handlerCount(irq);
210 finishLineTransitionLocked(irq);
215 advanceThreadedCookieLocked(irq);
217 m_ThreadedHardVetoRecovery[irq] =
false;
218 m_ThreadedHardVetoRecoveryGenerations[irq] = 0;
220 m_IrqState.handlerRegistered(irq, policy, IrqDelivery::Hard);
221 finishLineTransitionLocked(irq);
222 publishDiagnosticLineLocked(irq);
224 return irq + BASE_INTERRUPT_VECTOR;
227 if (irq >= PicIrqState::LineCount)
236 beginLineTransitionLocked(irq);
237 const uint8_t previous = m_ElcrPort.
read8(irq / 8);
238 if (!claimPciTriggerLocked(irq)) {
239 if (m_ElcrPort.
read8(irq / 8) != previous)
240 panic(
"PIC: cannot roll back failed PCI route reservation");
241 finishLineTransitionLocked(irq);
245 finishLineTransitionLocked(irq);
246 publishDiagnosticLineLocked(irq);
250bool Pic::claimPciTriggerLocked(uint8_t irq) {
253 uint8_t previous = 0;
255 irq,
true, [
this](
size_t bank) {
return m_ElcrPort.
read8(bank); },
256 [
this](
size_t bank, uint8_t value) { m_ElcrPort.
write8(value, bank); }, previous)) {
257 ERROR(
"PIC: cannot configure PCI IRQ " <<
Dec << irq <<
" as level triggered");
260 const uint16_t bit = uint16_t{1} << irq;
261 if (!(m_OwnedElcr & bit)) {
262 m_OriginalElcr = (m_OriginalElcr & ~bit) | ((uint16_t(previous) << ((irq / 8) * 8)) & bit);
268void Pic::restorePciTriggerLocked(uint8_t irq) {
269 const uint16_t bit = uint16_t{1} << irq;
270 if (!(m_OwnedElcr & bit) ||
m_IrqState.pciRouteReserved(irq))
272 uint8_t previous = 0;
274 irq, m_OriginalElcr & bit, [
this](
size_t bank) {
return m_ElcrPort.
read8(bank); },
275 [
this](
size_t bank, uint8_t value) { m_ElcrPort.
write8(value, bank); }, previous))
276 panic(
"PIC: cannot restore retired PCI trigger mode");
285 !policy.validForThreaded() || policy.trigger() != IrqTrigger::Level))
292 if (!
m_IrqState.canRegister(irq, policy, IrqDelivery::Threaded)) {
293 ERROR(
"PIC: PCI IRQ " <<
Dec << irq <<
" conflicts with an edge-triggered handler");
296 beginLineTransitionLocked(irq);
297 const bool triggerOwned = m_OwnedElcr & (uint16_t{1} << irq);
298 if (!claimPciTriggerLocked(irq)) {
299 finishLineTransitionLocked(irq);
302 const bool firstHandler = !
m_IrqState.handlerCount(irq);
305 restorePciTriggerLocked(irq);
306 finishLineTransitionLocked(irq);
311 advanceThreadedCookieLocked(irq);
313 m_ThreadedHardVetoRecovery[irq] =
false;
314 m_ThreadedHardVetoRecoveryGenerations[irq] = 0;
316 m_IrqState.handlerRegistered(irq, policy, IrqDelivery::Threaded);
317 finishLineTransitionLocked(irq);
318 publishDiagnosticLineLocked(irq);
320 return irq + BASE_INTERRUPT_VECTOR;
328 if (
UNLIKELY(irq >= PicIrqState::LineCount || !handler || !policy.validForHard() ||
329 policy.trigger() != IrqTrigger::Level))
335 if (!
m_IrqState.canRegister(irq, policy, IrqDelivery::Hard)) {
336 ERROR(
"PIC: PCI IRQ " <<
Dec << irq <<
" conflicts with an edge-triggered handler");
339 beginLineTransitionLocked(irq);
340 const bool triggerOwned = m_OwnedElcr & (uint16_t{1} << irq);
341 if (!claimPciTriggerLocked(irq)) {
342 finishLineTransitionLocked(irq);
345 const bool firstHandler = !
m_IrqState.handlerCount(irq);
348 restorePciTriggerLocked(irq);
349 finishLineTransitionLocked(irq);
354 advanceThreadedCookieLocked(irq);
356 m_ThreadedHardVetoRecovery[irq] =
false;
357 m_ThreadedHardVetoRecoveryGenerations[irq] = 0;
359 m_IrqState.handlerRegistered(irq, policy, IrqDelivery::Hard);
360 finishLineTransitionLocked(irq);
361 publishDiagnosticLineLocked(irq);
363 return irq + BASE_INTERRUPT_VECTOR;
368 if (irq >= PicIrqState::LineCount || !handler)
373 !
m_IrqState.canRegisterScheduler(irq, policy))
376 beginLineTransitionLocked(irq);
379 finishLineTransitionLocked(irq);
380 publishDiagnosticLineLocked(irq);
381 return irq + BASE_INTERRUPT_VECTOR;
385 if (Id != BASE_INTERRUPT_VECTOR || !handler)
392 beginLineTransitionLocked(0);
395 finishLineTransitionLocked(0);
396 publishDiagnosticLineLocked(0);
400void Pic::finishHandlerUnregisterLocked(uint8_t irq, IrqHandlerRegistry::UnregisterResult result,
401 IrqHandlerRegistry::LineMode removedDelivery) {
402 if (
UNLIKELY(irq >= PicIrqState::LineCount)) {
403 FATAL_NOLOCK(
"PIC handler unregister has an invalid IRQ.");
408 if (result == IrqHandlerRegistry::UnregisterResult::Completed ||
409 result == IrqHandlerRegistry::UnregisterResult::Deferred) {
410 assert(removedDelivery == IrqHandlerRegistry::LineMode::Threaded ||
411 removedDelivery == IrqHandlerRegistry::LineMode::HardOnly);
413 const IrqDelivery delivery = removedDelivery == IrqHandlerRegistry::LineMode::Threaded
414 ? IrqDelivery::Threaded
416 m_IrqState.handlerUnregistered(irq, delivery);
418 if (delivery == IrqDelivery::Hard && !
m_IrqState.hardHandlerCount(irq)) {
421 const bool hardStageEnded =
422 previousDelivery == IrqDelivery::Mixed && currentDelivery == IrqDelivery::Threaded;
423 const bool hardQuarantineEnded =
425 if (hardQuarantineEnded) {
428 if (hardStageEnded || hardQuarantineEnded) {
429 if (currentDelivery == IrqDelivery::Threaded || currentDelivery == IrqDelivery::Mixed) {
430 m_ThreadedHardAdmitted[irq] =
true;
431 m_ThreadedHardDisposition[irq] = HardIrqDisposition::Handled;
439 m_ThreadedHardVetoRecovery[irq] =
false;
440 m_ThreadedHardVetoRecoveryGenerations[irq] = 0;
443 if (currentDelivery == IrqDelivery::None ||
444 (previousDelivery == IrqDelivery::Mixed && currentDelivery == IrqDelivery::Hard)) {
445 if (currentDelivery == IrqDelivery::Hard) {
451 const size_t boundary = advanceThreadedCookieLocked(irq);
455 m_ThreadedHardAdmitted[irq] =
false;
456 m_ThreadedHardDisposition[irq] = HardIrqDisposition::NotHandled;
457 m_ThreadedHardVetoRecovery[irq] =
false;
458 m_ThreadedHardVetoRecoveryGenerations[irq] = 0;
460 if (currentDelivery == IrqDelivery::None) {
461 restorePciTriggerLocked(irq);
465 finishLineTransitionLocked(irq);
466 publishDiagnosticLineLocked(irq);
470 if (Id < BASE_INTERRUPT_VECTOR || Id >= BASE_INTERRUPT_VECTOR + PicIrqState::LineCount ||
474 const uint8_t irq = Id - BASE_INTERRUPT_VECTOR;
475 IrqHandlerRegistry::LineMode removedDelivery = IrqHandlerRegistry::LineMode::Empty;
476 IrqHandlerRegistry::UnregisterResult result = IrqHandlerRegistry::UnregisterResult::Rejected;
485 beginLineTransitionLocked(irq);
487 finishHandlerUnregisterLocked(irq, result, removedDelivery);
495 beginLineTransitionLocked(irq);
501 if (!guard.owned()) {
502 FATAL(
"PIC unregister lost its reserved controller boundary.");
505 finishHandlerUnregisterLocked(irq, result, removedDelivery);
509 if (result == IrqHandlerRegistry::UnregisterResult::Rejected) {
510 __atomic_add_fetch(&m_RemovalRejections[irq],
static_cast<size_t>(1), __ATOMIC_RELAXED);
512 return result == IrqHandlerRegistry::UnregisterResult::Completed;
525 WARNING(
"PIC: PCI level-trigger routing is unavailable");
543 for (
size_t i = 0; i < 16; i++)
547 for (
size_t i = 0; i < 16; i++) {
548 __atomic_store_n(&
m_IrqCount[i],
static_cast<size_t>(0), __ATOMIC_RELAXED);
563 const uint64_t apicBase =
565 if (!LocalApicLint0Policy::isBootstrapProcessor(apicBase)) {
566 ERROR(
"PIC: threaded IRQ workers must be initialised on the BSP");
588 ERROR(
"PIC: only the BSP may join PIC threaded IRQ workers");
603 for (
size_t irq = 0; irq < PicIrqState::LineCount; ++irq) {
604 const uint8_t line =
static_cast<uint8_t
>(irq);
605 beginLineTransitionLocked(line);
606 const size_t boundary = advanceThreadedCookieLocked(line);
611 m_ThreadedHardAdmitted[irq] =
false;
612 m_ThreadedHardDisposition[irq] = HardIrqDisposition::NotHandled;
613 m_ThreadedHardVetoRecovery[irq] =
false;
614 m_ThreadedHardVetoRecoveryGenerations[irq] = 0;
619 publishAllDiagnosticLinesLocked();
625 : m_SlavePort(
"PIC #2"),
626 m_MasterPort(
"PIC #1"),
627 m_ElcrPort(
"PIC trigger modes"),
631 m_SchedulerIrqHandler(nullptr),
633 m_ControllerStateGate(),
635 m_ControllerThreadMutex(),
636 m_ThreadedDispatcher(MakeConstantString(
"PIC IRQ bottom half"), ControllerWorkLine + 1,
637 dispatchThreadedLine, this),
639 m_ThreadedDispatchGenerations(),
640 m_HardStageGenerations(),
641 m_ThreadedHadHardStage(),
642 m_ThreadedHardAdmitted(),
643 m_ThreadedHardDisposition(),
644 m_ThreadedHardVetoRecovery(),
645 m_ThreadedHardVetoRecoveryGenerations(),
646 m_FailClosedReasons(),
647 m_ThreadedPublicationFailures(),
648 m_RemovalRejections(),
649 m_ControllerContentions(),
650 m_ControllerPromptAttempts(0),
651 m_ControllerPromptFailures(0),
652 m_ControllerPromptDestination(0),
654 m_ControllerTemporaryMask(0),
655 m_AppliedControllerMask(0xFFFF),
656 m_DeferredLineTransitions(0),
657 m_DeliveryProcessor(0),
660 m_UnregisterReservations(),
661 m_ShuttingDown(false),
663 m_SpuriousIrqCount(),
664 m_UnhandledIrqCount(),
666 m_MitigationThreshold() {
667 publishAllDiagnosticLinesLocked();
670void Pic::publishDiagnosticLineLocked(uint8_t irq) {
671 if (irq >= PicIrqState::LineCount) {
675 size_t targetBank = 0;
683 line.handlerCount =
m_IrqState.handlerCount(irq);
684 line.configured = line.handlerCount != 0;
686 line.effectiveMasked =
688 line.requestedEnabled =
m_IrqState.requestedEnabled(irq);
689 line.acknowledgementPending =
m_IrqState.acknowledgementPending(irq);
690 line.threadedPending =
m_IrqState.threadedPending(irq);
691 line.dispatchGeneration =
m_IrqState.dispatchGeneration(irq);
692 line.acknowledgedGeneration =
m_IrqState.acknowledgedGeneration(irq);
694 line.interruptCount = __atomic_load_n(&
m_IrqCount[irq], __ATOMIC_RELAXED);
698 line.removalRejections = __atomic_load_n(&m_RemovalRejections[irq], __ATOMIC_RELAXED);
701 line.controllerPromptFailures = __atomic_load_n(&m_ControllerPromptFailures, __ATOMIC_RELAXED);
702 line.controllerPromptDestination =
703 __atomic_load_n(&m_ControllerPromptDestination, __ATOMIC_RELAXED);
705 __atomic_load_n(&m_ControllerPromptState, __ATOMIC_RELAXED));
707 if (!line.configured) {
708 line.maskReasons |= IrqMaskNoHandler;
711 line.controllerAck =
m_IrqState.controllerAck(irq);
712 line.lineRelease =
m_IrqState.lineRelease(irq);
714 if (!line.requestedEnabled) {
715 line.maskReasons |= IrqMaskAdministrativelyDisabled;
717 if (line.acknowledgementPending) {
718 line.maskReasons |= IrqMaskAwaitingAcknowledgement;
720 if (line.threadedPending) {
721 line.maskReasons |= IrqMaskAwaitingThreadedCompletion;
724 line.maskReasons |= IrqMaskMitigated;
727 line.maskReasons |= IrqMaskShuttingDown;
730 line.maskReasons |= IrqMaskControllerContention;
733 line.maskReasons |= IrqMaskControllerContention;
740void Pic::publishAllDiagnosticLinesLocked() {
741 for (
size_t irq = 0; irq < PicIrqState::LineCount; ++irq) {
742 publishDiagnosticLineLocked(
static_cast<uint8_t
>(irq));
747 if (!out || !capacity) {
751 const size_t count = capacity < PicIrqState::LineCount ? capacity : PicIrqState::LineCount;
752 for (
size_t irq = 0; irq < count; ++irq) {
755 out[irq].line =
static_cast<uint8_t
>(irq);
760 const size_t controllerPromptAttempts =
762 const size_t controllerPromptFailures =
763 __atomic_load_n(&m_ControllerPromptFailures, __ATOMIC_RELAXED);
764 const size_t controllerPromptDestination =
765 __atomic_load_n(&m_ControllerPromptDestination, __ATOMIC_RELAXED);
767 __atomic_load_n(&m_ControllerPromptState, __ATOMIC_RELAXED));
768 for (
size_t irq = 0; irq < count; ++irq) {
771 static_cast<uint8_t
>(irq), out[irq].activeHardDispatchGeneration);
772 out[irq].hardStageActive = out[irq].activeHardDispatchCount != 0;
774 static_cast<uint8_t
>(irq), out[irq].activeThreadedHandlerIdentity);
778 out[irq].interruptCount = __atomic_load_n(&
m_IrqCount[irq], __ATOMIC_RELAXED);
781 out[irq].publicationFailures =
783 out[irq].removalRejections = __atomic_load_n(&m_RemovalRejections[irq], __ATOMIC_RELAXED);
784 out[irq].controllerContentions =
786 out[irq].controllerPromptAttempts = controllerPromptAttempts;
787 out[irq].controllerPromptFailures = controllerPromptFailures;
788 out[irq].controllerPromptDestination = controllerPromptDestination;
789 out[irq].controllerPromptState = controllerPromptState;
790 out[irq].diagnosticPublicationFailures =
m_Diagnostics.missedPublications(irq);
792 out[irq].dispatcherInitialised = dispatcherInitialised;
800size_t Pic::advanceThreadedCookieLocked(uint8_t irq) {
801 assert(irq < PicIrqState::LineCount);
809void Pic::beginLineTransitionLocked(uint8_t irq) {
810 assert(irq < PicIrqState::LineCount);
817void Pic::finishLineTransitionLocked(uint8_t irq) {
818 assert(irq < PicIrqState::LineCount);
819 const uint16_t lineMask =
static_cast<uint16_t
>(1U << irq);
841 uint8_t mask = 1 << (irq - 8);
845 return (isr & mask) == 0;
848 uint8_t mask = 1 << irq;
852 return (isr & mask) == 0;
856bool Pic::drainOnePendingControllerBatchLocked() {
864 size_t realEntries[PicIrqState::LineCount] = {};
865 size_t staleRealEntries[PicIrqState::LineCount] = {};
866 size_t nonMutatingSpuriousEntries[PicIrqState::LineCount] = {};
867 size_t spuriousCascadeEois = 0;
868 size_t staleSpuriousCascadeEois = 0;
869 bool maskChanged =
false;
871 for (
size_t irq = 0; irq < PicIrqState::LineCount; ++irq) {
874 finishHardDispatchLocked(tail);
878 for (
size_t irq = 0; irq < PicIrqState::LineCount; ++irq) {
879 currentActions.entry[irq] = pending.entry[irq];
880 currentActions.tail[irq] = pending.tail[irq];
881 currentActions.tailEoi[irq] = pending.tailEoi[irq];
882 staleActions.entry[irq] = pending.staleEntry[irq];
883 staleActions.tail[irq] = pending.staleTail[irq];
884 staleActions.tailEoi[irq] = pending.staleTailEoi[irq];
886 for (
size_t occurrence = 0; occurrence < pending.entry[irq]; ++occurrence) {
888 __atomic_add_fetch(&
m_SpuriousIrqCount[irq],
static_cast<size_t>(1), __ATOMIC_RELAXED);
889 ++nonMutatingSpuriousEntries[irq];
891 ++spuriousCascadeEois;
898 for (
size_t occurrence = 0; occurrence < pending.staleEntry[irq]; ++occurrence) {
900 __atomic_add_fetch(&
m_SpuriousIrqCount[irq],
static_cast<size_t>(1), __ATOMIC_RELAXED);
901 ++nonMutatingSpuriousEntries[irq];
903 ++staleSpuriousCascadeEois;
906 ++staleRealEntries[irq];
910 const size_t contentions =
911 pending.entry[irq] + pending.tail[irq] + pending.staleEntry[irq] + pending.staleTail[irq];
917 m_IrqState, irq, realEntries[irq], pending.tail[irq], pending.tailEoi[irq]);
918 assert(result.threadedOccurrences <= realEntries[irq]);
919 assert(result.threadedOccurrences <= currentActions.entry[irq]);
920 for (
size_t occurrence = 0; occurrence < result.threadedOccurrences; ++occurrence) {
921 admitThreadedOccurrenceLocked(
static_cast<uint8_t
>(irq));
923 realEntries[irq] -= result.threadedOccurrences;
924 currentActions.entry[irq] -= result.threadedOccurrences;
925 if (result.terminalWork) {
926 __atomic_add_fetch(&
m_UnhandledIrqCount[irq], result.unhandledOccurrences, __ATOMIC_RELAXED);
929 if (result.schedulerDrop) {
936 }
else if (result.quarantine) {
940 if (result.invalidateThreaded) {
941 const size_t boundary = advanceThreadedCookieLocked(
static_cast<uint8_t
>(irq));
945 m_ThreadedHardAdmitted[irq] =
false;
946 m_ThreadedHardDisposition[irq] = HardIrqDisposition::NotHandled;
947 m_ThreadedHardVetoRecovery[irq] =
false;
948 m_ThreadedHardVetoRecoveryGenerations[irq] = 0;
950 maskChanged |= result.maskChanged;
952 const size_t staleUnhandled = staleRealEntries[irq] + pending.staleTail[irq];
953 if (staleUnhandled) {
958 const uint16_t temporaryMask = temporaryPicMaskForDeferredWork(
967 auto writeController = [
this](PicControllerWriteTarget target, uint8_t value) {
969 case PicControllerWriteTarget::MasterCommand:
972 case PicControllerWriteTarget::MasterMask:
975 case PicControllerWriteTarget::SlaveCommand:
978 case PicControllerWriteTarget::SlaveMask:
983 emitPicContentionWrites(
m_IrqState,
false, realEntries, currentActions, spuriousCascadeEois,
985 emitPicContentionWrites(
m_IrqState,
false, staleRealEntries, staleActions,
986 staleSpuriousCascadeEois, writeController);
988 for (
size_t irq = 0; irq < PicIrqState::LineCount; ++irq) {
989 if (pending.entry[irq] || pending.tail[irq] || pending.staleEntry[irq] ||
990 pending.staleTail[irq]) {
991 publishDiagnosticLineLocked(
static_cast<uint8_t
>(irq));
997void Pic::drainPendingControllerActionsLocked() {
998 while (drainOnePendingControllerBatchLocked()) {
1002bool Pic::handControllerStateToWorker() {
1010void Pic::releaseControllerState() {
1012 drainPendingControllerActionsLocked();
1014 finishDeferredLineTransitionsLocked();
1018 if (!deliveryProcessor) {
1022 const uint16_t restorable =
1025 clearControllerTemporaryMaskLocked(restorable);
1028 const uint16_t pendingUnmask =
1031 if (handControllerStateToWorker()) {
1036 "PIC controller restore work could not reach its BSP "
1043 clearControllerTemporaryMaskLocked();
1048 if (interruptsWereEnabled) {
1054 drainPendingControllerActionsLocked();
1056 finishDeferredLineTransitionsLocked();
1059 clearControllerTemporaryMaskLocked();
1062 if (interruptsWereEnabled) {
1077void Pic::releaseControllerStateFromInterrupt() {
1079 handControllerStateToWorker()) {
1085 drainOnePendingControllerBatchLocked();
1088 drainOnePendingControllerBatchLocked();
1092 if (handControllerStateToWorker()) {
1098 drainOnePendingControllerBatchLocked();
1104 finishDeferredLineTransitionsLocked();
1107 clearControllerTemporaryMaskLocked();
1115void Pic::admitThreadedOccurrenceLocked(uint8_t irq) {
1116 if (
UNLIKELY(irq >= PicIrqState::LineCount)) {
1117 FATAL_NOLOCK(
"PIC threaded occurrence has an invalid IRQ.");
1120 assert(
m_IrqState.delivery(irq) == IrqDelivery::Threaded);
1124 const size_t dispatchGeneration =
m_IrqState.beginDispatch(irq);
1127 const bool wasEnabled =
m_IrqState.enabled(irq);
1133 if (controllerAck != IrqControllerAck::None) {
1138 publishDiagnosticLineLocked(irq);
1145 if (lineRelease == IrqLineRelease::AfterThreadedCompletion) {
1148 if (controllerAck == IrqControllerAck::BeforeHardStage) {
1152 const size_t threadedCookie = advanceThreadedCookieLocked(irq);
1155 m_ThreadedHardAdmitted[irq] =
false;
1156 m_ThreadedHardDisposition[irq] = HardIrqDisposition::NotHandled;
1157 m_ThreadedHardVetoRecovery[irq] =
false;
1158 m_ThreadedHardVetoRecoveryGenerations[irq] = 0;
1168 m_ThreadedHardAdmitted[irq] =
false;
1169 m_ThreadedHardDisposition[irq] = HardIrqDisposition::NotHandled;
1170 const bool wasEnabled =
m_IrqState.enabled(irq);
1179 if (controllerAck == IrqControllerAck::AfterHardStage) {
1182 publishDiagnosticLineLocked(irq);
1186 size_t irq = (interruptNumber - BASE_INTERRUPT_VECTOR);
1187 if (irq >= PicIrqState::LineCount) {
1192 "Legacy PIC interrupt reached a processor whose LINT0 must be "
1197 __atomic_add_fetch(&
m_IrqCount[irq],
static_cast<size_t>(1), __ATOMIC_RELAXED);
1199 HardStateGuard entry(*
this,
static_cast<uint8_t
>(irq), controllerLifetime);
1200 if (!entry.owned()) {
1206 if (schedulerHandler) {
1208 __atomic_add_fetch(&
m_SpuriousIrqCount[irq],
static_cast<size_t>(1), __ATOMIC_RELAXED);
1209 publishDiagnosticLineLocked(
static_cast<uint8_t
>(irq));
1213 const size_t generation =
m_IrqState.beginDispatch(irq);
1217 eoiLocked(
static_cast<uint8_t
>(irq));
1220 publishDiagnosticLineLocked(
static_cast<uint8_t
>(irq));
1222 schedulerHandler->schedulerIrq(
static_cast<irq_id_t
>(irq), state);
1229 bool hasThreadedStage =
false;
1230 size_t dispatchGeneration = 0;
1231 size_t hardStageGeneration = 0;
1232 size_t threadedCookie = 0;
1233 bool threadedPublished =
false;
1245 __atomic_add_fetch(&
m_SpuriousIrqCount[irq],
static_cast<size_t>(1), __ATOMIC_RELAXED);
1246 publishDiagnosticLineLocked(
static_cast<uint8_t
>(irq));
1251 if (delivery == IrqDelivery::Threaded) {
1252 admitThreadedOccurrenceLocked(
static_cast<uint8_t
>(irq));
1256 controllerAck =
m_IrqState.controllerAck(irq);
1258 dispatchGeneration =
m_IrqState.beginDispatch(irq);
1260 hasThreadedStage = delivery == IrqDelivery::Mixed;
1263 bool cutoffCaptured =
false;
1264 if (delivery == IrqDelivery::Mixed) {
1267 hardAdmissionCutoff = cutoffs.hard;
1268 threadedAdmissionCutoff = cutoffs.threaded;
1276 if (!cutoffCaptured) {
1277 const bool wasEnabled =
m_IrqState.enabled(irq);
1279 hasThreadedStage ? ThreadedPublicationQuarantine : UnhandledQuarantine;
1284 if (controllerAck != IrqControllerAck::None) {
1289 static_cast<size_t>(1), __ATOMIC_RELAXED);
1290 publishDiagnosticLineLocked(
static_cast<uint8_t
>(irq));
1294 if (hasThreadedStage) {
1298 if (lineRelease == IrqLineRelease::AfterThreadedCompletion) {
1301 if (controllerAck == IrqControllerAck::BeforeHardStage) {
1304 threadedCookie = advanceThreadedCookieLocked(irq);
1307 m_ThreadedHardAdmitted[irq] =
false;
1308 m_ThreadedHardDisposition[irq] = HardIrqDisposition::NotHandled;
1309 m_ThreadedHardVetoRecovery[irq] =
false;
1310 m_ThreadedHardVetoRecoveryGenerations[irq] = 0;
1313 if (!threadedPublished) {
1316 m_ThreadedHardAdmitted[irq] =
false;
1317 m_ThreadedHardDisposition[irq] = HardIrqDisposition::NotHandled;
1325 }
else if (controllerAck == IrqControllerAck::BeforeHardStage) {
1328 publishDiagnosticLineLocked(
static_cast<uint8_t
>(irq));
1335 dispatchGeneration, hardAdmissionCutoff);
1338 tail.irq =
static_cast<uint8_t
>(irq);
1339 tail.controllerLifetime = controllerLifetime;
1340 tail.dispatchGeneration = dispatchGeneration;
1341 tail.hardStageGeneration = hardStageGeneration;
1342 tail.threadedCookie = threadedCookie;
1343 tail.controllerAck = controllerAck;
1344 tail.hardDisposition = hardDisposition;
1345 tail.hasThreadedStage = hasThreadedStage;
1346 tail.threadedPublished = threadedPublished;
1347 tail.admitted = admitted;
1348 finishHardDispatchFromInterrupt(tail);
1352 const uint8_t irq = record.irq;
1353 if (
UNLIKELY(irq >= PicIrqState::LineCount)) {
1354 FATAL_NOLOCK(
"PIC hard dispatch completion has an invalid IRQ.");
1357 const size_t dispatchGeneration = record.dispatchGeneration;
1358 const size_t threadedCookie = record.threadedCookie;
1360 const bool hasThreadedStage = record.hasThreadedStage;
1361 bool threadedPublished = record.threadedPublished;
1362 const bool admitted = record.admitted;
1366 current.dispatchGeneration =
m_IrqState.dispatchGeneration(irq);
1370 current.hardHandlerCount =
m_IrqState.hardHandlerCount(irq);
1371 current.delivery = hasThreadedStage ?
m_IrqState.delivery(irq) : IrqDelivery::None;
1375 terminal.applyTemporaryMask([
this, irq]() {
1380 const IrqDelivery currentDelivery = current.delivery;
1381 const bool hardStageLifetimeCurrent = plan.hardStageLifetimeCurrent;
1382 const bool threadedLifetimeCurrent = plan.threadedLifetimeCurrent;
1385 if (hasThreadedStage && (hardHandoffFailed || (threadedLifetimeCurrent && !threadedPublished))) {
1386 const bool wasEnabled =
m_IrqState.enabled(irq);
1387 if (hardHandoffFailed) {
1394 if (hardHandoffFailed) {
1400 if (hasThreadedStage) {
1401 if (threadedLifetimeCurrent) {
1402 if (!threadedPublished) {
1404 m_ThreadedHardAdmitted[irq] =
false;
1405 m_ThreadedHardDisposition[irq] = HardIrqDisposition::NotHandled;
1406 const bool wasEnabled =
m_IrqState.enabled(irq);
1411 terminal.acknowledge([
this, irq]() { eoiLocked(irq); });
1412 publishDiagnosticLineLocked(
static_cast<uint8_t
>(irq));
1415 const bool hardStageQuiesced =
1416 !hardStageLifetimeCurrent || currentDelivery == IrqDelivery::Threaded;
1417 m_ThreadedHardAdmitted[irq] = admitted || hardStageQuiesced;
1418 m_ThreadedHardDisposition[irq] = effectiveHardDisposition;
1421 if (doorbell.invalidateStagedDispatch) {
1423 if (doorbell.quarantine) {
1427 m_ThreadedHardAdmitted[irq] =
false;
1428 m_ThreadedHardDisposition[irq] = HardIrqDisposition::NotHandled;
1432 threadedPublished = doorbell.published;
1433 if (doorbell.completeDispatch) {
1434 const bool wasEnabled =
m_IrqState.enabled(irq);
1440 }
else if (plan.threadedAction == PicHardTailThreadedAction::Quiesced) {
1445 const bool threadedStageQuiesced =
true;
1446 const bool aggregateAdmitted = admitted || threadedStageQuiesced;
1447 const bool aggregateAllowRearm =
1448 (effectiveHardDisposition == HardIrqDisposition::Handled || threadedStageQuiesced) &&
1450 const bool wasEnabled =
m_IrqState.enabled(irq);
1452 aggregateAdmitted && !aggregateAllowRearm);
1453 if (!aggregateAdmitted || (!aggregateAllowRearm && !hardHandoffFailed)) {
1461 terminal.acknowledge([
this, irq]() { eoiLocked(irq); });
1462 publishDiagnosticLineLocked(
static_cast<uint8_t
>(irq));
1466 const bool wasEnabled =
m_IrqState.enabled(irq);
1467 const bool needsAcknowledgement =
1469 (admitted && effectiveHardDisposition == HardIrqDisposition::NotHandled);
1470 if (needsAcknowledgement) {
1472 ? HardHandoffQuarantine
1473 : UnhandledQuarantine;
1479 }
else if (hardStageLifetimeCurrent &&
1480 (!admitted || effectiveHardDisposition == HardIrqDisposition::NotHandled)) {
1486 terminal.acknowledge([
this, irq]() { eoiLocked(irq); });
1487 publishDiagnosticLineLocked(
static_cast<uint8_t
>(irq));
1492 finishHardDispatchLocked(record);
1493 releaseControllerStateFromInterrupt();
1499 releaseControllerStateFromInterrupt();
1508 record.controllerAck == IrqControllerAck::AfterHardStage)) {
1509 releaseControllerStateFromInterrupt();
1513void Pic::dispatchThreadedLine(
void* context, uint8_t irq,
size_t cookie) {
1514 Pic* pic =
reinterpret_cast<Pic*
>(context);
1515 if (irq == ControllerWorkLine) {
1516 StateGuard guard(*pic);
1518 FATAL(
"PIC controller continuation could not claim thread state.");
1522 size_t dispatchGeneration = 0;
1524 StateGuard guard(*pic);
1525 if (!guard.owned()) {
1526 FATAL(
"PIC threaded dispatch could not snapshot line state.");
1529 if (irq >= PicIrqState::LineCount) {
1534 (delivery != IrqDelivery::Threaded && delivery != IrqDelivery::Mixed)) {
1544 StateGuard guard(*pic);
1545 if (!guard.owned()) {
1546 FATAL(
"PIC threaded completion could not claim line state.");
1552 (delivery != IrqDelivery::Threaded && delivery != IrqDelivery::Mixed)) {
1557 const bool aggregateAdmitted = pic->m_ThreadedHardAdmitted[irq] || admitted;
1559 const bool aggregateAllowRearm =
1560 (hardDisposition == HardIrqDisposition::Handled || result.allowRearm) &&
1562 const bool hardVetoRecovery =
1564 result.allowRearm &&
1565 pic->
m_IrqState.lineRelease(irq) == IrqLineRelease::AfterThreadedCompletion;
1566 pic->m_ThreadedHardVetoRecovery[irq] = hardVetoRecovery;
1567 pic->m_ThreadedHardVetoRecoveryGenerations[irq] = hardVetoRecovery ? dispatchGeneration : 0;
1568 const bool wasEnabled = pic->
m_IrqState.enabled(irq);
1569 if (!aggregateAdmitted ||
1572 __atomic_add_fetch(&pic->
m_UnhandledIrqCount[irq],
static_cast<size_t>(1), __ATOMIC_RELAXED);
1574 if (hadHardStage && pic->
m_IrqState.lineRelease(irq) == IrqLineRelease::AfterHardStage) {
1576 aggregateAdmitted && !aggregateAllowRearm);
1579 aggregateAdmitted && aggregateAllowRearm);
1581 pic->m_ThreadedHardAdmitted[irq] =
false;
1582 pic->m_ThreadedHardDisposition[irq] = HardIrqDisposition::NotHandled;
1583 if (wasEnabled != pic->
m_IrqState.enabled(irq)) {
1584 pic->applyMaskLocked();
1586 pic->publishDiagnosticLineLocked(irq);
1591bool Pic::promptThreadedWorker(
void* context, uint8_t line,
size_t workerProcessor) {
1592 Pic* pic =
reinterpret_cast<Pic*
>(context);
1598 __atomic_store_n(&pic->m_ControllerPromptDestination,
static_cast<size_t>(pic->
m_DeliveryApicId),
1601 __atomic_add_fetch(&pic->m_ControllerPromptFailures,
static_cast<size_t>(1), __ATOMIC_RELAXED);
1602 __atomic_store_n(&pic->m_ControllerPromptState,
1603 static_cast<size_t>(IrqControllerPromptState::Failed), __ATOMIC_RELEASE);
1610 const bool submitted = Pc::instance().getLocalApic().interProcessorInterrupt(
1611 pic->
m_DeliveryApicId, IPI_RESCHEDULE_VECTOR, LocalApic::deliveryModeFixed,
true,
false);
1613 __atomic_add_fetch(&pic->m_ControllerPromptFailures,
static_cast<size_t>(1), __ATOMIC_RELAXED);
1615 __atomic_store_n(&pic->m_ControllerPromptState,
1616 static_cast<size_t>(submitted ? IrqControllerPromptState::Submitted
1623void Pic::eoiLocked(uint8_t irq) {
1634void Pic::applyMaskLocked() {
1635 const uint16_t canonical =
m_IrqState.mask();
1636 const bool canUnmaskBeforeRelease =
1641 const uint16_t mask = effectiveMaskLocked();
1642 emitPicMaskWrites(mask, [
this](PicControllerWriteTarget target, uint8_t value) {
1643 if (target == PicControllerWriteTarget::MasterMask) {
1652uint16_t Pic::effectiveMaskLocked()
const {
1656void Pic::finishDeferredLineTransitionsLocked() {
1660 for (
size_t irq = 0; irq < PicIrqState::LineCount; ++irq) {
1661 if ((pending &
static_cast<uint16_t
>(1U << irq)) && !
m_IrqState.lineTransitionPending(irq)) {
1671void Pic::clearControllerTemporaryMaskLocked(uint16_t restoreMask) {
1678 finishDeferredLineTransitionsLocked();
1682 for (
size_t irq = 0; irq < PicIrqState::LineCount; ++irq) {
1683 if (restored &
static_cast<uint16_t
>(1U << irq)) {
1684 publishDiagnosticLineLocked(
static_cast<uint8_t
>(irq));
1689void Pic::setEnabledLocked(uint8_t irq,
bool enable) {
1694void Pic::enable(uint8_t irq,
bool enable) {
1695 if (irq >= PicIrqState::LineCount) {
1699 StateGuard guard(*
this);
1705 setEnabledLocked(irq, enable);
1706 publishDiagnosticLineLocked(irq);
1708void Pic::enableAll(
bool enable) {
1709 StateGuard guard(*
this);
1710 if (!guard.owned()) {
1711 FATAL(
"PIC could not claim controller state for a global mask.");
1716 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 dispatchThreaded(uint8_t irq, size_t dispatchGeneration, ThreadedDispatchResult &result, IrqHandler *onlyHandler=nullptr)
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)
void invalidateThreadedLine(uint8_t irq, size_t throughGeneration)
bool hardLineQuarantined(uint8_t irq) const
bool dispatchHard(uint8_t irq, InterruptState &state, HardIrqDisposition &disposition, HardIrqHandler *onlyHandler=nullptr, size_t dispatchGeneration=0)
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 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]
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
uint16_t m_DeferredLineTransitions
bool initialiseThreaded()
bool reservePciRoute(uint8_t irq)
size_t m_SpuriousIrqCount[16]
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 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