ASL语言学习(1)- 基本语法,作用域
本文介绍ASL语言的基本语法,作用域以及NamePath。
最近的工作中用到了很多ASL的代码,之前一直一知半解的看,发现到用的时候不熟练还是很影响效率。
ASL代码的中文教程几乎没有,为了方便自己以后看,也方便别人检索,这里根据ACPI Source Language (ASL) Tutorial 以及 ACPI Source Language (ASL) Reference,把我平时记的笔记整理成人话。这一篇简单介绍下一些基本的表达式用法。
ASL与AML
ASL - ACPI Source Language AML - ACPI Machine Language
ASL 代码文件通过 iASL compiler 生成 AML字节码文件,每一个table即一个ASL文件,生成一个对应的AML文件包含在Firmware中。
Grammar 语法
ASL的表达式(Statement)声明对象(Object)
1
Object := ObjectType FixedList VariableList
其中
 Operator 是ACPI中规定的操作符,如DefinitionBlock,Name,Method,Scope
 FixedList 是一组固定长度的变量,根据不同的Operator,有不同的长度,也可能为空,写法:(a, b, c),有时尾部的变量可以省略(Default值生效)
 VariableList是一组不定长度的变量,写法:{x, y, z},对于某些Operator,这里的变量(argument)可以是嵌套的对象。
整体就是有点C语言函数的样子。(虽然长得像但是有很显著的差异)
1
2
3
4
Operator (FixedVariableA, FixedVariableB, FixedVariableC) {
  VariableX,
  VariableY,
}
下面介绍一些常用的操作符与其对应的表达式
DefinationBlock - ASL的基础
每个DefinitionBlock定义一个ACPI table,所有的ASL code都需要写到DefinitionBlock内(作为VariableList),一个标准的DefinitionBlock如下:
1
2
3
4
5
6
7
8
9
10
11
DefinitionBlock (
    AMLFileName,
    TableSignature,     // Signature of the AML file (could be DSDT or SSDT) (4-character string)
    ComplianceRevision, // A value of 2 or greater enables 64-bit arithmetic; a value of 1 or less enables 32-bit arithmetic (8 bit unsigned integer)
    OEMID,              // (6-character string)
    TableID,            // (8-character string)
    OEMRevision         // (32-bit number)
    )
{
    //ASL Code
}
注意ComplianceRevision小于2时,NameSpace定义的Integer Object为32bit,反之则为64bit。
Name - ASL的“变量”
Name可以用来定义Integer, String, Buffer, Package等等对象:
1
2
// ObjectName 必须小于等于4个字母,可以下划线开头
Name (ObjectName, Object)
Object Types:
• Integer - An unsigned 64-bit or 32-bit integer. Size取决于ComplianceRevision
 • String - A null-terminated ASCII string. 字符串
 • Buffer - An array of bytes. 类似数组
 • Package - An array of ASL objects.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
DefinitionBlock ("", DSDT, 2, "", "", 0x0)
{
    Name (OBJ0, 0x1234)                            // Integer (DW or DD)
    Name (OBJ1, "Hello world")                     // String
    Name (BUF1, Buffer(3){0x00, 0x01, 0x02})       // Buffer  (Byte)
    Name (BUF2, Buffer(){0x00, 0x01, 0x02, 0x03})  // Buffer 长度自动初始化
    Name (PKG1, Package(3){0x1234, "Hello world", INT1}) // Package
    Name (PKG2, Package(){INT1, "Good bye"})             // Package 长度自动初始化
    Name (PKG3,
        Package(){
            Package() {0x00, 0x01, 0x02},
            Package() {0x03, 0x04, 0x05}
        }) // Package in Package also Package
    Name (PKG4, Package(){
        "ASL is fun",
        Package() {0xff, 0xfe, 0xfd, 0xfc, fb}})
    Name (PKG5, Package(){
        0x4321,
        Buffer() {0x1}
    })
}
OperationRegions and Fields - ASL的“内存指针”
1
2
OperationRegion (RegionName, RegionSpace, Offset, Length)
Field ( RegionName, AccessType, LockRule, UpdateRule ) {FieldUnitList}
(这个“操作区”,描述成“内存指针”也不是很贴切,总之OperationRegion类似一个指针,Field类似这个指针对应的结构体定义。)
这是ASL中用来访问系统内存或者IO Space的方法。
通常在C语言中有对应的结构体。
1
2
3
4
5
6
7
8
9
10
11
12
13
DefinitionBlock ("", "DSDT", 2, "", "", 0x1)
{
    //             (RegionName, RegionSpace,  Offset,  Length)
    OperationRegion(OPR1,       SystemMemory, 0x10000, 0x5)
    Field (OPR1)
    {
        FLD1, 8
        FLD2, 8
        Offset (3), //Start the next field unit at byte offset 3
        FLD3, 4
        FLD4, 12
    }
}
上面表达式生成的OperationRegion如下:
 (本图片来自ACPI Source Language (ASL) Tutorial)
 (本图片来自ACPI Source Language (ASL) Tutorial)
注:OperationRegion也是ObjectType的一种
Name Space 作用域
(不确定这么翻译准不准确。)
除了上文中提到Object Type以外,ASL语言还支持很多其他的Object Type,如:
• Device - Device or bus object. 硬件对象
 • Object Reference - A reference to an object created by RefOf, Index, or CondRefOf operators. 类似指针
 • Method - Control Method (Executable AML function). 类似函数
详细参见: All Data Types
Scope - ASL中的作用域
Scope和其他语言的scope一样,提供了一个作用域,在ASL中独特的是这个作用域需要与实际硬件的Location相关。
1
Scope (Location) {ObjectList}
Location - Predefined Root Namespaces:
- \: 根节点
- _SB: System Bus
- _GPE: General Purpose Event
- _PR: Processor NameSpace
- _TZ: Thermal Zone
注:在创建SSDT的时候通常需要使用Scope来更改namespace location,以便在DSDT定义的NameSpace中创建对象。
Device - ASL中描述硬件的作用域
1
Device (DeviceName) {TermList}
每个Device下是独立的作用域。
Device可以嵌套Device。一般来讲Device都定义在Scope里面。
下面这段代码定义了System Bus上的UART(COM0)与USB EHCI Host Controller(USB0),其中USB0中包含Root Hub(RHUB)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 这段代码来自edk2-platforms\Silicon\Qemu\SbsaQemu\AcpiTables\Dsdt.asl
DefinitionBlock ("DsdtTable.aml", "DSDT",
                 EFI_ACPI_6_0_DIFFERENTIATED_SYSTEM_DESCRIPTION_TABLE_REVISION,
                 "LINARO", "SBSAQEMU", FixedPcdGet32 (PcdAcpiDefaultOemRevision)) {
  Scope (_SB) {
    // UART PL011
    Device (COM0) {
      Name (_HID, "ARMH0011")
      Name (_UID, Zero)
      ...
    }
    // USB EHCI Host Controller
    Device (USB0) {
        Name (_HID, "LNRO0D20")
        Name (_CID, "PNP0D20")
        ...
        // Root Hub
        Device (RHUB) {
            Name (_ADR, 0x00000000)  // Address of Root Hub should be 0 as per ACPI 5.0 spec
            ...
        } // USB0_RHUB
    } // USB0
NamePath
在ASL中每一个Object都有一个NamePath,这个NamePath也标示出其作用域的范围。
举个例子:
注:NamePath中符号的意义
\- 根节点
.- 子节点
^- 上一层节点
_- Reserved by Specification
Method 执行函数
Method - ASL的“函数”
1
Method ( MethodName, NumArgs, SerializeRule, SyncLevel, ReturnType, ParameterTypes ) {TermList}
Method的参数比较多,下面详细介绍一下:
- MethodName:顾名思义
- NumArgs:入参的个数,Method最多可以传递 7 个参数,在Method里用 Arg0~Arg6 表示,不可以自定义。 (Default: 0)
- SerializeRule:当函数声明为 Serialized,内存中仅能存在一个实例(即不可重入)。(Default: NotSerialized)
- SyncLevel: Synchronization level (0 - 15). (Default: 0) (这一块没有深入的了解,目前看到的代码都是0)
- ReturnType: 定义返回值的Object Type. (Default: 不限制)
- ParameterTypes: 定义入参的Object Type,每个入参依次定义。(Default: 不限制)
此外:
- Method最多可以用 8 个局部变量,用 Local0~Local7,不可以自定义,使用前需要初始化。
- ASL methods can be called by other ASL methods or by the OS through the AML interpreter.
- 除了MethodName,其他均为可选参数。
示例:
1
2
3
4
5
6
7
8
9
10
11
// In this function
// MethodName     -- _DSM
// NumArgs        -- 4 (max 7, Arg0 ~ Arg6)
// SerializeRule  -- Serialized
// SyncLevel      -- 0
// ReturnType     -- {BuffObj, PkgObj}  // 2 kind possible return types
// ParameterTypes -- {BuffObj, IntObj, IntObj, PkgObj} // 4 input arg types in order
Method (_DSM, 4, Serialized, 0, {BuffObj, PkgObj}, {BuffObj, IntObj, IntObj, PkgObj}) {
  ...
}
运算符 (Operators)
| Math operators | Logical operators | Assignment operators | |||
| ASL 2.0 | Legacy ASL | ASL 2.0 | Legacy ASL | ASL 2.0 | Legacy ASL | 
| Z = X + Y | Add (X, Y, Z) | (X == Y) | LEqual (X, Y) | X = Y | Store (Y, X) | 
| Z = X / Y | Divide (X, Y, , Z) | (X != Y) | LNotEqual (X, Y) | X += Y | Add (X, Y, X) | 
| Z = X % Y | Mod (X, Y, Z) | (X < Y) | LLess (X, Y) | X /= Y | Divide (X, Y, , X) | 
| Z = X * Y | Multiply (X, Y, Z) | (X > Y) | LGreater (X, Y) | X %= Y | Mod (X, Y, X) | 
| Z = X - Y | Subtract (X, Y, Z) | (X <= Y) | LLessEqual (X, Y) | X *= Y | Multiply (X, Y, X) | 
| Z = X << Y | ShiftLeft (X, Y, Z) | (X >= Y) | LGreaterEqual (X, Y) | X -= Y | Subtract (X, Y, X) | 
| Z = X >> Y | ShiftRight (X, Y, Z) | (X && Y) | LAnd (X, Y) | X <<= Y | ShiftLeft (X, Y, X) | 
| Z = X & Y | And (X, Y, Z) | (X || Y) | LOr (X, Y) | X >>= Y | ShiftRight (X, Y, X) | 
| Z = X | Y | Or (X, Y, Z) | !X | LNot (X) | X &= Y | And (X, Y, X) | 
| Z = X ^ Y | Xor (X, Y, Z) | Miscellaneous | X |= Y | Or (X, Y, X) | |
| Z = ~X | Not (X, Z) | ASL 2.0 | Legacy ASL | X ^= Y | Xor (X, Y, X) | 
| X++ | Increment (X) | Z = X[Y] | Index (X, Y, Z) | ||
| X– | Decrement (X) | ||||
流程控制 (Control Flow)
ASL的Control Flow与C语言基本一致。包括:
For, While, Break, Continue – 循环
这里举个例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
DefinitionBlock ("", "DSDT", 2, "", "", 0x0)
{
  // Lagecy ASL
  Method (PRT1, 1) {
    Store(Arg0, Local0) //Local0 = Arg0
    While (Local0) {
      Mod (Local0, 2, Local1) //Local1 = Local0 % 2
      If (Local1) {
        Printf ("%o is odd", Local0)
      } Else {
        Printf ("%o is even", Local0)
      }
      Decrement (Local0)	//Local0--;
    }
  }
  // ASL2
  Method (PRT2, 1)
  {
    Local0 = Arg0
    For (Local0 = 0, Local0 < Arg0, Local0++)
    {
      Switch(Local0) {
        Case(5) {
          PRT1(Local0)
        }
        Case(10) {
          Break
        }
        Default {
          Continue
        }
      }
      Printf ("Now is %o", Local0)
    }
  }
}
后面还没整理完,下篇文章接着写。
