自己动手写DSDT系列教程1-ASL(ACPI Source Language)基础篇

写在前面的话,鄙人不是一个主板BIOS开发工作者,以下对ASL的理解仅仅来源于http://www.acpi.info/上的ACPI Specification文档。因此难免会出现不少错误的理解,以及错误的观点,希望大家谅解以及纠正。
    首先,不得不说一下DSDT(Differentiated System Description Table)。什么是DSDT呢?其实它属于ACPI其中的一个表格,而ACPI是Advanced Configuration & Power Interface的缩写,高级配置和电源接口,从文字上就可以理解ACPI是一系列的接口,这个接口包含了很多表格,所以DSDT即是其中的一张表格同时也是一些接口。所以不难想象ACPI主要的功能就是提供操作系统一些服务以及提供一些讯息给操作系统使用。DSDT自然也不例外。ACPI的一个特色就是专有一门语言来编写ACPI的那些表格。它就是ASL(ACPI Source Language)也就是这盘文章的主角,ASL经过编译器编译后,就变成了AML(ACPI Machine Language),然后由操作系统来执行。既然ASL是一门语言,那就有它的准则。

ASL准则:
1、变量命名不超过4个字符,且不能以数字开头。联想一下看到过的DSDT代码看看,绝对不会超过。
2、Scope形成作用域,概念类似于数学中的集合{}。有且仅有一个根作用域,所以DSDT都以


DefinitionBlock ("xxxx", "DSDT", 0x02, "xxxx", "xxxx", xxxx)
{


开始,同时以


}


结束。这个就是根作用域。xxxx参数依次表示输出文件名、OEMID、表ID、OEM版本。第三个参数根据第二个参数指定,如上面所示。如果是"DSDT"就一定是0x02,其他参数都可以自由修改。
3、以“_”字符开头的函数和变量都是系统保留的,这就是为什么反编译某些AML以后得到的ASL出现_T_X,重新编译的时候会出现警告。
4、Method定义函数,函数可以定义在Device下或者Scope下,但是不能脱离Scope定义单独的函数,所以不会有这种情况出现。


Method (xxxx, 0, NotSerialized)
{
......
}
DefinitionBlock ("xxxx", "DSDT", 0x02, "xxxx", "xxxx", xxxx)
{
......
}


5、根作用域下有\_GPE,\_PR,\_SB,\_SI,\_TZ五个作用域。\_GPE就是ACPI的事件处理,\_PR处理器,\_SB所有的设备和总线。\_SI系统指示灯。_TZ热区,用于读取某些温度。不同属性的东西放在对应的作用域下。例如:
设备Device (PCI0)放在Scope (\_SB)里面


Scope (\_SB)
{
    Device (PCI0)
    {
        ....
    }
    ....
}


CPU相关的信息放在Scope (_PR)


Scope (_PR)
{
    Processor (CPU0, 0x00, 0x00000410, 0x06)
    {
        ....
    }
    ....
}


Scope (_GPE)放着相关的事件处理


Scope (_GPE)
{
    Method (_L0D, 0, NotSerialized)
    {
        ....
    }
    ....
}


乍一看不是函数吗?当然函数也可以放在这里。但是请注意函数名“_”开头,是系统保留的函数。
6、Device(xxxx)也可看做是一个作用域
7、符号“\”引用根作用域,“^”引用上级作用域
8、ASL中没有运算符,+-*/=是不会出现了,但是有等价的函数。
9、函数最多可以传递8个参数,在函数里用Arg0~Arg7表示,不可以自定义。
10、函数最多可以用8个局部变量,用Local0~Local7,不用定义,但是需要初始化才能使用,也就是一定要有一次赋值操作。

ASL常用的数据类型:
Integer(整数)、String(字符串)、Event(事件)、Buffer(数组)、Package(对象集合)
ASL定义变量:
例如:
Name(TEST,0)//定义一个整数
Name(MSTR,"ASL")//定义一个字符串


Name (_PRW, Package (0x02)
                {
                    0x0D, 
                    0x03
                })


//定义一个Package
可以发现定义变量的时候不需要显式声明其类型
ASL赋值方法:
有且仅有一个,Store(a,b)如
Store(0,Local0)//Local0=0
Store(Local0,Local1)//Local0=Local1

ASL运算函数:
上面提到ASL没有运算符号,但是有运算函数
Add 整数相加
And 整数于
Decrement 整数自减1
Divide 整数除法
Increment 整数自增1
Mod 整数求余
Multiply 整数相乘
ShiftLeft 左移
ShiftRight 右移
Subtract 整数减法
Or 或
Not 取反
Nor 异或
等等,具体请查阅ACPI Specification
举例如下:


Add(1, 2, Local0) //Local0 = 1 + 2
And(0x11, 0x22, Local0) //Local0 = 0x11 & 0x22
Divide(10, 9, Local1, Local0) //Local0 = 10 / 9, Local1 = 10 % 9
Mod (10, 9, Local0) //Local0 = 10 % 9
Multiply(1, 2, Local0) //Local0 = 1 * 2
ShiftLeft(1, 20, Local0) // Local0 = 1 << 20
ShiftRight(0x10000, 4, Local0) // Local0 = 0x10000 >> 4
Subtract(2, 1, Local0) //Local0 = 2 - 1
Or(0x01, 0x02, Local0) //Local0 = 0x01 | 0x02
Not(0x00,Local0) //Local0 = ~(0x00)
Nor(0x11, 0x22, Local0) //Local0 = ~(0x11) & ~(0x22)


ASL逻辑运算:
LAnd 逻辑与
LEqual 逻辑相等
LGreater 逻辑大于
LGreaterEqual 逻辑大于等于
LLess 逻辑小于
LLessEqual 逻辑小于等于
LNot 逻辑反
LNotEqual 逻辑不等于
LOr 逻辑或


Store (LAnd(1, 1), Local0) //Local0 = 1 & 1
Store (LEqual(1, 1), Local0) //Local0 = (1 == 1)
Store (LGreater(1, 2), Local0) //Local0 = (1 > 2)
Store (LGreaterEqual(1, 2), Local0) //Local0 = (1 >= 2)
Store (LLess(1, 2), Local0) //Local0 = (1 < 2)
Store (LLessEqual(1, 2), Local0) //Local0 = (1 <= 2)
Store (LNot(0), Local0) //Local0 = !0
Store (LNotEqual(0, 1), Local0) //Local0 = (0 != 1)
Store (LOR(0, 1), Local0) //Local0 = (0 | 1)


不难发现逻辑运算只会有两种结果0或者1。

ASL函数的定义:
1、定义函数


Method(TEST)
{}


2、定义有两个输入参数的函数,以及使用局部变量Local0~Local7


Method(MADD,2)
{
    Store(Arg0, Local0)
    Store(Arg1, Local1)
    Add(Local0, Local1, Local0)
}


实现了两个参数的加法运算,因此传入的参数一定要隐式整形数。
3、定义带返回值的函数


Method(MADD,2)
{
    Store(Arg0, Local0)
    Store(Arg1, Local1)
    Add(Local0, Local1, Local0)
    Return (Local0)
}


例子为自定义加法的实现。函数实现如同系统函数Add一样的相加
Store(MADD(1,2), Local0) //Local0 = 1 + 2
4、定义可序列化的函数


Method(MADD, 2, Serialized)
{
Store(Arg0, Local0)
Store(Arg1, Local1)
Add(Local0, Local1, Local0)
Return (Local0)
}


这个有点类似于多线程同步的概念,也就是说,当函数声明为Serialized,内存中仅能存在一个实例。一般应用在函数中创建一个对象。应用举例说明:


Method(TEST, Serialized)
{
    Name(MSTR,"I will sucess"
}


如果这样子声明TEST这个函数,那么在两个地方同时调用这个函数


Device (Dev1)
{
     TEST()
}
Device (Dev2)
{
     TEST()
}


如果先执行Dev1的TEST,Dev2中的TEST将等待Dev1中的TEST函数执行完毕再执行。如果声明为


Method(TEST, NotSerialized)
{
Name(MSTR,"I will sucess"
}


那么将在其中一个Devx调用TEST的时候,另外一个调用失败,当它试图创建相同的字符串MSTR的时候。

ASL流程控制:
和常见的高级语言一样,ASL也有与之对应的控制流程语句。
Break
BreakPoint
Case
Continue
Default
Else
ElseIf
If
Return
Stall
Switch
While
(一)分支控制If, ElseIf, Else, Switch, Case
1、If用法介绍
例如下面的语句判断一下当前系统的接口是不是Darwin,如果是把OSYS = 0x2710


If (_OSI ("Darwin"))
{
    Store (0x2710, OSYS)
}


2、ElseIf、Else用法介绍,如果系统结构不是Darwin,另外如果系统不是Linux,那么OSYS = 0x07D0


If (_OSI ("Darwin"))
{
    Store (0x2710, OSYS)
}
ElseIf (_OSI ("Linux"))
{
    Store (0x03E8, OSYS)
}
Else
{
    Store(0x07D0, OSYS)
}


3、Switch, Case, Defaule, BreakPoint示例


Switch(Arg2)
{
    Case(1)
    {
If(LEqual(1, Arg1)
{
    Return (1)
}
BreakPoint
    }
    Case(2)
    {
        ....
        Return (2)
    }
    Default
    {
        BreakPoint
    }
}


Switch可以看做是一系列If....Else的集合。BreakPoint相当于断点,意味着退出当前Swtich
(二)循环控制While,Break,Continue以及暂停Stall示例


Store(10, Local0)
While (LAnd (0x00, Local0))
{
    Decrement (Local0)
    Stall (32)
}


Local0等于10,如果Local0逻辑于0不为真,Local0自减1,暂停32us,所以这段代码延时10 * 32 = 320 us。

累了,今天就写到这里,有了这些基础,鄙人觉得现在大部分论坛坛友对着DSDT的时候就不会是发呆又发呆,至少可以明白些少语句了。


原文出自:http://bbs.pcbeta.com/viewthread-944566-1-1.html

猜你喜欢

转载自blog.csdn.net/yuzaipiaofei/article/details/79394737