boredos_mirror/docs/architecture/ACPI/acpi_interface.md

5.2 KiB

ACPI

Power management and hardware enumeration via the Advanced Configuration and Power Interface.


BoredOS implements an ACPI subsystem that handles power state transitions, hardware discovery, and I2C device enumeration from the firmware tables. The implementation lives in src/drivers/ACPI/.

Table Discovery

At boot, the kernel requests the RSDP (Root System Description Pointer) from Limine. If the pointer is absent or fails its checksum, the kernel panics - this is a hard requirement, not a graceful fallback.

From the RSDP, the kernel determines which top-level descriptor table to use:

  • XSDT (64-bit pointers) - used when RSDP revision ≥ 2 and a valid xsdt_address is present.
  • RSDT (32-bit pointers) - fallback for older firmware.

This selection is handled transparently by acpi_get_sdt(), which walks whichever table is available and returns the first SDT matching a given 4-char signature.

Note

The DSDT is a special case - it is not listed in the XSDT/RSDT. It is instead pointed to directly by the dsdt field in the FADT. Use acpi_get_dsdt() to retrieve it.


FADT and ACPI Enable

The FADT (Fixed ACPI Description Table, signature "FACP") is located first. It contains the port addresses needed for power management and the SMI command port used to hand off hardware control from firmware to the OS.

If smi_cmd and acpi_enable are both non-zero, the kernel writes the enable byte to the SMI command port and spins on PM1a_CNT bit 0 until the hardware acknowledges. A timeout here is a fatal error.


MADT and Interrupt Routing

The MADT (Multiple APIC Description Table, signature "APIC") is parsed for Interrupt Source Override entries (type 2). These describe cases where a legacy ISA IRQ is wired to a different GSI on the I/O APIC - for example, IRQ 0 (PIT) may be redirected to GSI 2 on some platforms.

Two functions expose this mapping to the rest of the kernel:

  • acpi_irq_to_gsi(irq) - returns the GSI for a given IRQ, or the IRQ itself if no override exists.
  • acpi_irq_flags(irq) - returns the polarity and trigger mode flags for the override.

AML Parsing

The DSDT and any SSDTs present in the firmware contain AML (ACPI Machine Language) bytecode describing the hardware namespace. BoredOS does not implement a full AML interpreter - instead src/drivers/ACPI/acpi_aml.c implements a targeted byte-stream walker sufficient for I2C device enumeration and basic power management.

The walker handles:

  • _HID & _CID - Device identification. Supports inline strings and packed EISAID integers.
    • Specifically recognizes touchpads/input devices: PNP0C50, SYNA, ELAN, ALPS.
  • _CRS - Current Resource Settings. Scans for I2cSerialBusV2 descriptors (large item tag 0x8E) to extract slave address and bus speed.
  • Deep Scanning - If a device lacks a static _CRS, the walker scans AML methods for literal Buffer objects that contain I2C serial bus descriptors.
  • Controller Detection - Identifies Intel I2C controllers via HIDs INTC1040 and INTC1043.
  • Power States - Records presence of _PS0, _PS3, _PR0, _PR3 as flags for power management.

Important

While the walker can find literal buffers inside methods, it still cannot evaluate complex AML logic or dynamic expressions.


I2C Enumeration

acpi_i2c_enumerate() is called during kernel initialization. It performs a comprehensive sweep of the ACPI namespace:

  1. DSDT Walk: Scans the main DSDT for I2C devices.
  2. SSDT Walk: Iterates through all Secondary System Description Tables.
  3. Deep HID Search: Performs a raw byte search across all system tables for _HID patterns and specific EISAIDs (like SYNA) to catch devices that might be missed by the structured walker.

Devices are recorded in a global table, accessible via:

  • acpi_i2c_count() - Number of enumerated devices.
  • acpi_i2c_get(index) - Pointer to an aml_i2c_dev_t record containing the name, HID, slave address, and capabilities.

Shutdown

Performing a clean S5 (soft power-off) requires writing the correct SLP_TYP value to the PM1 Control Block. This value is firmware-specific and must be read from the _S5_ object in the DSDT.

aml_parse_s5() scans the DSDT AML for the _S5_ name and extracts SLP_TYPa and SLP_TYPb, pre-shifted to bit position 10 ready for outw. If the parse fails, the kernel falls back to SLP_TYP = 5, which works on the majority of hardware.

Shutdown sequence

On compliant hardware, step 1 is all that runs and the machine powers off immediately. The remaining steps target known virtual machine implementations that don't respond to standard ACPI.

  1. Write SLP_TYPa | SLP_EN to PM1a_CNT_BLK (and PM1b_CNT_BLK if present).
  2. Write known virtualizer-specific magic values - QEMU (0x604), VirtualBox (0x4004), Bochs (0xB004), Cloud Hypervisor (0x600) - to trigger their respective shutdown paths.
  3. Disable interrupts and halt indefinitely as a last resort.

Note

The virtualizer ports are sent after the ACPI write deliberately. On real hardware, sending those port values is harmless, but the ordering avoids any ambiguity about what triggered the power-off.