/* * Interrupts.cpp * Hardware interrupt registration and dispatch * Copyright (c) 2025 Daniel Hammer */ #include "Interrupts.hpp" #include "Apic.hpp" #include "IoApic.hpp" #include #include #include #include // Assembly-defined stub table and spurious handler extern "C" void* IrqStubTable[Hal::IRQ_COUNT]; extern "C" void IrqStubSpurious(); // Local APIC message address range; destination APIC ID in bits 19:12. static constexpr uint64_t MsiAddressBase = 0xFEE00000; namespace Hal { // Dispatch table: one handler per IRQ static Arch::IrqHandler g_irqHandlers[IRQ_COUNT] = {}; void InitializeIrqHandlers() { // Install IRQ stubs into IDT vectors 32..79 for (int i = 0; i < IRQ_COUNT; i++) { IDTEncodeInterrupt(IRQ_VECTOR_BASE + i, IrqStubTable[i], 0x8E); } // Install spurious interrupt handler at vector 0xFF IDTEncodeInterrupt(0xFF, (void*)IrqStubSpurious, 0x8E); Kt::KernelLogStream(Kt::OK, "IRQ") << "Installed " << base::dec << (uint64_t)IRQ_COUNT << " IRQ stubs (vectors " << (uint64_t)IRQ_VECTOR_BASE << "-" << (uint64_t)(IRQ_VECTOR_BASE + IRQ_COUNT - 1) << ")"; } }; namespace Arch { void RegisterIrqHandler(uint8_t irq, IrqHandler handler) { if (irq >= IRQ_COUNT) return; Hal::g_irqHandlers[irq] = handler; Kt::KernelLogStream(Kt::DEBUG, "IRQ") << "Registered handler for IRQ " << base::dec << (uint64_t)irq << " (vector " << (uint64_t)(Hal::IRQ_VECTOR_BASE + irq) << ")"; } void RouteLegacyIrq(uint8_t irq) { // RouteIrq applies the MADT's Interrupt Source Overrides (polarity, // trigger mode, GSI remapping). Hal::IoApic::RouteIrq(irq, Hal::IRQ_VECTOR_BASE + irq, (uint8_t)Hal::LocalApic::GetId()); } void EnableLegacyIrq(uint8_t irq) { Hal::IoApic::UnmaskIrq(Hal::IoApic::GetGsiForIrq(irq)); } bool PlatformLinesAreFixed() { return true; } bool AssignPlatformLine(uint8_t, uint32_t, bool, bool) { return false; } bool ComposeMsi(uint8_t irq, MsiMessage& out) { // Physical destination mode, fixed delivery, edge triggered, to the // BSP (APIC ID 0 -- the destination field is left zero, as every // driver has always programmed it). out = MsiMessage{ MsiAddressBase, (uint32_t)(Hal::IRQ_VECTOR_BASE + irq) }; return true; } bool SendIpi(int cpuIndex, uint8_t irq) { auto* cpu = Smp::GetCpuData(cpuIndex); if (cpu == nullptr) return false; return Hal::LocalApic::SendFixedIpi(cpu->arch.lapicId, Hal::IRQ_VECTOR_BASE + irq); } }; // C linkage dispatch function called from assembly stubs extern "C" void HalIrqDispatch(uint64_t irqNumber, uint64_t savedCs) { // Send EOI BEFORE calling the handler. The handler may context-switch // (via Tick -> Schedule -> SchedContextSwitch) and never return here. // If EOI is deferred until after the handler, the LAPIC timer vector // stays masked on this CPU -- no more timer interrupts, no more // scheduling, and the system eventually locks up. // This is safe because the ISR runs with IF=0; the LAPIC won't // deliver a new interrupt until iretq re-enables them. Hal::LocalApic::SendEOI(); if (irqNumber < Hal::IRQ_COUNT && Hal::g_irqHandlers[irqNumber] != nullptr) { Hal::g_irqHandlers[irqNumber]((uint8_t)irqNumber, (savedCs & 3) == 3); } }