DSDT(Differentiated System Description Table)是ACPI規格的一部分。它提供了關於一個給定系統中受支持的電源事件的信息。ACPI表是由製造商在固件裡提供的。通常,Linux遇到的問題是某些ACPI功能的缺失,比如風扇不轉,蓋子合上時屏幕不熄滅等等。這些問題可以歸咎於DSDT是為Windows所定製的。安裝後可以打補丁來修復這些問題。這篇文章的目標是分析並且重建一個有錯誤的DSDT,這樣內核就會略過默認的DSDT。
基本上來說,一個DSDT表是運行在ACPI(電源管理)上的代碼。
在你開始之前
硬體製造商可能已經發布了更新的固件,修復了與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"
出於好奇,你可以按照以下步驟提取你的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語言頭文件插入內核)
如果沒有錯誤,你應該可以接著往下進行了。
使用修改過的代碼
至少有兩種方式來使用定製的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.hex
和dsdt.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)