3. ACPI 表类型详解:核心表的功能与结构

ACPI 规范定义了一系列系统描述表,它们共同构成了操作系统与固件之间的标准化接口。本章将逐一剖析 RSDP、RSDT/XSDT、FADT、FACS、DSDT、SSDT、MADT、SRAT、SLIT 等主要表的结构、功能及在系统启动过程中的作用。

3.1 表类型概览

ACPI 表分为两类:根系统描述指针(RSDP) 和 系统描述表。RSDP 是操作系统定位所有其他 ACPI 表的入口点,而系统描述表则包含具体的硬件配置、电源管理、设备拓扑等信息。

表签名 全称 核心功能 是否必需
RSDP Root System Description Pointer ACPI 表入口指针,位于 EBDA 或系统内存特定区域 是
RSDT Root System Description Table 32 位表指针数组,指向其他描述表 是(32位)
XSDT Extended System Description Table 64 位表指针数组,RSDT 的扩展 是(64位)
FADT Fixed ACPI Description Table 定义 ACPI 硬件寄存器块、电源管理、唤醒向量 是
FACS Firmware ACPI Control Structure 固件与 OS 共享的唤醒和休眠控制结构 是
DSDT Differentiated System Description Table 主 ACPI 命名空间,包含设备定义和控制方法 是
SSDT Secondary System Description Table 辅助命名空间,补充或覆盖 DSDT 定义 否
MADT Multiple APIC Description Table 描述中断控制器拓扑(APIC/xAPIC/x2APIC) 否
SRAT System Resource Affinity Table 定义处理器和内存的 NUMA 亲和性 否
SLIT System Locality Information Table 描述 NUMA 节点间的相对距离 否
关键点:RSDP、RSDT/XSDT、FADT、FACS、DSDT 是 ACPI 规范要求的 必选表,缺少其中任何一个都会导致操作系统无法正常启用 ACPI。其余表为可选,但现代多核/多处理器系统通常必须提供 MADT 和 SRAT。

3.2 RSDP — 根系统描述指针

RSDP 是操作系统查找 ACPI 表的第一个数据结构。它位于以下两个位置之一(按优先级顺序):

  1. 扩展 BIOS 数据区(EBDA)的前 1KB 内
  2. 系统 BIOS 从 0x000E0000 到 0x000FFFFF 的物理内存区域

操作系统通过扫描上述区域中连续的 "RSD PTR " 签名来定位 RSDP。RSDP 结构分为两个版本:

字段 偏移 大小 描述
Signature 0 8 "RSD PTR "(注意末尾空格)
Checksum 8 1 前 20 字节的校验和,必须为 0
OEMID 9 6 OEM 标识符
Revision 15 1 0 表示 ACPI 1.0,2 表示 ACPI 2.0+
RsdtAddress 16 4 RSDT 的 32 位物理地址
Length 20 4 RSDP 结构总长度(ACPI 2.0+)
XsdtAddress 24 8 XSDT 的 64 位物理地址(ACPI 2.0+)
Extended Checksum 32 1 整个 RSDP 结构的校验和
开发陷阱:RSDP 的 Signature 字段是 "RSD PTR "(8 字节,包含尾部空格),不是 "RSDP"。许多调试工具和初学者容易忽略这个空格,导致扫描失败。校验和计算时,所有字节相加后取低 8 位必须为 0。

3.3 RSDT 与 XSDT — 描述表指针数组

RSDT(Root System Description Table)和 XSDT(Extended System Description Table)本质上是 指向其他 ACPI 表的指针数组。操作系统通过 RSDP 找到 RSDT 或 XSDT,然后遍历其中的指针来定位所有其他表。

RSDP → RSDT/XSDT → 其他 ACPI 表 查找流程 RSDP RSDT (32-bit 指针数组) XSDT (64-bit 指针数组) FADT MADT SRAT SSDT ... 说明 ACPI 2.0+ 系统优先使用 XSDT(64位地址), 但为了兼容性,通常同时提供 RSDT 和 XSDT。

RSDT 和 XSDT 的表头结构相同,区别仅在于指针数组的元素宽度:

  • RSDT:每个表项为 4 字节(32 位物理地址),适用于 ACPI 1.0
  • XSDT:每个表项为 8 字节(64 位物理地址),适用于 ACPI 2.0+
// RSDT/XSDT 通用表头结构
typedef struct {
    char     Signature[4];      // "RSDT" 或 "XSDT"
    uint32_t Length;            // 表总长度(含表头)
    uint8_t  Revision;          // 版本号
    uint8_t  Checksum;          // 整个表的校验和
    char     OEMID[6];          // OEM ID
    char     OEMTableID[8];     // OEM 表 ID
    uint32_t OEMRevision;       // OEM 版本
    uint32_t CreatorID;         // 创建者 ID
    uint32_t CreatorRevision;   // 创建者版本
    // 紧接着是表项数组:
    // RSDT: uint32_t EntryPoint[];  // 32位地址数组
    // XSDT: uint64_t EntryPoint[];  // 64位地址数组
} ACPI_TABLE_HEADER;

3.4 FADT — 固定 ACPI 描述表

FADT(Fixed ACPI Description Table)是 ACPI 硬件接口的核心,它定义了操作系统与 ACPI 硬件寄存器块的交互方式。FADT 包含以下关键信息:

  • 硬件寄存器块地址:PM1a_EVT_BLK、PM1b_EVT_BLK、PM1a_CNT_BLK、PM1b_CNT_BLK、PM2_CNT_BLK、PM_TMR_BLK、GPE0_BLK、GPE1_BLK 等
  • 唤醒向量:FIRMWARE_CTRL(指向 FACS)、DSDT 地址
  • 电源管理配置:SCI_EN、S4BIOS_REQ、PROC_CNT 等标志位
  • 睡眠状态支持:S0、S1、S2、S3、S4、S5 状态定义
字段 偏移 大小 说明
FIRMWARE_CTRL 36 4 FACS 的 32 位物理地址
DSDT 40 4 DSDT 的 32 位物理地址
SCI_INT 48 2 SCI 中断号
SMI_CMD 50 4 SMI 命令端口地址
ACPI_ENABLE 54 1 写入 SMI_CMD 以启用 ACPI 的值
ACPI_DISABLE 55 1 写入 SMI_CMD 以禁用 ACPI 的值
PM1a_EVT_BLK 56 4 PM1a 事件寄存器块基地址
PM1b_EVT_BLK 60 4 PM1b 事件寄存器块基地址
PM1a_CNT_BLK 64 4 PM1a 控制寄存器块基地址
PM1b_CNT_BLK 68 4 PM1b 控制寄存器块基地址
PM2_CNT_BLK 72 4 PM2 控制寄存器块基地址
PM_TMR_BLK 76 4 PM 定时器寄存器块基地址
GPE0_BLK 80 4 GPE0 寄存器块基地址
GPE1_BLK 84 4 GPE1 寄存器块基地址
FLAGS 112 4 固定 ACPI 硬件功能标志位
实战要点:在定制 FADT 时,务必确保 SCI_EN 标志位正确设置。如果 SCI_EN 为 0,操作系统会认为 ACPI 硬件未启用,从而回退到传统 BIOS 模式。另外,GPE0_BLK 和 GPE1_BLK 的地址必须指向有效的 I/O 或内存空间,否则系统无法处理 ACPI 事件。

3.5 FACS — 固件 ACPI 控制结构

FACS(Firmware ACPI Control Structure)是一个 由固件和操作系统共享 的内存结构,主要用于休眠/唤醒(S3/S4)状态切换。它位于系统内存中,由 FADT 的 FIRMWARE_CTRL 字段指向。

// FACS 结构定义
typedef struct {
    char     Signature[4];      // "FACS"
    uint32_t Length;            // 结构总长度(至少 64 字节)
    uint32_t HardwareSignature; // 硬件签名,用于检测硬件变化
    uint32_t FirmwareWakingVector;   // 32位唤醒向量地址
    uint32_t GlobalLock;        // 全局锁,用于多处理器同步
    uint32_t Flags;             // 标志位
    uint64_t XFirmwareWakingVector;  // 64位唤醒向量地址(ACPI 2.0+)
    uint8_t  Version;           // 版本号
    uint8_t  Reserved[3];       // 保留
    uint32_t OspmFlags;         // OSPM 标志位
    uint8_t  Reserved2[24];     // 保留
} FACS;

FACS 的关键字段:

  • FirmwareWakingVector / XFirmwareWakingVector:系统从 S3 休眠状态唤醒时,CPU 会跳转到这个地址执行。固件必须在此地址放置唤醒代码。
  • GlobalLock:用于多处理器系统中同步对 ACPI 寄存器的访问,防止竞态条件。
  • HardwareSignature:操作系统在每次休眠前记录此值,唤醒后比较是否变化,以检测硬件配置是否改变。

3.6 DSDT 与 SSDT — 命名空间定义表

DSDT(Differentiated System Description Table)和 SSDT(Secondary System Description Table)是 ACPI 命名空间的主要载体。它们包含用 ACPI 源语言(ASL)编写的设备定义、控制方法和配置数据。

DSDT 与 SSDT 的命名空间关系 DSDT (根命名空间) \_SB_ (系统总线) \_PR_ (处理器) \_TZ_ (热区) \_GPE (通用事件) SSDT (辅助命名空间,可覆盖/扩展 DSDT) 覆盖 \_SB_.PCI0 添加新设备定义 修补控制方法

DSDT 与 SSDT 的区别:

特性 DSDT SSDT
数量 只能有一个 可以有多个(SSDT1、SSDT2...)
加载时机 ACPI 初始化时首先加载 在 DSDT 之后按顺序加载
作用 定义根命名空间和基本设备 补充、覆盖或修补命名空间
修改难度 高(需重新编译固件) 低(可通过操作系统动态加载)
典型用途 主板级设备定义 CPU 补丁、显卡补丁、OEM 定制
开发技巧:在定制开发中,尽量使用 SSDT 而非修改 DSDT。SSDT 可以通过操作系统引导加载器(如 OpenCore、GRUB)动态注入,无需重新刷写固件。SSDT 中定义的设备和方法会覆盖 DSDT 中的同名对象,实现“热补丁”效果。

3.7 MADT — 多 APIC 描述表

MADT(Multiple APIC Description Table)描述了系统中的中断控制器拓扑结构,包括 I/O APIC、本地 APIC、x2APIC 等。操作系统使用 MADT 来初始化中断子系统和处理器拓扑。

MADT 的表头之后紧跟着一系列 中断控制器结构条目(Interrupt Controller Structure Entries),每个条目由类型(Type)和长度(Length)标识:

类型值 条目类型 说明
0 Processor Local APIC 描述一个处理器及其本地 APIC ID
1 I/O APIC 描述一个 I/O APIC 的基地址和全局中断基号
2 Interrupt Source Override 覆盖 ISA 中断到 I/O APIC 的映射
3 NMI Source 描述 NMI 中断源
4 Local APIC NMI 指定某个处理器的 NMI 引脚连接
5 Local APIC Address Override 覆盖本地 APIC 的默认基地址(0xFEE00000)
6 I/O SAPIC 用于 Itanium 平台的 SAPIC
7 Local SAPIC 用于 Itanium 平台的本地 SAPIC
8 Platform Interrupt Sources 平台级中断源
9 Processor Local x2APIC 描述 x2APIC 模式的处理器
10 Local x2APIC NMI x2APIC 模式的 NMI 连接
// MADT 表头结构
typedef struct {
    ACPI_TABLE_HEADER Header;   // 标准表头,Signature = "APIC"
    uint32_t          LocalApicAddress;  // 本地 APIC 基地址(默认 0xFEE00000)
    uint32_t          Flags;             // 标志位(bit0: PC-AT 兼容模式)
    // 紧接着是中断控制器结构条目数组
} MADT;
常见错误:在定制 MADT 时,Processor Local APIC 条目 中的 APIC ID 必须与 CPU 的实际 APIC ID 一致。如果 APIC ID 不匹配,操作系统可能无法正确识别处理器,导致某些核心无法被调度。对于支持超线程的 CPU,每个逻辑处理器都需要一个独立的条目。

3.8 SRAT 与 SLIT — NUMA 拓扑描述表

SRAT(System Resource Affinity Table)和 SLIT(System Locality Information Table)共同描述了 NUMA(非统一内存访问)系统的拓扑结构。

SRAT 定义了处理器和内存区域所属的 NUMA 域(Proximity Domain)。操作系统使用 SRAT 来优化内存分配策略,将进程调度到离其内存最近的处理器上。

SLIT 定义了不同 NUMA 节点之间的相对距离(Latency)。距离值越小,表示两个节点之间的通信延迟越低。操作系统使用 SLIT 来做出更精细的调度和内存分配决策。

特性 SRAT SLIT
签名 "SRAT" "SLIT"
核心内容 处理器和内存的 NUMA 亲和性 节点间的相对距离矩阵
条目类型 Processor Local APIC/SAPIC/x2APIC Affinity、Memory Affinity N×N 字节矩阵(N = 节点数)
距离值范围 不适用 10(本地节点)~ 255(最远节点)
典型用途 多路服务器、大型 NUMA 系统 优化跨节点内存访问
// SRAT 中的处理器亲和性条目示例
typedef struct {
    uint8_t  Type;              // 0: Processor Local APIC Affinity
    uint8_t  Length;            // 条目长度
    uint8_t  ProximityDomainLow; // 低8位 NUMA 域编号
    uint8_t  ApicId;            // 本地 APIC ID
    uint32_t Flags;             // 标志位(bit0: Enabled)
    uint8_t  LocalSapicEid;     // SAPIC EID
    uint8_t  ProximityDomainHigh[3]; // 高24位 NUMA 域编号
    uint32_t ClockDomain;       // 时钟域
} SRAT_PROCESSOR_AFFINITY;
实战案例:在一台 4 路服务器上,每个物理 CPU 有 16 个核心,系统共有 4 个 NUMA 节点。SRAT 中需要为每个核心(共 64 个逻辑处理器)创建一个 Processor Local APIC Affinity 条目,并指定其所属的 Proximity Domain(0~3)。SLIT 矩阵为 4×4,本地节点距离为 10,跨节点距离根据 QPI/UPI 链路跳数设置为 20 或 30。

3.9 表间依赖关系总结

ACPI 表之间存在严格的依赖关系,操作系统按照以下顺序解析它们:

  1. RSDP:通过扫描内存找到入口
  2. RSDT/XSDT:从 RSDP 获取地址,遍历指针数组
  3. FADT:从 RSDT/XSDT 中找到,获取 FACS 和 DSDT 地址
  4. FACS:从 FADT 的 FIRMWARE_CTRL 字段定位
  5. DSDT:从 FADT 的 DSDT 字段定位,加载根命名空间
  6. SSDT:从 RSDT/XSDT 中按顺序加载,补充命名空间
  7. MADT/SRAT/SLIT:从 RSDT/XSDT 中加载,用于中断和内存拓扑初始化

任何一张表的缺失或格式错误都可能导致后续表无法正确加载,进而影响系统启动或 ACPI 功能。在定制开发中,建议使用 ACPICA 工具集(如 iasl、acpidump、acpiextract)来验证表的完整性和正确性。