ASL语言

ASL的全称是ACPI Source language,ASL现在作为BIOS/UEFI的一部分,它包含在BIOS的源代码里面。

BIOS/UEFI中也有相关的ACPI操作的接口,它最终会将ACPI相关的内容放到内存的某段空间中,并将指向该区域的指针传递给OS。OS使用这里的内容来操作获取硬件信息并操作相关的硬件。这样的好处是,OS不需要直接去与底层硬件沟通,它只要直接操作这些ASL生成的内容就可以了。另一个好处是,只要支持ACPI,不管OS是Windows还是Linux,都可以完成与硬件交互,也就是OS Independent。

一、ASL基本准则

作者:freevanx

  1. 变量命名不超过4个字符,且不能以数字开头。
  2. 变量或者函数命名,不分大小写。
  3. Scope形成作用域,概念类似于C++中的namespace,Java中的package。
  4. Method 或者 Function 定义函数,函数可以定义在 Device 下或者 Scope 下,但是不能脱离 Scope 定义 单独的函数,也就是说,函数必须依附于对象(Scope or device)
  5. 以"_"字符开头的函数,都是系统保留的,不得给自己的函数取这样的名字
  6.  ASL 中没有运算符号(逻辑或者算术都是如此),但有与此等价的相应系统函数代替
  7. 符号“\”引用根作用域,“^”引用父级或称上级作用域
  8. 作用域,或者称路径,有相对和绝对之分。相对作用域从当前作用域开始,向上延伸。也就是说在当 前作用域中使用函数和变量时,解析器会首先从当前作用域中寻找它的定义,如果找不到,则会从父级 或称上级作用域中继续寻找,一直找到当前作用域的 root 为止。绝对路径,则从定义此变量或者函数的 root 作用域开始,一级级的写下去,一直写到此变量的作用域,作用域的引用使用符号“.”,例如 \SB.PCI0.ABCD 。
  9. 函数最多可传递 8 个参数,在函数里用 Arg0~Arg7 引用,不可以自己定义名字
  10.  在函数中最多可以使用 8 个局部变量,用 Local0~Local7 表示,不用定义,但是在把局部变量的值 赋给其他变量之前,局部变量必须是有效的值,也就是说,至少有一次把值赋给局部变量的操作
  11. 声明变量时不需要显式声明其类型,只学系统和应用型语言的童鞋可能会感到强大的不适应,而会 Perl 或者 Python 这类 Script 语言的童鞋则见怪不怪。

二、数据类型,赋值,基本运算

1. 数据类型 ASL 中支持的数据类型有:

  1.  整数- Integer,
  2. 字符串 - String,
  3.  事件 - Event,
  4. 数组 - Buffer,
  5. 对象集合 - Package

2. 定义变量 

定义一个整数  Name(MYTS, 0)

定义一个字符串 Name(TSTR, "Hello ASL")

3. 赋值

最常用的赋值函数只有一个,即 Store(),

如 Store(0x1234, Local0) // Local0 = 0x1234;

Store("Hello ASL", Local0) // Local0 = "Hello ASL"

4. 算术运算

算数运算的操作符如下图所示,请牢记第一节列出的准则,千万不要使用 + - * / 等符号计算 举例如下:

  1. Add(3, 5, Local0) // Local0 = 3 +5
  2. And (0xF4, 0x39, Local0) // Local0 = 0xF4 & 0x39
  3. Divide(100, 9, Local1, Local0) // Local0 = 100/9, Local1 = 100 % 9
  4. Mod(100, 9, Local0) // Local0 = 100 % 9
  5. Multiply(34, 25, Local0) // Local0 = 34 * 25
  6. Nor(0x34, 0xF8, Local0) // Local0 = (~0x34) & (~0xF8),
  7. 无对应的操作符 Not(0x00, Local0) // Local0 = ~0x00
  8. Or(0x3F, 0xF4, Local0) // Local0 = 0x3F | 0xF4
  9. ShiftLeft(3, 6, Local0) // Local0 = 3 > 6
  10. Subtract(100, 24, Local0) // Local0 = 100 - 24
  11. Xor(0x3F, 0x90, Local0) // Local0 = 0x3F ^ 0x90

除了上面列出的使用方法之外,这些函数还会将计算结果通过返回值传递回来,这样就方便一次连接很 长的算式。 例如: Store(Add(5, 4), Local0) // Local0 = 5 + 4

三、函数,

流程控制

1. 定义函数

Method(TMED) {  } 

2. 定义有两个输入参数的函数

Method(TMED, 2) { // Arg0 - 第一个输入参数 // Arg1 - 第二个输入参数 } 

3. 在函数中使用局部变量

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

4. 在函数中使用返回值

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

5. 调用函数 TMED(3, 5) 6. 保存函数的返回值 Store(TMED(4, 5), TMPD)

Scope(_SB)
	{
		Device(LID0)
		{
			Name(_HID, EISAID("PNP0C0D"))
			Method(_STA, 0, NotSerialized)
			{
				Return(Zero)
			}
		}
	}
	Name(_S0, Package(4) {Zero, Zero, Zero, Zero})
	If(SS1)
	{
		Name(_S1, Package(4) {One, Zero, Zero, Zero})
	}
	If(SS3)
	{
		Name(_S3, Package(4) {0x05, Zero, Zero, Zero})
	}
	If(SS4)
	{
		Name(_S4, Package(4) {0x06, Zero, Zero, Zero})
	}
	Name(_S5, Package(4) {0x07, Zero, Zero, Zero})
	Method(PTS, 1, NotSerialized)
	{
		If(Arg0)
		{
			\_SB.PCI0.LPCB.SPTS(Arg0)
			\_SB.PCI0.NPTS(Arg0)
			\_SB.SARM(Arg0)
			\_SB.PCI0.LPCB.SIOS(Arg0)
		}
	}
	Method(WAK, 1, NotSerialized)
	{
		\_SB.PCI0.LPCB.SWAK(Arg0)
		\_SB.PCI0.NWAK(Arg0)
		\_SB.PCI0.LPCB.S1RS(Arg0)
		\_SB.PCI0.LPCB.SIOW(Arg0)
	}
}

四、OperationRegion 的使用,IO,Memory,PCI,EC 读写

OperationRegion 是 ACPI 定义的一种操作 Register 方式,可以操作的 Register 包括 IO Memory PCI 等 等,ACPI 4.0 支持的 OperationRegion 共有以下几种。此外,用户还可以自己写一个 ACPI 驱动,注册自 己的 OperationRegion。

就当前来说,并不是上面所有的 OperationRegion 都受到支持且可以使用,一些 OperationRegion,受限 于编译器,OS 下 AML 的 Interpreter 支持等等因素,是不能确定能够在当前 ASL 中使用的,类似的 OperationRegion 有 CMOS,PCIBARTarget,IPMI,SMBus。另外,对于其他的一些 OperationRegion,ACPI Spec 有一些特殊的规定

  1. OS 必须保证 SystemIO OperationRegion 在任何情况下都可以使用
  2. OS 必须保证 PCI Root Bus 下的 PCI_Config OperationRegion 一定可用
  3. OS 必须保证,SystemMemory OperationRegion 在访问通过 Memory Map Report 的 Memory 时,一定可 用。事实上,这一条就是说明,只要是在有效地址空间中的 Memory 访问,OS 必须保证 Memory OperationRegion 可用

此外其他 OperationRegion,必须通过_Reg Method 去判断,如果 OperationRegion 已经 connect,则此 时此 OperationRegion 可用,如果没有 connect,或者已经 Disconnect,则不可通过此 OperationRegion 去访问设备的地址空间。

IO OperationRegion 接下来,将展示一个 IO OperationRegion 的使用,我们使用定义的 OperationRegion,将 debug code 输出到 80 Port

//示例开始

OperationRegion (DBGP, SystemIO, 0x80, 4) 
Field (DBGP, ByteAcc, Lock, Preserve) 
{ 
P80L, 8
} 
Store(0xA3, P80L) 

// 输出 A3 到 80 port

Memory OperationRegion

接下来,我们通过 Memory OperationRegion 展示一个相当好用函数。我们知道,对于 PCIe 设备来说, 有两种访问方法,一种是通过传统的 PCI 兼容方式,另外一种是通过 MMIO,不但可以访问 PCI 的 256 byte 的 PCI Space,而且可以访问全部的 4K PCI space,那么接下来,我们将展示这样一组函数,他能够让 你在 ASL 里面自由的访问 PCI Express 的 Configuration Space。

//示例开始

#define PCIE_BASE 0xE0000000 
Method (RDPB, 1) { 
Add (Arg0, PCIE_BASE, Local0) // Add PCI Express MMIO base address 
OperationRegion (PECF, SystemMemory, Local0, 0x1) 
Field (PECF, ByteAcc, Nolock, Preserve) { 
MCFG, 8 , 
} 
Return (MCFG) 
} 

上面展示的函数是直接通过 MMIO 访问 PCI Express 设备的 configuration space,但是上面函数只适合 比较熟练的开发者使用,因为需要自己计算 PCIe 设备的地址,那么,我们把上面的函数稍微转换一下, 变成下面的函数,这样就够直观了。

// Arg0 - bus no 
// Arg1 - dev no 
// Arg2 - func no 
// Arg3 - register offset 
Method (RDPB, 4) { 
ShiftLeft(Arg0, 20, Local0) // 计算 bus number PCIe address 
Or(ShiftLeft(Arg1, 15), Local0, Local0) // 计算 device number PCIe address
Or(ShiftLeft(Arg2, 12), Local0, Local0) // 计算 function number PCIe address 
Or(Arg3, Local0, Local0) // 计算 register,形成最终 PCIe address 
Add (Arg0, PCIE_BASE, Local0) // Add PCI Express MMIO base address 
OperationRegion (PECF, SystemMemory, Local0, 0x1) 
Field (PECF, ByteAcc, Nolock, Preserve) {
 MCFG, 8 , 
} 
Return (MCFG) 
}

有了上面的函数,我们可以直接在 ASL 读某个 PCIe 设备的一个 byte,例如,我们读 PCI 设备,bus 0, dev 31, func 3, register 0x40,可以使用如下语句:

Store(RDPB(0, 31, 3, 0x40), Local0)

上面是读 PCIe Register 的函数,接下来,我们将上面的函数稍作修改,写一个写 byte 到 PCIe register 的函数。

// Arg0 - bus no 
// Arg1 - dev no 
// Arg2 - func no 
// Arg3 - register offset 
// Arg4 - value 
Method (WRPB, 4) { 
ShiftLeft(Arg0, 20, Local0) // 计算 bus number PCIe address 
Or(ShiftLeft(Arg1, 15), Local0, Local0) // 计算 device number PCIe address
Or(ShiftLeft(Arg2, 12), Local0, Local0) // 计算 function number PCIe address 
Or(Arg3, Local0, Local0) // 计算 register,形成最终 PCIe address 
Add (Arg0, PCIE_BASE, Local0) // Add PCI Express MMIO base address 
OperationRegion (PECF, SystemMemory, Local0, 0x1) 
Field (PECF, ByteAcc, Nolock, Preserve) { 
MCFG, 8 ,
 } 
Store(Arg4, MCFG) 
} 

可以使用以下语句,将一个 byte 写入到 bus 0, dev 31, func 3, register 0x40

WRPB(0, 31, 3, 0x40, 5)

PCI_CONFIG OperationRegion

可以通过定义 PCI OperationRegion 来访问 PCI 和 PCI Express 设备的配置空间。不过,可以从 PCI OperationRegion 的定义看到,OperationRegion 本身只定义 Register 在 Configuration Space 的位置 和长度,但不能确定 PCI 设备 bus number,device number,和 function number,所以也就不能确定设 备的地址。 ACPI 引入了其他一些 Method 来确定 ACPI 中 PCI 设备的地址。 _SEG 函数定义 PCI 设备的 Segment,在 x86 架构中来说,一般不使用_SEG,所有的 PCI 设备默认都在 Segment 0. _BBN 函数定义 PCI Root Bridge 的 bus number,一般来说在 PCI Root Bridge 下定义_BBN(0),意指从 bus 0 开始。有 了 segment 和 bus,最后我们在 PCI 设备下定义一个_ADR 属性,确定此 PCI 设备的 device number 和 function number,_ADR 的返回值中,高 16 位表示 device number,低 16 位表示 function number,接 下来将展示一段 sample code,读写 bus 0 ,device 29,function 1 USB controller 的 PCI Configuration Space

//示例开始

Name(_ADR, 0x001d0001) // Device (HI WORD)=29, Func (LO WORD)=1 
OperationRegion(USBR, PCI_Config, 0xC4, 1) 
Field(USBR, ANYACC, NOLOCK, PRESERVE) { 
URES, 8
 } 
Method(TEMD) { 
Store(3, URES)
 } 

EC OperationRegion

EC OperationRegion 是定义 EC Space 操作的,可以在 ASL 里面定义 EC OperationRegion,直接读写 EC OperationRegion,OS 的 ACPI 或者 EC Driver 或将这些操作转换为对 EC Space 的读写。根据 ACPI spec, 对于一个读操作,driver 会向 EC 发送 0x80 command 读其中的 value,对于写操作,driver 会向 EC 发 0x81 command 将一个 value 写道 EC Space。接下来定义一个 EC OperationRegion,假设 EC Space offset 0x00 是 CPU 的温度。

//示例开始

OperationRegion (ECF2, EmbeddedControl, 0x00, 0xFF) 
Field (ECF2, ByteAcc, Lock, Preserve) { 
CTMP, 8, //CPU Temp 
} 
Store(CTMP, Local0) // Read CPU temperature from EC Space 

与上面其他 OperationRegion 不同的是,EC OperationRegion 并不是任何时刻都可以使用,所以我们要 follow ACPI spec,在同一 scope 中定义一个_Reg 属性,来判断 EC OperationRegion 是否可用

//示例开始

 Name(ECON, 0) // Variable to remember EC 
OperationRegion Status Method(_REG, 0x2) { 
if(LEqual(Arg0, 0x03)) // Is it EC OperationRegion? Yes EC =3
 { 
If(Arg1) // Is OperationRegion Connect? 
{ 
Store(0x01, ECON) // Available 
  } 
Else // OperationRegion Disconnect 
{ 
Store(0x00, ECON) // unavailable 
  } 
 } 
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值