edk2/OvmfPkg/Library/MemDebugLogLib/MemDebugLogCommon.c
Aaron Young ba05ea83b7 OvmfPkg: Add OVMF Memory Debug Logging MemDebugLogLib library
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>
2025-06-13 22:19:39 +00:00

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;
}