The Pedigree Project 0.1
Pic.cc
1/*
2 * Copyright (c) 2008-2014, Pedigree Developers
3 *
4 * Please see the CONTRIB file in the root of the source tree for a full
5 * list of contributors.
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#include "Pic.h"
21
22#include "LocalApicLint0Policy.h"
23#include "PicElcr.h"
24#if MULTIPROCESSOR
25#include "LocalApic.h"
26#include "Pc.h"
27#include "PciMessageInterrupts.h"
28#if ACPI
29#include "Acpi.h"
30#include "PciIoApicInterrupts.h"
31#endif
32#endif
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"
45
46#define BASE_INTERRUPT_VECTOR 0x20
47
48// Number of IRQs in a single millisecond before an IRQ source is blocked.
49// A value of 10, for example, would mean if an IRQ matches the threshold
50// and sustained its output for a second, 10,000 IRQs would be triggered.
51#define DEFAULT_IRQ_MITIGATE_THRESHOLD 10
52
54
56 public:
57 explicit StateGuard(Pic& pic) : m_Pic(pic), m_Owned(false), m_ThreadOwned(false) {
58 const bool canWait = Processor::executionContext() == ExecutionContext::WaitableThread;
59 if (!canWait) {
60 // The main hard path releases its entry guard before callbacks,
61 // so callback-time line replacement can claim a clean gate. An
62 // atomic caller which races a thread owner must fail, not wait.
63 m_Owned = m_Pic.m_ControllerStateGate.tryClaim();
64 if (m_Owned) {
65 m_Pic.drainPendingControllerActionsLocked();
66 }
67 return;
68 }
69
70 m_ThreadOwned = m_Pic.m_ControllerThreadMutex.acquire();
71 if (!m_ThreadOwned) {
72 return;
73 }
74
75 while (!m_Pic.m_ControllerStateGate.tryClaim()) {
77 }
78
79 m_Owned = true;
80 m_Pic.drainPendingControllerActionsLocked();
81 }
82
83 ~StateGuard() {
84 if (m_Owned) {
85 m_Pic.releaseControllerState();
86 }
87 if (m_ThreadOwned) {
89 }
90 }
91
92 bool owned() const {
93 return m_Owned;
94 }
95
96 private:
97 Pic& m_Pic;
98 bool m_Owned;
99 bool m_ThreadOwned;
100};
101
103 public:
104 HardStateGuard(Pic& pic, uint8_t irq, size_t lifetime)
105 : m_Pic(pic), m_Owned(pic.m_ControllerStateGate.tryAcquireClean()) {
106 if (m_Owned && lifetime != PicControllerStateGate::TransitionLifetime &&
107 lifetime == m_Pic.m_ControllerStateGate.currentLifetime(irq)) {
108 return;
109 }
110
111 const bool claimed = m_Pic.m_ControllerStateGate.queueEntry(irq, lifetime);
112 if (m_Owned) {
113 m_Pic.releaseControllerStateFromInterrupt();
114 m_Owned = false;
115 return;
116 }
117 if (claimed) {
118 m_Pic.releaseControllerStateFromInterrupt();
119 }
120 }
121
123 release();
124 }
125
126 bool owned() const {
127 return m_Owned;
128 }
129
130 void release() {
131 if (m_Owned) {
132 m_Pic.releaseControllerStateFromInterrupt();
133 m_Owned = false;
134 }
135 }
136
137 private:
138 Pic& m_Pic;
139 bool m_Owned;
140};
141
142void Pic::tick() {}
143
144bool Pic::control(uint8_t irq, ControlCode code, size_t argument) {
145 if (UNLIKELY(irq >= 16))
146 return false;
147
148 StateGuard guard(*this);
149 if (!guard.owned())
150 return false;
151
152 switch (code) {
153 case MitigationThreshold:
154 if (LIKELY(argument)) {
155 if (UNLIKELY(m_IrqState.handlerCount(irq) > 1))
156 m_MitigationThreshold[irq] += argument;
157 else
158 m_MitigationThreshold[irq] = argument;
159 } else
160 m_MitigationThreshold[irq] = DEFAULT_IRQ_MITIGATE_THRESHOLD;
161 return true;
162 }
163
164 return false;
165}
166
167irq_id_t Pic::registerIsaIrqHandler(uint8_t irq, IrqHandler* handler, const IrqPolicy& policy) {
168 if (UNLIKELY(irq >= PicIrqState::LineCount || !handler || !m_ThreadedDispatcher.isInitialised() ||
169 !policy.validForThreaded() || policy.trigger() == IrqTrigger::Synthetic))
170 return 0;
171
172 StateGuard guard(*this);
173 if (!guard.owned() || m_ShuttingDown || m_UnregisterReservations[irq] ||
174 !m_ThreadedDispatcher.isInitialised())
175 return 0;
176 if (!m_IrqState.canRegister(irq, policy, IrqDelivery::Threaded)) {
177 ERROR("PIC: IRQ " << Dec << irq << " was registered with incompatible trigger modes");
178 return 0;
179 }
180 beginLineTransitionLocked(irq);
181 const bool firstHandler = !m_IrqState.handlerCount(irq);
182 if (!m_Handlers.registerThreadedHandler(irq, handler, policy)) {
183 finishLineTransitionLocked(irq);
184 return 0;
185 }
186
187 if (firstHandler) {
188 advanceThreadedCookieLocked(irq);
189 m_FailClosedReasons[irq] = 0;
190 m_ThreadedHardVetoRecovery[irq] = false;
191 m_ThreadedHardVetoRecoveryGenerations[irq] = 0;
192 }
193 m_IrqState.handlerRegistered(irq, policy, IrqDelivery::Threaded);
194 finishLineTransitionLocked(irq);
195 publishDiagnosticLineLocked(irq);
196
197 return irq + BASE_INTERRUPT_VECTOR;
198}
199
200irq_id_t Pic::registerHardIsaIrqHandler(uint8_t irq, HardIrqHandler* handler,
201 const IrqPolicy& policy) {
202 if (UNLIKELY(irq >= PicIrqState::LineCount || !handler || !policy.validForHard() ||
203 policy.trigger() == IrqTrigger::Synthetic))
204 return 0;
205
206 StateGuard guard(*this);
207 if (!guard.owned() || m_ShuttingDown || m_UnregisterReservations[irq])
208 return 0;
209 if (!m_IrqState.canRegister(irq, policy, IrqDelivery::Hard)) {
210 ERROR("PIC: IRQ " << Dec << irq << " was registered with incompatible trigger modes");
211 return 0;
212 }
213 beginLineTransitionLocked(irq);
214 const bool firstHandler = !m_IrqState.handlerCount(irq);
215 if (!m_Handlers.registerHardHandler(irq, handler, policy)) {
216 finishLineTransitionLocked(irq);
217 return 0;
218 }
219
220 if (firstHandler) {
221 advanceThreadedCookieLocked(irq);
222 m_FailClosedReasons[irq] = 0;
223 m_ThreadedHardVetoRecovery[irq] = false;
224 m_ThreadedHardVetoRecoveryGenerations[irq] = 0;
225 }
226 m_IrqState.handlerRegistered(irq, policy, IrqDelivery::Hard);
227 finishLineTransitionLocked(irq);
228 publishDiagnosticLineLocked(irq);
229
230 return irq + BASE_INTERRUPT_VECTOR;
231}
232bool Pic::reservePciRoute(uint8_t irq) {
233 if (irq >= PicIrqState::LineCount)
234 return false;
235 StateGuard guard(*this);
236 if (!guard.owned() || m_ShuttingDown || !m_ElcrPort || m_UnregisterReservations[irq] ||
237 !m_IrqState.canReservePciRoute(irq))
238 return false;
239 if (m_IrqState.pciRouteReserved(irq))
240 return true;
241
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);
248 return false;
249 }
251 finishLineTransitionLocked(irq);
252 publishDiagnosticLineLocked(irq);
253 return true;
254}
255
256bool Pic::hasIoApic() const {
257#if MULTIPROCESSOR && ACPI
258 return m_Intx != nullptr;
259#else
260 return false;
261#endif
262}
263
264bool Pic::reservePciGsi(uint32_t gsi, bool activeLow) {
265#if MULTIPROCESSOR && ACPI
266 if (!m_Intx || !m_Intx->containsGsi(gsi)) {
267 return false;
268 }
269 uint16_t isa = 0;
270 if (!Acpi::instance().isaIrqsForGsi(gsi, isa)) {
271 return false;
272 }
273 {
274 StateGuard guard(*this);
275 if (!guard.owned() || m_ShuttingDown) {
276 return false;
277 }
278 for (size_t irq = 0; irq < PicIrqState::LineCount; ++irq) {
279 if ((isa & (1U << irq)) &&
280 (m_UnregisterReservations[irq] || !m_IrqState.canReserveIoApic(irq))) {
281 return false;
282 }
283 }
284 for (size_t irq = 0; irq < PicIrqState::LineCount; ++irq) {
285 if (isa & (1U << irq)) {
286 beginLineTransitionLocked(irq);
288 finishLineTransitionLocked(irq);
289 publishDiagnosticLineLocked(irq);
290 }
291 }
292 }
293 return m_Intx->reserveGsi(gsi, activeLow);
294#else
295 return false;
296#endif
297}
298
299bool Pic::claimPciTriggerLocked(uint8_t irq) {
300 if (!m_ElcrPort)
301 return false;
302 uint8_t previous = 0;
303 if (!updatePicElcr(
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");
307 return false;
308 }
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);
312 m_OwnedElcr |= bit;
313 }
314 return true;
315}
316
317void Pic::restorePciTriggerLocked(uint8_t irq) {
318 const uint16_t bit = uint16_t{1} << irq;
319 if (!(m_OwnedElcr & bit) || m_IrqState.pciRouteReserved(irq))
320 return;
321 uint8_t previous = 0;
322 if (!updatePicElcr(
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");
326 m_OwnedElcr &= ~bit;
327}
328
329irq_id_t Pic::registerPciIrqHandler(IrqHandler* handler, Device* pDevice, const IrqPolicy& policy) {
330 if (UNLIKELY(!pDevice))
331 return 0;
332#if MULTIPROCESSOR && ACPI
333 if (m_Intx) {
334 bool routed = false;
335 const irq_id_t id = m_Intx->registerThreaded(pDevice, handler, policy, routed);
336 if (id || routed) {
337 return id;
338 }
339 }
340#endif
341 irq_id_t irq = pDevice->getInterruptNumber();
342 if (UNLIKELY(irq >= PicIrqState::LineCount || !handler || !m_ThreadedDispatcher.isInitialised() ||
343 !policy.validForThreaded() || policy.trigger() != IrqTrigger::Level))
344 return 0;
345
346 StateGuard guard(*this);
347 if (!guard.owned() || m_ShuttingDown || m_UnregisterReservations[irq] ||
348 !m_ThreadedDispatcher.isInitialised())
349 return 0;
350 if (!m_IrqState.canRegister(irq, policy, IrqDelivery::Threaded)) {
351 ERROR("PIC: PCI IRQ " << Dec << irq << " conflicts with an edge-triggered handler");
352 return 0;
353 }
354 beginLineTransitionLocked(irq);
355 const bool triggerOwned = m_OwnedElcr & (uint16_t{1} << irq);
356 if (!claimPciTriggerLocked(irq)) {
357 finishLineTransitionLocked(irq);
358 return 0;
359 }
360 const bool firstHandler = !m_IrqState.handlerCount(irq);
361 if (!m_Handlers.registerThreadedHandler(irq, handler, policy)) {
362 if (!triggerOwned)
363 restorePciTriggerLocked(irq);
364 finishLineTransitionLocked(irq);
365 return 0;
366 }
367
368 if (firstHandler) {
369 advanceThreadedCookieLocked(irq);
370 m_FailClosedReasons[irq] = 0;
371 m_ThreadedHardVetoRecovery[irq] = false;
372 m_ThreadedHardVetoRecoveryGenerations[irq] = 0;
373 }
374 m_IrqState.handlerRegistered(irq, policy, IrqDelivery::Threaded);
375 finishLineTransitionLocked(irq);
376 publishDiagnosticLineLocked(irq);
377
378 return irq + BASE_INTERRUPT_VECTOR;
379}
380
382 const IrqPolicy& policy) {
383 if (UNLIKELY(!pDevice))
384 return 0;
385#if MULTIPROCESSOR && ACPI
386 if (m_Intx) {
387 bool routed = false;
388 const irq_id_t id = m_Intx->registerHard(pDevice, handler, policy, routed);
389 if (id || routed) {
390 return id;
391 }
392 }
393#endif
394 irq_id_t irq = pDevice->getInterruptNumber();
395 if (UNLIKELY(irq >= PicIrqState::LineCount || !handler || !policy.validForHard() ||
396 policy.trigger() != IrqTrigger::Level))
397 return 0;
398
399 StateGuard guard(*this);
400 if (!guard.owned() || m_ShuttingDown || m_UnregisterReservations[irq])
401 return 0;
402 if (!m_IrqState.canRegister(irq, policy, IrqDelivery::Hard)) {
403 ERROR("PIC: PCI IRQ " << Dec << irq << " conflicts with an edge-triggered handler");
404 return 0;
405 }
406 beginLineTransitionLocked(irq);
407 const bool triggerOwned = m_OwnedElcr & (uint16_t{1} << irq);
408 if (!claimPciTriggerLocked(irq)) {
409 finishLineTransitionLocked(irq);
410 return 0;
411 }
412 const bool firstHandler = !m_IrqState.handlerCount(irq);
413 if (!m_Handlers.registerHardHandler(irq, handler, policy)) {
414 if (!triggerOwned)
415 restorePciTriggerLocked(irq);
416 finishLineTransitionLocked(irq);
417 return 0;
418 }
419
420 if (firstHandler) {
421 advanceThreadedCookieLocked(irq);
422 m_FailClosedReasons[irq] = 0;
423 m_ThreadedHardVetoRecovery[irq] = false;
424 m_ThreadedHardVetoRecoveryGenerations[irq] = 0;
425 }
426 m_IrqState.handlerRegistered(irq, policy, IrqDelivery::Hard);
427 finishLineTransitionLocked(irq);
428 publishDiagnosticLineLocked(irq);
429
430 return irq + BASE_INTERRUPT_VECTOR;
431}
432
434 const IrqPolicy& intxFallbackPolicy) {
435#if MULTIPROCESSOR
436 if (m_Messages) {
437 bool fallbackSafe = true;
438 const irq_id_t id = m_Messages->registerThreaded(device, handler, fallbackSafe);
439 if (id) {
440 return id;
441 }
442 if (!fallbackSafe) {
443 return 0;
444 }
445 }
446#endif
448 if (!PciBus::instance().inspectFunction(device, state) ||
449 !PciBus::instance().disableMessageInterrupts(device, state)) {
450 return 0;
451 }
452 return registerPciIrqHandler(handler, device, intxFallbackPolicy);
453}
454
455bool Pic::registerPciMsixIrqHandlers(Device* device, IrqHandler* const* handlers, size_t count,
456 irq_id_t* ids, bool& fallbackSafe, const size_t* processors) {
457 fallbackSafe = true;
458#if MULTIPROCESSOR
459 if (m_Messages) {
460 return m_Messages->registerThreadedVectors(device, handlers, count, ids, fallbackSafe,
461 processors);
462 }
463#endif
464 return false;
465}
466
468 const IrqPolicy& intxFallbackPolicy) {
469#if MULTIPROCESSOR
470 if (m_Messages) {
471 bool fallbackSafe = true;
472 const irq_id_t id = m_Messages->registerHard(device, handler, fallbackSafe);
473 if (id) {
474 return id;
475 }
476 if (!fallbackSafe) {
477 return 0;
478 }
479 }
480#endif
482 if (!PciBus::instance().inspectFunction(device, state) ||
483 !PciBus::instance().disableMessageInterrupts(device, state)) {
484 return 0;
485 }
486 return registerHardPciIrqHandler(handler, device, intxFallbackPolicy);
487}
488
490 const IrqPolicy& policy) {
491 if (irq >= PicIrqState::LineCount || !handler)
492 return 0;
493
494 StateGuard guard(*this);
495 if (!guard.owned() || m_ShuttingDown || m_UnregisterReservations[irq] ||
496 !m_IrqState.canRegisterScheduler(irq, policy))
497 return 0;
498
499 beginLineTransitionLocked(irq);
500 m_SchedulerIrqHandler = handler;
501 m_IrqState.schedulerRegistered(irq, policy);
502 finishLineTransitionLocked(irq);
503 publishDiagnosticLineLocked(irq);
504 return irq + BASE_INTERRUPT_VECTOR;
505}
506
508 if (Id != BASE_INTERRUPT_VECTOR || !handler)
509 return false;
510
511 StateGuard guard(*this);
512 if (!guard.owned() || m_SchedulerIrqHandler != handler || !m_IrqState.schedulerRegistered(0))
513 return false;
514
515 beginLineTransitionLocked(0);
516 m_SchedulerIrqHandler = nullptr;
517 m_IrqState.schedulerUnregistered(0);
518 finishLineTransitionLocked(0);
519 publishDiagnosticLineLocked(0);
520 return true;
521}
522
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.");
527 return;
528 }
529 assert(m_UnregisterReservations[irq]);
531 if (result == IrqHandlerRegistry::UnregisterResult::Completed ||
532 result == IrqHandlerRegistry::UnregisterResult::Deferred) {
533 assert(removedDelivery == IrqHandlerRegistry::LineMode::Threaded ||
534 removedDelivery == IrqHandlerRegistry::LineMode::HardOnly);
535 const IrqDelivery previousDelivery = m_IrqState.delivery(irq);
536 const IrqDelivery delivery = removedDelivery == IrqHandlerRegistry::LineMode::Threaded
537 ? IrqDelivery::Threaded
538 : IrqDelivery::Hard;
539 m_IrqState.handlerUnregistered(irq, delivery);
540 const IrqDelivery currentDelivery = m_IrqState.delivery(irq);
541 if (delivery == IrqDelivery::Hard && !m_IrqState.hardHandlerCount(irq)) {
543 }
544 const bool hardStageEnded =
545 previousDelivery == IrqDelivery::Mixed && currentDelivery == IrqDelivery::Threaded;
546 const bool hardQuarantineEnded =
547 (m_FailClosedReasons[irq] & HardHandoffQuarantine) && !m_Handlers.hardLineQuarantined(irq);
548 if (hardQuarantineEnded) {
549 m_FailClosedReasons[irq] &= ~HardHandoffQuarantine;
550 }
551 if (hardStageEnded || hardQuarantineEnded) {
552 if (currentDelivery == IrqDelivery::Threaded || currentDelivery == IrqDelivery::Mixed) {
553 m_ThreadedHardAdmitted[irq] = true;
554 m_ThreadedHardDisposition[irq] = HardIrqDisposition::Handled;
555 }
556 if (!m_FailClosedReasons[irq] && m_IrqState.acknowledgementPending(irq)) {
557 m_IrqState.acknowledge(irq);
558 }
559 if (!m_FailClosedReasons[irq] && m_ThreadedHardVetoRecovery[irq] &&
560 m_IrqState.completeThreadedDispatch(irq, m_ThreadedHardVetoRecoveryGenerations[irq],
561 true)) {
562 m_ThreadedHardVetoRecovery[irq] = false;
563 m_ThreadedHardVetoRecoveryGenerations[irq] = 0;
564 }
565 }
566 if (currentDelivery == IrqDelivery::None ||
567 (previousDelivery == IrqDelivery::Mixed && currentDelivery == IrqDelivery::Hard)) {
568 if (currentDelivery == IrqDelivery::Hard) {
569 m_FailClosedReasons[irq] &= ~ThreadedPublicationQuarantine;
570 if (!m_FailClosedReasons[irq] && m_IrqState.acknowledgementPending(irq)) {
571 m_IrqState.acknowledge(irq);
572 }
573 }
574 const size_t boundary = advanceThreadedCookieLocked(irq);
575 m_Handlers.invalidateThreadedLine(irq, boundary);
577 m_ThreadedHadHardStage[irq] = false;
578 m_ThreadedHardAdmitted[irq] = false;
579 m_ThreadedHardDisposition[irq] = HardIrqDisposition::NotHandled;
580 m_ThreadedHardVetoRecovery[irq] = false;
581 m_ThreadedHardVetoRecoveryGenerations[irq] = 0;
582 }
583 if (currentDelivery == IrqDelivery::None) {
584 restorePciTriggerLocked(irq);
585 m_FailClosedReasons[irq] = 0;
586 }
587 }
588 finishLineTransitionLocked(irq);
589 publishDiagnosticLineLocked(irq);
590}
591
592bool Pic::unregisterHandler(irq_id_t Id, IrqHandlerBase* handler) {
593#if MULTIPROCESSOR
594 if (PciMessageInterrupts::contains(Id)) {
595 return m_Messages && m_Messages->unregisterHandler(Id, handler);
596 }
597#if ACPI
598 if (PciIoApicInterrupts::contains(Id)) {
599 return m_Intx && m_Intx->unregisterHandler(Id, handler);
600 }
601#endif
602#endif
603 if (Id < BASE_INTERRUPT_VECTOR || Id >= BASE_INTERRUPT_VECTOR + PicIrqState::LineCount ||
604 !handler)
605 return false;
606
607 const uint8_t irq = Id - BASE_INTERRUPT_VECTOR;
608 IrqHandlerRegistry::LineMode removedDelivery = IrqHandlerRegistry::LineMode::Empty;
609 IrqHandlerRegistry::UnregisterResult result = IrqHandlerRegistry::UnregisterResult::Rejected;
610 const bool atomicContext = Processor::executionContext() != ExecutionContext::WaitableThread;
611
612 if (atomicContext) {
613 StateGuard guard(*this);
614 if (!guard.owned() || !m_IrqState.handlerCount(irq) || m_UnregisterReservations[irq]) {
615 return false;
616 }
618 beginLineTransitionLocked(irq);
619 result = m_Handlers.unregisterHandler(irq, handler, removedDelivery);
620 finishHandlerUnregisterLocked(irq, result, removedDelivery);
621 } else {
622 {
623 StateGuard guard(*this);
624 if (!guard.owned() || !m_IrqState.handlerCount(irq) || m_UnregisterReservations[irq]) {
625 return false;
626 }
628 beginLineTransitionLocked(irq);
629 }
630
631 result = m_Handlers.unregisterHandler(irq, handler, removedDelivery);
632 {
633 StateGuard guard(*this);
634 if (!guard.owned()) {
635 FATAL("PIC unregister lost its reserved controller boundary.");
636 return false;
637 }
638 finishHandlerUnregisterLocked(irq, result, removedDelivery);
639 }
640 }
641
642 if (result == IrqHandlerRegistry::UnregisterResult::Rejected) {
643 __atomic_add_fetch(&m_RemovalRejections[irq], static_cast<size_t>(1), __ATOMIC_RELAXED);
644 }
645 return result == IrqHandlerRegistry::UnregisterResult::Completed;
646}
647
650
651 // Allocate the I/O ports
652 if (m_SlavePort.allocate(0xA0, 4) == false)
653 return false;
654 if (m_MasterPort.allocate(0x20, 4) == false)
655 return false;
656
657 if (!m_ElcrPort.allocate(0x4d0, 2))
658 WARNING("PIC: PCI level-trigger routing is unavailable");
659
660 // Initialise the slave and master PIC
661 m_MasterPort.write8(0x11, 0);
662 m_SlavePort.write8(0x11, 0);
663 m_MasterPort.write8(BASE_INTERRUPT_VECTOR, 1);
664 m_SlavePort.write8(BASE_INTERRUPT_VECTOR + 0x08, 1);
665 m_MasterPort.write8(0x04, 1);
666 m_SlavePort.write8(0x02, 1);
667 m_MasterPort.write8(0x01, 1);
668 m_SlavePort.write8(0x01, 1);
669 // Keep every source masked until vectors are installed and the canonical
670 // cascade policy is applied below.
671 m_MasterPort.write8(0xFF, 1);
672 m_SlavePort.write8(0xFF, 1);
673
674 // Register the interrupts
676 for (size_t i = 0; i < 16; i++)
677 if (IntManager.registerInterruptHandler(i + BASE_INTERRUPT_VECTOR, this) == false)
678 return false;
679
680 for (size_t i = 0; i < 16; i++) {
681 __atomic_store_n(&m_IrqCount[i], static_cast<size_t>(0), __ATOMIC_RELAXED);
682 __atomic_store_n(&m_SpuriousIrqCount[i], static_cast<size_t>(0), __ATOMIC_RELAXED);
683 __atomic_store_n(&m_UnhandledIrqCount[i], static_cast<size_t>(0), __ATOMIC_RELAXED);
684 __atomic_store_n(&m_ControllerContentions[i], static_cast<size_t>(0), __ATOMIC_RELAXED);
685 m_MitigatedIrqs[i] = false;
686 m_MitigationThreshold[i] = DEFAULT_IRQ_MITIGATE_THRESHOLD;
687 }
688
689 // Disable all IRQ's (exept IRQ2)
690 enableAll(false);
691
692#if MULTIPROCESSOR
693 if (Pc::instance().localApicAvailable()) {
694 m_Messages = new PciMessageInterrupts();
695 if (!m_Messages->initialise()) {
696 WARNING("PCI message interrupts unavailable; using PIC INTx");
697 delete m_Messages;
698 m_Messages = nullptr;
699 }
700#if ACPI
701 m_Intx = new PciIoApicInterrupts();
702 if (!m_Intx->initialise()) {
703 delete m_Intx;
704 m_Intx = nullptr;
705 } else {
706 NOTICE("PCI INTx: IOAPIC routing available");
707 }
708#endif
709 }
710#endif
711
712 return true;
713}
714
716 const uint64_t apicBase =
717 Processor::readMachineSpecificRegister(LocalApicLint0Policy::ApicBaseMsr);
718 if (!LocalApicLint0Policy::isBootstrapProcessor(apicBase)) {
719 ERROR("PIC: threaded IRQ workers must be initialised on the BSP");
720 return false;
721 }
722
723 // Processor topology is stable by initialise3(). The dispatcher pins its
724 // workers to this scheduler, so the controller continuation and ExtINT
725 // receiver now share one durable processor identity.
727#if MULTIPROCESSOR
728 // LAPIC destination IDs are physical routing identifiers, not scheduler
729 // topology indexes. Capture the BSP's actual ID alongside the worker so
730 // remote controller owners can force prompt BSP service.
731 m_DeliveryApicId = Pc::instance().getLocalApic().getId();
732 if (!m_ThreadedDispatcher.setRemoteWakeCallback(promptThreadedWorker, this)) {
733 return false;
734 }
735#endif
737 return false;
738 }
739#if MULTIPROCESSOR
740 if (m_Messages && !m_Messages->initialiseThreaded()) {
741 WARNING("PCI message IRQ workers unavailable; using PIC INTx");
742 }
743#if ACPI
744 if (m_Intx && !m_Intx->initialiseThreaded()) {
745 WARNING("PCI IOAPIC INTx workers unavailable; using PIC INTx");
746 }
747#endif
748#endif
749 return true;
750}
751
754 ERROR("PIC: only the BSP may join PIC threaded IRQ workers");
755 return false;
756 }
758 return false;
759 }
760#if MULTIPROCESSOR
761 if (m_Messages && !m_Messages->shutdownThreaded()) {
762 return false;
763 }
764#if ACPI
765 if (m_Intx && !m_Intx->shutdownThreaded()) {
766 return false;
767 }
768#endif
769#endif
770
771 TerminationDeferral shutdownTermination;
772 {
773 StateGuard guard(*this);
774 if (!guard.owned())
775 return false;
776 m_ShuttingDown = true;
777 m_IrqState.setAllEnabled(false);
778 m_IrqState.setEnabled(2, false);
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);
783 m_Handlers.invalidateThreadedLine(line, boundary);
786 m_ThreadedHadHardStage[irq] = false;
787 m_ThreadedHardAdmitted[irq] = false;
788 m_ThreadedHardDisposition[irq] = HardIrqDisposition::NotHandled;
789 m_ThreadedHardVetoRecovery[irq] = false;
790 m_ThreadedHardVetoRecoveryGenerations[irq] = 0;
791 m_FailClosedReasons[irq] = 0;
792 }
794 applyMaskLocked();
795 publishAllDiagnosticLinesLocked();
796 }
798}
799
801 : m_SlavePort("PIC #2"),
802 m_MasterPort("PIC #1"),
803 m_ElcrPort("PIC trigger modes"),
804 m_OwnedElcr(0),
805 m_OriginalElcr(0),
806 m_Handlers(),
807#if MULTIPROCESSOR
808 m_Messages(nullptr),
809#if ACPI
810 m_Intx(nullptr),
811#endif
812#endif
813 m_SchedulerIrqHandler(nullptr),
814 m_IrqState(),
815 m_ControllerStateGate(),
816 m_HardTailQueue(),
817 m_ControllerThreadMutex(),
818 m_ThreadedDispatcher(MakeConstantString("PIC IRQ bottom half"), ControllerWorkLine + 1,
819 dispatchThreadedLine, this),
820 m_ThreadedCookies(),
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),
835 m_ControllerPromptState(static_cast<size_t>(IrqControllerPromptState::NotRequired)),
836 m_ControllerTemporaryMask(0),
837 m_AppliedControllerMask(0xFFFF),
838 m_DeferredLineTransitions(0),
839 m_DeliveryProcessor(0),
840 m_DeliveryApicId(0),
841 m_Diagnostics(),
842 m_UnregisterReservations(),
843 m_ShuttingDown(false),
844 m_IrqCount(),
845 m_SpuriousIrqCount(),
846 m_UnhandledIrqCount(),
847 m_MitigatedIrqs(),
848 m_MitigationThreshold() {
849 publishAllDiagnosticLinesLocked();
850}
851
852void Pic::publishDiagnosticLineLocked(uint8_t irq) {
853 if (irq >= PicIrqState::LineCount) {
854 return;
855 }
856
857 size_t targetBank = 0;
859 if (!target) {
860 return;
861 }
862
864 line.line = irq;
865 line.handlerCount = m_IrqState.handlerCount(irq);
866 line.configured = line.handlerCount != 0;
867 line.delivery = m_IrqState.delivery(irq);
868 line.effectiveMasked =
869 !m_IrqState.enabled(irq) || (m_ControllerTemporaryMask & static_cast<uint16_t>(1U << irq));
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);
875 line.publicationCookie = m_ThreadedCookies[irq];
876 line.interruptCount = __atomic_load_n(&m_IrqCount[irq], __ATOMIC_RELAXED);
877 line.spuriousCount = __atomic_load_n(&m_SpuriousIrqCount[irq], __ATOMIC_RELAXED);
878 line.unhandledCount = __atomic_load_n(&m_UnhandledIrqCount[irq], __ATOMIC_RELAXED);
879 line.publicationFailures = __atomic_load_n(&m_ThreadedPublicationFailures[irq], __ATOMIC_RELAXED);
880 line.removalRejections = __atomic_load_n(&m_RemovalRejections[irq], __ATOMIC_RELAXED);
881 line.controllerContentions = __atomic_load_n(&m_ControllerContentions[irq], __ATOMIC_RELAXED);
882 line.controllerPromptAttempts = __atomic_load_n(&m_ControllerPromptAttempts, __ATOMIC_RELAXED);
883 line.controllerPromptFailures = __atomic_load_n(&m_ControllerPromptFailures, __ATOMIC_RELAXED);
884 line.controllerPromptDestination =
885 __atomic_load_n(&m_ControllerPromptDestination, __ATOMIC_RELAXED);
886 line.controllerPromptState = static_cast<IrqControllerPromptState>(
887 __atomic_load_n(&m_ControllerPromptState, __ATOMIC_RELAXED));
888
889 if (!line.configured) {
890 line.maskReasons |= IrqMaskNoHandler;
891 } else {
892 line.trigger = m_IrqState.trigger(irq);
893 line.controllerAck = m_IrqState.controllerAck(irq);
894 line.lineRelease = m_IrqState.lineRelease(irq);
895 }
896 if (!line.requestedEnabled) {
897 line.maskReasons |= IrqMaskAdministrativelyDisabled;
898 }
899 if (line.acknowledgementPending) {
900 line.maskReasons |= IrqMaskAwaitingAcknowledgement;
901 }
902 if (line.threadedPending) {
903 line.maskReasons |= IrqMaskAwaitingThreadedCompletion;
904 }
905 if (m_MitigatedIrqs[irq]) {
906 line.maskReasons |= IrqMaskMitigated;
907 }
908 if (m_ShuttingDown) {
909 line.maskReasons |= IrqMaskShuttingDown;
910 }
911 if (m_FailClosedReasons[irq] & ControllerContentionQuarantine) {
912 line.maskReasons |= IrqMaskControllerContention;
913 }
914 if (m_ControllerTemporaryMask & static_cast<uint16_t>(1U << irq)) {
915 line.maskReasons |= IrqMaskControllerContention;
916 }
917
918 *target = line;
919 m_Diagnostics.finishPublication(irq, targetBank);
920}
921
922void Pic::publishAllDiagnosticLinesLocked() {
923 for (size_t irq = 0; irq < PicIrqState::LineCount; ++irq) {
924 publishDiagnosticLineLocked(static_cast<uint8_t>(irq));
925 }
926}
927
928size_t Pic::snapshotIrqLines(IrqLineDiagnosticSnapshot* out, size_t capacity) const {
929 if (!out || !capacity) {
930 return 0;
931 }
932
933 const size_t count = capacity < PicIrqState::LineCount ? capacity : PicIrqState::LineCount;
934 for (size_t irq = 0; irq < count; ++irq) {
935 if (!m_Diagnostics.snapshot(irq, out[irq])) {
936 out[irq] = {};
937 out[irq].line = static_cast<uint8_t>(irq);
938 }
939 }
940
941 const bool dispatcherInitialised = m_ThreadedDispatcher.isInitialised();
942 const size_t controllerPromptAttempts =
943 __atomic_load_n(&m_ControllerPromptAttempts, __ATOMIC_RELAXED);
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);
948 const IrqControllerPromptState controllerPromptState = static_cast<IrqControllerPromptState>(
949 __atomic_load_n(&m_ControllerPromptState, __ATOMIC_RELAXED));
950 for (size_t irq = 0; irq < count; ++irq) {
951 out[irq].pendingCookie = m_ThreadedDispatcher.pendingCookie(static_cast<uint8_t>(irq));
952 out[irq].activeHardDispatchCount = m_Handlers.hardDispatchState(
953 static_cast<uint8_t>(irq), out[irq].activeHardDispatchGeneration);
954 out[irq].hardStageActive = out[irq].activeHardDispatchCount != 0;
955 out[irq].activeThreadedDispatchCount = m_Handlers.threadedDispatchState(
956 static_cast<uint8_t>(irq), out[irq].activeThreadedHandlerIdentity);
957 out[irq].activeCookie = m_ThreadedDispatcher.activeCookie(static_cast<uint8_t>(irq));
958 out[irq].completedCookie = m_ThreadedDispatcher.completedCookie(static_cast<uint8_t>(irq));
959 out[irq].completedBatches = m_ThreadedDispatcher.completedBatches(static_cast<uint8_t>(irq));
960 out[irq].interruptCount = __atomic_load_n(&m_IrqCount[irq], __ATOMIC_RELAXED);
961 out[irq].spuriousCount = __atomic_load_n(&m_SpuriousIrqCount[irq], __ATOMIC_RELAXED);
962 out[irq].unhandledCount = __atomic_load_n(&m_UnhandledIrqCount[irq], __ATOMIC_RELAXED);
963 out[irq].publicationFailures =
964 __atomic_load_n(&m_ThreadedPublicationFailures[irq], __ATOMIC_RELAXED);
965 out[irq].removalRejections = __atomic_load_n(&m_RemovalRejections[irq], __ATOMIC_RELAXED);
966 out[irq].controllerContentions =
967 __atomic_load_n(&m_ControllerContentions[irq], __ATOMIC_RELAXED);
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);
973 out[irq].workerIdentity = m_ThreadedDispatcher.workerIdentity(static_cast<uint8_t>(irq));
974 out[irq].dispatcherInitialised = dispatcherInitialised;
975 out[irq].dispatcherActive = m_ThreadedDispatcher.callbackActive(static_cast<uint8_t>(irq));
976 out[irq].dispatcherClosed = m_ThreadedDispatcher.publicationClosed(static_cast<uint8_t>(irq));
977 m_ThreadedDispatcher.snapshotDiagnostics(static_cast<uint8_t>(irq), out[irq]);
978 }
979 return count;
980}
981
982size_t Pic::advanceThreadedCookieLocked(uint8_t irq) {
983 assert(irq < PicIrqState::LineCount);
984 size_t cookie = ++m_ThreadedCookies[irq];
985 if (!cookie) {
986 cookie = ++m_ThreadedCookies[irq];
987 }
988 return cookie;
989}
990
991void Pic::beginLineTransitionLocked(uint8_t irq) {
992 assert(irq < PicIrqState::LineCount);
993 m_DeferredLineTransitions = cancelDeferredPicLineTransition(m_DeferredLineTransitions, irq);
994 m_IrqState.beginLineTransition(irq);
995 applyMaskLocked();
997}
998
999void Pic::finishLineTransitionLocked(uint8_t irq) {
1000 assert(irq < PicIrqState::LineCount);
1001 const uint16_t lineMask = static_cast<uint16_t>(1U << irq);
1003 // IF is disabled on the processor which receives ExtINT, so a fresh
1004 // lifetime can be published immediately before the physical unmask.
1005 m_DeferredLineTransitions &= static_cast<uint16_t>(~lineMask);
1007 m_IrqState.finishLineTransition(irq);
1008 applyMaskLocked();
1009 return;
1010 }
1011
1012 m_IrqState.finishLineTransition(irq);
1013 // A mask does not retract a vector already accepted by the BSP. Keep the
1014 // transition lifetime unpublished even when the replacement line ends
1015 // disabled, until the BSP closes that delivery window with IF disabled.
1016 m_DeferredLineTransitions |= lineMask;
1017 applyMaskLocked();
1018}
1019
1020bool Pic::spuriousLocked(size_t irq) {
1021 if (irq > 7) {
1022 // Get ISR for slave.
1023 uint8_t mask = 1 << (irq - 8);
1024 m_SlavePort.write8(0x0B, 0);
1025 uint8_t isr = m_SlavePort.read8(0);
1026 m_SlavePort.write8(0x0A, 0);
1027 return (isr & mask) == 0;
1028 } else {
1029 // Get ISR for master.
1030 uint8_t mask = 1 << irq;
1031 m_MasterPort.write8(0x0B, 0);
1032 uint8_t isr = m_MasterPort.read8(0);
1033 m_MasterPort.write8(0x0A, 0);
1034 return (isr & mask) == 0;
1035 }
1036}
1037
1038bool Pic::drainOnePendingControllerBatchLocked() {
1040 if (!m_ControllerStateGate.takePending(pending)) {
1041 return false;
1042 }
1043
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;
1052
1053 for (size_t irq = 0; irq < PicIrqState::LineCount; ++irq) {
1054 m_HardTailQueue.consume(static_cast<uint8_t>(irq), [this, irq](const PicHardTailRecord& tail) {
1055 __atomic_add_fetch(&m_ControllerContentions[irq], static_cast<size_t>(1), __ATOMIC_RELAXED);
1056 finishHardDispatchLocked(tail);
1057 });
1058 }
1059
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];
1067
1068 for (size_t occurrence = 0; occurrence < pending.entry[irq]; ++occurrence) {
1069 if ((!m_IrqState.enabled(irq) || irq == 7 || irq == 15) && spuriousLocked(irq)) {
1070 __atomic_add_fetch(&m_SpuriousIrqCount[irq], static_cast<size_t>(1), __ATOMIC_RELAXED);
1071 ++nonMutatingSpuriousEntries[irq];
1072 if (irq > 7) {
1073 ++spuriousCascadeEois;
1074 }
1075 } else {
1076 ++realEntries[irq];
1077 }
1078 }
1079
1080 for (size_t occurrence = 0; occurrence < pending.staleEntry[irq]; ++occurrence) {
1081 if ((!m_IrqState.enabled(irq) || irq == 7 || irq == 15) && spuriousLocked(irq)) {
1082 __atomic_add_fetch(&m_SpuriousIrqCount[irq], static_cast<size_t>(1), __ATOMIC_RELAXED);
1083 ++nonMutatingSpuriousEntries[irq];
1084 if (irq > 7) {
1085 ++staleSpuriousCascadeEois;
1086 }
1087 } else {
1088 ++staleRealEntries[irq];
1089 }
1090 }
1091
1092 const size_t contentions =
1093 pending.entry[irq] + pending.tail[irq] + pending.staleEntry[irq] + pending.staleTail[irq];
1094 if (contentions) {
1095 __atomic_add_fetch(&m_ControllerContentions[irq], contentions, __ATOMIC_RELAXED);
1096 }
1097
1098 const PicContentionLineResult result = resolvePicContentionLine(
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));
1104 }
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);
1109 }
1110
1111 if (result.schedulerDrop) {
1112 m_FailClosedReasons[irq] &= ~ControllerContentionQuarantine;
1113 // Keep the PIT physically one-shot until the BSP releases the
1114 // controller gate; otherwise its next edge can starve the worker
1115 // which completes this handoff.
1116 m_ControllerTemporaryMask |= static_cast<uint16_t>(1U << irq);
1117 maskChanged = true;
1118 } else if (result.quarantine) {
1119 m_FailClosedReasons[irq] |= ControllerContentionQuarantine;
1120 }
1121
1122 if (result.invalidateThreaded) {
1123 const size_t boundary = advanceThreadedCookieLocked(static_cast<uint8_t>(irq));
1124 m_Handlers.invalidateThreadedGenerationFromInterrupt(static_cast<uint8_t>(irq), boundary);
1126 m_ThreadedHadHardStage[irq] = false;
1127 m_ThreadedHardAdmitted[irq] = false;
1128 m_ThreadedHardDisposition[irq] = HardIrqDisposition::NotHandled;
1129 m_ThreadedHardVetoRecovery[irq] = false;
1130 m_ThreadedHardVetoRecoveryGenerations[irq] = 0;
1131 }
1132 maskChanged |= result.maskChanged;
1133
1134 const size_t staleUnhandled = staleRealEntries[irq] + pending.staleTail[irq];
1135 if (staleUnhandled) {
1136 __atomic_add_fetch(&m_UnhandledIrqCount[irq], staleUnhandled, __ATOMIC_RELAXED);
1137 }
1138 }
1139
1140 const uint16_t temporaryMask = temporaryPicMaskForDeferredWork(
1141 m_ControllerTemporaryMask, staleRealEntries, staleActions, nonMutatingSpuriousEntries);
1142 maskChanged |= temporaryMask != m_ControllerTemporaryMask;
1143 m_ControllerTemporaryMask = temporaryMask;
1144
1145 if (maskChanged) {
1146 applyMaskLocked();
1147 }
1148
1149 auto writeController = [this](PicControllerWriteTarget target, uint8_t value) {
1150 switch (target) {
1151 case PicControllerWriteTarget::MasterCommand:
1152 m_MasterPort.write8(value, 0);
1153 break;
1154 case PicControllerWriteTarget::MasterMask:
1155 m_MasterPort.write8(value, 1);
1156 break;
1157 case PicControllerWriteTarget::SlaveCommand:
1158 m_SlavePort.write8(value, 0);
1159 break;
1160 case PicControllerWriteTarget::SlaveMask:
1161 m_SlavePort.write8(value, 1);
1162 break;
1163 }
1164 };
1165 emitPicContentionWrites(m_IrqState, false, realEntries, currentActions, spuriousCascadeEois,
1166 writeController);
1167 emitPicContentionWrites(m_IrqState, false, staleRealEntries, staleActions,
1168 staleSpuriousCascadeEois, writeController);
1169
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));
1174 }
1175 }
1176 return true;
1177}
1178
1179void Pic::drainPendingControllerActionsLocked() {
1180 while (drainOnePendingControllerBatchLocked()) {
1181 }
1182}
1183
1184bool Pic::handControllerStateToWorker() {
1186 if (!m_ThreadedDispatcher.publishFromInterrupt(ControllerWorkLine, 1)) {
1187 return false;
1188 }
1190}
1191
1192void Pic::releaseControllerState() {
1193 for (;;) {
1194 drainPendingControllerActionsLocked();
1196 finishDeferredLineTransitionsLocked();
1197 }
1198
1199 const bool deliveryProcessor = Processor::index() == m_DeliveryProcessor;
1200 if (!deliveryProcessor) {
1201 // A stable lifetime does not need the BSP publication barrier.
1202 // Restore it while retaining Owner, so an interrupt racing the
1203 // unmask either observes Clean or makes releaseIfIdle fail.
1204 const uint16_t restorable =
1205 restorablePicTemporaryMask(m_ControllerTemporaryMask, m_DeferredLineTransitions);
1206 if (restorable) {
1207 clearControllerTemporaryMaskLocked(restorable);
1208 }
1209 }
1210 const uint16_t pendingUnmask =
1211 static_cast<uint16_t>(m_ControllerTemporaryMask & ~m_IrqState.mask());
1212 if (!deliveryProcessor && m_DeferredLineTransitions) {
1213 if (handControllerStateToWorker()) {
1214 return;
1215 }
1216 if (!m_ControllerStateGate.urgentPending()) {
1217 FATAL_NOLOCK(
1218 "PIC controller restore work could not reach its BSP "
1219 "continuation.");
1220 }
1221 continue;
1222 }
1223
1224 if (m_ControllerTemporaryMask && !pendingUnmask) {
1225 clearControllerTemporaryMaskLocked();
1226 }
1227
1228 if (deliveryProcessor && (m_ControllerTemporaryMask || m_DeferredLineTransitions)) {
1229 const bool interruptsWereEnabled = Processor::getInterrupts();
1230 if (interruptsWereEnabled) {
1232 }
1233
1234 // Close the final pending-vector window before publishing fresh
1235 // lifetimes, physically unmasking, and releasing ownership.
1236 drainPendingControllerActionsLocked();
1238 finishDeferredLineTransitionsLocked();
1239 }
1241 clearControllerTemporaryMaskLocked();
1242 }
1243 const bool released = m_ControllerStateGate.releaseIfIdle();
1244 if (interruptsWereEnabled) {
1246 }
1247 if (released) {
1248 return;
1249 }
1250 continue;
1251 }
1252
1254 return;
1255 }
1256 }
1257}
1258
1259void Pic::releaseControllerStateFromInterrupt() {
1260 if (m_ControllerStateGate.hasPending() && !m_ControllerStateGate.urgentPending() &&
1261 handControllerStateToWorker()) {
1262 return;
1263 }
1264
1265 // IRQ0 is the only mandatory hard-context batch because its worker cannot
1266 // run if the unacknowledged PIT edge is also the scheduler's next wake.
1267 drainOnePendingControllerBatchLocked();
1268 for (;;) {
1269 if (m_ControllerStateGate.urgentPending()) {
1270 drainOnePendingControllerBatchLocked();
1271 continue;
1272 }
1273 if (m_ControllerStateGate.hasPending()) {
1274 if (handControllerStateToWorker()) {
1275 return;
1276 }
1277 // Startup and late shutdown can legitimately have no worker. The
1278 // verified BSP-only ExtINT route keeps this synchronous fallback
1279 // finite because IF is disabled on the sole hard producer.
1280 drainOnePendingControllerBatchLocked();
1281 continue;
1282 }
1284 // Hard entries only arrive on the BSP with IF disabled, which is
1285 // also the safe terminal boundary for a replacement lifetime.
1286 finishDeferredLineTransitionsLocked();
1287 }
1289 clearControllerTemporaryMaskLocked();
1290 }
1292 return;
1293 }
1294 }
1295}
1296
1297void Pic::admitThreadedOccurrenceLocked(uint8_t irq) {
1298 if (UNLIKELY(irq >= PicIrqState::LineCount)) {
1299 FATAL_NOLOCK("PIC threaded occurrence has an invalid IRQ.");
1300 return;
1301 }
1302 assert(m_IrqState.delivery(irq) == IrqDelivery::Threaded);
1303
1304 const IrqControllerAck controllerAck = m_IrqState.controllerAck(irq);
1305 const IrqLineRelease lineRelease = m_IrqState.lineRelease(irq);
1306 const size_t dispatchGeneration = m_IrqState.beginDispatch(irq);
1307 IrqHandlerRegistry::AdmissionCutoff admissionCutoff = {};
1308 if (!m_Handlers.captureAdmissionCutoff(irq, admissionCutoff)) {
1309 const bool wasEnabled = m_IrqState.enabled(irq);
1310 m_FailClosedReasons[irq] |= ThreadedPublicationQuarantine;
1311 m_IrqState.completeDispatch(irq, dispatchGeneration, true);
1312 if (wasEnabled != m_IrqState.enabled(irq)) {
1313 applyMaskLocked();
1314 }
1315 if (controllerAck != IrqControllerAck::None) {
1316 eoiLocked(irq);
1317 }
1318 __atomic_add_fetch(&m_ThreadedPublicationFailures[irq], static_cast<size_t>(1),
1319 __ATOMIC_RELAXED);
1320 publishDiagnosticLineLocked(irq);
1321 return;
1322 }
1323
1324 // A one-shot threaded policy masks before EOI. Immediate-release
1325 // policies remain open while the worker runs.
1327 if (lineRelease == IrqLineRelease::AfterThreadedCompletion) {
1328 applyMaskLocked();
1329 }
1330 if (controllerAck == IrqControllerAck::BeforeHardStage) {
1331 eoiLocked(irq);
1332 }
1333
1334 const size_t threadedCookie = advanceThreadedCookieLocked(irq);
1335 m_ThreadedDispatchGenerations[irq] = dispatchGeneration;
1336 m_ThreadedHadHardStage[irq] = false;
1337 m_ThreadedHardAdmitted[irq] = false;
1338 m_ThreadedHardDisposition[irq] = HardIrqDisposition::NotHandled;
1339 m_ThreadedHardVetoRecovery[irq] = false;
1340 m_ThreadedHardVetoRecoveryGenerations[irq] = 0;
1341
1342 bool published = m_Handlers.publishThreadedDispatch(irq, threadedCookie, admissionCutoff);
1343 if (published && !m_ThreadedDispatcher.publishFromInterrupt(irq, threadedCookie)) {
1345 published = false;
1346 }
1347 if (!published) {
1348 m_FailClosedReasons[irq] |= ThreadedPublicationQuarantine;
1349 m_ThreadedHadHardStage[irq] = false;
1350 m_ThreadedHardAdmitted[irq] = false;
1351 m_ThreadedHardDisposition[irq] = HardIrqDisposition::NotHandled;
1352 const bool wasEnabled = m_IrqState.enabled(irq);
1353 m_IrqState.completeDispatch(irq, dispatchGeneration, true);
1354 if (wasEnabled != m_IrqState.enabled(irq)) {
1355 applyMaskLocked();
1356 }
1357 __atomic_add_fetch(&m_ThreadedPublicationFailures[irq], static_cast<size_t>(1),
1358 __ATOMIC_RELAXED);
1359 }
1360
1361 if (controllerAck == IrqControllerAck::AfterHardStage) {
1362 eoiLocked(irq);
1363 }
1364 publishDiagnosticLineLocked(irq);
1365}
1366
1367void Pic::interrupt(size_t interruptNumber, InterruptState& state) {
1368 size_t irq = (interruptNumber - BASE_INTERRUPT_VECTOR);
1369 if (irq >= PicIrqState::LineCount) {
1370 return;
1371 }
1373 FATAL_NOLOCK(
1374 "Legacy PIC interrupt reached a processor whose LINT0 must be "
1375 "masked.");
1376 return;
1377 }
1378
1379 __atomic_add_fetch(&m_IrqCount[irq], static_cast<size_t>(1), __ATOMIC_RELAXED);
1380 const size_t controllerLifetime = m_ControllerStateGate.currentLifetime(irq);
1381 HardStateGuard entry(*this, static_cast<uint8_t>(irq), controllerLifetime);
1382 if (!entry.owned()) {
1383 return;
1384 }
1385
1386 if (irq == 0) {
1387 SchedulerIrqHandler* schedulerHandler = m_SchedulerIrqHandler;
1388 if (schedulerHandler) {
1389 if (!m_IrqState.enabled(irq) && spuriousLocked(irq)) {
1390 __atomic_add_fetch(&m_SpuriousIrqCount[irq], static_cast<size_t>(1), __ATOMIC_RELAXED);
1391 publishDiagnosticLineLocked(static_cast<uint8_t>(irq));
1392 return;
1393 }
1394
1395 const size_t generation = m_IrqState.beginDispatch(irq);
1396
1397 // timer() can switch away permanently, so complete controller and
1398 // software acknowledgement before entering the scheduler.
1399 eoiLocked(static_cast<uint8_t>(irq));
1400 m_IrqState.acknowledge(irq);
1401 m_IrqState.completeDispatch(irq, generation, false);
1402 publishDiagnosticLineLocked(static_cast<uint8_t>(irq));
1403 entry.release();
1404 schedulerHandler->schedulerIrq(static_cast<irq_id_t>(irq), state);
1405 return;
1406 }
1407 }
1408
1409 IrqControllerAck controllerAck = IrqControllerAck::None;
1410 IrqDelivery delivery = IrqDelivery::None;
1411 bool hasThreadedStage = false;
1412 size_t dispatchGeneration = 0;
1413 size_t hardStageGeneration = 0;
1414 size_t threadedCookie = 0;
1415 bool threadedPublished = false;
1416 IrqHandlerRegistry::AdmissionCutoff hardAdmissionCutoff = {};
1417 {
1418 // IRQ7 and IRQ15 are the architectural spurious-vector cases. A
1419 // disabled line can also have a vector already in flight, so retain
1420 // the broader check before touching its in-service state.
1421 if ((!m_IrqState.enabled(irq) || irq == 7 || irq == 15) && spuriousLocked(irq)) {
1422 if (irq > 7) {
1423 // A spurious slave vector never entered the slave ISR, but
1424 // the master still accepted the cascade interrupt.
1425 m_MasterPort.write8(0x62, 0);
1426 }
1427 __atomic_add_fetch(&m_SpuriousIrqCount[irq], static_cast<size_t>(1), __ATOMIC_RELAXED);
1428 publishDiagnosticLineLocked(static_cast<uint8_t>(irq));
1429 return;
1430 }
1431
1432 delivery = m_IrqState.delivery(irq);
1433 if (delivery == IrqDelivery::Threaded) {
1434 admitThreadedOccurrenceLocked(static_cast<uint8_t>(irq));
1435 return;
1436 }
1437
1438 controllerAck = m_IrqState.controllerAck(irq);
1439 const IrqLineRelease lineRelease = m_IrqState.lineRelease(irq);
1440 dispatchGeneration = m_IrqState.beginDispatch(irq);
1441 hardStageGeneration = m_HardStageGenerations[irq];
1442 hasThreadedStage = delivery == IrqDelivery::Mixed;
1443
1444 IrqHandlerRegistry::AdmissionCutoff threadedAdmissionCutoff = {};
1445 bool cutoffCaptured = false;
1446 if (delivery == IrqDelivery::Mixed) {
1448 cutoffCaptured = m_Handlers.captureMixedAdmissionCutoffs(static_cast<uint8_t>(irq), cutoffs);
1449 hardAdmissionCutoff = cutoffs.hard;
1450 threadedAdmissionCutoff = cutoffs.threaded;
1451 } else {
1452 // Preserve the ordinary unhandled-vector path for a disabled line
1453 // which was already accepted by the controller.
1454 cutoffCaptured =
1455 m_Handlers.captureAdmissionCutoff(static_cast<uint8_t>(irq), hardAdmissionCutoff);
1456 }
1457
1458 if (!cutoffCaptured) {
1459 const bool wasEnabled = m_IrqState.enabled(irq);
1460 m_FailClosedReasons[irq] |=
1461 hasThreadedStage ? ThreadedPublicationQuarantine : UnhandledQuarantine;
1462 m_IrqState.completeDispatch(irq, dispatchGeneration, true);
1463 if (wasEnabled != m_IrqState.enabled(irq)) {
1464 applyMaskLocked();
1465 }
1466 if (controllerAck != IrqControllerAck::None) {
1467 eoiLocked(irq);
1468 }
1469 __atomic_add_fetch(
1470 hasThreadedStage ? &m_ThreadedPublicationFailures[irq] : &m_UnhandledIrqCount[irq],
1471 static_cast<size_t>(1), __ATOMIC_RELAXED);
1472 publishDiagnosticLineLocked(static_cast<uint8_t>(irq));
1473 return;
1474 }
1475
1476 if (hasThreadedStage) {
1477 // A one-shot threaded policy masks before EOI. Immediate-release
1478 // policies remain open while the worker runs.
1480 if (lineRelease == IrqLineRelease::AfterThreadedCompletion) {
1481 applyMaskLocked();
1482 }
1483 if (controllerAck == IrqControllerAck::BeforeHardStage) {
1484 eoiLocked(irq);
1485 }
1486 threadedCookie = advanceThreadedCookieLocked(irq);
1487 m_ThreadedDispatchGenerations[irq] = dispatchGeneration;
1488 m_ThreadedHadHardStage[irq] = true;
1489 m_ThreadedHardAdmitted[irq] = false;
1490 m_ThreadedHardDisposition[irq] = HardIrqDisposition::NotHandled;
1491 m_ThreadedHardVetoRecovery[irq] = false;
1492 m_ThreadedHardVetoRecoveryGenerations[irq] = 0;
1493 threadedPublished =
1494 m_Handlers.publishThreadedDispatch(irq, threadedCookie, threadedAdmissionCutoff);
1495 if (!threadedPublished) {
1496 m_FailClosedReasons[irq] |= ThreadedPublicationQuarantine;
1497 m_ThreadedHadHardStage[irq] = false;
1498 m_ThreadedHardAdmitted[irq] = false;
1499 m_ThreadedHardDisposition[irq] = HardIrqDisposition::NotHandled;
1500 __atomic_add_fetch(&m_ThreadedPublicationFailures[irq], static_cast<size_t>(1),
1501 __ATOMIC_RELAXED);
1502 }
1503
1504 // A mixed line's worker must not overlap the hard callbacks which
1505 // share its physical occurrence. Its doorbell is rung only after
1506 // the hard stage below has completed.
1507 } else if (controllerAck == IrqControllerAck::BeforeHardStage) {
1508 eoiLocked(irq);
1509 }
1510 publishDiagnosticLineLocked(static_cast<uint8_t>(irq));
1511 }
1512
1513 entry.release();
1514
1515 HardIrqDisposition hardDisposition = HardIrqDisposition::NotHandled;
1516 const bool admitted = m_Handlers.dispatchHard(irq, state, hardDisposition, nullptr,
1517 dispatchGeneration, hardAdmissionCutoff);
1518
1519 PicHardTailRecord tail = {};
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);
1531}
1532
1533void Pic::finishHardDispatchLocked(const PicHardTailRecord& record) {
1534 const uint8_t irq = record.irq;
1535 if (UNLIKELY(irq >= PicIrqState::LineCount)) {
1536 FATAL_NOLOCK("PIC hard dispatch completion has an invalid IRQ.");
1537 return;
1538 }
1539 const size_t dispatchGeneration = record.dispatchGeneration;
1540 const size_t threadedCookie = record.threadedCookie;
1541 const IrqControllerAck controllerAck = record.controllerAck;
1542 const bool hasThreadedStage = record.hasThreadedStage;
1543 bool threadedPublished = record.threadedPublished;
1544 const bool admitted = record.admitted;
1545
1546 PicHardTailCurrentState current = {};
1547 current.controllerLifetime = m_ControllerStateGate.currentLifetime(irq);
1548 current.dispatchGeneration = m_IrqState.dispatchGeneration(irq);
1549 current.hardStageGeneration = m_HardStageGenerations[irq];
1550 current.threadedCookie = m_ThreadedCookies[irq];
1551 current.threadedDispatchGeneration = m_ThreadedDispatchGenerations[irq];
1552 current.hardHandlerCount = m_IrqState.hardHandlerCount(irq);
1553 current.delivery = hasThreadedStage ? m_IrqState.delivery(irq) : IrqDelivery::None;
1554 current.hardLineQuarantined = m_Handlers.hardLineQuarantined(irq);
1555 const PicHardTailPlan plan = resolvePicHardTail(record, current);
1556 PicHardTailTerminalSequence terminal(!plan.controllerLifetimeCurrent, controllerAck);
1557 terminal.applyTemporaryMask([this, irq]() {
1558 m_ControllerTemporaryMask |= static_cast<uint16_t>(1U << irq);
1559 applyMaskLocked();
1560 });
1561
1562 const IrqDelivery currentDelivery = current.delivery;
1563 const bool hardStageLifetimeCurrent = plan.hardStageLifetimeCurrent;
1564 const bool threadedLifetimeCurrent = plan.threadedLifetimeCurrent;
1565 const HardIrqDisposition effectiveHardDisposition = plan.effectiveHardDisposition;
1566 const bool hardHandoffFailed = effectiveHardDisposition == HardIrqDisposition::KeepMasked;
1567 if (hasThreadedStage && (hardHandoffFailed || (threadedLifetimeCurrent && !threadedPublished))) {
1568 const bool wasEnabled = m_IrqState.enabled(irq);
1569 if (hardHandoffFailed) {
1570 m_FailClosedReasons[irq] |= HardHandoffQuarantine;
1571 }
1572 m_IrqState.completeDispatch(irq, dispatchGeneration, true);
1573 if (wasEnabled != m_IrqState.enabled(irq)) {
1574 applyMaskLocked();
1575 }
1576 if (hardHandoffFailed) {
1577 __atomic_add_fetch(&m_ThreadedPublicationFailures[irq], static_cast<size_t>(1),
1578 __ATOMIC_RELAXED);
1579 }
1580 }
1581
1582 if (hasThreadedStage) {
1583 if (threadedLifetimeCurrent) {
1584 if (!threadedPublished) {
1585 m_ThreadedHadHardStage[irq] = false;
1586 m_ThreadedHardAdmitted[irq] = false;
1587 m_ThreadedHardDisposition[irq] = HardIrqDisposition::NotHandled;
1588 const bool wasEnabled = m_IrqState.enabled(irq);
1589 m_IrqState.completeDispatch(irq, dispatchGeneration, true);
1590 if (wasEnabled != m_IrqState.enabled(irq)) {
1591 applyMaskLocked();
1592 }
1593 terminal.acknowledge([this, irq]() { eoiLocked(irq); });
1594 publishDiagnosticLineLocked(static_cast<uint8_t>(irq));
1595 return;
1596 }
1597 const bool hardStageQuiesced =
1598 !hardStageLifetimeCurrent || currentDelivery == IrqDelivery::Threaded;
1599 m_ThreadedHardAdmitted[irq] = admitted || hardStageQuiesced;
1600 m_ThreadedHardDisposition[irq] = effectiveHardDisposition;
1601 const PicHardTailDoorbellResult doorbell = resolvePicHardTailDoorbell(
1602 true, m_ThreadedDispatcher.publishFromInterrupt(irq, threadedCookie));
1603 if (doorbell.invalidateStagedDispatch) {
1605 if (doorbell.quarantine) {
1606 m_FailClosedReasons[irq] |= ThreadedPublicationQuarantine;
1607 }
1608 m_ThreadedHadHardStage[irq] = false;
1609 m_ThreadedHardAdmitted[irq] = false;
1610 m_ThreadedHardDisposition[irq] = HardIrqDisposition::NotHandled;
1611 __atomic_add_fetch(&m_ThreadedPublicationFailures[irq], static_cast<size_t>(1),
1612 __ATOMIC_RELAXED);
1613 }
1614 threadedPublished = doorbell.published;
1615 if (doorbell.completeDispatch) {
1616 const bool wasEnabled = m_IrqState.enabled(irq);
1617 m_IrqState.completeDispatch(irq, dispatchGeneration, true);
1618 if (wasEnabled != m_IrqState.enabled(irq)) {
1619 applyMaskLocked();
1620 }
1621 }
1622 } else if (plan.threadedAction == PicHardTailThreadedAction::Quiesced) {
1623 // The old threaded action was synchronously quiesced. A
1624 // replacement threaded handler belongs to the next
1625 // occurrence, but this hard result still needs one terminal
1626 // decision for the occurrence already in flight.
1627 const bool threadedStageQuiesced = true;
1628 const bool aggregateAdmitted = admitted || threadedStageQuiesced;
1629 const bool aggregateAllowRearm =
1630 (effectiveHardDisposition == HardIrqDisposition::Handled || threadedStageQuiesced) &&
1631 effectiveHardDisposition != HardIrqDisposition::KeepMasked;
1632 const bool wasEnabled = m_IrqState.enabled(irq);
1633 m_IrqState.completeDispatch(irq, dispatchGeneration,
1634 aggregateAdmitted && !aggregateAllowRearm);
1635 if (!aggregateAdmitted || (!aggregateAllowRearm && !hardHandoffFailed)) {
1636 m_FailClosedReasons[irq] |= UnhandledQuarantine;
1637 __atomic_add_fetch(&m_UnhandledIrqCount[irq], static_cast<size_t>(1), __ATOMIC_RELAXED);
1638 }
1639 if (wasEnabled != m_IrqState.enabled(irq)) {
1640 applyMaskLocked();
1641 }
1642 }
1643 terminal.acknowledge([this, irq]() { eoiLocked(irq); });
1644 publishDiagnosticLineLocked(static_cast<uint8_t>(irq));
1645 return;
1646 }
1647
1648 const bool wasEnabled = m_IrqState.enabled(irq);
1649 const bool needsAcknowledgement =
1650 effectiveHardDisposition == HardIrqDisposition::KeepMasked ||
1651 (admitted && effectiveHardDisposition == HardIrqDisposition::NotHandled);
1652 if (needsAcknowledgement) {
1653 m_FailClosedReasons[irq] |= effectiveHardDisposition == HardIrqDisposition::KeepMasked
1654 ? HardHandoffQuarantine
1655 : UnhandledQuarantine;
1656 }
1657 m_IrqState.completeDispatch(irq, dispatchGeneration, needsAcknowledgement);
1658 if (effectiveHardDisposition == HardIrqDisposition::KeepMasked) {
1659 __atomic_add_fetch(&m_ThreadedPublicationFailures[irq], static_cast<size_t>(1),
1660 __ATOMIC_RELAXED);
1661 } else if (hardStageLifetimeCurrent &&
1662 (!admitted || effectiveHardDisposition == HardIrqDisposition::NotHandled)) {
1663 __atomic_add_fetch(&m_UnhandledIrqCount[irq], static_cast<size_t>(1), __ATOMIC_RELAXED);
1664 }
1665 if (wasEnabled != m_IrqState.enabled(irq)) {
1666 applyMaskLocked();
1667 }
1668 terminal.acknowledge([this, irq]() { eoiLocked(irq); });
1669 publishDiagnosticLineLocked(static_cast<uint8_t>(irq));
1670}
1671
1672void Pic::finishHardDispatchFromInterrupt(const PicHardTailRecord& record) {
1674 finishHardDispatchLocked(record);
1675 releaseControllerStateFromInterrupt();
1676 return;
1677 }
1678
1679 if (m_HardTailQueue.publish(record.irq, record)) {
1680 if (m_ControllerStateGate.queueTailRecord(record.irq)) {
1681 releaseControllerStateFromInterrupt();
1682 }
1683 return;
1684 }
1685
1686 // The per-line slot cannot normally be occupied because gate ownership
1687 // prevents another callback on that line. Preserve the hardware terminal
1688 // obligations even if that invariant is violated instead of waiting.
1689 if (m_ControllerStateGate.queueTail(record.irq, record.controllerLifetime,
1690 record.controllerAck == IrqControllerAck::AfterHardStage)) {
1691 releaseControllerStateFromInterrupt();
1692 }
1693}
1694
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);
1699 if (!guard.owned())
1700 FATAL("PIC controller continuation could not claim thread state.");
1701 return;
1702 }
1703
1704 size_t dispatchGeneration = 0;
1705 {
1706 StateGuard guard(*pic);
1707 if (!guard.owned()) {
1708 FATAL("PIC threaded dispatch could not snapshot line state.");
1709 return;
1710 }
1711 if (irq >= PicIrqState::LineCount) {
1712 return;
1713 }
1714 const IrqDelivery delivery = pic->m_IrqState.delivery(irq);
1715 if (cookie != pic->m_ThreadedCookies[irq] ||
1716 (delivery != IrqDelivery::Threaded && delivery != IrqDelivery::Mixed)) {
1717 return;
1718 }
1719 dispatchGeneration = pic->m_ThreadedDispatchGenerations[irq];
1720 }
1721
1723 const bool admitted = pic->m_Handlers.dispatchThreaded(irq, cookie, result);
1724
1725 {
1726 StateGuard guard(*pic);
1727 if (!guard.owned()) {
1728 FATAL("PIC threaded completion could not claim line state.");
1729 return;
1730 }
1731 const IrqDelivery delivery = pic->m_IrqState.delivery(irq);
1732 if (cookie != pic->m_ThreadedCookies[irq] ||
1733 dispatchGeneration != pic->m_ThreadedDispatchGenerations[irq] ||
1734 (delivery != IrqDelivery::Threaded && delivery != IrqDelivery::Mixed)) {
1735 return;
1736 }
1737
1738 const bool hadHardStage = pic->m_ThreadedHadHardStage[irq];
1739 const bool aggregateAdmitted = pic->m_ThreadedHardAdmitted[irq] || admitted;
1740 const HardIrqDisposition hardDisposition = pic->m_ThreadedHardDisposition[irq];
1741 const bool aggregateAllowRearm =
1742 (hardDisposition == HardIrqDisposition::Handled || result.allowRearm) &&
1743 hardDisposition != HardIrqDisposition::KeepMasked;
1744 const bool hardVetoRecovery =
1745 hadHardStage && hardDisposition == HardIrqDisposition::KeepMasked && admitted &&
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 ||
1752 (!aggregateAllowRearm && hardDisposition != HardIrqDisposition::KeepMasked)) {
1753 pic->m_FailClosedReasons[irq] |= UnhandledQuarantine;
1754 __atomic_add_fetch(&pic->m_UnhandledIrqCount[irq], static_cast<size_t>(1), __ATOMIC_RELAXED);
1755 }
1756 if (hadHardStage && pic->m_IrqState.lineRelease(irq) == IrqLineRelease::AfterHardStage) {
1757 pic->m_IrqState.completeDispatch(irq, dispatchGeneration,
1758 aggregateAdmitted && !aggregateAllowRearm);
1759 }
1760 pic->m_IrqState.completeThreadedDispatch(irq, dispatchGeneration,
1761 aggregateAdmitted && aggregateAllowRearm);
1762 pic->m_ThreadedHadHardStage[irq] = false;
1763 pic->m_ThreadedHardAdmitted[irq] = false;
1764 pic->m_ThreadedHardDisposition[irq] = HardIrqDisposition::NotHandled;
1765 if (wasEnabled != pic->m_IrqState.enabled(irq)) {
1766 pic->applyMaskLocked();
1767 }
1768 pic->publishDiagnosticLineLocked(irq);
1769 }
1770}
1771
1772#if MULTIPROCESSOR
1773bool Pic::promptThreadedWorker(void* context, uint8_t line, size_t workerProcessor) {
1774 Pic* pic = reinterpret_cast<Pic*>(context);
1775 if (!pic) {
1776 return false;
1777 }
1778
1779 __atomic_add_fetch(&pic->m_ControllerPromptAttempts, static_cast<size_t>(1), __ATOMIC_RELAXED);
1780 __atomic_store_n(&pic->m_ControllerPromptDestination, static_cast<size_t>(pic->m_DeliveryApicId),
1781 __ATOMIC_RELAXED);
1782 if (line != ControllerWorkLine || workerProcessor != pic->m_DeliveryProcessor) {
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);
1786 return false;
1787 }
1788
1789 // The dispatcher has already staged the BSP scheduler doorbell and the
1790 // remote producer's cookie. This bounded ICR transaction only prompts the
1791 // owning BSP; it cannot retract the accepted controller occurrence.
1792 const bool submitted = Pc::instance().getLocalApic().interProcessorInterrupt(
1793 pic->m_DeliveryApicId, IPI_RESCHEDULE_VECTOR, LocalApic::deliveryModeFixed, true, false);
1794 if (!submitted) {
1795 __atomic_add_fetch(&pic->m_ControllerPromptFailures, static_cast<size_t>(1), __ATOMIC_RELAXED);
1796 }
1797 __atomic_store_n(&pic->m_ControllerPromptState,
1798 static_cast<size_t>(submitted ? IrqControllerPromptState::Submitted
1799 : IrqControllerPromptState::Failed),
1800 __ATOMIC_RELEASE);
1801 return submitted;
1802}
1803#endif
1804
1805void Pic::eoiLocked(uint8_t irq) {
1806 if (irq > 7) {
1807 m_SlavePort.write8(0x60 + (irq - 8), 0);
1808
1809 // ACK the cascade IRQ (IRQ2).
1810 m_MasterPort.write8(0x62, 0);
1811 } else {
1812 m_MasterPort.write8(0x60 + irq, 0);
1813 }
1814}
1815
1816void Pic::applyMaskLocked() {
1817 const uint16_t canonical = m_IrqState.mask();
1818 const bool canUnmaskBeforeRelease =
1821 preserveUnsafePicUnmasks(m_AppliedControllerMask, canonical, m_ControllerTemporaryMask,
1822 m_DeferredLineTransitions, canUnmaskBeforeRelease);
1823 const uint16_t mask = effectiveMaskLocked();
1824 emitPicMaskWrites(mask, [this](PicControllerWriteTarget target, uint8_t value) {
1825 if (target == PicControllerWriteTarget::MasterMask) {
1826 m_MasterPort.write8(value, 1);
1827 } else {
1828 m_SlavePort.write8(value, 1);
1829 }
1830 });
1832}
1833
1834uint16_t Pic::effectiveMaskLocked() const {
1835 return effectivePicMask(m_IrqState.mask(), m_ControllerTemporaryMask);
1836}
1837
1838void Pic::finishDeferredLineTransitionsLocked() {
1839 const uint16_t pending = m_DeferredLineTransitions;
1841 const bool canPublish = Processor::index() == m_DeliveryProcessor && !Processor::getInterrupts();
1842 for (size_t irq = 0; irq < PicIrqState::LineCount; ++irq) {
1843 if ((pending & static_cast<uint16_t>(1U << irq)) && !m_IrqState.lineTransitionPending(irq)) {
1844 if (canPublish) {
1846 } else {
1847 m_DeferredLineTransitions |= static_cast<uint16_t>(1U << irq);
1848 }
1849 }
1850 }
1851}
1852
1853void Pic::clearControllerTemporaryMaskLocked(uint16_t restoreMask) {
1854 const uint16_t restored = static_cast<uint16_t>(m_ControllerTemporaryMask & restoreMask);
1855 if (!restored) {
1856 return;
1857 }
1858
1860 finishDeferredLineTransitionsLocked();
1861 }
1862 m_ControllerTemporaryMask &= static_cast<uint16_t>(~restored);
1863 applyMaskLocked();
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));
1867 }
1868 }
1869}
1870
1871void Pic::setEnabledLocked(uint8_t irq, bool enable) {
1872 m_IrqState.setEnabled(irq, enable);
1873 applyMaskLocked();
1874}
1875
1876void Pic::enable(uint8_t irq, bool enable) {
1877#if MULTIPROCESSOR
1878 if (PciMessageInterrupts::contains(irq)) {
1879 if (m_Messages) {
1880 m_Messages->enable(irq, enable);
1881 }
1882 return;
1883 }
1884#if ACPI
1885 if (PciIoApicInterrupts::contains(irq)) {
1886 if (m_Intx) {
1887 m_Intx->enable(irq, enable);
1888 }
1889 return;
1890 }
1891#endif
1892#endif
1893 if (irq >= PicIrqState::LineCount) {
1894 return;
1895 }
1896
1897 StateGuard guard(*this);
1898 if (!guard.owned())
1899 return;
1900 if (m_ShuttingDown && enable) {
1901 return;
1902 }
1903 setEnabledLocked(irq, enable);
1904 publishDiagnosticLineLocked(irq);
1905}
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.");
1910 return;
1911 }
1912 m_IrqState.setAllEnabled(enable);
1913 applyMaskLocked();
1914 publishAllDiagnosticLinesLocked();
1915}
virtual uintptr_t getInterruptNumber()
Definition Device.h:271
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)
Definition IoPort.cc:29
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)
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)
Definition PicIrqState.h:84
void completeDispatch(size_t irq, size_t dispatchGeneration, bool needsAcknowledgement)
Definition Pic.h:52
size_t m_UnregisterReservations[PicIrqState::LineCount]
Definition Pic.h:233
size_t m_ThreadedPublicationFailures[PicIrqState::LineCount]
Definition Pic.h:211
size_t m_ThreadedDispatchGenerations[PicIrqState::LineCount]
Definition Pic.h:199
static EXPORTED_PUBLIC Pic m_Instance
Definition Pic.h:247
virtual bool unregisterHandler(irq_id_t Id, IrqHandlerBase *handler)
Definition Pic.cc:592
virtual bool unregisterSchedulerIrqHandler(irq_id_t Id, SchedulerIrqHandler *handler)
Definition Pic.cc:507
size_t m_HardStageGenerations[PicIrqState::LineCount]
Definition Pic.h:201
IrqDiagnosticSnapshotStore< PicIrqState::LineCount > m_Diagnostics
Definition Pic.h:231
uint8_t m_DeliveryApicId
Definition Pic.h:229
virtual bool control(uint8_t irq, ControlCode code, size_t argument)
Definition Pic.cc:144
virtual irq_id_t registerIsaIrqHandler(uint8_t irq, IrqHandler *handler, const IrqPolicy &policy)
Definition Pic.cc:167
virtual irq_id_t registerHardIsaIrqHandler(uint8_t irq, HardIrqHandler *handler, const IrqPolicy &policy)
Definition Pic.cc:200
virtual irq_id_t registerPciIrqHandler(IrqHandler *handler, Device *pDevice, const IrqPolicy &policy)
Definition Pic.cc:329
Mutex m_ControllerThreadMutex
Definition Pic.h:193
size_t m_ThreadedCookies[PicIrqState::LineCount]
Definition Pic.h:197
PicControllerStateGate m_ControllerStateGate
Definition Pic.h:189
IoPort m_MasterPort
Definition Pic.h:171
uint16_t m_ControllerTemporaryMask
Definition Pic.h:221
PicHardTailQueue m_HardTailQueue
Definition Pic.h:191
virtual irq_id_t registerSchedulerIrqHandler(uint8_t irq, SchedulerIrqHandler *handler, const IrqPolicy &policy)
Definition Pic.cc:489
uint8_t m_FailClosedReasons[PicIrqState::LineCount]
Definition Pic.h:209
size_t m_UnhandledIrqCount[16]
Definition Pic.h:241
virtual void tick()
Definition Pic.cc:142
bool m_ThreadedHadHardStage[PicIrqState::LineCount]
Definition Pic.h:203
size_t m_DeliveryProcessor
Definition Pic.h:227
virtual void interrupt(size_t interruptNumber, InterruptState &state)
Definition Pic.cc:1367
bool m_ShuttingDown
Definition Pic.h:235
size_t m_ControllerPromptAttempts
Definition Pic.h:216
IoPort m_SlavePort
Definition Pic.h:169
ThreadedIrqDispatcher m_ThreadedDispatcher
Definition Pic.h:195
virtual irq_id_t registerHardPciIrqHandler(HardIrqHandler *handler, Device *pDevice, const IrqPolicy &policy)
Definition Pic.cc:381
SchedulerIrqHandler * m_SchedulerIrqHandler
Definition Pic.h:185
bool registerPciMsixIrqHandlers(Device *device, IrqHandler *const *handlers, size_t count, irq_id_t *ids, bool &fallbackSafe, const size_t *processors=nullptr) override
Definition Pic.cc:455
uint16_t m_DeferredLineTransitions
Definition Pic.h:225
bool initialiseThreaded()
Definition Pic.cc:715
bool reservePciRoute(uint8_t irq)
Definition Pic.cc:232
size_t m_SpuriousIrqCount[16]
Definition Pic.h:239
bool m_MitigatedIrqs[16]
Definition Pic.h:243
irq_id_t registerHardPciMessageIrqHandler(HardIrqHandler *handler, Device *device, const IrqPolicy &intxFallbackPolicy) override
Definition Pic.cc:467
irq_id_t registerPciMessageIrqHandler(IrqHandler *handler, Device *device, const IrqPolicy &intxFallbackPolicy) override
Definition Pic.cc:433
size_t m_MitigationThreshold[16]
Definition Pic.h:245
IrqHandlerRegistry m_Handlers
Definition Pic.h:177
bool shutdownThreaded()
Definition Pic.cc:752
PicIrqState m_IrqState
Definition Pic.h:187
size_t m_ControllerContentions[PicIrqState::LineCount]
Definition Pic.h:214
Pic() INITIALISATION_ONLY
Definition Pic.cc:800
bool spuriousLocked(size_t irq)
Definition Pic.cc:1020
virtual size_t snapshotIrqLines(IrqLineDiagnosticSnapshot *out, size_t capacity) const
Definition Pic.cc:928
bool initialise() INITIALISATION_ONLY
Definition Pic.cc:648
uint16_t m_AppliedControllerMask
Definition Pic.h:223
size_t m_IrqCount[16]
Definition Pic.h:237
static bool getInterrupts()
static void pause()
static ExecutionContext executionContext()
Definition Processor.cc:109
static void setInterrupts(bool bEnable)
static size_t index()
void release(size_t n=1)
Definition Semaphore.cc:549
bool acquire(size_t n=1, size_t timeoutSecs=0, size_t timeoutUsecs=0)
Definition Semaphore.cc:355
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
Definition panic.cc:118
@ Dec
Definition Log.h:126
HardIrqDisposition
Definition IrqHandler.h:44