5#if MULTIPROCESSOR && ACPI
7#include "pedigree/kernel/LockGuard.h"
8#include "pedigree/kernel/Log.h"
9#include "pedigree/kernel/machine/Device.h"
10#include "pedigree/kernel/machine/IrqHandler.h"
11#include "pedigree/kernel/machine/Pci.h"
12#include "pedigree/kernel/processor/InterruptManager.h"
17#include "PciIoApicInterrupts.h"
20PciIoApicInterrupts::PciIoApicInterrupts()
23 m_Dispatcher(MakeConstantString(
"PCI INTx"), VectorCount, dispatchThreaded, this),
27 m_DestinationApicId(0),
29 m_ShuttingDown(false) {}
31bool PciIoApicInterrupts::initialise() {
32 const auto& controllers = Acpi::instance().getIoApicList();
33 for (
size_t i = 0; i < controllers.size() && m_ControllerCount < 4; ++i) {
34 const auto* info = controllers[i];
35 if (info && m_Controllers[m_ControllerCount].initialise(info->address, info->gsiBase)) {
39 if (!m_ControllerCount) {
44 for (; installed < VectorCount; ++installed) {
46 for (
size_t i = 0; i < installed; ++i) {
52 m_DestinationApicId = Pc::instance().getLocalApic().getId();
57bool PciIoApicInterrupts::initialiseThreaded() {
61bool PciIoApicInterrupts::containsGsi(uint32_t gsi)
const {
62 for (
size_t i = 0; m_Ready && i < m_ControllerCount; ++i) {
63 if (m_Controllers[i].contains(gsi)) {
70bool PciIoApicInterrupts::reserveGsi(uint32_t gsi,
bool activeLow) {
72 if (!m_Ready || m_ShuttingDown) {
75 size_t slot = VectorCount;
76 for (
size_t i = 0; i < VectorCount; ++i) {
77 const Line& line = m_Lines[i];
78 if (line.used && line.gsi == gsi) {
79 return line.activeLow == activeLow && !line.quarantined && !line.removing;
81 if (!line.used && slot == VectorCount) {
85 if (slot == VectorCount) {
88 for (
size_t i = 0; i < m_ControllerCount; ++i) {
89 if (m_Controllers[i].contains(gsi)) {
90 Line& line = m_Lines[slot];
91 line.controller = &m_Controllers[i];
93 line.activeLow = activeLow;
96 !line.controller->route(gsi, FirstVector + slot, m_DestinationApicId, activeLow);
97 return !line.quarantined;
103bool PciIoApicInterrupts::shutdownThreaded() {
110 m_ShuttingDown =
true;
111 for (Line& line : m_Lines) {
112 if (line.used && line.controller && !line.controller->mask(line.gsi,
true)) {
115 line.enabled =
false;
118 return masked && (!m_Dispatcher.isInitialised() || m_Dispatcher.shutdown());
121bool PciIoApicInterrupts::findRoute(
Device* device, uint32_t& gsi,
bool& activeLow) {
126 if (!PciBus::instance().readConfig8(device, 0x3d, pin) || pin < 1 || pin > 4) {
130 if (!Acpi::instance().pciInterruptRoute(device, pin, route)) {
134 activeLow = route.activeLow;
138irq_id_t PciIoApicInterrupts::registerThreaded(
Device* device,
IrqHandler* handler,
140 return registerHandler(device, handler, policy, Mode::Threaded, routed);
145 return registerHandler(device, handler, policy, Mode::Hard, routed);
149 const IrqPolicy& policy, Mode mode,
bool& routed) {
151 if (!m_Ready || !device || !handler || policy.trigger() != IrqTrigger::Level ||
152 (mode == Mode::Threaded ? (!policy.validForThreaded() || !m_Dispatcher.isInitialised())
153 : !policy.validForHard())) {
157 routed = Acpi::instance().hasPciInterruptRouter();
159 bool activeLow =
false;
160 if (!findRoute(device, gsi, activeLow)) {
163 IoApic* controller =
nullptr;
164 for (
size_t i = 0; i < m_ControllerCount; ++i) {
165 if (m_Controllers[i].contains(gsi)) {
166 controller = &m_Controllers[i];
179 if (m_ShuttingDown) {
182 size_t slot = VectorCount;
183 for (
size_t i = 0; i < VectorCount; ++i) {
184 if (m_Lines[i].used && m_Lines[i].gsi == gsi) {
189 if (slot == VectorCount) {
192 Line& line = m_Lines[slot];
193 if (line.removing || line.quarantined || line.controller != controller ||
194 line.activeLow != activeLow || (line.handlers && line.mode != mode)) {
197 const uint8_t vector = FirstVector + slot;
198 const bool registered =
199 mode == Mode::Threaded
200 ? m_Handlers.registerThreadedHandler(vector,
static_cast<IrqHandler*
>(handler), policy)
201 : m_Handlers.registerHardHandler(vector,
static_cast<HardIrqHandler*
>(handler), policy);
205 line.controller = controller;
207 line.activeLow = activeLow;
212 if (!line.inFlight && !controller->mask(gsi,
false)) {
213 line.quarantined =
true;
214 ERROR(
"PCI INTx: could not enable IOAPIC GSI " <<
Dec << gsi);
215 const auto result = m_Handlers.unregisterHandler(vector, handler);
216 if (result == IrqHandlerRegistry::UnregisterResult::Completed) {
218 if (!line.handlers) {
219 line.mode = Mode::None;
220 line.enabled =
false;
225 NOTICE(
"PCI INTx: IOAPIC GSI " <<
Dec << gsi <<
" -> vector " <<
Hex << vector);
229bool PciIoApicInterrupts::unregisterHandler(irq_id_t
id,
IrqHandlerBase* handler) {
230 if (!contains(
id) || !handler) {
233 const uint8_t slot =
id - FirstVector;
238 Line& line = m_Lines[slot];
239 if (!line.handlers || line.removing || !line.controller ||
240 !m_Handlers.containsHandler(
id, handler) || !line.controller->mask(line.gsi,
true)) {
243 line.removing =
true;
244 last = line.handlers == 1;
246 cookie = ++line.cookie;
250 m_Handlers.invalidateThreadedLine(
id, cookie);
252 const auto result = m_Handlers.unregisterHandler(
id, handler);
255 Line& line = m_Lines[slot];
257 line.inFlight =
false;
259 if ((result == IrqHandlerRegistry::UnregisterResult::Completed ||
260 result == IrqHandlerRegistry::UnregisterResult::Deferred) &&
263 if (!line.handlers) {
264 line.mode = Mode::None;
265 line.enabled =
false;
269 }
else if (result != IrqHandlerRegistry::UnregisterResult::Completed) {
270 line.quarantined =
true;
272 line.removing =
false;
273 if (line.handlers && line.enabled && !line.inFlight && !line.quarantined &&
274 !line.controller->mask(line.gsi,
false)) {
275 line.quarantined =
true;
278 return result == IrqHandlerRegistry::UnregisterResult::Completed;
281void PciIoApicInterrupts::enable(irq_id_t
id,
bool enabled) {
286 Line& line = m_Lines[
id - FirstVector];
287 if (!line.handlers || line.removing || line.quarantined || m_ShuttingDown) {
290 line.enabled = enabled;
291 if (!line.controller->mask(line.gsi, !enabled || line.inFlight)) {
292 line.quarantined =
true;
296void PciIoApicInterrupts::interrupt(
size_t vector, InterruptState& state) {
297 if (!contains(vector)) {
300 const uint8_t slot = vector - FirstVector;
301 Mode mode = Mode::None;
303 bool published =
false;
307 Line& line = m_Lines[slot];
308 if (line.controller) {
309 if (!line.controller->mask(line.gsi,
true)) {
310 line.quarantined =
true;
312 if (line.handlers && line.enabled && !line.removing && !line.inFlight && !line.quarantined &&
315 line.inFlight =
true;
316 cookie = ++line.cookie;
318 cookie = ++line.cookie;
320 if (m_Handlers.captureAdmissionCutoff(vector, cutoff)) {
321 if (mode == Mode::Threaded) {
322 published = m_Handlers.publishThreadedDispatch(vector, cookie, cutoff);
323 if (published && !m_Dispatcher.publishFromInterrupt(slot, cookie)) {
324 m_Handlers.invalidateThreadedGenerationFromInterrupt(vector, cookie);
332 line.quarantined =
true;
337 if (mode != Mode::Hard || !published) {
338 Pc::instance().getLocalApic().ack();
342 const bool admitted =
343 m_Handlers.dispatchHard(vector, state, disposition,
nullptr, cookie, cutoff);
344 Pc::instance().getLocalApic().ack();
346 Line& line = m_Lines[slot];
347 if (line.cookie != cookie) {
350 line.inFlight =
false;
351 if (!admitted || disposition != HardIrqDisposition::Handled) {
352 line.quarantined =
true;
354 if (line.enabled && !line.removing && !line.quarantined &&
355 !line.controller->mask(line.gsi,
false)) {
356 line.quarantined =
true;
360void PciIoApicInterrupts::dispatchThreaded(
void* context, uint8_t slot,
size_t cookie) {
361 auto& self = *
static_cast<PciIoApicInterrupts*
>(context);
362 if (slot >= VectorCount) {
365 const uint8_t vector = FirstVector + slot;
367 const bool admitted = self.m_Handlers.dispatchThreaded(vector, cookie, result);
369 Line& line = self.m_Lines[slot];
370 if (line.cookie != cookie) {
373 line.inFlight =
false;
374 if (!admitted || !result.allowRearm) {
375 line.quarantined =
true;
377 if (line.enabled && !line.removing && !line.quarantined &&
378 !line.controller->mask(line.gsi,
false)) {
379 line.quarantined =
true;
Handles interrupts and interrupt registrations from kernel components.
static EXPORTED_PUBLIC InterruptManager & instance()
virtual bool registerInterruptHandler(size_t nInterruptNumber, InterruptHandler *pHandler)=0
virtual void initialise() INITIALISATION_ONLY