RFC-0112:对 x86 设备提供 ACPI 支持

RFC-0112:x86 上的 ACPI 支持
状态已接受
区域
  • 设备
说明

改进了 x86 上的 ACPI 支持。

问题
Gerrit 更改
作者
审核人
提交日期(年-月-日)2021-06-11
审核日期(年-月-日)2021-07-21

摘要

高级配置和电源接口 (ACPI) 在大多数 x86 平台上用于提供有关设备拓扑、配置的信息,并向操作系统提供电源管理功能。Fuchsia 的 ACPI 支持目前非常有限:不支持由其他总线驱动程序(例如 PCI)枚举的 ACPI 设备,也不支持访问 x86 板级驱动程序之外的设备专用配置或电源管理方法的设备。

此 RFC 引入了一种发布复合设备的机制,该复合设备由 ACPI 设备和总线(即 I2C、SPI、PCI)设备组成。它还添加了对在 x86 板级驱动程序之外使用更广泛的 ACPI 功能(接收事件、访问配置和调用方法)的支持。

设计初衷

目前,所有受支持的仅限 ACPI 设备(即不在 PCI 总线上)都在单个 if/else 语句中枚举。需要访问其他 ACPI 数据(例如 _DSD 中的数据)的设备要么是 x86 板级驱动程序的一部分,要么将数据硬编码,或者使用设备专用黑客攻击来向设备提供它们自行配置所需的数据。

请注意,“x86 板级驱动程序”这个术语有误:并非所有 x86 平台都必须使用 ACPI。更好的术语可能是 PC 板级驱动程序,即 IBM PC 平台的板级驱动程序。为简单起见,我们将继续将该驱动程序称为 x86 板级驱动程序。虽然本 RFC 尝试概述一种适用于未来 x86 和 ARM 的合理通用 ACPI 方法,但其主要重点是 x86 板级驱动程序的更改,未来的 ARM ACPI 工作可能需要单独的板级驱动程序。

这种做法不可扩展。由于我们希望支持更多设备,因此可能会遇到以下情况:硬编码的值相互冲突,或者 x86 板级驱动程序中的驱动程序数量会变得非常庞大。通过此 RFC,我们可以添加更多 x86 平台和电源管理支持,而无需在开发板驱动程序中承担更多技术债务。

设计

x86 上的每台现有设备都将获得相应的 ACPI 设备(前提是该设备在 ACPI 表中具有条目)。x86 板级驱动程序将发布每个 ACPI 设备,并提供对设备的 ACPI 配置和方法的访问权限。x86 主板驱动程序还会发布复合设备,这些设备是“真实”设备和 ACPI 设备的子设备。然后,希望使用其中某个设备的驱动程序将绑定到复合设备,而不是直接绑定到“真实”设备。板级驱动程序还会发现静态枚举的总线(即 I2C、SPI)上的设备,并发布元数据,告知总线驱动程序其子设备数量。

一小部分设备还将获得执行更危险 ACPI 操作(例如获取 ACPI 全局锁)的权限。实际上,只有少数设备需要访问此功能,因此它只会向 x86 开发板驱动程序中列入许可名单的一组设备公开。

下面的示例展示了通过 SPI 总线连接的 TPM 设备的设备树的单个分支可能是什么样子。

SPI 的设备树分支

实现

发现设备关系并在系统上发布 ACPI 设备。

在发现 ACPI 设备时,我们需要确定给定设备是否为总线驱动程序,如果是,则确定它是哪种总线。这些信息非常重要,因为它们可让我们确定在下一步中添加的复合设备应如何与其 ACPI 和真实对应项绑定。ACPI 没有简单的方法来从设备确定这两个属性,也无法保证总线上的设备将是总线的子设备。因此,需要将设备发现和发布流程分为三个阶段:

  1. 发现树中的设备,将设备的 ACPI 句柄与其内部表示法建立映射,其中包括最终发布设备时所需的信息,例如其父设备、句柄和总线类型。
  2. 对于每台设备,请检查其 _CRS(“当前资源”),以了解其是否具有 I2C/SPI/等资源。如果是,请获取对总线设备的句柄(在资源中指定),在第 1 步中的映射中查找该句柄,并告知其新的子设备和总线类型。
  3. 按第 1 步中发现的顺序发布所有设备,包括将与设备总线相关的信息作为绑定属性(例如 PCI bus:device.function)发布,同时为每个总线设备创建总线 ID。这一点非常重要,因为我们需要区分不同总线上位于同一地址的两个 I2C 设备。

发布复合“ACPI + 真实”设备。

为了访问设备所在的总线提供的资源以及 ACPI 提供的配置方法,我们需要创建由总线驱动程序发布的“真实”设备和 x86 板级驱动程序发布的 ACPI 设备组成的复合设备。

板级驱动程序将使用在上一个实现阶段收集的信息执行以下两项操作之一,具体取决于设备使用的总线:

  1. 对于在运行时枚举的总线(例如 PCI、USB):提供 ACPI 通过特定于协议的机制知道的设备的相关信息(例如,对于 PCI,这将是作为 Pciroot 的 GetPciPlatformInfo() 调用的一部分返回的 bus:device.functions 列表)。然后,总线驱动程序将负责发布绑定到 ACPI 设备和总线驱动程序发布的“真实”设备的复合设备。
  2. 对于在运行时未枚举的总线(例如 I2C、SPI):ACPI 驱动程序将发布复合设备,并使用现有机制(例如 DEVICE_METADATA_I2C_CHANNELS)告知总线驱动程序其子设备。在这种情况下,总线驱动程序只会发布由复合 fragment 绑定的设备。x86 板级驱动程序会发布复合设备。

之所以进行这种职责分离,是因为对于 PCI 等运行时枚举的总线,设备驱动程序使用总线驱动程序提供的信息进行绑定(例如供应商 ID 和设备 ID)。从 x86 板级驱动程序发布这些复合元件需要设计一种从总线驱动程序收集此类信息的机制。

同样,I2C 和 SPI 等其他总线也不包含设备驱动程序想要绑定的相关信息。他们通常只知道足够的信息来寻址设备(例如 I2C 地址、SPI 芯片选择编号),但不知道连接到设备的是什么类型的设备。相反,这些信息以硬件 ID 或兼容 ID 的形式显示在 ACPI 表中。因此,以有用的方式从总线驱动程序发布这些复合元件需要告知总线驱动程序它实际上不需要的信息(HID 和 CID)。

绑定程序示例

对于第一种情况,x86 板级驱动程序将告知总线需要将哪些设备组合起来,然后总线驱动程序将发布一个包含两个 fragment 的复合设备。例如,PCI 总线驱动程序可能会发布包含两个 fragment 的复合设备。第一个会绑定到 PCI 总线上的设备,绑定程序类似于:

BIND_PROTOCOL == PCI
BIND_PCI_TOPO == 0x5a

第二个会绑定到 ACPI 总线发布的等效设备:

BIND_PROTOCOL == ACPI
BIND_ACPI_BUS_TYPE == PCI
BIND_PCI_TOPO == 0x5a

对于第二种情况,x86 板级驱动程序将使用它对每个总线的了解来创建两个 fragment,与上述情况类似。例如,I2C 复合设备会使用类似于以下内容的绑定程序绑定到其 I2C 父级:

BIND_PROTOCOL == I2C
BIND_I2C_ADDRESS == 0xaa
BIND_I2C_BUS_ID == 0

相应的 ACPI 绑定程序如下所示:

BIND_PROTOCOL == ACPI
BIND_ACPI_BUS_TYPE == I2C
BIND_I2C_ADDRESS == 0xaa
BIND_I2C_BUS_ID == 0

通过 FIDL 公开 ACPI 事件、配置和方法

x86 板级驱动程序使用 ACPICA 库与 ACPI 交互。我们将以 FIDL 接口的形式向每个设备公开 ACPICA 库的一部分(我们将来可能希望公开更多内容,但这涵盖了撰写本文时所有树内驱动程序使用的所有内容):

  • AcpiEvaluateObject - 允许设备对树中的评估方法进行任意访问,以控制设备状态或获取配置信息。我们将限制设备仅对自身和其子级评估方法。
  • 事件处理脚本 - ACPI 支持三种事件类型:固定事件、通用事件和设备对象通知。这三种事件大致类似:设备可以启用/停用事件,并安装事件处理程序,以便在收到事件时调用该处理程序。我们将通过 FIDL 公开启用、停用和安装方法调用 - 移除将通过关闭传递给安装的通道的设备端来隐式实现。
  • 地址空间处理脚本 - 与事件处理脚本非常相似,并且将以类似的方式处理。

还有一种更危险的机制,我们将其限制为仅向列入许可名单的设备公开的单独 FIDL 协议 AcpiAcquireGlobalLock。目前,只有 acpi-ec 驱动程序使用此方法,并且它似乎在 EC 之外没有广泛使用。我们将根据 HID 将其列入许可名单,首先仅列入 ACPI EC HID。

迁移驱动程序

至此,我们已准备好开始迁移驱动程序以使用 ACPI。对于 x86 板级驱动程序中当前使用的驱动程序,这将涉及重写,以使用新的 ACPI-over-FIDL 协议。对于 x86 板级驱动程序之外的驱动程序(例如 Intel I2C 驱动程序或 I2C HID 驱动程序),这相对简单 - 只需将现有的硬编码配置值替换为通过 ACPI 确定的新值即可。

性能

对广告效果的影响预计会很轻微。大部分新开销将来自 x86 板级驱动程序中当前的驱动程序被移至自己的驱动程序,并被迫执行 IPC 以与 ACPI 交互。

安全注意事项

  • ACPI 方法可以执行任何操作 - 归根结底,即使采用上述范围限定,ACPI 方法也可以执行它想执行的任何操作(例如在不合适的时机关闭硬件),因此,有权调用该方法的任何驱动程序也可以执行它想执行的任何操作。因此,我们需要信任使用 ACPI(以及 ACPI 表本身)的所有驱动程序。这与现状(所有 ACPI 驱动程序都在板级驱动程序中)没有任何变化。另请注意,所有开发板驱动程序都具有相同的权限,并且开发板驱动程序可以由外部提供(即未在 fuchsia.git 中编写和构建)。未来可能需要改进可审核性和控制功能。

隐私注意事项

无。

测试

由于 ACPI 假定对整个系统有访问权限,因此我们将非常依赖单元测试来测试板级驱动程序实现。这些单元测试将验证所有功能是否按预期运行(例如总线类型推理、设备发现等)。我们还将添加测试,以确保 ACPI 设备实现的所有 FIDL 方法均已正确实现。

设备驱动程序将能够非常轻松地模拟新的 ACPI FIDL 协议,这让我们能够为之前无法测试(或难以测试)的设备驱动程序编写单元测试和集成测试。

文档

FIDL ACPI 协议将包含有关如何使用该协议及其限制的文档。需要编写一些文档,说明 ACPI 系统上设备的“ACPI+real”复合模型,以及驱动程序应如何在这种情况下处理绑定。

缺点、替代方案和未知情况

不支持任意总线类型

如需在 x86 系统上支持通过 ACPI 公开的新总线类型,则需要修改 x86 板级驱动程序。遗憾的是,这是 ACPI 实现方式的限制:不支持“通用”或“未知”总线。我们预计,这样做的维护负担相对较小,并且可以通过能够在板级驱动程序之外访问 ACPI 表带来的好处轻松抵消。

在先技术和参考文档