Clone this wiki locally

ACPI is an abbreviation of Advanced Configuration and Power Interface. The standard can be found at http://acpi.info.

ACPI is defining states for the whole system and for each component independently.

System states are:

G0 (S0) : system fully ON.

G1 : sleeping. Several sub-levels are defined here:

S1 : power on suspend.

S2 : suspend to RAM

S3 : standby

S4 : hibernate

G2 (S5) : Soft OFF. System still listening to some events (keyboard, USB, network) in order to start if received.

G3 (S6) : Mechanical OFF. Only CMOS is running.

Device states are:

D0 : ON.

D1, D2 : intermediate, depend on device. May not be present.

D3 : OFF.

When a device is in the D0 state, it could exist Performance states for it, starting from P0 (fully powered) and until P16 when accurate.

There are also CPU states, but they're not interesting for us.

The language used to manage ACPI is called ASL (ACPI Source Language). This language is, as far as we know, only described by the ACPI specs. However, as Intel gives out the code of the interpreter/compiler, we maybe be able to understand the language this way.

The language is compiled into AML (ACPI Machine Language) in a lot of tables, some of them are specific to the computer. List of tables :

RSDP (Root System Description Pointer)

RSDT (Root System Description Table)

DSDT (Differentiated System Description Table). This table contains the primary AML code for the system.

Integer : didn't determined size actually, will do that ASAP, they are something like 0xFFFFFFFFFFF.

Bin : 0x00, 0x01, but also found as Zero, One.

Function : a suit of instructions

String : never seen any as of today

All of those things are having a name of maximum four latin caracters or _ (e.g. FUNC, VAR, but also _VAR, ...).

Naming

I (Lekensteyn) could not find a good reference for the names used for the identifiers and was especially wondering what PEGP stands for. I've found a reference to PEG in the Intel documentation on the i7 processor, and from that, I've guessed the name below.

Extracting each part from \_SB_.PCI0.P0P2.PEGP._STA:

\_SB - System bus tree (Package)

PCI0 - PCI Bus (Bus Object)

P0P2 - Device (note that it's a P - Zero - P and not P - O - P.

PEGP - PCI Express Graphics Port (guessed)

_STA - a method for checking whether a device is enabled or not

Methods

_REG - initialize all Operation Regions

_STA, _INI - initialize Device, Processor and Thermal objects. If there is no _INI method, _STA won't be considered. _STA returns One if a device is enabled and Zero otherwise. If this method is not found on a device, it's assumed to be powered on.

ACPI and the nvidia card

What is interesting us here is theorically DSDT and SSDTs (there may be more than one SSDT table for a single computer), so we already started gathering those.

For now, we never see any card supporting D1 and D2. So, our goal here is to be able to switch between D0 and D3.

After analysing tons of tables, we accumulated enough knowledge to know how to use _DSM calls properly for quite every laptops, even Legacy Optimus ones.

If you're interested in more details, here is what we've seen (for Optimus laptops, Legacy one are a bit different) :

_ON and _OFF funcs should never be called as standalone. Some computers are having a better behavior here by integrating them at the right place directly or by checking some values before running them. We will call those two functions level 1 ones.

What we need to call is _PS0 and _PS3. However, once again, these shouldn't be called as standalone. However, they look to have better stability/security mecanisms. We will call those functions level 2 ones. By the way, calling _PS3 requires to call _DSM first with the following args : {0xF8,0xD8,0x86,0xA4,0xDA,0x0B,0x1B,0x47,0xA7,0x2B,0x60,0x42,0xA6,0xB5,0xBE,0xE0} 0x100 0x1A {0x1,0x0,0x0,0x3}.

_PS0 and _PS3 should be called using _DSM if needed with accurate args. However, Lekensteyn found a pci function that appears to be doing quite the same, i.e. that seems to disable the card with the same effectiveness. However, it can't wake up a card put in _PS3 via acpi_call.

Additional informations : they seem to be some special interfaces to use this function for Windows, often called WMMX in Asus laptops tables, and NVOP/NV3D in other ones. Their behaviors are hard to understand, and they're not easy to reverse, but they seems to build the proper args to call _DSM for Optimus needs. We're not sure that it won't be a waste of time to look further in this direction, and as we got everything working, we stopped our investigations here.

Important informations when playing with ACPI

You must never try to set a device to a state it is already in !!! Normally, if you're using proper calls, they're providing enough security to avoid problems. But if it's not the case (_ON or _OFF for example), Asking a card to go to D3 when already in could lead to crash, unability to use the card, ...
-It is needed on the card before switching off because it can leads to some problems else. Before, that was also needed before going to sleep (the computer, not you :P), but thanks to Lekensteyn and his work in bbswitch, that isn't anymore needed.

Tools

A proof of concept for using ACPI easily has been developped by @mkottman, it's called acpi_call and the latest source is available in our acpi_call repo. Please note that even if some improvements were made by Lekensteyn, that's still a proof of concept and you can really break your computer if playing dangerously with it.

ACPICA provides nice tools for working with this ACPI stuff. You may already have used iasl or acpixtract, but acpiexec is another useful tool. Download the source from https://acpica.org/downloads, extract it and run:

cd acpica-unix-*/tools/acpiexec
make

This will create the acpiexec executable in the current directory. If you get an error like the below, edit the Makefile file and remove the -Werror flag from CWARNINGFLAGS.

../../os_specific/service_layers/osunixxf.c:352:29: error: variable 'Count' set but not used [-Werror=unused-but-set-variable]

If you get the below error, edit the Makefile file and change LDFLAGS += -lpthread to LDFLAGS += -pthread.

acpiexec can be used for quickly getting all available methods, run acpiexec file.dat where file.dat is a DSDT or SSDT table generated by acpixtract. Do not pass the disassembled file from iasl, that won't work. After running something like acpiexec DSDT.dsl, you can view all methods by running the methods command. Run help in the acpiexec shell for more options.

After resume, this is gone and the regular information is shown instead. With one big difference: the drivers won't load / X won't start. The nouveau will complain with:

[drm] nouveau 0000:01:00.0: Unsupported chipset 0xffffffff

This is caused by the fact that before suspend, the kernel saves the configuration space. If the card is disabled, the returned configuration space will contain enabled bits only (all 1's, hexadecimal FF...). On resume, the card is enabled (D0 state) and this configuration space is restored. Since the card is enabled, the configuration space is meaningful, but overwritten by junk data.
After restoring the correct configuration space (which would be performed by rebooting as well), the driver would load again. This space can be written in /proc/bus/pci/01/00.0 assuming PCI Bus ID 01.00.0.

Consider the below side-by-side diff of the configuration space. On the left side, the card was on. The right side shows the state when resuming after the card was off.