<2024年4月>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

文章分类

导航

订阅

如何跟踪ACPI代码

ACPI是现代计算机系统中BIOS与操作系统间的桥梁。ACPI不仅定义了很多电源和休眠有关的标准,它还定义了如何通过FADTXSDTRSDT以及ASL语言来实现与操作系统的交互。

有些系统问题,比如无法进入睡眠状态,无法唤醒(自动重启,或死机)等,是与ACPI直接有关的。但如何定位出错的具体原因一直是一个比较困难的课题。很多时候不得不使用排除及试探等间接方法来解决,效率很低。本文介绍一种直接跟踪ASL源代码的直接方法。

1,   将被调试机器上的ACPI.SYS换成Checked Build (可以在保护模式下覆盖原来的文件),并建立两台机器组成的内核调试环境(详见WinDbg帮助文档),或者使用虚拟机模拟两台机器(参阅本站的http://advdbg.org/blogs/advdbg_system/articles/130.aspx) 。

2,   在调试机上按Ctrl+Break将被调试机中断到调试器。

3,   尝试使用!amli debugger命令启动AMLI调试器。第一次运行该命令可能失败。典型的出错信息如下:

AMLI_DBGERR: failed to get debugger flag address

此错误的根本原因是本地的AMLI调试模块没有找到合适的调试符号。原因有两个:

1)   被调试机没有使用调试版本的ACPI驱动(ACPI.DLL);

2)   WinDbg还没有加载ACPI调试符号文件。对于此种情况,可尝试使用.reload命令,通常就可以解决问题了。

重新运行!amli debugger,如果没有任何显示,那么本地的AMLI调试模块就合被调试机的ACPI解释器成功握手了(通信)。当ACPI解释器再得到执行时,它就会立刻break到调试机的调试器中。因此,需要执行g命令让被调试机跑起来,才可能真正中断到AMLI的脚本调试器。

本地的AMLI调试扩展模块名称如下:

Windows NT 4.0

Not available

Windows 2000

acpikd.dll

Windows XP and later

kdexts.dll

 

4,   可以运行一些AMLI命令来检查它是否工作正常。比如!amli dns可以打印出ACPI空间中的所有对象。

!amli dns

ACPI Name Space: \ (ffffffff81b3c024)

Unknown(\___)

| Unknown(_GPE)

| | Method(_L03:Flags=0x0,CodeBuff=ffffffff81b3c841,Len=31)

| | Method(_L04:Flags=0x0,CodeBuff=ffffffff81b3c8c1,Len=31)

| | Method(_L05:Flags=0x0,CodeBuff=ffffffff81b3c941,Len=31)

| | Method(_L0B:Flags=0x0,CodeBuff=ffffffff81b3c9c1,Len=31)

| | Method(_L0C:Flags=0x0,CodeBuff=ffffffff81b3ca41,Len=31)

| | Method(_L0D:Flags=0x0,CodeBuff=ffffffff81b3cac1,Len=31)

| | Method(_L0E:Flags=0x0,CodeBuff=ffffffff81b3cb41,Len=31)

| | Method(_L1D:Flags=0x0,CodeBuff=ffffffff81b3cbc1,Len=32)

| Unknown(_PR_)

| | Processor(CPU0:Processor ID=0x1,PBlk=0x410,PBlkLen=6)

| | Processor(CPU1:Processor ID=0x2,PBlk=0x410,PBlkLen=6)

| Unknown(_SB_)

| | Device(SLPB)

| | | Integer(_HID:Value=0x000000000e0cd041[235720769])

| | | Method(_PRW:Flags=0x0,CodeBuff=ffffffff81b3cccd,Len=8)

| | Device(PCI0)

| | | Integer(_HID:Value=0x00000000030ad041[51040321])

| | | Integer(_ADR:Value=0x0000000000000000[0])

| | | Method(_INI:Flags=0x0,CodeBuff=ffffffff81b3d6dd,Len=16)

| | | Buffer(PBRS:Ptr=ffffffff81b3d71c,Len=136){

    0x88,0x0d,0x00,0x02,0x0c,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00

    0x00,0x01,0x47,0x01,0xf8,0x0c,0xf8,0x0c,0x01,0x08,0x88,0x0d,0x00,0x01

    0x0c,0x03,0x00,0x00,0x00,0x00,0xf7,0x0c,0x00,0x00,0xf8,0x0c,0x88,0x0d

……(省略很多行)

也可以通过命令行参数,显示ACPI空间树中从某个设备开始的子树。比如一下命令显示的是电池设备。

kd> !amli dns /s \_sb_.pci0.bat0

 

ACPI Name Space: \_SB_.PCI0.BAT0 (ffffffff81b290f4)

Device(BAT0)

| Integer(_HID:Value=0x000000000a0cd041[168611905])

| Method(_STA:Flags=0x0,CodeBuff=ffffffff81b291d1,Len=32)

| Method(_BIF:Flags=0x0,CodeBuff=ffffffff81b29255,Len=274)

| Method(_BST:Flags=0x0,CodeBuff=ffffffff81b293c9,Len=262)

| | Package(PBST:NumElements=4){

| | | Integer(:Value=0x0000000000000001[1])

| | | Integer(:Value=0x0000000000000a6e[2670])

| | | Integer(:Value=0x0000000000000000[0])

| | | Integer(:Value=0x0000000000002ee0[12000])

| | }

| Method(_PCL:Flags=0x0,CodeBuff=ffffffff81b29531,Len=5)

5,   发出g命令,让被调试机恢复运行状态。因为ACPI的代码的使用频率很高,所以被调试机的ACPI解释器通常很快就会得到执行,并中断进调试器。其状态如图1所示。可见,命令行提示符已经由原来的kd>变为Input> AMLI(? For help))。在此状态下,只可以运行AMLI调试器的命令。而且不需要再使用!amli前缀。

1,中断到AMLI调试器

 

6,   AMLI调试器也提高了一系列断点命令以实现跟踪功能。比如可以通过以下命令在电池设备的_STA方法入口处设置断点。

AMLI(? for help)-> bp \_sb.pci0.bat0._sta

bp \_sb.pci0.bat0._sta

成功设置的断点,AMLI调试器仅仅会回显该断点命令。如果设置失败,还会附加有错误信息,例如:

AMLI(? for help)-> bp \_sb.pci0.bat0.sta

bp \_sb.pci0.bat0.sta

AMLI_DBGERR: object not found or object is not a method - \_SB.PCI0.BAT0.STA

 

7,   设置断点后,可发出g命令让被调试机恢复运行。

8,   当断点被触发时,被调试机会被中断到调试器。

AMLI(? for help)-> g

g

 

Hit Breakpoint 0.

 

AMLI(? for help)->

可以通过ln命令判断本次断点最近的方法名:

AMLI(? for help)-> ln

ln

81b291d1: \_SB.PCI0.BAT0._STA

通过r命令可以显示出当前的上下文。包括线程、堆栈、ASL变量值等信息。

AMLI(? for help)-> r

r

 

Context=81adf000*, Queue=00000000, ResList=00000000

ThreadID=81b4c020, Flags=00000030

StackTop=81ae0f0c, UsedStackSize=244 bytes, FreeStackSize=7708 bytes

LocalHeap=81adf0bc, CurrentHeap=81adf0bc, UsedHeapSize=52 bytes

Object=\_SB.PCI0.BAT0._STA, Scope=\_SB.PCI0.BAT0._STA, ObjectOwner=81adf0e0, SyncLevel=0

AsyncCallBack=f99e6e98, CallBackData=e32de8b0, CallBackContext=f9e9a760

 

MethodObject=\_SB.PCI0.BAT0._STA

81ae0f5c: Local0=Unknown()

81ae0f70: Local1=Unknown()

81ae0f84: Local2=Unknown()

81ae0f98: Local3=Unknown()

81ae0fac: Local4=Unknown()

81ae0fc0: Local5=Unknown()

81ae0fd4: Local6=Unknown()

81ae0fe8: Local7=Unknown()

81adf040: RetObj=Unknown()

 

Next AML Pointer: 81b291d1 [\_SB.PCI0.BAT0._STA]

81b291d1: Store(^^SBUS.SRDW(0x16, 0xa), Local0)

81b291e2: If(LEqual(Local0, 0xffff))

81b291e9: {

81b291e9: | Return(0xf)

81b291ec: }

Press to continue and 'q' to quit?

 

81b291ec: Else

81b291ee: {

81b291ee: | Return(0x1f)

81b291f1: }

通过u命令可以将当前的AML代码反汇编成ASL

9,   可以使用pt命令跟踪ASL代码。

AMLI(? for help)-> p

p

Store(^^SBUS.SRDW(0x16,0xa)

81b4bc69: {

81b4bc69: If(LNot(LEqual(Acquire(MSMB,0xfffe)=0x0,Zero)=0xffffffff)=0x0)

AMLI(? for help)-> p

p

 

81b4bc7a: If(STRT()

81b4bf55: {

81b4bf55: Store(0x3e8,Local0)=0x3e8

AMLI(? for help)->

 

10,  执行set traceon 命令,可以使能ACPI解释器的踪迹功能,打印出所有ACPI代码的执行情况。

81b4bc90: Store(0xbf,HSTS)=0xbf

81b4bc97: Store(Or(Arg0=0x16,One,)=0x17,TXSA)=0x17

81b4bca0: Store(Arg1=0xa,HCOM)=0xa

81b4bca6: Store(0x4c,HCON)=0x4c

81b4bcad: If(COMP()

81b29035: {

81b29035: Store(0xfa0,Local0)=0xfa0

81b2903a: While(Local0=0xfa0)

81b2903d: {

81b2903d: Store(HSTS=0x42,Local1)=0x42

81b29043: If(And(Local1=0x42,0x1e,)=0x2)

81b2904a: {

81b2904a: If(And(Local1=0x42,0x2,)=0x2)

81b29051: {

81b29051: Return(One)

……

 

 

posted on 2005年11月17日 18:41 由 dbg

# re: 如何跟踪ACPI代码 @ 2008年9月11日 22:50

在Vista中,应该在具有管理员权限的控制台窗口执行如下命令后再替换ACPI.sys:
takeown /f acpi.sys
cacls acpi.sys /G <username>:F

Raymond

# re: 如何跟踪ACPI代码 @ 2009年7月21日 13:37

如果确信已经使用check版本的ACPI.SYS,还出现下面的错误提示:
1: kd> !amli debugger
AMLI_DBGERR: failed to get debugger flag address
那么可以执行.reload重新加载一下模块和符号

Raymond

Powered by Community Server Powered by CnForums.Net