在霧裡欣賞 BIOS 裡的 ACPI Description Table
ACPI(Advanced Configuration & Power Interface) 是近年來廣泛被使用的電源和硬體狀態控制標準,在 PC 產業中已經被用來取代 APM(Advanced Power Management),並做為一個電源控制的首選規範。截至本文為止, ACPI 的最新版本號是 3.0a,詳細說明資料可參閱官方網站(http://www.acpi.info/)。
常聽一些人解釋說:『因為休眠、暫停功能在 WindowsXP 系統上沒問題,所以 BIOS 在 ACPI 的處理上應該正確無誤。要是在 Linux 等其他系統上出現問題,那一定是其它作業系統的錯。』
乍聽之下,這樣的解釋真的很合理,完美的證明了 BIOS 沒有問題,但實際上,這樣的說法就好像賣藥郎中說:『這藥強健筋骨、治百病,男人可壯陽,女人可改善體質,絕對百利而無一害,要是服用後病沒好,仍然一厥不振,且頭暈目炫、口吐白沫,那肯定是你身體有問題,虛不受補。藥,肯定是沒問題的。』
要窺探並破解 ACPI 的迷思,我們可以直接檢查存在於 BIOS 裡的 ACPI Source ,了解到底發生了什麼事,會讓 ACPI 功能在不同系統上有不同的表現,雖然會有如霧裡看花,但沒關係,反正世上最美的事物就是最矇矓的恐龍呀:P。如果是在 Linux 系統下,我們可以很輕易的將 ACPI 的 DSDT(Differentiated System Description Table) 給 Dump 下來:
『dsdt.aml』是一個 Binary 的 ACPI Machine Language 檔,我們可以使用 isal (Intel ACPI Source Language compiler/decompiler)將它 disassemble,若是在 Debian/Ubuntu 之下,可直接用 apt-get 安裝該工具:
然後使用 iasl 直接 disassemble dsdt.aml:
完成後會產生一個人類看得懂的『dsdt.dsl』,該檔是用 ACPI Source Language 寫成,我們可以用 vim 等文字編輯器開啟瀏覽。你可以搜尋關鍵字『Linux』,會發現有用來判斷作業系統的相關描述:
你也許會看到更多有關於 Microsoft Windows 的描述:
這意味著,ACPI 會依照不同作業系統而有不同的行為,可能有些功能在 Linux 下就會被關掉,亦或是因為會對記憶體位址做特殊存取,雖不會造成 Windows 任何影響,但卻可能剛好破壞掉 Linux Kernel 的運作,導致 boot/reboot 或 S3/S4 時會 hangs 或 crash。有時候缺少正確的 Return 值,也會造成 S3/S4 resume 的失敗。以下就是一個實際缺少 Return 導致 resume 不正常的例子,但我自行修正並補上正確的回傳值:
如此遺漏而產生出來的 ACPI bugs 有非常多,在此也不便一一列舉,日後有時間再慢慢彙出來。不過,既然我們已經得到 DSDT,意味著只要有心,都可在此改寫並修正一些 ACPI 的問題。雖然我們並不能將修正過後的 DSDT 寫回 BIOS,但 Linux Kernel 編譯時若有開啟『CONFIG_ACPI_CUSTOM_DSDT』,就可以使用自定的 DSDT 取代原本機器上的 table,只要將檔案放到 /boot 下即可(在 Debian Lenny/Sid 上的 Kernel 似乎沒有開啟這項功能)。
將改好的 dsdt.dsl 重新 compile:
此外若你是在 Ubuntu 下,請直接將編譯好的 dsdt.aml 放到 /etc/initramfs-tools 之下,再 update initrd:
然後重開機後,便可使用自訂的 DSDT 來控制 ACPI 的行為。
至於更多的 ACPI Spec 細節,可直接下載 spec 文件:
http://www.acpi.info/spec.htm
亦或是可參考 isal 工具的官方資料:
http://developer.intel.com/technology/iapc/acpi/
常聽一些人解釋說:『因為休眠、暫停功能在 WindowsXP 系統上沒問題,所以 BIOS 在 ACPI 的處理上應該正確無誤。要是在 Linux 等其他系統上出現問題,那一定是其它作業系統的錯。』
乍聽之下,這樣的解釋真的很合理,完美的證明了 BIOS 沒有問題,但實際上,這樣的說法就好像賣藥郎中說:『這藥強健筋骨、治百病,男人可壯陽,女人可改善體質,絕對百利而無一害,要是服用後病沒好,仍然一厥不振,且頭暈目炫、口吐白沫,那肯定是你身體有問題,虛不受補。藥,肯定是沒問題的。』
要窺探並破解 ACPI 的迷思,我們可以直接檢查存在於 BIOS 裡的 ACPI Source ,了解到底發生了什麼事,會讓 ACPI 功能在不同系統上有不同的表現,雖然會有如霧裡看花,但沒關係,反正世上最美的事物就是最矇矓的恐龍呀:P。如果是在 Linux 系統下,我們可以很輕易的將 ACPI 的 DSDT(Differentiated System Description Table) 給 Dump 下來:
sudo cat /sys/firmware/acpi/tables/DSDT > dsdt.aml
『dsdt.aml』是一個 Binary 的 ACPI Machine Language 檔,我們可以使用 isal (Intel ACPI Source Language compiler/decompiler)將它 disassemble,若是在 Debian/Ubuntu 之下,可直接用 apt-get 安裝該工具:
apt-get install iasl
然後使用 iasl 直接 disassemble dsdt.aml:
iasl -d dsdt.aml
完成後會產生一個人類看得懂的『dsdt.dsl』,該檔是用 ACPI Source Language 寫成,我們可以用 vim 等文字編輯器開啟瀏覽。你可以搜尋關鍵字『Linux』,會發現有用來判斷作業系統的相關描述:
If (MCTH (_OS, "Linux"))
{
Store (0x03, OSVR)
}
你也許會看到更多有關於 Microsoft Windows 的描述:
If (_OSI ("Windows 2001.1 SP1"))
{
Store (Zero, OSVR)
}
這意味著,ACPI 會依照不同作業系統而有不同的行為,可能有些功能在 Linux 下就會被關掉,亦或是因為會對記憶體位址做特殊存取,雖不會造成 Windows 任何影響,但卻可能剛好破壞掉 Linux Kernel 的運作,導致 boot/reboot 或 S3/S4 時會 hangs 或 crash。有時候缺少正確的 Return 值,也會造成 S3/S4 resume 的失敗。以下就是一個實際缺少 Return 導致 resume 不正常的例子,但我自行修正並補上正確的回傳值:
Method (_WAK, 1, NotSerialized)
{
...
...
Return(Package(0x02){0x00, 0x00})
}
如此遺漏而產生出來的 ACPI bugs 有非常多,在此也不便一一列舉,日後有時間再慢慢彙出來。不過,既然我們已經得到 DSDT,意味著只要有心,都可在此改寫並修正一些 ACPI 的問題。雖然我們並不能將修正過後的 DSDT 寫回 BIOS,但 Linux Kernel 編譯時若有開啟『CONFIG_ACPI_CUSTOM_DSDT』,就可以使用自定的 DSDT 取代原本機器上的 table,只要將檔案放到 /boot 下即可(在 Debian Lenny/Sid 上的 Kernel 似乎沒有開啟這項功能)。
將改好的 dsdt.dsl 重新 compile:
iasl -tc dsdt.dsl
此外若你是在 Ubuntu 下,請直接將編譯好的 dsdt.aml 放到 /etc/initramfs-tools 之下,再 update initrd:
sudo update-initramfs -c -k all
然後重開機後,便可使用自訂的 DSDT 來控制 ACPI 的行為。
至於更多的 ACPI Spec 細節,可直接下載 spec 文件:
http://www.acpi.info/spec.htm
亦或是可參考 isal 工具的官方資料:
http://developer.intel.com/technology/iapc/acpi/
賞個神奇的 disassembled codesnip 瞧瞧? :-)
回覆刪除這不就是之前著名的foxconn BIOS不支援Linux事件?
回覆刪除http://linux.slashdot.org/article.pl?sid=08/07/25/1150218&tid=190
嗨,你好:
回覆刪除請教一個問題,
我是用 Debian lenny,我自已編了一個kernel (用 defconfig產生的 .config檔)
我看了些文章
「CONFIG_STANDLONE=n」
「CONFIG_ACPI_CUSTOM_DSDT=y」、「CONFIG_ACPI_CUSTOM_DSDT_FILE="DSDT.hex"」
說是只要將上面的設定好後就能用自訂的DSDT表取代BIOS裡的DSDT,可是實際上並沒有,
請問你有用debin成功的取代過嗎?
( DSDT table dump的方法是跟你一樣的,Kernel: 2.6.32.2)
謝謝
2.6.32.2 didn't support DSDT override, you need to use early kernel version 2.6.28.xx
回覆刪除