fix: make ACPI shutdown compliant with spec
This commit is contained in:
@@ -17,7 +17,8 @@ using namespace Kt;
|
|||||||
namespace Hal {
|
namespace Hal {
|
||||||
namespace AcpiShutdown {
|
namespace AcpiShutdown {
|
||||||
|
|
||||||
static constexpr uint16_t SLP_EN = 1 << 13;
|
static constexpr uint16_t SLP_EN = 1 << 13;
|
||||||
|
static constexpr uint16_t SLP_TYP_MASK = 0x1C00; // bits 10-12
|
||||||
|
|
||||||
static bool g_available = false;
|
static bool g_available = false;
|
||||||
static uint32_t g_pm1aControlBlock = 0;
|
static uint32_t g_pm1aControlBlock = 0;
|
||||||
@@ -60,16 +61,32 @@ namespace Hal {
|
|||||||
|
|
||||||
KernelLogStream(INFO, "ACPI") << "Performing ACPI S5 shutdown...";
|
KernelLogStream(INFO, "ACPI") << "Performing ACPI S5 shutdown...";
|
||||||
|
|
||||||
// Write SLP_TYPa | SLP_EN to PM1a control register
|
asm volatile("cli");
|
||||||
Io::Out16((g_slpTypA << 10) | SLP_EN, (uint16_t)g_pm1aControlBlock);
|
|
||||||
|
// Phase 1: Write SLP_TYP to PM1x_CNT without SLP_EN,
|
||||||
|
// preserving existing register bits
|
||||||
|
uint16_t pm1a = Io::In16((uint16_t)g_pm1aControlBlock);
|
||||||
|
pm1a = (pm1a & ~SLP_TYP_MASK) | (g_slpTypA << 10);
|
||||||
|
Io::Out16(pm1a, (uint16_t)g_pm1aControlBlock);
|
||||||
|
|
||||||
// If PM1b exists, write to it as well
|
|
||||||
if (g_pm1bControlBlock != 0) {
|
if (g_pm1bControlBlock != 0) {
|
||||||
Io::Out16((g_slpTypB << 10) | SLP_EN, (uint16_t)g_pm1bControlBlock);
|
uint16_t pm1b = Io::In16((uint16_t)g_pm1bControlBlock);
|
||||||
|
pm1b = (pm1b & ~SLP_TYP_MASK) | (g_slpTypB << 10);
|
||||||
|
Io::Out16(pm1b, (uint16_t)g_pm1bControlBlock);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we're still here, the shutdown didn't work immediately.
|
// Flush all caches before entering sleep state
|
||||||
// Halt and wait for the hardware to power off.
|
asm volatile("wbinvd");
|
||||||
|
|
||||||
|
// Phase 2: Assert SLP_EN to trigger the transition
|
||||||
|
Io::Out16(pm1a | SLP_EN, (uint16_t)g_pm1aControlBlock);
|
||||||
|
|
||||||
|
if (g_pm1bControlBlock != 0) {
|
||||||
|
uint16_t pm1b = Io::In16((uint16_t)g_pm1bControlBlock);
|
||||||
|
Io::Out16(pm1b | SLP_EN, (uint16_t)g_pm1bControlBlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Halt and wait for the hardware to power off
|
||||||
for (;;) {
|
for (;;) {
|
||||||
asm volatile("hlt");
|
asm volatile("hlt");
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user