185 lines
7.2 KiB
C
185 lines
7.2 KiB
C
// Kernel power coordinator and optional firmware-provider binding.
|
|
|
|
#include <ntlite/syscall.h>
|
|
#include <ntlite/ddk/power.h>
|
|
#include <kernel/ke_power.h>
|
|
#include <kernel/ke_process.h>
|
|
#include <kernel/ke_event.h>
|
|
#include <kernel/ke_spinlock.h>
|
|
#include <kernel/ke_settings.h>
|
|
#include <kernel/ke_debug.h>
|
|
#include <string.h>
|
|
|
|
static SPINLOCK gPowerLock = SPINLOCK_INIT;
|
|
static EVENT* gPowerRequest;
|
|
static EVENT* gPowerComplete;
|
|
static PROCESS* gInit;
|
|
static uint32_t gAction;
|
|
static bool gCommitted;
|
|
static bool gPolicyReady;
|
|
static bool gEarlyButtonPressed;
|
|
static uint32_t gButtonAction;
|
|
static NTL_FIRMWARE_POWER_PROVIDER gFirmwareProvider;
|
|
static bool gFirmwarePrepared;
|
|
static uint32_t gPreparedAction;
|
|
|
|
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;
|
|
}
|
|
|
|
// The resident firmware owner may be ACPICA or another platform implementation.
|
|
__declspec(dllexport) KSTATUS KeRegisterFirmwarePowerProvider(
|
|
const NTL_FIRMWARE_POWER_PROVIDER* provider) {
|
|
if (!provider || provider->Size != sizeof(*provider) ||
|
|
!provider->Prepare || !provider->Enter)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
uintptr_t flags = KeAcquireSpinlock(&gPowerLock);
|
|
KSTATUS status = gFirmwareProvider.Size ? NTLSTATUS_ALREADY_EXISTS : NTLSTATUS_SUCCESS;
|
|
if (KSUCCESS(status)) gFirmwareProvider = *provider;
|
|
KeReleaseSpinlock(&gPowerLock, flags);
|
|
if (KSUCCESS(status)) DbgPrint("power: firmware provider registered\n");
|
|
return status;
|
|
}
|
|
|
|
// Events before system-root policy becomes available are retained once.
|
|
__declspec(dllexport) KSTATUS KeNotifyPowerButton(void) {
|
|
uintptr_t flags = KeAcquireSpinlock(&gPowerLock);
|
|
if (!gPowerRequest) {
|
|
KeReleaseSpinlock(&gPowerLock, flags);
|
|
return NTLSTATUS_NOT_SUPPORTED;
|
|
}
|
|
if (!gPolicyReady) {
|
|
gEarlyButtonPressed = true;
|
|
KeReleaseSpinlock(&gPowerLock, flags);
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
uint32_t action = gButtonAction;
|
|
KeReleaseSpinlock(&gPowerLock, flags);
|
|
return action ? KePowerRequestHardware(action) : NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
// System-root policy is loaded independently of the presence of a firmware driver.
|
|
void KePowerInitializePolicy(void) {
|
|
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);
|
|
uint32_t action = 0;
|
|
if (strcmp(mode, "shutdown") == 0) action = SYS_POWER_ACTION_SHUTDOWN;
|
|
else if (strcmp(mode, "restart") == 0) action = SYS_POWER_ACTION_RESTART;
|
|
else if (strcmp(mode, "ignore") != 0)
|
|
KePanic(KE_PANIC_CODE_INIT, "power: invalid [power] button=%s\n", mode);
|
|
|
|
uintptr_t flags = KeAcquireSpinlock(&gPowerLock);
|
|
gButtonAction = action;
|
|
gPolicyReady = true;
|
|
bool pending = gEarlyButtonPressed;
|
|
bool providerPresent = gFirmwareProvider.Size != 0;
|
|
gEarlyButtonPressed = false;
|
|
KeReleaseSpinlock(&gPowerLock, flags);
|
|
DbgPrint("power: button policy=%s firmware provider=%s\n",
|
|
mode, providerPresent ? "present" : "absent");
|
|
if (pending && action) (void)KePowerRequestHardware(action);
|
|
}
|
|
|
|
// A provider prepares firmware while threads, device resources and AML can run.
|
|
KSTATUS KePowerPrepareFirmware(uint32_t action) {
|
|
if (action != SYS_POWER_ACTION_SHUTDOWN && action != SYS_POWER_ACTION_RESTART)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
NTL_FIRMWARE_POWER_PROVIDER provider;
|
|
uintptr_t flags = KeAcquireSpinlock(&gPowerLock);
|
|
provider = gFirmwareProvider;
|
|
KeReleaseSpinlock(&gPowerLock, flags);
|
|
if (!provider.Size) return NTLSTATUS_NOT_SUPPORTED;
|
|
flags = KeAcquireSpinlock(&gPowerLock);
|
|
gFirmwarePrepared = false;
|
|
gPreparedAction = 0;
|
|
KeReleaseSpinlock(&gPowerLock, flags);
|
|
KSTATUS status = provider.Prepare(provider.Context, action);
|
|
if (KSUCCESS(status)) {
|
|
flags = KeAcquireSpinlock(&gPowerLock);
|
|
gFirmwarePrepared = true;
|
|
gPreparedAction = action;
|
|
KeReleaseSpinlock(&gPowerLock, flags);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
// Any returned transition leaves the HAL as the supported final fallback.
|
|
KSTATUS KePowerEnterFirmware(uint32_t action) {
|
|
NTL_FIRMWARE_POWER_PROVIDER provider;
|
|
uintptr_t flags = KeAcquireSpinlock(&gPowerLock);
|
|
bool prepared = gFirmwarePrepared && gPreparedAction == action;
|
|
provider = gFirmwareProvider;
|
|
KeReleaseSpinlock(&gPowerLock, flags);
|
|
if (!prepared || !provider.Size) return NTLSTATUS_NOT_SUPPORTED;
|
|
return provider.Enter(provider.Context, action);
|
|
}
|
|
|
|
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;
|
|
}
|