2013/05/21

Debug ACPI in Ubuntu





做為一位 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 來用,這裡會用到有:

  1. systemtap, http://packages.ubuntu.com/quantal/systemtap
  2. systemtap-common, http://packages.ubuntu.com/quantal/systemtap-common
  3. 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



  1. Using systemtap to do runtime aml, http://smackerelofopinion.blogspot.tw/2011/06/using-systemtap-to-do-runtime-aml.html
  2. 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: