8#ifndef PEDIGREE_KERNEL_MACHINE_MACH_PC_PICIRQSTATE_H
9#define PEDIGREE_KERNEL_MACHINE_MACH_PC_PICIRQSTATE_H
10#include "pedigree/kernel/machine/IrqHandler.h"
11#include "pedigree/kernel/machine/IrqManager.h"
12#include "pedigree/kernel/processor/types.h"
13#include "pedigree/kernel/utilities/assert.h"
30 static constexpr size_t LineCount = 16;
31 static constexpr size_t CascadeIrq = 2;
33 enum class TriggerMode : uint8_t {
40 for (
size_t i = 0; i < LineCount; ++i) {
41 m_TriggerModes[i] = TriggerMode::Unconfigured;
42 m_ControllerAck[i] = IrqControllerAck::AfterHardStage;
43 m_HardHandlerCounts[i] = 0;
44 m_ThreadedHandlerCounts[i] = 0;
45 m_DispatchGenerations[i] = 0;
46 m_AcknowledgedGenerations[i] = 0;
47 m_AcknowledgementPending[i] =
false;
48 m_ThreadedPending[i] =
false;
49 m_TransitionPending[i] =
false;
50 m_RequestedEnabled[i] =
true;
51 m_SchedulerOwned[i] =
false;
52 m_PciRouteReserved[i] =
false;
57 if (irq >= LineCount || irq == CascadeIrq || m_SchedulerOwned[irq] ||
58 (delivery != IrqDelivery::Hard && delivery != IrqDelivery::Threaded)) {
62 if ((delivery == IrqDelivery::Hard && !policy.validForHard()) ||
63 (delivery == IrqDelivery::Threaded && !policy.validForThreaded())) {
67 const TriggerMode requested =
68 policy.trigger() == IrqTrigger::Edge ? TriggerMode::Edge : TriggerMode::Level;
69 return m_TriggerModes[irq] == TriggerMode::Unconfigured ||
70 (m_TriggerModes[irq] == requested && m_ControllerAck[irq] == policy.controllerAck());
73 bool canRegister(
size_t irq,
const IrqPolicy& policy)
const {
74 return canRegister(irq, policy, legacyDelivery(policy));
77 bool canReservePciRoute(
size_t irq)
const {
78 return irq < LineCount && (PicElcrProgrammable & bit(irq)) &&
79 canRegister(irq, IrqPolicy::pciIntxThreaded(), IrqDelivery::Threaded);
84 assert(canReservePciRoute(irq));
85 m_PciRouteReserved[irq] =
true;
86 m_TriggerModes[irq] = TriggerMode::Level;
87 m_ControllerAck[irq] = IrqControllerAck::AfterHardStage;
88 if (!handlerCount(irq))
89 m_RequestedEnabled[irq] =
false;
93 bool pciRouteReserved(
size_t irq)
const {
94 assert(irq < LineCount);
95 return m_PciRouteReserved[irq];
98 bool canRegisterScheduler(
size_t irq,
const IrqPolicy& policy)
const {
99 return irq == 0 && !m_SchedulerOwned[irq] && !handlerCount(irq) &&
100 policy == IrqPolicy::edgeHard();
103 void schedulerRegistered(
size_t irq,
const IrqPolicy& policy) {
104 assert(canRegisterScheduler(irq, policy));
105 handlerRegistered(irq, policy, IrqDelivery::Hard);
106 m_SchedulerOwned[irq] =
true;
109 void schedulerUnregistered(
size_t irq) {
110 assert(irq < LineCount && m_SchedulerOwned[irq]);
111 m_SchedulerOwned[irq] =
false;
112 handlerUnregistered(irq, IrqDelivery::Hard);
115 bool schedulerRegistered(
size_t irq)
const {
116 assert(irq < LineCount);
117 return m_SchedulerOwned[irq];
121 assert(canRegister(irq, policy, delivery));
122 const bool firstHandler = handlerCount(irq) == 0;
123 if (m_TriggerModes[irq] == TriggerMode::Unconfigured) {
124 m_TriggerModes[irq] =
125 policy.trigger() == IrqTrigger::Edge ? TriggerMode::Edge : TriggerMode::Level;
126 m_ControllerAck[irq] = policy.controllerAck();
128 if (delivery == IrqDelivery::Hard) {
129 ++m_HardHandlerCounts[irq];
131 ++m_ThreadedHandlerCounts[irq];
134 m_AcknowledgementPending[irq] =
false;
135 m_ThreadedPending[irq] =
false;
136 m_AcknowledgedGenerations[irq] = m_DispatchGenerations[irq];
137 m_RequestedEnabled[irq] =
true;
142 void handlerRegistered(
size_t irq,
const IrqPolicy& policy) {
143 handlerRegistered(irq, policy, legacyDelivery(policy));
146 void handlerUnregistered(
size_t irq,
IrqDelivery delivery) {
147 assert(irq < LineCount);
148 assert(delivery == IrqDelivery::Hard || delivery == IrqDelivery::Threaded);
149 if (delivery == IrqDelivery::Hard) {
150 assert(m_HardHandlerCounts[irq]);
151 --m_HardHandlerCounts[irq];
153 assert(m_ThreadedHandlerCounts[irq]);
154 --m_ThreadedHandlerCounts[irq];
155 if (!m_ThreadedHandlerCounts[irq] && m_ThreadedPending[irq]) {
156 m_ThreadedPending[irq] =
false;
161 if (!handlerCount(irq)) {
162 m_AcknowledgementPending[irq] =
false;
163 m_ThreadedPending[irq] =
false;
164 m_AcknowledgedGenerations[irq] = m_DispatchGenerations[irq];
165 m_RequestedEnabled[irq] =
false;
166 m_TriggerModes[irq] =
167 m_PciRouteReserved[irq] ? TriggerMode::Level : TriggerMode::Unconfigured;
168 m_ControllerAck[irq] = IrqControllerAck::AfterHardStage;
173 void handlerUnregistered(
size_t irq) {
174 assert(irq < LineCount);
175 assert(!m_HardHandlerCounts[irq] || !m_ThreadedHandlerCounts[irq]);
176 handlerUnregistered(irq,
177 m_ThreadedHandlerCounts[irq] ? IrqDelivery::Threaded :
IrqDelivery::Hard);
180 size_t beginDispatch(
size_t irq) {
181 assert(irq < LineCount);
182 size_t generation = ++m_DispatchGenerations[irq];
184 generation = ++m_DispatchGenerations[irq];
195 assert(irq < LineCount);
196 if (!needsAcknowledgement || !handlerCount(irq) ||
197 generationReached(m_AcknowledgedGenerations[irq], dispatchGeneration)) {
201 m_AcknowledgementPending[irq] =
true;
205 bool acknowledge(
size_t irq) {
206 assert(irq < LineCount);
207 if (!handlerCount(irq)) {
211 m_AcknowledgedGenerations[irq] = m_DispatchGenerations[irq];
212 if (m_AcknowledgementPending[irq]) {
213 m_AcknowledgementPending[irq] =
false;
221 assert(irq < LineCount);
222 if (lineRelease(irq) == IrqLineRelease::AfterThreadedCompletion) {
223 m_ThreadedPending[irq] =
true;
233 assert(irq < LineCount);
234 if (m_DispatchGenerations[irq] != dispatchGeneration) {
238 if (lineRelease(irq) == IrqLineRelease::AfterThreadedCompletion &&
239 (allowRearm || !handlerCount(irq))) {
240 m_ThreadedPending[irq] =
false;
246 bool threadedPending(
size_t irq)
const {
247 assert(irq < LineCount);
248 return m_ThreadedPending[irq];
251 bool acknowledgementPending(
size_t irq)
const {
252 assert(irq < LineCount);
253 return m_AcknowledgementPending[irq];
256 void beginLineTransition(
size_t irq) {
257 assert(irq < LineCount);
258 m_TransitionPending[irq] =
true;
262 void finishLineTransition(
size_t irq) {
263 assert(irq < LineCount);
264 m_TransitionPending[irq] =
false;
268 bool lineTransitionPending(
size_t irq)
const {
269 assert(irq < LineCount);
270 return m_TransitionPending[irq];
273 size_t handlerCount(
size_t irq)
const {
274 assert(irq < LineCount);
275 return m_HardHandlerCounts[irq] + m_ThreadedHandlerCounts[irq];
278 size_t hardHandlerCount(
size_t irq)
const {
279 assert(irq < LineCount);
280 return m_HardHandlerCounts[irq];
283 size_t threadedHandlerCount(
size_t irq)
const {
284 assert(irq < LineCount);
285 return m_ThreadedHandlerCounts[irq];
289 assert(irq < LineCount);
290 const bool hard = m_HardHandlerCounts[irq] != 0;
291 const bool threaded = m_ThreadedHandlerCounts[irq] != 0;
292 if (hard && threaded) {
293 return IrqDelivery::Mixed;
296 return IrqDelivery::Hard;
299 return IrqDelivery::Threaded;
301 return IrqDelivery::None;
304 bool edgeTriggered(
size_t irq)
const {
305 assert(irq < LineCount);
306 return m_TriggerModes[irq] == TriggerMode::Edge;
310 assert(irq < LineCount);
311 return m_TriggerModes[irq] == TriggerMode::Edge ? IrqTrigger::Edge : IrqTrigger::Level;
315 assert(irq < LineCount);
316 return m_ControllerAck[irq];
320 assert(irq < LineCount);
321 return m_TriggerModes[irq] == TriggerMode::Level && m_ThreadedHandlerCounts[irq]
322 ? IrqLineRelease::AfterThreadedCompletion
323 : IrqLineRelease::AfterHardStage;
326 bool enabled(
size_t irq)
const {
327 assert(irq < LineCount);
328 return (m_Mask & bit(irq)) == 0;
331 bool requestedEnabled(
size_t irq)
const {
332 assert(irq < LineCount);
333 return m_RequestedEnabled[irq];
336 size_t dispatchGeneration(
size_t irq)
const {
337 assert(irq < LineCount);
338 return m_DispatchGenerations[irq];
341 size_t acknowledgedGeneration(
size_t irq)
const {
342 assert(irq < LineCount);
343 return m_AcknowledgedGenerations[irq];
346 void setEnabled(
size_t irq,
bool enabled) {
347 assert(irq < LineCount);
348 m_RequestedEnabled[irq] = enabled;
352 void setAllEnabled(
bool enabled) {
353 for (
size_t i = 0; i < LineCount; ++i) {
354 m_RequestedEnabled[i] = enabled;
360 m_RequestedEnabled[2] =
true;
365 uint16_t mask()
const {
369 uint8_t masterMask()
const {
370 return static_cast<uint8_t
>(m_Mask & 0xFF);
373 uint8_t slaveMask()
const {
374 return static_cast<uint8_t
>(m_Mask >> 8);
379 if (policy.lineRelease() == IrqLineRelease::AfterThreadedCompletion ||
380 (policy.trigger() == IrqTrigger::Edge &&
381 policy.controllerAck() == IrqControllerAck::AfterHardStage)) {
382 return IrqDelivery::Threaded;
384 return IrqDelivery::Hard;
387 static uint16_t bit(
size_t irq) {
388 return static_cast<uint16_t
>(
static_cast<uint16_t
>(1U) << irq);
391 static bool generationReached(
size_t current,
size_t target) {
392 return static_cast<intptr_t
>(current - target) >= 0;
397 for (
size_t i = 0; i < LineCount; ++i) {
398 if (!m_RequestedEnabled[i] || m_AcknowledgementPending[i] || m_ThreadedPending[i] ||
399 m_TransitionPending[i] || (m_PciRouteReserved[i] && !handlerCount(i))) {
406 if ((mask &
static_cast<uint16_t
>(0xFF00)) !=
static_cast<uint16_t
>(0xFF00)) {
407 mask &=
static_cast<uint16_t
>(~bit(2));
413 TriggerMode m_TriggerModes[LineCount];
415 size_t m_HardHandlerCounts[LineCount];
416 size_t m_ThreadedHandlerCounts[LineCount];
417 size_t m_DispatchGenerations[LineCount];
418 size_t m_AcknowledgedGenerations[LineCount];
419 bool m_AcknowledgementPending[LineCount];
420 bool m_ThreadedPending[LineCount];
421 bool m_TransitionPending[LineCount];
422 bool m_RequestedEnabled[LineCount];
423 bool m_SchedulerOwned[LineCount];
424 bool m_PciRouteReserved[LineCount];
431 bool invalidateThreaded;
433 size_t threadedOccurrences;
434 size_t unhandledOccurrences;
439 size_t realEntries,
size_t deferredTails,
440 size_t deferredTailEois) {
441 assert(irq < PicIrqState::LineCount);
444 const uint16_t maskBefore = state.mask();
445 const bool hasTailAction = deferredTails || deferredTailEois;
446 result.terminalWork = realEntries || hasTailAction;
447 if (!result.terminalWork) {
451 if (irq == 0 && state.schedulerRegistered(0) && !hasTailAction) {
452 result.schedulerDrop =
true;
453 result.unhandledOccurrences = realEntries;
454 for (
size_t occurrence = 0; occurrence < realEntries; ++occurrence) {
455 const size_t generation = state.beginDispatch(irq);
456 state.acknowledge(irq);
459 result.maskChanged = state.mask() != maskBefore;
464 if (realEntries && !hasTailAction && delivery == IrqDelivery::Threaded) {
468 result.threadedOccurrences = realEntries;
472 result.quarantine =
true;
473 result.unhandledOccurrences = realEntries + deferredTails;
474 result.invalidateThreaded = delivery == IrqDelivery::Threaded || delivery == IrqDelivery::Mixed;
475 const size_t generation = state.beginDispatch(irq);
477 result.maskChanged = state.mask() != maskBefore;
491 static constexpr size_t LineCount = PicIrqState::LineCount;
492 static constexpr size_t CountBits = 12;
493 static constexpr size_t CountMask = (
static_cast<size_t>(1) << CountBits) - 1;
494 static constexpr size_t LifetimeMask =
static_cast<size_t>(~static_cast<size_t>(0)) >> CountBits;
495 static constexpr size_t TransitionLifetime = LifetimeMask;
497 static_assert(
sizeof(size_t) >=
sizeof(uint64_t),
498 "PIC lifetime-qualified handoff requires lock-free 64-bit words");
499 static_assert(__atomic_always_lock_free(
sizeof(
size_t),
nullptr),
500 "PIC controller handoff words must be lock-free");
508 for (
size_t i = 0; i < LineCount; ++i) {
519 for (
size_t i = 0; i < LineCount; ++i) {
520 if (entry[i] || tail[i] || tailEoi[i] || staleEntry[i] || staleTail[i] || staleTailEoi[i]) {
527 size_t entry[LineCount];
528 size_t tail[LineCount];
529 size_t tailEoi[LineCount];
530 size_t staleEntry[LineCount];
531 size_t staleTail[LineCount];
532 size_t staleTailEoi[LineCount];
536 for (
size_t i = 0; i < LineCount; ++i) {
537 m_LifetimeGenerations[i] = 1;
538 m_LineLifetimes[i] = 1;
539 m_EntryCounts[i] = tagged(1, 0);
540 m_TailNoEoiCounts[i] = tagged(1, 0);
541 m_TailEoiCounts[i] = tagged(1, 0);
542 m_StaleEntryCounts[i] = 0;
543 m_StaleTailNoEoiCounts[i] = 0;
544 m_StaleTailEoiCounts[i] = 0;
550 size_t expected = Clean;
551 return __atomic_compare_exchange_n(&m_State, &expected, Owner,
false, __ATOMIC_ACQUIRE,
557 const size_t state = __atomic_load_n(&m_State, __ATOMIC_ACQUIRE);
562 size_t expected = state;
563 return __atomic_compare_exchange_n(&m_State, &expected, state | Owner,
false, __ATOMIC_ACQ_REL,
567 size_t currentLifetime(
size_t irq)
const {
568 assert(irq < LineCount);
569 return __atomic_load_n(&m_LineLifetimes[irq], __ATOMIC_ACQUIRE);
574 assert(irq < LineCount);
575 replaceLifetime(irq, TransitionLifetime);
580 assert(irq < LineCount);
581 size_t lifetime = ++m_LifetimeGenerations[irq];
582 lifetime &= LifetimeMask;
583 if (!lifetime || lifetime == TransitionLifetime) {
585 m_LifetimeGenerations[irq] = lifetime;
587 replaceLifetime(irq, lifetime);
593 assert(irq < LineCount);
594 queueTagged(m_EntryCounts[irq], m_StaleEntryCounts[irq], lifetime);
595 publishPending(irq == 0);
600 bool queueTail(
size_t irq,
size_t lifetime,
bool owesEoi) {
601 assert(irq < LineCount);
603 queueTagged(m_TailEoiCounts[irq], m_StaleTailEoiCounts[irq], lifetime);
605 queueTagged(m_TailNoEoiCounts[irq], m_StaleTailNoEoiCounts[irq], lifetime);
607 publishPending(irq == 0);
613 assert(irq < LineCount);
614 publishPending(irq == 0);
621 size_t expected = __atomic_load_n(&m_State, __ATOMIC_ACQUIRE);
623 if ((expected & (Owner | Pending)) != (Owner | Pending)) {
626 }
while (!__atomic_compare_exchange_n(&m_State, &expected, Owner,
false, __ATOMIC_ACQ_REL,
629 for (
size_t i = 0; i < LineCount; ++i) {
630 const size_t lifetime = currentLifetime(i);
631 takeTagged(m_EntryCounts[i], m_StaleEntryCounts[i], lifetime, actions.entry[i],
632 actions.staleEntry[i]);
634 size_t tailNoEoi = 0;
635 size_t staleTailNoEoi = 0;
636 takeTagged(m_TailNoEoiCounts[i], m_StaleTailNoEoiCounts[i], lifetime, tailNoEoi,
638 takeTagged(m_TailEoiCounts[i], m_StaleTailEoiCounts[i], lifetime, actions.tailEoi[i],
639 actions.staleTailEoi[i]);
640 actions.tail[i] = tailNoEoi + actions.tailEoi[i];
641 actions.staleTail[i] = staleTailNoEoi + actions.staleTailEoi[i];
648 size_t expected = Owner;
649 return __atomic_compare_exchange_n(&m_State, &expected, Clean,
false, __ATOMIC_RELEASE,
653 bool hasPending()
const {
654 return (__atomic_load_n(&m_State, __ATOMIC_ACQUIRE) & Pending) != 0;
657 bool urgentPending()
const {
658 return (__atomic_load_n(&m_State, __ATOMIC_ACQUIRE) & Urgent) != 0;
663 publishPending(
false);
668 size_t expected = Owner | Pending;
669 return __atomic_compare_exchange_n(&m_State, &expected, Pending,
false, __ATOMIC_RELEASE,
674 static constexpr size_t Clean = 0;
675 static constexpr size_t Owner = 1U << 0;
676 static constexpr size_t Pending = 1U << 1;
677 static constexpr size_t Urgent = 1U << 2;
679 static size_t tagged(
size_t lifetime,
size_t count) {
680 return ((lifetime & LifetimeMask) << CountBits) | (count & CountMask);
683 static size_t taggedLifetime(
size_t value) {
684 return value >> CountBits;
687 static size_t taggedCount(
size_t value) {
688 return value & CountMask;
691 static void addStale(
size_t& counter,
size_t count) {
693 __atomic_fetch_add(&counter, count, __ATOMIC_RELAXED);
697 static void queueTagged(
size_t& currentCounter,
size_t& staleCounter,
size_t lifetime) {
698 const size_t qualifiedLifetime = lifetime & LifetimeMask;
699 if (!qualifiedLifetime || qualifiedLifetime == TransitionLifetime) {
700 addStale(staleCounter, 1);
704 size_t expected = __atomic_load_n(¤tCounter, __ATOMIC_ACQUIRE);
705 const size_t count = taggedCount(expected);
706 if (taggedLifetime(expected) != qualifiedLifetime || count == CountMask ||
707 !__atomic_compare_exchange_n(¤tCounter, &expected,
708 tagged(qualifiedLifetime, count + 1),
false, __ATOMIC_ACQ_REL,
713 addStale(staleCounter, 1);
717 static void takeTagged(
size_t& currentCounter,
size_t& staleCounter,
size_t lifetime,
718 size_t& current,
size_t& stale) {
720 __atomic_exchange_n(¤tCounter, tagged(lifetime, 0), __ATOMIC_ACQ_REL);
721 const size_t count = taggedCount(value);
722 if (taggedLifetime(value) == lifetime) {
727 stale += __atomic_exchange_n(&staleCounter,
static_cast<size_t>(0), __ATOMIC_ACQ_REL);
730 void replaceLifetime(
size_t irq,
size_t lifetime) {
732 size_t value = __atomic_exchange_n(&m_EntryCounts[irq], tagged(lifetime, 0), __ATOMIC_ACQ_REL);
733 moved += taggedCount(value);
734 addStale(m_StaleEntryCounts[irq], taggedCount(value));
736 value = __atomic_exchange_n(&m_TailNoEoiCounts[irq], tagged(lifetime, 0), __ATOMIC_ACQ_REL);
737 moved += taggedCount(value);
738 addStale(m_StaleTailNoEoiCounts[irq], taggedCount(value));
740 value = __atomic_exchange_n(&m_TailEoiCounts[irq], tagged(lifetime, 0), __ATOMIC_ACQ_REL);
741 moved += taggedCount(value);
742 addStale(m_StaleTailEoiCounts[irq], taggedCount(value));
744 __atomic_store_n(&m_LineLifetimes[irq], lifetime, __ATOMIC_RELEASE);
746 publishPending(irq == 0);
750 void publishPending(
bool urgent) {
751 __atomic_fetch_or(&m_State, Pending | (urgent ? Urgent : 0), __ATOMIC_RELEASE);
755 size_t m_LifetimeGenerations[LineCount];
756 size_t m_LineLifetimes[LineCount];
757 size_t m_EntryCounts[LineCount];
758 size_t m_TailNoEoiCounts[LineCount];
759 size_t m_TailEoiCounts[LineCount];
760 size_t m_StaleEntryCounts[LineCount];
761 size_t m_StaleTailNoEoiCounts[LineCount];
762 size_t m_StaleTailEoiCounts[LineCount];
771 size_t controllerLifetime;
772 size_t dispatchGeneration;
773 size_t hardStageGeneration;
774 size_t threadedCookie;
777 bool hasThreadedStage;
778 bool threadedPublished;
784 size_t controllerLifetime;
785 size_t dispatchGeneration;
786 size_t hardStageGeneration;
787 size_t threadedCookie;
788 size_t threadedDispatchGeneration;
789 size_t hardHandlerCount;
791 bool hardLineQuarantined;
794enum class PicHardTailThreadedAction : uint8_t {
804 bool controllerLifetimeCurrent;
805 bool hardStageLifetimeCurrent;
806 bool threadedLifetimeCurrent;
808 PicHardTailThreadedAction threadedAction;
814 plan.controllerLifetimeCurrent = record.controllerLifetime == current.controllerLifetime;
815 plan.hardStageLifetimeCurrent = record.hardStageGeneration == current.hardStageGeneration;
816 plan.threadedLifetimeCurrent =
817 record.hasThreadedStage && record.threadedCookie == current.threadedCookie &&
818 record.dispatchGeneration == current.threadedDispatchGeneration &&
819 (current.delivery == IrqDelivery::Threaded || current.delivery == IrqDelivery::Mixed);
820 plan.effectiveHardDisposition =
821 !plan.hardStageLifetimeCurrent ||
822 (record.hasThreadedStage && current.delivery == IrqDelivery::Threaded) ||
824 !current.hardLineQuarantined)
825 ? HardIrqDisposition::Handled
826 : record.hardDisposition;
828 if (!record.hasThreadedStage) {
829 plan.threadedAction = PicHardTailThreadedAction::None;
830 }
else if (plan.threadedLifetimeCurrent) {
831 plan.threadedAction = record.threadedPublished ? PicHardTailThreadedAction::Publish
832 : PicHardTailThreadedAction::MissingPublication;
833 }
else if (record.dispatchGeneration == current.dispatchGeneration && current.hardHandlerCount &&
834 (current.delivery == IrqDelivery::Hard || current.delivery == IrqDelivery::Mixed)) {
835 plan.threadedAction = PicHardTailThreadedAction::Quiesced;
837 plan.threadedAction = PicHardTailThreadedAction::Stale;
844 bool invalidateStagedDispatch;
846 bool completeDispatch;
852 result.published = staged && accepted;
853 result.invalidateStagedDispatch = staged && !accepted;
854 result.quarantine = result.invalidateStagedDispatch;
855 result.completeDispatch = !result.published;
863 : m_TemporaryMaskRequired(temporaryMaskRequired),
864 m_TemporaryMaskApplied(
false),
865 m_ControllerAck(controllerAck),
866 m_Acknowledged(
false) {}
868 template <
typename Sink>
869 void applyTemporaryMask(Sink&& sink) {
870 if (!m_TemporaryMaskRequired) {
874 m_TemporaryMaskApplied =
true;
877 template <
typename Sink>
878 void acknowledge(Sink&& sink) {
879 assert(!m_Acknowledged);
880 if (m_ControllerAck == IrqControllerAck::AfterHardStage) {
881 assert(!m_TemporaryMaskRequired || m_TemporaryMaskApplied);
884 m_Acknowledged =
true;
888 bool m_TemporaryMaskRequired;
889 bool m_TemporaryMaskApplied;
905 static constexpr size_t LineCount = PicIrqState::LineCount;
908 for (
size_t irq = 0; irq < LineCount; ++irq) {
909 __atomic_store_n(&m_States[irq], Empty, __ATOMIC_RELAXED);
914 if (irq >= LineCount || record.irq != irq) {
918 size_t expected = Empty;
919 if (!__atomic_compare_exchange_n(&m_States[irq], &expected, Claimed,
false, __ATOMIC_ACQ_REL,
923 m_Records[irq] = record;
924 __atomic_store_n(&m_States[irq], Ready, __ATOMIC_RELEASE);
929 if (irq >= LineCount) {
933 size_t expected = Ready;
934 if (!__atomic_compare_exchange_n(&m_States[irq], &expected, Claimed,
false, __ATOMIC_ACQUIRE,
938 record = m_Records[irq];
942 void complete(uint8_t irq) {
943 assert(irq < LineCount);
944 size_t expected = Claimed;
945 const bool completed = __atomic_compare_exchange_n(&m_States[irq], &expected, Empty,
false,
946 __ATOMIC_RELEASE, __ATOMIC_RELAXED);
951 template <
typename Finalizer>
952 bool consume(uint8_t irq, Finalizer&& finalizer) {
954 if (!take(irq, record)) {
962 bool pending(uint8_t irq)
const {
963 return irq < LineCount && __atomic_load_n(&m_States[irq], __ATOMIC_ACQUIRE) == Ready;
967 static constexpr size_t Empty = 0;
968 static constexpr size_t Claimed = 1;
969 static constexpr size_t Ready = 2;
971 size_t m_States[LineCount];
978inline uint16_t effectivePicMask(uint16_t canonical, uint16_t temporary) {
979 return canonical | temporary;
983inline uint16_t preserveUnsafePicUnmasks(uint16_t applied, uint16_t canonical, uint16_t temporary,
984 uint16_t deferredTransitions,
985 bool canPublishBeforeRelease) {
986 if (!canPublishBeforeRelease) {
987 const uint16_t newlyUnmasked =
988 static_cast<uint16_t
>(applied & ~effectivePicMask(canonical, temporary));
989 temporary |=
static_cast<uint16_t
>(newlyUnmasked & deferredTransitions);
995inline uint16_t restorablePicTemporaryMask(uint16_t temporary, uint16_t deferredTransitions) {
996 return static_cast<uint16_t
>(temporary & ~deferredTransitions);
999inline uint16_t cancelDeferredPicLineTransition(uint16_t deferred,
size_t irq) {
1000 assert(irq < PicIrqState::LineCount);
1001 return static_cast<uint16_t
>(deferred & ~static_cast<uint16_t>(1U << irq));
1005inline uint16_t temporaryPicMaskForDeferredWork(
1006 uint16_t existing,
const size_t (&realEntries)[PicIrqState::LineCount],
1008 const size_t (&spuriousEntries)[PicIrqState::LineCount]) {
1009 uint16_t result = existing;
1010 for (
size_t irq = 0; irq < PicIrqState::LineCount; ++irq) {
1011 if (realEntries[irq] || actions.tailEoi[irq] || spuriousEntries[irq]) {
1012 result |=
static_cast<uint16_t
>(1U << irq);
1018enum class PicControllerWriteTarget : uint8_t {
1025template <
typename Sink>
1026void emitPicMaskWrites(uint16_t mask, Sink&& sink) {
1027 sink(PicControllerWriteTarget::MasterMask,
static_cast<uint8_t
>(mask & 0xFF));
1028 sink(PicControllerWriteTarget::SlaveMask,
static_cast<uint8_t
>(mask >> 8));
1032template <
typename Sink>
1033void emitPicContentionWrites(
const PicIrqState& state,
bool writeMask,
1034 const size_t (&realEntries)[PicIrqState::LineCount],
1036 size_t spuriousCascadeEois, Sink&& sink) {
1038 sink(PicControllerWriteTarget::MasterMask, state.masterMask());
1039 sink(PicControllerWriteTarget::SlaveMask, state.slaveMask());
1042 for (
size_t irq = 0; irq < PicIrqState::LineCount; ++irq) {
1043 const size_t owedEois = realEntries[irq] + pending.tailEoi[irq];
1044 for (
size_t occurrence = 0; occurrence < owedEois; ++occurrence) {
1046 sink(PicControllerWriteTarget::SlaveCommand,
static_cast<uint8_t
>(0x60 + (irq - 8)));
1047 sink(PicControllerWriteTarget::MasterCommand, 0x62);
1049 sink(PicControllerWriteTarget::MasterCommand,
static_cast<uint8_t
>(0x60 + irq));
1054 for (
size_t occurrence = 0; occurrence < spuriousCascadeEois; ++occurrence) {
1055 sink(PicControllerWriteTarget::MasterCommand, 0x62);
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)