feat: expanded ACPI support, initial support for S3 sleep

This commit is contained in:
2026-03-15 00:55:19 +01:00
parent 261b536041
commit 64c26f4288
39 changed files with 4403 additions and 59 deletions
+24
View File
@@ -64,6 +64,30 @@ namespace Hal {
<< " max LVT=" << base::dec << (uint64_t)((version >> 16) & 0xFF);
}
void Reinitialize() {
if (!g_apicBase) return;
// Re-assert the APIC Global Enable in the IA32_APIC_BASE MSR.
// Some firmware clears bit 11 during S3 resume. If the global
// enable is off, all subsequent MMIO register writes (SVR, TPR,
// timer LVT, etc.) are silently ignored - meaning no interrupts
// are delivered, no timer fires, and the system appears frozen.
uint64_t msrValue = ReadMSR(MSR_APIC_BASE);
if (!(msrValue & (1ULL << 11))) {
msrValue |= (1ULL << 11);
WriteMSR(MSR_APIC_BASE, msrValue);
}
// Re-enable APIC software enable bit in SVR + set spurious vector
uint32_t svr = ReadRegister(REG_SPURIOUS);
svr |= (1 << 8);
svr = (svr & 0xFFFFFF00) | SPURIOUS_VECTOR;
WriteRegister(REG_SPURIOUS, svr);
// Accept all interrupts
WriteRegister(REG_TPR, 0);
}
void SendEOI() {
WriteRegister(REG_EOI, 0);
}
+6
View File
@@ -32,6 +32,12 @@ namespace Hal {
constexpr uint32_t MSR_APIC_BASE = 0x1B;
void Initialize(uint64_t apicBasePhys);
// Re-enable the Local APIC after S3 resume.
// The MMIO mapping and base address survive (they're in page tables / RAM).
// Only the SVR and TPR hardware registers need reprogramming.
void Reinitialize();
void SendEOI();
uint32_t GetId();
+30
View File
@@ -21,6 +21,11 @@ namespace Hal {
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;
@@ -39,6 +44,13 @@ namespace Hal {
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) {
@@ -106,6 +118,24 @@ namespace Hal {
<< " -> 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);
+5
View File
@@ -29,6 +29,11 @@ namespace Hal {
void Initialize(uint64_t ioApicBasePhys, uint32_t gsiBase,
MADT::InterruptSourceOverride* overrides, int overrideCount);
// Re-apply all I/O APIC redirection entries after S3 resume.
// The I/O APIC hardware state is lost during S3; the saved routing
// table (in RAM) is replayed into the redirection registers.
void Reinitialize();
void SetRedirectionEntry(uint8_t irq, uint64_t entry);
uint64_t GetRedirectionEntry(uint8_t irq);