// Optional firmware-event driver using the resident ACPICA namespace. #include #include #include #include #include #include "third_party/acpica/include/acpi.h" #include "third_party/acpica/include/aclocal.h" #include "third_party/acpica/include/acobject.h" #include "third_party/acpica/include/acglobal.h" #include "acpi_local.h" #include #include // Firmware callbacks remain resident until the final ACPI S5 transition. static KSTATUS AcpiNtlPreparePower(void* context, uint32_t action) { (void)context; if (action != SYS_POWER_ACTION_SHUTDOWN) return NTLSTATUS_NOT_SUPPORTED; ACPI_STATUS status = AcpiEnterSleepStatePrep(ACPI_STATE_S5); if (ACPI_FAILURE(status)) { DbgPrint("acpi: S5 preparation failed status=0x%x\n", (unsigned)status); return NTLSTATUS_NOT_SUPPORTED; } return NTLSTATUS_SUCCESS; } static KSTATUS AcpiNtlEnterPower(void* context, uint32_t action) { (void)context; if (action != SYS_POWER_ACTION_SHUTDOWN) return NTLSTATUS_NOT_SUPPORTED; // ACPICA requires interrupts disabled for the final sleep-control writes. uintptr_t flags = KeDisableInterrupts(); ACPI_STATUS status = AcpiEnterSleepState(ACPI_STATE_S5); KeRestoreInterrupts(flags); DbgPrint("acpi: S5 transition returned status=0x%x\n", (unsigned)status); return NTLSTATUS_NOT_SUPPORTED; } static volatile int32_t gFixedButtonPending; static uint32_t gFixedButtonEvents; static uint32_t gMethodButtonEvents; static uint32_t gMethodButtons; static void AcpiNtlDeliverButton(void* context) { (void)context; KSTATUS result = KeNotifyPowerButton(); if (!KSUCCESS(result)) DbgPrint("acpi: power button delivery failed status=0x%x\n", result); } static void AcpiNtlDeliverFixedButton(void* context) { AcpiNtlDeliverButton(context); NtlAtomicExchange32(&gFixedButtonPending, 0); } bool AcpiNtlFixedButtonHasDeferred(void) { return NtlAtomicLoad32(&gFixedButtonPending) == 2; } void AcpiNtlFixedButtonDrainDeferred(void) { if (NtlAtomicCompareExchange32(&gFixedButtonPending, 1, 2) == 2) AcpiNtlDeliverFixedButton(NULL); } // Fixed-event handlers run in the SCI and delegate delivery to passive work. static UINT32 AcpiNtlFixedButton(void* context) { (void)context; gFixedButtonEvents++; if (NtlAtomicCompareExchange32(&gFixedButtonPending, 1, 0) == 0 && ACPI_FAILURE(AcpiOsExecute(OSL_NOTIFY_HANDLER, AcpiNtlDeliverFixedButton, NULL))) { NtlAtomicExchange32(&gFixedButtonPending, 2); AcpiNtlWakeExecutor(); } return ACPI_INTERRUPT_HANDLED; } // 0x80 is the normal S0 press event; 0x02 is a wake notification. static void AcpiNtlButtonNotify(ACPI_HANDLE handle, UINT32 event, void* context) { (void)handle; (void)context; if (event == 0x80u) { gMethodButtonEvents++; AcpiNtlDeliverButton(NULL); } } static ACPI_STATUS AcpiNtlFindButton(ACPI_HANDLE handle, UINT32 depth, void* context, void** returnValue) { (void)depth; (void)context; (void)returnValue; ACPI_STATUS result = AcpiInstallNotifyHandler(handle, ACPI_DEVICE_NOTIFY, AcpiNtlButtonNotify, NULL); if (ACPI_SUCCESS(result)) gMethodButtons++; return AE_OK; } static KSTATUS AcpiDriverInitialize(NTL_MODULE* module) { (void)module; uint64_t root = 0; KSTATUS status = KeFirmwareQueryAcpiRsdp(&root); if (!KSUCCESS(status)) { DbgPrint("acpi: RSDP query failed status=0x%x\n", status); return status; } ACPI_STATUS result = AcpiInitializeSubsystem(); if (ACPI_FAILURE(result)) goto fail; result = AcpiInitializeTables(NULL, 32, TRUE); if (ACPI_FAILURE(result)) goto fail; result = AcpiLoadTables(); if (ACPI_FAILURE(result)) goto fail; AcpiNtlRestrictRuntimeMappings(); status = AcpiNtlPrepareMmioRegisters(); if (!KSUCCESS(status)) { DbgPrint("acpi: MMIO register mappings unavailable st=0x%x\n", status); result = AE_NO_MEMORY; goto fail; } status = AcpiNtlStartExecutor(); if (!KSUCCESS(status)) { DbgPrint("acpi: event worker unavailable st=0x%x\n", status); result = AE_NO_MEMORY; goto fail; } result = AcpiEnableSubsystem(0); if (ACPI_FAILURE(result)) goto fail; ACPI_STATUS ecStatus = AcpiNtlEcInitialize(); if (ACPI_FAILURE(ecStatus)) DbgPrint("acpi: EC enumeration failed status=0x%x\n", (unsigned)ecStatus); result = AcpiInitializeObjects(0); if (ACPI_FAILURE(result)) goto fail; AcpiNtlEcActivate(); ACPI_STATUS fixedStatus = AE_SUPPORT; if (!(AcpiGbl_FADT.Flags & ACPI_FADT_POWER_BUTTON)) { fixedStatus = AcpiInstallFixedEventHandler( ACPI_EVENT_POWER_BUTTON, AcpiNtlFixedButton, NULL); } ACPI_STATUS methodStatus = AcpiGetDevices("PNP0C0C", AcpiNtlFindButton, NULL, NULL); DbgPrint("acpi: ready rsdp=0x%016llX fixed=0x%x method=0x%x buttons=%u ec=%u\n", (unsigned long long)root, (unsigned)fixedStatus, (unsigned)methodStatus, gMethodButtons, AcpiNtlEcCount()); const NTL_FIRMWARE_POWER_PROVIDER provider = { .Size = sizeof(NTL_FIRMWARE_POWER_PROVIDER), .Context = NULL, .Prepare = AcpiNtlPreparePower, .Enter = AcpiNtlEnterPower, }; status = KeRegisterFirmwarePowerProvider(&provider); if (!KSUCCESS(status)) DbgPrint("acpi: power provider registration failed status=0x%x\n", status); return NTLSTATUS_SUCCESS; fail: DbgPrint("acpi: namespace initialization failed status=0x%x\n", (unsigned)result); AcpiNtlEcTerminate(); (void)AcpiTerminate(); AcpiNtlStopExecutor(); AcpiNtlReleaseMmioRegisters(); return NTLSTATUS_NOT_SUPPORTED; } NTL_DECLARE_MODULE(AcpiDriverInitialize);