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

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

改进了 x86 上的 ACPI 支持。

问题
  • 78198
Gerrit 更改
  • 542001
作者
审核人
提交日期(年-月-日)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,但 RFC 的主要重点是对 x86 开发板驱动程序的更改,未来的 ARM ACPI 工作可能需要单独的开发板驱动程序。

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

设计

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

一组有限的设备还可以访问更危险的 ACPI 操作,例如获取 ACPI 全局锁。在实践中,需要访问此功能的设备并不多,因此它只会提供给在 x86 开发板驱动程序中列入许可名单的一组设备。

下例展示了对于通过 SPI 总线连接的 TPM 设备,设备树的单个分支可能如下所示。

SPI 设备树的分支

实现

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

在发现 ACPI 设备时,我们需要确定指定设备是否为总线驱动程序,以及是否为总线类型。这些信息非常重要,因为我们可以通过这些信息确定下一步中添加的复合设备应如何与其 ACPI 以及实际设备绑定。ACPI 并没有简单地从设备确定这两个属性,也无法保证总线上的设备将成为总线的子节点。这就需要将设备发现和发布流程分为三个阶段:

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

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

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

利用在前一实现阶段收集的信息,板驱动程序将根据设备使用的总线执行以下两种操作之一:

  1. 对于在运行时枚举的总线(例如 PCI、USB):通过协议专用机制(例如,对于 PCI,这将是作为 Pciroot 的 GetPciPlatformInfo() 调用的一部分返回的 bus:device.functions)提供 ACPI 已知的设备的相关信息。然后,总线驱动程序将负责发布绑定到 ACPI 设备和由总线驱动程序发布的“实际”设备的复合设备。
  2. 对于未在运行时枚举的总线(例如 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 连接。我们将以 FIDL 接口的形式向每个设备公开 ACPICA 库的一部分(我们以后可能会公开更多该库,但这涵盖了撰写本文时所有树内驱动程序使用的所有内容):

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

还有一种危险的机制,我们将限制其使用单独的 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+实际”复合模型,以及驱动程序应在这种情况下如何处理绑定。

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

不支持任意总线类型

要在 x86 系统上支持通过 ACPI 公开的新总线类型,需要修改 x86 开发板驱动程序。遗憾的是,这限制了 ACPI 的实现方式:不支持“通用”或“未知”总线。我们预计此过程的维护负担相对较小,并且很容易被因能够在板驱动程序外部访问 ACPI 表而获益抵消。

早期技术和参考资料