ACPI Power States: G, S, C, P, and D States
The Advanced Configuration and Power Interface (ACPI) is the firmware-defined standard that gives an operating system a uniform vocabulary for power management on x86 (and on ARM servers that ship ACPI tables). It defines five families of power state, each scoped to a different unit of hardware: G-states describe the whole machine globally (G0 working through G3 mechanical off); S-states are the system sleeping states (S0 working through S5 soft off) that live inside the global G1 sleeping state; C-states describe how deeply a single processor core is idle (C0 executing, C1…Cn progressively asleep); P-states describe a performance/voltage-frequency operating point while the core is awake in C0; and D-states describe an individual device’s power (D0 fully on through D3cold fully off). Linux does not invent its own state model — its power subsystems are thin firmware-neutral abstractions over exactly these families: cpuidle selects C-states, cpufreq selects P-states, the suspend code enters S-states, and runtime PM drives D-states. ACPI is the x86 firmware that names and enumerates these states; on ARM the same hardware states are described by Device Tree or by ACPI tables instead.
Uncertain
Verify: the precise normative wording of the G-state and D-state definitions against the ACPI specification itself. Reason: the authoritative source — the ACPI 6.5 specification on
uefi.org(both/specs/ACPI/6.5/and the older/htmlspecs/HTML, and the PDF) — is behind a Cloudflare bot-challenge that blocked every automated fetch (HTTP 403 / “Just a moment…” interstitial) during this research. The state names, numbering, and Linux consumption below are pinned to Linux v6.12 kernel source (verified) and the kernel’s own admin-guide docs; the human-readable G/S/D descriptions are corroborated from Wikipedia’s ACPI article rather than the spec text. To resolve: fetch the ACPI 6.5 spec (uefi.org, chapters 2 “Definition of Terms” and 16 “Waking and Sleeping”) through a browser session that clears the challenge, and reconcile the exact wording. uncertain
Mental Model: Five Scopes, One Tree
The trick to keeping the families straight is to notice that each family answers “what is the power state of a different thing,” and the things nest. The whole machine is in exactly one G-state. When the machine is working (G0) every core is in some C-state and, if that C-state is C0 (executing), at some P-state; meanwhile every device is independently in some D-state. When the machine is sleeping (the global G1), it is in one of the S-states (S1–S4), and the C/P/D distinctions for individual components are subsumed into the system-wide sleep. The two “off” globals — G2 and G3 — correspond to the deepest S-states (S5 and effectively “no S-state, mechanical off”). The families are therefore not parallel alternatives; they are statements about hardware at different granularities, some of which only apply inside particular global states.
flowchart TB subgraph G0["G0 — Working (machine runs user space)"] direction TB CORE["Each CPU core: a C-state"] C0["C0 = executing → has a P-state (P0…Pn)"] CN["C1…Cn = idle (deeper = lower power, higher exit latency)"] DEV["Each device: a D-state (D0…D3cold)"] CORE --> C0 CORE --> CN end subgraph G1["G1 — Sleeping (no user space)"] direction TB S1["S1 / S2 / S3 (suspend-to-RAM) / S4 (hibernate)"] end G2["G2 — Soft Off (≈ S5)"] G3["G3 — Mechanical Off (power removed; RTC only)"] G0 -->|"suspend"| G1 G1 -->|"resume (wake event)"| G0 G0 -->|"shutdown"| G2 G2 -->|"power switch"| G3
The ACPI state families and how they nest. What it shows: the global G-state is the outermost scope; C/P/D states only have meaning inside G0 (working), while S1–S4 are the substates of G1 (sleeping). The insight to take: C-states, P-states, and D-states are not “lighter sleeps than S-states” — they are orthogonal, finer-grained statements about cores, performance, and devices that coexist while the machine is fully awake; the S-states are the only ones that suspend the machine as a whole.
Walking Each Family Symbol by Symbol
G-states — global system states
The G-states (also written Gx) describe the machine as a whole. There are four, and they form the coarsest possible power ladder (Wikipedia: ACPI):
- G0 — Working. “The computer is running and the CPU executes instructions.” This is the only state in which user space runs; it is the home of all working-state power management (cpufreq, cpuidle, runtime PM). Every C/P/D-state below exists inside G0.
- G1 — Sleeping. An umbrella for the sleeping substates S1–S4. No instructions execute; the machine retains enough state (in RAM or on disk) to resume to G0 without a full reboot. This is where Linux’s system sleep lives.
- G2 — Soft Off. The machine is powered down but the power supply still provides standby power so that a wake event (a power button, a network packet) can turn it back on. ACPI equates G2 with the S5 “soft off” sleep state. Resuming from G2 is a full boot, not a resume.
- G3 — Mechanical Off. “The computer’s power has been totally removed via a mechanical switch”; only the real-time clock keeps running off its battery. No wake event short of physically powering on is possible.
The kernel does not have a single “G-state” variable — G0 is “running normally,” G1 corresponds to the suspend/hibernate code being active, and G2/G3 are reached by the normal shutdown/poweroff path, not by the PM core. The G-states matter mostly as the containing scope that decides which finer states are meaningful.
S-states — system sleeping states (inside G1)
The S-states number S0 through S5. S0 is the working state (equivalent to G0); S1–S4 are the genuine sleep states under G1; S5 is soft-off (G2). The progression is, again, deeper savings for longer resume (Wikipedia: ACPI; cross-checked against the kernel sleep-states doc):
- S0 — Working. Full operation. Modern platforms add S0ix (“Low-Power S0 Idle,” marketed as Modern Standby / connected standby) — a working-state idle so deep it substitutes for S3 on hardware that no longer implements firmware S3. Note S0ix is reached by suspend-to-idle, which is software-driven and stays in S0.
- S1 — Power-on Suspend. CPU stops executing; CPU caches and RAM stay powered. Lowest-latency true sleep, smallest savings. Rare in practice.
- S2 — deeper than S1. CPU powered off, caches flushed to RAM. Rarely implemented.
- S3 — Suspend-to-RAM. “RAM remains powered” in self-refresh; nearly everything else is off. The platform firmware bootstraps the CPU back on resume. This is what Linux calls the
deepmem-sleep mode — see Suspend-to-RAM S3 Deep. - S4 — Suspend-to-Disk (Hibernation). “All content of the main memory is saved to non-volatile memory” and the machine powers off; on resume the firmware boots and the OS reloads the image. Linux’s hibernation implements the concept of S4 largely in software (it can hibernate even without firmware S4 support, because it writes the image itself).
- S5 — Soft Off. Equivalent to G2.
Linux’s mapping is explicit. In include/linux/suspend.h (v6.12) the suspend enum is PM_SUSPEND_ON = 0, PM_SUSPEND_TO_IDLE = 1, PM_SUSPEND_STANDBY = 2, PM_SUSPEND_MEM = 3 (suspend.h v6.12). PM_SUSPEND_MEM is the one that maps to ACPI S3 when the platform’s suspend_ops supports it; PM_SUSPEND_STANDBY corresponds to ACPI S1; PM_SUSPEND_TO_IDLE is the firmware-independent s2idle that stays in S0/S0ix; and hibernation (S4) is a separate code path entirely.
C-states — processor idle states (inside G0)
C-states describe how deeply a single logical CPU is idle. They only have meaning in G0/S0. C0 is the active state where the core executes instructions; C1 through Cn are progressively deeper idle states where the core consumes less power but takes longer to resume. Per the ACPI processor chapter, “Each processor sleeping state has a latency associated with entering and exiting that corresponds to the power savings” — the deeper the state, the higher the entry/exit latency and the greater the saving (ACPI 6.5 Ch. 8, via search summary; see the uncertainty flag above regarding spec access). The canonical three are:
- C1 — Halt. The CPU stops fetching instructions (typically via the
HLTinstruction or an architectural idle instruction); near-instant resume. Mandatory for every ACPI-compliant processor. - C2 — Stop-Clock. Software-visible CPU state is retained; longer exit latency than C1. Optional.
- C3 — Sleep / “Deep Sleep.” The CPU need not keep its caches coherent; deeper savings, longer exit latency. Optional. Vendors expose many sub-variants (C6, C7, package C-states, etc.) all enumerated through the same mechanism.
The firmware describes available C-states to the OS via the _CST (“C-States”) control method on each processor object. Linux’s ACPI processor-idle driver consumes them in drivers/acpi/processor_idle.c (v6.12). It evaluates _CST (acpi_processor_evaluate_cst()), falling back to the FADT-defined states if _CST is absent, and stores each entry in a struct acpi_processor_cx carrying type (C1/C2/C3), address (the I/O port or MWAIT hint), entry_method, and latency (processor_idle.c v6.12). It then translates each verified _CST row into a cpuidle state in acpi_processor_setup_cstates():
state = &drv->states[count];
state->exit_latency = cx->latency; /* µs, from _CST */
state->target_residency = cx->latency * latency_factor; /* default factor 2 */
state->enter = acpi_idle_enter;So the _CST-reported latency becomes cpuidle’s exit latency, and target residency is derived from it (default latency × 2). The actual entry mechanism depends on the _CST entry method, dispatched in acpi_idle_do_entry(): ACPI_CSTATE_FFH (“Fixed Function Hardware,” i.e. the MWAIT instruction) → acpi_processor_ffh_cstate_enter(); ACPI_CSTATE_HALT → acpi_safe_halt(); otherwise an I/O port read at cx->address (processor_idle.c v6.12). This is the concrete bridge from ACPI firmware tables to the kernel’s idle loop. See Processor C-States for the cpuidle side in depth.
P-states — performance states (inside C0)
P-states only have meaning while a core is awake in C0. They are operating points — a (frequency, voltage) pair — that trade CPU capacity for power: “While in the C0 state, ACPI allows the performance of the processor to be altered … through transitions into multiple performance states (P-states)” (ACPI 6.5 Ch. 8). P0 is the highest performance (and power); P1…Pn step down, with a spec maximum of 16 states per device. P-states are the target of cpufreq / DVFS.
The firmware advertises the available P-states via the _PSS (“Performance Supported States”) control method, with _PCT describing the control/status register pair and _PPC an upper limit imposed at runtime. Linux reads them in drivers/acpi/processor_perflib.c (v6.12): acpi_processor_get_performance_states() evaluates _PSS and unpacks each six-field package row into a struct acpi_processor_px:
status = acpi_evaluate_object(pr->handle, "_PSS", NULL, &buffer);
/* each row, format "NNNNNN": */
/* core_frequency (MHz) */
/* power (mW) */
/* transition_latency (µs) */
/* bus_master_latency (µs) */
/* control (value written to the control reg) */
/* status (value read back from the status reg) */acpi_processor_get_performance_info() orchestrates this: it checks _PCT, reads the control-register locations, populates the states from _PSS, and applies the firmware platform limit from _PPC (processor_perflib.c v6.12). Note the driver also includes amd_fixup_frequency() to correct AMD frequency values that some BIOSes round incorrectly — a small reminder that firmware tables are not always trustworthy. On Intel and AMD, the generic _PSS path is frequently bypassed by intel_pstate / amd_pstate, which talk to model-specific registers and hardware-managed P-state (HWP/CPPC) interfaces directly.
D-states — device power states (inside G0)
D-states describe an individual device’s power, independent of the CPU’s C/P-state. There are five (device_pm.c v6.12; Wikipedia: ACPI):
- D0 — Fully On. The device is operational and fully powered.
- D1, D2 — Intermediate. Device-class-dependent low-power states; many devices implement neither.
- D3hot — Off, bus still powered. The device is in a low-power state but remains addressable on its bus (its configuration space can still be read); it retains auxiliary power and can still signal wake.
- D3cold — Off, bus power removed. “Device powered off and unresponsive to its bus.” The deepest device state; the device must be re-initialized on the way back up.
Linux’s ACPI device-PM layer maps these in drivers/acpi/device_pm.c (v6.12) with constants ACPI_STATE_D0, ACPI_STATE_D1, ACPI_STATE_D2, ACPI_STATE_D3_HOT, ACPI_STATE_D3_COLD, stringified by acpi_power_state_string(). To drive a device into a D-state, acpi_device_set_power() evaluates the per-device _PSx control method (_PS0/_PS1/_PS2/_PS3) — acpi_dev_pm_explicit_set() builds the method name dynamically — and toggles the device’s power resources (the shared on/off rails declared by _PR0…_PR3). For a transition to D0, power resources are turned on first and then _PS0 runs; for a transition to a deeper state, _PSx runs and then unused power resources are turned off (device_pm.c v6.12). The current state is read back via the _PSC (“Power State Current”) method. These are exactly the operations runtime PM and system suspend invoke through the device’s dev_pm_ops when ACPI is the device’s power-management authority.
How Linux Subsystems Map onto ACPI States
Putting the families together, the correspondence is one-to-one and is the organizing principle of the whole Linux PM subsystem:
| ACPI family | Scope | Linux subsystem | Firmware method |
|---|---|---|---|
| C-states | one CPU core, idle | cpuidle → Processor C-States | _CST |
| P-states | one core, in C0 | cpufreq / DVFS | _PSS / _PCT / _PPC |
| S-states | whole system, sleeping (G1) | system suspend / hibernate | \_Sx (e.g. _S3) |
| D-states | one device | runtime PM / system suspend | _PSx / _PRx / _PSC |
| G-states | whole machine, globally | (containing scope; G2/G3 via shutdown path) | — |
The crucial point for a Linux reader is that the kernel abstractions are firmware-neutral. cpuidle, cpufreq, genpd, and the suspend core do not contain ACPI logic; ACPI is one backend that feeds them state tables. On an ARM platform the same physical C-state-like idle states and D-state-like device power are described by Device Tree (idle-states, power-domains, regulator/clock bindings) or by ACPI tables on ARM servers — see ACPI Device Enumeration for how each firmware enumerates devices and their power resources, and Linux Device Drivers and Device Model MOC for the device model these tables populate.
Failure Modes and Common Misunderstandings
- Treating C-states and S-states as one ladder. A frequent confusion is “C6 is deeper than S3.” They are different scopes: C-states are per-core idle states while the machine runs (G0/S0); S3 suspends the whole machine (G1). A core in C6 still belongs to a fully-working system; S3 is the whole system asleep. They are not comparable points on a single scale.
- Expecting D3hot to cut all power. D3hot leaves the bus powered and the device addressable — it is not fully off. Only D3cold removes bus power, and re-entering D0 from D3cold requires full re-initialization, which can fail in drivers that assumed D3hot semantics (state silently lost).
- Assuming
_PSSfrequencies are exact. Asamd_fixup_frequency()in the kernel shows, firmware P-state tables can carry rounded or wrong frequency values; trusting_PSSblindly leads to mis-reportedscaling_cur_freq. Theintel_pstate/amd_pstatedrivers exist partly to bypass these tables. - Believing S3 still exists everywhere. Many post-2019 x86 laptops ship firmware that omits a working S3 in favor of S0ix/Modern Standby. The OS sees only s2idle for “suspend”; “sleep drains the battery” then becomes a working-state idle (failure to reach deep package C-states / s0ix) problem, not an S3 problem. See the discussion in Power Management Strategies Working State vs Sleep State.
Production Notes
The ACPI tables a machine ships with are routinely the root cause of Linux PM bugs, because the kernel can only manage the states the firmware declares. A laptop that “won’t reach C8” usually has a _CST that stops at C3, or a BIOS option that hides deeper states; an Intel server stuck at base frequency may have an empty or limited _PSS with intel_pstate falling back to ACPI mode. Diagnostics therefore start at the firmware boundary: cpupower idle-info and cpupower frequency-info show what cpuidle/cpufreq derived from _CST/_PSS; /sys/firmware/acpi/tables/ exposes the raw tables (decode with acpidump/iasl); and turbostat shows the achieved package C-state and P-state residency, which is the ground truth that reveals when firmware-declared states are never actually entered. The recurring lesson is that ACPI is a contract written by firmware, and Linux’s job is to honor whatever (sometimes buggy, sometimes incomplete) contract it was handed — which is exactly why amd_fixup_frequency() and the long list of ACPI quirks exist in the tree.
See Also
- Power Management Strategies Working State vs Sleep State — how the working-state/sleep-state split maps onto these state families
- Processor C-States — the cpuidle side of C-states in depth
- Linux System Sleep States — the kernel’s S-state implementation (s2idle / S3 / hibernate)
- The cpufreq Subsystem — the kernel’s P-state / DVFS layer
- ACPI Device Enumeration — how ACPI enumerates devices and their power resources
- Runtime Power Management — drives device D-states while the system runs
- Linux Power Management MOC — the parent map of this subsystem