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