feat: make kernel architecture-agnostic; primitive work on arm port
This commit is contained in:
1 parent
dab7d5427b
commit
1bae77ec84
164 files changed
+3664
-1641
No files matched your search
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Interrupts.cpp
|
||||
* Hardware interrupt registration and dispatch
|
||||
* Copyright (c) 2025 Daniel Hammer
|
||||
*/
|
||||
|
||||
#include "Interrupts.hpp"
|
||||
#include "Apic.hpp"
|
||||
#include "IoApic.hpp"
|
||||
#include <Arch/x86_64/IDT.hpp>
|
||||
#include <Arch/Smp.hpp>
|
||||
#include <Terminal/Terminal.hpp>
|
||||
#include <CppLib/Stream.hpp>
|
||||
|
||||
// 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 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);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user