The wiki is being retired!

Documentation is now handled by the same processes we use for code: Add something to the Documentation/ directory in the coreboot repo, and it will be rendered to Contributions welcome!

This page contains some (developer) documentation about how ACPI can be used in coreboot.


Please have a look at the files in src/mainboard/asus/a8v-e_se. Please also check out, which contains the ACPI specification.

Set up hardware

Set the PMIO base address to some known address, and set up the desired ACPI IRQ (usually IRQ9; sometimes it is called the SCI interrupt).

Fixed ACPI Description Table (FADT)

You may skip this section if your SB has it already. Just call it from your MB ACPI setup code (check M2V-MX SE for details).

Now you will need to create an ACPI table which describes the I/O port location for kernel ACPI implementation. This is the FACP table. You will need to create the fadt.c file and fill in the I/O port values plus IRQ:

fadt->sci_int = 9;
fadt->pm1a_evt_blk = VT8237R_ACPI_IO_BASE;
fadt->pm1b_evt_blk = 0x0;
fadt->pm1a_cnt_blk = VT8237R_ACPI_IO_BASE + 0x4;
fadt->pm1b_cnt_blk = 0x0;
fadt->pm2_cnt_blk = 0x0;
fadt->pm_tmr_blk = VT8237R_ACPI_IO_BASE + 0x8;
fadt->gpe0_blk = VT8237R_ACPI_IO_BASE + 0x20;
fadt->gpe1_blk = 0x0;

In this example the ACPI IRQ is 9, and the PM1A event block starts at VT8237R_ACPI_IO_BASE. You may obtain some values from cat /proc/ioport if running with the proprietary BIOS. Not all blocks are necessary—usually only PM1A PMTMR and GPE0 are used. Please note that this table has the I/O port information stored twice using different formats. Please consult the ACPI specification for details. Most settings in fadt.c can use their default values.

If Linux complains about "IRQ 9 nobody cared", recheck these values. The gpeX_blk must span both status and enable bits (which is easy to get wrong given the unclear documentation on this). If GPE0_STS is 64bit, you have to configure 16 bytes (or 128 bits in the x_* variant).

ACPI spec says to set only one of FIRMWARE_CTRL and X_FIRMWARE_CTRL, which is different to how other x_* values are handled. Set firmware_ctrl if located at <4GB, x_firmware_ctrl if located at >=4GB, and set the other value to 0. It's generally more useful to set firmware_ctrl (to support 32bit operating systems).

Differentiated System Description Table (DSDT)

The DSDT table contains a bytecode that is executed by a driver in the kernel. This table stores also ACPI routing information in _PRT methods. You may add those _PRT methods later.

Generic part of DSDT

A very generic DSDT table would look similar to the ASUS A8V-E/ASUS M2V-MX SE src/mainboard/asus/a8v-e_se/dsdt.asl file.

        Scope (\_PR)
                Processor (\_PR.CPU0, 0x00, 0x000000, 0x00) {}
                Processor (\_PR.CPU1, 0x01, 0x000000, 0x00) {}

This is here for compatibility. More interesting is:

        /* For now only define 2 power states:
         *  - S0 which is fully on
         *  - S5 which is soft off
         * any others would involve declaring the wake up methods
        Name (\_S0, Package () {0x00, 0x00, 0x00, 0x00 })
        Name (\_S5, Package () {0x02, 0x02, 0x00, 0x00 })

This defines the SLP_TYP fields in PM1A register. In my case I need to store 010 to perform soft-off, and 000 to wake up. Modify it to fit your chipset needs.

Interrupt routing in DSDT

The _PRT methods define the routing, similar to PIR and MP Table.

     Package (0x04) { 0x000BFFFF, 0x00, 0x00, 0x10 }, //slot 0xB
     Package (0x04) { 0x000BFFFF, 0x01, 0x00, 0x11 },
     Package (0x04) { 0x000BFFFF, 0x02, 0x00, 0x12 },
     Package (0x04) { 0x000BFFFF, 0x03, 0x00, 0x13 },

This defines the slot 0xB (all functions FFFF) routing as follows:


Note: you cannot indicate the special functions like:

Package (0x04) { 0x000F0001, 0x00, 0x00, 0x14 }, // 0xf Native IDE IRQ 20

It means 0:0f.1 INTA is routed to IRQ20. Linux likes it. Windows does not (code 12). The ACPI standard requires that function is always 0xffff.

Please note that the 0x10, 0x11 are called GSI (global system interrupt). All your interrupts routed through first APIC will start with 0x00, second APIC will perhaps start at IRQ24 etc. This example has no support for legacy PIC routing. For PIC routing you would need to alter the rest of the fields in the _PRT package and also crete PIRQA-PIRQD special devices.

The described above uses static IRQ assignments. Some chipsets like MPC55/CK804 have a configuration register which indicates what APIC pins are routed to what interrupt. Those typically use dynamic IRQ routing which provides a _CRS and _SRS methods to set such registers. For now, those registers are filled in MP-Table setup of each MCP55/CK804 board. All you need is to have static wiring like the MP-Table has.

The rest of the file contains just some legacy devices to make certain OS installers happy. Don't forget to install the iasl compiler and also adjust the coreboot buildsystem to build the binary DSDT for you.

CPU Power Management

The CPU power management is hardware specific. It is described in ACPI specs and also in AMD BIOS and Kernel Developer guide. The rest of this section describes the AMD specific part. AMD needs ACPI objects which describe the similar info as the legacy PowerNow table. Check the BKDG for details.

The content of the tables must be generated at runtime, which is a bit of a problem, because the AML code must be generated or DSDT patched. There is an acpigen infrastructure to generate the AML code.

The actual content for family 0fh revF and later P-States can be generated by complex algorithm implemented in amd_model_fxx_generate_powernow(). This function should be called in acpi_fill_ssdt_generator() callback. Up to revE, all P state info must be hardcoded in tables (not supported).

C States

C states are processor power states. C1 is mandantory and reached on IA32 compatible processors using the HLT instruction, C2 and C3 are optional and must be configured.

C states can be configured in ACPI using two methods:

  1. by defining the P_BLK base address in the Processor() Definition, and P_LVLx_LAT values in the FADT
  2. using the _CST object

P_BLK is easier to configure, if the hardware supports that method. ACPI defines that there must be two registers at P_BLK+4 and P_BLK+5 that initiate a transition to C2 or C3 when the register is read. After sleep, the read returns 0. P_LVLx_LAT define the worst case latency of the state transition.

_CST is necessary if you want to support more than 3 C states, or if the transition procedure doesn't follow the ACPI requirement.

PCI root bus _CRS method

Windows needs to know the actual decode ranges for PCI root bus (and any other). Windows needs to know platform independent way, how is I/O routed on PCI0 bus (and other busses).

DSDT debugging

There are two ways. You can store values in "debug" object, which will print it in dmesg. Check how to turn that on. In DSDT use store method to write to Debug object. You can write buffers, ints etc:

* Store ("The answer to the question of life, the universe, and everything is:", Debug) 
* Store (42, Debug) 

Second method is userspace interpretation of DSDT table. This can be achieved with ACPI CA Unix package. It is located in acpica-unix-20081204/tools/acpiexec. You can eval the objects and run the methods, like _CRS for example.

If you receive a BSOD with STOP code 0xa5, check this:

Other tables

Rest of the ACPI tables is located at acpi_tables.c. I will describe briefly all methods:


If your platform supports MMCONFIG (memory mapped PCI configuration registers, aka extended PCIe configuration) just modify the function with correct base address.


A system with 8259s and APICs

This table describes the ACPI IRQ information, as well as IRQ override. For code example check the M2V-MX SE acpi_tables. You will need to create the sub-table for LAPIC (the APIC counterpart in CPU) and describe the APICs and also deal with so called IRQ overrides.

The interrupt sources are on the right side. The legacy IRQs and the PCI IRQs are connected to both APIC and 8259.

In the legacy case, the APIC is programmed in virtual wire mode. It will just interconnect pin0 of APIC with its output, bypassing APIC completely. OS uses 8259s, and ignores APICs at all.

The APIC should be in this mode in BIOS, to do that for your SB, check the setup_ioapic in vt8237r_lpc.c. Please note that there is some bit which also says if APIC is delivering through wires, or through FSB messages.

Last thing in this table are IRQ overrides. Usually there are two IRQ overrides. IRQ0 override means that IRQ0 is not connected to pin 0 on APIC but to another, most likely pin 2. Check the figure above why. Second IRQ override is for ACPI IRQ. This overrides the 'level' of the interrupt to 'active low'. The rest of the table is filled with NMI entries for the processor.


This is the main function which constructs the tables. Functions described above are callbacks from the "construct" functions called here. You may omit the HPET and MCFG tables.

FACS table

This table must be aligned to 64B boundary (Windows checks this).

Suspend to RAM

There are patches on the mailing list which add support for suspend to RAM in coreboot. The resume start of the computer does not differ until OS waking vector is executed instead of payload.

Checklist of things which needs to be setup correctly:

Interfacing mainboard and EC ASL

Coreboot has strict requirements for interfacing EC and mainboard ACPI files. This is done in order to minimize the dependency on the preprocessor, and to provide efficient bytecode execution. This specification is documented in detail on this page.

ACPI bytecode generator

Some ACPI stuff is generated runtime. To achieve this goal we have a AML code generator which generates binary ACPI bytecode. Such code then resides typically in SSDT table. There is a helper function which creates such a table - acpi_create_ssdt_generator(). The content of the table is created through the callback function acpi_fill_ssdt_generator(). So far we have two big users of the generator k8acpi_write_vars() and amd_model_fxx_generate_powernow(). The first function will generate some runtime configuration of HT bus and PCI decode ranges. Second function generates the P-States.

The available functions are in acpigen.h. Mostly there are functions generating some primitive named data structures. However sometimes it's necessary to put more data to a package. ACPI AML code needs to know the block lengths. The len is unknown until we have filled the payload of such package. Therefore when we are done, we need to call function acpigen_patch_len(int len) which will patch last object (package) which need patching. It uses stack internally so more structures can be nested. Look to acpigen.c and learn what functions call acpigen_write_len_f() - those needs patching.

Debugging ACPI

When CONFIG_ACPI_DEBUG is compiled into the kernel, the ACPI debug level can be specified on the kernel command line:

(warn, error und tables debug enabled)

The values can be checked at runtime:

# cat /sys/module/acpi/parameters/debug_level
Description                     Hex        SET 
ACPI_LV_ERROR                   0x00000001 [*]
ACPI_LV_WARN                    0x00000002 [*]
ACPI_LV_INIT                    0x00000004 [*]
ACPI_LV_DEBUG_OBJECT            0x00000008 [ ]
ACPI_LV_INFO                    0x00000010 [ ]
ACPI_LV_INIT_NAMES              0x00000020 [ ]
ACPI_LV_PARSE                   0x00000040 [ ]
ACPI_LV_LOAD                    0x00000080 [ ]
ACPI_LV_DISPATCH                0x00000100 [ ]
ACPI_LV_EXEC                    0x00000200 [ ]
ACPI_LV_NAMES                   0x00000400 [ ]
ACPI_LV_OPREGION                0x00000800 [ ]
ACPI_LV_BFIELD                  0x00001000 [ ]
ACPI_LV_TABLES                  0x00002000 [ ]
ACPI_LV_VALUES                  0x00004000 [ ]
ACPI_LV_OBJECTS                 0x00008000 [ ]
ACPI_LV_RESOURCES               0x00010000 [ ]
ACPI_LV_USER_REQUESTS           0x00020000 [ ]
ACPI_LV_PACKAGE                 0x00040000 [ ]
ACPI_LV_ALLOCATIONS             0x00100000 [ ]
ACPI_LV_FUNCTIONS               0x00200000 [ ]
ACPI_LV_OPTIMIZATIONS           0x00400000 [ ]
ACPI_LV_MUTEX                   0x01000000 [ ]
ACPI_LV_THREADS                 0x02000000 [ ]
ACPI_LV_IO                      0x04000000 [ ]
ACPI_LV_INTERRUPTS              0x08000000 [ ]
ACPI_LV_AML_DISASSEMBLE         0x10000000 [ ]
ACPI_LV_VERBOSE_INFO            0x20000000 [ ]
ACPI_LV_FULL_TABLES             0x40000000 [ ]
ACPI_LV_EVENTS                  0x80000000 [ ]
debug_level = 0x00000007 (* = enabled)

Random ACPI wisdom

Windows Errors

At first, not an error, but something to take note: Windows might cache system information and only detect ACPI changes if you modify the table versions. So tweak them liberally when debugging ACPI issues with Windows.

STOP 0xa5

A Blue Screen Of Death with STOP code 0x000000A5 is ACPI related, and it seems that Microsoft is very strict when it comes to ACPI compliance. explains some of the error codes, but not all of them.

The documentation of windbg has more detailed information about STOP 0xa5 than the MSDN article. STOP 0xa5 can be debugged by using checked builds of ntoskrnl and hal.dll and a second machine connected with a null-modem cable and windbg as kernel debugger.

"unexpected error" in Windows XP / Server 2003 setup


Windows XP or Server 2003 setup might fail with an error message such as:

   "An unexpected error (805262864) occurred at line 1768 of d:\xpclient\base\boot\setup\arcdisp.c"

The value 805262864 varies, and is the physical address, in decimal, of one of the ACPI tables.

The error message is displayed when a 1024 dword page table array used by setupldr runs out of space.

This table is used for mapping various physical addresses, such as those of ACPI tables (a separate table identity maps the lower 16MB used by setupldr code and data). Setupldr only looks at ACPI tables (FACP) to determine make and model of the system. The make and model of the system is needed when setupldr scans the good/bad bios lists contained in txtsetup.sif. The good/bad bios lists are used to bypass installation of the ACPI enabled kernel on certain systems known to have ACPI problems. The code loop that scans the lists creates a new mapping each time it reads an ACPI table, and never frees mappings. The code uses FACP OEM ID to determine the system model. The code sequentially reads tables listed in the RSDT array until the FACP is found. Each read consumes one page table entry. If more that 4 tables precede the FACP in the RSDT array, the 1024 entry page table array will run out of space before the good/bad bios list processing completes.

BIOS can work around this Windows XP/Server 2003 limitation by placing the FACP early in the RSDT array.

Other errors

Using checked builds

There's a faulty assert in acpi.sys that trips only on checked builds. Get rid of "else" statements in ASL code that the compiler can't optimize away (ie. "if () { Return Foo } else { Return Bar }" is changed to "if () { Return Foo } Return Bar" and thus won't trigger this assert). We primarily had that with acpigen generated ElseOps (since those see no optimizer pass).

Don't nest scopes improperly

Windows ACPI doesn't like

Scope(\foo) {
  Name(\ { ... }

Either make that

Scope(\foo) {
  Name (bar) { .. }

or eliminate the scope:

Name(\ { ... }

Removable Devices

Windows does not like to install on removable devices. And some devices (like eMMC) are assumed to be removable, unless you tell Windows they are not, by adding a node under your eMMC controller device providing an _RMV method:

 /* eMMC is non-removable device */
 Device (CARD)
    Name (_ADR, 0x00000008)
    Method(_RMV, 0x0, NotSerialized)
      Return (0)

Linux Errors

ACPI 2.0/3.0 without XSDT

Linux 2.6.12.x requires an XSDT if the RSDP revision is larger than 0 as it's hardcoded to use that instead of the RSDT then. Fixed in later Linux versions.

PCI Hotplug _BBN fail

pci_hotplug: PCI Hot Plug PCI Core version: 0.5
pciehp: acpi_pciehprm:\_SB_.PCI0 evaluate _BBN fail=0x5
pciehp: acpi_pciehprm:get_device PCI ROOT HID fail=0x5

Add the following under PCI0 device to get rid of the error:

Name(_ADR, 0) 
Name(_BBN, 0)

Random Notes

Shutdown sequences differ between systems

Success with shutting down a system from Windows doesn't mean that Linux properly shuts down the system (and this probably applies the other way around, too)

Further Resources