Files
MontaukOS/kernel/src/Arch/x86_64/Apic/IoApic.cpp
T

168 lines
6.3 KiB
C++

/*
* 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;
// Shadow copy of redirection entries for S3 resume replay
static constexpr uint32_t MAX_REDIR_ENTRIES = 24;
static uint64_t g_savedRedirEntries[MAX_REDIR_ENTRIES];
static uint32_t g_savedEntryCount = 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));
// Shadow the entry for S3 resume replay
if (index < MAX_REDIR_ENTRIES) {
g_savedRedirEntries[index] = entry;
if (index >= g_savedEntryCount)
g_savedEntryCount = index + 1;
}
}
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 Reinitialize() {
if (!g_ioApicBase || g_savedEntryCount == 0) return;
// Replay all saved redirection entries into the I/O APIC hardware.
// The hardware registers are lost during S3 but our shadow copy in
// RAM survives.
for (uint32_t i = 0; i < g_savedEntryCount; i++) {
uint32_t regLow = IOREDTBL_BASE + (i * 2);
uint32_t regHigh = IOREDTBL_BASE + (i * 2) + 1;
WriteRegister(regHigh, (uint32_t)(g_savedRedirEntries[i] >> 32));
WriteRegister(regLow, (uint32_t)(g_savedRedirEntries[i] & 0xFFFFFFFF));
}
KernelLogStream(DEBUG, "IOAPIC") << "Restored " << base::dec
<< (uint64_t)g_savedEntryCount << " redirection entries after S3 resume";
}
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";
}
};
};