replace nob with ms' build, move disk creation to build.bat, major comment cleanup, remove old and stupid tests, overhaul interrupt management with per-CPU vector allocation, affinity migration and safe teardown, add proper IOAPIC routing and PCI MSI/MSI-X/INTx support, migrate drivers to the new interrupt API, integrate ACPICA with a proper OS services layer, move ACPI firmware events, power button handling and S5 shutdown into acpi.sys, add parallel driver startup and atomic publication, replace PnP arrays with dynamic lists, redesign service startup again to be asynchronous, add delayed startup mode for services, allow omitting keys in services.ini, fix acpi ec deferred work handling, fix kernel threads having garbage exit code.

This commit is contained in:
maksw committed 2026-10-28 15:18:43 +01:00
1 parent d36f19509c
commit 36eef8f58d
696 files changed
+147281 -7530

No files matched your search

+249
View File
@@ -0,0 +1,249 @@
// ACPI embedded-controller SystemIO transport and passive GPE query handling.
#include "third_party/acpica/include/acpi.h"
#include <ntlite/ddk/ddk.h>
#include <ntlite/ddk/debug.h>
#include <ntlite/ddk/object.h>
#include <ntlite/ddk/semaphore.h>
#include <ntlite/ddk/platform.h>
#include <intrin.h>
#include "acpi_local.h"
#define ACPI_NTL_EC_COUNT 4u
#define EC_STATUS_OBF 0x01u
#define EC_STATUS_IBF 0x02u
#define EC_STATUS_SCI 0x20u
#define EC_CMD_READ 0x80u
#define EC_CMD_WRITE 0x81u
#define EC_CMD_QUERY 0x84u
#define EC_POLL_COUNT 10000u
typedef struct ACPI_NTL_EC {
ACPI_HANDLE Handle;
SEMAPHORE* Mutex;
uint16_t DataPort;
uint16_t CommandPort;
ACPI_HANDLE GpeDevice;
uint32_t Gpe;
volatile long Pending;
uint8_t PortCount;
bool HaveGpe;
bool HandlerInstalled;
bool GpeInstalled;
} ACPI_NTL_EC;
static ACPI_NTL_EC gEc[ACPI_NTL_EC_COUNT];
static uint32_t gEcCount;
static bool AcpiNtlEcWait(ACPI_NTL_EC* ec, uint8_t mask, bool asserted) {
for (uint32_t i = 0; i < EC_POLL_COUNT; i++) {
bool set = (__inbyte(ec->CommandPort) & mask) != 0;
if (set == asserted) return true;
KeStallExecution(10);
}
return false;
}
static bool AcpiNtlEcReadByte(ACPI_NTL_EC* ec, uint8_t offset, uint8_t* result) {
if (!AcpiNtlEcWait(ec, EC_STATUS_IBF, false)) return false;
__outbyte(ec->CommandPort, EC_CMD_READ);
if (!AcpiNtlEcWait(ec, EC_STATUS_IBF, false)) return false;
__outbyte(ec->DataPort, offset);
if (!AcpiNtlEcWait(ec, EC_STATUS_OBF, true)) return false;
*result = __inbyte(ec->DataPort);
return true;
}
static bool AcpiNtlEcWriteByte(ACPI_NTL_EC* ec, uint8_t offset, uint8_t value) {
if (!AcpiNtlEcWait(ec, EC_STATUS_IBF, false)) return false;
__outbyte(ec->CommandPort, EC_CMD_WRITE);
if (!AcpiNtlEcWait(ec, EC_STATUS_IBF, false)) return false;
__outbyte(ec->DataPort, offset);
if (!AcpiNtlEcWait(ec, EC_STATUS_IBF, false)) return false;
__outbyte(ec->DataPort, value);
return AcpiNtlEcWait(ec, EC_STATUS_IBF, false);
}
static ACPI_STATUS AcpiNtlEcRegion(UINT32 function, ACPI_PHYSICAL_ADDRESS address,
UINT32 width, UINT64* value, void* context,
void* regionContext) {
(void)regionContext;
ACPI_NTL_EC* ec = (ACPI_NTL_EC*)context;
if (!ec || !value || !width || width > 64u || (width % 8u) ||
address > 0xFFu || width / 8u > 256u - address ||
(function != ACPI_READ && function != ACPI_WRITE)) return AE_BAD_PARAMETER;
if (KeWaitSemaphore(ec->Mutex, KE_WAIT_INFINITE) != NTLSTATUS_SUCCESS) return AE_ERROR;
UINT64 data = function == ACPI_WRITE ? *value : 0;
bool success = true;
for (uint32_t i = 0; i < width / 8u; i++) {
if (function == ACPI_READ) {
uint8_t byte = 0;
success = AcpiNtlEcReadByte(ec, (uint8_t)(address + i), &byte);
data |= (UINT64)byte << (8u * i);
} else {
success = AcpiNtlEcWriteByte(ec, (uint8_t)(address + i), (uint8_t)(data >> (8u * i)));
}
if (!success) break;
}
KeSignalSemaphore(ec->Mutex);
if (!success) return AE_TIME;
if (function == ACPI_READ) *value = data;
return AE_OK;
}
// A disabled GPE remains masked until its passive query callback completes.
static void AcpiNtlEcQuery(void* context) {
ACPI_NTL_EC* ec = (ACPI_NTL_EC*)context;
for (unsigned count = 0; count < 32u; count++) {
if (KeWaitSemaphore(ec->Mutex, KE_WAIT_INFINITE) != NTLSTATUS_SUCCESS) break;
uint8_t status = __inbyte(ec->CommandPort);
bool ready = (status & EC_STATUS_SCI) != 0;
bool success = false;
uint8_t query = 0;
if (ready && AcpiNtlEcWait(ec, EC_STATUS_IBF, false)) {
__outbyte(ec->CommandPort, EC_CMD_QUERY);
if (AcpiNtlEcWait(ec, EC_STATUS_OBF, true)) {
query = __inbyte(ec->DataPort);
success = true;
}
}
KeSignalSemaphore(ec->Mutex);
if (!ready || !success) break;
if (query) {
static const char digits[] = "0123456789ABCDEF";
char method[5] = { '_', 'Q', digits[query >> 4], digits[query & 15], 0 };
(void)AcpiEvaluateObject(ec->Handle, method, NULL, NULL);
}
}
_InterlockedExchange(&ec->Pending, 0);
if (ec->GpeInstalled) (void)AcpiFinishGpe(ec->GpeDevice, ec->Gpe);
}
static UINT32 AcpiNtlEcGpe(ACPI_HANDLE device, UINT32 number, void* context) {
(void)device; (void)number;
ACPI_NTL_EC* ec = (ACPI_NTL_EC*)context;
if (_InterlockedCompareExchange(&ec->Pending, 1, 0) == 0) {
if (ACPI_FAILURE(AcpiOsExecute(OSL_GPE_HANDLER, AcpiNtlEcQuery, ec))) {
_InterlockedExchange(&ec->Pending, 2);
AcpiNtlWakeExecutor();
}
}
// ACPICA leaves this GPE masked until the query worker calls FinishGpe.
return ACPI_INTERRUPT_HANDLED;
}
// The ACPI executor drains deferred EC queries after queued work completes.
bool AcpiNtlEcDrainDeferred(void) {
bool drained = false;
for (uint32_t i = 0; i < gEcCount; ++i) {
ACPI_NTL_EC* ec = &gEc[i];
if (_InterlockedCompareExchange(&ec->Pending, 1, 2) != 2) continue;
AcpiNtlEcQuery(ec);
drained = true;
}
return drained;
}
bool AcpiNtlEcHasDeferred(void) {
for (uint32_t i = 0; i < gEcCount; ++i)
if (_InterlockedCompareExchange(&gEc[i].Pending, 2, 2) == 2) return true;
return false;
}
static ACPI_STATUS AcpiNtlEcResource(ACPI_RESOURCE* resource, void* context) {
ACPI_NTL_EC* ec = (ACPI_NTL_EC*)context;
uint16_t port;
if (resource->Type == ACPI_RESOURCE_TYPE_IO) port = resource->Data.Io.Minimum;
else if (resource->Type == ACPI_RESOURCE_TYPE_FIXED_IO) port = resource->Data.FixedIo.Address;
else return AE_OK;
if (ec->PortCount == 0) ec->DataPort = port;
else if (ec->PortCount == 1) ec->CommandPort = port;
ec->PortCount++;
return AE_OK;
}
static ACPI_STATUS AcpiNtlEcDiscover(ACPI_HANDLE handle, UINT32 depth, void* context,
void** returnValue) {
(void)depth; (void)context; (void)returnValue;
if (gEcCount >= ACPI_NTL_EC_COUNT) return AE_OK;
ACPI_NTL_EC* ec = &gEc[gEcCount];
ec->Handle = handle;
ACPI_STATUS status = AcpiWalkResources(handle, "_CRS", AcpiNtlEcResource, ec);
if (ACPI_FAILURE(status) || ec->PortCount != 2 || !ec->DataPort || !ec->CommandPort ||
ec->DataPort == ec->CommandPort) {
DbgPrint("acpi: EC has unsupported _CRS resources st=0x%x\n", (unsigned)status);
ec->PortCount = 0;
return AE_OK;
}
if (KeSemaphoreCreate(1, 1, &ec->Mutex) != NTLSTATUS_SUCCESS) return AE_NO_MEMORY;
status = AcpiInstallAddressSpaceHandler(handle, ACPI_ADR_SPACE_EC, AcpiNtlEcRegion,
NULL, ec);
if (ACPI_FAILURE(status)) {
ObDereferenceObject((OBJECT*)ec->Mutex);
ec->Mutex = NULL;
return AE_OK;
}
ec->HandlerInstalled = true;
ACPI_BUFFER result = { ACPI_ALLOCATE_BUFFER, NULL };
status = AcpiEvaluateObject(handle, "_GPE", NULL, &result);
if (ACPI_SUCCESS(status) && result.Pointer) {
ACPI_OBJECT* gpe = (ACPI_OBJECT*)result.Pointer;
if (gpe->Type == ACPI_TYPE_INTEGER && gpe->Integer.Value <= UINT32_MAX) {
ec->Gpe = (uint32_t)gpe->Integer.Value;
ec->GpeDevice = NULL;
ec->HaveGpe = true;
} else if (gpe->Type == ACPI_TYPE_PACKAGE && gpe->Package.Count == 2 &&
gpe->Package.Elements &&
gpe->Package.Elements[0].Type == ACPI_TYPE_LOCAL_REFERENCE &&
gpe->Package.Elements[1].Type == ACPI_TYPE_INTEGER &&
gpe->Package.Elements[1].Integer.Value <= UINT32_MAX) {
ec->GpeDevice = gpe->Package.Elements[0].Reference.Handle;
ec->Gpe = (uint32_t)gpe->Package.Elements[1].Integer.Value;
ec->HaveGpe = ec->GpeDevice != NULL;
}
AcpiOsFree(result.Pointer);
}
gEcCount++;
return AE_OK;
}
ACPI_STATUS AcpiNtlEcInitialize(void) {
// Region handlers are established before ACPICA executes _REG methods.
return AcpiGetDevices("PNP0C09", AcpiNtlEcDiscover, NULL, NULL);
}
void AcpiNtlEcActivate(void) {
for (uint32_t i = 0; i < gEcCount; i++) {
ACPI_NTL_EC* ec = &gEc[i];
if (!ec->HaveGpe) continue;
ACPI_STATUS status = AcpiInstallGpeHandler(ec->GpeDevice, ec->Gpe,
ACPI_GPE_LEVEL_TRIGGERED, AcpiNtlEcGpe, ec);
if (ACPI_SUCCESS(status)) {
ec->GpeInstalled = true;
status = AcpiEnableGpe(ec->GpeDevice, ec->Gpe);
}
if (ACPI_FAILURE(status))
DbgPrint("acpi: EC GPE enable failed gpe=%u status=0x%x\n", ec->Gpe, (unsigned)status);
}
}
void AcpiNtlEcTerminate(void) {
for (uint32_t i = 0; i < gEcCount; i++) {
ACPI_NTL_EC* ec = &gEc[i];
if (ec->GpeInstalled) {
(void)AcpiDisableGpe(ec->GpeDevice, ec->Gpe);
(void)AcpiRemoveGpeHandler(ec->GpeDevice, ec->Gpe, AcpiNtlEcGpe);
ec->GpeInstalled = false;
}
}
AcpiOsWaitEventsComplete();
for (uint32_t i = 0; i < gEcCount; i++) {
ACPI_NTL_EC* ec = &gEc[i];
if (ec->HandlerInstalled)
(void)AcpiRemoveAddressSpaceHandler(ec->Handle, ACPI_ADR_SPACE_EC, AcpiNtlEcRegion);
if (ec->Mutex) ObDereferenceObject((OBJECT*)ec->Mutex);
ec->Mutex = NULL;
}
gEcCount = 0;
}
uint32_t AcpiNtlEcCount(void) { return gEcCount; }