What is ACPI: why firmware shouldn't power off by toggling a GPIO
Why shouldn't firmware toggle a GPIO to power off? ACPI is the contract firmware writes and the OS reads: tables, AML, power states, and debugging broken sleep and wake.
The original question: why shouldn’t firmware power off by toggling a GPIO?
When I first started getting familiar with SMM, I was working on emergency shutdown logic for a POS terminal — the kind of thing you need when a unit running in a hot environment exceeds a thermal threshold. My first instinct was entirely natural: SMM runs at extremely high privilege, no OS can intervene, so just toggle the GPIO pin that cuts power and be done with it.
A colleague looked at the draft code and asked one question: “Which GPIO? Every board is different. Are you hardcoding this?”
That question opened a chain of explanations that I want to write down here: why firmware shouldn’t power off by toggling a GPIO, and how ACPI fills that gap.
The short answer: whichever GPIO pin relates to power is platform-specific. Hardcoding it into an SMM handler or firmware routine ties the code to a specific board. More importantly, in a normal shutdown flow, power transition is not SMM’s decision alone. It’s part of ACPI and OS power management.
The longer answer requires stepping back to understand how firmware and the OS divide control of the hardware — and that’s exactly what ACPI exists for.
Firmware and the OS see hardware differently
After ExitBootServices() is called, the OS owns the platform. But the OS doesn’t know hardware-specific details: which GPIO pin is the power button, which EC register controls the fan, which rails need to be sequenced off in which order when going to sleep.
Firmware knows all of that, because firmware is what initialized the hardware from scratch. But firmware can’t just “explain it verbally” to the OS. It has to leave a standardized description behind.
ACPI is that description.
Firmware initializes hardware
Firmware configures chipset, devices, policy, and platform resources
Publish ACPI tables
Firmware installs DSDT, SSDT, FADT, MADT, and related tables
ExitBootServices
Firmware hands over boot-time control to the OS
OS reads ACPI
OS walks from RSDP, reads DSDT/SSDT, interprets AML
OS calls ACPI methods
Device probing, sleep, wake, power button all go through the ACPI model
ACPI is not a direct function-call API between OS and firmware. It is a data and control method contract: firmware publishes tables and AML bytecode; the OS reads the tables and executes AML methods when needed. Every power management operation that happens after the OS is running goes through this contract.
Back to the question: why not toggle a GPIO in SMM?
In SMM, you technically can access hardware registers directly. But using that to power off the system in a normal flow creates several problems.
First, it bypasses the ACPI power sequence. The OS has no opportunity to flush disks, close file handles, notify driver cleanup, or prepare devices before power is cut. The result may be filesystem corruption or a device landing in an undefined state after the next power-on.
Second, it’s not portable. Which GPIO pin, which register, which EC command relates to power is board-specific and silicon-specific. Hardcoding this in an SMM handler ties the firmware to a specific platform.
Third, it’s wrong ownership. SMM exists to handle small firmware services, protect resources, and enforce security policy. It’s not meant to replace the OS as the overall power manager. In a normal shutdown, the OS/OSPM is the one coordinating the power transition.
In a normal ACPI flow, firmware doesn’t decide to power down on its own once the OS is running. Firmware provides ACPI tables — including FADT, DSDT/SSDT, and _S5. The OS/OSPM reads this, prepares devices, flushes the filesystem, calls the necessary ACPI methods, and then requests the chipset to enter S5 by writing SLP_TYP and SLP_EN.
In other words: firmware writes the contract; the OS reads the contract and controls the power transition.
Publish ACPI tables
FADT, DSDT/SSDT, _S5, _PTS, _WAK, and device methods
Read ACPI
OS understands devices, resources, wake sources, and sleep states
Prepare power transition
Flush I/O, notify drivers, run required ACPI methods
SLP_TYP + SLP_EN
OS requests the chipset to enter sleep/off state per the ACPI spec
Power transition
Platform transitions to S5 following the proper ACPI flow
If this were a genuine thermal emergency, the platform typically has its own hardware protection path — EC, PMIC, chipset thermal trip, or board-specific safety logic. But that’s an emergency protection mechanism, not a normal OS-controlled shutdown flow. These two should not be mixed.
What ACPI is: a firmware engineer’s view
ACPI (Advanced Configuration and Power Interface) is not just “a config table.” It has two main parts.
1. Static tables — data firmware publishes at boot time.
2. AML code — bytecode that an OS interpreter executes at runtime when it needs to ask the platform “what should I do now?”
This is what many people miss: ACPI is not read once at boot and forgotten. The OS continuously calls ACPI methods throughout its lifetime — when a device is probed, when the system goes to sleep, when a device loses power, when the user presses the power button.
ACPI table structure
Firmware publishes a tree of tables. The OS finds its entry point through the RSDP (Root System Description Pointer) and from there walks to the other tables:
RSDP → XSDT
├─ FADT → DSDT (main AML)
├─ SSDT (supplemental AML)
├─ MADT (interrupt controllers)
├─ MCFG (PCIe ECAM base)
└─ ... (other platform-specific tables)
The most important tables for a firmware engineer:
| Table | Full name | Debug relevance |
|---|---|---|
| FADT | Fixed ACPI Description Table | Contains PM register info, ACPI flags, and the pointer to DSDT |
| DSDT | Differentiated System Description Table | Main AML for the platform. Contains the device namespace and control methods like _STA, _CRS, _PS0, _PRW |
| SSDT | Secondary System Description Table | Supplements the namespace by CPU, device, SKU, or Setup option |
| MADT | Multiple APIC Description Table | Describes APIC and interrupt routing. Errors here often cause SMP or interrupt failures |
| MCFG | PCI ECAM Table | Tells the OS the base address for accessing PCI config space via ECAM |
AML: the language that runs inside the OS
The most interesting — and most complex — part of ACPI is AML, ACPI Machine Language.
Firmware engineers write ASL (ACPI Source Language). The compiler translates ASL to AML binary, which is embedded in DSDT/SSDT. The OS has an interpreter that executes this AML at runtime.
// ASL source — this is what the firmware engineer writes
Device (PWRB) { // Power Button device
Name (_HID, "PNP0C0C")
Method (_STA, 0) {
Return (0x0F) // Present, enabled, visible, functioning
}
}
Device (SLPB) { // Sleep Button
Name (_HID, "PNP0C0E")
Method (_PRW, 0) { // Power Resource for Wake
Return (Package() { 0x1D, 0x04 }) // GPE 0x1D, wakes from S4
}
}
When the OS wants to know if the power button is functional, it calls _STA. When it wants to know whether the sleep button can wake from S4, it calls _PRW. Firmware doesn’t need to know when the OS will ask — it just needs to implement these methods correctly.
Power states: ACPI defines them, the OS decides, firmware describes
ACPI defines Global System States from S0 to S5:
| State | Meaning | Debug notes |
|---|---|---|
| S0 | Working — system is running | S0ix / Modern Standby also lives within S0 |
| S3 | Suspend to RAM | DRAM holds data, devices/rails off, fast resume |
| S4 | Hibernate | State saved to storage, system nearly fully off |
| S5 | Soft Off | Soft power-off, only wake logic and power button remain |
| G3 | Mechanical Off | Complete power loss — not an S-state in the same sense as S0-S5 |
When the OS wants to sleep — for example, entering S3 — it doesn’t just turn off the display. It needs a preparation sequence:
- Call
_PTS(Prepare To Sleep) — firmware prepares the platform - Power down devices one by one via each device’s
_PS3 - Call methods to disable power resources if the platform describes them
- Write the PM Control Register to put the chipset into the sleep state
- When a wake event occurs, firmware runs part of the startup sequence again
- OS calls
_WAK— firmware restores required state - Devices are powered on again via
_PS0
This is why firmware shouldn’t “directly power off via a single GPIO” in a normal flow. Each step in the ACPI power transition exists for a reason. Skipping _PS3 for a device can leave it in an undefined state after the next power-on. Skipping _PTS means the EC may not be notified about the upcoming sleep state.
Where firmware writes ACPI in the boot flow
ACPI tables are created and published in the DXE phase, before BDS loads the OS:
Platform DXE module
Reads Setup options, board config, SKU, and policy
Patch DSDT/SSDT
Creates or modifies ACPI tables based on actual config
InstallAcpiTable()
Calls EFI_ACPI_TABLE_PROTOCOL->InstallAcpiTable()
Load OS
BDS loads the OS loader
Read ACPI
OS reads tables via RSDP and interprets AML
Important: ACPI tables reflect Setup options. If the user disables a feature in BIOS Setup, firmware can patch that device’s _STA method to return 0x00, making the device disappear from the OS even though the hardware is still physically present on the board.
This is the source of many hard-to-explain bugs: a device doesn’t appear in Device Manager even though it has power when measured. It’s not an OS driver problem — _STA is returning 0x00 because some Setup option has disabled it.
Real-world scenario: sleep/wake broken after a BIOS update
A very common pattern when working on BIOS:
Symptom: machine sleeps fine but after resume touchpad/USB is missing
The right debug approach is not reinstalling OS drivers. Follow ACPI.
Step 1: Identify which device is missing after resume
Device Manager, or lspci, lsusb, dmesg on Linux can show which device has a warning or didn’t come back after resume.
Step 2: Dump ACPI tables
# Linux
sudo acpidump -o acpi.dat
acpixtract acpi.dat
iasl -d DSDT.dat SSDT*.dat
Step 3: Find the methods related to that device
Search for _PS0 and _PS3 for the device in the decompiled AML. Check:
- Power sequence: rail → delay → clock → reset, in the right order
- GPIO state restored correctly after resume
- EC commands sent back after resume
Step 4: Check _PRW for the device
// Incorrect _PRW can cause instant wake
Method (_PRW, 0) {
Return (Package() { 0x1D, 0x03 }) // GPE, sleep state
}
If the GPE in _PRW is already active before the system goes to sleep, the machine will wake up immediately after just entering sleep.
Sleep/wake and ACPI debug checklist
SMM and ACPI: two separate layers, not competitors
To close the original question: SMM and ACPI don’t compete. They have different roles.
SMM handles small, isolated firmware services, protects resources, and processes events the OS shouldn’t know about. It runs fast and should not take on large responsibilities like managing the normal power state.
ACPI is the contract between firmware and the OS covering all hardware description and power management. Everything related to power states, device enumeration, and sleep/wake has to go through the ACPI model.
In a normal shutdown, the OS/OSPM reads _S5, prepares the system, and requests the chipset to enter S5 via SLP_TYP and SLP_EN.
For a genuine thermal emergency, the platform typically has its own protection path: EC, PMIC, chipset thermal trip, or board-specific safety logic. That is an emergency protection mechanism — not the normal ACPI shutdown flow.
Summary
- ACPI is the contract firmware writes and the OS reads, describing hardware, power states, and control methods
- AML is the bytecode in DSDT/SSDT; the OS interpreter executes it at runtime when needed
- Power states (S0-S5) are defined by ACPI, decided by the OS, and described and supported by firmware via tables and methods
- Firmware shouldn’t toggle a GPIO to power off because it bypasses the power sequence, isn’t portable, and violates ownership in a normal flow
- Debug device disappearance or broken sleep/wake by starting with ACPI: dump tables, decompile AML, check methods
References
- ACPI Specification, uefi.org — official ACPI spec, free download
- iasl compiler, acpica.org — tool for decompiling AML to ASL, cross-platform
- ACPICA Project, GitHub — reference interpreter and tools, open source
- EDK II ACPI modules, GitHub — ACPI DXE driver reference
Read next
- DXE Phase: Drivers, Protocols, and reading logs when nothing runs
- SMM in UEFI: System Management Mode from a debug perspective
- What is SMM?
- What is an SMI?
Found this useful?
Save it or share it with someone learning firmware, BIOS/UEFI, and embedded systems.
Nội dung liên quan
Một số bài viết, ghi chú hoặc project có liên quan đến nội dung bạn vừa đọc.
ACPI Architecture Overview
How firmware describes platform hardware to the operating system through ACPI tables, AML, namespace objects, and device methods.
DSDT vs SSDT for Firmware Engineers
A practical explanation of why platforms use both DSDT and SSDT, how they are loaded, and how to debug table layering issues.
How are DSDT vs SSDT different?
Quick note explaining DSDT vs SSDT for BIOS/UEFI and embedded firmware readers.
Đọc thêm về BIOS/UEFI
Khám phá các bài viết về BIOS/UEFI, embedded firmware, debugging và system-level thinking.