本文介绍ACPICA debugger tools,acpidbg和acpidump、acpiexec等;并演示如何使用这些工具,配合initrd,在不重新加载firmware的情况下,修改ACPI table。
一、ACPICA debugger acpidbg
acpidbg是一个runtime调试AML code的工具,不过Linux kernel 4.13中才引入。
1.1 how to enable?
参考Documentations/acpi/aml-debugger.txt文档。
使用acpidbg工具需要kernel和userspace都支持
kernel
编译kernel时,需要设置
CONFIG_ACPI_DEBUGGER=y;
CONFIG_ACPI_DEBUGGER_USER=m
之后,编译kernel,得到acpi_dbg.ko;
userspace tools:acpidbg
$ cd tools
$ make acpi
生成的可执行程序在tools/acpi/power/acpi/acpidbg/acpidbg
$make install
将acpidbg安装到系统的执行路径中;
1.2 how to start userspace debugger
重启系统,使用acpidbg built-in的kernel启动
$ mount -t debugfs none /sys/kernel/debug
$ modprobe acpi_dbg
$ tools/acpi/power/acpi/acpidbg/acpidbg
之后,就可以进入到可交互的AML debugger环境中了,可以输入debugger command执行。
1.3 command supported
acpidbg 启动之后,可以输入help命令,查看acpidbg支持的command如下:
$ acpidbg
- help
Summary of AML Debugger Commands
Namespace Access: //访问namespace的命令
Businfo //Display system bus info
Disassemble <Method> //Disassemble a control method
Find <AcpiName> (? is wildcard) //Find ACPI name(s) with wildcards
Integrity //Validate namespace integrity
Methods //Display list of loaded control methods
Namespace [Object] [Depth] //Display loaded namespace tree/subtree
Notify <Object> <Value> //Send a notification on Object
Objects [ObjectType] //Display summary of all objects or just given type
Owner <OwnerId> [Depth] //Display loaded namespace by object owner
Paths Display full pathnames of namespace objects
Predefined Check all predefined names
Prefix [<Namepath>] Set or Get current execution prefix
References <Addr> Find all references to object at addr
Resources [DeviceName] Display Device resources (no arg = all devices)
Set N <NamedObject> <Value> Set value for named integer
Template <Object> Format/dump a Buffer/ResourceTemplate
Type <Object> Display object type
Control Method Execution:
1) Evaluate <Namepath> [Arguments] //Evaluate object or control method
2) Execute <Namepath> [Arguments] //Synonym for Evaluate
3) Debug <Namepath> [Arguments] //Single-Step a control method
Miscellaneous:
Allocations //Display list of current memory allocations
Dump <Address>|<Namepath> [Byte|Word|Dword|Qword]
Handlers //Info about global handlers
Help [Command] //This help screen or individual command
History //Display command history buffer
Level <DebugLevel>] [console] //Get/Set debug level for file or console
Locks //Current status of internal mutexes
Osi [Install|Remove <name>] //Display or modify global _OSI list
Quit or Exit //Exit this command
Stats <SubCommand> //Display namespace and memory statistics
Allocations Display list of current memory allocations
Memory Dump internal memory lists
Misc Namespace search and mutex stats
Objects Summary of namespace objects
Sizes Sizes for each of the internal objects
Stack Display CPU stack usage
Tables Info about current ACPI table(s)
Tables Display info about loaded ACPI tables
! <CommandNumber> Execute command from history buffer
!! Execute last command again
Method and Namespace Debugging:
Trace <State> [<Namepath>] [Once] Trace control method execution
Enable Enable all messages
Disable Disable tracing
Method Enable method execution messages
Opcode Enable opcode execution messages
Test <TestName> Invoke a debug test
Objects Read/write/compare all namespace data objects
Predefined Validate all ACPI predefined names (_STA, etc.)
Execute predefined Execute all predefined (public) methods
Control Method Single-Step Execution:
Arguments (or Args) Display method arguments
Breakpoint <AmlOffset> Set an AML execution breakpoint
Call Run to next control method invocation
Go Allow method to run to completion
Information Display info about the current method
Into Step into (not over) a method call
List [# of Aml Opcodes] Display method ASL statements
Locals Display method local variables
Results Display method result stack
Set <A|L> <#> <Value> Set method data (Arguments/Locals)
Stop Terminate control method
Tree Display control method calling tree
<Enter> Single step next AML opcode (over calls)
在我的DELL机器上运行acpidbg的实例如下:
- namespace \_SB.PWRB
ACPI Namespace (from PWRB (00000000570a7360) subtree):
0 _HID Integer 0000000086484fc8 01 = 000000000C0CD041
0 _UID Integer 000000008432cef4 01 = 00000000000000AA
0 _STA Integer 00000000699fce6e 01 = 000000000000000B
0 _PRW Method 00000000467154ae 01 Args 0 Len 0009 Aml 00000000f0406bd4
Namespace node count: 4
- execute \_SB.PWRB._STA //execute _STA method
Evaluating \_SB.PWRB._STA
Evaluation of \_SB.PWRB._STA returned object 000000005d4a43c1, external buffer length 18
[Integer] = 000000000000000B
- handlers: //list handlers info
Operation Region Handlers at the namespace root:
SystemMemory (00) : Default (000000008e3627f4)
SystemIO (01) : Default (00000000c26e80d4)
PCI_Config (02) : Default (00000000df357aeb)
EmbeddedControl (03) : None
SMBus (04) : None
SystemCMOS (05) : None
PCIBARTarget (06) : None
IPMI (07) : None
GeneralPurposeIo (08) : None
GenericSerialBus (09) : None
DataTable (7E) : Default (00000000b76a34e6)
FunctionalFixedHW (7F) : None
Fixed Event Handlers:
PM_Timer (00) : None
GlobalLock (01) : User (00000000c25964bf)
PowerButton (02) : User (00000000d0879d19)
SleepButton (03) : None
RealTimeClock (04) : User (00000000dbb9df24)
Miscellaneous Global Handlers:
System Notifications : User (0000000030e91327)
Device Notifications : User (0000000061dc2179)
ACPI Table Events : User (000000005fb8bcf3)
Control Method Exceptions : User (0000000075b0eefe)
OSI Invocations : User (0000000096a26980)
Operation Region Handlers for specific devices:
PCI_Config (02) : Default (00000000df357aeb) Device Name: \_SB.PCI0 (0000000099b391d1)
SystemCMOS (05) : User (00000000787d56eb) Device Name: \_SB.PCI0.LPCB.RTC (00000000bece04ca)
GenericSerialBus (09) : User (00000000289e2622) Device Name: \_SB.PCI0.SBUS (0000000061eb7af3)
SystemIO (01) : User (0000000097b46fee) Device Name: \_SB.PCI0.SBUS (0000000061eb7af3)
GenericSerialBus (09) : User (00000000289e2622) Device Name: \_SB.PCI0.GFX0 (0000000099865817)
1.4 退出userspace debugger
ctrl+C、“quit”、“exit”都可以退出acpidbg;
之后$ rmmod acpi_dbg卸载模块即可。
二、ACPICA tools
acpidbg很好用,但是直到kernel 4.13 中才引入,那老版本的kernel怎么办呢?
别担心,在4.13 之前,虽然没有acpidbg,但是kernel提供了很多ACPICA tools,包括acpidump、acpiexec、acpixtract等工具,也能帮助我们对AML code进行调试。
下面我依然以执行PWRB device的_STA control method为例,介绍这些ACPICA tools的常用方法。
2.1 获取所有的ACPI tables
$ sudo acpidump > acpi.log
2.2 提取DSDT和SSDT信息
$acpixtract acpi.log
该命令会从acpi.log中摘取DSDT和所有的SSDT信息,生成dsdt.dat和ssdtN.dat文件。
DSDT 和SSDT是device configuration table,构成了整个ACPI namespace,包括所有的device node 信息。
2.3 反编译dsdt 和ssdt 文件
$ iasl -d *.dat
得到DSDT和SSDT相应的ASL 文件。
2.4 查看PWRB device在哪个table里
从ACPI spec中可以查到PWRB device的HID为“PNP0C0C”
$grep "PNP0C0C" *.dsl
dsdt.dsl: Name (_HID, EisaId ("PNP0C0C") /* Power Button Device */)
之后,我们就可以使用vim/vi 打开dsdt.dsl文件,查看PWRB device的详细信息如下:
scope(\_SB) {
Device (PWRB) {
Name (_HID, EisaId ("PNP0C0C") /* Power Button Device */)
Name (_UID, 0xAA)
Name (_STA, 0x0B)
Method (_PRW, 0, NotSerialized) {
Return (GPRW (0x1B, 0x03))
}
}
}
2.5 通过acpiexec执行PWRB device的_STA method
acpiexec的使用和acpidbg类似,不过它需要先加载ACPI table,然后在仿真环境中执行AML code,即acpiexec的执行不会真正地操作hardware register或memory。
$acpiexec dsdt.dat
- execute \_SB.PWRB._STA
Evaluating \_SB.PWRB._STA
Evaluation of \_SB.PWRB._STA returned object 0x55bcd14acc90, external buffer length 18
[Integer] = 000000000000000B
- execute \_SB.PWRB._PRW
Evaluating \_SB.PWRB._PRW
Evaluation of \_SB.PWRB._PRW returned object 0x55bcd14acc90, external buffer length 48
[Package] Contains 2 Elements:
[Integer] = 000000000000001B
[Integer] = 0000000000000003
三、利用initrd修改ACPI table
前面两节介绍了如何使用acpidbg和ACPICA tools工具对AML code进行debug,我们接下来介绍如何使用这些工具配合initrd修改ACPI table。
参考文档Documentations/acpi/initrd_table_override.txt
如果kernel设置了CONFIG_ACPI_TABLE_UPGRADE=y,那就可以通过initrd修改或新增ACPI table。 除了RSDP、FACS table之外的其他ACPI table都可以用这种方式来修改。
接下来,我会通过一个实例,向DSDT table中新增一个IPMI device,来演示如何通过initrd来修改ACPI table。
3.1 获取当前的DSDT table
$ mkdir /tmp
$ cd tmp
$ acpidump > acpidump
$ acpixtract -a acpidump
$ iasl -d dsdt.dat //解析得到dsdt.dsl 文件
3.2 修改dsdt.dsl文件
在PCI0 scope中添加IPMI device
scope (_PCI0) {
Device (MI00) {
Name(_HID,"IPI0001")
Name(_IFT,0x01)
Method (_STA, 0, NotSerialized) {
Return (0x0F)
}
}
}
3.3 增加DSDT table中的OEM version
before:
DefinitionBlock ("DSDT.aml", "DSDT", 2, "INTEL ", "TEMPLATE", 0x00000000)
after:
DefinitionBlock ("DSDT.aml", "DSDT", 2, "INTEL ", "TEMPLATE", 0x00000001)
我们加入新的DSDT table时,如果platform中已经有了一个DSDT table,会去比较二者的OEM version。若new table的OEM version比较大,则会替换原来的table,否则依然使用原来的table。因此,我们要把new DSDT table的OEM revision加大,这样才能用新的DSDT替换原来的table。
3.4 编译修改的dsdt.dsl
$ iasl -sa dsdt.dsl
得到dsdt.aml 文件
3.5 将编译后的dsdt.aml文件加入到initrd中
将编译后的dsdt.dml加入到一个new uncompressed cpio archive中;
dsdt.aml 需要放到kernel/firmware/acpi/目录下
$ mkdir -p kernel/firmware/acpi
$ cp dsdt.aml kernel/firmware/acpi/
创建acpi_test_initrd 文件,放到/boot/目录下:
$ find kernel | cpio -H newc --create > /boot/acpi_test_initrd
新创建的acpi_test_initrd需要放在最前面,之前的压缩的cpio文件initrd.img-4.20.17放 在其后面
$ cat /boot/initrd.img-4.20.17 >> /boot/acpi_test_initrd
3.6 之后重启系统,使用新的initrd文件
重启时需要在grub中修改boot initrd:
before:
initrd /boot/initrd.img-4.20.17
after:
initrd /boot/acpi_test_initrd
3.7 系统启动之后,查看是否IPMI device MI00是否已经加进去了。
启动acpidbg查看
$ modprobe acpi_dbg
$ acpidbg
- find MI00
\_SB.PCI0.MI00 Device 000000001350acba 01
- execute _SB.PCI0.MI00._STA
Evaluating \_SB.PCI0.MI00._STA
Evaluation of \_SB.PCI0.MI00._STA returned object 000000000c9bc94a, external buffer length 18
[Integer] = 000000000000000F
启动acpidbg之后,通过find命令在ACPI namespace查找MI00 device,能看到IPMI device已经成功地加入到ACPI table中了,而且我们还能够执行MI00 device的control method,如果有问题,可以使用acpidbg提供的命令进行调试并修改了。