SSDT 疊加層¶
為了支援 ACPI 開放式硬體配置(例如開發板),我們需要一種方法來增強韌體映像提供的 ACPI 配置。一個常見的例子是在開發板的 I2C/SPI 總線上連線感測器。
雖然這可以透過建立核心平臺驅動程式或重新編譯帶有更新 ACPI 表的韌體映像來完成,但這兩種方法都不實用:前者會使特定於板的核心程式碼蔓延,而後者需要訪問韌體工具,這些工具通常不公開提供。
由於 ACPI 支援 AML 程式碼中的外部引用,一種更實用的方法是動態載入包含特定於板資訊的自定義 SSDT 表來增強韌體 ACPI 配置。
例如,要在 Minnowboard MAX 開發板的 I2C 總線上透過 LSE 聯結器 [1] 列舉 Bosch BMA222E 加速度計,可以使用以下 ASL 程式碼:
DefinitionBlock ("minnowmax.aml", "SSDT", 1, "Vendor", "Accel", 0x00000003)
{
External (\_SB.I2C6, DeviceObj)
Scope (\_SB.I2C6)
{
Device (STAC)
{
Name (_HID, "BMA222E")
Name (RBUF, ResourceTemplate ()
{
I2cSerialBus (0x0018, ControllerInitiated, 0x00061A80,
AddressingMode7Bit, "\\_SB.I2C6", 0x00,
ResourceConsumer, ,)
GpioInt (Edge, ActiveHigh, Exclusive, PullDown, 0x0000,
"\\_SB.GPO2", 0x00, ResourceConsumer, , )
{ // Pin list
0
}
})
Method (_CRS, 0, Serialized)
{
Return (RBUF)
}
}
}
}
然後可以將其編譯為 AML 二進位制格式
$ iasl minnowmax.asl
Intel ACPI Component Architecture
ASL Optimizing Compiler version 20140214-64 [Mar 29 2014]
Copyright (c) 2000 - 2014 Intel Corporation
ASL Input: minnomax.asl - 30 lines, 614 bytes, 7 keywords
AML Output: minnowmax.aml - 165 bytes, 6 named objects, 1 executable opcodes
[1] https://www.elinux.org/Minnowboard:MinnowMax#Low_Speed_Expansion_.28Top.29
生成的 AML 程式碼可以透過以下方法之一由核心載入。
從 initrd 載入 ACPI SSDT¶
此選項允許從 initrd 載入使用者定義的 SSDT,當系統不支援 EFI 或 EFI 儲存空間不足時,此選項很有用。
它的工作方式與基於 initrd 的 ACPI 表覆蓋/升級類似:SSDT AML 程式碼必須放置在第一個未壓縮的 initrd 中,路徑為“kernel/firmware/acpi”。可以使用多個檔案,這將轉化為載入多個表。只允許 SSDT 和 OEM 表。有關更多詳細資訊,請參閱透過 initrd 升級 ACPI 表。
這是一個例子
# Add the raw ACPI tables to an uncompressed cpio archive.
# They must be put into a /kernel/firmware/acpi directory inside the
# cpio archive.
# The uncompressed cpio archive must be the first.
# Other, typically compressed cpio archives, must be
# concatenated on top of the uncompressed one.
mkdir -p kernel/firmware/acpi
cp ssdt.aml kernel/firmware/acpi
# Create the uncompressed cpio archive and concatenate the original initrd
# on top:
find kernel | cpio -H newc --create > /boot/instrumented_initrd
cat /boot/initrd >>/boot/instrumented_initrd
從 EFI 變數載入 ACPI SSDT¶
當平臺支援 EFI 時,這是首選方法,因為它允許以持久、獨立於作業系統的方式儲存使用者定義的 SSDT。目前正在開展工作以實現 EFI 對載入使用者定義 SSDT 的支援,使用此方法將使其更容易在 EFI 載入機制到來時進行轉換。要啟用它,應將 CONFIG_EFI_CUSTOM_SSDT_OVERLAYS 選擇為 y。
為了從 EFI 變數載入 SSDT,可以使用 "efivar_ssdt=..." 核心命令列引數(名稱限制為 16 個字元)。該選項的引數是要使用的變數名。如果有多個同名但供應商 GUID 不同的變數,它們都將被載入。
為了將 AML 程式碼儲存在 EFI 變數中,可以使用 efivarfs 檔案系統。在所有最新發行版中,它預設在 /sys/firmware/efi/efivars 中啟用並掛載。
在 /sys/firmware/efi/efivars 中建立一個新檔案將自動建立一個新的 EFI 變數。更新 /sys/firmware/efi/efivars 中的檔案將更新 EFI 變數。請注意,檔名需要特殊格式為“Name-GUID”,並且檔案中的前 4 個位元組(小端格式)表示 EFI 變數的屬性(請參閱 include/linux/efi.h 中的 EFI_VARIABLE_MASK)。寫入檔案也必須透過一次寫入操作完成。
例如,您可以使用以下 bash 指令碼根據給定檔案的內容建立/更新 EFI 變數:
#!/bin/sh -e
while [ -n "$1" ]; do
case "$1" in
"-f") filename="$2"; shift;;
"-g") guid="$2"; shift;;
*) name="$1";;
esac
shift
done
usage()
{
echo "Syntax: ${0##*/} -f filename [ -g guid ] name"
exit 1
}
[ -n "$name" -a -f "$filename" ] || usage
EFIVARFS="/sys/firmware/efi/efivars"
[ -d "$EFIVARFS" ] || exit 2
if stat -tf $EFIVARFS | grep -q -v de5e81e4; then
mount -t efivarfs none $EFIVARFS
fi
# try to pick up an existing GUID
[ -n "$guid" ] || guid=$(find "$EFIVARFS" -name "$name-*" | head -n1 | cut -f2- -d-)
# use a randomly generated GUID
[ -n "$guid" ] || guid="$(cat /proc/sys/kernel/random/uuid)"
# efivarfs expects all of the data in one write
tmp=$(mktemp)
/bin/echo -ne "\007\000\000\000" | cat - $filename > $tmp
dd if=$tmp of="$EFIVARFS/$name-$guid" bs=$(stat -c %s $tmp)
rm $tmp
從 configfs 載入 ACPI SSDT¶
此選項允許透過 configfs 介面從使用者空間載入使用者定義的 SSDT。必須選擇 CONFIG_ACPI_CONFIGFS 選項並且必須掛載 configfs。在以下示例中,我們假設 configfs 已掛載到 /sys/kernel/config 中。
可以透過在 /sys/kernel/config/acpi/table 中建立新目錄並將 SSDT AML 程式碼寫入 aml 屬性來載入新表:
cd /sys/kernel/config/acpi/table
mkdir my_ssdt
cat ~/ssdt.aml > my_ssdt/aml