http://www.columbia.edu/~ariel/acpi/acpi_howto.txt
ACPI – the Advanced Configuration & Power Interface. ACPI是OS,BIOS和硬體之間的抽象層。它允許OS和平台獨立的發展,比如新的OS可以控制老的平台,老的OS也可以控制新的平台而不需要額外的修改。ACPI的基本結構圖如下:
電腦領域的一個基本方法是增加一個抽象層,從而使得抽象層的上下兩層獨立的發展,ACPI事實上也是借鑒類似的思想。ACPI的抽象主要是通過ACPI表中提供的資訊來實現,這些資訊包括ACPI寄存器、AML代碼、配置資訊等。
- ACPI寄存器,描述了和ACPI相關的寄存器。OS可以直接從ACPI表中取得這些寄存器的資訊,因此不必瞭解具體的硬體設定。
- AML代碼,一種類C的代碼。由BIOS提供,OS的AML解譯器(在Linux中叫ACPI CA)解釋執行這些代碼。這是ACPI抽象層的關鍵,下面會詳細介紹。
- 配置資訊,ACPI包含的配置資訊很多,比如多處理器配置資訊(MADT)、NUMA配置資訊(SRAT、SLIT)、高精度定時器(HPET)等。
AML代碼是抽象的關鍵。為了消除平台相關性,BIOS把平台相關的操作用AML代碼來實現。OS不需要知道平台細節,它只是解釋執行這些代碼,在解釋執行的過程中平台相關的操作就被執行了。ACPI規範定義了一些標準的AML函數,OS解釋執行一個這樣的標準函數就可以實現特定的功能。舉個例子:
Scope (_SB.PCI0.LPC) { OperationRegion (LPCS, PCI_Config, 0x00, 0x0100) Field (LPCS, AnyAcc, NoLock, Preserve) { Offset (0x60), PIRA, 8, } } Device (LNKA) { Method (_DIS, 0, NotSerialized) { Or (\_SB.PCI0.LPC.PIRA, 0x80, \_SB.PCI0.LPC.PIRA) } }
比如我們想禁止LNKA裝置,ACPI規範定義了OS必須執行此裝置的_DIS函數。從硬體的角度來講,禁止LNKA裝置需要將某個PCI裝置的配置空間的寄存器0×60的最高位置上。OS不需要知道硬體的細節,它解釋執行_DIS函數即可。上面的代碼就是AML代碼,由BIOS提供,語句‘Or (\_SB.PCI0.LPC.PIRA, 0×80, \_SB.PCI0.LPC.PIRA)’實際上就是給寄存器\_SB.PCI0.LPC.PIRA置上最高位, 而PIRA就是PCI裝置LPC的配置空間的寄存器0×60。從此段代碼我們可以明顯的看出BIOS以AML代碼的形式隱藏(抽象)了硬體的細節,從而使得OS看到的是一個平台無關的硬體。
更多的ACPI預定義的函數可以在ACPI規範中找到,可以在http://www.acpi.info下載最新的規範。
Linux/ACPI實現中使用的AML解譯器是ACPICA -the ACPI Component Architecture. 可以從http://www.intel.com/technology/iapc/acpi/downloads.htm得到。它包含一個AML解譯器(Linux kernel包含了這個解譯器,很多其他OS也是如此,比如BSD、opensolaris等),一個編譯器(將ACPI Source Language (ASL)編譯成AML代碼)和一些測試載入器。
最新的Linux/ACPI的代碼可以使用git在http://www.kernel.org/git/?p=linux/kernel/git/lenb/linux-acpi-2.6.git得到。
為什麼學習ACPI
ACPI是Intel(i386,x86_64,IA64)平台的標準韌體規範,絕大部分OS需要從BIOS得到的資訊都可以從ACPI得到,並且現在的趨勢是未來的任何新的特性相關的資訊都只能從ACPI得到。ACPI的內容龐雜,學習ACPI至少可以協助我們理解:
- 配置資訊。這些資訊從legacy PNP裝置的配置,到多處理器,到NUMA,比如現在的Multiple Core的資訊就只能從ACPI得到。Linux啟動很多代碼就是處理這些配置資訊,比如APIC,IOAPIC設定等。
- ACPI相關裝置。主要是膝上型電腦相關的裝置,包括電源開關,電池,外接電源,風扇,熱鍵等。
- 底層硬體。比如PCI中斷路由,chipset(主要是南橋PCI-to-LPC bridge)操作等。
- 電源管理。ACPI定義的電源管理包括CPU的電源管理(調頻率P-state,idle C-state,throtting T-state),裝置電源管理(D-state),系統電源管理(Suspend-to-Ram, Suspend-to-Disk, power off)等。
- 裝置熱插拔。ACPI用一種統一的方式來描述裝置的熱插拔,這樣的裝置從單一的PCI裝置,到膝上型電腦的Docking Station,到整個PCI hierarchy,CPU,Memory,甚至整個NUMA節點。
可以說要理解現代PC平台必須瞭解ACPI。
解決ACPI問題的小竅門
首先可以看看是否這是一個regression,如果以前版本的Linux kernel可以工作,但新的不行,則是一個regression,可以測試不同的kernel從而找出哪個版本引入了bug。使用git-bisect是一個好的選擇,它可以幫你定位到哪個patch導致了regression。一些git-bisect相關的資料如下: http://www.stardust.webpages.pl/files/handbook/handbook-en.pdf http://www.kernel.org/pub/software/scm/git/docs/tutorial.html http://www.kernel.org/pub/software/scm/git/docs/git-bisect.html
系統不能啟動
試試kernel參數”acpi=off”,如果此參數沒有帶來任何改變,那麼這不是一個ACPI bug。反之,則這很可能是一個ACPI bug。 確定了是一個ACPI bug後,我們還有其他一些參數來更進一步的區分到底是ACPI哪個部分的bug。
acpi=ht
這個參數和"acpi=off"幾乎一樣,它禁止了除多處理器配置相關的內容以外的所有ACPI功能。如果acpi=off正常,但acpi=ht 不正常, 則解析ACPI 表或者Linux SMP的代碼有bug.
pci=noacpi
禁止使用ACPI來處理任何PCI相關的內容,包括PCI root bus的枚舉和PCI裝置中斷路由。
acpi=noirq
禁止使用ACPI來處理PCI裝置中斷路由,和pci=noacpi的區別是它允許使用ACPI來枚舉PCI root bus.
pnpacpi=off
禁止使用ACPI來枚舉PNP裝置,比如串口、PS2鍵盤滑鼠等。
noapic
禁止使用io-apic來做裝置中斷路由,這樣做的效果之一是ACPI返回的中斷路由表將是針對PIC(8259)的。
nolapic
禁止使用Local-APIC和IO-APIC。
裝置中斷相關的問題
出現中斷問題的可能性很多,比如驅動程式有bug。由ACPI導致的最常見的中斷問題是kernel打出:”irqXX: nobody cared!”。這意味著kernel收到一個中斷,但是沒有驅動程式來處理此中斷。Kernel會將此中斷禁止,從而導致掛在此中斷上的所有裝置都停止工作。pci=noacpi, acpi=noirq, pnpacpi=off, noapic, nolapic這些參數可以協助隔離一些問題。另外一個有用的參數是”irqpoll”,出現上面的中斷問題時,它可以使kernel自動探測哪個裝置發出了中斷。這個參數對於調試那些中斷路由有問題的系統很有用。
Suspend to RAM問題
STR的一個常見問題是Resume回來後黑屏,但是系統並沒有死掉,比如可以通過網路訪問系統或者鍵盤燈工作正常。可以試試kernel參數acpi_sleep=s3_bios/s3_mode,它會嘗試將顯示器開啟。如果不行可以試試vbetools(http://www.srcf.ucam.org/~mjg59/vbetool),在resume回來後輸入 $ vbetool post 為了方便,你可以在你的STR指令碼中調用此命令。
STR的另一個常見問題是系統沒法resume回來,你可以試試acpi_sleep=s3_beep。如果你聽不到電腦的擴音器產生的聲音,那麼resume的代碼完全沒被執行。這可能是BIOS的原因,也可能是Linux的原因,目前還沒有太好的辦法處理。反之,很有可能是Linux driver的原因,你可以嘗試儘可能少的載入驅動程式,只保留最基本的驅動,比如硬碟驅動。
Suspend to Disk 問題
TBD
ACPI debug參數
參數是acpi.debug_level and acpi.debug_layer。如果開啟debug參數,ACPI可以產生很多詳細的運行輸出。這些輸出可以協助我們定位出錯的原因。
對於debug_layer和debug_level,include/acpi/acoutput.h裡麵包含了很多值,這些值決定了Linux/ACPI輸出資訊的詳細程度和內容約制。acpi.debug_level和 acpi.debug_layer是kernel參數,也可以在系統運行時改變這些值,它們是/sys/module/acpi/parameters/debug_{level,layer}。
注意,這些輸出資訊可能很快就將kernel的ring buffer用完,你可能需要使用log_buf_len=XY來增加ring buffer的大小。使用serial console (Documentation/serial-console.txt)來得到kernel輸出是一個好的方法。如果你的膝上型電腦沒有串口,可以試試netconsole (Documentation/networking/netconsole.txt)。
使用定製的 DSDT
DSDT (Differentiated System Description Table)是一個主要的ACPI表,它包含了很多AML代碼。因為BIOS的bug,這些代碼本身可能有錯。Linux提供的一種方法能讓你使用定製的DSDT表,這對於調試很有協助。讓kernel使用定製的DSDT步驟如下:
首先要得到原始的DSDT表(後面的章節會介紹acpidump等工具): $ acpidump > acpidump.out $ acpixtract DSDT acpidump > DSDT.dat 這樣我們就得到了DSDT表的二進位檔案,將它反組譯碼 $ iasl -d DSDT.dat 我們會得到一個AML代碼檔案,你可以修改它 $ vi DSDT.dsl 然後重新編譯 $ iasl -tc DSDT.dsl 把它拷貝到kernel source中 $ cp DSDT.hex $SRC/include/
加入下面幾行到你的kernel設定檔(.config):
CONFIG_STANDALONE=n CONFIG_ACPI_CUSTOM_DSDT=y CONFIG_ACPI_CUSTOM_DSDT_FILE=”DSDT.hex”
編譯kernel,運行,你的dmesg中應該有如下輸出: Table [DSDT] replaced by host OS
使用這種方法,你可以修正DSDT的bug。這種方法帶來的一個有用的debug方法是:將ACPI的debug選項開啟,然後在你的DSDT中加入類似如下的語句: Store(”hello world!”, Debug) Store(Local0, Debug) 即將某個變數儲存到特殊的目標Debug中。加入了這樣語句後的函數被kernel解釋執行時你可以看到如下輸出: [ACPI Debug] String: [0x0C] “hello world!” [ACPI Debug] Integer: 0×00000042 由此我們可以在AML代碼層級進行調試。
報告ACPI bug
Linux/ACPI社區使用kernel bugzilla來跟蹤bug 。http://bugzilla.kernel.org/enter_bug.cgi?product=ACPI。這個網站主要是跟蹤base kernel的bug,如果你有特定發行版的bug,不要發到這個網站。Linux/ACPI有自己的郵件清單(linux-acpi@vger.kernel.org),你也可以在那裡討論問題。另外,Intel的Linux/ACPI組也有一個郵件地址(acpi@linux.intel.com),如果你的問題不方便公開,可以發到這兒。
如果你報告一個bug,請提交如下資訊:
- 產生bug的kernel版本
- 以前的kernel有沒有這樣的bug。如果這是一個regression,最近可以工作的kernel版本是什麼。如果你能使用git-bisect找到哪個patch帶來regression,那問題基本上就等於解決了。
- 出錯的kernel和最近工作kernel的dmesg資訊。你可能需要使用serial console來得到這些資訊。
- 如果這是中斷相關的問題,可能的話請提供kernel出錯和工作的時候/proc/interrupts的輸出。/sbin/lspci –vvv和/sbin/lspci -xxx的輸出也很有用。
- 請提供acpidump的輸出。Acpidump是一個工具,它可以將系統中的ACPI表打出來。你可以在http://ftp.kernel.org/pub/linux/kernel/people/lenb/acpi/utils/找到這個工具。注意acpidump輸出的是BIOS的表,不同的BIOS版本可能會有不同的表。
- 如果我們發現BIOS有問題,我們可以將此系統列入黑名單,在這種情況下需要提供dmidecode(通常在/usr/sbin/下)工具的輸出。
- 產生bug的kernel設定檔
如何使用ACPI工具
http://ftp.kernel.org/pub/linux/kernel/people/lenb/acpi/utils/,按照包裡面的README編譯。使用步驟如下:匯出所有的表,這些表都是二進位的 $ acpidump > acpidump.out
上面的輸出包含了很多個ACPI表,如果你希望將它們分離開,使用 $ acpixtract -a acpidump.out
反組譯碼某個表 $ iasl -d TABLE.dat 這樣就得到類C的AML代碼。
參考文檔
ACPI in Linux – Myths vs. Reality(OLS 2007) paper: https://ols2006.108.redhat.com/2007/Reprints/brown_1-Reprint.pdf presentation: http://ftp.kernel.org/pub/linux/kernel/people/lenb/acpi/doc/OLS2007-acpi-myths-web/
ACPI in Linux – Architecture, Advances, and Challenges(OLS 2005) paper: http://www.linuxsymposium.org/2005/linuxsymposium_procv1.pdf presentation: http://ftp.kernel.org/pub/linux/kernel/people/lenb/acpi/doc/ACPI_OLS_2005.pdf
The State of ACPI in the Linux Kernel(OLS 2004) http://ftp.kernel.org/pub/linux/kernel/people/lenb/acpi/doc/Reprint-Brown-OLS2004.pdf
TODO列表
- Suspend/resume的穩定性。Suspend-to-ram在很多膝上型電腦上不能工作。很多驅動程式沒有實現.suspend/.resume方法或者實現有問題。
- Hotkey的支援。很多膝上型電腦廠商使用完全不同的方法來支援hotkey,現在Linux支援IBM,Asus,Toshiba等。但是還有很多廠商的不支援,即使支援的廠商也有很多筆記本型號不支援。
- 運行時裝置電源管理。Linux還缺乏一個架構在系統運行時對裝置進行電源管理,例如在某個裝置空閑時將它關閉而不影響整個系統的運行。
- Device model方面的改進。Linux仍然缺乏一個好的機制將ACPI裝置和它對應的物理裝置統一起來處理。
- Bugzilla上有很多ACPI的bug
轉自:http://wiki.zh-kernel.org/project/linux-acpi
閱讀全文
類別:預設分類 查看評論