UEFI開發探索59-UDK Debugger Tool調試Firmware

(請保留-> 作者: 羅冰   https://blog.csdn.net/luobing4365)

這篇博客使用的是Windbg+UDK Debugger Tool調試代碼,在Windows10下操作,具體搭建方法可以查看以前的博客。

在最近的開發中,因爲碰到不少問題,對UEFI的調試方法更爲深入了。在第39篇博客中留下的問題(7 小尾巴),也有了解答,後續看什麼時候再把解答補上。

雖然在日常開發中,很少直接用到固件,但在學習中,需要跟蹤UEFI每個階段的工作,直接使用OvmfPkg編譯後的Firmware,對其進行調試是個不錯的方法。

圖1爲UEFI運行的7個階段:

圖1 UEFI運行過程

我使用的工具是windbg+intel UDK Debugger Tool,當然,在Linux下使用gdb+UDK debugger Tool也是可以的,有空時也把Linux下調試過程記錄下來。

針對幾個階段的調試過程,介紹如下。

添加調試支持

所添加的調試庫(DebugAgentLib),用來支持源碼級別的調試。DSC文件中需要修改的內容如下:

Libraries
[LibraryClasess] General
PeCoffExtraActionLib
[LibraryClasses.IA32] PEI
DebugAgentLib
[LibraryClasses.X64] DXE
DebugAgentLib
[LibraryClasses.X64.DXE_SMM_DRIVER] SMM
DebugAgentLib

源碼級調試,其SourceLevelDebugPkg Lib對應Module的INF文件爲:

PeCoffExtraActionLibDebug.inf
SecPeiDebugAgentLib.inf
DxeDebugAgentLib.inf
SmmDebugAgentLib.in

FDF文件中,需要添加TerminalDxe.inf,示例如下:

[FV.FVMAIN]
. . .
# DXE Phase modules
. . .
Comment out module for
TerminalDxe.inf
#INF MdeModulePkg/Universal/Console/TerminalDxe/TerminalDxe.inf

在OvmfPkg中,上述調試支持已經添加了。注意查看其DSC文件,以編譯宏開關SOURCE_DEBUG_ENABLE包含了相應的調試庫支持。比如:

!ifdef $(SOURCE_DEBUG_ENABLE)
PeCoffExtraActionLib|SourceLevelDebugPkg/Library/PeCoffExtraActionLibDebug/PeCoffExtraActionLibDebug.inf
DebugCommunicationLib|SourceLevelDebugPkg/Library/DebugCommunicationLibSerialPort/DebugCommunicationLibSerialPort.inf
!else
PeCoffExtraActionLib|MdePkg/Library/BasePeCoffExtraActionLibNull/BasePeCoffExtraActionLibNull.inf  DebugAgentLib|MdeModulePkg/Library/DebugAgentLibNull/DebugAgentLibNull.inf
!endif

當然,也並不是所有應用環境都能調試,以下限制需要注意:

  • 不要去調試調試器本身;
  • 不支持MSR(Model Specific Register)讀寫訪問;
  • 不支持多處理器;
  • 不支持純粹的32位平臺;
  • 不是所有Windbg命令都支持;
  • 在Module沒有加載前,無法對其設置斷點;
  • 部分代碼無法被調試,比如早期的SEC、早期SMM;

編譯OvmfPkg的方法,在之前的博客中已經討論過了,比如針對x64,其編譯命令爲:

build -a X64 -p OvmfPkg\OvmfPkgX64.dsc -b NOOPT -D SOURCE_DEBUG_ENABLE

2 SEC階段調試

其實最好是從Reset Vector開始調試,可是我沒找到在實模式下ffff:fff0下斷點的方法。如果有哪位朋友知道如何下斷,望不吝告知(在評論中寫就可以了,多謝^^)。

圖2 Flash佈局

目前我採用的辦法是,從最開始處,通過彙編指令一直跟蹤下去。曾經嘗試過在PEI階段讀取到了SECMAIN模塊的符號,然後對SECMAIN下的函數下斷,比如bp SECMAIN!IsS3Resume,沒有成功。直接對執行地址下斷,也沒有成功。

找個機會問問人吧。

SEC階段可以去查看CAR(Cache As RAM)的實現,如何進入保護模式的過程等,這個過程比較枯燥,有興趣的話可以跟蹤試試。

3 PEI階段調試

在啓動調試的時候,可以執行如下命令:

bp PEICORE!PeiCore

執行命令‘g’之後,程序會斷在$MyWorkspace\mdemodulepkg\core\pei\peimain\peimain.c中的PeiCore()函數上,之後就可以跟蹤PEI階段的代碼了。

如果想直接看Pei Module的分配過程,可以在如下函數上下斷:

bp PEICORE!PeiDispatcher

函數位於$MyWorkspace \mdemodulepkg\core\pei\dispatcher\dispatcher.c下。

4 DXE階段調試

調試DXE階段的驅動加載過程,可以在下面的函數上下斷點:

bp DXECORE!CoreStartImage

函數位於$MyWorkspace \mdemodulepkg\core\dxe\image\image.c下。

在執行到此處時,使用lm命令,查看得到的模塊信息如下:

0: kd> lm
start             end                 module name
00000000`00820120 00000000`0083a260   PEICORE    (private pdb symbols) 
00000000`0083a400 00000000`00843940   PCDPEIM    (private pdb symbols)  00000000`008439a0 00000000`008486e0   REPORTSTATUSCODEROUTERPEI   (private pdb symbols) 
00000000`008487a0 00000000`0084df60   STATUSCODEHANDLERPEI   (private pdb symbols) 
00000000`0084e020 00000000`0085f060   PLATFORMPEI   (private pdb symbols)
00000000`07b88000 00000000`07b9ae60   DEVICEPATHDXE   (private pdb symbols) 
00000000`07ed3000 00000000`07f2ed60   DXECORE    (private pdb symbols) 
00000000`07f38000 00000000`07f49180   CPUMPPEI   (private pdb symbols) 
00000000`07f4a000 00000000`07f53f60   S3RESUME2PEI   (private pdb symbols) 
00000000`07f54000 00000000`07f5fd80   DXEIPL     (private pdb symbols) 
00000000`07f60000 00000000`07f7a140   PEICORE_7f60000   (private pdb symbols)
00000000`fffcc094 00000000`fffe2f54   SECMAIN    (private pdb symbols)

可以使用x命令,查看各模塊的符號,以及哪些函數可以調試。

5 BDS階段調試

下斷點:

bp DXECORE!DxeMain

函數位於$MyWorkspace \mdemodulepkg\core\dxe\dxemain\dxemain.c。函數中的這幾句,是進入BDS的入口:

gBds->Entry (gBds);
// BDS should never return
ASSERT (FALSE)

使用windbg過程中的一些常用命令:

.reboot //重啓目標機器 x  //顯示模塊符號,包括函數、變量等 lm  //列出所有加載模塊 bp  //下斷點 ba  //在物理地址上下斷點,示例ba e1 0x820000 g  //執行,直到斷點被命中