fix(acpi): read S5 sleep types from the DSDT _S5 object
The FADT signature constant was byte-swapped ("FCAP"), so FADT lookup
always failed. Correct it to "FACP".
SLP_TYP is platform-defined and must come from the DSDT _S5 package.
QEMU/OVMF defines _S5 as 0, so the previous hardcoded value 5 never
triggered S5. Parse _S5 and use its SLP_TYPa/SLP_TYPb for PM1a/PM1b.
This commit is contained in:
1 parent
b33fbb75dc
commit
cec0647708
1 file changed
+67
-6
@@ -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)))
|
||||
Reference in new issue
Block a user