| /** @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); | |
| } |