feat: make kernel architecture-agnostic; primitive work on arm port

This commit is contained in:
daniel committed 2026-10-24 21:25:18 +02:00
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);
}
}