diff --git a/OvmfPkg/Include/Library/MemDebugLogLib.h b/OvmfPkg/Include/Library/MemDebugLogLib.h new file mode 100644 index 0000000000..6dde585574 --- /dev/null +++ b/OvmfPkg/Include/Library/MemDebugLogLib.h @@ -0,0 +1,214 @@ +/** @file + Interface functions for the Memory Debug Log Library. + + Copyright (C) 2025, Oracle and/or its affiliates. + + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef _MEM_DEBUG_LOG_LIB_H_ +#define _MEM_DEBUG_LOG_LIB_H_ + +#include +#include + +// +// Cap max buffer at 2MB (0x200 4K pages) +// +#define MAX_MEM_DEBUG_LOG_PAGES 0x200 + +#define MEM_DEBUG_LOG_MAGIC1 0x3167646d666d766f // "ovmfmdg1" +#define MEM_DEBUG_LOG_MAGIC2 0x3267646d666d766f // "ovmfmdg2" + +#pragma pack(1) +// +// Mem Debug Log buffer header. +// The Log buffer is circular. Only the most +// recent messages are retained. Older messages +// will be discarded if the buffer overflows. +// The Debug Log starts just after the header. +// +typedef struct { + // + // Magic values + // These fields are used by tools to locate the buffer in + // memory. These MUST be the first two fields of the structure. + // Use a 128 bit Magic to vastly reduce the possibility of + // a collision with random data in memory. + UINT64 Magic1; + UINT64 Magic2; + // + // Header Size + // This MUST be the third field of the structure + // + UINT64 HeaderSize; + // + // Debug log size (minus header) + // + UINT64 DebugLogSize; + // + // Protect the log from potential MP access (by APs during + // vCPU init) to maintain integrity of the Head/Tail Offsets. + // NOTE: MemDebugLogLock is used as a SPIN_LOCK (which is type + // UINTN). Thus, we declared it as a UINT64 to ensure a + // consistent structure size. + // + volatile UINT64 MemDebugLogLock; + // + // Debug log head offset + // + UINT64 DebugLogHeadOffset; + // + // Debug log tail offset + // + UINT64 DebugLogTailOffset; + // + // Flag to indicate if the buffer wrapped and was thus truncated. + // + UINT64 Truncated; + // + // Firmware Build Version (PcdFirmwareVersionString) + // + CHAR8 FirmwareVersion[128]; +} MEM_DEBUG_LOG_HDR; + +// +// HOB used to pass the mem debug log buffer addr from PEI to DXE +// +typedef struct { + EFI_PHYSICAL_ADDRESS MemDebugLogBufAddr; +} MEM_DEBUG_LOG_HOB_DATA; + +#pragma pack() + +/** + Write a CHAR8 string to the memory debug log. + This is the interface function used by DebugLib. + There are several versions for each boot + phase (i.e. SEC, PEI, DXE, Runtime). + Each version will obtain the proper memory debug log + buffer address and call MemDebugLogWriteBuffer(). + + @param[in] Buffer The buffer containing the string of CHAR8s + + @param[in] Length The buffer length (number of CHAR8s) + not including the NULL terminator byte. + + @retval RETURN_SUCCESS String succcessfully written to the memory log buffer. + + @retval RETURN_NOT_FOUND Memory log buffer is not properly initialized. + + @retval EFI_INVALID_PARAMETER Invalid input parameters. +**/ +EFI_STATUS +EFIAPI +MemDebugLogWrite ( + IN CHAR8 *Buffer, + IN UINTN Length + ); + +/** + Return the memory debug log buffer size (in pages). + This function is implemented by PEIM version of + MemDebugLogLib only. + + @retval UINT32 Buffer size in pages +**/ +UINT32 +EFIAPI +MemDebugLogPages ( + VOID + ); + +/** + Write a CHAR8 string to a memory debug log circular + buffer located at the given address. + + @param MemDebugLogBufAddr Address of the memory debug log buffer. + + @param Buffer Pointer to a CHAR8 string to write to the + debug log buffer. + + @param Length Length of the CHAR8 string to write to the + debug log buffer. Not including NULL terminator + byte. + + @retval RETURN_SUCCESS String succcessfully written to the memory log buffer. + + @retval RETURN_NOT_FOUND Memory log buffer is not properly initialized. + + @retval EFI_INVALID_PARAMETER Invalid input parameters. +**/ +EFI_STATUS +EFIAPI +MemDebugLogWriteBuffer ( + IN EFI_PHYSICAL_ADDRESS MemDebugLogBufAddr, + IN CHAR8 *Buffer, + IN UINTN Length + ); + +/** + Initialize the memory debug log buffer header + + @param MemDebugLogBufAddr Address of the memory debug log buffer. + + @param MemDebugLogBufSize Size of the memory debug log buffer. + + @retval RETURN_SUCCESS Log buffer successfully initialized. + + @retval EFI_INVALID_PARAMETER Invalid input parameters. +**/ +EFI_STATUS +EFIAPI +MemDebugLogInit ( + IN EFI_PHYSICAL_ADDRESS MemDebugLogBufAddr, + UINT32 MemDebugLogBufSize + ); + +/** + Copy the memory debug log buffer + + @param MemDebugLogBufDestAddr Address of destination memory debug log buffer. + + @param MemDebugLogBufSrcAddr Address of source memory debug log buffer. + + @retval RETURN_SUCCESS Log buffer successfuly copied. + + @retval EFI_INVALID_PARAMETER Invalid input parameters. +**/ +EFI_STATUS +EFIAPI +MemDebugLogCopy ( + IN EFI_PHYSICAL_ADDRESS MemDebugLogBufDestAddr, + IN EFI_PHYSICAL_ADDRESS MemDebugLogBufSrcAddr + ); + +/** + Obtain the Memory Debug Log Buffer Addr from the HOB + + @param MemDebugLogBufAddr Address of memory debug log buffer. + + @retval RETURN_SUCCESS Log buffer address successfuly obtained. + + @retval EFI_NOT_FOUND HOB not found. +**/ +EFI_STATUS +EFIAPI +MemDebugLogAddrFromHOB ( + EFI_PHYSICAL_ADDRESS *MemDebugLogBufAddr + ); + +/** + Return whether the Memory Debug Logging feature is enabled + + @retval TRUE Feature is enabled + + @retval FALSE Feature is not enabled +**/ +BOOLEAN +EFIAPI +MemDebugLogEnabled ( + VOID + ); + +#endif // _MEM_DEBUG_LOG_LIB_H_ diff --git a/OvmfPkg/Library/MemDebugLogLib/MemDebugLogCommon.c b/OvmfPkg/Library/MemDebugLogLib/MemDebugLogCommon.c new file mode 100644 index 0000000000..8c9ce61cb6 --- /dev/null +++ b/OvmfPkg/Library/MemDebugLogLib/MemDebugLogCommon.c @@ -0,0 +1,297 @@ +/** @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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/OvmfPkg/Library/MemDebugLogLib/MemDebugLogDxe.c b/OvmfPkg/Library/MemDebugLogLib/MemDebugLogDxe.c new file mode 100644 index 0000000000..9655257315 --- /dev/null +++ b/OvmfPkg/Library/MemDebugLogLib/MemDebugLogDxe.c @@ -0,0 +1,44 @@ +/** @file + * + Memory Debug Log Library - DXE/Smm + + Copyright (C) 2025, Oracle and/or its affiliates. + + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include +#include + +EFI_PHYSICAL_ADDRESS mMemDebugLogBufAddr; +BOOLEAN mMemDebugLogBufAddrInit; + +EFI_STATUS +EFIAPI +MemDebugLogWrite ( + IN CHAR8 *Buffer, + IN UINTN Length + ) +{ + EFI_STATUS Status; + + if (!mMemDebugLogBufAddrInit) { + // + // Obtain the Memory Debug Log buffer addr from HOB + // + Status = MemDebugLogAddrFromHOB (&mMemDebugLogBufAddr); + if (EFI_ERROR (Status)) { + mMemDebugLogBufAddr = 0; + } + + mMemDebugLogBufAddrInit = TRUE; + } + + if (mMemDebugLogBufAddr) { + Status = MemDebugLogWriteBuffer (mMemDebugLogBufAddr, Buffer, Length); + } else { + Status = EFI_NOT_FOUND; + } + + return Status; +} diff --git a/OvmfPkg/Library/MemDebugLogLib/MemDebugLogDxeLib.inf b/OvmfPkg/Library/MemDebugLogLib/MemDebugLogDxeLib.inf new file mode 100644 index 0000000000..edb405dac1 --- /dev/null +++ b/OvmfPkg/Library/MemDebugLogLib/MemDebugLogDxeLib.inf @@ -0,0 +1,37 @@ +## @file +# Instance of MemDebugLog Library for DXE/Smm +# +# Copyright (C) 2025, Oracle and/or its affiliates. +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = MemDebugLogLib + FILE_GUID = 4988621E-8EE8-4D27-862F-EB98BD8F17E6 + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = MemDebugLogLib|DXE_CORE DXE_DRIVER UEFI_DRIVER UEFI_APPLICATION SMM_CORE DXE_SMM_DRIVER + + +[Sources] + MemDebugLogDxe.c + MemDebugLogCommon.c + +[Packages] + MdeModulePkg/MdeModulePkg.dec + MdePkg/MdePkg.dec + OvmfPkg/OvmfPkg.dec + +[LibraryClasses] + HobLib + SynchronizationLib + +[Guids] + gMemDebugLogHobGuid ## CONSUMES + +[FixedPcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdFirmwareVersionString ## CONSUMES diff --git a/OvmfPkg/Library/MemDebugLogLib/MemDebugLogLibNull.inf b/OvmfPkg/Library/MemDebugLogLib/MemDebugLogLibNull.inf new file mode 100644 index 0000000000..33f8dfee9c --- /dev/null +++ b/OvmfPkg/Library/MemDebugLogLib/MemDebugLogLibNull.inf @@ -0,0 +1,31 @@ +## @file +# Null Instance of MemDebugLog Library +# +# Copyright (C) 2025, Oracle and/or its affiliates. +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = MemDebugLogLibNull + FILE_GUID = 11b4523c-2c30-44f7-9dee-e6d59eef3d04 + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = MemDebugLogLib|SEC PEI_CORE PEIM DXE_CORE DXE_DRIVER DXE_RUNTIME_DRIVER UEFI_APPLICATION UEFI_DRIVER SMM_CORE DXE_SMM_DRIVER + + +[Sources] + MemDebugLogNull.c + +[Packages] + MdePkg/MdePkg.dec + OvmfPkg/OvmfPkg.dec + +[LibraryClasses] + +[FixedPcd] + +[Guids] diff --git a/OvmfPkg/Library/MemDebugLogLib/MemDebugLogNull.c b/OvmfPkg/Library/MemDebugLogLib/MemDebugLogNull.c new file mode 100644 index 0000000000..59bb7c87a0 --- /dev/null +++ b/OvmfPkg/Library/MemDebugLogLib/MemDebugLogNull.c @@ -0,0 +1,40 @@ +/** @file + * + Memory Debug Log Library - Null. + + Copyright (C) 2025, Oracle and/or its affiliates. + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include + +EFI_STATUS +EFIAPI +MemDebugLogWrite ( + IN CHAR8 *Buffer, + IN UINTN Length + ) +{ + // Null Instance - NOP + return EFI_SUCCESS; +} + +UINT32 +EFIAPI +MemDebugLogPages ( + VOID + ) +{ + return 0; +} + +BOOLEAN +EFIAPI +MemDebugLogEnabled ( + VOID + ) +{ + return FALSE; +} diff --git a/OvmfPkg/Library/MemDebugLogLib/MemDebugLogPei.c b/OvmfPkg/Library/MemDebugLogLib/MemDebugLogPei.c new file mode 100644 index 0000000000..cbdab6c22f --- /dev/null +++ b/OvmfPkg/Library/MemDebugLogLib/MemDebugLogPei.c @@ -0,0 +1,89 @@ +/** @file + * + Memory Debug Log Library - PEI Phase + + Copyright (C) 2025, Oracle and/or its affiliates. + + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include +#include +#include +#include + +EFI_PHYSICAL_ADDRESS mMemDebugLogBufAddr; +BOOLEAN mMemDebugLogBufAddrInit; + +EFI_STATUS +EFIAPI +MemDebugLogWrite ( + IN CHAR8 *Buffer, + IN UINTN Length + ) +{ + EFI_STATUS Status; + + if (!mMemDebugLogBufAddrInit) { + // + // Obtain the Memory Debug Log buffer addr from HOB + // NOTE: This is expected to fail until the HOB is created. + // + Status = MemDebugLogAddrFromHOB (&mMemDebugLogBufAddr); + if (EFI_ERROR (Status)) { + mMemDebugLogBufAddr = 0; + } else { + mMemDebugLogBufAddrInit = TRUE; + } + } + + if (mMemDebugLogBufAddr) { + Status = MemDebugLogWriteBuffer (mMemDebugLogBufAddr, Buffer, Length); + } else { + // + // HOB has not yet been created, so + // write to the early debug log buffer. + // + if (FixedPcdGet32 (PcdOvmfEarlyMemDebugLogBase) != 0x0) { + Status = MemDebugLogWriteBuffer ( + (EFI_PHYSICAL_ADDRESS)(UINTN)FixedPcdGet32 (PcdOvmfEarlyMemDebugLogBase), + Buffer, + Length + ); + } else { + Status = EFI_NOT_FOUND; + } + } + + return Status; +} + +UINT32 +EFIAPI +MemDebugLogPages ( + VOID + ) +{ + UINT32 FwCfg_MemDebugLogPages; + UINT32 MemDebugLogBufPages; + EFI_STATUS Status; + + // + // Allow FwCfg value to override Pcd. + // + Status = QemuFwCfgParseUint32 ("opt/ovmf/MemDebugLogPages", TRUE, &FwCfg_MemDebugLogPages); + if (Status == EFI_SUCCESS) { + MemDebugLogBufPages = FwCfg_MemDebugLogPages; + } else { + MemDebugLogBufPages = FixedPcdGet32 (PcdMemDebugLogPages); + } + + // + // Cap max memory debug log size at MAX_MEM_DEBUG_LOG_PAGES + // + if (MemDebugLogBufPages > MAX_MEM_DEBUG_LOG_PAGES) { + MemDebugLogBufPages = MAX_MEM_DEBUG_LOG_PAGES; + } + + return MemDebugLogBufPages; +} diff --git a/OvmfPkg/Library/MemDebugLogLib/MemDebugLogPeiCore.c b/OvmfPkg/Library/MemDebugLogLib/MemDebugLogPeiCore.c new file mode 100644 index 0000000000..1e95d65aaa --- /dev/null +++ b/OvmfPkg/Library/MemDebugLogLib/MemDebugLogPeiCore.c @@ -0,0 +1,58 @@ +/** @file + * + Memory Debug Log Library - PEI Core + + Copyright (C) 2025, Oracle and/or its affiliates. + + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include +#include +#include + +EFI_PHYSICAL_ADDRESS mMemDebugLogBufAddr; +BOOLEAN mMemDebugLogBufAddrInit; + +EFI_STATUS +EFIAPI +MemDebugLogWrite ( + IN CHAR8 *Buffer, + IN UINTN Length + ) +{ + EFI_STATUS Status; + + if (!mMemDebugLogBufAddrInit) { + // + // Obtain the Memory Debug Log buffer addr from HOB + // NOTE: This is expected to fail until the HOB is created. + // + Status = MemDebugLogAddrFromHOB (&mMemDebugLogBufAddr); + if (EFI_ERROR (Status)) { + mMemDebugLogBufAddr = 0; + } else { + mMemDebugLogBufAddrInit = TRUE; + } + } + + if (mMemDebugLogBufAddr) { + Status = MemDebugLogWriteBuffer (mMemDebugLogBufAddr, Buffer, Length); + } else { + // + // HOB has not yet been created, so + // write to the early debug log buffer. + // + if (FixedPcdGet32 (PcdOvmfEarlyMemDebugLogBase) != 0x0) { + Status = MemDebugLogWriteBuffer ( + (EFI_PHYSICAL_ADDRESS)(UINTN)FixedPcdGet32 (PcdOvmfEarlyMemDebugLogBase), + Buffer, + Length + ); + } else { + Status = EFI_NOT_FOUND; + } + } + + return Status; +} diff --git a/OvmfPkg/Library/MemDebugLogLib/MemDebugLogPeiCoreLib.inf b/OvmfPkg/Library/MemDebugLogLib/MemDebugLogPeiCoreLib.inf new file mode 100644 index 0000000000..56908caa5a --- /dev/null +++ b/OvmfPkg/Library/MemDebugLogLib/MemDebugLogPeiCoreLib.inf @@ -0,0 +1,40 @@ +## @file +# Instance of MemDebugLog Library for PEI phase +# +# Copyright (C) 2025, Oracle and/or its affiliates. +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = MemDebugLogLib + FILE_GUID = EEAF8A01-167A-4222-A647-80EB16AEEC69 + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = MemDebugLogLib|PEI_CORE + + +[Sources] + MemDebugLogPeiCore.c + MemDebugLogCommon.c + +[Packages] + MdeModulePkg/MdeModulePkg.dec + MdePkg/MdePkg.dec + OvmfPkg/OvmfPkg.dec + +[LibraryClasses] + SynchronizationLib + +[Guids] + gMemDebugLogHobGuid + +[Ppis] + +[FixedPcd] + gUefiOvmfPkgTokenSpaceGuid.PcdOvmfEarlyMemDebugLogBase + gUefiOvmfPkgTokenSpaceGuid.PcdOvmfEarlyMemDebugLogSize + gEfiMdeModulePkgTokenSpaceGuid.PcdFirmwareVersionString ## CONSUMES diff --git a/OvmfPkg/Library/MemDebugLogLib/MemDebugLogPeiLib.inf b/OvmfPkg/Library/MemDebugLogLib/MemDebugLogPeiLib.inf new file mode 100644 index 0000000000..18f06e45b3 --- /dev/null +++ b/OvmfPkg/Library/MemDebugLogLib/MemDebugLogPeiLib.inf @@ -0,0 +1,49 @@ +## @file +# Instance of MemDebugLog Library for PEI phase +# +# Copyright (C) 2025, Oracle and/or its affiliates. +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = MemDebugLogLib + FILE_GUID = D473DE36-0D8A-4F6B-9FA0-126185F36D9D + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = MemDebugLogLib|PEIM + + +[Sources] + MemDebugLogPei.c + MemDebugLogCommon.c + +[Packages] + MdeModulePkg/MdeModulePkg.dec + MdePkg/MdePkg.dec + OvmfPkg/OvmfPkg.dec + +[LibraryClasses] + SynchronizationLib + PcdLib + +[LibraryClasses.Ia32] + QemuFwCfgSimpleParserLib + +[LibraryClasses.X64] + QemuFwCfgSimpleParserLib + +[Guids] + gMemDebugLogHobGuid + +[Ppis] + +[FixedPcd] + gUefiOvmfPkgTokenSpaceGuid.PcdMemDebugLogPages + gUefiOvmfPkgTokenSpaceGuid.PcdOvmfEarlyMemDebugLogBase + gUefiOvmfPkgTokenSpaceGuid.PcdOvmfEarlyMemDebugLogSize + gEfiMdeModulePkgTokenSpaceGuid.PcdFirmwareVersionString ## CONSUMES + diff --git a/OvmfPkg/Library/MemDebugLogLib/MemDebugLogRt.c b/OvmfPkg/Library/MemDebugLogLib/MemDebugLogRt.c new file mode 100644 index 0000000000..456bb70a85 --- /dev/null +++ b/OvmfPkg/Library/MemDebugLogLib/MemDebugLogRt.c @@ -0,0 +1,54 @@ +/** @file + * + Memory Debug Log Library - Runtime + + Copyright (C) 2025, Oracle and/or its affiliates. + + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include +#include +#include + +EFI_PHYSICAL_ADDRESS mMemDebugLogBufAddr; +BOOLEAN mMemDebugLogBufAddrInit; + +EFI_STATUS +EFIAPI +MemDebugLogWrite ( + IN CHAR8 *Buffer, + IN UINTN Length + ) +{ + EFI_STATUS Status; + + // + // Stop logging after we have switched to virtual mode + // to avoid potential problems (such as crashes accessing + // physical pointers). + // + if (EfiGoneVirtual ()) { + return EFI_SUCCESS; + } + + if (!mMemDebugLogBufAddrInit) { + // + // Obtain the Memory Debug Log buffer addr from HOB + // + Status = MemDebugLogAddrFromHOB (&mMemDebugLogBufAddr); + if (EFI_ERROR (Status)) { + mMemDebugLogBufAddr = 0; + } + + mMemDebugLogBufAddrInit = TRUE; + } + + if (mMemDebugLogBufAddr) { + Status = MemDebugLogWriteBuffer (mMemDebugLogBufAddr, Buffer, Length); + } else { + Status = EFI_NOT_FOUND; + } + + return Status; +} diff --git a/OvmfPkg/Library/MemDebugLogLib/MemDebugLogRtLib.inf b/OvmfPkg/Library/MemDebugLogLib/MemDebugLogRtLib.inf new file mode 100644 index 0000000000..a2e4729555 --- /dev/null +++ b/OvmfPkg/Library/MemDebugLogLib/MemDebugLogRtLib.inf @@ -0,0 +1,38 @@ +## @file +# Instance of MemDebugLog Library for Runtime +# +# Copyright (C) 2025, Oracle and/or its affiliates. +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = MemDebugLogLib + FILE_GUID = BE0D0FFD-206C-48F3-9910-C32467567C44 + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = MemDebugLogLib|DXE_RUNTIME_DRIVER + + +[Sources] + MemDebugLogRt.c + MemDebugLogCommon.c + +[Packages] + MdeModulePkg/MdeModulePkg.dec + MdePkg/MdePkg.dec + OvmfPkg/OvmfPkg.dec + +[LibraryClasses] + HobLib + UefiRuntimeLib + SynchronizationLib + +[Guids] + gMemDebugLogHobGuid ## CONSUMES + +[FixedPcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdFirmwareVersionString ## CONSUMES diff --git a/OvmfPkg/Library/MemDebugLogLib/MemDebugLogSec.c b/OvmfPkg/Library/MemDebugLogLib/MemDebugLogSec.c new file mode 100644 index 0000000000..62905fc177 --- /dev/null +++ b/OvmfPkg/Library/MemDebugLogLib/MemDebugLogSec.c @@ -0,0 +1,48 @@ +/** @file + * + Memory Debug Log Library - SEC Phase + + Copyright (C) 2025, Oracle and/or its affiliates. + + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include + +EFI_STATUS +EFIAPI +MemDebugLogWrite ( + IN CHAR8 *Buffer, + IN UINTN Length + ) +{ + EFI_STATUS Status; + + if (FixedPcdGet32 (PcdOvmfEarlyMemDebugLogBase) != 0x0) { + Status = MemDebugLogWriteBuffer ( + (EFI_PHYSICAL_ADDRESS)(UINTN)FixedPcdGet32 (PcdOvmfEarlyMemDebugLogBase), + Buffer, + Length + ); + } else { + Status = EFI_NOT_FOUND; + } + + return Status; +} + +RETURN_STATUS +EFIAPI +MemDebugLogLibConstructor ( + VOID + ) +{ + if (FixedPcdGet32 (PcdOvmfEarlyMemDebugLogSize) != 0) { + MemDebugLogInit ( + (EFI_PHYSICAL_ADDRESS)(UINTN)FixedPcdGet32 (PcdOvmfEarlyMemDebugLogBase), + (UINT32)FixedPcdGet32 (PcdOvmfEarlyMemDebugLogSize) + ); + } + + return RETURN_SUCCESS; +} diff --git a/OvmfPkg/Library/MemDebugLogLib/MemDebugLogSecLib.inf b/OvmfPkg/Library/MemDebugLogLib/MemDebugLogSecLib.inf new file mode 100644 index 0000000000..0817e570c5 --- /dev/null +++ b/OvmfPkg/Library/MemDebugLogLib/MemDebugLogSecLib.inf @@ -0,0 +1,39 @@ +## @file +# Instance of MemDebugLog Library for SEC phase +# +# Copyright (C) 2025, Oracle and/or its affiliates. +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = MemDebugLogLib + FILE_GUID = 9B3A8F82-CBCE-4E3A-A3E0-DBD7172E9506 + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = MemDebugLogLib|SEC + CONSTRUCTOR = MemDebugLogLibConstructor + + +[Sources] + MemDebugLogSec.c + MemDebugLogCommon.c + +[Packages] + MdeModulePkg/MdeModulePkg.dec + MdePkg/MdePkg.dec + OvmfPkg/OvmfPkg.dec + +[LibraryClasses] + SynchronizationLib + +[Guids] + gMemDebugLogHobGuid + +[FixedPcd] + gUefiOvmfPkgTokenSpaceGuid.PcdOvmfEarlyMemDebugLogBase + gUefiOvmfPkgTokenSpaceGuid.PcdOvmfEarlyMemDebugLogSize + gEfiMdeModulePkgTokenSpaceGuid.PcdFirmwareVersionString ## CONSUMES