Chapter 18: ACPI
Advanced Configuration and Power Interface tables in UEFI.
Overview
When to Work with ACPI
Work with ACPI when you need to:
- Describe platform hardware to the operating system
- Implement power management (sleep states, CPU power states)
- Configure interrupt routing and device resources
- Support hot-plug devices or dynamic platform configuration
| Scenario | ACPI Table | Purpose |
|---|---|---|
| CPU/interrupt topology | MADT | Describe CPUs, IOAPICs, GICs |
| PCIe configuration | MCFG | ECAM base address for PCIe |
| Device enumeration | DSDT/SSDT | Non-discoverable devices |
| Power management | FADT, _PSS, _CST | Sleep states, P-states, C-states |
| Memory-mapped devices | DSDT (Device nodes) | GPIO, I2C, SPI controllers |
| Hot-plug support | DSDT (_EJ0, _OST) | Device ejection methods |
ACPI vs Device Tree (ARM):
| Factor | ACPI | Device Tree |
|---|---|---|
| Platform | x86, ARM servers | ARM embedded, Linux |
| Complexity | Higher | Lower |
| Power management | Full support | Limited |
| OS support | Windows, Linux | Primarily Linux |
| Standardization | UEFI Forum spec | Less formal |
Who Works with ACPI:
| Role | ACPI Tasks |
|---|---|
| Platform developer | Generate tables, define devices, power states |
| Silicon vendor | Reference DSDT, chipset-specific tables |
| BIOS engineer | Table customization, ASL debugging |
| OS developer | Consume tables, debug ACPI issues |
Key ACPI Concepts:
- ASL (ACPI Source Language): High-level language for DSDT/SSDT
- AML (ACPI Machine Language): Compiled bytecode in tables
- Control Methods: Executable code for dynamic behavior
- Namespace: Hierarchical device/object organization
ACPI Architecture
ACPI provides platform configuration data and power management to the operating system:
flowchart TB
subgraph UEFI Firmware
FW[ACPI Table Generation]
INSTALL[Table Installation]
end
subgraph ACPI Tables
RSDP[RSDP<br/>Root Pointer]
XSDT[XSDT<br/>Extended SDT]
FADT[FADT<br/>Fixed ACPI]
DSDT[DSDT<br/>Differentiated SDT]
SSDT[SSDT<br/>Secondary SDT]
MADT[MADT<br/>Multiple APIC]
MCFG[MCFG<br/>PCIe Config]
end
subgraph Operating System
OSPM[OS Power Management]
DRIVER[ACPI Drivers]
end
FW --> INSTALL
INSTALL --> RSDP
RSDP --> XSDT
XSDT --> FADT
XSDT --> DSDT
XSDT --> SSDT
XSDT --> MADT
XSDT --> MCFG
FADT --> OSPM
DSDT --> DRIVER
style RSDP fill:#e74c3c,color:#fff
style DSDT fill:#2ecc71,color:#fff
style XSDT fill:#3498db,color:#fff
Key ACPI Tables
| Table | Full Name | Purpose |
|---|---|---|
| RSDP | Root System Description Pointer | Entry point, locates RSDT/XSDT |
| XSDT | Extended System Description Table | 64-bit pointers to other tables |
| RSDT | Root System Description Table | 32-bit pointers (legacy) |
| FADT | Fixed ACPI Description Table | Fixed hardware addresses |
| DSDT | Differentiated System Description Table | AML code for device tree |
| SSDT | Secondary System Description Table | Additional AML code |
| MADT | Multiple APIC Description Table | Interrupt controllers |
| MCFG | Memory Mapped Configuration | PCIe ECAM base |
| HPET | High Precision Event Timer | Timer configuration |
| BGRT | Boot Graphics Resource Table | Boot logo |
Initialization
ACPI Table Protocol
#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Protocol/AcpiTable.h>
#include <IndustryStandard/Acpi.h>
EFI_ACPI_TABLE_PROTOCOL *gAcpiTable;
EFI_STATUS
InitializeAcpi (
VOID
)
{
return gBS->LocateProtocol(
&gEfiAcpiTableProtocolGuid,
NULL,
(VOID **)&gAcpiTable
);
}
Finding ACPI Tables
#include <Guid/Acpi.h>
EFI_STATUS
FindAcpiTables (
OUT EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER **Rsdp
)
{
EFI_STATUS Status;
VOID *Table;
//
// Look in EFI Configuration Table
//
Status = EfiGetSystemConfigurationTable(
&gEfiAcpi20TableGuid,
&Table
);
if (!EFI_ERROR(Status)) {
*Rsdp = (EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER *)Table;
return EFI_SUCCESS;
}
//
// Try ACPI 1.0 GUID
//
Status = EfiGetSystemConfigurationTable(
&gEfiAcpi10TableGuid,
&Table
);
if (!EFI_ERROR(Status)) {
*Rsdp = (EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER *)Table;
return EFI_SUCCESS;
}
return EFI_NOT_FOUND;
}
EFI_STATUS
FindTableBySignature (
IN UINT32 Signature,
OUT EFI_ACPI_DESCRIPTION_HEADER **Table
)
{
EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER *Rsdp;
EFI_ACPI_SDT_HEADER *Xsdt;
UINT64 *Entry;
UINTN EntryCount;
UINTN Index;
EFI_ACPI_DESCRIPTION_HEADER *Header;
if (FindAcpiTables(&Rsdp) != EFI_SUCCESS) {
return EFI_NOT_FOUND;
}
Xsdt = (EFI_ACPI_SDT_HEADER *)(UINTN)Rsdp->XsdtAddress;
EntryCount = (Xsdt->Length - sizeof(EFI_ACPI_SDT_HEADER)) / sizeof(UINT64);
Entry = (UINT64 *)(Xsdt + 1);
for (Index = 0; Index < EntryCount; Index++) {
Header = (EFI_ACPI_DESCRIPTION_HEADER *)(UINTN)Entry[Index];
if (Header->Signature == Signature) {
*Table = Header;
return EFI_SUCCESS;
}
}
return EFI_NOT_FOUND;
}
Configuration
Installing ACPI Tables
EFI_STATUS
InstallAcpiTable (
IN VOID *Table,
IN UINTN TableSize
)
{
EFI_STATUS Status;
UINTN TableKey;
if (gAcpiTable == NULL) {
Status = InitializeAcpi();
if (EFI_ERROR(Status)) {
return Status;
}
}
Status = gAcpiTable->InstallAcpiTable(
gAcpiTable,
Table,
TableSize,
&TableKey
);
return Status;
}
EFI_STATUS
UninstallAcpiTable (
IN UINTN TableKey
)
{
return gAcpiTable->UninstallAcpiTable(gAcpiTable, TableKey);
}
Building ACPI Table Header
//
// Common ACPI table header
//
typedef struct {
UINT32 Signature;
UINT32 Length;
UINT8 Revision;
UINT8 Checksum;
UINT8 OemId[6];
UINT64 OemTableId;
UINT32 OemRevision;
UINT32 CreatorId;
UINT32 CreatorRevision;
} EFI_ACPI_DESCRIPTION_HEADER;
VOID
InitializeAcpiHeader (
OUT EFI_ACPI_DESCRIPTION_HEADER *Header,
IN UINT32 Signature,
IN UINT8 Revision,
IN UINT32 Length
)
{
ZeroMem(Header, sizeof(EFI_ACPI_DESCRIPTION_HEADER));
Header->Signature = Signature;
Header->Length = Length;
Header->Revision = Revision;
CopyMem(Header->OemId, "OEMID ", 6);
Header->OemTableId = SIGNATURE_64('O','E','M','T','A','B','L','E');
Header->OemRevision = 1;
Header->CreatorId = SIGNATURE_32('E','D','K','2');
Header->CreatorRevision = 1;
//
// Checksum calculated after table is complete
//
}
UINT8
CalculateChecksum (
IN UINT8 *Buffer,
IN UINTN Size
)
{
UINT8 Sum = 0;
UINTN Index;
for (Index = 0; Index < Size; Index++) {
Sum = (UINT8)(Sum + Buffer[Index]);
}
return (UINT8)(0 - Sum);
}
MADT (Multiple APIC Description Table)
#include <IndustryStandard/Acpi62.h>
#pragma pack(1)
typedef struct {
EFI_ACPI_6_2_MULTIPLE_APIC_DESCRIPTION_TABLE_HEADER Header;
EFI_ACPI_6_2_PROCESSOR_LOCAL_APIC_STRUCTURE LocalApic[MAX_CPU];
EFI_ACPI_6_2_IO_APIC_STRUCTURE IoApic;
EFI_ACPI_6_2_INTERRUPT_SOURCE_OVERRIDE_STRUCTURE IntOverride[16];
} MY_MADT_TABLE;
#pragma pack()
EFI_STATUS
BuildMadtTable (
OUT MY_MADT_TABLE *Madt,
IN UINTN CpuCount
)
{
UINTN Index;
UINTN Offset;
ZeroMem(Madt, sizeof(MY_MADT_TABLE));
//
// Header
//
Madt->Header.Header.Signature = EFI_ACPI_6_2_MULTIPLE_APIC_DESCRIPTION_TABLE_SIGNATURE;
Madt->Header.Header.Revision = EFI_ACPI_6_2_MULTIPLE_APIC_DESCRIPTION_TABLE_REVISION;
CopyMem(Madt->Header.Header.OemId, "OEMID ", 6);
Madt->Header.Header.OemTableId = SIGNATURE_64('M','A','D','T','T','B','L',' ');
Madt->Header.Header.OemRevision = 1;
Madt->Header.Header.CreatorId = SIGNATURE_32('E','D','K','2');
Madt->Header.Header.CreatorRevision = 1;
Madt->Header.LocalApicAddress = 0xFEE00000; // Default LAPIC address
Madt->Header.Flags = EFI_ACPI_6_2_PCAT_COMPAT;
//
// Local APIC entries
//
for (Index = 0; Index < CpuCount; Index++) {
Madt->LocalApic[Index].Type = EFI_ACPI_6_2_PROCESSOR_LOCAL_APIC;
Madt->LocalApic[Index].Length = sizeof(EFI_ACPI_6_2_PROCESSOR_LOCAL_APIC_STRUCTURE);
Madt->LocalApic[Index].AcpiProcessorUid = (UINT8)Index;
Madt->LocalApic[Index].ApicId = (UINT8)Index;
Madt->LocalApic[Index].Flags = EFI_ACPI_6_2_LOCAL_APIC_ENABLED;
}
//
// I/O APIC
//
Madt->IoApic.Type = EFI_ACPI_6_2_IO_APIC;
Madt->IoApic.Length = sizeof(EFI_ACPI_6_2_IO_APIC_STRUCTURE);
Madt->IoApic.IoApicId = 0;
Madt->IoApic.IoApicAddress = 0xFEC00000; // Default IOAPIC address
Madt->IoApic.GlobalSystemInterruptBase = 0;
//
// Calculate total size and checksum
//
Madt->Header.Header.Length = sizeof(MY_MADT_TABLE);
Madt->Header.Header.Checksum = CalculateChecksum((UINT8 *)Madt,
Madt->Header.Header.Length);
return EFI_SUCCESS;
}
Porting Guide
Platform ACPI Configuration
#
# Platform DSC file - ACPI configuration
#
[PcdsFixedAtBuild]
# ACPI table settings
gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiDefaultOemId|"OEMID "
gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiDefaultOemTableId|0x454C424154454D45
gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiDefaultCreatorId|0x32333234
gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiDefaultCreatorRevision|0x01000001
[Components]
# ACPI infrastructure
MdeModulePkg/Universal/Acpi/AcpiTableDxe/AcpiTableDxe.inf
MdeModulePkg/Universal/Acpi/AcpiPlatformDxe/AcpiPlatformDxe.inf
# Platform ACPI tables
$(PLATFORM_PKG)/AcpiTables/AcpiTables.inf
ASL (ACPI Source Language)
/*
* Example DSDT (Differentiated System Description Table)
*/
DefinitionBlock ("DSDT.aml", "DSDT", 2, "OEMID", "DSDT", 0x00000001)
{
//
// Root scope
//
Scope (\_SB)
{
//
// Processor declaration
//
Device (PR00)
{
Name (_HID, "ACPI0007") // Processor Device
Name (_UID, 0)
}
//
// PCI Root Bridge
//
Device (PCI0)
{
Name (_HID, EISAID("PNP0A08")) // PCI Express Root Bridge
Name (_CID, EISAID("PNP0A03")) // PCI Bus
Name (_SEG, 0)
Name (_BBN, 0)
Name (_UID, 0)
//
// PCI routing table
//
Name (_PRT, Package()
{
// Device, Pin, Source, Source Index
Package() { 0x0000FFFF, 0, LNKA, 0 },
Package() { 0x0000FFFF, 1, LNKB, 0 },
Package() { 0x0000FFFF, 2, LNKC, 0 },
Package() { 0x0000FFFF, 3, LNKD, 0 },
})
//
// Resources
//
Name (_CRS, ResourceTemplate()
{
WordBusNumber(ResourceProducer, MinFixed, MaxFixed, PosDecode,
0x0000, 0x0000, 0x00FF, 0x0000, 0x0100)
DWordMemory(ResourceProducer, PosDecode, MinFixed, MaxFixed,
Cacheable, ReadWrite,
0x00000000, 0x000A0000, 0x000BFFFF, 0x00000000, 0x00020000)
DWordMemory(ResourceProducer, PosDecode, MinFixed, MaxFixed,
Cacheable, ReadWrite,
0x00000000, 0x80000000, 0xFEBFFFFF, 0x00000000, 0x7EC00000)
})
}
}
//
// Power management
//
Scope (\_SB)
{
Device (PWRB)
{
Name (_HID, EISAID("PNP0C0C")) // Power Button
}
Device (SLPB)
{
Name (_HID, EISAID("PNP0C0E")) // Sleep Button
}
}
}
Compiling ASL to AML
#
# IASL (Intel ACPI compiler) usage
#
# Compile ASL to AML
iasl -tc Dsdt.asl
# Output:
# Dsdt.aml - Binary table
# Dsdt.hex - C header with table bytes
#
# Include compiled table in firmware
#
# In INF file:
# [Sources]
# Dsdt.asl
#
# Build system runs IASL automatically
#
Dynamic ACPI Tables
ACPI SDT Protocol
#include <Protocol/AcpiSdt.h>
EFI_ACPI_SDT_PROTOCOL *gAcpiSdt;
EFI_STATUS
ModifyAcpiTable (
IN UINT32 Signature,
IN VOID (*ModifyCallback)(EFI_ACPI_DESCRIPTION_HEADER *)
)
{
EFI_STATUS Status;
UINTN Index;
EFI_ACPI_SDT_HEADER *Table;
EFI_ACPI_TABLE_VERSION Version;
UINTN TableKey;
Status = gBS->LocateProtocol(
&gEfiAcpiSdtProtocolGuid,
NULL,
(VOID **)&gAcpiSdt
);
if (EFI_ERROR(Status)) {
return Status;
}
//
// Find table
//
Index = 0;
while (TRUE) {
Status = gAcpiSdt->GetAcpiTable(
Index,
&Table,
&Version,
&TableKey
);
if (EFI_ERROR(Status)) {
break;
}
if (Table->Signature == Signature) {
//
// Modify table in place
//
ModifyCallback((EFI_ACPI_DESCRIPTION_HEADER *)Table);
//
// Update checksum
//
Table->Checksum = 0;
Table->Checksum = CalculateChecksum((UINT8 *)Table, Table->Length);
return EFI_SUCCESS;
}
Index++;
}
return EFI_NOT_FOUND;
}
Example: ACPI Table Dump
/** @file
ACPI Table Information Display
**/
#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Guid/Acpi.h>
#include <IndustryStandard/Acpi.h>
VOID
PrintSignature (
IN UINT32 Signature
)
{
CHAR8 Sig[5];
Sig[0] = (CHAR8)(Signature & 0xFF);
Sig[1] = (CHAR8)((Signature >> 8) & 0xFF);
Sig[2] = (CHAR8)((Signature >> 16) & 0xFF);
Sig[3] = (CHAR8)((Signature >> 24) & 0xFF);
Sig[4] = 0;
Print(L"%a", Sig);
}
EFI_STATUS
EFIAPI
UefiMain (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER *Rsdp;
EFI_ACPI_SDT_HEADER *Xsdt;
UINT64 *Entry;
UINTN EntryCount;
UINTN Index;
EFI_ACPI_DESCRIPTION_HEADER *Table;
Print(L"=== ACPI Tables ===\n\n");
//
// Find RSDP
//
Status = EfiGetSystemConfigurationTable(&gEfiAcpi20TableGuid, (VOID **)&Rsdp);
if (EFI_ERROR(Status)) {
Print(L"ACPI 2.0 tables not found\n");
return Status;
}
Print(L"RSDP at 0x%p\n", Rsdp);
Print(L" Revision: %d\n", Rsdp->Revision);
Print(L" RSDT: 0x%08x\n", Rsdp->RsdtAddress);
Print(L" XSDT: 0x%016lx\n", Rsdp->XsdtAddress);
//
// Parse XSDT
//
Xsdt = (EFI_ACPI_SDT_HEADER *)(UINTN)Rsdp->XsdtAddress;
EntryCount = (Xsdt->Length - sizeof(EFI_ACPI_SDT_HEADER)) / sizeof(UINT64);
Entry = (UINT64 *)(Xsdt + 1);
Print(L"\nXSDT contains %d tables:\n\n", EntryCount);
Print(L" Signature Address Length Rev\n");
Print(L" --------- ----------------- ------- ---\n");
for (Index = 0; Index < EntryCount; Index++) {
Table = (EFI_ACPI_DESCRIPTION_HEADER *)(UINTN)Entry[Index];
Print(L" ");
PrintSignature(Table->Signature);
Print(L" 0x%016lx %7d %3d\n",
Entry[Index],
Table->Length,
Table->Revision
);
}
//
// Show FADT details
//
for (Index = 0; Index < EntryCount; Index++) {
Table = (EFI_ACPI_DESCRIPTION_HEADER *)(UINTN)Entry[Index];
if (Table->Signature == EFI_ACPI_6_2_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE) {
EFI_ACPI_6_2_FIXED_ACPI_DESCRIPTION_TABLE *Fadt =
(EFI_ACPI_6_2_FIXED_ACPI_DESCRIPTION_TABLE *)Table;
Print(L"\nFADT Details:\n");
Print(L" DSDT: 0x%016lx\n", Fadt->XDsdt);
Print(L" PM1a Event Block: 0x%x\n", Fadt->Pm1aEvtBlk);
Print(L" PM1a Control Block: 0x%x\n", Fadt->Pm1aCntBlk);
Print(L" PM Timer Block: 0x%x\n", Fadt->PmTmrBlk);
Print(L" Flags: 0x%08x\n", Fadt->Flags);
break;
}
}
Print(L"\nPress any key to exit...\n");
{
EFI_INPUT_KEY Key;
UINTN EventIndex;
gBS->WaitForEvent(1, &gST->ConIn->WaitForKey, &EventIndex);
gST->ConIn->ReadKeyStroke(gST->ConIn, &Key);
}
return EFI_SUCCESS;
}
ACPI Specification Reference
- ACPI Spec 6.4: Full ACPI specification
- ACPI Spec Chapter 5: ACPI Software Programming Model
- ACPI Spec Chapter 19: ASL Reference
Summary
- RSDP is the entry point to ACPI tables
- XSDT contains pointers to all other tables
- DSDT/SSDT contain AML code describing the platform
- MADT describes interrupt controllers
- ACPI Table Protocol installs tables to firmware
- ASL is compiled to AML bytecode
Next Steps
- Chapter 19: Capsule Updates - Firmware updates
- Chapter 20: BMC Integration - Server management
Tool: Use
acpidumpandiasl -don Linux to dump and disassemble ACPI tables for debugging.