The Pedigree Project 0.1
mach_virt/IrqManager.cc
1/*
2 * Copyright (c) 2026, Pedigree Developers
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted.
6 */
7
8#include "IrqManager.h"
9#include "pedigree/kernel/LockGuard.h"
10#include "pedigree/kernel/Log.h"
11#include "pedigree/kernel/machine/Device.h"
12#include "pedigree/kernel/machine/IrqHandler.h"
13#include "pedigree/kernel/machine/Pci.h"
14#include "pedigree/kernel/machine/SchedulerIrqHandler.h"
15#include "pedigree/kernel/processor/Processor.h"
16
17#include "DeviceTree.h"
18#include "system/kernel/core/processor/DeviceHardIrqContext.h"
19
20namespace {
21constexpr uintptr_t DistControl = 0x000;
22constexpr uintptr_t DistType = 0x004;
23constexpr uintptr_t DistGroup = 0x080;
24constexpr uintptr_t DistEnable = 0x100;
25constexpr uintptr_t DistDisable = 0x180;
26constexpr uintptr_t DistClearPending = 0x280;
27constexpr uintptr_t DistPriority = 0x400;
28constexpr uintptr_t DistTarget = 0x800;
29constexpr uintptr_t DistConfig = 0xc00;
30constexpr uintptr_t DistRouter = 0x6000;
31constexpr uintptr_t CpuControl = 0x000;
32constexpr uintptr_t CpuPriorityMask = 0x004;
33constexpr uintptr_t CpuAcknowledge = 0x00c;
34constexpr uintptr_t CpuEndOfInterrupt = 0x010;
35constexpr uintptr_t RedistWaker = 0x014;
36constexpr uintptr_t RedistSgi = 0x10000;
37
38// QEMU HVF needs unindexed, single-register MMIO accesses with valid syndromes.
39uint32_t read32(uintptr_t base, uintptr_t offset) {
40 uint32_t value;
41#if ARMV7
42 asm volatile("ldr %0, [%1]" : "=r"(value) : "r"(base + offset) : "memory");
43#else
44 asm volatile("ldr %w0, [%1]" : "=r"(value) : "r"(base + offset) : "memory");
45#endif
46 return value;
47}
48
49void write32(uintptr_t base, uintptr_t offset, uint32_t value) {
50#if ARMV7
51 asm volatile("str %1, [%0]" : : "r"(base + offset), "r"(value) : "memory");
52#else
53 asm volatile("str %w1, [%0]" : : "r"(base + offset), "r"(value) : "memory");
54#endif
55}
56
57#if ARM64
58void write64(uintptr_t base, uintptr_t offset, uint64_t value) {
59 asm volatile("str %1, [%0]" : : "r"(base + offset), "r"(value) : "memory");
60}
61#endif
62
63void barrier() {
64 asm volatile("dsb sy\n\tisb" : : : "memory");
65}
66
67#if ARM64
68uint64_t processorAffinity() {
69 uint64_t mpidr;
70 asm volatile("mrs %0, mpidr_el1" : "=r"(mpidr));
71 return (mpidr & 0xff00000000ULL) | (mpidr & 0x00ffffffULL);
72}
73#endif
74} // namespace
75
76VirtIrqManager VirtIrqManager::m_Instance;
77
78VirtIrqManager& VirtIrqManager::instance() {
79 return m_Instance;
80}
81
82VirtIrqManager::VirtIrqManager()
83 : m_Hard(),
84 m_Scheduler(),
85 m_PciHandlers(),
86 m_PciDispatcher(MakeConstantString("Virt PCI IRQ"), MaxPciLines, dispatchPciLine, this),
87 m_PciLock(false),
88 m_PciLines(),
89 m_Its(),
90 m_Version(0),
91 m_IrqCount(0),
92 m_MsiFirst(0),
93 m_MsiLast(0),
94 m_MsiAddress(0),
95 m_MsiReady(false),
96 m_Initialised(false) {}
97
98bool VirtIrqManager::initialise() {
99 if (m_Initialised) {
100 return true;
101 }
102 m_Version = VirtDeviceTree::gicVersion();
103 m_Initialised = m_Version == 2 ? initialiseV2() : m_Version == 3 && initialiseV3();
104 if (m_Initialised) {
105 m_MsiReady = initialiseMsi();
106 }
107 return m_Initialised;
108}
109
110bool VirtIrqManager::initialiseThreaded() {
111 return m_Initialised && m_PciDispatcher.initialise();
112}
113
114bool VirtIrqManager::shutdownThreaded() {
115 {
116 LockGuard<Spinlock> guard(m_PciLock);
117 for (const PciLine& line : m_PciLines) {
118 if (line.handlers || line.reserved) {
119 return false;
120 }
121 }
122 for (const PciLine& line : m_PciLines) {
123 if (line.irq) {
124 setEnabled(line.irq, false);
125 }
126 }
127 }
128 return m_PciDispatcher.shutdown();
129}
130
131bool VirtIrqManager::initialiseV2() {
132 const uintptr_t dist = VirtDeviceTree::gicDistributorBase();
133 const uintptr_t cpu = VirtDeviceTree::gicCpuBase();
134 if (!dist || !cpu) {
135 return false;
136 }
137
138 write32(dist, DistControl, 0);
139 const size_t lines = ((read32(dist, DistType) & 0x1f) + 1) * 32;
140 const size_t count = lines < MaxIrqs ? lines : MaxIrqs;
141 m_IrqCount = count;
142 for (size_t irq = 0; irq < count; irq += 32) {
143 write32(dist, DistDisable + irq / 8, 0xffffffff);
144 }
145 for (size_t irq = 0; irq < count; irq += 4) {
146 write32(dist, DistPriority + irq, 0xa0a0a0a0);
147 if (irq >= 32) {
148 write32(dist, DistTarget + irq, 0x01010101);
149 }
150 }
151 write32(cpu, CpuPriorityMask, 0xff);
152 write32(cpu, CpuControl, 1);
153 write32(dist, DistControl, 1);
154 barrier();
155 return true;
156}
157
158bool VirtIrqManager::initialiseV3() {
159#if ARMV7
160 return false;
161#else
162 const uintptr_t dist = VirtDeviceTree::gicDistributorBase();
163 const uintptr_t redist = VirtDeviceTree::gicRedistributorBase();
164 if (!dist || !redist) {
165 return false;
166 }
167
168 write32(dist, DistControl, 0);
169 for (size_t wait = 0; wait < 100000 && (read32(dist, DistControl) & (1U << 31)); ++wait) {
170 asm volatile("yield");
171 }
172 // GICD_IROUTER is available only after affinity routing is enabled.
173 write32(dist, DistControl, 1U << 4);
174 for (size_t wait = 0; wait < 100000 && (read32(dist, DistControl) & (1U << 31)); ++wait) {
175 asm volatile("yield");
176 }
177 if (!(read32(dist, DistControl) & (1U << 4))) {
178 return false;
179 }
180 const size_t lines = ((read32(dist, DistType) & 0x1f) + 1) * 32;
181 const size_t count = lines < MaxIrqs ? lines : MaxIrqs;
182 m_IrqCount = count;
183 for (size_t irq = 32; irq < count; irq += 32) {
184 write32(dist, DistDisable + irq / 8, 0xffffffff);
185 write32(dist, DistGroup + irq / 8, 0xffffffff);
186 }
187 for (size_t irq = 32; irq < count; irq += 4) {
188 write32(dist, DistPriority + irq, 0xa0a0a0a0);
189 }
190
191 const uint64_t affinity = processorAffinity();
192 for (size_t irq = 32; irq < count; ++irq) {
193 write64(dist, DistRouter + irq * 8, affinity);
194 }
195
196 write32(redist, RedistWaker, read32(redist, RedistWaker) & ~(1U << 1));
197 for (size_t wait = 0; wait < 100000 && (read32(redist, RedistWaker) & (1U << 2)); ++wait) {
198 asm volatile("yield");
199 }
200 if (read32(redist, RedistWaker) & (1U << 2)) {
201 return false;
202 }
203
204 const uintptr_t sgi = redist + RedistSgi;
205 write32(sgi, DistDisable, 0xffffffff);
206 write32(sgi, DistGroup, 0xffffffff);
207 for (size_t irq = 0; irq < 32; irq += 4) {
208 write32(sgi, DistPriority + irq, 0xa0a0a0a0);
209 }
210
211 uint64_t sre;
212 asm volatile("mrs %0, ICC_SRE_EL1" : "=r"(sre));
213 sre |= 1;
214 asm volatile("msr ICC_SRE_EL1, %0\n\tisb" : : "r"(sre) : "memory");
215 uint64_t cpuControl;
216 asm volatile("mrs %0, ICC_CTLR_EL1" : "=r"(cpuControl));
217 cpuControl &= ~(1U << 1); // EOIR1 also deactivates the interrupt.
218 asm volatile("msr ICC_CTLR_EL1, %0" : : "r"(cpuControl) : "memory");
219 asm volatile("msr ICC_PMR_EL1, %0" : : "r"(uint64_t(0xff)) : "memory");
220 asm volatile("msr ICC_BPR1_EL1, %0" : : "r"(uint64_t(0)) : "memory");
221 asm volatile("msr ICC_IGRPEN1_EL1, %0" : : "r"(uint64_t(1)) : "memory");
222
223 // The two Group 1 enables cover both single- and two-security-state GICs.
224 write32(dist, DistControl, (1U << 4) | (1U << 1) | 1U);
225 barrier();
226 return true;
227#endif
228}
229
230bool VirtIrqManager::setEnabled(uint32_t irq, bool enabled) {
231 if (!m_Initialised) {
232 return false;
233 }
234 if (irq >= MaxIrqs) {
235 return m_Its.setEnabled(irq, enabled);
236 }
237 uintptr_t base = VirtDeviceTree::gicDistributorBase();
238 if (m_Version == 3 && irq < 32) {
239 base = VirtDeviceTree::gicRedistributorBase() + RedistSgi;
240 }
241 write32(base, (enabled ? DistEnable : DistDisable) + (irq / 32) * 4, 1U << (irq % 32));
242 barrier();
243 return true;
244}
245
246void VirtIrqManager::setLevel(uint32_t irq) {
247 const uintptr_t dist = VirtDeviceTree::gicDistributorBase();
248 const uintptr_t offset = DistConfig + (irq / 16) * 4;
249 write32(dist, offset, read32(dist, offset) & ~(2U << ((irq % 16) * 2)));
250 barrier();
251}
252
253void VirtIrqManager::setEdge(uint32_t irq) {
254 const uintptr_t dist = VirtDeviceTree::gicDistributorBase();
255 const uintptr_t offset = DistConfig + (irq / 16) * 4;
256 write32(dist, offset, read32(dist, offset) | (2U << ((irq % 16) * 2)));
257 barrier();
258}
259
260bool VirtIrqManager::initialiseMsi() {
261 VirtMsiController controller = {};
262 if (!VirtDeviceTree::pciMsiController(controller)) {
263 return false;
264 }
265 if (controller.type == VirtMsiController::Type::GicV3Its) {
266 if (!m_Its.initialise(controller, VirtDeviceTree::gicRedistributorBase(),
267 VirtDeviceTree::gicRedistributorPhysical())) {
268 return false;
269 }
270 m_MsiAddress = m_Its.messageAddress();
271 NOTICE("PCI: GICv3 ITS LPIs " << Dec << GicIts::FirstLpi << ".."
272 << GicIts::FirstLpi + MaxPciLines - 1);
273 return true;
274 }
275 if (controller.type != VirtMsiController::Type::GicV2m || controller.size < 0x44 ||
276 controller.base > UINT64_MAX - 0x40) {
277 return false;
278 }
279#if ARMV7
280 constexpr uintptr_t DirectMapBase = 0x80000000;
281#else
282 constexpr uintptr_t DirectMapBase = 0xffff000000000000ULL;
283#endif
284 const uint32_t typer = read32(DirectMapBase + controller.base, 0x8);
285 const uint32_t first = controller.spiBase ? controller.spiBase : (typer >> 16) & 0x3ff;
286 const uint32_t count = controller.spiCount ? controller.spiCount : typer & 0x3ff;
287 if (first < 32 || first >= m_IrqCount || !count || count > m_IrqCount - first) {
288 return false;
289 }
290 m_MsiFirst = first;
291 m_MsiLast = first + count;
292 m_MsiAddress = controller.base + 0x40;
293 NOTICE("PCI: GICv2m MSI SPIs " << Dec << m_MsiFirst << ".." << m_MsiLast - 1);
294 return true;
295}
296
297void VirtIrqManager::enable(uint8_t irq, bool enabled) {
298 setEnabled(irq, enabled);
299}
300
302 return 0;
303}
304
305size_t VirtIrqManager::pciLine(uint32_t irq) const {
306 for (size_t i = 0; i < MaxPciLines; ++i) {
307 if (m_PciLines[i].irq == irq) {
308 return i;
309 }
310 }
311 return MaxPciLines;
312}
313
314uint8_t VirtIrqManager::pciRegistryLine(size_t slot) const {
315 return static_cast<uint8_t>(m_PciLines[slot].lpi ? slot : m_PciLines[slot].irq);
316}
317
318irq_id_t VirtIrqManager::registerPciHandler(IrqHandlerBase* handler, Device* device,
319 const IrqPolicy& policy, bool hard) {
320 if (!m_Initialised || !device || !handler || policy.trigger() != IrqTrigger::Level ||
321 (hard ? !policy.validForHard() : !policy.validForThreaded()) ||
322 (!hard && !m_PciDispatcher.isInitialised())) {
323 return 0;
324 }
325 uint8_t pin = 0;
326 PciBus& pci = PciBus::instance();
327 if (!pci.readConfig8(device, 0x3d, pin)) {
328 return 0;
329 }
330 const uint32_t irq =
331 pci.interruptRoute(device->getPciBusPosition(), device->getPciDevicePosition(),
332 device->getPciFunctionNumber(), pin);
333 if (irq < 32 || irq >= MaxIrqs || irq != device->getInterruptNumber()) {
334 return 0;
335 }
336
337 LockGuard<Spinlock> guard(m_PciLock);
338 if (m_Hard[irq] || m_Scheduler[irq]) {
339 return 0;
340 }
341 size_t slot = pciLine(irq);
342 if (slot == MaxPciLines) {
343 for (size_t i = 0; i < MaxPciLines; ++i) {
344 if (!m_PciLines[i].irq) {
345 slot = i;
346 break;
347 }
348 }
349 }
350 if (slot == MaxPciLines) {
351 return 0;
352 }
353 PciLine& line = m_PciLines[slot];
354 if (line.removing || line.quarantined || line.reserved || line.message ||
355 (line.handlers && line.hard != hard)) {
356 return 0;
357 }
358 const bool first = !line.handlers;
359 if (first) {
360 setEnabled(irq, false);
361 setLevel(irq);
362 }
363 const bool registered =
364 hard ? m_PciHandlers.registerHardHandler(static_cast<uint8_t>(irq),
365 static_cast<HardIrqHandler*>(handler), policy)
366 : m_PciHandlers.registerThreadedHandler(static_cast<uint8_t>(irq),
367 static_cast<IrqHandler*>(handler), policy);
368 if (!registered) {
369 return 0;
370 }
371 line.irq = irq;
372 line.hard = hard;
373 ++line.handlers;
374 if (!line.inFlight) {
375 setEnabled(irq, true);
376 }
377 return irq;
378}
379
381 const IrqPolicy& policy) {
382 return registerPciHandler(handler, device, policy, false);
383}
384
385irq_id_t VirtIrqManager::registerPciMessageHandler(IrqHandlerBase* handler, Device* device,
386 bool hard, bool& allowFallback) {
387 allowFallback = true;
388 if (!m_MsiReady || !handler || !device || (!hard && !m_PciDispatcher.isInitialised())) {
389 return 0;
390 }
391 PciBus& pci = PciBus::instance();
393 if (!pci.inspectFunction(device, original, false) || (!original.msi && !original.msix)) {
394 return 0;
395 }
396 uint32_t deviceId = 0;
397 if (!VirtDeviceTree::pciMsiDeviceId(device->getPciBusPosition(), device->getPciDevicePosition(),
398 device->getPciFunctionNumber(), deviceId)) {
399 return 0;
400 }
401 const bool lpi = m_Its.ready();
402 size_t slot = MaxPciLines;
403 uint32_t irq = 0;
404 {
405 LockGuard<Spinlock> guard(m_PciLock);
406 for (const PciLine& line : m_PciLines) {
407 if (line.message && line.deviceId == deviceId) {
408 allowFallback = false;
409 return 0;
410 }
411 }
412 for (size_t i = 0; i < MaxPciLines; ++i) {
413 if (!m_PciLines[i].irq && !m_PciLines[i].reserved && !m_PciLines[i].quarantined) {
414 slot = i;
415 break;
416 }
417 }
418 if (slot == MaxPciLines) {
419 return 0;
420 }
421 if (lpi) {
422 irq = GicIts::FirstLpi + slot;
423 } else {
424 for (uint32_t candidate = m_MsiFirst; candidate < m_MsiLast; ++candidate) {
425 if (pciLine(candidate) == MaxPciLines && !m_Hard[candidate] && !m_Scheduler[candidate]) {
426 irq = candidate;
427 break;
428 }
429 }
430 }
431 if (!irq) {
432 return 0;
433 }
434 PciLine& line = m_PciLines[slot];
435 line.irq = irq;
436 line.reserved = true;
437 line.message = true;
438 line.lpi = lpi;
439 line.deviceId = deviceId;
440 line.device = device;
441 line.hard = hard;
442 line.spuriousSafe = !hard && static_cast<IrqHandler*>(handler)->acceptsSpuriousInterrupts();
443 if (!lpi) {
444 setEnabled(irq, false);
445 setEdge(irq);
446 const uintptr_t dist = VirtDeviceTree::gicDistributorBase();
447 write32(dist, DistClearPending + (irq / 32) * 4, 1U << (irq % 32));
448 barrier();
449 }
450 }
451
452 if (lpi && !m_Its.mapDevice(slot, deviceId, irq)) {
453 LockGuard<Spinlock> guard(m_PciLock);
454 PciLine& line = m_PciLines[slot];
455 line.reserved = false;
456 if (m_Its.mappingActive(slot)) {
457 line.quarantined = true;
458 allowFallback = false;
459 } else {
460 line.irq = 0;
461 line.message = false;
462 line.lpi = false;
463 line.device = nullptr;
464 }
465 return 0;
466 }
467
468 bool registered = false;
469 {
470 LockGuard<Spinlock> guard(m_PciLock);
471 const uint8_t key = pciRegistryLine(slot);
472 registered = hard ? m_PciHandlers.registerHardHandler(
473 key, static_cast<HardIrqHandler*>(handler), IrqPolicy::edgeHard())
474 : m_PciHandlers.registerThreadedHandler(
475 key, static_cast<IrqHandler*>(handler), IrqPolicy::edgeThreaded());
476 if (registered) {
477 PciLine& line = m_PciLines[slot];
478 line.handlers = 1;
479 }
480 }
481
482 const uint32_t data = lpi ? 0 : irq;
483 const bool msix = registered && original.msix && pci.enableMsix(device, m_MsiAddress, data);
484 const bool msi = registered && !msix && original.msi && pci.enableMsi(device, m_MsiAddress, data);
485 if (msix || msi) {
486 LockGuard<Spinlock> guard(m_PciLock);
487 PciLine& line = m_PciLines[slot];
488 line.msix = msix;
489 line.reserved = false;
490 if (setEnabled(irq, true)) {
491 return irq;
492 }
493 line.quarantined = true;
494 }
495
496 uint16_t command = 0;
497 const bool disabled =
498 !registered ||
499 (pci.disableMessageInterrupts(device, original) &&
500 pci.updateCommand(device, 0x400U, original.command & 0x400U) &&
501 pci.readConfig16(device, 4, command) && ((command ^ original.command) & 0x400U) == 0);
502 const auto removed =
503 registered ? m_PciHandlers.unregisterHandler(
504 lpi ? static_cast<uint8_t>(slot) : static_cast<uint8_t>(irq), handler)
505 : IrqHandlerRegistry::UnregisterResult::Completed;
506 const bool unmapped = !lpi || (disabled && m_Its.unmapDevice(slot));
507 {
508 LockGuard<Spinlock> guard(m_PciLock);
509 PciLine& line = m_PciLines[slot];
510 line.reserved = false;
511 if (disabled && unmapped && removed == IrqHandlerRegistry::UnregisterResult::Completed) {
512 line.irq = 0;
513 line.handlers = 0;
514 line.message = false;
515 line.msix = false;
516 line.lpi = false;
517 line.device = nullptr;
518 } else {
519 line.quarantined = true;
520 allowFallback = false;
521 }
522 }
523 return 0;
524}
525
527 const IrqPolicy& intxFallbackPolicy) {
528 bool fallback = true;
529 const irq_id_t id = registerPciMessageHandler(handler, device, false, fallback);
530 return id || !fallback ? id : registerPciIrqHandler(handler, device, intxFallbackPolicy);
531}
532
534 size_t count, irq_id_t* ids, bool& fallbackSafe,
535 const size_t*) {
536 fallbackSafe = true;
537 if (!m_MsiReady || !m_PciDispatcher.isInitialised() || !device || !handlers || !ids || !count ||
538 count > MaxPciLines) {
539 return false;
540 }
541 for (size_t i = 0; i < count; ++i) {
542 ids[i] = 0;
543 if (!handlers[i]) {
544 return false;
545 }
546 }
547
548 PciBus& pci = PciBus::instance();
550 if (!pci.inspectFunction(device, original, false) || !original.msix) {
551 return false;
552 }
553 uint32_t deviceId = 0;
554 if (!VirtDeviceTree::pciMsiDeviceId(device->getPciBusPosition(), device->getPciDevicePosition(),
555 device->getPciFunctionNumber(), deviceId)) {
556 return false;
557 }
558
559 const bool lpi = m_Its.ready();
560 size_t slots[MaxPciLines] = {};
561 uint32_t irqs[MaxPciLines] = {};
562 uint32_t data[MaxPciLines] = {};
563 size_t reserved = 0;
564 {
565 LockGuard<Spinlock> guard(m_PciLock);
566 for (const PciLine& line : m_PciLines) {
567 if (line.message && line.deviceId == deviceId) {
568 fallbackSafe = false;
569 return false;
570 }
571 }
572 for (; reserved < count; ++reserved) {
573 size_t slot = MaxPciLines;
574 for (size_t i = 0; i < MaxPciLines; ++i) {
575 if (!m_PciLines[i].irq && !m_PciLines[i].reserved && !m_PciLines[i].quarantined) {
576 slot = i;
577 break;
578 }
579 }
580 if (slot == MaxPciLines) {
581 break;
582 }
583 uint32_t irq = lpi ? GicIts::FirstLpi + slot : 0;
584 if (!lpi) {
585 for (uint32_t candidate = m_MsiFirst; candidate < m_MsiLast; ++candidate) {
586 if (pciLine(candidate) == MaxPciLines && !m_Hard[candidate] && !m_Scheduler[candidate]) {
587 irq = candidate;
588 break;
589 }
590 }
591 }
592 if (!irq) {
593 break;
594 }
595 slots[reserved] = slot;
596 irqs[reserved] = irq;
597 data[reserved] = lpi ? static_cast<uint32_t>(reserved) : irq;
598 PciLine& line = m_PciLines[slot];
599 line.irq = irq;
600 line.reserved = true;
601 line.message = true;
602 line.msix = true;
603 line.msixIndex = static_cast<uint8_t>(reserved);
604 line.lpi = lpi;
605 line.deviceId = deviceId;
606 line.device = device;
607 if (!lpi) {
608 setEnabled(irq, false);
609 setEdge(irq);
610 const uintptr_t dist = VirtDeviceTree::gicDistributorBase();
611 write32(dist, DistClearPending + (irq / 32) * 4, 1U << (irq % 32));
612 barrier();
613 }
614 }
615 if (reserved != count) {
616 for (size_t i = 0; i < reserved; ++i) {
617 PciLine& line = m_PciLines[slots[i]];
618 const size_t cookie = line.cookie;
619 line = PciLine{};
620 line.cookie = cookie;
621 }
622 return false;
623 }
624 }
625
626 if (lpi && !m_Its.mapDeviceGroup(slots, count, deviceId)) {
627 bool active = false;
628 for (size_t i = 0; i < count; ++i) {
629 active = m_Its.mappingActive(slots[i]) || active;
630 }
631 LockGuard<Spinlock> guard(m_PciLock);
632 for (size_t i = 0; i < count; ++i) {
633 PciLine& line = m_PciLines[slots[i]];
634 if (active) {
635 line.quarantined = true;
636 } else {
637 const size_t cookie = line.cookie;
638 line = PciLine{};
639 line.cookie = cookie;
640 }
641 }
642 fallbackSafe = !active;
643 return false;
644 }
645
646 size_t registered = 0;
647 {
648 LockGuard<Spinlock> guard(m_PciLock);
649 for (; registered < count; ++registered) {
650 const uint8_t key = pciRegistryLine(slots[registered]);
651 if (!m_PciHandlers.registerThreadedHandler(key, handlers[registered],
652 IrqPolicy::edgeThreaded())) {
653 break;
654 }
655 m_PciLines[slots[registered]].handlers = 1;
656 m_PciLines[slots[registered]].spuriousSafe =
657 handlers[registered]->acceptsSpuriousInterrupts();
658 }
659 }
660
661 bool enabled = registered == count;
662 if (enabled) {
663 LockGuard<Spinlock> guard(m_PciLock);
664 for (size_t i = 0; i < count; ++i) {
665 m_PciLines[slots[i]].reserved = false;
666 if (!setEnabled(irqs[i], true)) {
667 enabled = false;
668 break;
669 }
670 }
671 }
672
673 bool touched = false;
674 if (enabled && pci.enableMsixVectors(device, m_MsiAddress, data, count, &touched)) {
675 for (size_t i = 0; i < count; ++i) {
676 ids[i] = irqs[i];
677 }
678 return true;
679 }
680
681 bool safe = true;
682 if (enabled) {
683 uint16_t command = 0;
684 safe = pci.disableMessageInterrupts(device, original);
685 if (touched) {
686 for (size_t i = 0; i < count; ++i) {
687 const bool masked = pci.setMsixVectorMask(device, i, true);
688 safe = masked && safe;
689 }
690 }
691 safe = pci.updateCommand(device, 0x400U, original.command & 0x400U) && safe;
692 safe = pci.readConfig16(device, 4, command) && ((command ^ original.command) & 0x400U) == 0 &&
693 safe;
694 }
695
696 size_t cookies[MaxPciLines] = {};
697 {
698 LockGuard<Spinlock> guard(m_PciLock);
699 for (size_t i = 0; i < count; ++i) {
700 PciLine& line = m_PciLines[slots[i]];
701 line.removing = true;
702 const bool masked = setEnabled(irqs[i], false);
703 safe = masked && safe;
704 cookies[i] = line.cookie;
705 ++line.cookie;
706 if (!line.cookie) {
707 ++line.cookie;
708 }
709 }
710 }
711 for (size_t i = 0; i < registered; ++i) {
712 const uint8_t key = lpi ? static_cast<uint8_t>(slots[i]) : static_cast<uint8_t>(irqs[i]);
713 m_PciHandlers.invalidateThreadedLine(key, cookies[i]);
714 if (m_PciHandlers.unregisterHandler(key, handlers[i]) !=
715 IrqHandlerRegistry::UnregisterResult::Completed) {
716 FATAL("ARM PCI MSI-X setup could not drain its handler");
717 }
718 }
719 if (lpi) {
720 for (size_t i = 0; i < count; ++i) {
721 const bool unmapped = m_Its.unmapDevice(slots[i]);
722 safe = unmapped && safe;
723 }
724 }
725 {
726 LockGuard<Spinlock> guard(m_PciLock);
727 for (size_t i = 0; i < count; ++i) {
728 PciLine& line = m_PciLines[slots[i]];
729 if (safe) {
730 const size_t cookie = line.cookie;
731 line = PciLine{};
732 line.cookie = cookie;
733 } else {
734 line.quarantined = true;
735 line.reserved = true;
736 line.handlers = 0;
737 }
738 }
739 }
740 fallbackSafe = safe;
741 return false;
742}
743
745 const IrqPolicy& policy) {
746 if (!m_Initialised || irq < 16 || !handler || !policy.validForHard()) {
747 return 0;
748 }
749 const bool interrupts = Processor::getInterrupts();
751 if (m_Hard[irq] || m_Scheduler[irq]) {
752 Processor::setInterrupts(interrupts);
753 return 0;
754 }
755 m_Hard[irq] = handler;
756 setEnabled(irq, true);
757 Processor::setInterrupts(interrupts);
758 return irq;
759}
760
762 const IrqPolicy& policy) {
763 return registerPciHandler(handler, device, policy, true);
764}
765
767 const IrqPolicy& intxFallbackPolicy) {
768 bool fallback = true;
769 const irq_id_t id = registerPciMessageHandler(handler, device, true, fallback);
770 return id || !fallback ? id : registerHardPciIrqHandler(handler, device, intxFallbackPolicy);
771}
772
774 const IrqPolicy& policy) {
775 if (!m_Initialised || irq < 16 || !handler || !policy.validForHard()) {
776 return 0;
777 }
778 const bool interrupts = Processor::getInterrupts();
780 if (m_Hard[irq] || m_Scheduler[irq]) {
781 Processor::setInterrupts(interrupts);
782 return 0;
783 }
784 m_Scheduler[irq] = handler;
785 setEnabled(irq, true);
786 Processor::setInterrupts(interrupts);
787 return irq;
788}
789
791 if (!m_Initialised || id >= MaxIrqs || !handler) {
792 return false;
793 }
794 const bool interrupts = Processor::getInterrupts();
796 if (m_Scheduler[id] != handler) {
797 Processor::setInterrupts(interrupts);
798 return false;
799 }
800 setEnabled(id, false);
801 m_Scheduler[id] = nullptr;
802 Processor::setInterrupts(interrupts);
803 return true;
804}
805
807 if (!m_Initialised || !handler ||
808 (id >= MaxIrqs &&
809 (!m_Its.ready() || id < GicIts::FirstLpi || id >= GicIts::FirstLpi + MaxPciLines))) {
810 return false;
811 }
812 size_t slot = MaxPciLines;
813 Device* messageDevice = nullptr;
814 bool messageMsix = false;
815 bool messageLpi = false;
816 bool messageLast = false;
817 uint8_t msixIndex = 0;
818 uint8_t registryKey = 0;
819 {
820 LockGuard<Spinlock> guard(m_PciLock);
821 slot = pciLine(id);
822 if (slot != MaxPciLines) {
823 if (Processor::executionContext() != ExecutionContext::WaitableThread ||
824 !Processor::getInterrupts() || m_PciDispatcher.isCurrentWorker()) {
825 ERROR("PCI: IRQ " << Dec << id << " retirement requires a waitable thread");
826 return false;
827 }
828 PciLine& line = m_PciLines[slot];
829 if (line.removing || !line.handlers) {
830 ERROR("PCI: IRQ " << Dec << id << " is not active for retirement");
831 return false;
832 }
833 if (line.message) {
834 for (size_t i = 0; i < MaxPciLines; ++i) {
835 if (i != slot && m_PciLines[i].message && m_PciLines[i].device == line.device &&
836 m_PciLines[i].removing) {
837 ERROR("PCI: IRQ " << Dec << id << " has another vector retiring");
838 return false;
839 }
840 }
841 }
842 line.removing = true;
843 if (!setEnabled(id, false)) {
844 ERROR("PCI: could not mask GIC IRQ " << Dec << id);
845 line.removing = false;
846 line.quarantined = true;
847 return false;
848 }
849 registryKey = pciRegistryLine(slot);
850 if (line.message) {
851 messageDevice = line.device;
852 messageMsix = line.msix;
853 messageLpi = line.lpi;
854 msixIndex = line.msixIndex;
855 messageLast = true;
856 for (size_t i = 0; i < MaxPciLines; ++i) {
857 if (i != slot && m_PciLines[i].message && m_PciLines[i].device == messageDevice) {
858 messageLast = false;
859 break;
860 }
861 }
862 }
863 }
864 }
865 if (slot != MaxPciLines) {
866 bool sourceDisabled = true;
867 if (messageDevice) {
868 if (messageMsix) {
869 sourceDisabled = PciBus::instance().setMsixVectorMask(messageDevice, msixIndex, true);
870 if (sourceDisabled && messageLast) {
871 sourceDisabled = PciBus::instance().disableMsix(messageDevice);
872 }
873 } else {
874 sourceDisabled = PciBus::instance().disableMsi(messageDevice);
875 }
876 }
877 if (!sourceDisabled) {
878 ERROR("PCI: could not disable message source for IRQ " << Dec << id);
879 LockGuard<Spinlock> guard(m_PciLock);
880 PciLine& line = m_PciLines[slot];
881 line.removing = false;
882 line.quarantined = true;
883 return false;
884 }
885 if (messageLpi && !m_Its.unmapDevice(slot)) {
886 ERROR("PCI: could not unmap ITS LPI " << Dec << id);
887 LockGuard<Spinlock> guard(m_PciLock);
888 PciLine& line = m_PciLines[slot];
889 line.removing = false;
890 line.quarantined = true;
891 return false;
892 }
893 const auto removed = m_PciHandlers.unregisterHandler(registryKey, handler);
894 size_t retiredCookie = 0;
895 {
896 LockGuard<Spinlock> guard(m_PciLock);
897 PciLine& line = m_PciLines[slot];
898 if (removed != IrqHandlerRegistry::UnregisterResult::Completed) {
899 ERROR("PCI: could not retire IRQ handler " << Dec << id);
900 if (removed != IrqHandlerRegistry::UnregisterResult::NotFound) {
901 line.quarantined = true;
902 }
903 line.removing = false;
904 if (line.handlers && !line.message && !line.inFlight && !line.quarantined) {
905 setEnabled(id, true);
906 }
907 return false;
908 }
909 --line.handlers;
910 if (!line.handlers) {
911 retiredCookie = line.cookie;
912 ++line.cookie;
913 if (!line.cookie) {
914 ++line.cookie;
915 }
916 line.inFlight = false;
917 line.quarantined = false;
918 if (line.message) {
919 line.irq = 0;
920 line.message = false;
921 line.msix = false;
922 line.lpi = false;
923 line.device = nullptr;
924 }
925 }
926 }
927 if (retiredCookie) {
928 m_PciHandlers.invalidateThreadedLine(registryKey, retiredCookie);
929 }
930 {
931 LockGuard<Spinlock> guard(m_PciLock);
932 PciLine& line = m_PciLines[slot];
933 line.removing = false;
934 if (line.handlers && !line.inFlight && !line.quarantined) {
935 setEnabled(id, true);
936 }
937 }
938 return true;
939 }
940 if (id >= MaxIrqs) {
941 return false;
942 }
943 const bool interrupts = Processor::getInterrupts();
945 if (static_cast<IrqHandlerBase*>(m_Hard[id]) != handler) {
946 Processor::setInterrupts(interrupts);
947 return false;
948 }
949 setEnabled(id, false);
950 m_Hard[id] = nullptr;
951 Processor::setInterrupts(interrupts);
952 return true;
953}
954
955void VirtIrqManager::dispatchPciLine(void* context, uint8_t slot, size_t cookie) {
956 auto& manager = *static_cast<VirtIrqManager*>(context);
957 uint32_t irq = 0;
958 uint8_t registryKey = 0;
959 {
960 LockGuard<Spinlock> guard(manager.m_PciLock);
961 if (slot >= MaxPciLines || !manager.m_PciLines[slot].irq ||
962 manager.m_PciLines[slot].cookie != cookie || manager.m_PciLines[slot].hard) {
963 return;
964 }
965 irq = manager.m_PciLines[slot].irq;
966 registryKey = manager.pciRegistryLine(slot);
967 }
968
970 const bool admitted =
971 manager.m_PciHandlers.dispatchThreaded(registryKey, cookie, result, nullptr, irq);
972 {
973 LockGuard<Spinlock> guard(manager.m_PciLock);
974 PciLine& line = manager.m_PciLines[slot];
975 if (line.irq != irq || line.cookie != cookie) {
976 return;
977 }
978 line.inFlight = false;
979 if (!admitted || (!result.allowRearm && !(line.message && line.spuriousSafe))) {
980 line.quarantined = true;
981 }
982 if (line.handlers && !line.removing && !line.quarantined) {
983 if (!manager.setEnabled(irq, true)) {
984 line.quarantined = true;
985 }
986 }
987 }
988}
989
990uint32_t VirtIrqManager::acknowledge() {
991#if ARM64
992 if (m_Version == 3) {
993 uint64_t value;
994 asm volatile("mrs %0, ICC_IAR1_EL1" : "=r"(value));
995 asm volatile("isb" : : : "memory");
996 return static_cast<uint32_t>(value);
997 }
998#endif
999 return read32(VirtDeviceTree::gicCpuBase(), CpuAcknowledge);
1000}
1001
1002void VirtIrqManager::complete(uint32_t value) {
1003#if ARM64
1004 if (m_Version == 3) {
1005 asm volatile("msr ICC_EOIR1_EL1, %0\n\tisb" : : "r"(uint64_t(value)) : "memory");
1006 } else {
1007#endif
1008 write32(VirtDeviceTree::gicCpuBase(), CpuEndOfInterrupt, value);
1009#if ARM64
1010 }
1011#endif
1012}
1013
1014void VirtIrqManager::handle(InterruptState& state) {
1015 if (!m_Initialised) {
1016 return;
1017 }
1018 const uint32_t acknowledgeValue = acknowledge();
1019 const uint32_t irq = acknowledgeValue & (m_Version == 3 ? 0xffffff : 0x3ff);
1020 if (irq >= 1020 && irq <= 1023) {
1021 return;
1022 }
1023 if (irq >= MaxIrqs &&
1024 (!m_Its.ready() || irq < GicIts::FirstLpi || irq >= GicIts::FirstLpi + MaxPciLines)) {
1025 complete(acknowledgeValue);
1026 return;
1027 }
1028
1029 SchedulerIrqHandler* scheduler = irq < MaxIrqs ? m_Scheduler[irq] : nullptr;
1030 if (scheduler) {
1031 if (irq == VirtDeviceTree::virtualTimerIrq()) {
1032#if ARMV7
1033 uint32_t disabled = 0;
1034 asm volatile("mcr p15, 0, %0, c14, c3, 1\n\tisb" : : "r"(disabled) : "memory");
1035#else
1036 asm volatile("msr cntv_ctl_el0, %0\n\tisb" : : "r"(uint64_t(0)) : "memory");
1037#endif
1038 }
1039 // A scheduler callback may abandon this interrupt frame permanently.
1040 complete(acknowledgeValue);
1041 scheduler->schedulerIrq(irq, state);
1042 return;
1043 }
1044
1045 size_t pciSlot = MaxPciLines;
1046 size_t pciCookie = 0;
1047 bool pciHard = false;
1048 bool pciMessage = false;
1049 uint8_t registryKey = 0;
1050 {
1051 LockGuard<Spinlock> guard(m_PciLock);
1052 pciSlot = pciLine(irq);
1053 if (pciSlot != MaxPciLines) {
1054 PciLine& line = m_PciLines[pciSlot];
1055 if (!setEnabled(irq, false)) {
1056 line.quarantined = true;
1057 complete(acknowledgeValue);
1058 return;
1059 }
1060 if (!line.handlers || line.reserved || line.inFlight || line.removing || line.quarantined) {
1061 complete(acknowledgeValue);
1062 return;
1063 }
1064 line.inFlight = true;
1065 ++line.cookie;
1066 if (!line.cookie) {
1067 ++line.cookie;
1068 }
1069 pciCookie = line.cookie;
1070 pciHard = line.hard;
1071 pciMessage = line.message;
1072 registryKey = pciRegistryLine(pciSlot);
1073 if (pciMessage) {
1074 complete(acknowledgeValue);
1075 }
1076 if (!pciHard) {
1077 const bool published =
1078 m_PciHandlers.publishThreadedDispatch(registryKey, pciCookie) &&
1079 m_PciDispatcher.publishFromInterrupt(static_cast<uint8_t>(pciSlot), pciCookie);
1080 if (!published) {
1081 m_PciHandlers.invalidateThreadedGenerationFromInterrupt(registryKey, pciCookie);
1082 line.inFlight = false;
1083 line.quarantined = true;
1084 }
1085 if (!pciMessage) {
1086 complete(acknowledgeValue);
1087 }
1088 return;
1089 }
1090 }
1091 }
1092
1093 if (pciSlot != MaxPciLines && pciHard) {
1094 HardIrqDisposition disposition = HardIrqDisposition::NotHandled;
1095 const bool admitted =
1096 m_PciHandlers.dispatchHard(registryKey, state, disposition, nullptr, 0, irq);
1097 LockGuard<Spinlock> guard(m_PciLock);
1098 PciLine& line = m_PciLines[pciSlot];
1099 if (!pciMessage) {
1100 complete(acknowledgeValue);
1101 }
1102 if (line.cookie == pciCookie) {
1103 line.inFlight = false;
1104 if (!admitted || disposition != HardIrqDisposition::Handled) {
1105 line.quarantined = true;
1106 }
1107 if (line.handlers && !line.removing && !line.quarantined) {
1108 if (!setEnabled(irq, true)) {
1109 line.quarantined = true;
1110 }
1111 }
1112 }
1113 return;
1114 }
1115
1116 if (irq >= MaxIrqs) {
1117 complete(acknowledgeValue);
1118 return;
1119 }
1120
1121 HardIrqHandler* hard = m_Hard[irq];
1122 if (hard) {
1123 size_t previousDepth = 0;
1124 bool restoreDepth = false;
1125 DeviceHardIrqContext context(previousDepth, restoreDepth);
1126 if (hard->irq(irq, state) == HardIrqDisposition::KeepMasked) {
1127 setEnabled(irq, false);
1128 }
1129 } else {
1130 setEnabled(irq, false);
1131 }
1132 complete(acknowledgeValue);
1133}
1134
1135extern "C" void virtHandleIrq(InterruptState& state) {
1136 VirtIrqManager::instance().handle(state);
1137}
virtual HardIrqDisposition irq(irq_id_t number, InterruptState &state)=0
void invalidateThreadedGenerationFromInterrupt(uint8_t irq, size_t throughGeneration)
bool publishThreadedDispatch(uint8_t irq, size_t dispatchGeneration)
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 registerHardHandler(uint8_t irq, HardIrqHandler *handler)
virtual bool acceptsSpuriousInterrupts() const
Definition IrqHandler.h:83
Definition Pci.h:31
static bool getInterrupts()
static ExecutionContext executionContext()
Definition Processor.cc:109
static void setInterrupts(bool bEnable)
bool initialise(bool lazy=false)
bool publishFromInterrupt(uint8_t line, size_t cookie)
irq_id_t registerHardPciIrqHandler(HardIrqHandler *handler, Device *device, const IrqPolicy &policy) override
irq_id_t registerIsaIrqHandler(uint8_t irq, IrqHandler *handler, const IrqPolicy &policy) override
irq_id_t registerPciMessageIrqHandler(IrqHandler *handler, Device *device, const IrqPolicy &intxFallbackPolicy) override
bool unregisterHandler(irq_id_t id, IrqHandlerBase *handler) override
bool registerPciMsixIrqHandlers(Device *device, IrqHandler *const *handlers, size_t count, irq_id_t *ids, bool &fallbackSafe, const size_t *processors=nullptr) override
bool unregisterSchedulerIrqHandler(irq_id_t id, SchedulerIrqHandler *handler) override
irq_id_t registerHardPciMessageIrqHandler(HardIrqHandler *handler, Device *device, const IrqPolicy &intxFallbackPolicy) override
irq_id_t registerHardIsaIrqHandler(uint8_t irq, HardIrqHandler *handler, const IrqPolicy &policy) override
irq_id_t registerSchedulerIrqHandler(uint8_t irq, SchedulerIrqHandler *handler, const IrqPolicy &policy) override
irq_id_t registerPciIrqHandler(IrqHandler *handler, Device *device, const IrqPolicy &policy) override
@ Dec
Definition Log.h:126
HardIrqDisposition
Definition IrqHandler.h:44