diff --git a/VKernel/Kernel/platform/acpi/acpi.vp b/VKernel/Kernel/platform/acpi/acpi.vp index 4553781..c6cfcb6 100644 --- a/VKernel/Kernel/platform/acpi/acpi.vp +++ b/VKernel/Kernel/platform/acpi/acpi.vp @@ -4,11 +4,13 @@ import asm import drivers.serial.uart.serial as serial define ACPI_RSDP_SIG: uint64 = 0x2052545020445352 # "RSD PTR " -define ACPI_FADT_SIG: uint32 = 0x50414346 # "FACP" -define ACPI_SLP_TYP_S5: uint16 = 5 +define ACPI_FADT_SIG: uint32 = 0x50434146 # "FACP" define ACPI_SLP_EN: uint16 = 0x2000 define ACPI_PM1_CNT_PM1A: uint64 = 64 # FADT PM1a_CNT_BLK define ACPI_PM1_CNT_PM1B: uint64 = 68 # FADT PM1b_CNT_BLK +define ACPI_DSDT: uint64 = 40 # FADT DSDT +define ACPI_X_DSDT: uint64 = 140 # FADT X_DSDT +define ACPI_S5_NOT_FOUND: uint64 = 0xFFFFFFFFFFFFFFFF rsdp_phys: uint64 = 0 @@ -20,6 +22,10 @@ def _rd8(addr: uint64) -> uint8: p: uint8* = uint8*(addr) return p[0] +def _rd16(addr: uint64) -> uint16: + p: uint16* = uint16*(addr) + return p[0] + def _rd32(addr: uint64) -> uint32: p: uint32* = uint32*(addr) return p[0] @@ -67,20 +73,75 @@ def _find_fadt() -> uint64: rsdt: uint64 = uint64(_rd32(rsdp + uint64(16))) return _find_table(rsdt, 4, ACPI_FADT_SIG) +def _find_dsdt(fadt: uint64) -> uint64: + dsdt: uint64 = uint64(_rd32(fadt + ACPI_DSDT)) + if dsdt == 0 and _rd32(fadt + uint64(4)) >= 148: + dsdt = _rd64(fadt + ACPI_X_DSDT) + return dsdt + +# Size in bytes of an AML integer element. +def _aml_int_size(addr: uint64) -> uint64: + op: uint8 = _rd8(addr) + if op == 0x0A: return 2 + if op == 0x0B: return 3 + if op == 0x0C: return 5 + if op == 0x0E: return 9 + if op == 0x0D: + n: uint64 = 1 + while _rd8(addr + n) != 0: n = n + 1 + return n + 1 + return 1 + +def _aml_int(addr: uint64) -> uint64: + op: uint8 = _rd8(addr) + if op == 0x00: return 0 + if op == 0x01: return 1 + if op == 0x0A: return uint64(_rd8(addr + 1)) + if op == 0x0B: return uint64(_rd16(addr + 1)) + if op == 0x0C: return uint64(_rd32(addr + 1)) + if op == 0x0E: return _rd64(addr + 1) + if op == 0xFF: return 0xFFFFFFFFFFFFFFFF + return 0 + +# Locate _S5 and return SLP_TYPa in bits 0..15, SLP_TYPb in bits 16..31. +def _s5_from_table(table: uint64) -> uint64: + if table == 0: return ACPI_S5_NOT_FOUND + length: uint32 = _rd32(table + uint64(4)) + if length < 45: return ACPI_S5_NOT_FOUND + off: uint64 = 36 + while off + 9 < uint64(length): + if _rd8(table + off) == 0x5F and _rd8(table + off + 1) == 0x53: + if _rd8(table + off + 2) == 0x35 and _rd8(table + off + 3) == 0x5F: + pkg: uint64 = table + off + 4 + if _rd8(pkg) == 0x12: + lead: uint8 = _rd8(pkg + 1) + body: uint64 = pkg + 2 + uint64(lead >> 6) + if _rd8(body) >= 2: + a_addr: uint64 = body + 1 + a: uint64 = _aml_int(a_addr) + b_addr: uint64 = a_addr + _aml_int_size(a_addr) + b: uint64 = _aml_int(b_addr) + return (a & 0xFFFF) | ((b & 0xFFFF) << 16) + off = off + 1 + return ACPI_S5_NOT_FOUND + def poweroff(): - """Enter ACPI S5 using the PM1 control block advertised by the FADT.""" + """Enter ACPI S5 using the PM1 control blocks and _S5 types.""" serial.puts("[acpi] S5 shutdown\n") fadt: uint64 = _find_fadt() if fadt == 0: serial.puts("[acpi] FADT not found\n") return + s5: uint64 = _s5_from_table(_find_dsdt(fadt)) + if s5 == ACPI_S5_NOT_FOUND: + serial.puts("[acpi] _S5 not found\n") + return pm1a: uint32 = _rd32(fadt + ACPI_PM1_CNT_PM1A) pm1b: uint32 = _rd32(fadt + ACPI_PM1_CNT_PM1B) - value: uint16 = (ACPI_SLP_TYP_S5 << 10) | ACPI_SLP_EN if pm1a == 0 and pm1b == 0: serial.puts("[acpi] no PM1 control block\n") return if pm1a != 0: - asm.outw(uint16(pm1a), value) + asm.outw(uint16(pm1a), uint16(((s5 & 0xFFFF) << 10) | uint64(ACPI_SLP_EN))) if pm1b != 0: - asm.outw(uint16(pm1b), value) + asm.outw(uint16(pm1b), uint16((((s5 >> 16) & 0xFFFF) << 10) | uint64(ACPI_SLP_EN)))