做為一位 Ubuntu on x86 的軟體工程師,一定都會碰到 ACPI 相關問題(是嗎?),這次就來談談 ACPI 的除錯方法。本文的測試環境為 Ubuntu 12.04 kernel 3.5.0-22。
匯出 ACPI tables
除錯的第一步,可以先看看 ACPI tables 有沒有正確被寫進 100000h 以下的位置,方法百百種,推薦用
$ sudo fwts acpidump - $ # acpidump 是另一個工具,如果需要對照 hex 輸出可選用它 $ # sudo acpidump > acpidump.dat
匯出的結果大概如這般
Dump ACPI tables. ---------------------------------------------------------------------------------------------------- Test 1 of 1: Dump ACPI tables. RSDP @ f0490 (36 bytes) --------------- [0x000 0000 8] Signature: RSD PTR [0x008 0008 1] Checksum: 0x51 [0x009 0009 6] OEM ID: DELL [0x00f 0015 1] Revision: 0x02 [0x010 0016 4] RsdtAddress: 0xd6ff3028 [0x014 0020 4] Length: 0x00000024 [0x018 0024 8] XsdtAddress: 0x00000000d6ff3080 [0x020 0032 1] Extended Checksum: 0x57 [0x021 0033 3] Reserved: 0x00 [0] [0x021 0033 3] Reserved: 0x00 [1] [0x021 0033 3] Reserved: 0x00 [2]
通常比較有參考價值的是 FACP,像是 SMI command port, ACPI enable/disable value, PM block and GPE block 等等。相關資料也可在 ACPI spec 4.8 ACPI Register Model 裡找到。
另外,DSDT and SSDT(s) 這 2 類 table 由於放了 AML 紀錄,像是熱插拔,Q, L, E event 的實作,也常會需要拿來除錯,並且多半有問題的都是 DSDT。這些 tables 可以用下面方法匯出:
$ sudo cp /sys/firmware/acpi/tables/DSDT ./ && sudo chown doro DSDT $ sudo cp /sys/firmware/acpi/tables/SSDT ./ && sudo chown doro SSDT* $ # 若是剛才有用 acpidump,這裡可用 $ # acpixtract -sSSDT acpidump.dat $ # acpixtract -sDSDT acpidump.dat
接著,再把那些 AML 反組譯成 ASL,如下:
$ iasl -d DSDT.dat SSDT*.dat
結果變存成 DSDT.dsl and SSDT*.dsl。得到 ASL 後,痛苦的除錯便開始了。
追蹤 ASL(DSDT)
一般來說,若只是要追 ACPI events 有沒有被呼叫,可以先用 acpi_listen 來做初步的確認,如下:
$ sudo acpi_listen video LCD 00000087 00000000 video LCD 00000086 00000000
看來 LCD 亮度調整的 event 有送出,非常好。如是沒有只好再進入下一步:追 ASL。
目前小弟知道有 2 種可以追蹤 ASL 的方法,第一種是重編 kernel 把 CONFIG_ACPI_DEBUG 設成 y,如此便能用 /sys/module/acpi/parameters/debug_{layer,level} 來追流程。這種方法可以參考 ACPI Tricks and Tips 這份文件的方法。雖然這種方法確實不錯用,可惜小弟上次除錯遇到了極端的例子,問題一重編 kernel 就不見了,於是只好用第二種 systemtap 的方法。
首先,先安裝 kernel debug symbols
$ sudo tee /etc/apt/sources.list.d/ddebs.list << EOF deb http://ddebs.ubuntu.com/ precise main restricted universe multiverse deb http://ddebs.ubuntu.com/ precise-security main restricted universe multiverse deb http://ddebs.ubuntu.com/ precise-updates main restricted universe multiverse deb http://ddebs.ubuntu.com/ precise-proposed main restricted universe multiverse EOF $ sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys ECDCAD72428D7C01 $ sudo apt-get update $ sudo apt-get install linux-image-$(uname -r)-dbgsym
接著應該再 sudo apt-get install systemtap 即可,但小弟又遇到 Ubuntu 12.04 的 systemtap 版本太舊的問題,於是只好抓 12.10 的 package 來用,這裡會用到有:
- systemtap, http://packages.ubuntu.com/quantal/systemtap
- systemtap-common, http://packages.ubuntu.com/quantal/systemtap-common
- systemtap-runtime, http://packages.ubuntu.com/quantal/systemtap-runtime
這些抓下來後 sudo dpkg -i *.deb 即安裝成功。接下來請再下載 systemtap script aml.stp,最後我們便能用 sudo stap -g aml.stp 追蹤所有 ASL 的 operations,一開始除錯時通常會配合著 grep Evaluate and Call 先對整個 method 呼叫流程有初步認識,如下:
$ sudo stap -g aml.stp | ts | tee acpi_ok.log | grep '\(Evalua\|Call\)' May 21 09:47:13 Evaluate _Q66(): May 21 09:47:13 Call NEVT(): May 21 09:47:13 Call ECG1(): May 21 09:47:13 Call ECRW(): May 21 09:47:13 Call ECR2(): May 21 09:47:13 Call ECR1(): May 21 09:47:13 Call ECR1(): May 21 09:47:13 Call ECGD(): May 21 09:47:13 Call ECRW(): May 21 09:47:13 Call ECR2(): May 21 09:47:13 Call ECR1(): May 21 09:47:13 Call ECR1(): May 21 09:47:13 Call EV4_(): May 21 09:47:13 Call WMNF(): May 21 09:47:13 Call SWEV(): May 21 09:47:13 Call SMIE(): May 21 09:47:13 Call GENS(): May 21 09:47:13 Call SMBI(): May 21 09:47:13 Call SNVC(): May 21 09:47:13 Evaluate _WED():
這是我在 NB 上面按下亮度的 hotkey,大概可以看到一開始 Q event 被發動,接著就很長很長一段不想看的... 我就說痛苦的除錯吧
覆蓋 DSDT
在經過一翻波折好不容易用找到 DSDT 的問題所在,接著便會想把解法在不修改 BIOS 情況下覆蓋上去,目前小弟得知有 2 種以上方法。一是改用 kernel 編譯參數,小弟很懶沒試,有興趣可參考 Debian Wiki,第二種比較推薦,因為只需加 grub 參數即可,但前提是 grub2 才有支援 (Ubuntu 在很早以前就是 grub2 了)。
假設你已經由 iasl 拿到 dsdt.dsl 了,並且在 dsdt.dsl 加入自己想要改的東西,再用 iasl 組譯 dsdt.dsl 成 dsdt.aml,接在把它複製到 /boot/ 下:
$ # make some modification you need $ iasl -tc dsdt.dsl $ # it may have warning and error, debug it by yourself $ sudo cp dsdt.aml /boot/
最後修改 /boot/grub/grub.cfg 裡的 linux 開機參數,找到
linux /boot/vmlinuz-3.5.0-22-generic root=UUID=026d718b-cb17-41af-a0b3-cb5fa97db936 ro quiet splash initrd /boot/initrd.img-3.5.0-22-generic
結尾加上 acpi /boot/dsdt.aml
linux /boot/vmlinuz-3.5.0-22-generic root=UUID=026d718b-cb17-41af-a0b3-cb5fa97db936 ro quiet splash initrd /boot/initrd.img-3.5.0-22-generic acpi /boot/dsdt.aml
重開機後,再 dump 一次 dsdt 就會發現剛才加的 code 已經被塞進去了。
References
- Using systemtap to do runtime aml, http://smackerelofopinion.blogspot.tw/2011/06/using-systemtap-to-do-runtime-aml.html
- SystemTap print statements from "embedded C" functions, http://smackerelofopinion.blogspot.tw/2011/09/systemtap-print-statements-from.html
終於寫完了,我的 bug 還在痛苦的除錯中...
Update 2013/8/10
systemtap 2.2.1 for 12.04 已經 backport 到我的 PPA 裡 ppa:fcwu-tw/ppa
No comments:
Post a Comment