Section 23.6: Device Tree & ACPI
ARM platforms support both Device Tree (DT) and ACPI for hardware description. Understanding when and how to use each is crucial for ARM UEFI development.
Device Tree vs ACPI
graph TB
subgraph "Device Tree"
DT1[Static hardware description]
DT2[Flat binary format .dtb]
DT3[Passed to kernel at boot]
DT4[Common in embedded/IoT]
end
subgraph "ACPI"
ACPI1[Dynamic hardware description]
ACPI2[Tables + AML bytecode]
ACPI3[Runtime discovery]
ACPI4[Required for servers]
end
subgraph "Use Cases"
E[Embedded/IoT] --> DT1
S[Servers] --> ACPI1
M[Mixed] --> H[Hybrid Approach]
end
style DT1 fill:#9f9,stroke:#333
style ACPI1 fill:#99f,stroke:#333
Comparison Table
| Aspect | Device Tree | ACPI |
|---|---|---|
| Format | DTS (source) → DTB (binary) | ASL (source) → AML (binary) |
| Runtime | Static, passed at boot | Dynamic, queried at runtime |
| Power Mgmt | Minimal | Comprehensive (S-states, P-states) |
| Hotplug | Limited | Full support |
| SystemReady | IR (IoT) | SR (Server), ES (optional) |
| Complexity | Lower | Higher |
| Tooling | dtc, fdtdump | iasl, acpica |
Device Tree in UEFI
DT Source Structure
// Example platform device tree
/dts-v1/;
/ {
compatible = "vendor,platform";
model = "Vendor Platform Board";
#address-cells = <2>;
#size-cells = <2>;
cpus {
#address-cells = <1>;
#size-cells = <0>;
cpu@0 {
device_type = "cpu";
compatible = "arm,cortex-a76";
reg = <0x0>;
enable-method = "psci";
};
cpu@1 {
device_type = "cpu";
compatible = "arm,cortex-a76";
reg = <0x1>;
enable-method = "psci";
};
};
psci {
compatible = "arm,psci-1.0";
method = "smc";
};
memory@80000000 {
device_type = "memory";
reg = <0x0 0x80000000 0x0 0x80000000>; /* 2GB at 0x80000000 */
};
timer {
compatible = "arm,armv8-timer";
interrupts = <GIC_PPI 13 IRQ_TYPE_LEVEL_LOW>,
<GIC_PPI 14 IRQ_TYPE_LEVEL_LOW>,
<GIC_PPI 11 IRQ_TYPE_LEVEL_LOW>,
<GIC_PPI 10 IRQ_TYPE_LEVEL_LOW>;
};
gic: interrupt-controller@8000000 {
compatible = "arm,gic-v3";
reg = <0x0 0x08000000 0x0 0x10000>, /* GICD */
<0x0 0x080a0000 0x0 0x200000>; /* GICR */
#interrupt-cells = <3>;
interrupt-controller;
};
uart0: serial@9000000 {
compatible = "arm,pl011", "arm,primecell";
reg = <0x0 0x09000000 0x0 0x1000>;
interrupts = <GIC_SPI 1 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&apb_clk>;
clock-names = "apb_pclk";
};
pcie@10000000 {
compatible = "pci-host-ecam-generic";
reg = <0x0 0x4010000000 0x0 0x10000000>;
bus-range = <0 255>;
#address-cells = <3>;
#size-cells = <2>;
ranges = <0x01000000 0x0 0x00000000 0x0 0x3eff0000 0x0 0x10000>,
<0x02000000 0x0 0x10000000 0x0 0x10000000 0x0 0x2eff0000>,
<0x03000000 0x4 0x00000000 0x4 0x00000000 0x4 0x00000000>;
};
};
Compiling Device Tree
# Compile DTS to DTB
dtc -I dts -O dtb -o platform.dtb platform.dts
# Decompile DTB to DTS
dtc -I dtb -O dts -o platform.dts platform.dtb
# Include from fragments
dtc -@ -I dts -O dtb \
-i /path/to/include \
-o platform.dtb platform.dts
# Verify DTB
fdtdump platform.dtb
UEFI DTB Handling
// Loading and patching DTB in UEFI
EFI_STATUS
PrepareDtb (
OUT VOID **DtbBase,
OUT UINTN *DtbSize
)
{
EFI_STATUS Status;
VOID *OriginalDtb;
VOID *UpdatedDtb;
INT32 FdtErr;
// Load DTB from FV or filesystem
Status = GetSectionFromAnyFv(
&gDtbFileGuid,
EFI_SECTION_RAW,
0,
&OriginalDtb,
DtbSize
);
// Create working copy with extra space
UpdatedDtb = AllocatePages(EFI_SIZE_TO_PAGES(*DtbSize + DTB_PADDING));
CopyMem(UpdatedDtb, OriginalDtb, *DtbSize);
// Open for editing
FdtErr = fdt_open_into(UpdatedDtb, UpdatedDtb, *DtbSize + DTB_PADDING);
if (FdtErr != 0) {
return EFI_DEVICE_ERROR;
}
// Patch memory node with actual detected memory
PatchMemoryNode(UpdatedDtb);
// Add chosen node with boot arguments
PatchChosenNode(UpdatedDtb);
// Add UEFI-specific nodes
AddUefiNode(UpdatedDtb);
// Pack the DTB
fdt_pack(UpdatedDtb);
*DtbBase = UpdatedDtb;
*DtbSize = fdt_totalsize(UpdatedDtb);
return EFI_SUCCESS;
}
STATIC
VOID
PatchMemoryNode (
IN VOID *Dtb
)
{
INT32 MemNode;
UINT64 MemBase = PcdGet64(PcdSystemMemoryBase);
UINT64 MemSize = PcdGet64(PcdSystemMemorySize);
UINT64 RegProp[2];
MemNode = fdt_path_offset(Dtb, "/memory");
if (MemNode < 0) {
return;
}
// Set memory base and size (big-endian)
RegProp[0] = cpu_to_fdt64(MemBase);
RegProp[1] = cpu_to_fdt64(MemSize);
fdt_setprop(Dtb, MemNode, "reg", RegProp, sizeof(RegProp));
}
STATIC
VOID
PatchChosenNode (
IN VOID *Dtb
)
{
INT32 ChosenNode;
CHAR8 *BootArgs = "console=ttyAMA0 earlycon=pl011,0x09000000";
ChosenNode = fdt_path_offset(Dtb, "/chosen");
if (ChosenNode < 0) {
ChosenNode = fdt_add_subnode(Dtb, 0, "chosen");
}
fdt_setprop_string(Dtb, ChosenNode, "bootargs", BootArgs);
// Set stdout-path
fdt_setprop_string(Dtb, ChosenNode, "stdout-path", "serial0:115200n8");
}
Installing DTB as Configuration Table
// Install DTB for OS access
EFI_STATUS
InstallDtbConfigurationTable (
IN VOID *Dtb,
IN UINTN DtbSize
)
{
EFI_STATUS Status;
// Validate DTB
if (fdt_check_header(Dtb) != 0) {
return EFI_INVALID_PARAMETER;
}
// Install as UEFI configuration table
Status = gBS->InstallConfigurationTable(
&gFdtTableGuid,
Dtb
);
return Status;
}
ACPI Tables for ARM
Required ARM ACPI Tables
// ARM-specific ACPI tables
typedef struct {
CHAR8 Signature[4];
BOOLEAN Required;
CHAR8 *Description;
} ARM_ACPI_TABLE_INFO;
ARM_ACPI_TABLE_INFO ArmAcpiTables[] = {
// Standard tables
{ "XSDT", TRUE, "Extended System Description Table" },
{ "FACP", TRUE, "Fixed ACPI Description Table" },
{ "DSDT", TRUE, "Differentiated System Description Table" },
// ARM-mandatory tables
{ "MADT", TRUE, "Multiple APIC Description Table (GIC)" },
{ "GTDT", TRUE, "Generic Timer Description Table" },
{ "IORT", TRUE, "IO Remapping Table" },
{ "PPTT", TRUE, "Processor Properties Topology Table" },
{ "SPCR", TRUE, "Serial Port Console Redirection" },
{ "DBG2", TRUE, "Debug Port Table 2" },
// Optional but common
{ "MCFG", FALSE, "PCI Express config (if PCIe)" },
{ "SSDT", FALSE, "Secondary System Description Table" },
{ "BERT", FALSE, "Boot Error Record Table" },
{ "HEST", FALSE, "Hardware Error Source Table" },
{ "SDEI", FALSE, "Software Delegated Exception Interface" },
};
MADT for GICv3
// MADT structure for ARM GICv3
#pragma pack(1)
typedef struct {
EFI_ACPI_6_4_MULTIPLE_APIC_DESCRIPTION_TABLE_HEADER Header;
EFI_ACPI_6_4_GIC_STRUCTURE GicC[MAX_CPU_COUNT];
EFI_ACPI_6_4_GIC_DISTRIBUTOR_STRUCTURE GicD;
EFI_ACPI_6_4_GIC_REDISTRIBUTOR_STRUCTURE GicR;
EFI_ACPI_6_4_GIC_ITS_STRUCTURE GicIts;
} MADT_TABLE;
#pragma pack()
STATIC MADT_TABLE Madt = {
.Header = {
.Header = {
.Signature = EFI_ACPI_6_4_MULTIPLE_APIC_DESCRIPTION_TABLE_SIGNATURE,
.Length = sizeof(MADT_TABLE),
.Revision = 6,
},
.LocalApicAddress = 0,
.Flags = 0,
},
// CPU interfaces (one per core)
.GicC[0] = {
.Type = EFI_ACPI_6_4_GIC,
.Length = sizeof(EFI_ACPI_6_4_GIC_STRUCTURE),
.CPUInterfaceNumber = 0,
.AcpiProcessorUid = 0,
.Flags = EFI_ACPI_6_4_GIC_ENABLED,
.MPIDR = 0x00000000,
.ParkedAddress = 0,
.PhysicalBaseAddress = 0, // GICv3: not used
.GICV = 0,
.GICH = 0,
.VGICMaintenanceInterrupt = 25,
.GICRBaseAddress = GICR_BASE + 0 * GICR_SIZE,
},
// ... more cores ...
// Distributor
.GicD = {
.Type = EFI_ACPI_6_4_GICD,
.Length = sizeof(EFI_ACPI_6_4_GIC_DISTRIBUTOR_STRUCTURE),
.PhysicalBaseAddress = GICD_BASE,
.SystemVectorBase = 0,
.GicVersion = EFI_ACPI_6_4_GIC_V3,
},
// Redistributor
.GicR = {
.Type = EFI_ACPI_6_4_GICR,
.Length = sizeof(EFI_ACPI_6_4_GIC_REDISTRIBUTOR_STRUCTURE),
.DiscoveryRangeBaseAddress = GICR_BASE,
.DiscoveryRangeLength = CPU_COUNT * GICR_SIZE,
},
// ITS (Interrupt Translation Service)
.GicIts = {
.Type = EFI_ACPI_6_4_GIC_ITS,
.Length = sizeof(EFI_ACPI_6_4_GIC_ITS_STRUCTURE),
.PhysicalBaseAddress = GITS_BASE,
},
};
GTDT (Generic Timer Description Table)
#pragma pack(1)
typedef struct {
EFI_ACPI_6_4_GENERIC_TIMER_DESCRIPTION_TABLE Header;
EFI_ACPI_6_4_GTDT_GT_BLOCK_STRUCTURE GtBlock;
EFI_ACPI_6_4_GTDT_GT_BLOCK_TIMER_STRUCTURE Timer0;
EFI_ACPI_6_4_GTDT_SBSA_GENERIC_WATCHDOG Watchdog;
} GTDT_TABLE;
#pragma pack()
STATIC GTDT_TABLE Gtdt = {
.Header = {
.Header = {
.Signature = EFI_ACPI_6_4_GTDT_SIGNATURE,
.Length = sizeof(GTDT_TABLE),
.Revision = 3,
},
.CntControlBasePhysicalAddress = 0xFFFFFFFFFFFFFFFF,
.SecurePL1TimerGSIV = 29,
.SecurePL1TimerFlags = EFI_ACPI_6_4_GTDT_GT_FLAG_TIMER_INTERRUPT_POLARITY,
.NonSecurePL1TimerGSIV = 30,
.NonSecurePL1TimerFlags = EFI_ACPI_6_4_GTDT_GT_FLAG_TIMER_INTERRUPT_POLARITY,
.VirtualTimerGSIV = 27,
.VirtualTimerFlags = EFI_ACPI_6_4_GTDT_GT_FLAG_TIMER_INTERRUPT_POLARITY,
.NonSecurePL2TimerGSIV = 26,
.NonSecurePL2TimerFlags = EFI_ACPI_6_4_GTDT_GT_FLAG_TIMER_INTERRUPT_POLARITY,
.CntReadBasePhysicalAddress = 0xFFFFFFFFFFFFFFFF,
.PlatformTimerCount = 2,
.PlatformTimerOffset = sizeof(EFI_ACPI_6_4_GENERIC_TIMER_DESCRIPTION_TABLE),
},
// Memory-mapped timer block
.GtBlock = {
.Type = EFI_ACPI_6_4_GTDT_GT_BLOCK,
.Length = sizeof(EFI_ACPI_6_4_GTDT_GT_BLOCK_STRUCTURE) +
sizeof(EFI_ACPI_6_4_GTDT_GT_BLOCK_TIMER_STRUCTURE),
.PhysicalAddressCntCtlBase = 0x09010000,
.GTBlockTimerCount = 1,
.GTBlockTimerOffset = sizeof(EFI_ACPI_6_4_GTDT_GT_BLOCK_STRUCTURE),
},
.Timer0 = {
.GTFrameNumber = 0,
.PhysicalAddressCntBase = 0x09020000,
.PhysicalAddressCntEL0Base = 0xFFFFFFFFFFFFFFFF,
.PhysicalTimerGSIV = 42,
.PhysicalTimerFlags = EFI_ACPI_6_4_GTDT_GT_FLAG_TIMER_INTERRUPT_POLARITY,
.VirtualTimerGSIV = 43,
.VirtualTimerFlags = EFI_ACPI_6_4_GTDT_GT_FLAG_TIMER_INTERRUPT_POLARITY,
.CommonFlags = EFI_ACPI_6_4_GTDT_GT_BLOCK_COMMON_FLAG_SECURE_TIMER |
EFI_ACPI_6_4_GTDT_GT_BLOCK_COMMON_FLAG_ALWAYS_ON,
},
// SBSA Watchdog
.Watchdog = {
.Type = EFI_ACPI_6_4_GTDT_SBSA_GENERIC_WATCHDOG,
.Length = sizeof(EFI_ACPI_6_4_GTDT_SBSA_GENERIC_WATCHDOG),
.RefreshFramePhysicalAddress = 0x09030000,
.WatchdogControlFramePhysicalAddress = 0x09040000,
.WatchdogTimerGSIV = 48,
.WatchdogTimerFlags = 0,
},
};
IORT (IO Remapping Table)
// IORT for SMMU and PCIe
#pragma pack(1)
typedef struct {
EFI_ACPI_6_4_IO_REMAPPING_TABLE Header;
EFI_ACPI_6_4_IO_REMAPPING_ITS_NODE ItsNode;
UINT32 ItsIdentifier;
EFI_ACPI_6_4_IO_REMAPPING_SMMU3_NODE SmmuNode;
EFI_ACPI_6_4_IO_REMAPPING_ID_TABLE SmmuIdMap;
EFI_ACPI_6_4_IO_REMAPPING_RC_NODE RcNode;
EFI_ACPI_6_4_IO_REMAPPING_ID_TABLE RcIdMap;
} IORT_TABLE;
#pragma pack()
STATIC IORT_TABLE Iort = {
.Header = {
.Header = {
.Signature = EFI_ACPI_6_4_IORT_SIGNATURE,
.Length = sizeof(IORT_TABLE),
.Revision = 5,
},
.NumNodes = 3,
.NodeOffset = sizeof(EFI_ACPI_6_4_IO_REMAPPING_TABLE),
},
// ITS Node
.ItsNode = {
.Node = {
.Type = EFI_ACPI_IORT_TYPE_ITS_GROUP,
.Length = sizeof(EFI_ACPI_6_4_IO_REMAPPING_ITS_NODE) + sizeof(UINT32),
.NumIdMappings = 0,
.IdReference = 0,
},
.NumItsIdentifiers = 1,
},
.ItsIdentifier = 0, // ITS ID 0
// SMMU Node
.SmmuNode = {
.Node = {
.Type = EFI_ACPI_IORT_TYPE_SMMUv3,
.Length = sizeof(EFI_ACPI_6_4_IO_REMAPPING_SMMU3_NODE) +
sizeof(EFI_ACPI_6_4_IO_REMAPPING_ID_TABLE),
.NumIdMappings = 1,
.IdReference = sizeof(EFI_ACPI_6_4_IO_REMAPPING_SMMU3_NODE),
},
.Base = SMMU_BASE,
.Flags = EFI_ACPI_IORT_SMMUv3_FLAG_COHAC_OVERRIDE,
.EventGsiv = SMMU_EVENT_IRQ,
.PriGsiv = SMMU_PRI_IRQ,
.GerrGsiv = SMMU_GERR_IRQ,
.SyncGsiv = SMMU_SYNC_IRQ,
.Model = EFI_ACPI_IORT_SMMUv3_MODEL_GENERIC,
},
.SmmuIdMap = {
.InputBase = 0,
.NumIds = 0x10000,
.OutputBase = 0,
.OutputReference = offsetof(IORT_TABLE, ItsNode),
.Flags = 0,
},
// Root Complex Node
.RcNode = {
.Node = {
.Type = EFI_ACPI_IORT_TYPE_ROOT_COMPLEX,
.Length = sizeof(EFI_ACPI_6_4_IO_REMAPPING_RC_NODE) +
sizeof(EFI_ACPI_6_4_IO_REMAPPING_ID_TABLE),
.NumIdMappings = 1,
.IdReference = sizeof(EFI_ACPI_6_4_IO_REMAPPING_RC_NODE),
},
.CacheCoherent = EFI_ACPI_IORT_ROOT_COMPLEX_ATS_SUPPORTED,
.AllocationHints = 0,
.MemoryAccessFlags = EFI_ACPI_IORT_MEM_ACCESS_FLAGS_CPM |
EFI_ACPI_IORT_MEM_ACCESS_FLAGS_DACS,
.AtsAttribute = EFI_ACPI_IORT_ROOT_COMPLEX_ATS_SUPPORTED,
.PciSegmentNumber = 0,
.MemoryAddressSize = 48,
},
.RcIdMap = {
.InputBase = 0,
.NumIds = 0x10000,
.OutputBase = 0,
.OutputReference = offsetof(IORT_TABLE, SmmuNode),
.Flags = EFI_ACPI_IORT_ID_MAPPING_FLAGS_SINGLE,
},
};
PPTT (Processor Properties Topology Table)
// PPTT for CPU topology
#pragma pack(1)
typedef struct {
EFI_ACPI_6_4_PROCESSOR_PROPERTIES_TOPOLOGY_TABLE_HEADER Header;
// Package (socket)
EFI_ACPI_6_4_PPTT_STRUCTURE_PROCESSOR Package;
// Clusters
EFI_ACPI_6_4_PPTT_STRUCTURE_PROCESSOR Cluster0;
EFI_ACPI_6_4_PPTT_STRUCTURE_PROCESSOR Cluster1;
// Cores
EFI_ACPI_6_4_PPTT_STRUCTURE_PROCESSOR Core[8];
// Caches
EFI_ACPI_6_4_PPTT_STRUCTURE_CACHE L1ICache[8];
EFI_ACPI_6_4_PPTT_STRUCTURE_CACHE L1DCache[8];
EFI_ACPI_6_4_PPTT_STRUCTURE_CACHE L2Cache[8];
EFI_ACPI_6_4_PPTT_STRUCTURE_CACHE L3Cache;
} PPTT_TABLE;
#pragma pack()
// Cache configuration
#define L1I_SIZE (32 * 1024)
#define L1I_ASSOC 4
#define L1I_LINE_SIZE 64
#define L1D_SIZE (32 * 1024)
#define L1D_ASSOC 4
#define L1D_LINE_SIZE 64
#define L2_SIZE (256 * 1024)
#define L2_ASSOC 8
#define L2_LINE_SIZE 64
#define L3_SIZE (4 * 1024 * 1024)
#define L3_ASSOC 16
#define L3_LINE_SIZE 64
Hybrid DT/ACPI Approach
Using DT for Discovery, ACPI for Runtime
// Hybrid approach: Use DT from TF-A, generate ACPI
EFI_STATUS
CreateAcpiFromDtb (
IN VOID *Dtb
)
{
INT32 Node;
INT32 Len;
CONST VOID *Prop;
// Parse CPU nodes from DTB
Node = fdt_path_offset(Dtb, "/cpus");
if (Node >= 0) {
ParseCpuNodes(Dtb, Node);
}
// Parse memory from DTB
Node = fdt_path_offset(Dtb, "/memory");
if (Node >= 0) {
Prop = fdt_getprop(Dtb, Node, "reg", &Len);
ParseMemoryRegions(Prop, Len);
}
// Parse GIC from DTB
Node = fdt_node_offset_by_compatible(Dtb, -1, "arm,gic-v3");
if (Node >= 0) {
ParseGicConfig(Dtb, Node);
}
// Generate ACPI tables from parsed data
GenerateMadt();
GenerateGtdt();
GeneratePptt();
return EFI_SUCCESS;
}
Dynamic ACPI Table Generation
// Dynamically generate MADT based on runtime CPU count
EFI_STATUS
GenerateDynamicMadt (
OUT VOID **MadtTable,
OUT UINTN *MadtSize
)
{
UINTN CpuCount;
UINTN TableSize;
MADT_HEADER *Madt;
GIC_CPU_INTERFACE *GicC;
UINTN i;
// Get CPU count from PSCI or DT
CpuCount = GetCpuCount();
// Calculate table size
TableSize = sizeof(MADT_HEADER) +
CpuCount * sizeof(GIC_CPU_INTERFACE) +
sizeof(GIC_DISTRIBUTOR) +
sizeof(GIC_REDISTRIBUTOR);
Madt = AllocateZeroPool(TableSize);
// Fill header
Madt->Header.Signature = EFI_ACPI_6_4_MADT_SIGNATURE;
Madt->Header.Length = TableSize;
Madt->Header.Revision = 6;
// Add GIC CPU interfaces
GicC = (GIC_CPU_INTERFACE *)(Madt + 1);
for (i = 0; i < CpuCount; i++) {
GicC[i].Type = EFI_ACPI_6_4_GIC;
GicC[i].Length = sizeof(GIC_CPU_INTERFACE);
GicC[i].CPUInterfaceNumber = i;
GicC[i].AcpiProcessorUid = i;
GicC[i].MPIDR = GetCpuMpidr(i);
GicC[i].Flags = EFI_ACPI_6_4_GIC_ENABLED;
GicC[i].GICRBaseAddress = GICR_BASE + i * GICR_SIZE;
}
// Add distributor and redistributor...
*MadtTable = Madt;
*MadtSize = TableSize;
return EFI_SUCCESS;
}
DSDT/SSDT for ARM
ARM-Specific ASL
// ARM platform DSDT example
DefinitionBlock ("Dsdt.aml", "DSDT", 2, "VENDOR", "PLATFORM", 1) {
Scope (\_SB) {
// Processor container
Device (CPUS) {
Name (_HID, "ACPI0010") // Processor Container
Name (_UID, 0)
// Individual CPUs
Device (CPU0) {
Name (_HID, "ACPI0007") // Processor
Name (_UID, 0)
Method (_STA) { Return (0xF) }
}
Device (CPU1) {
Name (_HID, "ACPI0007")
Name (_UID, 1)
Method (_STA) { Return (0xF) }
}
}
// PL011 UART
Device (COM0) {
Name (_HID, "ARMH0011") // ARM PL011
Name (_UID, 0)
Name (_CRS, ResourceTemplate () {
Memory32Fixed (ReadWrite, 0x09000000, 0x1000)
Interrupt (ResourceConsumer, Level, ActiveHigh, Exclusive) { 33 }
})
}
// PCIe Root Complex
Device (PCI0) {
Name (_HID, "PNP0A08") // PCI Express
Name (_CID, "PNP0A03") // PCI Bus
Name (_SEG, 0)
Name (_BBN, 0)
Name (_CRS, ResourceTemplate () {
// Bus number range
WordBusNumber (ResourceProducer, MinFixed, MaxFixed, PosDecode,
0, 0, 255, 0, 256)
// IO window
DWordIO (ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange,
0, 0x0000, 0xFFFF, 0x3eff0000, 0x10000)
// 32-bit MMIO window
DWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed,
Cacheable, ReadWrite,
0, 0x10000000, 0x3EFEFFFF, 0, 0x2EFF0000)
// 64-bit MMIO window
QWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed,
Cacheable, ReadWrite,
0, 0x8000000000, 0xFFFFFFFFFF, 0, 0x8000000000)
})
// _OSC method for OS capability negotiation
Method (_OSC, 4) {
// Handle PCIe _OSC
}
}
// GPIO Controller
Device (GPI0) {
Name (_HID, "ARMH0061") // ARM PL061
Name (_UID, 0)
Name (_CRS, ResourceTemplate () {
Memory32Fixed (ReadWrite, 0x09030000, 0x1000)
Interrupt (ResourceConsumer, Level, ActiveHigh, Exclusive) { 39 }
})
}
}
// Power Management
Scope (\_SB) {
Device (PWRB) {
Name (_HID, "PNP0C0C") // Power Button
}
}
}
Compiling ASL
# Compile ASL to AML
iasl -tc platform.asl
# Output: platform.aml (binary), platform.hex (C array)
# Disassemble existing AML
iasl -d existing.aml
# Include multiple files
iasl -tc -I include/ main.asl
Installing Tables
// Install all ACPI tables
EFI_STATUS
InstallAcpiTables (
VOID
)
{
EFI_STATUS Status;
EFI_ACPI_TABLE_PROTOCOL *AcpiTable;
Status = gBS->LocateProtocol(
&gEfiAcpiTableProtocolGuid,
NULL,
(VOID **)&AcpiTable
);
if (EFI_ERROR(Status)) {
return Status;
}
// Install MADT
UINTN MadtKey;
Status = AcpiTable->InstallAcpiTable(
AcpiTable,
&Madt,
sizeof(Madt),
&MadtKey
);
// Install GTDT
UINTN GtdtKey;
Status = AcpiTable->InstallAcpiTable(
AcpiTable,
&Gtdt,
sizeof(Gtdt),
&GtdtKey
);
// Install IORT
UINTN IortKey;
Status = AcpiTable->InstallAcpiTable(
AcpiTable,
&Iort,
sizeof(Iort),
&IortKey
);
// Install DSDT (via FACP reference)
InstallDsdt();
return EFI_SUCCESS;
}
References
Next: Section 23.7: Debugging Techniques - JTAG, ETM, and exception analysis.