4. ASL 语言基础:语法结构、对象类型、数据类型、运算符、控制流语句

ASL(ACPI Source Language)是定义 ACPI 表(尤其是 DSDT/SSDT)行为的硬件抽象语言。它既不像 C 语言那样直接操作内存,也不像 Verilog 那样描述硬件连线,而是处于“固件策略层”:通过解释执行来配置电源管理、设备枚举和热插拔事件。掌握 ASL 语法是定制 ACPI 表的必修课。

4.1 ASL 语法结构

ASL 源文件由 DefinitionBlock 包裹,内部包含命名空间(Scope)、设备(Device)、方法(Method)和各类对象。基本骨架如下:

DefinitionBlock ("DSDT.aml", "DSDT", 2, "ACPI", "TEMPLATE", 0x00000001)
{
    Scope (\_SB)
    {
        Device (PCI0)
        {
            Name (_HID, EisaId ("PNP0A08"))  // PCI Express 总线
            Method (_STA, 0, NotSerialized)
            {
                Return (0x0F)
            }
        }
    }
}
语法元素 说明 示例
DefinitionBlock 定义整个 ACPI 表,包含表签名、OEM ID、修订号等 DefinitionBlock ("SSDT.aml", "SSDT", ...)
Scope 打开或进入已有命名空间路径 Scope (\_SB.PCI0)
Device 声明一个设备对象,可包含 _HID、_STA、_CRS 等控制方法 Device (BAT0)
Method 定义可被 AML 解释器调用的控制方法 Method (_INI, 0, Serialized)
Name 声明一个命名对象并赋予初始值 Name (PBUF, Buffer (128) {})
命名规范:ASL 标识符最长 255 个字符,区分大小写,通常使用大写字母加数字。以反斜杠 \ 开头的路径表示从根命名空间开始,例如 \_SB.PCI0。

4.2 对象类型

ASL 中的“对象”是 AML 解释器可操作的最小单元。所有数据、设备、方法、事件都以对象形式存在。主要对象类型如下:

  • 命名对象(Named Object):通过 Name 或 Device 声明,具有全局或局部路径。
  • 控制方法(Control Method):可执行代码块,通过 Method 定义,支持参数和返回值。
  • 操作区域(OperationRegion):映射到系统内存、IO 空间或 PCI 配置空间的区域,用于硬件访问。
  • 事件(Event):用于同步和唤醒,通过 Event 声明,配合 Notify 使用。
  • 缓冲(Buffer):字节序列,常用于存储 _CRS 资源描述符。
  • 包(Package):对象数组,可包含不同类型元素。
关键区别:命名对象在编译时分配路径,控制方法在运行时被调用。操作区域不占用 AML 堆栈空间,而是直接映射到硬件地址。

4.3 数据类型

ASL 的数据类型比 C 语言更抽象,所有数据最终以 AML 字节码形式存储。常用数据类型如下表:

类型 关键字 取值范围/示例 典型用途
整数 Integer 0 ~ 264-1 状态码、掩码、索引
字符串 String "ACPI", "PNP0C0A" 设备 ID、路径名
缓冲 Buffer Buffer (4) { 0x00, 0xFF, ... } 资源描述符、UUID
包 Package Package (3) { 1, "abc", Buffer() } 多值返回、配置列表
对象引用 Object Reference RefOf (\_SB.PCI0) 传递对象句柄
事件 Event Event (EVA0) 中断同步
注意:ASL 没有浮点类型。所有小数运算必须通过整数模拟或使用固定点算法。Buffer 和 Package 在声明时必须指定元素个数或长度。

4.4 运算符

ASL 运算符分为算术、逻辑、位运算和比较四类。所有运算符都返回 Integer 类型。常用运算符如下:

类别 运算符 说明 示例
算术 + - * / 加、减、乘、整除 Add (A, B, C)
% 取模 Mod (A, 10, R)
<< >> 左移、右移 ShiftLeft (1, 3, R)
~ 按位取反 Not (A, R)
逻辑 && || 逻辑与、逻辑或 LAnd (A, B, R)
! 逻辑非 LNot (A, R)
比较 == != 等于、不等于 LEqual (A, B, R)
< > <= >= 大小比较 LGreater (A, B, R)
=== 严格等于(类型+值) LEqual (TypeOf(A), TypeOf(B), R)
位运算 & | ^ 按位与、或、异或 And (A, B, R)
~ 按位取反 Not (A, R)
函数式语法:ASL 运算符通常以函数形式调用,例如 Add (A, B, C) 等价于 C = A + B。第三个参数是目标存储位置。

4.5 控制流语句

ASL 支持条件分支、循环和跳转,但语法与 C 语言差异较大。所有控制流语句必须写在 Method 内部。

4.5.1 条件分支

If (LEqual (Local0, 0x0F))
{
    Return (1)
}
ElseIf (LGreater (Local0, 0x0F))
{
    Return (2)
}
Else
{
    Return (0)
}

4.5.2 循环

While (LGreater (Local0, 0))
{
    Multiply (Local0, 2, Local0)
    Decrement (Local0)
}

4.5.3 无条件跳转

// 使用 BreakPoint 或 Goto(仅调试)
// 实际开发中常用 Return 提前退出
If (LEqual (Arg0, 0))
{
    Return (0xFF)
}
避坑:ASL 没有 for 循环,必须用 While 配合计数器实现。循环内不要修改循环变量以外的全局对象,避免 AML 解释器状态不一致。

4.6 ASL 对象与命名空间关系图

下图展示 ASL 中命名对象、控制方法、操作区域和事件在命名空间中的组织方式:

ASL 命名空间对象关系 \ (Root) \_SB Device (PCI0) Device (BAT0) Device (EC0) Method (_STA) Name (_HID) OpRegion (ERAM) Method (_Qxx) Event (EVA0) Field (ERAM) 命名对象 控制方法 数据对象 操作区域

4.7 实战要点与常见错误

  • 路径引用:使用 ^ 表示父作用域,^^ 表示祖父作用域,避免硬编码绝对路径。
  • 方法序列化:如果方法可能被并发调用,声明为 Serialized 防止重入。
  • 返回值类型:方法必须返回 Integer、Buffer 或 Package,不能返回 Void。无返回值时用 Return (0)。
  • 局部变量:Local0~Local7 是方法内临时变量,方法退出后失效。全局状态用 Name 存储。
  • 调试技巧:使用 Debug = <expression> 输出到 ACPI 调试日志,但生产代码中应移除。