1.我们调试中常用Debug 打印信息,这些会输出到BIOS串口日志中
EFI_STATUSEFIAPIHelloWorld2(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE *SystemTable){EFI_STATUS Status;Status=EFI_SUCCESS;gST->ConOut->OutputString(gST->ConOut,L"ConOut:Hellow World \n\r");Print(L"Print:Hellow World\n");DEBUG ((DEBUG_ERROR, "Debug:Hellow World\n"));return Status;}
UEFI shell下运行效果:我们发现这几种输出都打印到了Shell界面而不是串口中
通常在shell 界面显示打印信息会使用到Print() 和 gST->ConOut->OutputString,而Debug 是打印在串口日志中
Debug 对于调试至关重要,那么我们需要深入了解DebugLib 和Debug()实现
/**Macro that calls DebugPrint().If MDEPKG_NDEBUG is not defined and the DEBUG_PROPERTY_DEBUG_PRINT_ENABLEDbit of PcdDebugProperyMask is set, then this macro passes Expression toDebugPrint().@param Expression Expression containing an error level, a format string,and a variable argument list based on the format string.**/#if !defined (MDEPKG_NDEBUG)#define DEBUG(Expression) \do { \if (DebugPrintEnabled ()) { \_DEBUG (Expression); \} \} while (FALSE)#else#define DEBUG(Expression)#endif
有多个Lib 对DebugPrintEnabled 做了定义,因此可以使用不同的Lib 让Debug()产生不同的效果,这点也适用于其他函数
对于不同的架构实现方式可能不同,但是可以用同样的模块代码,通过替换Lib去替换实现函数
EDK常用的Debuglib
DebugLib|MdeModulePkg/Library/PeiDxeDebugLibReportStatusCode/PeiDxeDebugLibReportStatusCode.inf
DebugLib|MdePkg/Library/BaseDebugLibSerialPort/BaseDebugLibSerialPort.inf
DebugLib|MdePkg/Library/UefiDebugLibConOut/UefiDebugLibConOut.inf
其中MDEPKG_NDEBUG和DebugPrintEnabled()时调试信息开关和等级的控制方法
包括PcdDebugPropertyMask DEBUG_PROPERTY_DEBUG_PRINT_ENABLED
BOOLEAN
EFIAPI
DebugPrintEnabled (
VOID
)
{
return (BOOLEAN)((PcdGet8 (PcdDebugPropertyMask) & DEBUG_PROPERTY_DEBUG_PRINT_ENABLED) != 0);
}
PcdDebugPropertyMask 定义
## The mask is used to control DebugLib behavior.<BR><BR>
# BIT0 - Enable Debug Assert.<BR>
# BIT1 - Enable Debug Print.<BR>
# BIT2 - Enable Debug Code.<BR>
# BIT3 - Enable Clear Memory.<BR>
# BIT4 - Enable BreakPoint as ASSERT.<BR>
# BIT5 - Enable DeadLoop as ASSERT.<BR>
# BIT6 - Enable Debug Assert as BreakPoint only.<BR>
# @Prompt Debug Property.
# @Expression 0x80000002 | (gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask & 0xC0) == 0
gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask|0|UINT8|0x00000005
//
// Declare bits for PcdDebugPropertyMask
//
#define DEBUG_PROPERTY_DEBUG_ASSERT_ENABLED 0x01
#define DEBUG_PROPERTY_DEBUG_PRINT_ENABLED 0x02
#define DEBUG_PROPERTY_DEBUG_CODE_ENABLED 0x04
#define DEBUG_PROPERTY_CLEAR_MEMORY_ENABLED 0x08
#define DEBUG_PROPERTY_ASSERT_BREAKPOINT_ENABLED 0x10
#define DEBUG_PROPERTY_ASSERT_DEADLOOP_ENABLED 0x20
#define DEBUG_PROPERTY_ASSERT_ONLY_BREAKPOINT 0x40
第二个相关的Pcd : PcdFixedDebugPrintErrorLevel 打印信息等级常用的就是EFI_D_ERROR EFI_D_WARN EFI_D_INFO 等
## This flag is used to control build time optimization based on debug print level.
# Its default value is 0xFFFFFFFF to expose all debug print level.
# BIT0 - Initialization message.<BR>
# BIT1 - Warning message.<BR>
# BIT2 - Load Event message.<BR>
# BIT3 - File System message.<BR>
# BIT4 - Allocate or Free Pool message.<BR>
# BIT5 - Allocate or Free Page message.<BR>
# BIT6 - Information message.<BR>
# BIT7 - Dispatcher message.<BR>
# BIT8 - Variable message.<BR>
# BIT10 - Boot Manager message.<BR>
# BIT12 - BlockIo Driver message.<BR>
# BIT14 - Network Driver message.<BR>
# BIT16 - UNDI Driver message.<BR>
# BIT17 - LoadFile message.<BR>
# BIT19 - Event message.<BR>
# BIT20 - Global Coherency Database changes message.<BR>
# BIT21 - Memory range cachability changes message.<BR>
# BIT22 - Detailed debug message.<BR>
# BIT31 - Error message.<BR>
# @Prompt Fixed Debug Message Print Level.
gEfiMdePkgTokenSpaceGuid.PcdFixedDebugPrintErrorLevel|0xFFFFFFFF|UINT32|0x30001016
//
// Declare bits for PcdDebugPrintErrorLevel and the ErrorLevel parameter of DebugPrint()
//
#define DEBUG_INIT 0x00000001 // Initialization
#define DEBUG_WARN 0x00000002 // Warnings
#define DEBUG_LOAD 0x00000004 // Load events
#define DEBUG_FS 0x00000008 // EFI File system
#define DEBUG_POOL 0x00000010 // Alloc & Free (pool)
#define DEBUG_PAGE 0x00000020 // Alloc & Free (page)
#define DEBUG_INFO 0x00000040 // Informational debug messages
#define DEBUG_DISPATCH 0x00000080 // PEI/DXE/SMM Dispatchers
#define DEBUG_VARIABLE 0x00000100 // Variable
#define DEBUG_BM 0x00000400 // Boot Manager
#define DEBUG_BLKIO 0x00001000 // BlkIo Driver
#define DEBUG_NET 0x00004000 // Network Io Driver
#define DEBUG_UNDI 0x00010000 // UNDI Driver
#define DEBUG_LOADFILE 0x00020000 // LoadFile
#define DEBUG_EVENT 0x00080000 // Event messages
#define DEBUG_GCD 0x00100000 // Global Coherency Database changes
#define DEBUG_CACHE 0x00200000 // Memory range cachability changes
#define DEBUG_VERBOSE 0x00400000 // Detailed debug messages that may
// significantly impact boot performance
#define DEBUG_MANAGEABILITY 0x00800000 // Detailed debug and payload message of manageability
// related modules, such Redfish, IPMI, MCTP and etc.
#define DEBUG_ERROR 0x80000000 // Error
MdeModulePkg/Application/HelloWorld/HelloWorld.inf {
<PcdsFixedAtBuild>
gEfiMdePkgTokenSpaceGuid.PcdFixedDebugPrintErrorLevel | 0xffffffff
gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask|0xff
<LibraryClasses>
#DebugLib|MdePkg/Library/BaseDebugLibSerialPort/BaseDebugLibSerialPort.inf
#DebugLib|MdePkg/Library/UefiDebugLibConOut/UefiDebugLibConOut.inf
DebugLib|MdeModulePkg/Library/PeiDxeDebugLibReportStatusCode/PeiDxeDebugLibReportStatusCode.inf
}
gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask|0x0 就不会打印Debug 信息
gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask|0xff 进行打印
_DEBUG (Expression); #define _DEBUG(Expression) _DEBUG_PRINT Expression#define _DEBUG_PRINT(PrintLevel, ...) \do { \if (DebugPrintLevelEnabled (PrintLevel)) { \DebugPrint (PrintLevel, ##__VA_ARGS__); \} \} while (FALSE)BOOLEANEFIAPIDebugPrintLevelEnabled (IN CONST UINTN ErrorLevel){return (BOOLEAN)((ErrorLevel & PcdGet32 (PcdFixedDebugPrintErrorLevel)) != 0);}**/VOIDEFIAPIDebugPrint (IN UINTN ErrorLevel,IN CONST CHAR8 *Format,...){VA_LIST Marker;VA_START (Marker, Format);DebugVPrint (ErrorLevel, Format, Marker);VA_END (Marker);}VOIDEFIAPIDebugVPrint (IN UINTN ErrorLevel,IN CONST CHAR8 *Format,IN VA_LIST VaListMarker){DebugPrintMarker (ErrorLevel, Format, VaListMarker, NULL);}1.BaseDebugLibSerialPort Debuglib 使用的是SerialPortWrite 打印信息DebugLib|MdePkg/Library/BaseDebugLibSerialPort/BaseDebugLibSerialPort.infMdePkg\Library\BaseDebugLibSerialPort\DebugLib.cVOIDDebugPrintMarker (IN UINTN ErrorLevel,IN CONST CHAR8 *Format,IN VA_LIST VaListMarker,IN BASE_LIST BaseListMarker){CHAR8 Buffer[MAX_DEBUG_MESSAGE_LENGTH];//// If Format is NULL, then ASSERT().//ASSERT (Format != NULL);//// Check driver debug mask value and global mask//if ((ErrorLevel & GetDebugPrintErrorLevel ()) == 0) {return;}if (AsciiStrStr (Format, "\n") != NULL) {UINTN ModuleNameLength;ModuleNameLength = AsciiStrLen (gEfiCallerBaseName) + 2;*Buffer = '[';AsciiStrnCpyS (Buffer + 1,ModuleNameLength -1,gEfiCallerBaseName,ModuleNameLength - 2);*(Buffer + ModuleNameLength - 1) = ']';*(Buffer + ModuleNameLength) = 0;//// Send the print string to a Serial Port//SerialPortWrite ((UINT8 *)Buffer, AsciiStrLen (Buffer));}//// Convert the DEBUG() message to an ASCII String//if (BaseListMarker == NULL) {AsciiVSPrint (Buffer, sizeof (Buffer), Format, VaListMarker);} else {AsciiBSPrint (Buffer, sizeof (Buffer), Format, BaseListMarker);}//// Send the print string to a Serial Port//SerialPortWrite ((UINT8 *)Buffer, AsciiStrLen (Buffer));}2. PeiDxeDebugLibReportStatusCode是更通用的Debug 实现了从PEI DXE 等都有实现DebugLib|MdeModulePkg/Library/PeiDxeDebugLibReportStatusCode/PeiDxeDebugLibReportStatusCode.infMdeModulePkg\Library\PeiDxeDebugLibReportStatusCode\DebugLib.c**/VOIDDebugPrintMarker (IN UINTN ErrorLevel,IN CONST CHAR8 *Format,IN VA_LIST VaListMarker,IN BASE_LIST BaseListMarker){UINT64 Buffer[(EFI_STATUS_CODE_DATA_MAX_SIZE / sizeof (UINT64)) + 1];EFI_DEBUG_INFO *DebugInfo;UINTN TotalSize;BASE_LIST BaseListMarkerPointer;CHAR8 *FormatString;BOOLEAN Long;//// If Format is NULL, then ASSERT().//ASSERT (Format != NULL);//// Check driver Debug Level value and global debug level//if ((ErrorLevel & GetDebugPrintErrorLevel ()) == 0) {return;}//// Compute the total size of the record.// Note that the passing-in format string and variable parameters will be constructed to// the following layout://// Buffer->|------------------------|// | Padding | 4 bytes// DebugInfo->|------------------------|// | EFI_DEBUG_INFO | sizeof(EFI_DEBUG_INFO)// BaseListMarkerPointer->|------------------------|// | ... |// | variable arguments | 12 * sizeof (UINT64)// | ... |// |------------------------|// | Format String |// |------------------------|<- (UINT8 *)Buffer + sizeof(Buffer)//TotalSize = 4 + sizeof (EFI_DEBUG_INFO) + 12 * sizeof (UINT64) + AsciiStrSize (Format);//// If the TotalSize is larger than the maximum record size, then truncate it.//if (TotalSize > sizeof (Buffer)) {TotalSize = sizeof (Buffer);}//// Fill in EFI_DEBUG_INFO//// Here we skip the first 4 bytes of Buffer, because we must ensure BaseListMarkerPointer is// 64-bit aligned, otherwise retrieving 64-bit parameter from BaseListMarkerPointer will cause// exception on IPF. Buffer starts at 64-bit aligned address, so skipping 4 types (sizeof(EFI_DEBUG_INFO))// just makes address of BaseListMarkerPointer, which follows DebugInfo, 64-bit aligned.//DebugInfo = (EFI_DEBUG_INFO *)(Buffer) + 1;DebugInfo->ErrorLevel = (UINT32)ErrorLevel;BaseListMarkerPointer = (BASE_LIST)(DebugInfo + 1);FormatString = (CHAR8 *)((UINT64 *)(DebugInfo + 1) + 12);//// Copy the Format string into the record. It will be truncated if it's too long.//AsciiStrnCpyS (FormatString,sizeof (Buffer) - (4 + sizeof (EFI_DEBUG_INFO) + 12 * sizeof (UINT64)),Format,sizeof (Buffer) - (4 + sizeof (EFI_DEBUG_INFO) + 12 * sizeof (UINT64)) - 1);//// The first 12 * sizeof (UINT64) bytes following EFI_DEBUG_INFO are for variable arguments// of format in DEBUG string, which is followed by the DEBUG format string.// Here we will process the variable arguments and pack them in this area.////// Use the actual format string.//Format = FormatString;for ( ; *Format != '\0'; Format++) {//// Only format with prefix % is processed.//if (*Format != '%') {continue;}Long = FALSE;//// Parse Flags and Width//for (Format++; TRUE; Format++) {if ((*Format == '.') || (*Format == '-') || (*Format == '+') || (*Format == ' ')) {//// These characters in format field are omitted.//continue;}if ((*Format >= '0') && (*Format <= '9')) {//// These characters in format field are omitted.//continue;}if ((*Format == 'L') || (*Format == 'l')) {//// 'L" or "l" in format field means the number being printed is a UINT64//Long = TRUE;continue;}if (*Format == '*') {//// '*' in format field means the precision of the field is specified by// a UINTN argument in the argument list.//if (BaseListMarker == NULL) {BASE_ARG (BaseListMarkerPointer, UINTN) = VA_ARG (VaListMarker, UINTN);} else {BASE_ARG (BaseListMarkerPointer, UINTN) = BASE_ARG (BaseListMarker, UINTN);}continue;}if (*Format == '\0') {//// Make no output if Format string terminates unexpectedly when// looking up for flag, width, precision and type.//Format--;}//// When valid argument type detected or format string terminates unexpectedly,// the inner loop is done.//break;}//// Pack variable arguments into the storage area following EFI_DEBUG_INFO.//if ((*Format == 'p') && (sizeof (VOID *) > 4)) {Long = TRUE;}if ((*Format == 'p') || (*Format == 'X') || (*Format == 'x') || (*Format == 'd') || (*Format == 'u')) {if (Long) {if (BaseListMarker == NULL) {BASE_ARG (BaseListMarkerPointer, INT64) = VA_ARG (VaListMarker, INT64);} else {BASE_ARG (BaseListMarkerPointer, INT64) = BASE_ARG (BaseListMarker, INT64);}} else {if (BaseListMarker == NULL) {BASE_ARG (BaseListMarkerPointer, int) = VA_ARG (VaListMarker, int);} else {BASE_ARG (BaseListMarkerPointer, int) = BASE_ARG (BaseListMarker, int);}}} else if ((*Format == 's') || (*Format == 'S') || (*Format == 'a') || (*Format == 'g') || (*Format == 't')) {if (BaseListMarker == NULL) {BASE_ARG (BaseListMarkerPointer, VOID *) = VA_ARG (VaListMarker, VOID *);} else {BASE_ARG (BaseListMarkerPointer, VOID *) = BASE_ARG (BaseListMarker, VOID *);}} else if (*Format == 'c') {if (BaseListMarker == NULL) {BASE_ARG (BaseListMarkerPointer, UINTN) = VA_ARG (VaListMarker, UINTN);} else {BASE_ARG (BaseListMarkerPointer, UINTN) = BASE_ARG (BaseListMarker, UINTN);}} else if (*Format == 'r') {if (BaseListMarker == NULL) {BASE_ARG (BaseListMarkerPointer, RETURN_STATUS) = VA_ARG (VaListMarker, RETURN_STATUS);} else {BASE_ARG (BaseListMarkerPointer, RETURN_STATUS) = BASE_ARG (BaseListMarker, RETURN_STATUS);}}//// If the converted BASE_LIST is larger than the 12 * sizeof (UINT64) allocated bytes, then ASSERT()// This indicates that the DEBUG() macro is passing in more argument than can be handled by// the EFI_DEBUG_INFO record//ASSERT ((CHAR8 *)BaseListMarkerPointer <= FormatString);//// If the converted BASE_LIST is larger than the 12 * sizeof (UINT64) allocated bytes, then return//if ((CHAR8 *)BaseListMarkerPointer > FormatString) {return;}}//// Send the DebugInfo record//REPORT_STATUS_CODE_EX (EFI_DEBUG_CODE,(EFI_SOFTWARE_DXE_BS_DRIVER | EFI_DC_UNSPECIFIED),0,NULL,&gEfiStatusCodeDataTypeDebugGuid,DebugInfo,TotalSize);}**/#define REPORT_STATUS_CODE_EX(Type, Value, Instance, CallerId, ExtendedDataGuid, ExtendedData, ExtendedDataSize) \(ReportProgressCodeEnabled() && ((Type) & EFI_STATUS_CODE_TYPE_MASK) == EFI_PROGRESS_CODE) ? \ReportStatusCodeEx(Type,Value,Instance,CallerId,ExtendedDataGuid,ExtendedData,ExtendedDataSize) : \(ReportErrorCodeEnabled() && ((Type) & EFI_STATUS_CODE_TYPE_MASK) == EFI_ERROR_CODE) ? \ReportStatusCodeEx(Type,Value,Instance,CallerId,ExtendedDataGuid,ExtendedData,ExtendedDataSize) : \(ReportDebugCodeEnabled() && ((Type) & EFI_STATUS_CODE_TYPE_MASK) == EFI_DEBUG_CODE) ? \ReportStatusCodeEx(Type,Value,Instance,CallerId,ExtendedDataGuid,ExtendedData,ExtendedDataSize) : \EFI_UNSUPPORTED#endif
最后调用的是ReportStatusCodeEx,该函数在PEI,DXE Runtime 和SMM Driver中都有实现
ReportStatusCodeEx
PEI实现举例:最终调用的是PeiService 的ppi
**/EFI_STATUSEFIAPIReportStatusCodeEx (IN EFI_STATUS_CODE_TYPE Type,IN EFI_STATUS_CODE_VALUE Value,IN UINT32 Instance,IN CONST EFI_GUID *CallerId OPTIONAL,IN CONST EFI_GUID *ExtendedDataGuid OPTIONAL,IN CONST VOID *ExtendedData OPTIONAL,IN UINTN ExtendedDataSize){EFI_STATUS_CODE_DATA *StatusCodeData;UINT64 Buffer[(MAX_EXTENDED_DATA_SIZE / sizeof (UINT64)) + 1];//// If ExtendedData is NULL and ExtendedDataSize is not zero, then ASSERT().//ASSERT (!((ExtendedData == NULL) && (ExtendedDataSize != 0)));//// If ExtendedData is not NULL and ExtendedDataSize is zero, then ASSERT().//ASSERT (!((ExtendedData != NULL) && (ExtendedDataSize == 0)));if (ExtendedDataSize > (MAX_EXTENDED_DATA_SIZE - sizeof (EFI_STATUS_CODE_DATA))) {//// The local variable Buffer not large enough to hold the extended data associated// with the status code being reported.//DEBUG ((DEBUG_ERROR, "Status code extended data is too large to be reported!\n"));return EFI_OUT_OF_RESOURCES;}StatusCodeData = (EFI_STATUS_CODE_DATA *)Buffer;StatusCodeData->HeaderSize = (UINT16)sizeof (EFI_STATUS_CODE_DATA);StatusCodeData->Size = (UINT16)ExtendedDataSize;if (ExtendedDataGuid == NULL) {ExtendedDataGuid = &gEfiStatusCodeSpecificDataGuid;}CopyGuid (&StatusCodeData->Type, ExtendedDataGuid);if (ExtendedData != NULL) {CopyMem (StatusCodeData + 1, ExtendedData, ExtendedDataSize);}if (CallerId == NULL) {CallerId = &gEfiCallerIdGuid;}return InternalReportStatusCode (Type, Value, Instance, CallerId, StatusCodeData);}////// Pei service instance///EFI_PEI_SERVICES gPs = {{PEI_SERVICES_SIGNATURE,PEI_SERVICES_REVISION,sizeof (EFI_PEI_SERVICES),0,0},PeiInstallPpi,PeiReInstallPpi,PeiLocatePpi,PeiNotifyPpi,PeiGetBootMode,PeiSetBootMode,PeiGetHobList,PeiCreateHob,PeiFfsFindNextVolume,PeiFfsFindNextFile,PeiFfsFindSectionData,PeiInstallPeiMemory,PeiAllocatePages,PeiAllocatePool,(EFI_PEI_COPY_MEM)CopyMem,(EFI_PEI_SET_MEM)SetMem,PeiReportStatusCode,PeiResetSystem,&gPeiDefaultCpuIoPpi,&gPeiDefaultPciCfg2Ppi,PeiFfsFindFileByName,PeiFfsGetFileInfo,PeiFfsGetVolumeInfo,PeiRegisterForShadow,PeiFfsFindSectionData3,PeiFfsGetFileInfo2,PeiResetSystem2,PeiFreePages,};
UEFI中 PeiService 中包含的函数
//
// Status Code
//
EFI_PEI_REPORT_STATUS_CODE ReportStatusCode;
PeiServices->PeiReportStatusCode
//// Locate StatusCode Ppi.//Status = PeiServicesLocatePpi (&gEfiPeiStatusCodePpiGuid,0,NULL,(VOID **)&StatusCodePpi);EFI_STATUSInternalReportStatusCode (IN EFI_STATUS_CODE_TYPE Type,IN EFI_STATUS_CODE_VALUE Value,IN UINT32 Instance,IN CONST EFI_GUID *CallerId OPTIONAL,IN EFI_STATUS_CODE_DATA *Data OPTIONAL){CONST EFI_PEI_SERVICES **PeiServices;EFI_STATUS Status;if ((ReportProgressCodeEnabled () && (((Type) & EFI_STATUS_CODE_TYPE_MASK) == EFI_PROGRESS_CODE)) ||(ReportErrorCodeEnabled () && (((Type) & EFI_STATUS_CODE_TYPE_MASK) == EFI_ERROR_CODE)) ||(ReportDebugCodeEnabled () && (((Type) & EFI_STATUS_CODE_TYPE_MASK) == EFI_DEBUG_CODE))){GUID_WITH_MODULENAME GuidWithName;EFI_GUID *CallerGuid;CallerGuid = (EFI_GUID *)CallerId;if (CallerGuid != NULL) {CopyGuid (&GuidWithName.Guid, CallerId);GuidWithName.Signature = REPORT_STATUS_GUID_MODULE_SIGNATURE;GuidWithName.ModuleName = gEfiCallerBaseName;CallerGuid = &GuidWithName.Guid;}PeiServices = GetPeiServicesTablePointer ();Status = (*PeiServices)->ReportStatusCode (PeiServices,Type,Value,Instance,(EFI_GUID *)CallerGuid,Data);if (Status == EFI_NOT_AVAILABLE_YET) {Status = OemHookStatusCodeInitialize ();if (!EFI_ERROR (Status)) {return OemHookStatusCodeReport (Type, Value, Instance, (EFI_GUID *)CallerGuid, Data);}}return Status;}return EFI_UNSUPPORTED;}
3.UefiDebugLibConOut.inf 这个Lib就是开始我们提到的将debug信息打印到Shell 使用的Lib
#DebugLib|MdePkg/Library/UefiDebugLibConOut/UefiDebugLibConOut.inf
MdePkg\Library\UefiDebugLibConOut\DebugLib.c
同样关注不同Lib中的 DebugPrintMarker 实现 了解各DebugLib的区别
#define MAX_DEBUG_MESSAGE_LENGTH 0x100 定义了最大的Buffer size
VOIDDebugPrintMarker (IN UINTN ErrorLevel,IN CONST CHAR8 *Format,IN VA_LIST VaListMarker,IN BASE_LIST BaseListMarker){CHAR16 Buffer[MAX_DEBUG_MESSAGE_LENGTH];if (!mPostEBS) {//// If Format is NULL, then ASSERT().//ASSERT (Format != NULL);//// Check driver debug mask value and global mask//if ((ErrorLevel & GetDebugPrintErrorLevel ()) == 0) {return;}//// Convert the DEBUG() message to a Unicode String//if (BaseListMarker == NULL) {UnicodeVSPrintAsciiFormat (Buffer, sizeof (Buffer), Format, VaListMarker);} else {UnicodeBSPrintAsciiFormat (Buffer, sizeof (Buffer), Format, BaseListMarker);}//// Send the print string to the Console Output device//if ((mDebugST != NULL) && (mDebugST->ConOut != NULL)) {mDebugST->ConOut->OutputString (mDebugST->ConOut, Buffer);}}}
可以看到最后调用的就是 mDebugST->ConOut->OutputString (mDebugST->ConOut, Buffer);
最后EFI Shell下常用的Print 最后调用的都是gRT->ConOut->OutputString ()
*
*/UINTNEFIAPIPrint (IN CONST CHAR16 *Format,...){VA_LIST Marker;UINTN Return;VA_START (Marker, Format);Return = InternalPrint (Format, gST->ConOut, Marker);VA_END (Marker);return Return;}UINTNInternalPrint (IN CONST CHAR16 *Format,IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *Console,IN VA_LIST Marker){EFI_STATUS Status;UINTN Return;CHAR16 *Buffer;UINTN BufferSize;ASSERT (Format != NULL);ASSERT (((UINTN)Format & BIT0) == 0);ASSERT (Console != NULL);BufferSize = (PcdGet32 (PcdUefiLibMaxPrintBufferSize) + 1) * sizeof (CHAR16);Buffer = (CHAR16 *)AllocatePool (BufferSize);ASSERT (Buffer != NULL);Return = UnicodeVSPrint (Buffer, BufferSize, Format, Marker);if ((Console != NULL) && (Return > 0)) {//// To be extra safe make sure Console has been initialized//Status = Console->OutputString (Console, Buffer);if (EFI_ERROR (Status)) {Return = 0;}}FreePool (Buffer);return Return;}
- 除了常用的Debug() 我们还可以使用其他调试手段
(1)ASSERT()
(2)CpuBreakpoint ();
(3)CpuDeadLoop ();
(4)CpuPause ();
ASSERT是一种在程序中用于检查特定条件是否为真的方法。当条件为假时,断言会触发错误处理机制,通常会导致程序中
止或输出错误信息。
HelloWorld2(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE *SystemTable){EFI_STATUS Status;Status=EFI_SUCCESS;UINT8 index=0;ASSERT(index == 1);gST->ConOut->OutputString(gST->ConOut,L"ConOut:Hellow World \n\r");Print(L"Print:Hellow World\n");DEBUG ((DEBUG_ERROR, "Debug:Hellow World\n"));return Status;}
当运行到Assert 就会出现Assert 信息 会打印所在的函数 行数 和 报错条件不满足原因
**/#if !defined (MDEPKG_NDEBUG)#define ASSERT(Expression) \do { \if (DebugAssertEnabled ()) { \if (!(Expression)) { \_ASSERT (Expression); \ANALYZER_UNREACHABLE (); \} \} \} while (FALSE)#else#define ASSERT(Expression)#endif
执行 CpuDeadLoop (); 后会出现Hang机现象 原因是出现循环
现在Shell应该在CpuDeadLoop()处循环等待。
**/VOIDEFIAPICpuDeadLoop (VOID){volatile UINTN Index;for (Index = 0; Index == 0;) {CpuPause ();}}
CpuPause (); 其实是等待 Nop 命令
CpuPausenopnopnopnopnopret
CpuBreakpoint () 是设置断点 后续可以进行单步操作
VOIDEFIAPICpuBreakpoint (VOID){__asm__ __volatile__ ("int $3");}__asm{int 3;}
x86 系列处理器从其第一代产品英特尔 8086 开始就提供了一条专门用来支持调试的指令,即 INT 3。简单地说,这条指令
的目的就是使 CPU 中断(break)到调试器,以供调试者对执行现场进行各种分析。
当我们调试程序时,可以在可能有问题的地方插入一条 INT 3 指令,使 CPU 执行到这一点时停下来。这便是软件调试中经
常用到的断点(breakpoint)功能,因此 INT 3 指令又被称为断点指令。