feat: Local/IO APIC, PS/2 keyboard & mouse
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Apic.cpp
|
||||
* Local APIC (Advanced Programmable Interrupt Controller)
|
||||
* Copyright (c) 2025 Daniel Hammer
|
||||
*/
|
||||
|
||||
#include "Apic.hpp"
|
||||
#include <Terminal/Terminal.hpp>
|
||||
#include <CppLib/Stream.hpp>
|
||||
#include <Memory/HHDM.hpp>
|
||||
|
||||
using namespace Kt;
|
||||
|
||||
namespace Hal {
|
||||
namespace LocalApic {
|
||||
static volatile uint32_t* g_apicBase = nullptr;
|
||||
|
||||
static inline uint64_t ReadMSR(uint32_t msr) {
|
||||
uint32_t lo, hi;
|
||||
asm volatile("rdmsr" : "=a"(lo), "=d"(hi) : "c"(msr));
|
||||
return ((uint64_t)hi << 32) | lo;
|
||||
}
|
||||
|
||||
static inline void WriteMSR(uint32_t msr, uint64_t value) {
|
||||
uint32_t lo = (uint32_t)(value & 0xFFFFFFFF);
|
||||
uint32_t hi = (uint32_t)(value >> 32);
|
||||
asm volatile("wrmsr" : : "a"(lo), "d"(hi), "c"(msr));
|
||||
}
|
||||
|
||||
uint32_t ReadRegister(uint32_t reg) {
|
||||
return g_apicBase[reg / 4];
|
||||
}
|
||||
|
||||
void WriteRegister(uint32_t reg, uint32_t value) {
|
||||
g_apicBase[reg / 4] = value;
|
||||
}
|
||||
|
||||
void Initialize(uint64_t apicBasePhys) {
|
||||
// Read the APIC base MSR to verify/confirm the base address
|
||||
uint64_t msrValue = ReadMSR(MSR_APIC_BASE);
|
||||
uint64_t msrBase = msrValue & 0xFFFFF000;
|
||||
|
||||
KernelLogStream(DEBUG, "APIC") << "MSR APIC base: " << base::hex << msrBase;
|
||||
KernelLogStream(DEBUG, "APIC") << "MADT APIC base: " << base::hex << apicBasePhys;
|
||||
|
||||
// Use the MADT-provided address (it should match MSR)
|
||||
g_apicBase = (volatile uint32_t*)Memory::HHDM(apicBasePhys);
|
||||
|
||||
// Enable the APIC by setting bit 8 (APIC Software Enable) in SVR
|
||||
// and set the spurious interrupt vector
|
||||
uint32_t svr = ReadRegister(REG_SPURIOUS);
|
||||
svr |= (1 << 8); // Enable APIC
|
||||
svr = (svr & 0xFFFFFF00) | SPURIOUS_VECTOR; // Set spurious vector
|
||||
WriteRegister(REG_SPURIOUS, svr);
|
||||
|
||||
// Set Task Priority to 0 to accept all interrupts
|
||||
WriteRegister(REG_TPR, 0);
|
||||
|
||||
uint32_t version = ReadRegister(REG_VERSION);
|
||||
uint32_t id = GetId();
|
||||
|
||||
KernelLogStream(OK, "APIC") << "Local APIC initialized: id=" << base::dec << (uint64_t)id
|
||||
<< " version=" << base::hex << (uint64_t)(version & 0xFF)
|
||||
<< " max LVT=" << base::dec << (uint64_t)((version >> 16) & 0xFF);
|
||||
}
|
||||
|
||||
void SendEOI() {
|
||||
WriteRegister(REG_EOI, 0);
|
||||
}
|
||||
|
||||
uint32_t GetId() {
|
||||
return (ReadRegister(REG_ID) >> 24) & 0xFF;
|
||||
}
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Apic.hpp
|
||||
* Local APIC (Advanced Programmable Interrupt Controller)
|
||||
* Copyright (c) 2025 Daniel Hammer
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
|
||||
namespace Hal {
|
||||
namespace LocalApic {
|
||||
// Local APIC register offsets
|
||||
constexpr uint32_t REG_ID = 0x020;
|
||||
constexpr uint32_t REG_VERSION = 0x030;
|
||||
constexpr uint32_t REG_TPR = 0x080;
|
||||
constexpr uint32_t REG_EOI = 0x0B0;
|
||||
constexpr uint32_t REG_SPURIOUS = 0x0F0;
|
||||
constexpr uint32_t REG_ICR_LOW = 0x300;
|
||||
constexpr uint32_t REG_ICR_HIGH = 0x310;
|
||||
constexpr uint32_t REG_TIMER_LVT = 0x320;
|
||||
constexpr uint32_t REG_LINT0_LVT = 0x350;
|
||||
constexpr uint32_t REG_LINT1_LVT = 0x360;
|
||||
constexpr uint32_t REG_ERROR_LVT = 0x370;
|
||||
constexpr uint32_t REG_TIMER_INITIAL = 0x380;
|
||||
constexpr uint32_t REG_TIMER_CURRENT = 0x390;
|
||||
constexpr uint32_t REG_TIMER_DIVIDE = 0x3E0;
|
||||
|
||||
// Spurious vector number
|
||||
constexpr uint8_t SPURIOUS_VECTOR = 0xFF;
|
||||
|
||||
// MSR for APIC base
|
||||
constexpr uint32_t MSR_APIC_BASE = 0x1B;
|
||||
|
||||
void Initialize(uint64_t apicBasePhys);
|
||||
void SendEOI();
|
||||
uint32_t GetId();
|
||||
|
||||
uint32_t ReadRegister(uint32_t reg);
|
||||
void WriteRegister(uint32_t reg, uint32_t value);
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* ApicInit.cpp
|
||||
* APIC subsystem initialization
|
||||
* Copyright (c) 2025 Daniel Hammer
|
||||
*/
|
||||
|
||||
#include "ApicInit.hpp"
|
||||
#include "Pic.hpp"
|
||||
#include "Apic.hpp"
|
||||
#include "IoApic.hpp"
|
||||
#include "Interrupts.hpp"
|
||||
#include <ACPI/MADT.hpp>
|
||||
#include <Hal/IDT.hpp>
|
||||
#include <Terminal/Terminal.hpp>
|
||||
#include <CppLib/Stream.hpp>
|
||||
#include <Memory/Paging.hpp>
|
||||
#include <Memory/HHDM.hpp>
|
||||
|
||||
using namespace Kt;
|
||||
|
||||
namespace Hal {
|
||||
void ApicInitialize(ACPI::CommonSDTHeader* xsdt) {
|
||||
KernelLogStream(INFO, "APIC") << "Initializing APIC subsystem";
|
||||
|
||||
// Step 1: Parse MADT
|
||||
MADT::ParsedMADT madt{};
|
||||
if (!MADT::Parse(xsdt, madt)) {
|
||||
KernelLogStream(ERROR, "APIC") << "Failed to parse MADT, cannot initialize APIC";
|
||||
return;
|
||||
}
|
||||
|
||||
if (madt.IoApicAddress == 0) {
|
||||
KernelLogStream(ERROR, "APIC") << "No IOAPIC found in MADT";
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 2: Map APIC MMIO regions into kernel page tables
|
||||
// The HHDM only covers physical RAM; MMIO regions need explicit mapping.
|
||||
if (Memory::VMM::g_paging) {
|
||||
Memory::VMM::g_paging->MapMMIO(madt.LocalApicAddress, Memory::HHDM(madt.LocalApicAddress));
|
||||
KernelLogStream(DEBUG, "APIC") << "Mapped Local APIC MMIO at phys " << base::hex << madt.LocalApicAddress;
|
||||
|
||||
Memory::VMM::g_paging->MapMMIO(madt.IoApicAddress, Memory::HHDM(madt.IoApicAddress));
|
||||
KernelLogStream(DEBUG, "APIC") << "Mapped IOAPIC MMIO at phys " << base::hex << madt.IoApicAddress;
|
||||
}
|
||||
|
||||
// Step 3: Disable legacy 8259 PIC
|
||||
DisableLegacyPic();
|
||||
|
||||
// Step 4: Install IRQ stubs into IDT
|
||||
InitializeIrqHandlers();
|
||||
IDTReload();
|
||||
|
||||
// Step 5: Initialize Local APIC
|
||||
LocalApic::Initialize(madt.LocalApicAddress);
|
||||
|
||||
// Step 6: Initialize IOAPIC
|
||||
IoApic::Initialize(madt.IoApicAddress, madt.IoApicGsiBase,
|
||||
madt.Overrides, madt.OverrideCount);
|
||||
|
||||
// Step 7: Route keyboard (IRQ1) and mouse (IRQ12) to BSP
|
||||
uint8_t bspApicId = (uint8_t)LocalApic::GetId();
|
||||
|
||||
IoApic::RouteIrq(IRQ_KEYBOARD, IRQ_VECTOR_BASE + IRQ_KEYBOARD, bspApicId);
|
||||
IoApic::RouteIrq(IRQ_MOUSE, IRQ_VECTOR_BASE + IRQ_MOUSE, bspApicId);
|
||||
|
||||
// Step 8: Enable interrupts
|
||||
asm volatile("sti");
|
||||
|
||||
KernelLogStream(OK, "APIC") << "APIC subsystem initialized, interrupts enabled";
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* ApicInit.hpp
|
||||
* APIC subsystem initialization
|
||||
* Copyright (c) 2025 Daniel Hammer
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <ACPI/ACPI.hpp>
|
||||
|
||||
namespace Hal {
|
||||
// Initialize the full APIC subsystem:
|
||||
// 1. Parse MADT from XSDT
|
||||
// 2. Disable legacy PIC
|
||||
// 3. Install IRQ stubs in IDT
|
||||
// 4. Initialize Local APIC
|
||||
// 5. Initialize IOAPIC
|
||||
// 6. Route keyboard (IRQ1) and mouse (IRQ12)
|
||||
// 7. Enable interrupts
|
||||
//
|
||||
// xsdt: pointer to the XSDT (already HHDM-mapped)
|
||||
void ApicInitialize(ACPI::CommonSDTHeader* xsdt);
|
||||
};
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Interrupts.cpp
|
||||
* Hardware interrupt registration and dispatch
|
||||
* Copyright (c) 2025 Daniel Hammer
|
||||
*/
|
||||
|
||||
#include "Interrupts.hpp"
|
||||
#include "Apic.hpp"
|
||||
#include <Hal/IDT.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();
|
||||
|
||||
namespace Hal {
|
||||
// Dispatch table: one handler per IRQ
|
||||
static IrqHandler g_irqHandlers[IRQ_COUNT] = {};
|
||||
|
||||
void RegisterIrqHandler(uint8_t irq, IrqHandler handler) {
|
||||
if (irq >= IRQ_COUNT) return;
|
||||
g_irqHandlers[irq] = handler;
|
||||
Kt::KernelLogStream(Kt::DEBUG, "IRQ") << "Registered handler for IRQ " << base::dec << (uint64_t)irq
|
||||
<< " (vector " << (uint64_t)(IRQ_VECTOR_BASE + irq) << ")";
|
||||
}
|
||||
|
||||
void InitializeIrqHandlers() {
|
||||
// Install IRQ stubs into IDT vectors 32..55
|
||||
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) << ")";
|
||||
}
|
||||
};
|
||||
|
||||
// C linkage dispatch function called from assembly stubs
|
||||
extern "C" void HalIrqDispatch(uint64_t irqNumber) {
|
||||
if (irqNumber < Hal::IRQ_COUNT && Hal::g_irqHandlers[irqNumber] != nullptr) {
|
||||
Hal::g_irqHandlers[irqNumber]((uint8_t)irqNumber);
|
||||
}
|
||||
|
||||
// Send End of Interrupt to the Local APIC
|
||||
Hal::LocalApic::SendEOI();
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Interrupts.hpp
|
||||
* Hardware interrupt registration and dispatch
|
||||
* Copyright (c) 2025 Daniel Hammer
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
|
||||
namespace Hal {
|
||||
// IRQ handler function type. The parameter is the IRQ number (0-23).
|
||||
using IrqHandler = void(*)(uint8_t irq);
|
||||
|
||||
// Number of IRQ lines supported (IOAPIC inputs)
|
||||
constexpr int IRQ_COUNT = 24;
|
||||
|
||||
// IRQ vector base: hardware IRQs start at IDT vector 32
|
||||
constexpr uint8_t IRQ_VECTOR_BASE = 32;
|
||||
|
||||
// Well-known ISA IRQ assignments
|
||||
constexpr uint8_t IRQ_TIMER = 0;
|
||||
constexpr uint8_t IRQ_KEYBOARD = 1;
|
||||
constexpr uint8_t IRQ_CASCADE = 2;
|
||||
constexpr uint8_t IRQ_COM2 = 3;
|
||||
constexpr uint8_t IRQ_COM1 = 4;
|
||||
constexpr uint8_t IRQ_FLOPPY = 6;
|
||||
constexpr uint8_t IRQ_RTC = 8;
|
||||
constexpr uint8_t IRQ_MOUSE = 12;
|
||||
constexpr uint8_t IRQ_ATA1 = 14;
|
||||
constexpr uint8_t IRQ_ATA2 = 15;
|
||||
|
||||
// Register a handler for the given IRQ number (0-23)
|
||||
void RegisterIrqHandler(uint8_t irq, IrqHandler handler);
|
||||
|
||||
// Install IRQ stubs into the IDT and set up the dispatch table
|
||||
void InitializeIrqHandlers();
|
||||
};
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* IoApic.cpp
|
||||
* I/O APIC (I/O Advanced Programmable Interrupt Controller)
|
||||
* Copyright (c) 2025 Daniel Hammer
|
||||
*/
|
||||
|
||||
#include "IoApic.hpp"
|
||||
#include <Terminal/Terminal.hpp>
|
||||
#include <CppLib/Stream.hpp>
|
||||
#include <Memory/HHDM.hpp>
|
||||
|
||||
using namespace Kt;
|
||||
|
||||
namespace Hal {
|
||||
namespace IoApic {
|
||||
static volatile uint32_t* g_ioApicBase = nullptr;
|
||||
static uint32_t g_gsiBase = 0;
|
||||
static uint32_t g_maxRedirEntries = 0;
|
||||
|
||||
// Stored overrides for IRQ remapping
|
||||
static MADT::InterruptSourceOverride g_overrides[MADT::ParsedMADT::MaxOverrides];
|
||||
static int g_overrideCount = 0;
|
||||
|
||||
static uint32_t ReadRegister(uint32_t reg) {
|
||||
// Write the register index to IOREGSEL (offset 0x00)
|
||||
g_ioApicBase[0] = reg;
|
||||
// Read the value from IOWIN (offset 0x10 = index 4 in uint32_t array)
|
||||
return g_ioApicBase[4];
|
||||
}
|
||||
|
||||
static void WriteRegister(uint32_t reg, uint32_t value) {
|
||||
g_ioApicBase[0] = reg;
|
||||
g_ioApicBase[4] = value;
|
||||
}
|
||||
|
||||
void SetRedirectionEntry(uint8_t index, uint64_t entry) {
|
||||
uint32_t regLow = IOREDTBL_BASE + (index * 2);
|
||||
uint32_t regHigh = IOREDTBL_BASE + (index * 2) + 1;
|
||||
|
||||
WriteRegister(regHigh, (uint32_t)(entry >> 32));
|
||||
WriteRegister(regLow, (uint32_t)(entry & 0xFFFFFFFF));
|
||||
}
|
||||
|
||||
uint64_t GetRedirectionEntry(uint8_t index) {
|
||||
uint32_t regLow = IOREDTBL_BASE + (index * 2);
|
||||
uint32_t regHigh = IOREDTBL_BASE + (index * 2) + 1;
|
||||
|
||||
uint64_t low = ReadRegister(regLow);
|
||||
uint64_t high = ReadRegister(regHigh);
|
||||
|
||||
return (high << 32) | low;
|
||||
}
|
||||
|
||||
void MaskIrq(uint8_t irq) {
|
||||
uint64_t entry = GetRedirectionEntry(irq);
|
||||
entry |= REDIR_MASKED;
|
||||
SetRedirectionEntry(irq, entry);
|
||||
}
|
||||
|
||||
void UnmaskIrq(uint8_t irq) {
|
||||
uint64_t entry = GetRedirectionEntry(irq);
|
||||
entry &= ~REDIR_MASKED;
|
||||
SetRedirectionEntry(irq, entry);
|
||||
}
|
||||
|
||||
uint32_t GetGsiForIrq(uint8_t isaIrq) {
|
||||
for (int i = 0; i < g_overrideCount; i++) {
|
||||
if (g_overrides[i].IrqSource == isaIrq) {
|
||||
return g_overrides[i].GlobalSystemInterrupt;
|
||||
}
|
||||
}
|
||||
// No override found, identity map
|
||||
return isaIrq;
|
||||
}
|
||||
|
||||
void RouteIrq(uint8_t isaIrq, uint8_t vector, uint8_t destinationApicId) {
|
||||
uint32_t gsi = GetGsiForIrq(isaIrq);
|
||||
uint32_t ioApicIndex = gsi - g_gsiBase;
|
||||
|
||||
// Build redirection entry
|
||||
uint64_t entry = 0;
|
||||
entry |= vector; // Vector
|
||||
entry |= ((uint64_t)destinationApicId << 56); // Destination
|
||||
|
||||
// Check if there's an override with specific polarity/trigger settings
|
||||
for (int i = 0; i < g_overrideCount; i++) {
|
||||
if (g_overrides[i].IrqSource == isaIrq) {
|
||||
uint16_t flags = g_overrides[i].Flags;
|
||||
// Polarity: bits 0-1 (0b11 = active low)
|
||||
if ((flags & 0x03) == 0x03) {
|
||||
entry |= REDIR_ACTIVE_LOW;
|
||||
}
|
||||
// Trigger mode: bits 2-3 (0b11 = level triggered)
|
||||
if ((flags & 0x0C) == 0x0C) {
|
||||
entry |= REDIR_LEVEL_TRIGGER;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SetRedirectionEntry(ioApicIndex, entry);
|
||||
|
||||
KernelLogStream(DEBUG, "IOAPIC") << "Routed ISA IRQ " << base::dec << (uint64_t)isaIrq
|
||||
<< " -> GSI " << (uint64_t)gsi
|
||||
<< " -> vector " << (uint64_t)vector
|
||||
<< " -> APIC " << (uint64_t)destinationApicId;
|
||||
}
|
||||
|
||||
void Initialize(uint64_t ioApicBasePhys, uint32_t gsiBase,
|
||||
MADT::InterruptSourceOverride* overrides, int overrideCount) {
|
||||
g_ioApicBase = (volatile uint32_t*)Memory::HHDM(ioApicBasePhys);
|
||||
g_gsiBase = gsiBase;
|
||||
|
||||
// Store overrides
|
||||
g_overrideCount = overrideCount;
|
||||
for (int i = 0; i < overrideCount; i++) {
|
||||
g_overrides[i] = overrides[i];
|
||||
}
|
||||
|
||||
// Read IOAPIC version and max redirection entries
|
||||
uint32_t version = ReadRegister(IOAPICVER);
|
||||
g_maxRedirEntries = ((version >> 16) & 0xFF) + 1;
|
||||
uint32_t ioapicId = (ReadRegister(IOAPICID) >> 24) & 0x0F;
|
||||
|
||||
KernelLogStream(OK, "IOAPIC") << "IOAPIC initialized: id=" << base::dec << (uint64_t)ioapicId
|
||||
<< " version=" << base::hex << (uint64_t)(version & 0xFF)
|
||||
<< " entries=" << base::dec << (uint64_t)g_maxRedirEntries;
|
||||
|
||||
// Mask all redirection entries initially
|
||||
for (uint32_t i = 0; i < g_maxRedirEntries; i++) {
|
||||
SetRedirectionEntry(i, REDIR_MASKED | (IRQ_VECTOR_BASE + i));
|
||||
}
|
||||
|
||||
KernelLogStream(OK, "IOAPIC") << "All redirection entries masked";
|
||||
}
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* IoApic.hpp
|
||||
* I/O APIC (I/O Advanced Programmable Interrupt Controller)
|
||||
* Copyright (c) 2025 Daniel Hammer
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <ACPI/MADT.hpp>
|
||||
|
||||
namespace Hal {
|
||||
namespace IoApic {
|
||||
// IOAPIC register indices (written to IOREGSEL)
|
||||
constexpr uint32_t IOAPICID = 0x00;
|
||||
constexpr uint32_t IOAPICVER = 0x01;
|
||||
constexpr uint32_t IOAPICARB = 0x02;
|
||||
// Redirection table entries start at 0x10, each entry is 2 x 32-bit registers
|
||||
constexpr uint32_t IOREDTBL_BASE = 0x10;
|
||||
|
||||
// Redirection entry flags
|
||||
constexpr uint64_t REDIR_MASKED = (1ULL << 16);
|
||||
constexpr uint64_t REDIR_LEVEL_TRIGGER = (1ULL << 15);
|
||||
constexpr uint64_t REDIR_ACTIVE_LOW = (1ULL << 13);
|
||||
constexpr uint64_t REDIR_LOGICAL_DEST = (1ULL << 11);
|
||||
|
||||
// IRQ vector base: hardware IRQs start at vector 32
|
||||
constexpr uint8_t IRQ_VECTOR_BASE = 32;
|
||||
|
||||
void Initialize(uint64_t ioApicBasePhys, uint32_t gsiBase,
|
||||
MADT::InterruptSourceOverride* overrides, int overrideCount);
|
||||
|
||||
void SetRedirectionEntry(uint8_t irq, uint64_t entry);
|
||||
uint64_t GetRedirectionEntry(uint8_t irq);
|
||||
|
||||
void MaskIrq(uint8_t irq);
|
||||
void UnmaskIrq(uint8_t irq);
|
||||
|
||||
// Route an ISA IRQ to a specific vector, applying source overrides
|
||||
void RouteIrq(uint8_t isaIrq, uint8_t vector, uint8_t destinationApicId);
|
||||
|
||||
// Get the GSI for a given ISA IRQ (applies overrides)
|
||||
uint32_t GetGsiForIrq(uint8_t isaIrq);
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,127 @@
|
||||
;
|
||||
; IsrStubs.asm
|
||||
; Hardware interrupt (IRQ) entry stubs for APIC
|
||||
; Copyright (c) 2025 Daniel Hammer
|
||||
;
|
||||
|
||||
[bits 64]
|
||||
section .text
|
||||
|
||||
; External C++ handler function
|
||||
extern HalIrqDispatch
|
||||
|
||||
; Macro to define an IRQ stub
|
||||
; Each stub pushes the IRQ number and jumps to the common handler
|
||||
%macro IRQ_STUB 1
|
||||
global IrqStub%1
|
||||
IrqStub%1:
|
||||
push rax ; Save rax (we use it for the IRQ number)
|
||||
mov rax, %1 ; IRQ number
|
||||
jmp IrqCommon
|
||||
%endmacro
|
||||
|
||||
; Common IRQ handler: saves all general-purpose registers,
|
||||
; calls the C++ dispatch function, restores registers, and returns.
|
||||
IrqCommon:
|
||||
; rax is already on the stack and holds the IRQ number
|
||||
push rcx
|
||||
push rdx
|
||||
push rbx
|
||||
push rbp
|
||||
push rsi
|
||||
push rdi
|
||||
push r8
|
||||
push r9
|
||||
push r10
|
||||
push r11
|
||||
push r12
|
||||
push r13
|
||||
push r14
|
||||
push r15
|
||||
|
||||
; Pass IRQ number as first argument (rdi)
|
||||
mov rdi, rax
|
||||
|
||||
; Align stack to 16 bytes before call (we pushed 15 registers x 8 = 120 bytes
|
||||
; + return address 8 = 128, which is 16-byte aligned, so we're good)
|
||||
cld
|
||||
call HalIrqDispatch
|
||||
|
||||
pop r15
|
||||
pop r14
|
||||
pop r13
|
||||
pop r12
|
||||
pop r11
|
||||
pop r10
|
||||
pop r9
|
||||
pop r8
|
||||
pop rdi
|
||||
pop rsi
|
||||
pop rbp
|
||||
pop rbx
|
||||
pop rdx
|
||||
pop rcx
|
||||
pop rax
|
||||
|
||||
iretq
|
||||
|
||||
; Define stubs for IRQs 0..23 (vectors 32..55)
|
||||
; This covers all standard ISA IRQs plus some extra IOAPIC inputs
|
||||
IRQ_STUB 0
|
||||
IRQ_STUB 1
|
||||
IRQ_STUB 2
|
||||
IRQ_STUB 3
|
||||
IRQ_STUB 4
|
||||
IRQ_STUB 5
|
||||
IRQ_STUB 6
|
||||
IRQ_STUB 7
|
||||
IRQ_STUB 8
|
||||
IRQ_STUB 9
|
||||
IRQ_STUB 10
|
||||
IRQ_STUB 11
|
||||
IRQ_STUB 12
|
||||
IRQ_STUB 13
|
||||
IRQ_STUB 14
|
||||
IRQ_STUB 15
|
||||
IRQ_STUB 16
|
||||
IRQ_STUB 17
|
||||
IRQ_STUB 18
|
||||
IRQ_STUB 19
|
||||
IRQ_STUB 20
|
||||
IRQ_STUB 21
|
||||
IRQ_STUB 22
|
||||
IRQ_STUB 23
|
||||
|
||||
; Spurious interrupt handler (vector 0xFF) - do nothing, no EOI
|
||||
global IrqStubSpurious
|
||||
IrqStubSpurious:
|
||||
iretq
|
||||
|
||||
; Export the stub table for C++ to reference
|
||||
section .data
|
||||
global IrqStubTable
|
||||
IrqStubTable:
|
||||
dq IrqStub0
|
||||
dq IrqStub1
|
||||
dq IrqStub2
|
||||
dq IrqStub3
|
||||
dq IrqStub4
|
||||
dq IrqStub5
|
||||
dq IrqStub6
|
||||
dq IrqStub7
|
||||
dq IrqStub8
|
||||
dq IrqStub9
|
||||
dq IrqStub10
|
||||
dq IrqStub11
|
||||
dq IrqStub12
|
||||
dq IrqStub13
|
||||
dq IrqStub14
|
||||
dq IrqStub15
|
||||
dq IrqStub16
|
||||
dq IrqStub17
|
||||
dq IrqStub18
|
||||
dq IrqStub19
|
||||
dq IrqStub20
|
||||
dq IrqStub21
|
||||
dq IrqStub22
|
||||
dq IrqStub23
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Pic.cpp
|
||||
* Legacy 8259 PIC disable
|
||||
* Copyright (c) 2025 Daniel Hammer
|
||||
*/
|
||||
|
||||
#include "Pic.hpp"
|
||||
#include <Io/IoPort.hpp>
|
||||
#include <Terminal/Terminal.hpp>
|
||||
#include <CppLib/Stream.hpp>
|
||||
|
||||
namespace Hal {
|
||||
// 8259 PIC ports
|
||||
constexpr uint16_t PIC1_COMMAND = 0x20;
|
||||
constexpr uint16_t PIC1_DATA = 0x21;
|
||||
constexpr uint16_t PIC2_COMMAND = 0xA0;
|
||||
constexpr uint16_t PIC2_DATA = 0xA1;
|
||||
|
||||
// ICW1 flags
|
||||
constexpr uint8_t ICW1_INIT = 0x11;
|
||||
// ICW4 flags
|
||||
constexpr uint8_t ICW4_8086 = 0x01;
|
||||
|
||||
void DisableLegacyPic() {
|
||||
// Remap the PIC to vectors 0xF0-0xF7 (master) and 0xF8-0xFF (slave)
|
||||
// so they don't conflict with CPU exceptions or our APIC vectors.
|
||||
// Then mask all IRQs.
|
||||
|
||||
// ICW1: begin initialization sequence
|
||||
Io::Out8(ICW1_INIT, PIC1_COMMAND);
|
||||
Io::IoPortWait();
|
||||
Io::Out8(ICW1_INIT, PIC2_COMMAND);
|
||||
Io::IoPortWait();
|
||||
|
||||
// ICW2: remap IRQ base offsets
|
||||
Io::Out8(0xF0, PIC1_DATA); // Master offset
|
||||
Io::IoPortWait();
|
||||
Io::Out8(0xF8, PIC2_DATA); // Slave offset
|
||||
Io::IoPortWait();
|
||||
|
||||
// ICW3: cascade wiring
|
||||
Io::Out8(0x04, PIC1_DATA); // Slave on IRQ2
|
||||
Io::IoPortWait();
|
||||
Io::Out8(0x02, PIC2_DATA); // Cascade identity
|
||||
Io::IoPortWait();
|
||||
|
||||
// ICW4: 8086 mode
|
||||
Io::Out8(ICW4_8086, PIC1_DATA);
|
||||
Io::IoPortWait();
|
||||
Io::Out8(ICW4_8086, PIC2_DATA);
|
||||
Io::IoPortWait();
|
||||
|
||||
// Mask all IRQs on both PICs
|
||||
Io::Out8(0xFF, PIC1_DATA);
|
||||
Io::Out8(0xFF, PIC2_DATA);
|
||||
|
||||
Kt::KernelLogStream(Kt::OK, "PIC") << "Legacy 8259 PIC disabled";
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* Pic.hpp
|
||||
* Legacy 8259 PIC disable
|
||||
* Copyright (c) 2025 Daniel Hammer
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace Hal {
|
||||
void DisableLegacyPic();
|
||||
};
|
||||
@@ -111,7 +111,7 @@ namespace Hal {
|
||||
void IDTInitialize() {
|
||||
IDT = (InterruptDescriptor*)Memory::g_pfa->Allocate();
|
||||
Kt::KernelLogStream(Kt::DEBUG, "IDT") << "Allocated IDT at " << base::hex << (uint64_t)IDT;
|
||||
IDTR.Limit = 0x0FF;
|
||||
IDTR.Limit = (256 * sizeof(InterruptDescriptor)) - 1;
|
||||
IDTR.Base = (uint64_t)IDT;
|
||||
Kt::KernelLogStream(Kt::DEBUG, "IDT") << "Set IDTR Base to " << base::hex << IDTR.Base << " and Limit to " << base::hex << IDTR.Limit;
|
||||
|
||||
@@ -123,4 +123,8 @@ namespace Hal {
|
||||
|
||||
Kt::KernelLogStream(Kt::OK, "Hal") << "Loaded new IDT";
|
||||
}
|
||||
|
||||
void IDTReload() {
|
||||
LoadIDT(IDTR);
|
||||
}
|
||||
};
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
namespace Hal {
|
||||
struct InterruptDescriptor {
|
||||
@@ -24,4 +25,6 @@ namespace Hal {
|
||||
}__attribute__((packed));
|
||||
|
||||
void IDTInitialize();
|
||||
void IDTEncodeInterrupt(std::size_t i, void* handler, uint8_t type_attr);
|
||||
void IDTReload();
|
||||
};
|
||||
Reference in New Issue
Block a user