usb hubs, hotplug support, unplug support, saving log files to disk, smt
This commit is contained in:
1 parent
b838f8d83d
commit
27b31f62b6
39 files changed
+6294
-735
No files matched your search
+202
-70
@@ -82,11 +82,13 @@ static uintptr_t gIoapicPhys = 0xFEC00000;
|
||||
static uintptr_t gHpetPhys = 0xFED00000;
|
||||
static HAL_CPU_TOPOLOGY gCpuTopology;
|
||||
|
||||
// ACPI power-off state (filled in by ParseFadt / ScanDsdtForS5)
|
||||
// ACPI power-off state (filled in by ParseFadt / ScanDsdtForS5).
|
||||
// Do not guess SLP_TYP on real hardware: these values are firmware-specific.
|
||||
static uint16_t gPm1aCntBlk = 0;
|
||||
static uint16_t gPm1bCntBlk = 0;
|
||||
static uint8_t gSlpTypA = 5; // S5 SLP_TYP for PM1a; 5 is correct for QEMU/most VMs
|
||||
static uint8_t gSlpTypB = 5; // S5 SLP_TYP for PM1b
|
||||
static uint8_t gSlpTypA = 0;
|
||||
static uint8_t gSlpTypB = 0;
|
||||
static int gS5Found = 0;
|
||||
|
||||
int debugEnabled = 0;
|
||||
int _fltused; // MSVC floating-point helper anchor
|
||||
@@ -106,73 +108,183 @@ static void *HalPhysToVirt(uint64_t phys) {
|
||||
return (void *)(phys + gPhysBase);
|
||||
}
|
||||
|
||||
// FADT (Fixed ACPI Description Table) - partial layout up to PM1b_CNT_BLK.
|
||||
// We only need offsets 40 (DSDT ptr), 64 (PM1a_CNT_BLK), 68 (PM1b_CNT_BLK).
|
||||
// The full table is ~276 bytes (ACPI 2.0+); we never access beyond offset 68.
|
||||
// FADT (Fixed ACPI Description Table) offsets used here. ACPI 2.0+
|
||||
// systems may put the real DSDT and PM1 control registers in the extended
|
||||
// X_* fields, so do not rely only on the legacy 32-bit fields.
|
||||
#define FADT_DSDT_OFFSET 40u
|
||||
#define FADT_PM1A_CNT_BLK_OFFSET 64u
|
||||
#define FADT_PM1B_CNT_BLK_OFFSET 68u
|
||||
#define FADT_X_DSDT_OFFSET 140u
|
||||
#define FADT_X_PM1A_CNT_BLK_OFFSET 172u
|
||||
#define FADT_X_PM1B_CNT_BLK_OFFSET 184u
|
||||
|
||||
#pragma pack(push, 1)
|
||||
typedef struct {
|
||||
ACPISDTHeader Header; // 0 (36 bytes)
|
||||
uint32_t FirmwareCtrl; // 36
|
||||
uint32_t Dsdt; // 40 - physical addr of DSDT
|
||||
uint8_t Ignored44; // 44
|
||||
uint8_t PreferredPmProfile; // 45
|
||||
uint16_t SciInt; // 46
|
||||
uint32_t SmiCmd; // 48
|
||||
uint8_t AcpiEnable; // 52
|
||||
uint8_t AcpiDisable; // 53
|
||||
uint8_t S4BiosReq; // 54
|
||||
uint8_t PstateCnt; // 55
|
||||
uint32_t Pm1aEvtBlk; // 56
|
||||
uint32_t Pm1bEvtBlk; // 60
|
||||
uint32_t Pm1aCntBlk; // 64 - I/O port for PM1a control register
|
||||
uint32_t Pm1bCntBlk; // 68 - I/O port for PM1b control register
|
||||
} FADT;
|
||||
uint8_t AddressSpaceId;
|
||||
uint8_t RegisterBitWidth;
|
||||
uint8_t RegisterBitOffset;
|
||||
uint8_t AccessSize;
|
||||
uint64_t Address;
|
||||
} ACPI_GAS;
|
||||
#pragma pack(pop)
|
||||
|
||||
// Scan DSDT AML for the _S5_ Name object and extract SLP_TYP values.
|
||||
// The encoding looks like:
|
||||
// 0x08 '_' 'S' '5' '_' ; NameOp + "_S5_"
|
||||
// 0x12 <PkgLen> ; PackageOp + encoded length
|
||||
// <NumElements> ; usually 0x04
|
||||
// 0x0A <slp_typ_a> ; BytePrefix + value (or 0x00 for zero)
|
||||
// 0x0A <slp_typ_b>
|
||||
static void ScanDsdtForS5(const uint8_t* aml, uint32_t len) {
|
||||
for (uint32_t i = 0; i + 9 < len; i++) {
|
||||
// Match NameOp + "_S5_" + PackageOp
|
||||
if (aml[i] != 0x08) continue;
|
||||
if (aml[i+1] != 0x5F || aml[i+2] != 0x53 ||
|
||||
aml[i+3] != 0x35 || aml[i+4] != 0x5F) continue;
|
||||
if (aml[i+5] != 0x12) continue;
|
||||
static uint32_t AcpiReadU32(const void* table, uint32_t tableLen, uint32_t off) {
|
||||
uint32_t value = 0;
|
||||
if (off + sizeof(value) <= tableLen)
|
||||
memcpy(&value, (const uint8_t*)table + off, sizeof(value));
|
||||
return value;
|
||||
}
|
||||
|
||||
// PkgLength encoding: low 6 bits = byte count (0 → only this byte);
|
||||
// bits [7:6] = number of following bytes in the length field.
|
||||
uint8_t pkgByte = aml[i+6];
|
||||
uint32_t pkgExtra = pkgByte >> 6; // 0..3 extra bytes
|
||||
uint32_t j = i + 7 + pkgExtra; // skip PkgLen field
|
||||
if (j + 2 >= len) break;
|
||||
j++; // skip NumElements byte
|
||||
static uint64_t AcpiReadU64(const void* table, uint32_t tableLen, uint32_t off) {
|
||||
uint64_t value = 0;
|
||||
if (off + sizeof(value) <= tableLen)
|
||||
memcpy(&value, (const uint8_t*)table + off, sizeof(value));
|
||||
return value;
|
||||
}
|
||||
|
||||
// SLP_TYP_A
|
||||
if (aml[j] == 0x0A && j + 1 < len) {
|
||||
gSlpTypA = aml[j + 1]; j += 2;
|
||||
} else if (aml[j] == 0x00) {
|
||||
gSlpTypA = 0; j++;
|
||||
} else {
|
||||
break; // unexpected encoding - keep defaults
|
||||
static uint16_t AcpiReadGasIoPort(const void* table, uint32_t tableLen, uint32_t off) {
|
||||
ACPI_GAS gas;
|
||||
if (off + sizeof(gas) > tableLen) return 0;
|
||||
|
||||
memcpy(&gas, (const uint8_t*)table + off, sizeof(gas));
|
||||
|
||||
// 0x01 == System I/O. HalShutdown currently writes PM1_CNT with outw(),
|
||||
// so ignore MMIO GAS entries until the HAL grows MMIO register access here.
|
||||
if (gas.AddressSpaceId != 0x01 || gas.Address == 0 || gas.Address > 0xFFFF)
|
||||
return 0;
|
||||
|
||||
return (uint16_t)gas.Address;
|
||||
}
|
||||
|
||||
static int AmlParsePkgLength(const uint8_t* p, const uint8_t* end,
|
||||
const uint8_t** body, const uint8_t** pkgEnd) {
|
||||
if (p >= end) return 0;
|
||||
|
||||
const uint8_t* start = p;
|
||||
uint8_t first = *p++;
|
||||
uint32_t follow = first >> 6;
|
||||
uint32_t length;
|
||||
|
||||
if (follow == 0) {
|
||||
length = first & 0x3F;
|
||||
} else {
|
||||
length = first & 0x0F;
|
||||
for (uint32_t i = 0; i < follow; i++) {
|
||||
if (p >= end) return 0;
|
||||
length |= ((uint32_t)*p++) << (4 + i * 8);
|
||||
}
|
||||
}
|
||||
|
||||
// SLP_TYP_B
|
||||
if (j < len && aml[j] == 0x0A && j + 1 < len)
|
||||
gSlpTypB = aml[j + 1];
|
||||
else if (j < len && aml[j] == 0x00)
|
||||
gSlpTypB = 0;
|
||||
if (start + length < p || start + length > end) return 0;
|
||||
*body = p;
|
||||
*pkgEnd = start + length;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const uint8_t* AmlParseNameString(const uint8_t* p, const uint8_t* end,
|
||||
int* lastSegmentIsS5) {
|
||||
uint32_t segments = 1;
|
||||
*lastSegmentIsS5 = 0;
|
||||
|
||||
// Optional root or parent prefixes: '\\', '^', '^^', ...
|
||||
while (p < end && (*p == 0x5C || *p == 0x5E)) p++;
|
||||
if (p >= end) return NULL;
|
||||
|
||||
if (*p == 0x00) { // NullName
|
||||
return p + 1;
|
||||
} else if (*p == 0x2E) { // DualNamePrefix
|
||||
p++;
|
||||
segments = 2;
|
||||
} else if (*p == 0x2F) { // MultiNamePrefix
|
||||
p++;
|
||||
if (p >= end) return NULL;
|
||||
segments = *p++;
|
||||
}
|
||||
|
||||
for (uint32_t s = 0; s < segments; s++) {
|
||||
if (p + 4 > end) return NULL;
|
||||
if (s == segments - 1 && memcmp(p, "_S5_", 4) == 0)
|
||||
*lastSegmentIsS5 = 1;
|
||||
p += 4;
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
static int AmlReadInteger(const uint8_t** pp, const uint8_t* end, uint8_t* out) {
|
||||
const uint8_t* p = *pp;
|
||||
if (p >= end) return 0;
|
||||
|
||||
switch (*p++) {
|
||||
case 0x00: // ZeroOp
|
||||
*out = 0;
|
||||
break;
|
||||
case 0x01: // OneOp
|
||||
*out = 1;
|
||||
break;
|
||||
case 0x0A: // BytePrefix
|
||||
if (p >= end) return 0;
|
||||
*out = *p++;
|
||||
break;
|
||||
case 0x0B: // WordPrefix
|
||||
if (p + 2 > end) return 0;
|
||||
*out = p[0];
|
||||
p += 2;
|
||||
break;
|
||||
case 0x0C: // DWordPrefix
|
||||
if (p + 4 > end) return 0;
|
||||
*out = p[0];
|
||||
p += 4;
|
||||
break;
|
||||
case 0x0E: // QWordPrefix
|
||||
if (p + 8 > end) return 0;
|
||||
*out = p[0];
|
||||
p += 8;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
*pp = p;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Scan DSDT AML for the _S5_ Name object and extract SLP_TYP values. Real
|
||||
// firmware commonly encodes the name as "\\_S5_" instead of just "_S5_", and
|
||||
// package integers may be ZeroOp/OneOp/Byte/Word/DWord/QWord constants.
|
||||
static void ScanDsdtForS5(const uint8_t* aml, uint32_t len) {
|
||||
const uint8_t* end = aml + len;
|
||||
|
||||
for (const uint8_t* cur = aml; cur < end; cur++) {
|
||||
if (*cur != 0x08) continue; // NameOp
|
||||
|
||||
const uint8_t* p = cur + 1;
|
||||
int isS5 = 0;
|
||||
p = AmlParseNameString(p, end, &isS5);
|
||||
if (!p || !isS5 || p >= end || *p != 0x12) continue; // PackageOp
|
||||
|
||||
const uint8_t* body;
|
||||
const uint8_t* pkgEnd;
|
||||
if (!AmlParsePkgLength(p + 1, end, &body, &pkgEnd)) continue;
|
||||
if (body >= pkgEnd) continue;
|
||||
|
||||
uint8_t elements = *body++;
|
||||
if (elements < 2) continue;
|
||||
|
||||
uint8_t typA, typB;
|
||||
if (!AmlReadInteger(&body, pkgEnd, &typA)) continue;
|
||||
if (!AmlReadInteger(&body, pkgEnd, &typB)) continue;
|
||||
|
||||
gSlpTypA = typA & 0x07;
|
||||
gSlpTypB = typB & 0x07;
|
||||
gS5Found = 1;
|
||||
|
||||
HalDbgPrint("HAL: _S5_ SLP_TYP_A=0x%02x SLP_TYP_B=0x%02x\n",
|
||||
gSlpTypA, gSlpTypB);
|
||||
return;
|
||||
}
|
||||
HalDbgPrint("HAL: _S5_ not found in DSDT, using defaults (A=0x%02x B=0x%02x)\n",
|
||||
gSlpTypA, gSlpTypB);
|
||||
|
||||
HalDbgPrint("HAL: _S5_ not found in DSDT; ACPI shutdown disabled\n");
|
||||
}
|
||||
|
||||
static int validate_checksum(void* table, uint32_t length) {
|
||||
@@ -184,19 +296,33 @@ static int validate_checksum(void* table, uint32_t length) {
|
||||
return sum == 0;
|
||||
}
|
||||
|
||||
static void ParseFadt(const FADT* fadt) {
|
||||
gPm1aCntBlk = (uint16_t)fadt->Pm1aCntBlk;
|
||||
gPm1bCntBlk = (uint16_t)fadt->Pm1bCntBlk;
|
||||
HalDbgPrint("HAL: FADT PM1a_CNT=0x%04x PM1b_CNT=0x%04x DSDT=0x%08x\n",
|
||||
gPm1aCntBlk, gPm1bCntBlk, fadt->Dsdt);
|
||||
static void ParseFadt(const ACPISDTHeader* header) {
|
||||
const void* fadt = header;
|
||||
uint32_t len = header->Length;
|
||||
|
||||
if (fadt->Dsdt) {
|
||||
const ACPISDTHeader* dsdt =
|
||||
(const ACPISDTHeader*)((uintptr_t)fadt->Dsdt + gPhysBase);
|
||||
gPm1aCntBlk = (uint16_t)AcpiReadU32(fadt, len, FADT_PM1A_CNT_BLK_OFFSET);
|
||||
gPm1bCntBlk = (uint16_t)AcpiReadU32(fadt, len, FADT_PM1B_CNT_BLK_OFFSET);
|
||||
|
||||
if (!gPm1aCntBlk)
|
||||
gPm1aCntBlk = AcpiReadGasIoPort(fadt, len, FADT_X_PM1A_CNT_BLK_OFFSET);
|
||||
if (!gPm1bCntBlk)
|
||||
gPm1bCntBlk = AcpiReadGasIoPort(fadt, len, FADT_X_PM1B_CNT_BLK_OFFSET);
|
||||
|
||||
uint64_t dsdtPhys = AcpiReadU64(fadt, len, FADT_X_DSDT_OFFSET);
|
||||
if (!dsdtPhys)
|
||||
dsdtPhys = AcpiReadU32(fadt, len, FADT_DSDT_OFFSET);
|
||||
|
||||
HalDbgPrint("HAL: FADT PM1a_CNT=0x%04x PM1b_CNT=0x%04x DSDT=0x%016llx\n",
|
||||
gPm1aCntBlk, gPm1bCntBlk, dsdtPhys);
|
||||
|
||||
if (dsdtPhys) {
|
||||
const ACPISDTHeader* dsdt = (const ACPISDTHeader*)(dsdtPhys + gPhysBase);
|
||||
if (memcmp(dsdt->Signature, "DSDT", 4) == 0 &&
|
||||
validate_checksum((void*)dsdt, dsdt->Length)) {
|
||||
const uint8_t* aml = (const uint8_t*)(dsdt + 1);
|
||||
ScanDsdtForS5(aml, dsdt->Length - (uint32_t)sizeof(ACPISDTHeader));
|
||||
} else {
|
||||
HalDbgPrint("HAL: DSDT invalid or checksum failed\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -319,7 +445,7 @@ static void WalkAcpi(uintptr_t rsdpPhys) {
|
||||
}
|
||||
// Parse FADT (signature "FACP") for PM1 control ports and DSDT pointer
|
||||
else if (memcmp(header->Signature, "FACP", 4) == 0) {
|
||||
ParseFadt((const FADT*)header);
|
||||
ParseFadt(header);
|
||||
}
|
||||
// Parse HPET
|
||||
else if (memcmp(header->Signature, "HPET", 4) == 0) {
|
||||
@@ -337,11 +463,17 @@ static void HalShutdown(void) {
|
||||
// Silence! Stop the world!
|
||||
_disable();
|
||||
|
||||
// SLP_EN (bit 13) | SLP_TYPx (bits 12:10)
|
||||
uint16_t valA = (uint16_t)((gSlpTypA << 10) | (1 << 13));
|
||||
uint16_t valB = (uint16_t)((gSlpTypB << 10) | (1 << 13));
|
||||
if (!gS5Found || !gPm1aCntBlk) {
|
||||
// Refuse to guess. Guessing SLP_TYP is exactly how real hardware gets
|
||||
// put into S3/S4 sleep instead of S5 soft-off.
|
||||
while (true) __halt();
|
||||
}
|
||||
|
||||
if (gPm1aCntBlk) __outword(gPm1aCntBlk, valA);
|
||||
// SLP_EN (bit 13) | SLP_TYPx (bits 12:10)
|
||||
uint16_t valA = (uint16_t)(((uint16_t)gSlpTypA << 10) | (1 << 13));
|
||||
uint16_t valB = (uint16_t)(((uint16_t)gSlpTypB << 10) | (1 << 13));
|
||||
|
||||
__outword(gPm1aCntBlk, valA);
|
||||
if (gPm1bCntBlk) __outword(gPm1bCntBlk, valB);
|
||||
|
||||
// Die!
|
||||
|
||||
Reference in new issue
Block a user