{% set rfcid = “RFC-0112” %} {% include “docs/contribute/governance/rfcs/_common/_rfc_header.md” %}
The Advanced Configuration and Power Interface (ACPI) is used on most x86 platforms to provide information about device topology, configuration, and provide power management functionality to the OS. Fuchsia's ACPI support is currently very limited: no support for ACPI devices which are enumerated by another bus driver (e.g. PCI), and no support for devices accessing device-specific configuration or power management methods outside of the x86 board driver.
This RFC introduces a mechanism for publishing composite devices consisting of both an ACPI device and a bus (i.e. I2C, SPI, PCI) device. It also adds support for using a wider range of ACPI functionality (receiving events, accessing configuration, and calling methods) outside of the x86 board driver.
Currently, all supported ACPI-only devices (i.e. not on the PCI bus) are enumerated in a single if/else statement. Devices that require access to other ACPI data (e.g. data from _DSD) are either part of the x86 board driver, hard code the data, or use device-specific hacks to give devices the data they need to configure themselves.
Note that x86 board driver is a misnomer: not all x86 platforms necessarily use ACPI. A better term might be PC board driver -- a board driver for the IBM PC platform. For simplicity's sake, we continue to refer to the driver as the x86 board driver. While this RFC attempts to outline a reasonably generic approach to ACPI that would be applicable to both x86 and ARM in the future, the primary focus of the RFC is changes to the x86 board driver, and future ARM ACPI work would likely require a separate board driver.
This way of doing things isn‘t scalable. As we want to support more devices, we’ll run into cases where our hardcoded values conflict with one another, or the number of drivers in the x86 board driver will become huge. This RFC will allow us to add more x86 platform and power management support without taking on more technical debt in the board driver.
Every existing device on x86 will be given a corresponding ACPI device (so long as the device has an entry in the ACPI tables). Each ACPI device will be published by the x86 board driver and will provide access to the device’s ACPI configuration and methods. The x86 board driver will also publish composite devices, which are children of the “real” device and the ACPI device. Rather than binding directly to the “real” device, a driver that wishes to use one of these devices will then bind to the composite device. The board driver will also discover devices on statically enumerated buses (i.e. I2C, SPI), and publish metadata informing the bus driver of the number of child devices it has.
A limited set of devices will also be given access to more dangerous ACPI operations like acquiring the ACPI global lock. In practice, not many devices require access to this functionality, so it will only be exposed to a set of devices allowlisted in the x86 board driver.
Below is an example of what a single branch of the device tree might look like, for a TPM device connected via a SPI bus.
When discovering ACPI devices, we need to determine if a given device is a bus driver, and if it is, what kind of bus it is. This information is important because it allows us to determine how composite devices added in the next step should bind with their ACPI and real counterparts. ACPI doesn't have a simple way of determining these two properties from a device, and nor does it guarantee that devices on a bus will be children of the bus. These necessitate splitting the device discovery and publishing process into three stages:
In order to access resources provided by the bus that a device exists on and configuration methods provided by ACPI, we need to create composite devices consisting of the “real” device published by the bus driver, and the ACPI device published by the x86 board driver.
Using the information gathered in the previous stage of implementation, the board driver will do one of two things, depending on the bus the device uses:
GetPciPlatformInfo()
call). The bus driver will then be responsible for publishing composite devices that bind to the ACPI device and the “real” device published by the bus driver.The reason for this separation of responsibilities is that for runtime-enumerated buses like PCI, device drivers use information that is provided by the bus driver to bind (e.g. vendor and device IDs). Publishing these composites from the x86 board driver would involve devising a mechanism for gathering this information from the bus driver.
Similarly, other buses like I2C and SPI don‘t have information that device drivers want to bind against. They typically only know enough information to address the device (e.g. an I2C address, a SPI chip select number), but not what kind of device is attached to the device. This information is instead present in the ACPI tables as hardware or compatible IDs. Publishing these composites from the bus driver in a useful manner would therefore involve telling the bus driver information which it doesn’t really need (the HID and CID).
For the first case, the x86 board driver will tell the bus which devices need to be made composite, and the bus driver will publish a composite device with two fragments. For instance, the PCI bus driver might publish a composite device with two fragments. The first would bind to the device on the PCI bus, with a bind program similar to:
BIND_PROTOCOL == PCI BIND_PCI_TOPO == 0x5a
The second would bind to the equivalent device published by the ACPI bus:
BIND_PROTOCOL == ACPI BIND_ACPI_BUS_TYPE == PCI BIND_PCI_TOPO == 0x5a
For the second case, the x86 board driver will use information it knows about each bus to create the two fragments, similar to above. For instance, an I2C composite device would bind to its I2C parent with a bind program similar to:
BIND_PROTOCOL == I2C BIND_I2C_ADDRESS == 0xaa BIND_I2C_BUS_ID == 0
and the corresponding ACPI bind program would look like:
BIND_PROTOCOL == ACPI BIND_ACPI_BUS_TYPE == I2C BIND_I2C_ADDRESS == 0xaa BIND_I2C_BUS_ID == 0
The x86 board driver uses the ACPICA library to interface with ACPI. We will expose a subset of the ACPICA library to each device as a FIDL interface (we may want to expose more in the future, but this covers everything used by all in-tree drivers at the time of writing):
AcpiEvaluateObject
- which allows devices arbitrary access to evaluate methods in the tree, either to control device state or get configuration information. We will restrict devices to evaluating methods on itself and its children.There is one more dangerous mechanism that we are going to restrict to a separate FIDL protocol exposed only to allowlisted devices, AcpiAcquireGlobalLock
. This is currently only used by the acpi-ec
driver, and it doesn‘t appear to be widely used outside of the EC. We’ll allowlist this based on HID, starting with only the ACPI EC HID.
At this stage, we'll be ready to start migrating drivers to use ACPI. For drivers currently in the x86 board driver, this will involve a rewrite to use the new ACPI-over-FIDL protocol. For drivers outside of the x86 board driver, such as the Intel I2C driver or I2C HID driver, this will be relatively simple - replacing existing hardcoded configuration values with new values determined from ACPI.
The performance impact is expected to be minimal. Most of the new overhead will come from drivers currently in the x86 board driver being moved to their own drivers and being forced to do IPC to interact with ACPI.
None.
We‘ll rely heavily on unit tests for testing the board driver implementation, since ACPI assumes access to the entire system. These unit tests will verify all functionality works as expected (e.g. bus type inference, device discovery, etc.). We’ll also include tests to make sure that all the FIDL methods implemented by the ACPI device are implemented correctly.
Device drivers will be able to mock out the new ACPI FIDL protocol fairly easily, which will allow us to write unit and integration tests for device drivers that previously could not be tested (or were difficult to test).
The FIDL ACPI protocol will contain documentation on how to use it and its limitations. Some documentation will need to be written explaining the “ACPI+real” composite model for devices on ACPI systems, and how drivers should handle binding in that scenario.
Support for new bus types on x86 systems that are exposed via ACPI will require modifications to the x86 board driver. This is unfortunately a limitation of the way ACPI is implemented: there is no support for “generic” or “unknown” buses. We expect the maintenance burden of this to be relatively small, and easily offset by the gains from being able to access ACPI tables outside the board driver.