mirror of
https://github.com/tianocore/edk2
synced 2026-08-27 00:23:19 -04:00
Add the Memory Debug Logging feature MemDebugLogLib library which provides the key MemDebugLogWrite() function. Several versions (i.e. SEC, PEIM, DXE, runtime) of the function are included to provide the proper method to write the debug messages to the memory debug log buffer. The library also provides the core functions to maintain the circular memory debug log buffer. Cc: Gerd Hoffmann <kraxel@redhat.com> Cc: Ard Biesheuvel <ardb+tianocore@kernel.org> Cc: Jiewen Yao <jiewen.yao@intel.com> Signed-off-by: Aaron Young <aaron.young@oracle.com>
297 lines
8.2 KiB
C
297 lines
8.2 KiB
C
/** @file
|
|
Memory Debug Log common defs/funcs to access the memory buffer.
|
|
|
|
Copyright (C) 2025, Oracle and/or its affiliates.
|
|
|
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
|
|
**/
|
|
|
|
#include <PiPei.h>
|
|
#include <PiDxe.h>
|
|
#include <Library/BaseLib.h>
|
|
#include <Library/BaseMemoryLib.h>
|
|
#include <Library/HobLib.h>
|
|
#include <Library/PrintLib.h>
|
|
#include <Library/SynchronizationLib.h>
|
|
#include <Library/MemDebugLogLib.h>
|
|
#include <Library/PcdLib.h>
|
|
|
|
#define MEMDEBUGLOG_COPYSIZE 0x200
|
|
|
|
STATIC
|
|
VOID
|
|
MemDebugLogLockInit (
|
|
IN volatile UINT64 *MemDebugLogLock
|
|
)
|
|
{
|
|
InitializeSpinLock ((SPIN_LOCK *)MemDebugLogLock);
|
|
}
|
|
|
|
STATIC
|
|
VOID
|
|
MemDebugLogLockAcquire (
|
|
IN volatile UINT64 *MemDebugLogLock
|
|
)
|
|
{
|
|
AcquireSpinLock ((SPIN_LOCK *)MemDebugLogLock);
|
|
}
|
|
|
|
STATIC
|
|
VOID
|
|
MemDebugLogLockRelease (
|
|
IN volatile UINT64 *MemDebugLogLock
|
|
)
|
|
{
|
|
ReleaseSpinLock ((SPIN_LOCK *)MemDebugLogLock);
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
MemDebugLogWriteBuffer (
|
|
IN EFI_PHYSICAL_ADDRESS MemDebugLogBufAddr,
|
|
IN CHAR8 *Buffer,
|
|
IN UINTN Length
|
|
)
|
|
{
|
|
volatile UINT64 *MemDebugLogLock;
|
|
MEM_DEBUG_LOG_HDR *MemDebugLogHdr;
|
|
UINTN BufSpaceLeft;
|
|
CHAR8 *BufStart;
|
|
CHAR8 *BufHead;
|
|
CHAR8 *BufTail;
|
|
CHAR8 *BufEnd;
|
|
|
|
//
|
|
// NOTE: we cannot call DEBUG or ASSERT from this function.
|
|
//
|
|
|
|
if (!MemDebugLogBufAddr || !Buffer) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (Length == 0) {
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
MemDebugLogHdr = (MEM_DEBUG_LOG_HDR *)(UINTN)MemDebugLogBufAddr;
|
|
MemDebugLogLock = &(MemDebugLogHdr->MemDebugLogLock);
|
|
|
|
//
|
|
// Validate the header magic before proceeding
|
|
//
|
|
if ((MemDebugLogHdr->Magic1 != MEM_DEBUG_LOG_MAGIC1) ||
|
|
(MemDebugLogHdr->Magic2 != MEM_DEBUG_LOG_MAGIC2))
|
|
{
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
if (Length >= MemDebugLogHdr->DebugLogSize) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
MemDebugLogLockAcquire (MemDebugLogLock);
|
|
|
|
BufStart = (CHAR8 *)(UINTN)(MemDebugLogBufAddr + MemDebugLogHdr->HeaderSize);
|
|
BufEnd = (CHAR8 *)(UINTN)(MemDebugLogBufAddr + MemDebugLogHdr->HeaderSize + MemDebugLogHdr->DebugLogSize) - 1;
|
|
BufHead = BufStart + MemDebugLogHdr->DebugLogHeadOffset;
|
|
BufTail = BufStart + MemDebugLogHdr->DebugLogTailOffset;
|
|
|
|
//
|
|
// Maintain a circular (wrap around) log buffer
|
|
// NOTES:
|
|
// tail always points to next available slot to populate
|
|
// Algorithm to process/display strings from buffer in time order:
|
|
// 1. head==tail indicates empty buffer
|
|
// 2. if (head < tail), process from head (tail-head) bytes
|
|
// 3. if (head > tail), process from head (bufend-head) bytes
|
|
// process from bufstart (tail-bufstart) bytes
|
|
//
|
|
|
|
if ((BufTail + Length) <= BufEnd) {
|
|
//
|
|
// There's enough room from tail to end of the buffer
|
|
//
|
|
CopyMem (BufTail, Buffer, Length);
|
|
//
|
|
// If we have previously wrapped around, need to keep Head updated
|
|
//
|
|
if (BufHead == (BufTail + 1)) {
|
|
BufHead += Length;
|
|
//
|
|
// Check if we need to wrap Head
|
|
//
|
|
if (BufHead > BufEnd) {
|
|
BufHead = BufStart;
|
|
}
|
|
}
|
|
|
|
BufTail += Length;
|
|
} else {
|
|
//
|
|
// We need to wrap around.
|
|
//
|
|
// Fill remaining buffer space with initial part of the string
|
|
//
|
|
BufSpaceLeft = (UINTN)(BufEnd - BufTail + 1);
|
|
CopyMem (BufTail, Buffer, BufSpaceLeft);
|
|
|
|
//
|
|
// Wrap to start of the buffer for the rest of the string
|
|
//
|
|
BufTail = BufStart;
|
|
CopyMem (BufTail, (Buffer + BufSpaceLeft), (Length - BufSpaceLeft));
|
|
BufTail += (Length - BufSpaceLeft);
|
|
BufHead = (BufTail + 1);
|
|
|
|
MemDebugLogHdr->Truncated = 1;
|
|
}
|
|
|
|
//
|
|
// Write the new buffer offsets back to the header
|
|
//
|
|
MemDebugLogHdr->DebugLogHeadOffset = BufHead - BufStart;
|
|
MemDebugLogHdr->DebugLogTailOffset = BufTail - BufStart;
|
|
|
|
MemDebugLogLockRelease (MemDebugLogLock);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
MemDebugLogInit (
|
|
IN EFI_PHYSICAL_ADDRESS MemDebugLogBufAddr,
|
|
UINT32 MemDebugLogBufSize
|
|
)
|
|
{
|
|
MEM_DEBUG_LOG_HDR *MemDebugLogHdr;
|
|
|
|
if (MemDebugLogBufAddr == 0) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
ZeroMem ((VOID *)(UINTN)MemDebugLogBufAddr, MemDebugLogBufSize);
|
|
|
|
MemDebugLogHdr = (MEM_DEBUG_LOG_HDR *)(UINTN)MemDebugLogBufAddr;
|
|
MemDebugLogHdr->Magic1 = MEM_DEBUG_LOG_MAGIC1;
|
|
MemDebugLogHdr->Magic2 = MEM_DEBUG_LOG_MAGIC2;
|
|
MemDebugLogHdr->HeaderSize = sizeof (MEM_DEBUG_LOG_HDR);
|
|
MemDebugLogHdr->DebugLogSize = (MemDebugLogBufSize - MemDebugLogHdr->HeaderSize);
|
|
MemDebugLogHdr->DebugLogHeadOffset = 0;
|
|
MemDebugLogHdr->DebugLogTailOffset = 0;
|
|
MemDebugLogLockInit (&(MemDebugLogHdr->MemDebugLogLock));
|
|
MemDebugLogHdr->Truncated = 0;
|
|
AsciiSPrint (MemDebugLogHdr->FirmwareVersion, 128, "%s", (CHAR16 *)PcdGetPtr (PcdFirmwareVersionString));
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
MemDebugLogCopy (
|
|
IN EFI_PHYSICAL_ADDRESS MemDebugLogBufDestAddr,
|
|
IN EFI_PHYSICAL_ADDRESS MemDebugLogBufSrcAddr
|
|
)
|
|
{
|
|
MEM_DEBUG_LOG_HDR *MemDebugLogSrcHdr;
|
|
MEM_DEBUG_LOG_HDR *MemDebugLogDestHdr;
|
|
CHAR8 *BufStart;
|
|
CHAR8 *BufHead;
|
|
CHAR8 *BufTail;
|
|
CHAR8 *BufEnd;
|
|
CHAR8 *BufPtr;
|
|
|
|
if ((MemDebugLogBufSrcAddr == 0) || (MemDebugLogBufDestAddr == 0)) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
MemDebugLogSrcHdr = (MEM_DEBUG_LOG_HDR *)(UINTN)MemDebugLogBufSrcAddr;
|
|
MemDebugLogDestHdr = (MEM_DEBUG_LOG_HDR *)(UINTN)MemDebugLogBufDestAddr;
|
|
|
|
BufStart = (CHAR8 *)(UINTN)(MemDebugLogBufSrcAddr + MemDebugLogSrcHdr->HeaderSize);
|
|
BufEnd = (CHAR8 *)(UINTN)(MemDebugLogBufSrcAddr + MemDebugLogSrcHdr->HeaderSize + MemDebugLogSrcHdr->DebugLogSize);
|
|
BufHead = BufStart + MemDebugLogSrcHdr->DebugLogHeadOffset;
|
|
BufTail = BufStart + MemDebugLogSrcHdr->DebugLogTailOffset;
|
|
|
|
MemDebugLogDestHdr->Truncated = MemDebugLogSrcHdr->Truncated;
|
|
|
|
if (BufHead == BufTail) {
|
|
//
|
|
// Source Debug Log empty
|
|
//
|
|
return EFI_SUCCESS;
|
|
} else if (BufHead < BufTail) {
|
|
//
|
|
// Source buffer didn't wrap, so copy debug messages
|
|
// from Source buffer (head to tail) to the Dest buffer
|
|
// NOTE: we limit each copy to MEMDEBUGLOG_COPYSIZE
|
|
// to ensure to not copy too much at a time and ensure
|
|
// the dest buffer head/tail pointers are created properly.
|
|
//
|
|
for (BufPtr = BufHead; (BufTail - BufPtr) > MEMDEBUGLOG_COPYSIZE; BufPtr += MEMDEBUGLOG_COPYSIZE) {
|
|
MemDebugLogWriteBuffer (MemDebugLogBufDestAddr, BufPtr, MEMDEBUGLOG_COPYSIZE);
|
|
}
|
|
|
|
//
|
|
// write remaining bytes
|
|
//
|
|
MemDebugLogWriteBuffer (MemDebugLogBufDestAddr, BufPtr, (BufTail - BufPtr));
|
|
} else {
|
|
//
|
|
// Source buffer wrapped.
|
|
// First copy (bufend - head) chars from head to Dest buffer
|
|
//
|
|
for (BufPtr = BufHead; (BufEnd - BufPtr) > MEMDEBUGLOG_COPYSIZE; BufPtr += MEMDEBUGLOG_COPYSIZE) {
|
|
MemDebugLogWriteBuffer (MemDebugLogBufDestAddr, BufPtr, MEMDEBUGLOG_COPYSIZE);
|
|
}
|
|
|
|
//
|
|
// write remaining bytes
|
|
//
|
|
MemDebugLogWriteBuffer (MemDebugLogBufDestAddr, BufPtr, (BufEnd - BufPtr));
|
|
|
|
//
|
|
// Next, copy (bufend - head) chars from start to Dest buffer
|
|
//
|
|
for (BufPtr = BufStart; (BufTail - BufPtr) > MEMDEBUGLOG_COPYSIZE; BufPtr += MEMDEBUGLOG_COPYSIZE) {
|
|
MemDebugLogWriteBuffer (MemDebugLogBufDestAddr, BufPtr, MEMDEBUGLOG_COPYSIZE);
|
|
}
|
|
|
|
//
|
|
// write remaining bytes
|
|
//
|
|
MemDebugLogWriteBuffer (MemDebugLogBufDestAddr, BufPtr, (BufTail - BufPtr));
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
MemDebugLogAddrFromHOB (
|
|
EFI_PHYSICAL_ADDRESS *MemDebugLogBufAddr
|
|
)
|
|
{
|
|
EFI_HOB_GUID_TYPE *GuidHob;
|
|
MEM_DEBUG_LOG_HOB_DATA *HobData;
|
|
|
|
GuidHob = GetFirstGuidHob (&gMemDebugLogHobGuid);
|
|
if (GuidHob == NULL) {
|
|
return EFI_NOT_FOUND;
|
|
} else {
|
|
HobData = (MEM_DEBUG_LOG_HOB_DATA *)GET_GUID_HOB_DATA (GuidHob);
|
|
*MemDebugLogBufAddr = HobData->MemDebugLogBufAddr;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
BOOLEAN
|
|
EFIAPI
|
|
MemDebugLogEnabled (
|
|
VOID
|
|
)
|
|
{
|
|
return TRUE;
|
|
}
|