#include #include #include #include #include #include #include #include #include #include #include #include static SPINLOCK gPowerLock = SPINLOCK_INIT; static EVENT* gPowerRequest; static EVENT* gPowerComplete; static PROCESS* gInit; static uint32_t gAction; static bool gCommitted; static const HAL_API* gButtonHal; static uint32_t gButtonAction; static KDPC gPowerButtonDpc; KSTATUS KePowerInitialize(void) { if (gPowerRequest) return NTLSTATUS_ALREADY_EXISTS; KSTATUS st = KeEventCreate(EventManualReset, false, &gPowerRequest); if (!KSUCCESS(st)) return st; st = KeEventCreate(EventManualReset, false, &gPowerComplete); return st; } // Delivers the physical action after interrupt-exit processing. static void KiPowerButtonDpc(KDPC* dpc, void* context, void* arg1, void* arg2) { (void)dpc; (void)context; (void)arg1; (void)arg2; (void)KePowerRequestHardware(gButtonAction); } // The ACPI SCI handler only acknowledges the fixed event and queues normal work. static void KiPowerButtonIsr(void) { if (!gButtonHal || !gButtonHal->Power.ConsumeFixedButton || !gButtonHal->Power.ConsumeFixedButton()) return; (void)KeInsertQueueDpc(&gPowerButtonDpc, NULL, NULL); } // Reads immutable boot-time button policy and attaches it to the fixed ACPI SCI. void KePowerInitializeButton(const HAL_API* hal) { char mode[24]; KSTATUS status = KeGetSettingString("power", "button", mode, sizeof(mode)); if (!KSUCCESS(status)) KePanic(KE_PANIC_CODE_INIT, "power: missing [power] button, status=0x%x\n", status); if (strcmp(mode, "ignore") == 0) { DbgPrint("power: ACPI power button ignored by policy\n"); return; } if (strcmp(mode, "shutdown") == 0) gButtonAction = SYS_POWER_ACTION_SHUTDOWN; else if (strcmp(mode, "restart") == 0) gButtonAction = SYS_POWER_ACTION_RESTART; else KePanic(KE_PANIC_CODE_INIT, "power: invalid [power] button=%s\n", mode); if (!hal || !hal->Power.QueryFixedButtonIrq || !hal->Power.ArmFixedButton || !hal->Power.ConsumeFixedButton) { DbgPrint("power: no ACPI fixed power button HAL support\n"); return; } uint8_t irq = 0; status = hal->Power.QueryFixedButtonIrq(&irq); if (!KSUCCESS(status)) { DbgPrint("power: ACPI fixed button unavailable (0x%x); AML button not yet supported\n", status); return; } // The IOAPIC is still masked for this IRQ. Arm the firmware source first, // then install the kernel handler and finally unmask via KeIrqRegister. status = hal->Power.ArmFixedButton(); if (!KSUCCESS(status)) { DbgPrint("power: cannot arm fixed button IRQ %u, status=0x%x\n", irq, status); return; } KeInitializeDpc(&gPowerButtonDpc, KiPowerButtonDpc, NULL); gButtonHal = hal; KeIrqRegister(irq, KiPowerButtonIsr); DbgPrint("power: ACPI fixed button armed on IRQ %u, action=%s\n", irq, mode); } void KePowerSetInitProcess(PROCESS* init) { uintptr_t flags = KeAcquireSpinlock(&gPowerLock); gInit = init; KeReleaseSpinlock(&gPowerLock, flags); } // Records one power transition under the coordinator lock. The hardware event // may arrive before init exists; syscall-originated requests require init. static KSTATUS KiPowerQueueRequest(uint32_t action, bool requireInit) { if (action != SYS_POWER_ACTION_SHUTDOWN && action != SYS_POWER_ACTION_RESTART) return NTLSTATUS_INVALID_PARAMETER; uintptr_t flags = KeAcquireSpinlock(&gPowerLock); KSTATUS status = NTLSTATUS_SUCCESS; if (!gPowerRequest || (requireInit && !gInit)) status = NTLSTATUS_NOT_SUPPORTED; else if (gAction) status = NTLSTATUS_ALREADY_EXISTS; else gAction = action; KeReleaseSpinlock(&gPowerLock, flags); if (KSUCCESS(status)) KeSignalEvent(gPowerRequest); return status; } // Accepts a privileged explicit power request only after init exists. KSTATUS KePowerRequest(uint32_t action) { return KiPowerQueueRequest(action, true); } // Latches a physical power-button request even during init startup. KSTATUS KePowerRequestHardware(uint32_t action) { return KiPowerQueueRequest(action, false); } EVENT* KePowerRequestEvent(PROCESS* caller) { uintptr_t flags = KeAcquireSpinlock(&gPowerLock); EVENT* event = caller && caller == gInit ? gPowerRequest : (EVENT*)0; KeReleaseSpinlock(&gPowerLock, flags); return event; } KSTATUS KePowerGetRequest(PROCESS* caller, uint32_t* actionOut) { if (!actionOut) return NTLSTATUS_INVALID_PARAMETER; uintptr_t flags = KeAcquireSpinlock(&gPowerLock); KSTATUS st = caller && caller == gInit ? NTLSTATUS_SUCCESS : NTLSTATUS_ACCESS_DENIED; if (KSUCCESS(st)) *actionOut = gAction; KeReleaseSpinlock(&gPowerLock, flags); return st; } KSTATUS KePowerComplete(PROCESS* caller, uint32_t action) { uintptr_t flags = KeAcquireSpinlock(&gPowerLock); KSTATUS st = caller && caller == gInit ? NTLSTATUS_SUCCESS : NTLSTATUS_ACCESS_DENIED; if (KSUCCESS(st) && (!gAction || action != gAction || gCommitted)) st = NTLSTATUS_INVALID_PARAMETER; if (KSUCCESS(st)) gCommitted = true; KeReleaseSpinlock(&gPowerLock, flags); if (KSUCCESS(st)) KeSignalEvent(gPowerComplete); return st; } EVENT* KePowerCompletionEvent(void) { return gPowerComplete; } uint32_t KePowerCommittedAction(void) { uintptr_t flags = KeAcquireSpinlock(&gPowerLock); uint32_t result = gCommitted ? gAction : 0; KeReleaseSpinlock(&gPowerLock, flags); return result; }