| RFC-0112:x86 上的 ACPI 支持 | |
|---|---|
| 状态 | 已接受 |
| 区域 |
|
| 说明 | 改进了 x86 上的 ACPI 支持。 |
| 问题 | |
| Gerrit 更改 | |
| 作者 | |
| 审核人 | |
| 提交日期(年-月-日) | 2021-06-11 |
| 审核日期(年-月-日) | 2021-07-21 |
摘要
大多数 x86 平台都使用高级配置与电源接口 (ACPI) 来提供有关设备拓扑、配置的信息,并为操作系统提供电源管理功能。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 尝试概述一种合理的通用 ACPI 方法,该方法将来适用于 x86 和 ARM,但此 RFC 的主要重点是更改 x86 板驱动程序,而未来的 ARM ACPI 工作可能需要单独的板驱动程序。
这种做法无法扩缩。由于我们希望支持更多设备,因此会遇到硬编码值相互冲突的情况,或者 x86 板驱动程序中的驱动程序数量会变得非常庞大。此 RFC 将允许我们添加更多 x86 平台和电源管理支持,而不会在板驱动程序中承担更多技术债务。
设计
x86 上的每个现有设备都将获得相应的 ACPI 设备(只要该设备在 ACPI 表中有一个条目)。 每个 ACPI 设备都将由 x86 板驱动程序发布,并将提供对设备的 ACPI 配置和方法的访问权限。x86 板驱动程序还将发布复合设备,这些设备是“真实”设备和 ACPI 设备的子设备。如果驱动程序希望使用其中一个设备,则会绑定到复合设备,而不是直接绑定到“真实”设备。板驱动程序还会在静态枚举的总线(即 I2C、SPI)上发现设备,并发布元数据,告知总线驱动程序其拥有的子设备数量。
一小部分设备还将获得对更危险的 ACPI 操作(例如获取 ACPI 全局锁)的访问权限。实际上,没有多少设备需要访问此功能,因此它只会向 x86 板驱动程序中列入许可名单的一组设备公开。
下面是一个示例,展示了通过 SPI 总线连接的 TPM 设备的设备树的单个分支可能是什么样的。

实现
发现设备关系并在系统上发布 ACPI 设备。
发现 ACPI 设备时,我们需要确定给定设备是否为总线驱动程序,如果是,则确定它是哪种总线。此信息非常重要,因为它允许我们确定在下一步中添加的复合设备应如何与其 ACPI 和真实对应项绑定。ACPI 没有简单的方法可以从设备确定这两个属性,也没有保证总线上的设备将是总线的子设备。因此,我们需要将设备发现和发布过程分为三个阶段:
- 发现树中的设备,设置设备 ACPI 句柄到其内部表示的映射,其中包括我们最终发布设备时所需的信息,例如其父设备、句柄和总线类型。
- 对于每个设备,检查其 _CRS(“当前资源”)以查看它是否具有 I2C/SPI/等资源。如果有,请获取总线设备(在资源中指定)的句柄,在第 1 步中的映射中查找该句柄,并告知其新的子设备和总线类型。
- 按照在第 1 步中发现的顺序发布所有设备,包括与设备的总线相关的绑定属性(例如 PCI 总线:device.function)的信息,同时为每个总线设备创建总线 ID。这一点非常重要,因为我们需要区分例如不同总线上具有相同地址的两个 I2C 设备。
发布复合“ACPI + 真实”设备。
为了访问设备所在的总线提供的资源以及 ACPI 提供的配置方法,我们需要创建由总线驱动程序发布的“真实”设备和 x86 板驱动程序发布的 ACPI 设备组成的复合设备。
使用在实现的前一阶段收集的信息,板驱动程序将执行以下两项操作之一,具体取决于设备使用的总线:
- 对于在运行时枚举的总线(例如 PCI、USB):通过协议专用机制提供有关 ACPI 已知的设备的信息(例如,对于 PCI,这将是作为 Pciroot 的
GetPciPlatformInfo()调用的一部分返回的 bus:device.functions 列表)。然后,总线驱动程序将负责发布绑定到 ACPI 设备和总线驱动程序发布的“真实”设备的复合设备。 - 对于在运行时未枚举的总线(例如 I2C、SPI):ACPI 驱动程序将发布复合设备,并使用现有机制(例如 DEVICE_METADATA_I2C_CHANNELS)告知总线驱动程序其子设备。在这种情况下,总线驱动程序仅发布由复合 fragment 绑定的设备。x86 板驱动程序发布复合设备。
这种职责分离的原因是,对于运行时枚举的总线(例如 PCI),设备驱动程序使用总线驱动程序提供的信息进行绑定(例如供应商和设备 ID)。从 x86 板驱动程序发布这些复合设备将涉及设计一种从总线驱动程序收集此信息的机制。
同样,其他总线(例如 I2C 和 SPI)也没有设备驱动程序想要绑定的信息。它们通常只知道足够的信息来寻址设备(例如 I2C 地址、SPI 芯片选择号),但不知道哪种设备连接到该设备。此信息改为以硬件或兼容 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 进行交互。我们将 ACPICA 库的子集作为 FIDL 接口公开给每个设备(我们可能希望将来公开更多,但此子集涵盖了编写时所有树内驱动程序使用的所有内容):
AcpiEvaluateObject- 允许设备任意访问以评估 树中的方法,以控制设备状态或获取配置 信息。我们将限制设备评估自身及其子设备的方法。- 事件处理程序 - ACPI 支持三种事件类型:固定事件、通用事件和设备对象通知。这三种事件类型大致相似:设备可以启用/停用事件并安装事件处理脚本,该处理脚本在收到事件时调用。我们将通过 FIDL 公开启用、停用和安装方法调用 - 移除将通过关闭传递给安装的通道的设备端隐式实现。
- 地址空间处理程序 - 与事件处理程序非常相似,并将以类似的方式处理。
我们还将限制另一种危险机制,即 AcpiAcquireGlobalLock,它是一个单独的 FIDL 协议,仅向列入许可名单的设备公开。目前,它仅由
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+真实”复合模型,以及驱动程序应如何处理该场景中的绑定。
缺点、替代方案和未知事项
不支持任意总线类型
对通过 ACPI 公开的 x86 系统上的新总线类型的支持将需要修改 x86 板驱动程序。遗憾的是,这是 ACPI 实现方式的限制:不支持“通用”或“未知”总线。 我们预计此维护负担相对较小,并且可以轻松地通过能够访问板驱动程序之外的 ACPI 表所带来的收益来抵消。