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.

Updated 11 min read
Đọc bằng English Tiếng Việt 日本語
BIOS / UEFI cover

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.

01 DXE

Firmware initializes hardware

Firmware configures chipset, devices, policy, and platform resources

02 ACPI

Publish ACPI tables

Firmware installs DSDT, SSDT, FADT, MADT, and related tables

03 EBS

ExitBootServices

Firmware hands over boot-time control to the OS

04 OS

OS reads ACPI

OS walks from RSDP, reads DSDT/SSDT, interprets AML

05 Runtime

OS calls ACPI methods

Device probing, sleep, wake, power button all go through the ACPI model

ACPI is the contract: firmware publishes it, the OS reads and executes AML methods when needed

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.

01 Firmware

Publish ACPI tables

FADT, DSDT/SSDT, _S5, _PTS, _WAK, and device methods

02 OS/OSPM

Read ACPI

OS understands devices, resources, wake sources, and sleep states

03 Prepare

Prepare power transition

Flush I/O, notify drivers, run required ACPI methods

04 PM Control

SLP_TYP + SLP_EN

OS requests the chipset to enter sleep/off state per the ACPI spec

05 Chipset

Power transition

Platform transitions to S5 following the proper ACPI flow

Normal ACPI shutdown: firmware describes it, OS decides and requests the power transition

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:

Key ACPI tables
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:

ACPI Global System States
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:

  1. Call _PTS (Prepare To Sleep) — firmware prepares the platform
  2. Power down devices one by one via each device’s _PS3
  3. Call methods to disable power resources if the platform describes them
  4. Write the PM Control Register to put the chipset into the sleep state
  5. When a wake event occurs, firmware runs part of the startup sequence again
  6. OS calls _WAK — firmware restores required state
  7. 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:

01 DXE

Platform DXE module

Reads Setup options, board config, SKU, and policy

02 Patch

Patch DSDT/SSDT

Creates or modifies ACPI tables based on actual config

03 Install

InstallAcpiTable()

Calls EFI_ACPI_TABLE_PROTOCOL->InstallAcpiTable()

04 BDS

Load OS

BDS loads the OS loader

05 OS

Read ACPI

OS reads tables via RSDP and interprets AML

ACPI tables are created/published by firmware in DXE before the OS runs

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

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.

Đọ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.