ASL的全称是ACPI Source language。
在ACPI基础中已经有介绍过ASL语言,它是用来描述硬件信息以及相关硬件操作给OS使用的表达式。
简单来说,ASL可以看成是类似C语言的计算机语言,不过它功能更加的单一。不过虽然功能单一,但是因为涉及到各种各样的硬件,所以实际上需要记忆的东西会非常多,并且还在不断的变多。
目前ACPI的最新版本是6.1版本,相关的SPEC可以在http://www.uefi.org/specifications中下载到。
ASL现在作为BIOS/UEFI的一部分,它包含在BIOS的源代码里面,在https://code.csdn.net/jiangwei0512/edk2-udk2017.git这个GIT仓库中可以下载的UEFI的代码,搜索后缀是asl的文件可以找到源代码。由于edk2-udk2017是开源的代码,里面包含的硬件相关的东西很少,所以能够找到的asl文件并不多,不过实际使用的UEFI中,asl文件要多很多。
在编译BIOS的时候,会使用iasl编译器来编译ASL源代码,下面是一些使用到的工具(也包含了iasl):
BIOS/UEFI中也有相关的ACPI操作的接口,它最终会将ACPI相关的内容放到内存的某段空间中,并将指向该区域的指针传递给OS。
OS使用这里的内容来操作获取硬件信息并操作相关的硬件。
这样的好处是,OS不需要直接去与底层硬件沟通,它只要直接操作这些ASL生成的内容就可以了。
另一个好处是,只要支持ACPI,不管OS是Windows还是Linux,都可以完成与硬件交互,也就是OS Independent。
上图中的acpidump.exe可以用来打印上述提到的那段内存空间:
以上是截图的一部分,实际上它包含很多的内容。
OS就是要解析这些信息,它们也是我们写的ASL最终成为的内容。
通过Windows下的RW.exe工具,可以看到该机器下的ASL语言:
基本上一般情况下我们很难看到多少ASL语言的实例,所以这是我们能够得到的很少的几个学习素材之一。
ASL语言中需要掌握的东西分为下面的几个部分:
1. ASL表达式。ASL的表达式很简单,就个句式,不像C语言那样有有声明变量、声明函数啊等很多的形式;
2. 操作符。ASL定义了很多的操作符,它像是C语言中的关键字和函数名的合体,需要记忆;
3. ACPI预留的对象。这一部分是真正的难点,在ACPI SPEC中,定义了对应各种硬件的对象,它们的命名大多以_开头,且数量非常的多,需要很熟悉ACPI规范以及各种硬件设备才能真正了解这些预留对象。
ASL的表达式,简单来说就一句话:
Operator FixedList VariableList
Operator是ACPI中规定的操作符,在ACPI6.1版本中一共有148个,SPEC中有专门的章节来介绍它们,下面是截取的一角:
FixedList是一组可选的变量,用()括起来。
VariableList也是一组可选的变量,用{}括起来。
当上述三个部分都存在的时候就很像C语言中的函数了。不过它跟函数还是有些不同的,事实上有一个操作符Method是用来定义ACPI中的函数的,它比C语言中的函数更广义。
另外需要说明ASL表达式是部分大小写的,无论大小写在编译时都被转换成了大写。
下面是一个例子:
DefinitionBlock ("Dsdt.aml", "DSDT", 1, "INTEL ", "OVMF ", 4) {
//
// System Sleep States
//
// We build S3 and S4 with GetSuspendStates() in
// "OvmfPkg/AcpiPlatformDxe/Qemu.c".
//
Name (\_S0, Package () {5, 0, 0, 0}) // Working
Name (\_S5, Package () {0, 0, 0, 0}) // Soft Off
// 后略
}
这里DefinitionBlock是一个Operator(事实上它是ASL的入口),()里面包含若干个变量,{}里面也包含若干变量,这里可以看到ASL表达式是可以嵌套的。
Name另一个Operator,它用来为对象命名,它不带{}。
前面已经提到了操作符。它就是表达式的开始。
由于操作符太多,不可能在这里一一介绍。
这里选择几个比较常见的进行说明。
Name:
Name (\_S0, Package () {5, 0, 0, 0}) // Working
Name的作用是定义一个变量。
它带两个FixedList,分别是(名称,对象)。
这里就是定义了一个Package,它的名字是_S0。
这里有几点需要说明:
1. “\”是根命名空间。ASL中有不同的命令空间,“\”是起始命名空间,有一些操作符会开辟新的命名空间;还有一个符号“^”表示上一层命名空间;
2. ASL中变量的名称都是固定的4个字节大小,不包括“\”,不满4个字节的,后面用“_”补全;
3.“_”开头的是规范预留的名称,虽然名称是预留了,但是定义并没有,我们写ASL的时候需要将它们补全(当然前提是我们需要用它);
4. 对象可以有不同的数据类型,ASL下有很多的数据类型,它们通过不同的操作符创建,这里就使用了Package这个操作符来创建Package类型的对象;
Package:
Package () {5, 0, 0, 0}
Package用来定义一组数据。
它带有一个可选的FixedList和一个可选的VariableList(虽说可选但是一般都会用,不然数据怎么定义),格式是(数据个数) {具体数据}。
这里需要注意几点:
1. 数据个数如果是空的,则编译器会通过计算后面的具体数据来得到值;
2. 具体数据可是实际的数据对象,也可以是对象引用——就是用Name定义的对象的名称;
这个太大了,就不在这里讲了。
前面的介绍中多次ASL中的数据类型,这里统一说明下。
ASL中的数据类型大多通过操作符生成,但是像整数和字符串这样的也可以直接写。
下面是ASL中的所有类型:
1. 未初始化;
2. Buffer:由操作符Buffer创建;
3. Buffer Field:Buffer中的成员,由操作符CreateBitFiled等一系列的CreateXXXFiled操作符创建;
4. Debug Object:可以输出到系统Debug口的对象;
5. Device:设备或者总线对象,由操作符Device创建;
6. Event:时间同步对象,由操作符Event创建;
7. Field Unit:在Operation Region中,使用Filed等操作符创建,是地址空间的一部分(portion of an address space);
8. Integer:整型;
9. Integer Constant:有如下的几个:ONE(1),ONES(全FF),ZERO(0),REVISION(版本号),它们本身就是操作符;
10. Method:可执行的方法,由Method操作符创建;
11. Mutex:同步互斥锁对象,由Mutex操作符创建;
12. Object Reference:对象引用,就是Name中的名称;
13. Operation Region:表示地址空间中的一段区域,由OperationRegion操作符创建;
14. Package:ASL对象集合,由Package操作符创建;
15. Power Resource:Power Resource描述对象,由PowerResource操作符创建;
16. Processor:处理器描述对象,现在不再使用了,用Device代替了;
17. String:字符串;
18. Thermal Zone:温度区域描述符,由ThermalZone操作符创建;