AxlAcpi — ACPI table access
ACPI table discovery and typed readers.
Locates ACPI tables published by firmware in the EFI configuration table, transparently walks RSDT (ACPI 1.0) or XSDT (ACPI 2.0+), and serves up identity-mapped table-header pointers. Cursor-style iteration matches AxlSmbios:
AxlAcpiHeader *h = NULL;
while ((h = axl_acpi_find_next("APIC", h)) != NULL) {
/* process each MADT — typically just one, but the API is
correct for OEM platforms that publish more */
}
Header: <axl/axl-acpi.h>.
Scope
Discovery and typed readers for the small set of tables the SDK
consumes directly. AML interpretation is out of scope — that’s
ACPICA-sized work that diagnostic tools don’t need. Other tables
stay raw; consumers walk them via the AxlAcpiHeader cursor and
the table’s own definitions from the ACPI spec.
Discovery
axl_acpi_find() and axl_acpi_find_next() look up tables by
their 4-byte signature (NOT nul-terminated — match the on-wire
field exactly):
AxlAcpiHeader *mcfg = axl_acpi_find("MCFG");
AxlAcpiHeader *fadt = axl_acpi_find("FACP"); /* spec quirk */
axl_acpi_next() iterates every table regardless of signature.
axl_acpi_revision() returns the RSDP revision byte (0 = ACPI 1.0,
2+ = ACPI 2.0+) for callers that care; _find() handles
RSDT-vs-XSDT internally so consumers don’t choose. Lazy on first
use — no explicit init.
Checksums
Validate a table before trusting its contents. Returns true if the
unsigned-byte sum across the header’s length field equals 0
modulo 256 (the ACPI invariant):
if (!axl_acpi_checksum_ok(table)) {
axl_warning("checksum invalid; skipping");
return -1;
}
The typed readers below already invoke this internally.
Typed Readers
MCFG — PCIe ECAM
Decodes the per-segment configuration-space allocation entries.
Required for axl_pci_* (R+2):
AxlAcpiMcfg mcfg;
if (axl_acpi_read_mcfg(&mcfg) == 0) {
for (size_t i = 0; i < mcfg.count; i++) {
AxlAcpiMcfgEntry *e = &mcfg.segments[i];
axl_printf("seg %u buses %02x..%02x base 0x%llx\n",
e->segment, e->start_bus, e->end_bus,
(unsigned long long)e->base_addr);
}
}
MADT — Interrupt controllers
x86 firmware populates ioapics; AArch64 firmware populates
gic_regions. Consumers check whichever is non-empty for the
running arch:
AxlAcpiMadt madt;
if (axl_acpi_read_madt(&madt) == 0) {
for (size_t i = 0; i < madt.ioapic_count; i++) {
axl_printf("IOAPIC %u addr 0x%x irq base %u\n",
madt.ioapics[i].id,
madt.ioapics[i].addr,
madt.ioapics[i].global_irq_base);
}
}
FACP/FADT — Fixed-feature pointers
A minimal subset: SMI command port + ACPI enable/disable for
power-state interaction, PM1 event/control block ports for SCI
handling, DSDT pointer for callers that walk AML themselves, and
the boot-arch flags. The full FADT has many more fields — read it
directly via axl_acpi_find("FACP", NULL) if you need them.
AxlAcpiFacp facp;
if (axl_acpi_read_facp(&facp) == 0) {
axl_printf("SMI cmd port: 0x%x enable: 0x%x\n",
facp.smi_cmd, facp.acpi_enable);
}
API Reference
ACPI table discovery and typed readers.
Locates ACPI tables published in the firmware’s configuration table, walks the RSDT (ACPI 1.0) or XSDT (ACPI 2.0+) automatically, and returns header pointers identity-mapped into the running address space. Cursor-style iteration matches the SMBIOS module:
AxlAcpiHeader *h = NULL;
while ((h = axl_acpi_find_next("APIC", h)) != NULL) {
// process each MADT (rare to have more than one, but correct)
}
Typed readers populate caller-allocated structs for the small set of tables the SDK consumes directly: MCFG (PCIe ECAM), MADT (interrupt controllers), and FACP/FADT (fixed-feature pointers). Other tables stay raw — consumers can walk them via the AxlAcpiHeader cursor and the table’s own definitions.
Scope is discovery + typed readers; AML interpretation is out of scope (that’s ACPICA-sized and not what diagnostic tools need).
Defines
-
AXL_ACPI_MCFG_MAX_SEGMENTS
Maximum PCI segments captured from MCFG. Multi-segment platforms rarely exceed a handful; 16 is room to spare.
-
AXL_ACPI_MADT_MAX_IOAPICS
Caps on per-arch MADT entries we capture. Platforms with more stuff still get the first N.
-
AXL_ACPI_MADT_MAX_GIC_REGS
Functions
-
AxlAcpiHeader *axl_acpi_find(const char sig[4])
Find the first ACPI table with the given 4-byte signature.
_find()resolves RSDT versus XSDT internally based on the RSDP revision the firmware published — consumers don’t choose.- Parameters:
sig – 4-byte signature, NOT nul-terminated (e.g. {‘M’,’C’,’F’,’G’})
- Returns:
pointer to the table header, or NULL if not found.
-
AxlAcpiHeader *axl_acpi_find_next(const char sig[4], AxlAcpiHeader *prev)
Find the next ACPI table with
sigafterprev.Pass NULL as
prevto find the first (same as axl_acpi_find). Use in a loop to enumerate all tables sharing a signature.- Parameters:
sig – 4-byte signature, NOT nul-terminated
prev – previous result, or NULL to start
- Returns:
pointer to next table header, or NULL if no more.
-
AxlAcpiHeader *axl_acpi_next(AxlAcpiHeader *prev)
Iterate every ACPI table regardless of signature.
Pass NULL for the first call; pass the previous result for subsequent calls. Returns NULL when there are no more.
- Parameters:
prev – previous result, or NULL to start
- Returns:
pointer to next table header, or NULL if no more.
-
int axl_acpi_revision(uint8_t *rev)
Get the RSDP revision the firmware published.
0 = ACPI 1.0 (32-bit RSDT pointers only). 2+ = ACPI 2.0 or later (64-bit XSDT preferred when present).
- Parameters:
rev – [out] receives RSDP revision byte
- Returns:
0 on success, -1 if RSDP is not available.
-
bool axl_acpi_checksum_ok(const AxlAcpiHeader *h)
Verify a table’s whole-table checksum.
Computes the unsigned-byte sum across
h->lengthbytes; valid tables sum to 0 modulo 256. Useful before trusting the typed reader output on suspect firmware.- Parameters:
h – table header
- Returns:
true if the checksum is valid; false if
his NULL, the length field is too small to be a valid table, or the bytes don’t sum to zero.
-
int axl_acpi_read_mcfg(AxlAcpiMcfg *out)
Read and decode the MCFG table.
- Parameters:
out – [out] receives decoded MCFG
- Returns:
0 on success, -1 if MCFG is missing or malformed.
-
int axl_acpi_read_madt(AxlAcpiMadt *out)
Read and decode the MADT (APIC) table.
- Parameters:
out – [out] receives decoded MADT
- Returns:
0 on success, -1 if MADT is missing or malformed.
-
int axl_acpi_read_facp(AxlAcpiFacp *out)
Read and decode the FADT.
- Parameters:
out – [out] receives decoded FADT
- Returns:
0 on success, -1 if the table is missing or malformed.
-
struct AxlAcpiHeader
- #include <axl-acpi.h>
Common ACPI table header (System Description Table).
Every ACPI table starts with this 36-byte header. None of the fixed-size character arrays are nul-terminated.
Public Members
-
char signature[4]
table signature, NOT nul-terminated
-
uint32_t length
total table length in bytes (incl. header)
-
uint8_t revision
table-specific revision
-
uint8_t checksum
whole-table sum mod 256 must equal 0
-
char oem_id[6]
NOT nul-terminated.
-
char oem_table_id[8]
NOT nul-terminated.
-
uint32_t oem_revision
-
char creator_id[4]
NOT nul-terminated.
-
uint32_t creator_revision
-
char signature[4]
-
struct AxlAcpiMcfgEntry
- #include <axl-acpi.h>
One MCFG configuration-space allocation entry.
-
struct AxlAcpiMcfg
- #include <axl-acpi.h>
Decoded MCFG table.
-
struct AxlAcpiIoapic
- #include <axl-acpi.h>
x86 IOAPIC entry (MADT subtable type 1).
-
struct AxlAcpiGicRegion
- #include <axl-acpi.h>
AArch64 GIC redistributor / distributor region (MADT subtable types 12 = GICD, 14 = GICR).
-
struct AxlAcpiMadt
- #include <axl-acpi.h>
Decoded MADT table.
x86 firmware populates
ioapics; AArch64 firmware populatesgic_regions. Diagnostic code checks which set is non-empty for the running arch; the unused side stays zero.Public Members
-
uint32_t local_apic_addr
x86 LAPIC base (0 on AArch64)
-
uint32_t flags
MADT flags field.
-
size_t ioapic_count
-
AxlAcpiIoapic ioapics[16]
-
size_t gic_region_count
-
AxlAcpiGicRegion gic_regions[8]
-
uint32_t local_apic_addr
-
struct AxlAcpiFacp
- #include <axl-acpi.h>
Decoded FADT (Fixed ACPI Description Table).
Captures the small set of fields diagnostic code reaches for — SMI port + ACPI enable/disable values for power-state interaction, PM1 event/control block addresses for SCI handling, DSDT pointer for callers that walk AML themselves. The full FADT has many more fields; consumers needing them can read the table directly via
axl_acpi_find("FACP", NULL).Public Members
-
uint32_t firmware_ctrl
32-bit FACS pointer (legacy field)
-
uint64_t x_firmware_ctrl
64-bit FACS pointer (ACPI 2.0+, 0 if absent)
-
uint32_t dsdt
32-bit DSDT pointer (legacy field)
-
uint64_t x_dsdt
64-bit DSDT pointer (ACPI 2.0+, 0 if absent)
-
uint32_t smi_cmd
SMI command port.
-
uint8_t acpi_enable
value to write to smi_cmd to enable ACPI mode
-
uint8_t acpi_disable
value to write to smi_cmd to disable ACPI mode
-
uint16_t pm1a_evt_blk
PM1a event block port.
-
uint16_t pm1b_evt_blk
PM1b event block port (0 if not present)
-
uint16_t pm1a_cnt_blk
PM1a control block port.
-
uint16_t pm1b_cnt_blk
PM1b control block port (0 if not present)
-
uint8_t pm1_evt_len
length of PM1 event block in bytes
-
uint8_t pm1_cnt_len
length of PM1 control block in bytes
-
uint16_t iapc_boot_arch
IA-PC boot architecture flags (legacy enables)
-
uint16_t arm_boot_arch
ARM boot architecture flags (PSCI capabilities)
-
uint32_t firmware_ctrl