跳至內容
出自 Arch Linux 中文维基

DSDT(Differentiated System Description Table)是ACPI規格的一部分。它提供了關於一個給定系統中受支持的電源事件的信息。ACPI表是由製造商在固件裡提供的。通常,Linux遇到的問題是某些ACPI功能的缺失,比如風扇不轉,蓋子合上時屏幕不熄滅等等。這些問題可以歸咎於DSDT是為Windows所定製的。安裝後可以打補丁來修復這些問題。這篇文章的目標是分析並且重建一個有錯誤的DSDT,這樣內核就會略過默認的DSDT。

基本上來說,一個DSDT表是運行在ACPI(電源管理)上的代碼。

注意:Linux ACPI[失效連結 2023-09-16 ⓘ]項目的目標讓Linux工作在未修改的固件上。如果你還是覺得這種方法在現代內核上是必要的,那麼你應該考慮一下報告bug

在你開始之前

硬體製造商可能已經發布了更新的固件,修復了與ACPI相關的問題。安裝更新的固件通常比這種方法更可取,因為它可以避免重複勞動。

此過程會修改你安裝中的一些相當基礎的代碼。你需要絕對確定所做的更改。你可能還希望在之前克隆你的磁碟

即使在嘗試自己修復DSDT之前,你也可以嘗試以下幾種捷徑:

讓內核報告某個Windows版本

使用變量acpi_os_name作為內核參數。例如:

acpi_os_name="Microsoft Windows NT"

要添加一個被識別的作業系統接口,使用變量acpi_osi

acpi_osi="Linux"

要僅使用一個作業系統接口,添加acpi_osi=!。這會告訴固件只有一個支持的作業系統,因此這通常是推薦的解決方案。

acpi_osi=! acpi_osi="Windows 2022"

要移除一個接口,在字符串的開頭使用感嘆號。

acpi_osi="!Windows 2012"

其他可以測試的字符串:

  • "Microsoft Windows XP"
  • "Microsoft Windows 2000"
  • "Microsoft Windows 2000.1"
  • "Microsoft Windows ME: Millennium Edition"
  • "Microsoft WindowsME: Millennium Edition"
  • "Windows 2001"
  • "Windows 2006"
  • "Windows 2009"
  • "Windows 2012"
  • "Windows 2015"
  • "Windows 2020"
提示:雖然不推薦,但你可以嘗試"Linux"。

出於好奇,你可以按照以下步驟提取你的DSDT並搜索.dsl文件。只需grep "Windows"並查看結果。

找到一個修復過的DSDT

DSDT文件最初是用ACPI源語言(一個.asl/.dsl文件)編寫的。使用編譯器可以生成一個'ACPI機器語言'文件(.aml)或一個十六進制表(.hex)。要將該文件整合到你的Arch安裝中,你需要獲取一個編譯好的.aml文件——無論是自己編譯還是信任網際網路上的某個陌生人,這取決於你。如果你從網際網路上下載文件,它很可能是一個壓縮的.asl文件。因此,你需要解壓並編譯它。這樣做的好處是你不必自己研究特定的代碼修復。

像你一樣使用同一種筆記本的Arch用戶是少數中的少數中的少數。嘗試瀏覽其他發行版/Linux論壇,尋找關於相同型號的討論。很可能他們有相同的問題,並且因為有很多人,或者因為他們技術嫻熟——有人已經製作了一個可用的DSDT,甚至可能提供預編譯版本(再次提醒,使用時需自行承擔風險)。 搜尋引擎是你的最佳工具。嘗試保持簡短:'型號名稱' + 'dsdt' 可能會產生結果。

自己重新編譯

在這個過程中你的最好的資源將會是ACPI Spec homepage,和Linux ACPI Project[失效連結 2023-09-16 ⓘ],這些將會替代acpi.sourceforge.net。 簡而言之,你可以使用英特爾的ASL編譯器將你系統的DSDT錶轉換成原始碼,定位並修復錯誤,最後重新編譯。

你將會需要安裝acpica來修改代碼。 什麼編譯器編譯了原始代碼? 查看你系統的DSDT使用英特爾的還是微軟的編譯器編譯的:

 # dmesg|grep DSDT 
ACPI: DSDT 00000000bf7e5000 0A35F (v02 Intel  CALPELLA 06040000 INTL 20060912)
ACPI: EC: Look up EC in DSDT

如果是微軟的編譯器,INTL將會變成MSFT。 在這個例子中,在反編譯/編譯的DSDT時共有五個錯誤,有兩個在谷歌一下和研究ACPI規範之後很容易解決。另外三個是由於使用了不同版本的編譯器。後來你會發現,它們三個會在啟動時被ACPICA處理。內核的ACPICA部分可以處理編譯DSDT時產生的的大部分不重要的錯誤.所以如果你的系統正在按照正常方式運行,請不要為了編譯錯誤煩惱。

1.) 提取ACPI表(作為超級用戶): # cat /sys/firmware/acpi/tables/DSDT > dsdt.dat

2.) 反編譯: iasl -d dsdt.dat

3.) 重新編譯: iasl -tc dsdt.dsl

4.) 檢查錯誤並修復。例如:

dsdt.dsl   6727:                         Name (_PLD, Buffer (0x10)  
Error    4105 -          Invalid object type for reserved name ^  (found BUFFER, requires Package) 
 nano +6727 dsdt.dsl
(_PLD, Package(1) {Buffer (0x10)...

5.) 增添OEM版本,否則內核不會應用修改過的ACPI表。例如在修改前:

DefinitionBlock ("DSDT.aml", "DSDT", 2, "INTEL ", "TEMPLATE", 0x00000000)

修改後:

DefinitionBlock ("DSDT.aml", "DSDT", 2, "INTEL ", "TEMPLATE", 0x00000001)

6.) 編譯被修復過的代碼: iasl -tc dsdt.dsl (你可能需要使用命令行參數-ic來將C語言頭文件插入內核)

如果沒有錯誤,你應該可以接著往下進行了。

使用修改過的代碼

警告:每次BIOS更新後你都要重新修復DSDT表並且重複這些步驟!

至少有兩種方式來使用定製的DSDT:

  • 創建一個由內核在啟動早期加載的未壓縮CPIO文檔
  • 把它編譯進內核

使用mkinitcpio的acpi_override鉤子

mkinitcpio提供了一個acpi_override鉤子,它會將/usr/initcpio/acpi_override//etc/initcpio/acpi_override/中找到的所有.aml文件放入/kernel/firmware/acpi/中的一個早期未壓縮CPIO文檔中。這避免了手動創建單獨的CPIO文檔或更改引導加載程序配置的需要,因為mkinitcpio將未壓縮的CPIO文檔與主initramfs映像打包到一個文件中。

首先,創建/etc/initcpio/acpi_override目錄並將所有需要的.aml文件複製到其中。例如:

# mkdir /etc/initcpio/acpi_override
# cp dsdt.aml /etc/initcpio/acpi_override/

acpi_override添加到/etc/mkinitcpio.conf中的HOOKS數組:

/etc/mkinitcpio.conf
HOOKS=(... acpi_override)

最後,重新生成initramfs並重啟。

使用一個CPIO文檔

這個方法有一個優點就是你不用重新編譯你的內核,升級內核後你也無需重複這些步驟

這個方法要求內核參數ACPI_TABLE_UPGRADE=y被啟用(對於linux被設置為true)。瀏覽[1] 來查看更多詳細內容。

首先,創建下面的目錄結構:

$ mkdir -p kernel/firmware/acpi

將修復過的ACPI表拷貝進剛剛創建的kernel/firmware/acpi文件夾,例如:

$ cp dsdt.aml ssdt1.aml kernel/firmware/acpi

在新創建的kernel/目錄所在的目錄下,運行:

$ find kernel | cpio -H newc --create > acpi_override

這條命令會創建含有修復了的ACPI表的CPIO文檔。將文檔拷貝到boot目錄。

# cp acpi_override /boot

最後,配置bootloader來加載你的CPIO文檔。例如,使用Systemd-boot的話, /boot/loader/entries/arch.conf可能看起來像這樣:

title	 Arch Linux
linux	 /vmlinuz-linux
initrd   /acpi_override
initrd	 /initramfs-linux.img
options  root=PARTUUID=ec9d5998-a9db-4bd8-8ea0-35a45df04701 resume=PARTUUID=58d0aa86-d39b-4fe1-81cf-45e7add275a0 ...

現在,剩下需要做的事情就是重啟電腦並且確認結果

編譯進內核

你會想要熟悉編譯自己的內核。最直接的方式就是用「傳統」方法。 編譯DSDT之後,iasl產生兩個文件:dsdt.hexdsdt.aml

使用 menuconfig:

  • 禁用"Select only drivers that don't need compile-time external firmware"。位於"Device Drivers -> Generic Driver Options"。
  • 啟用"Include Custom DSDT"並且指明你修復過的DSDT文件的絕對路徑(dsdt.hex,而不是dsdt.aml)。位於"Power management and ACPI options -> ACPI (Advanced Configuration and Power Interface) Support"。

在GRUB中使用AML

如果你使用GRUB,你可以使用一個更簡單的方法。將上面創建的.aml文件複製到你的引導分區:

# cp dsdt_patch.aml /boot

然後在你的GRUB配置中添加以下行:

acpi /dsdt_patch.aml

你可以將其添加到/etc/grub.d/40_custom中,別忘了之後生成你的GRUB配置。

在dracut中使用AML

如果你使用Dracut,你可以簡單地將上面創建的.aml文件複製到一個定義的位置。必須創建一個相應的配置文件/etc/dracut.conf.d/acpi-fix.conf

acpi_override="yes"
acpi_table_dir="/usr/local/lib/firmware/acpi"

確認覆蓋成功

查找確認覆蓋的消息,例如:

# dmesg | grep ACPI
[    0.000000] ACPI: Override [DSDT-   A M I], this is unsafe: tainting kernel
[    0.000000] ACPI: DSDT 00000000be9b1190 Logical table override, new table: ffffffff81865af0
[    0.000000] ACPI: DSDT ffffffff81865af0 0BBA3 (v02 ALASKA    A M I 000000F3 INTL 20130517)

參見