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 sig after prev.

Pass NULL as prev to 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->length bytes; 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 h is 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
struct AxlAcpiMcfgEntry
#include <axl-acpi.h>

One MCFG configuration-space allocation entry.

Public Members

uint64_t base_addr

ECAM base physical address for this segment.

uint16_t segment

PCI segment group number.

uint8_t start_bus

first bus number covered (inclusive)

uint8_t end_bus

last bus number covered (inclusive)

struct AxlAcpiMcfg
#include <axl-acpi.h>

Decoded MCFG table.

Public Members

size_t count

populated entries

AxlAcpiMcfgEntry segments[16]
struct AxlAcpiIoapic
#include <axl-acpi.h>

x86 IOAPIC entry (MADT subtable type 1).

Public Members

uint8_t id

IOAPIC ID.

uint32_t addr

IOAPIC physical address.

uint32_t global_irq_base

first global system interrupt the IOAPIC covers

struct AxlAcpiGicRegion
#include <axl-acpi.h>

AArch64 GIC redistributor / distributor region (MADT subtable types 12 = GICD, 14 = GICR).

Public Members

uint8_t subtable_type

12=GICD, 14=GICR

uint64_t addr

region physical base

uint32_t length

region length (GICR only; GICD is a fixed 64KiB)

struct AxlAcpiMadt
#include <axl-acpi.h>

Decoded MADT table.

x86 firmware populates ioapics; AArch64 firmware populates gic_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]
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)