/** @file | |
Debug Library based on report status code library. | |
Note that if the debug message length is larger than the maximum allowable | |
record length, then the debug message will be ignored directly. | |
Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include <PiPei.h> | |
#include <Guid/StatusCodeDataTypeId.h> | |
#include <Guid/StatusCodeDataTypeDebug.h> | |
#include <Library/DebugLib.h> | |
#include <Library/BaseLib.h> | |
#include <Library/BaseMemoryLib.h> | |
#include <Library/ReportStatusCodeLib.h> | |
#include <Library/PcdLib.h> | |
#include <Library/DebugPrintErrorLevelLib.h> | |
// | |
// VA_LIST can not initialize to NULL for all compiler, so we use this to | |
// indicate a null VA_LIST | |
// | |
VA_LIST mVaListNull; | |
/** | |
Prints a debug message to the debug output device if the specified error level is enabled. | |
If any bit in ErrorLevel is also set in DebugPrintErrorLevelLib function | |
GetDebugPrintErrorLevel (), then print the message specified by Format and the | |
associated variable argument list to the debug output device. | |
If Format is NULL, then ASSERT(). | |
If the length of the message string specificed by Format is larger than the maximum allowable | |
record length, then directly return and not print it. | |
@param ErrorLevel The error level of the debug message. | |
@param Format Format string for the debug message to print. | |
@param ... Variable argument list whose contents are accessed | |
based on the format string specified by Format. | |
**/ | |
VOID | |
EFIAPI | |
DebugPrint ( | |
IN UINTN ErrorLevel, | |
IN CONST CHAR8 *Format, | |
... | |
) | |
{ | |
VA_LIST Marker; | |
VA_START (Marker, Format); | |
DebugVPrint (ErrorLevel, Format, Marker); | |
VA_END (Marker); | |
} | |
/** | |
Prints a debug message to the debug output device if the specified | |
error level is enabled base on Null-terminated format string and a | |
VA_LIST argument list or a BASE_LIST argument list. | |
If any bit in ErrorLevel is also set in DebugPrintErrorLevelLib function | |
GetDebugPrintErrorLevel (), then print the message specified by Format and | |
the associated variable argument list to the debug output device. | |
Only one list type is used. | |
If BaseListMarker == NULL, then use VaListMarker. | |
Otherwise use BaseListMarker and the VaListMarker should be initilized as | |
mVaListNull. | |
If Format is NULL, then ASSERT(). | |
@param ErrorLevel The error level of the debug message. | |
@param Format Format string for the debug message to print. | |
@param VaListMarker VA_LIST marker for the variable argument list. | |
@param BaseListMarker BASE_LIST marker for the variable argument list. | |
**/ | |
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 | |
); | |
} | |
/** | |
Prints a debug message to the debug output device if the specified | |
error level is enabled. | |
If any bit in ErrorLevel is also set in DebugPrintErrorLevelLib function | |
GetDebugPrintErrorLevel (), then print the message specified by Format and | |
the associated variable argument list to the debug output device. | |
If Format is NULL, then ASSERT(). | |
@param ErrorLevel The error level of the debug message. | |
@param Format Format string for the debug message to print. | |
@param VaListMarker VA_LIST marker for the variable argument list. | |
**/ | |
VOID | |
EFIAPI | |
DebugVPrint ( | |
IN UINTN ErrorLevel, | |
IN CONST CHAR8 *Format, | |
IN VA_LIST VaListMarker | |
) | |
{ | |
DebugPrintMarker (ErrorLevel, Format, VaListMarker, NULL); | |
} | |
/** | |
Prints a debug message to the debug output device if the specified | |
error level is enabled. | |
This function use BASE_LIST which would provide a more compatible | |
service than VA_LIST. | |
If any bit in ErrorLevel is also set in DebugPrintErrorLevelLib function | |
GetDebugPrintErrorLevel (), then print the message specified by Format and | |
the associated variable argument list to the debug output device. | |
If Format is NULL, then ASSERT(). | |
@param ErrorLevel The error level of the debug message. | |
@param Format Format string for the debug message to print. | |
@param BaseListMarker BASE_LIST marker for the variable argument list. | |
**/ | |
VOID | |
EFIAPI | |
DebugBPrint ( | |
IN UINTN ErrorLevel, | |
IN CONST CHAR8 *Format, | |
IN BASE_LIST BaseListMarker | |
) | |
{ | |
DebugPrintMarker (ErrorLevel, Format, mVaListNull, BaseListMarker); | |
} | |
/** | |
Prints an assert message containing a filename, line number, and description. | |
This may be followed by a breakpoint or a dead loop. | |
Print a message of the form "ASSERT <FileName>(<LineNumber>): <Description>\n" | |
to the debug output device. If DEBUG_PROPERTY_ASSERT_BREAKPOINT_ENABLED bit of | |
PcdDebugProperyMask is set then CpuBreakpoint() is called. Otherwise, if | |
DEBUG_PROPERTY_ASSERT_DEADLOOP_ENABLED bit of PcdDebugProperyMask is set then | |
CpuDeadLoop() is called. If neither of these bits are set, then this function | |
returns immediately after the message is printed to the debug output device. | |
DebugAssert() must actively prevent recursion. If DebugAssert() is called while | |
processing another DebugAssert(), then DebugAssert() must return immediately. | |
If FileName is NULL, then a <FileName> string of "(NULL) Filename" is printed. | |
If Description is NULL, then a <Description> string of "(NULL) Description" is printed. | |
@param FileName Pointer to the name of the source file that generated the assert condition. | |
@param LineNumber The line number in the source file that generated the assert condition | |
@param Description Pointer to the description of the assert condition. | |
**/ | |
VOID | |
EFIAPI | |
DebugAssert ( | |
IN CONST CHAR8 *FileName, | |
IN UINTN LineNumber, | |
IN CONST CHAR8 *Description | |
) | |
{ | |
UINT64 Buffer[EFI_STATUS_CODE_DATA_MAX_SIZE / sizeof (UINT64)]; | |
EFI_DEBUG_ASSERT_DATA *AssertData; | |
UINTN HeaderSize; | |
UINTN TotalSize; | |
CHAR8 *Temp; | |
UINTN ModuleNameSize; | |
UINTN FileNameSize; | |
UINTN DescriptionSize; | |
// | |
// Get string size | |
// | |
HeaderSize = sizeof (EFI_DEBUG_ASSERT_DATA); | |
// | |
// Compute string size of module name enclosed by [] | |
// | |
ModuleNameSize = 2 + AsciiStrSize (gEfiCallerBaseName); | |
FileNameSize = AsciiStrSize (FileName); | |
DescriptionSize = AsciiStrSize (Description); | |
// | |
// Make sure it will all fit in the passed in buffer. | |
// | |
if (HeaderSize + ModuleNameSize + FileNameSize + DescriptionSize > sizeof (Buffer)) { | |
// | |
// remove module name if it's too long to be filled into buffer | |
// | |
ModuleNameSize = 0; | |
if (HeaderSize + FileNameSize + DescriptionSize > sizeof (Buffer)) { | |
// | |
// FileName + Description is too long to be filled into buffer. | |
// | |
if (HeaderSize + FileNameSize < sizeof (Buffer)) { | |
// | |
// Description has enough buffer to be truncated. | |
// | |
DescriptionSize = sizeof (Buffer) - HeaderSize - FileNameSize; | |
} else { | |
// | |
// FileName is too long to be filled into buffer. | |
// FileName will be truncated. Reserved one byte for Description NULL terminator. | |
// | |
DescriptionSize = 1; | |
FileNameSize = sizeof (Buffer) - HeaderSize - DescriptionSize; | |
} | |
} | |
} | |
// | |
// Fill in EFI_DEBUG_ASSERT_DATA | |
// | |
AssertData = (EFI_DEBUG_ASSERT_DATA *)Buffer; | |
AssertData->LineNumber = (UINT32)LineNumber; | |
TotalSize = sizeof (EFI_DEBUG_ASSERT_DATA); | |
Temp = (CHAR8 *)(AssertData + 1); | |
// | |
// Copy Ascii [ModuleName]. | |
// | |
if (ModuleNameSize != 0) { | |
CopyMem (Temp, "[", 1); | |
CopyMem (Temp + 1, gEfiCallerBaseName, ModuleNameSize - 3); | |
CopyMem (Temp + ModuleNameSize - 2, "] ", 2); | |
} | |
// | |
// Copy Ascii FileName including NULL terminator. | |
// | |
Temp = CopyMem (Temp + ModuleNameSize, FileName, FileNameSize); | |
Temp[FileNameSize - 1] = 0; | |
TotalSize += (ModuleNameSize + FileNameSize); | |
// | |
// Copy Ascii Description include NULL terminator. | |
// | |
Temp = CopyMem (Temp + FileNameSize, Description, DescriptionSize); | |
Temp[DescriptionSize - 1] = 0; | |
TotalSize += DescriptionSize; | |
REPORT_STATUS_CODE_EX ( | |
(EFI_ERROR_CODE | EFI_ERROR_UNRECOVERED), | |
(EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_EC_ILLEGAL_SOFTWARE_STATE), | |
0, | |
NULL, | |
NULL, | |
AssertData, | |
TotalSize | |
); | |
// | |
// Generate a Breakpoint, DeadLoop, or NOP based on PCD settings | |
// | |
if ((PcdGet8 (PcdDebugPropertyMask) & DEBUG_PROPERTY_ASSERT_BREAKPOINT_ENABLED) != 0) { | |
CpuBreakpoint (); | |
} else if ((PcdGet8 (PcdDebugPropertyMask) & DEBUG_PROPERTY_ASSERT_DEADLOOP_ENABLED) != 0) { | |
CpuDeadLoop (); | |
} | |
} | |
/** | |
Fills a target buffer with PcdDebugClearMemoryValue, and returns the target buffer. | |
This function fills Length bytes of Buffer with the value specified by | |
PcdDebugClearMemoryValue, and returns Buffer. | |
If Buffer is NULL, then ASSERT(). | |
If Length is greater than (MAX_ADDRESS - Buffer + 1), then ASSERT(). | |
@param Buffer Pointer to the target buffer to be filled with PcdDebugClearMemoryValue. | |
@param Length Number of bytes in Buffer to fill with zeros PcdDebugClearMemoryValue. | |
@return Buffer Pointer to the target buffer filled with PcdDebugClearMemoryValue. | |
**/ | |
VOID * | |
EFIAPI | |
DebugClearMemory ( | |
OUT VOID *Buffer, | |
IN UINTN Length | |
) | |
{ | |
ASSERT (Buffer != NULL); | |
return SetMem (Buffer, Length, PcdGet8 (PcdDebugClearMemoryValue)); | |
} | |
/** | |
Returns TRUE if ASSERT() macros are enabled. | |
This function returns TRUE if the DEBUG_PROPERTY_DEBUG_ASSERT_ENABLED bit of | |
PcdDebugProperyMask is set. Otherwise FALSE is returned. | |
@retval TRUE The DEBUG_PROPERTY_DEBUG_ASSERT_ENABLED bit of PcdDebugProperyMask is set. | |
@retval FALSE The DEBUG_PROPERTY_DEBUG_ASSERT_ENABLED bit of PcdDebugProperyMask is clear. | |
**/ | |
BOOLEAN | |
EFIAPI | |
DebugAssertEnabled ( | |
VOID | |
) | |
{ | |
return (BOOLEAN)((PcdGet8 (PcdDebugPropertyMask) & DEBUG_PROPERTY_DEBUG_ASSERT_ENABLED) != 0); | |
} | |
/** | |
Returns TRUE if DEBUG() macros are enabled. | |
This function returns TRUE if the DEBUG_PROPERTY_DEBUG_PRINT_ENABLED bit of | |
PcdDebugProperyMask is set. Otherwise FALSE is returned. | |
@retval TRUE The DEBUG_PROPERTY_DEBUG_PRINT_ENABLED bit of PcdDebugProperyMask is set. | |
@retval FALSE The DEBUG_PROPERTY_DEBUG_PRINT_ENABLED bit of PcdDebugProperyMask is clear. | |
**/ | |
BOOLEAN | |
EFIAPI | |
DebugPrintEnabled ( | |
VOID | |
) | |
{ | |
return (BOOLEAN)((PcdGet8 (PcdDebugPropertyMask) & DEBUG_PROPERTY_DEBUG_PRINT_ENABLED) != 0); | |
} | |
/** | |
Returns TRUE if DEBUG_CODE() macros are enabled. | |
This function returns TRUE if the DEBUG_PROPERTY_DEBUG_CODE_ENABLED bit of | |
PcdDebugProperyMask is set. Otherwise FALSE is returned. | |
@retval TRUE The DEBUG_PROPERTY_DEBUG_CODE_ENABLED bit of PcdDebugProperyMask is set. | |
@retval FALSE The DEBUG_PROPERTY_DEBUG_CODE_ENABLED bit of PcdDebugProperyMask is clear. | |
**/ | |
BOOLEAN | |
EFIAPI | |
DebugCodeEnabled ( | |
VOID | |
) | |
{ | |
return (BOOLEAN)((PcdGet8 (PcdDebugPropertyMask) & DEBUG_PROPERTY_DEBUG_CODE_ENABLED) != 0); | |
} | |
/** | |
Returns TRUE if DEBUG_CLEAR_MEMORY() macro is enabled. | |
This function returns TRUE if the DEBUG_PROPERTY_CLEAR_MEMORY_ENABLED bit of | |
PcdDebugProperyMask is set. Otherwise FALSE is returned. | |
@retval TRUE The DEBUG_PROPERTY_CLEAR_MEMORY_ENABLED bit of PcdDebugProperyMask is set. | |
@retval FALSE The DEBUG_PROPERTY_CLEAR_MEMORY_ENABLED bit of PcdDebugProperyMask is clear. | |
**/ | |
BOOLEAN | |
EFIAPI | |
DebugClearMemoryEnabled ( | |
VOID | |
) | |
{ | |
return (BOOLEAN)((PcdGet8 (PcdDebugPropertyMask) & DEBUG_PROPERTY_CLEAR_MEMORY_ENABLED) != 0); | |
} | |
/** | |
Returns TRUE if any one of the bit is set both in ErrorLevel and PcdFixedDebugPrintErrorLevel. | |
This function compares the bit mask of ErrorLevel and PcdFixedDebugPrintErrorLevel. | |
@retval TRUE Current ErrorLevel is supported. | |
@retval FALSE Current ErrorLevel is not supported. | |
**/ | |
BOOLEAN | |
EFIAPI | |
DebugPrintLevelEnabled ( | |
IN CONST UINTN ErrorLevel | |
) | |
{ | |
return (BOOLEAN)((ErrorLevel & PcdGet32 (PcdFixedDebugPrintErrorLevel)) != 0); | |
} |