| 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 总线:设备.功能),同时为每个总线设备创建总线 ID。这一点非常重要,因为我们需要区分例如不同总线上地址相同的两个 I2C 设备。
发布复合“ACPI + 真实”设备。
为了访问设备所在总线提供的资源和 ACPI 提供的配置方法,我们需要创建由总线驱动程序发布的“真实”设备和 x86 板驱动程序发布的 ACPI 设备组成的复合设备。
根据在实现的上一个阶段收集的信息,主板驱动程序将执行以下两项操作之一,具体取决于设备使用的总线:
- 对于在运行时枚举的总线(例如 PCI、USB):通过特定于协议的机制(例如,对于 PCI,这将是作为 Pciroot 的
GetPciPlatformInfo()调用的一部分返回的 bus:device.functions 列表)提供有关 ACPI 已知的设备的信息。然后,总线驱动程序将负责发布绑定到 ACPI 设备和总线驱动程序发布的“真实”设备的复合设备。 - 对于未在运行时枚举的总线(例如 I2C、SPI):ACPI 驱动程序将发布复合设备,并使用现有机制(例如 DEVICE_METADATA_I2C_CHANNELS)将其子设备告知总线驱动程序。在这种情况下,总线驱动程序仅发布由复合 fragment 绑定的设备。x86 板驱动程序发布复合设备。
之所以要分离这些职责,是因为对于 PCI 等运行时枚举总线,设备驱动程序会使用总线驱动程序提供的信息来绑定(例如供应商和设备 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 进行交互。我们将 ACPICA 库的一个子集作为 FIDL 接口公开给每个设备(我们可能希望将来公开更多,但目前这涵盖了所有树内驱动程序在撰写本文时使用的所有内容):
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”复合模型,以及驱动程序应如何处理该场景中的绑定。
缺点、替代方案和未知因素
不支持任意总线类型
支持通过 ACPI 在 x86 系统上公开的新总线类型将需要修改 x86 板驱动程序。遗憾的是,这是 ACPI 实现方式的限制:不支持“通用”或“未知”总线。我们预计,维护此功能的负担相对较小,并且能够轻松通过访问主板驱动程序之外的 ACPI 表来抵消。