ASL語言

瀟灑Anthony發表於2020-12-17

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 
  } 
 } 
}

 

相關文章