2. ACPI基础概念
ACPI(Advanced Configuration and Power Interface,高级配置与电源接口)是操作系统与平台固件之间的“契约层”。它把电源管理、设备枚举、热插拔、性能状态等平台细节,从操作系统内核中抽象出来,交给固件以表(Table)的形式描述。理解 ACPI 表的定制开发,必须先建立三个基础认知:规范如何演进、表体系如何组织、以及操作系统与固件如何通过表完成交互。
一、ACPI规范演进:从电源管理到平台描述
ACPI 由 Intel、Microsoft、Toshiba 等厂商在 1996 年提出,最初目标是替代 APM(高级电源管理),统一操作系统对电源状态的控制方式。其演进大致可分为几个阶段:
- ACPI 1.0(1996):确立基本电源状态 S0–S5、处理器状态 C0–C3、设备状态 D0–D3,引入 DSDT、FADT 等核心表。
- ACPI 2.0(2000):增加 64 位地址支持、扩展 FADT、引入 ECDT,为 64 位平台铺路。
- ACPI 3.0(2004):引入 SATA、PCI Express 相关描述,增强热插拔与设备配置能力。
- ACPI 4.0(2009):支持 USB 3.0、多处理器电源管理,强化 NUMA 描述。
- ACPI 5.0(2011):引入 ARM 架构支持、低功耗空闲(LPI)、通用事件设备(GED),使 ACPI 不再局限于 x86。
- ACPI 6.x(2015 至今):持续增强异构计算、CXL、安全启动与虚拟化场景下的平台描述能力。
对定制开发者而言,规范版本决定了可用的表类型、AML 操作码和字段宽度。例如在 ACPI 6.0 之后,_DSM、_PRT 等方法的参数约定更加明确,跨平台兼容性显著提升。
二、ACPI表体系:一张“平台说明书”的目录结构
ACPI 表体系由一组固定格式的二进制表组成,它们通常由固件在启动时放置在内存中,并通过 RSDP(Root System Description Pointer)被操作系统发现。RSDP 指向 RSDT 或 XSDT,后者再指向其余表。整体结构如下:
常见表及其职责可归纳为下表:
| 表名 | 全称 | 主要职责 |
|---|---|---|
| RSDP | Root System Description Pointer | 系统启动时第一个被查找的指针,指向 RSDT/XSDT |
| FADT | Fixed ACPI Description Table | 固定电源管理寄存器、睡眠状态、复位寄存器等 |
| DSDT | Differentiated System Description Table | 平台基础设备树与 AML 方法,系统唯一 |
| SSDT | Secondary System Description Table | 补充设备、方法或覆盖定义,可多张 |
| MADT | Multiple APIC Description Table | 描述 CPU、IOAPIC、中断源 |
| MCFG | Memory Mapped Configuration Table | PCIe 配置空间基址 |
| SRAT/SLIT | NUMA 相关表 | 描述内存与 CPU 亲和关系 |
这些表共同构成操作系统眼中的“平台地图”。定制开发通常聚焦于 DSDT 与 SSDT,因为它们承载了设备树和 AML 方法,是设备枚举、电源切换、GPIO 控制等逻辑的核心。
三、DSDT与SSDT:基础表与扩展表的区别
DSDT 和 SSDT 都使用 AML(ACPI Machine Language)编写,最终由操作系统内核中的 AML 解释器执行。但二者在定位、数量和修改策略上有明显差异:
- DSDT:系统唯一,描述平台基础设备与全局方法,如 \_SB、\_PR、\_SI 等命名空间根节点。它通常由平台厂商提供,修改风险高。
- SSDT:可存在多张,用于补充设备、添加方法、覆盖或扩展 DSDT 中的定义。定制开发更倾向于新增 SSDT,而不是直接改 DSDT。
- 加载顺序:操作系统先加载 DSDT,再按顺序加载 SSDT;SSDT 中的定义可以引用 DSDT 已存在的对象,但反向引用需谨慎。
- 命名空间:两者共享同一命名空间,若 SSDT 定义与 DSDT 同名对象,可能产生冲突或覆盖,需通过 External 声明和唯一路径规避。
四、操作系统与固件交互机制:从表到 AML 执行
操作系统与固件的交互并非“读表即结束”,而是一个持续的解释与调用过程。其典型流程如下:
- 固件在启动阶段将 ACPI 表写入内存,并在 UEFI/BIOS 中留下 RSDP 地址。
- 操作系统内核启动 ACPI 子系统,通过 RSDP 找到 RSDT/XSDT,遍历并校验各表。
- 内核解析 FADT,初始化电源管理寄存器和固定事件。
- 内核加载 DSDT 与 SSDT,构建 ACPI 命名空间,并注册设备节点。
- 设备驱动通过 ACPI 命名空间调用 AML 方法,如 _STA、_CRS、_DSM、_PS0 等,完成枚举、资源配置和电源切换。
- 运行时,操作系统通过 ACPI 事件(如 GPE、SCI)与固件协作,处理热插拔、休眠唤醒等动态行为。
下面是一段典型的 AML 方法调用示例,展示操作系统如何通过 _DSM 获取设备特定数据:
Method (_DSM, 4, NotSerialized)
{
If (LEqual (Arg0, ToUUID ("...")) )
{
If (LEqual (Arg2, Zero))
{
Return (Buffer (One) { 0x03 })
}
Return (Package () { "CustomDevice", 0x01 })
}
Return (Buffer (One) { 0x00 })
}
这段代码中,Arg0 是 UUID,Arg1 是修订号,Arg2 是功能索引,Arg3 是参数包。操作系统通过 _DSM 向固件询问设备能力,固件则以 Package 或 Buffer 返回结果。理解这一交互模型,是后续编写和调试 SSDT 的关键。
五、常见误区与复盘
误区二:忽略 ACPI 规范版本,使用高版本操作码却在低版本固件上运行,导致 AML 解释器报错。
误区三:直接反编译 DSDT 后大改,而不抽取为 SSDT,导致固件升级后维护成本极高。
误区四:只关注表内容,忽略 RSDP、XSDT 的校验和与内存布局,导致系统找不到表。
复盘时建议按“表发现 → 表校验 → 命名空间构建 → 方法调用 → 事件响应”五步排查。多数 ACPI 定制问题并非 AML 语法错误,而是命名空间路径、加载顺序或规范版本不匹配所致。