1.我们调试中常用Debug 打印信息,这些会输出到BIOS串口日志中
cpp
EFI_STATUS
EFIAPI
HelloWorld2(
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()实现
cpp
/**
Macro that calls DebugPrint().
If MDEPKG_NDEBUG is not defined and the DEBUG_PROPERTY_DEBUG_PRINT_ENABLED
bit of PcdDebugProperyMask is set, then this macro passes Expression to
DebugPrint().
@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 进行打印
cpp
_DEBUG (Expression);
#define _DEBUG(Expression) _DEBUG_PRINT Expression
#define _DEBUG_PRINT(PrintLevel, ...) \
do { \
if (DebugPrintLevelEnabled (PrintLevel)) { \
DebugPrint (PrintLevel, ##__VA_ARGS__); \
} \
} while (FALSE)
BOOLEAN
EFIAPI
DebugPrintLevelEnabled (
IN CONST UINTN ErrorLevel
)
{
return (BOOLEAN)((ErrorLevel & PcdGet32 (PcdFixedDebugPrintErrorLevel)) != 0);
}
**/
VOID
EFIAPI
DebugPrint (
IN UINTN ErrorLevel,
IN CONST CHAR8 *Format,
...
)
{
VA_LIST Marker;
VA_START (Marker, Format);
DebugVPrint (ErrorLevel, Format, Marker);
VA_END (Marker);
}
VOID
EFIAPI
DebugVPrint (
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.inf
MdePkg\Library\BaseDebugLibSerialPort\DebugLib.c
VOID
DebugPrintMarker (
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.inf
MdeModulePkg\Library\PeiDxeDebugLibReportStatusCode\DebugLib.c
**/
VOID
DebugPrintMarker (
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
cpp
**/
EFI_STATUS
EFIAPI
ReportStatusCodeEx (
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
cpp
//
// Locate StatusCode Ppi.
//
Status = PeiServicesLocatePpi (
&gEfiPeiStatusCodePpiGuid,
0,
NULL,
(VOID **)&StatusCodePpi
);
EFI_STATUS
InternalReportStatusCode (
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
cpp
VOID
DebugPrintMarker (
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 ()
*
cpp
*/
UINTN
EFIAPI
Print (
IN CONST CHAR16 *Format,
...
)
{
VA_LIST Marker;
UINTN Return;
VA_START (Marker, Format);
Return = InternalPrint (Format, gST->ConOut, Marker);
VA_END (Marker);
return Return;
}
UINTN
InternalPrint (
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是一种在程序中用于检查特定条件是否为真的方法。当条件为假时,断言会触发错误处理机制,通常会导致程序中
止或输出错误信息。
cpp
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 信息 会打印所在的函数 行数 和 报错条件不满足原因
cpp
**/
#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()处循环等待。
cpp
**/
VOID
EFIAPI
CpuDeadLoop (
VOID
)
{
volatile UINTN Index;
for (Index = 0; Index == 0;) {
CpuPause ();
}
}
CpuPause (); 其实是等待 Nop 命令
bash
CpuPause
nop
nop
nop
nop
nop
ret
CpuBreakpoint () 是设置断点 后续可以进行单步操作
cpp
VOID
EFIAPI
CpuBreakpoint (
VOID
)
{
__asm__ __volatile__ ("int $3");
}
__asm{
int 3;
}
x86 系列处理器从其第一代产品英特尔 8086 开始就提供了一条专门用来支持调试的指令,即 INT 3。简单地说,这条指令
的目的就是使 CPU 中断(break)到调试器,以供调试者对执行现场进行各种分析。
当我们调试程序时,可以在可能有问题的地方插入一条 INT 3 指令,使 CPU 执行到这一点时停下来。这便是软件调试中经
常用到的断点(breakpoint)功能,因此 INT 3 指令又被称为断点指令。