feat: expanded ACPI support, initial support for S3 sleep
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user