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 节点间的相对距离 | 否 |
3.2 RSDP — 根系统描述指针
RSDP 是操作系统查找 ACPI 表的第一个数据结构。它位于以下两个位置之一(按优先级顺序):
- 扩展 BIOS 数据区(EBDA)的前 1KB 内
- 系统 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 结构的校验和 |
3.3 RSDT 与 XSDT — 描述表指针数组
RSDT(Root System Description Table)和 XSDT(Extended System Description Table)本质上是 指向其他 ACPI 表的指针数组。操作系统通过 RSDP 找到 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 硬件功能标志位 |
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 | SSDT |
|---|---|---|
| 数量 | 只能有一个 | 可以有多个(SSDT1、SSDT2...) |
| 加载时机 | ACPI 初始化时首先加载 | 在 DSDT 之后按顺序加载 |
| 作用 | 定义根命名空间和基本设备 | 补充、覆盖或修补命名空间 |
| 修改难度 | 高(需重新编译固件) | 低(可通过操作系统动态加载) |
| 典型用途 | 主板级设备定义 | CPU 补丁、显卡补丁、OEM 定制 |
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;
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;
3.9 表间依赖关系总结
ACPI 表之间存在严格的依赖关系,操作系统按照以下顺序解析它们:
- RSDP:通过扫描内存找到入口
- RSDT/XSDT:从 RSDP 获取地址,遍历指针数组
- FADT:从 RSDT/XSDT 中找到,获取 FACS 和 DSDT 地址
- FACS:从 FADT 的 FIRMWARE_CTRL 字段定位
- DSDT:从 FADT 的 DSDT 字段定位,加载根命名空间
- SSDT:从 RSDT/XSDT 中按顺序加载,补充命名空间
- MADT/SRAT/SLIT:从 RSDT/XSDT 中加载,用于中断和内存拓扑初始化
任何一张表的缺失或格式错误都可能导致后续表无法正确加载,进而影响系统启动或 ACPI 功能。在定制开发中,建议使用 ACPICA 工具集(如 iasl、acpidump、acpiextract)来验证表的完整性和正确性。