Tìm hiểu về ASL
Bài viết yêu cầu bảng đã có 1 chút nền tảng về lập trình
Bài viết này được viết dựa trên các tài liệu được công bố ở diễn đàng ACPI Specifications
Do bài viết được tổng hợp từ nhiều nguồn nên có gì sai sót mong các bạn thông cảm
ASL là gì?
Với ACPI, nó sử dụng một ngôn ngữ độc quyền, được gọi là ASL
Viết tắt của ACPI Source Language
Đây sẽ là phần trọng tâm của bài viết
Để tạo ra các bảng ACPI
Các bảng này chứa thông tin về phần cứng.
Thêm nữa sau khi được biên dịch ASL sẽ trở thành AML
Viết tắt của ACPI Machine Language
Và lúc này hệ thống sẽ có thể thực thi các bảng ACPI
Và cuối cùng ASL là một ngôn ngữ, nó có các quy tắc và hướng dẫn riêng.
Phần tiếp theo sẽ nói rõ về các quy tắc và hướng dẫn này
ASL cơ bản
Sơ lược về DefinitionBlock
DefinitionBlock
Nếu có 1 chút kinh nghiệm với hackintosh có lẽ cụm từ DefinitionBlock
hoàn toàn không hễ khó bắt gặp trong các SSDT/DSDT rồi đúng không nào
Ta có thể hiểu rằng DefinitionBlock
là nền tảng của mội đoạn mã ASL
Cụ thể hơn là của mọi bảng ACPI
Tạm dịch: Khối định nghĩa
Như bài trước ta đã tìm hiểu các bảng ACPI đóng vai trò chẳng khác gì một quyển từ điển cung cấp các thông tin cho firmware
Tất cả mã ASL đều phải nằm bên trong DefinitionBlock
Khối DefinitionBlock
này được xác định bởi cặp ngoặc nhọn {}
Bất kỳ mã
ASL
nào nằm bên trong cặp ngoặc nhọn này được gọi làRoot Scope
.Và phạm vi giữa hai dấu ngoặc nhọn được gọi là phạm vi gốc
Thêm nữa bất kì đoạn mã ASL nào nằm ngoài khối DefinitionBlock
đều được xem là không hợp lệ
Tức là không được chấp nhận bởi hệ thống ACPI
Ở đây chúng ta sẽ đến với một ví dụ
Dưới đây là một số ví dụ
Nếu bạn viết bảng thay thế (ví dụ: cho khai báo cổng USB), bạn cần sử dụng cùng một OEM Table ID với bảng bạn muốn thay thế.
Nghe hơi khó hiểu nhưng cụ thể là nếu bạn muốn viết một SSDT thay thế cho một bảng ACPI nào khác ví dụ như một SSDT khác hay một DSDT khác thì phải cùng OEM Table ID
Hiểu chi tiết những gì mình nói xem tại đây
Control Method
Quy tắc đặc tên
Trong ASL, khi bạn đặt tên cho method và biến, hãy tránh dùng dấu gạch dưới _
ở đầu.
Do chúng được "đánh dấu" là của hệ điều hành.
Nếu sau khi dịch ngược bảng ASL mà bạn thấy cảnh báo _T_X
Thì rất có thể bạn đã "đụng hàng" tên với hệ thống
Mối quan hệ của Method và Scope
Một Method
phải luôn nằm bên trong một Scope
của một Device
nào đó.
Do đó, ví dụ dưới đây không hợp lệ vì
Method
được đặt ngay sauDefinitionBlock
, trong khiDefinitionBlock
không phải là mộtScope
Mối quan hệ của Method
và Device
Method
không chứaDevice
Method
không thể định nghĩa hoặc khai báoDevice
trực tiếp bên trong nó.Device
phải được khai báo trong mộtScope
.
Method
thường nằm trongScope
củaDevice
Trong hầu hết trường hợp,
Method
được đặt bên trongScope
của mộtDevice
cụ thể.Điều này cho phép
Method
truy cập và thao tác với các thuộc tính và chức năng củaDevice
đó một cách dễ dàng.
Method
thao tác vớiDevice
thế nào:Bên trong
Method
, bạn có thể sử dụng tên củaDevice
Nếu nó nằm trong cùng
Scope
hoặcScope
cha để:Đọc và ghi giá trị thuộc tính của
Device
. Ví dụ, bạn có thể đọc trạng thái hiện tại củaDevice
hoặc thay đổi cấu hình của nó.Gọi các
Method
khác được định nghĩa trongDevice
. Điều này cho phép bạn thực hiện các tác vụ phức tạp hơn vớiDevice
.
Các Scope gốc
\_GPE,\_PR,\_SB,\_SI,\_TZ
thuộc phạm vị root scope /
Trong ASL
, các thành phần với thuộc tính khác nhau được đặt bên trong các Scope
tương ứng để tổ chức mã nguồn một cách logic và dễ quản lý.
Ví dụ:
Device (PCI0) (Thiết bị PCI0) sẽ được đặt bên trong Scope (_SB) (Phạm vi System Bus) vì PCI là một loại bus hệ thống.
Processor (CPU0, ...) (Bộ xử lý CPU0) sẽ được đặt bên trong Scope (_PR) (Phạm vi Processor).
Các phương thức xử lý ngắt từ bàn phím (Method (_KBD, ...) ) sẽ được đặt bên trong Scope (_GPE) (Phạm vi General Purpose Event).
Lợi ích của việc tổ chức theo Scope:
Dễ đọc, dễ hiểu: Mã nguồn rõ ràng, dễ dàng tìm kiếm và theo dõi các thành phần liên quan.
Tránh xung đột tên: Các thành phần có thể có cùng tên nhưng nằm trong Scope khác nhau, giúp tránh nhầm lẫn.
Quản lý quyền truy cập: Scope kiểm soát phạm vi nhìn thấy của các tên, giúp bảo vệ dữ liệu và ngăn chặn lỗi sửa đổi ngoài ý muốn.
Nhắc lại một lần nữa
Các phương thức (Methods) và biến (variables) bắt đầu bằng dấu gạch dưới (_) được dành riêng cho các hệ điều hành.
Nhận diện Device
Device (xxxx) cũng có thể được nhận diện như một scope, nó chứa các mô tả về thiết bị, ví dụ: _ADR, _CID, _UID, _DSM, _STA.
Giải thích: Trong ASL, một thiết bị (Device) cũng có thể hoạt động như một phạm vi (scope). Bên trong phạm vi này, các mô tả chi tiết về thiết bị có thể được định nghĩa. Các mô tả này bao gồm:
_ADR: Địa chỉ của thiết bị.
_CID: Mã nhận diện tương thích của thiết bị.
_UID: Mã nhận diện duy nhất của thiết bị.
_DSM: Phương thức đặc biệt dành cho thiết bị.
_STA: Trạng thái của thiết bị.
Ký hiệu Scope
Ký hiệu \ trích dẫn root scope
Ký hiệu ^ trích dẫn superior scope.
Tương tự, ^^ đại diện cho phạm vi cao hơn một cấp nữa so với ^
Giải thích: Trong ASL, ký hiệu \ được sử dụng để trích dẫn root scope
Tức là phạm vi gốc của toàn bộ hệ thống.
Ký hiệu ^ được dùng để trích dẫn superior scope
Tức là phạm vi cấp trên so với phạm vi hiện tại.
Khi sử dụng nhiều dấu ^ liên tiếp, mỗi dấu đại diện cho một cấp độ phạm vi cao hơn.
ASL+ (ASL2.0)
Bảng ACPI 2.0, nó giới thiệu các toán tử của ngôn ngữ C như +-*/=, <<, >> và các phép so sánh logic ==, !=, v.v.
Giải thích: ASL+ (phiên bản 2.0 của ASL) mở rộng cú pháp của ASL, bao gồm việc giới thiệu các toán tử toán học và logic của ngôn ngữ lập trình C. Các toán tử này bao gồm:
Toán tử số học: +, -, *, /, = (cộng, trừ, nhân, chia, gán)
Toán tử dịch bit: <<, >> (dịch trái, dịch phải)
Toán tử logic: ==, != (bằng, không bằng)
Method trong ASL
Các phương thức (Methods) trong ASL có thể chấp nhận tối đa 7 tham số; chúng được biểu diễn bằng Arg0 đến Arg6 và không thể tùy chỉnh.
Giải thích: Trong ASL, một phương thức (method) có thể nhận đến 7 tham số đầu vào. Các tham số này được đặt tên từ Arg0 đến Arg6 và không thể đổi tên hoặc tùy chỉnh. Điều này có nghĩa là khi viết phương thức, bạn chỉ có thể sử dụng các tham số đã được định sẵn này.
Local variables trong ASL
Các biến cục bộ (Local variables) trong ASL có thể chấp nhận tối đa 8 tham số, được biểu diễn bằng Local0 đến Local7. Các định nghĩa không cần thiết, nhưng phải được khởi tạo, nói cách khác, cần có sự gán giá trị.
Giải thích: Trong ASL, các biến cục bộ có thể nhận đến 8 tham số, được đặt tên từ Local0 đến Local7. Không cần thiết phải định nghĩa các biến này trước khi sử dụng, nhưng chúng phải được khởi tạo bằng cách gán giá trị ban đầu. Điều này có nghĩa là bạn phải gán giá trị cho biến trước khi sử dụng nó trong phương thức.
Các khai báo trong External
External
Các External references phải được sử dụng để truy cập các đối tượng trong DSDT, nơi chúng được định nghĩa. Nói cách khác: bạn cần thông báo cho SSDT vị trí của Device, Method hoặc bất kỳ đối tượng nào mà bạn muốn thay đổi trong cấu trúc của DSDT.
Các card dưới đây sẽ tuân theo quy tắc sau:
Dòng đầu Quote Types
Dòng tiếp theo External Reference
Dòng kế đó Addressed Parameter
Dòng cuối cùng là Giải thích
Ví dụ:
ACPI Preset Functions
ACPI Preset Functions là các phương thức và giao diện được xác định sẵn trong ACPI
Để hỗ trợ cấu hình và quản lý tài nguyên phần cứng của hệ thống.
Các chức năng này cho phép hệ điều hành và firmware giao tiếp và kiểm soát các thiết bị phần cứng một cách linh hoạt và hiệu quả.
_OSI
(Operating System Interfaces)
_OSI
(Operating System Interfaces)Method_OSI (Giao diện Hệ điều hành) dễ dàng lấy được tên và phiên bản của hệ điều hành hiện tại.
Ví dụ, chúng ta có thể áp dụng một bản vá cụ thể cho Windows hoặc macOS.
Giá trị cho các Method _OSI phải được chọn từ bảng dưới đây
Lưu ý
Các phiên bản Windows khác nhau yêu cầu một chuỗi riêng biệt
Đọc thêm tại: WinACPI-OSI Documentation.
_STA
(Status)
_STA
(Status)Có hai loại _STA
Đừng nhầm lẫn với _STA từ PowerResource!
Phương thức _STA có thể trả về 5 loại bit, giải thích như sau
Chúng ta cần chuyển đổi các bit này từ hệ thập lục phân sang hệ nhị phân.
Ví dụ, 0x0F chuyển thành 1111, có nghĩa là kích hoạt nó (bốn bit đầu tiên)
Trong khi Zero có nghĩa là vô hiệu hóa.
Chúng ta cũng gặp 0x0B và 0x1F.
Dạng nhị phân của 0x0B là 1011
Có nghĩa là thiết bị được kích hoạt nhưng không được phép giải mã tài nguyên của nó.
0x0B thường được sử dụng trong SSDT-PNLF.
0x1F (11111) chỉ xuất hiện để mô tả các thiết bị pin trên máy tính xách tay
Bit cuối cùng được sử dụng để thông báo cho thiết bị Pin Điều khiển Phương thức PNP0C0A rằng pin hiện diện.
Có lẽ vẫn hơi mơ hồ hãy đọc phần giiar thích chi tiết sau nhé
Giải thích cho Method _STA
_STA
Trong biểu diễn nhị phân, các bit được đánh số từ phải sang trái, bắt đầu từ 0
Dưới đây là cách phân tích chi tiết giá trị nhị phân
00001111
để xác định từng bit và ý nghĩa của chúng:
Vị trí các bit:
Ý nghĩa từng bit
Tổng kết
Bit [0] là bit ở vị trí ngoài cùng bên phải.
Bit [1] là bit ngay cạnh bên trái của bit [0].
_CRS
(Current Resource Settings)
_CRS
(Current Resource Settings)_CRS
trả về một Bộ đệm (Buffer
)
Thường được sử dụng để lấy các thiết bị cảm ứng như GPIO Pin, APIC Pin để điều khiển chế độ ngắt.
Các kiểu dữ liệu cơ bản trong ASL
Định nghĩa Biến trong ASL
Định nghĩa Integer (Số nguyên)
Name (TEST, 0): Định nghĩa một biến số nguyên có tên là
TEST
với giá trị ban đầu là0
.
Định nghĩa String (Chuỗi)
Name (MSTR, "ASL"): Định nghĩa một biến chuỗi có tên là
MSTR
với giá trị ban đầu là"ASL"
.
Định nghĩa Package (Gói)
Name (_PRW, Package (0x02) { ... }): Định nghĩa một biến gói (package) có tên là
_PRW
với hai phần tử bên trong là0x0D
và0x03
.
Định nghĩa Buffer Field (Trường bộ đệm)
Có 6 loại trường bộ đệm có sẵn trong ASL, mỗi loại có kích thước khác nhau và cú pháp riêng biệt.
Các số trong cập ( ) mình dùng ở đây là size của field
CreateBitField (1-Bit)
CreateBitField (AAAA, Zero, CCCC): Tạo một trường bộ đệm 1-bit từ bộ đệm
AAAA
bắt đầu từ vị tríZero
và đặt tên cho trường làCCCC
.
CreateByteField (8-Bit)
CreateByteField (DDDD, 0x01, EEEE): Tạo một trường bộ đệm 8-bit từ bộ đệm
DDDD
bắt đầu từ vị trí0x01
và đặt tên cho trường làEEEE
.
CreateWordField (16-Bit)
CreateWordField (FFFF, 0x05, GGGG): Tạo một trường bộ đệm 16-bit từ bộ đệm
FFFF
bắt đầu từ vị trí0x05
và đặt tên cho trường làGGGG
.
CreateDWordField (32-Bit)
CreateDWordField (HHHH, 0x06, IIII): Tạo một trường bộ đệm 32-bit từ bộ đệm
HHHH
bắt đầu từ vị trí0x06
và đặt tên cho trường làIIII
.
CreateQWordField (64-Bit)
CreateQWordField (JJJJ, 0x14, KKKK): Tạo một trường bộ đệm 64-bit từ bộ đệm
JJJJ
bắt đầu từ vị trí0x14
và đặt tên cho trường làKKKK
.
CreateField (Kích thước bất kỳ)
CreateField (LLLL, Local0, 0x38, MMMM): Tạo một trường bộ đệm với kích thước bất kỳ từ bộ đệm
LLLL
, bắt đầu từ vị tríLocal0
, có độ dài0x38
và đặt tên cho trường làMMMM
.
Trong ASL việc định nghĩa một biến không cần phải thông báo rõ ràng loại của biến.
Điều này có nghĩa là bạn có thể định nghĩa các biến mà không cần chỉ rõ chúng là số nguyên, chuỗi, gói, hay bộ đệm.
ASL sẽ tự động xử lý và xác định loại biến dựa trên cách biến đó được sử dụng và giá trị được gán cho nó.
Ví dụ:
Name (TEST, 0)
Các bạn có thể thấy biến
Test
được khai báo nhưng không chỉ rõ loại biến. Tuy vậy do giá trị ban đầu là0
nênASL
sẽ hiểu đây là biến có kiểu dữ liệu nguyên (integer)
Gán giá trị trong ASL
Trong ASL, việc gán giá trị cho các biến và đối tượng rất quan trọng để thiết lập và thay đổi trạng thái của hệ thống phần cứng.
Dưới đây là cách sử dụng các lệnh gán giá trị trong ASL
Các phép tính trong ASL
Bảng Toán Tử trong ASL và Legacy ASL
ASL Logic
Các phép tính logic chỉ có hai kết quả là 0
hoặc 1
.
Định nghĩa Method trong ASL
Định nghĩa một Method
Method (TEST): Định nghĩa một Method với tên
TEST
.
Định nghĩa Method với 2 tham số và sử dụng Biến Cục bộ
Số lượng tham số mặc định là 0. Bạn có thể định nghĩa Method với các tham số và sử dụng các biến cục bộ từ Local0 đến Local7.
Method (MADD, 2): Định nghĩa một Method với tên
MADD
và 2 tham số.Local0, Local1: Sử dụng các biến cục bộ.
Arg0, Arg1: Tham số đầu vào của Method.
Định nghĩa Method có Giá trị Trả về
Return (Local0): Trả về giá trị của Local0.
Local0 = 1 + 2: Ví dụ trong ASL+.
Store (MADD (1, 2), Local0): Ví dụ trong Legacy ASL
Gọi Method
MADD
với tham số 1 và 2,Sau đó gán kết quả cho Local0.
Định nghĩa Method được Tuần tự hóa
Nếu không định nghĩa Serialized
hoặc NotSerialized
, mặc định sẽ là NotSerialized
.
Serialized: Chỉ định rằng Method được tuần tự hóa.
Điều này có nghĩa là chỉ một thể hiện (instance) của Method đó có thể tồn tại và thực thi trong bộ nhớ tại một thời điểm.
Nói cách khác, các lần gọi Method đó sẽ được thực hiện tuần tự, không đồng thời.
Nếu Method không được chỉ định là
Serialized
Mặc định là
NotSerialized
Các lần gọi Method này có thể thực thi đồng thời
Điều này có thể dẫn đến xung đột nếu các lần gọi này cùng tạo hoặc thay đổi cùng một tài nguyên.
Ví dụ về Method Serialized
Method TEST: Nếu gọi từ hai Method khác nhau:
Khi thực thi
TEST
trongDev1
TEST
trongDev2
sẽ phải chờ cho đến khiTEST
trongDev1
hoàn tất.
Ví dụ về Method NotSerialized
Method TEST: Nếu gọi từ hai Method khác nhau:
Nếu một trong các
TEST
được gọi từDevx
, mộtTEST
khác sẽ không thể tạoMSTR
, dẫn đến thất bại.Cụ thể :
Khi cả hai phương thức
TEST()
đang chạy đồng thời, cả hai đều cố gắng tạo biếnMSTR
cùng một lúc.Vì
MSTR
không thể được tạo đồng thời trong hai Method không tuần tự hóa, dẫn đến xung đột.
Kiểm Soát Luồng trong ASL
ASL có các phương pháp để kiểm soát luồng tương tự như trong các ngôn ngữ lập trình khác
Bao gồm:
Switch
Case
Default
BreakPoint
While
Break
Continue
If
Else
ElseIf
Stall
Điều Khiển Rẽ Nhánh If & Switch
Giới thiệu về IF:
Mã sau kiểm tra xem hệ thống có phải là Darwin không, nếu đúng thì gán OSYS = 0x2710
.
Giới thiệu ElseIf và Else
Mã sau kiểm tra xem hệ thống có phải là Darwin không
Nếu không phải thì kiểm tra hệ thống có phải là Linux không
Nếu đúng thì gán OSYS = 0x03E8
Nếu không thì gán OSYS = 0x07D0
.
Giới thiệu về Switch, Case, Default, BreakPoint
Ví dụ sau sử dụng cấu trúc Switch
, Case
, Default
, và BreakPoint
để kiểm soát luồng.
Kiểm Soát Vòng Lặp
Giới thiệu về While & Stall
Ví dụ sau sử dụng While
và Stall
để kiểm soát vòng lặp.
Local0 = 10
: Khởi tạoLocal0
bằng 10.While (Local0 >= 0x00)
: Thực hiện vòng lặp khiLocal0
lớn hơn hoặc bằng 0.Local0--
: Giảm giá trị củaLocal0
đi 1.Stall (32)
: Tạm dừng 32 microsecond.
Cấu trúc for
trong ASL tương tự như trong C, Java.
Cấu trúc For
trên tương đương với While
dưới đây.
CondRefOf
hữu ích để kiểm tra xem đối tượng có tồn tại hay không.
Mã này được trích từ SSDT-I2CxConf.
Khi hệ thống không phải là MacOS, và XSCN
tồn tại dưới I2C0
, nó trả về giá trị gốc.
Trình tự tải SSDT
Thông thường, các bản vá SSDT được nhắm vào ACPI của máy
Có thể là DSDT hoặc các SSDT gốc máy khác
Vì ACPI gốc được tải trước các bản vá SSDT
Nên không cần phải tải các SSDT trong danh sách Add theo thứ tự cụ thể.
Tuy nhiên, có những ngoại lệ đối với quy tắc này.
Ví dụ, nếu bạn có hai SSDT-X và SSDT-Y
Trong đó SSDT-X định nghĩa một thiết bị mà SSDT-Y tham chiếu chéo thông qua Scope
Thì hai bản vá này phải được tải theo đúng thứ tự để toàn bộ bản vá hoạt động.
Nói chung, các SSDT được "scoped" vào phải được tải trước các SSDT khác.
Ví dụ
SSDT-X
SSDT-X: Định nghĩa một thiết bị XXXX
dưới Scope _SB.PCI0.LPCB
.
SSDT-Y
SSDT-Y: Tham chiếu thiết bị XXXX
được định nghĩa trong SSDT-X
và định nghĩa một Method YYYY
.
Trong ví dụ này SSDT-X phải được load trước SSDT-Y
Thiết lập trình tự tải cho SSDT
Đảm bảo các file SSDT của bạn đã được load vào config.plist
B1: Mở Config.plist bằng Propertree
Chú ý vào phần ACPI --> ADD
Như hình trên thì SSDT-PLUG-DRTNIA sẽ được load trước SSDT-EC-USBX-DESKTOP
B2: Nếu bạn muốn thay đổi thứ tự load của SSDT nào chỉ cần kéo "dòng" của SSDT đó lên trước
Như này thì SSDT-EC-USBX-DESKTOP đã load trước SSDT-PLUG-DRTNIA
Last updated