For the complete documentation index, see llms.txt. This page is also available as Markdown.

嵌入式控制器的 ACPI 支持

ARM64 是广泛应用于各种产品的单一架构——它既可用于最小型的嵌入式设备,也可用于移动设备、企业级产品,甚至是服务器级解决方案。对服务器级设备的支持要求遵循一定的标准,例如启动方式、与固件的交互、平台描述等。而事实证明,这种模式同样适用于非服务器设备。本文将探讨 FreeBSD 对这些设备的支持情况,重点关注 ACPI 体系下的嵌入式控制器处理方式。

处理遗留问题

近十年前,64 位 ARM 架构问世时,直接继承了其 32 位前身的生态系统,而 32 位 ARM 在嵌入式市场中长期占据主导地位。然而,为每个平台维护完全定制化的板级支持包(BSP)成为新架构发展的沉重负担。

在一定程度上,设备树(DT)的引入提高了可移植性,并允许在多个设备上使用单一内核镜像,但这并未彻底解决问题。设备树的描述方式十分灵活,不幸的是,厂商经常滥用这种灵活性,导致设备树绑定随时间推移变得不一致。哪怕在今天,U-Boot 使用的设备树 blob 仍然可能与操作系统引导时使用的设备树不同(尽管它们描述的是同一块硬件!),而且通常缺乏向后兼容性。在这样的限制下,实现广泛的软件生态系统和多操作系统支持这一长期目标将十分困难。

不过,早已有成熟的解决方案存在。x86 体系所使用的接口被引入并扩展到了 ARM64,包括启动过程、EFI、SMBIOS 和 ACPI。在符合这些标准并使用合适固件的服务器级设备上,现在可以直接使用安装镜像来安装 FreeBSD、其他操作系统或虚拟机管理程序(hypervisor)。

那么,小型嵌入式平台呢?幸运的是,它们也能以类似方式利用这一丰富的生态系统。当然,这需要一定的前提——硬件不能偏离标准(至少不能偏离太多),并且固件需要满足严格的要求。这些规范被归纳在一系列标准中,即 BSA(ARM 基本系统架构)和 BBR(ARM 基础启动要求)。结果是,现在已经有一些 ARM64 平台可以通过单一固件镜像和 ACPI 描述成功引导 FreeBSD、Windows 和多个 Linux 发行版。

这些设备有何特殊之处?与传统服务器(通常拥有大量 CPU、DRAM 和 PCIe 根复合体)相比,嵌入式领域的 SoC 还支持连接到其内部总线的各种控制器。因此,这些控制器不会在 PCIe 枚举过程中被自动发现,而是需要采用不同的处理方式。硬件描述必须明确引用这些接口,并包含能够被操作系统解析和解释的平台数据。最近,FreeBSD 内核扩展了从 ACPI 表中获取这类信息的能力,新增了一些特性。

什么是 ACPI?

在深入讨论细节之前,值得简要介绍一下 ACPI(高级配置与电源管理接口)。ACPI 是固件与操作系统之间的接口,主要用于描述和配置硬件。该 标准 已经发展了近 30 年,并涵盖了多个关键 概念,例如电源管理、温度/电池管理、硬件配置和嵌入式控制器的描述。ACPI 还定义了 ACPI 源语言(ASL),可用于创建低级硬件配置例程等。ASL 编译为字节码——ACPI 机器语言(AML),由内核解释并执行。

平台信息存储在所谓的“表”(table)中,这些表本质上是系统内存地址空间中的一组层次化结构。ACPI 的起点是根系统描述指针(RSDP),它由固件配置,并指向扩展系统描述表(XSDT),后者进一步链接到次级表。首个次级表始终是固定 ACPI 描述表(FADT),其中包含描述硬件固定 ACPI 特性的各种固定长度条目。

ACPI RSDP 和表结构示意图

图 1. 根系统描述指针(RSDP)和表

来源:UEFI ACPI 6.4 规范

ACPI 规范定义了多种 专用表,但对嵌入式设备而言,有几种表格尤为重要,例如:

  • 通用定时器描述表(GTDT)

  • 多 APIC 描述表(MADT)

  • 处理器属性拓扑表(PPTT)

  • 串口控制台重定向表(SPCR)

  • PCI Express 内存映射配置空间基地址描述表(MCFG)

  • 差异化系统描述表(DSDT)

上述表格中的最后一项——DSDT,尤为关键。DSDT 始终由 FADT 引用,并包含 CPU 列表、电源管理特性、PCIe 根复合体(root complex)和所有嵌入式控制器的描述。通常,它会配有 SSDT(次级系统描述表),可能是一个或多个实例。这种结构允许程序员在平台描述代码中按逻辑拆分各种功能。

上述表格的定义已扩展,以涵盖 ARM64 特定的值和类型(例如中断控制器)——所有这些都被汇总到 ACPICA(ACPI 组件架构) 中。ACPICA 是开源参考代码库,供各操作系统使用和补充。FreeBSD 始终与其最新版本保持同步。现在来看看这些表格在 FreeBSD ARM64 移植中的处理方式。

ARM64 的 ACPI 基础部分

ARM64 SoC 依照标准由 ACPI 表格描述。例如,GTDT 列出了定时器和看门狗,MADT 则包含中断控制器(目前仅支持 GICv2 和 GICv3)。进一步来看嵌入式控制器,控制台由 SPCR(可选 DBG2 表)描述——推荐使用 ARM SBSA UART(PL011) 或兼容 16550 的 UART,不过近年来 ARM 生态中的更多 UART 类型也已加入支持 列表

PCIe 控制器的描述更为复杂,必须包含在 MCFG 和 DSDT/SSDT 表中。对于 ARM64,唯一允许的类型是完全兼容标准 ECAM(Enhanced Configuration Access Mechanism) 的通用 PCIe 控制器,该类型受 pci_host_generic_acpi 驱动程序支持。建议新设计在芯片中保留未经修改的标准 ECAM 实现,但对于现有产品,往往无法满足这一要求。因此,FreeBSD 的 pci_host_generic_acpi 驱动现在允许通过 配置空间访问 quirks 处理对标准的偏离。另一种解决方案是通过 SMCCC(Secure Monitor Call Calling Convention) 接口,支持从固件执行低级例程。目前该机制在 树莓派 4 上可用,但 FreeBSD 仍未实现该选项。

处理嵌入式控制器

连接到 SoC 内部总线的嵌入式控制器在 ACPI 表中可以两种方式处理:

  1. 使用“方法”(指令)编译为 AML——这样操作系统可以直接解释并执行相应指令。例如,热管理(thermal management)、SMBUSGPIO 就采用这种方式。

  2. 使用标准对象(Standard Objects)描述——对于 ACPI 规范未明确定义的设备或子系统,可利用 ACPI 提供的标准对象,让操作系统解析并获取驱动程序所需的所有硬件资源。

第二种方式是 ACPI 支持非服务器级 ARM64 SoC 的关键,并且 FreeBSD 内核已实现该方案。

ACPI 与设备树总线层次结构对比图

图 2. ACPI 和设备树世界中 FreeBSD 总线层次结构示例的高层比较。

从高层来看,FreeBSD 中嵌入式控制器的总线层级在 ACPIDT(Device Tree) 体系下是相似的(参见 图 2)。这对于设计设备驱动程序非常有帮助,因为在内核初始化过程中,平台数据结构可以在两种情况下以相同方式填充。随后,探测到的驱动程序可以凭 ACPI_HID 字段值匹配,该字段相当于 Device Tree 中的 compatible 字符串。此外,其他标准类型的资源也以类似方式处理。

FreeBSDACPI 支持的前两种 ARM64 嵌入式控制器是 USBSATA。其中,SATA 的匹配方式较为特殊,并非通过 _HID 进行匹配,而是通过 设备类值(device class value) 进行匹配,即 ACPI _CLS 对象(参见 清单 1)。

清单 1. ACPI 表 中的 AHCI 控制器描述示例

FreeBSDXHCIAHCI 驱动程序需要在 DSDT/SSDT 中使用完全通用的描述。清单 1 展示了 AHCI 控制器的示例,它包含引用 唯一 ID 的对象,以及 缓存一致性信息内存/中断资源。所有 非标准 配置(例如 寄存器配置、时钟管理电源管理 处理)都必须由 固件 预先配置并实现。

自定义 ACPI 描述

如果某个控制器需要由其专用驱动程序处理的自定义绑定,该如何实现?

在 FreeBSD 中,直到最近还只能在 DT(Device Tree) 体系下实现自定义绑定,方法是使用节点属性。但 ACPI 规范实际上定义了一个可选对象 _DSD (Device Specific Data),它可以包含相同的信息。

借助 FreeBSD 的总线层级体系(参见 图 2),设计并实现了 新的通用方案,支持在不依赖具体描述方式的情况下获取控制器特定的数据。此外,还引入了以下辅助函数:

这些函数允许子设备驱动以相同方式访问父总线提供的设备特定数据,无论系统是以 ACPI 还是 DT 启动,都能执行相同的代码路径。该方案后来 进一步扩展,以涵盖 ACPIDT 体系下的多种属性类型。

该方案的典型应用是 SD/MMC 子系统,其中包括:

其中,Marvell Xenon 驱动被拆分为三个文件:

  1. ACPI 适配代码

  2. simplebus 适配代码(适用于 Device Tree

除了宏 DRIVER_MODULE/DEFINE_CLASS_1 的不同使用方式,simplebus 适配代码还额外解析了稳压器(regulators)和卡检测 GPIO 引脚(card detect GPIO pins),而 ACPI 方式下这些信息则由固件预先设定。

清单 2. 设备树中的 Marvell Xenon SD/MMC 控制器

清单 3. ACPI 中的 Marvell Xenon SD/MMC 控制器

清单 2 和 3 展示了同一控制器实例的 DT(设备树)和 ACPI 节点,以说明这两种描述方式的相似性。借助 FreeBSD 内核的新方法,该控制器可以在所有固件配置下正常运行。

结论

随着 FreeBSD 内核的最新改进,嵌入式产品中使用的 ARM64 SoC 现在可以同时通过 ACPI 和设备树(DT)获得类似功能集的支持。对于 ACPI 而言,自定义控制器的分层表示也足够灵活,能够适用于大多数类型的设备和子系统。例如,一些复杂的网络控制器和通用 MDIO 层的实现也证明了这一点。

FreeBSD 现在沿这条路径发展没有限制,尤其是其总线架构允许干净、优雅地扩展,就像 SD/MMC 控制器 示例所展示的那样。


MARCIN WOJTASSemihalf 工程主管,同时也是 FreeBSD src 提交者(mw@)。他对嵌入式软件和硬件充满热情,并为多个开源项目贡献力量,包括 Linux 内核、Tianocore EDK2 和 TF-A

最后更新于