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 → RSDT/XSDT → { FADT, DSDT, SSDT, MADT, MCFG, HPET, SRAT, SLIT, ... }

常见表及其职责可归纳为下表:

表名全称主要职责
RSDPRoot System Description Pointer系统启动时第一个被查找的指针,指向 RSDT/XSDT
FADTFixed ACPI Description Table固定电源管理寄存器、睡眠状态、复位寄存器等
DSDTDifferentiated System Description Table平台基础设备树与 AML 方法,系统唯一
SSDTSecondary System Description Table补充设备、方法或覆盖定义,可多张
MADTMultiple APIC Description Table描述 CPU、IOAPIC、中断源
MCFGMemory Mapped Configuration TablePCIe 配置空间基址
SRAT/SLITNUMA 相关表描述内存与 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 声明和唯一路径规避。
实践建议:优先使用 SSDT 做增量定制。这样既能保留原厂 DSDT 的完整性,又便于在系统升级或固件更新后快速回退。

四、操作系统与固件交互机制:从表到 AML 执行

操作系统与固件的交互并非“读表即结束”,而是一个持续的解释与调用过程。其典型流程如下:

  1. 固件在启动阶段将 ACPI 表写入内存,并在 UEFI/BIOS 中留下 RSDP 地址。
  2. 操作系统内核启动 ACPI 子系统,通过 RSDP 找到 RSDT/XSDT,遍历并校验各表。
  3. 内核解析 FADT,初始化电源管理寄存器和固定事件。
  4. 内核加载 DSDT 与 SSDT,构建 ACPI 命名空间,并注册设备节点。
  5. 设备驱动通过 ACPI 命名空间调用 AML 方法,如 _STA、_CRS、_DSM、_PS0 等,完成枚举、资源配置和电源切换。
  6. 运行时,操作系统通过 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 的关键。

五、常见误区与复盘

误区一:认为 DSDT 和 SSDT 可以随意互相覆盖。实际上命名空间冲突会导致加载失败或设备异常。
误区二:忽略 ACPI 规范版本,使用高版本操作码却在低版本固件上运行,导致 AML 解释器报错。
误区三:直接反编译 DSDT 后大改,而不抽取为 SSDT,导致固件升级后维护成本极高。
误区四:只关注表内容,忽略 RSDP、XSDT 的校验和与内存布局,导致系统找不到表。

复盘时建议按“表发现 → 表校验 → 命名空间构建 → 方法调用 → 事件响应”五步排查。多数 ACPI 定制问题并非 AML 语法错误,而是命名空间路径、加载顺序或规范版本不匹配所致。

ACPI 表体系与 OS/固件交互模型 固件层(UEFI / BIOS) 启动时构建 ACPI 表并写入内存,留下 RSDP 指针 RSDP RSDT / XSDT FADT DSDT SSDT×N 操作系统层(ACPI 子系统 + 驱动) 表发现与校验 RSDP → RSDT/XSDT 命名空间构建 DSDT + SSDT 合并 AML 方法调用 _STA / _CRS / _DSM 事件与电源管理 GPE / SCI / S 状态 表加载 方法调用 / 事件通知 定制开发关注点 DSDT 基础定义 系统唯一,谨慎修改 SSDT 增量扩展 可多张,推荐方式 命名空间路径 External 声明与唯一性 规范版本匹配 操作码与字段宽度