From b61207852be0322f621e48dbceaec9a8d71811c1 Mon Sep 17 00:00:00 2001 From: Michael Kubacki Date: Mon, 3 Feb 2025 10:01:15 -0500 Subject: [PATCH] MdeModulePkg: Add Standalone MM Core performance support Adds a new library instance to support MM core functionality for performance in Standalone MM. - Add StandaloneMmCorePerformanceLib instance - Move common MM logic to a new file `MmCorePerformanceLib.c` - Define interfaces with implementation specific to MM environment type in `SmmCorePerformanceLibInternal.h` and implement those functions in the Standalone MM and Traditional MM specific C files Note: StandaloneMmCorePerformanceLib supports both `MM_CORE_STANDALONE` and `MM_STANDALONE` as some Standalone MM environments need to link this functionality in a ring 3 driver that is outside the ring 0 core driver. Signed-off-by: Michael Kubacki --- .../DxeCorePerformanceLib.c | 64 +- .../MmCorePerformanceLib.c | 1374 ++++++++++++++++ .../SmmCorePerformanceLib.c | 1430 ++--------------- .../SmmCorePerformanceLib.inf | 34 +- .../SmmCorePerformanceLibInternal.h | 134 +- .../StandaloneMmCorePerformanceLib.c | 147 ++ .../StandaloneMmCorePerformanceLib.inf | 55 + MdeModulePkg/MdeModulePkg.dsc | 1 + 8 files changed, 1853 insertions(+), 1386 deletions(-) create mode 100644 MdeModulePkg/Library/SmmCorePerformanceLib/MmCorePerformanceLib.c create mode 100644 MdeModulePkg/Library/SmmCorePerformanceLib/StandaloneMmCorePerformanceLib.c create mode 100644 MdeModulePkg/Library/SmmCorePerformanceLib/StandaloneMmCorePerformanceLib.inf diff --git a/MdeModulePkg/Library/DxeCorePerformanceLib/DxeCorePerformanceLib.c b/MdeModulePkg/Library/DxeCorePerformanceLib/DxeCorePerformanceLib.c index 0e418f4a32..335c34ef84 100644 --- a/MdeModulePkg/Library/DxeCorePerformanceLib/DxeCorePerformanceLib.c +++ b/MdeModulePkg/Library/DxeCorePerformanceLib/DxeCorePerformanceLib.c @@ -22,10 +22,12 @@ SPDX-License-Identifier: BSD-2-Clause-Patent // // Data for FPDT performance records. // -#define SMM_BOOT_RECORD_COMM_SIZE (OFFSET_OF (EFI_SMM_COMMUNICATE_HEADER, Data) + sizeof(SMM_BOOT_RECORD_COMMUNICATE)) -#define STRING_SIZE (FPDT_STRING_EVENT_RECORD_NAME_LENGTH * sizeof (CHAR8)) -#define FIRMWARE_RECORD_BUFFER 0x10000 -#define CACHE_HANDLE_GUID_COUNT 0x800 +// MU_CHANGE [BEGIN] - Standalone MM Perf Support +#define SMM_BOOT_RECORD_COMM_SIZE (OFFSET_OF (EFI_MM_COMMUNICATE_HEADER, Data) + sizeof(SMM_BOOT_RECORD_COMMUNICATE)) +// MU_CHANGE [END] - Standalone MM Perf Support +#define STRING_SIZE (FPDT_STRING_EVENT_RECORD_NAME_LENGTH * sizeof (CHAR8)) +#define FIRMWARE_RECORD_BUFFER 0x10000 +#define CACHE_HANDLE_GUID_COUNT 0x800 BOOT_PERFORMANCE_TABLE *mAcpiBootPerformanceTable = NULL; BOOT_PERFORMANCE_TABLE mBootPerformanceTableTemplate = { @@ -228,16 +230,18 @@ InternalGetSmmPerfData ( { EFI_STATUS Status; UINT8 *SmmBootRecordCommBuffer; - EFI_SMM_COMMUNICATE_HEADER *SmmCommBufferHeader; + EFI_MM_COMMUNICATE_HEADER *SmmCommBufferHeader; // MU_CHANGE - Standalone MM Perf Support SMM_BOOT_RECORD_COMMUNICATE *SmmCommData; UINTN CommSize; EFI_SMM_COMMUNICATION_PROTOCOL *Communication; EDKII_PI_SMM_COMMUNICATION_REGION_TABLE *SmmCommRegionTable; EFI_MEMORY_DESCRIPTOR *SmmCommMemRegion; UINTN Index; + VOID *CurrentBootRecordData; // MU_CHANGE - Standalone MM Perf Support VOID *SmmBootRecordData; UINTN SmmBootRecordDataSize; UINTN ReservedMemSize; + UINTN BootRecordDataPayloadSize; // MU_CHANGE - Standalone MM Perf Support UINTN SmmBootRecordDataRetrieved; // @@ -278,7 +282,7 @@ InternalGetSmmPerfData ( // if (ReservedMemSize > SMM_BOOT_RECORD_COMM_SIZE) { SmmBootRecordCommBuffer = (VOID *)(UINTN)SmmCommMemRegion->PhysicalStart; - SmmCommBufferHeader = (EFI_SMM_COMMUNICATE_HEADER *)SmmBootRecordCommBuffer; + SmmCommBufferHeader = (EFI_MM_COMMUNICATE_HEADER *)SmmBootRecordCommBuffer; SmmCommData = (SMM_BOOT_RECORD_COMMUNICATE *)SmmCommBufferHeader->Data; ZeroMem ((UINT8 *)SmmCommData, sizeof (SMM_BOOT_RECORD_COMMUNICATE)); @@ -292,7 +296,10 @@ InternalGetSmmPerfData ( SmmCommData->Function = SMM_FPDT_FUNCTION_GET_BOOT_RECORD_SIZE; SmmCommData->BootRecordData = NULL; Status = Communication->Communicate (Communication, SmmBootRecordCommBuffer, &CommSize); - + // MU_CHANGE [BEGIN] - Standalone MM Perf Support + ASSERT_EFI_ERROR (Status); + ASSERT_EFI_ERROR (SmmCommData->ReturnStatus); + // MU_CHANGE [END] - Standalone MM Perf Support if (!EFI_ERROR (Status) && !EFI_ERROR (SmmCommData->ReturnStatus) && (SmmCommData->BootRecordSize != 0)) { if (SkipGetPerfData) { *SmmPerfDataSize = SmmCommData->BootRecordSize; @@ -308,26 +315,37 @@ InternalGetSmmPerfData ( SmmBootRecordData = AllocateZeroPool (SmmBootRecordDataSize); SmmBootRecordDataRetrieved = 0; ASSERT (SmmBootRecordData != NULL); - SmmCommData->BootRecordData = (VOID *)((UINTN)SmmCommMemRegion->PhysicalStart + SMM_BOOT_RECORD_COMM_SIZE); - SmmCommData->BootRecordSize = ReservedMemSize - SMM_BOOT_RECORD_COMM_SIZE; - while (SmmBootRecordDataRetrieved < SmmBootRecordDataSize) { - Status = Communication->Communicate (Communication, SmmBootRecordCommBuffer, &CommSize); - ASSERT_EFI_ERROR (Status); - ASSERT_EFI_ERROR (SmmCommData->ReturnStatus); - if (SmmBootRecordDataRetrieved + SmmCommData->BootRecordSize > SmmBootRecordDataSize) { - CopyMem ((UINT8 *)SmmBootRecordData + SmmBootRecordDataRetrieved, SmmCommData->BootRecordData, SmmBootRecordDataSize - SmmBootRecordDataRetrieved); - } else { - CopyMem ((UINT8 *)SmmBootRecordData + SmmBootRecordDataRetrieved, SmmCommData->BootRecordData, SmmCommData->BootRecordSize); + // MU_CHANGE [BEGIN] - Standalone MM Perf Support + if (SmmBootRecordData != NULL) { + CurrentBootRecordData = (VOID *)((UINTN)SmmCommMemRegion->PhysicalStart + SMM_BOOT_RECORD_COMM_SIZE); + while (SmmBootRecordDataRetrieved < SmmBootRecordDataSize) { + // Note: Maximum comm buffer data payload size is ReservedMemSize - SMM_BOOT_RECORD_COMM_SIZE + BootRecordDataPayloadSize = MIN ( + ReservedMemSize - SMM_BOOT_RECORD_COMM_SIZE, + SmmBootRecordDataSize - SmmBootRecordDataRetrieved + ); + SmmCommBufferHeader->MessageLength = sizeof (SMM_BOOT_RECORD_COMMUNICATE) + BootRecordDataPayloadSize; + CommSize = sizeof (EFI_MM_COMMUNICATE_HEADER) + (UINTN)SmmCommBufferHeader->MessageLength; + Status = Communication->Communicate (Communication, SmmBootRecordCommBuffer, &CommSize); + ASSERT_EFI_ERROR (Status); + ASSERT_EFI_ERROR (SmmCommData->ReturnStatus); + if (SmmBootRecordDataRetrieved + SmmCommData->BootRecordSize > SmmBootRecordDataSize) { + CopyMem ((UINT8 *)SmmBootRecordData + SmmBootRecordDataRetrieved, CurrentBootRecordData, SmmBootRecordDataSize - SmmBootRecordDataRetrieved); + } else { + CopyMem ((UINT8 *)SmmBootRecordData + SmmBootRecordDataRetrieved, CurrentBootRecordData, SmmCommData->BootRecordSize); + } + + SmmBootRecordDataRetrieved += SmmCommData->BootRecordSize; + SmmCommData->BootRecordOffset += SmmCommData->BootRecordSize; } - SmmBootRecordDataRetrieved += SmmCommData->BootRecordSize; - SmmCommData->BootRecordOffset += SmmCommData->BootRecordSize; - } + mSmmBootRecordOffset = SmmCommData->BootRecordOffset; - mSmmBootRecordOffset = SmmCommData->BootRecordOffset; + *SmmPerfData = SmmBootRecordData; + *SmmPerfDataSize = SmmBootRecordDataSize; + } - *SmmPerfData = SmmBootRecordData; - *SmmPerfDataSize = SmmBootRecordDataSize; + // MU_CHANGE [END] - Standalone MM Perf Support } } } diff --git a/MdeModulePkg/Library/SmmCorePerformanceLib/MmCorePerformanceLib.c b/MdeModulePkg/Library/SmmCorePerformanceLib/MmCorePerformanceLib.c new file mode 100644 index 0000000000..3268bc5f2f --- /dev/null +++ b/MdeModulePkg/Library/SmmCorePerformanceLib/MmCorePerformanceLib.c @@ -0,0 +1,1374 @@ +/** @file + This library is mainly used by a MM Core to start performance logging to ensure that + MM Performance and PerformanceEx Protocol are installed as early as possible in the MM phase. + + Caution: This module requires additional review when modified. + - This driver will have external input - performance data and communicate buffer in SMM mode. + - This external input must be validated carefully to avoid security issue like + buffer overflow, integer overflow. + + SmmPerformanceHandlerEx(), SmmPerformanceHandler() will receive untrusted input and do basic validation. + + Copyright (c) 2006 - 2023, Intel Corporation. All rights reserved.
+ (C) Copyright 2016 Hewlett Packard Enterprise Development LP
+ Copyright (c) Microsoft Corporation. + SPDX-License-Identifier: BSD-2-Clause-Patent + + MU_CHANGE [WHOLE FILE] - Standalone MM Perf Support + +**/ + +#include "SmmCorePerformanceLibInternal.h" + +#include +#include + +SMM_BOOT_PERFORMANCE_TABLE *mSmmBootPerformanceTable = NULL; + +typedef struct { + EFI_HANDLE Handle; + CHAR8 NameString[FPDT_STRING_EVENT_RECORD_NAME_LENGTH]; + EFI_GUID ModuleGuid; +} HANDLE_GUID_MAP; + +HANDLE_GUID_MAP mCacheHandleGuidTable[CACHE_HANDLE_GUID_COUNT]; +UINTN mCachePairCount = 0; + +UINT32 mPerformanceLength = sizeof (SMM_BOOT_PERFORMANCE_TABLE); +UINT32 mMaxPerformanceLength = 0; +UINT32 mLoadImageCount = 0; +BOOLEAN mFpdtDataIsReported = FALSE; +BOOLEAN mLackSpaceIsReport = FALSE; +SPIN_LOCK mSmmFpdtLock; +UINT32 mCachedLength = 0; +UINT32 mBootRecordSize = 0; +BOOLEAN mPerformanceMeasurementEnabled; + +// +// Interfaces for SMM PerformanceMeasurement Protocol. +// +EDKII_PERFORMANCE_MEASUREMENT_PROTOCOL mPerformanceMeasurementInterface = { + CreatePerformanceMeasurement, +}; + +/** + Return the module name and optionally module GUID for a given handle. + + @param[in] Handle The image handle. + @param[out] NameString The buffer to store the name string. + @param[in] BufferSize The size of the NameString buffer in bytes. + @param[out] ModuleGuid An optional buffer to store the module GUID. + + @retval EFI_SUCCESS The module information was successfully retrieved. + @retval EFI_INVALID_PARAMETER A required pointer argument is null or the given handle is invalid. + +**/ +EFI_STATUS +EFIAPI +GetModuleInfoFromHandle ( + IN EFI_HANDLE Handle, + OUT CHAR8 *NameString, + IN UINTN BufferSize, + OUT EFI_GUID *ModuleGuid OPTIONAL + ) +{ + EFI_STATUS Status; + EFI_LOADED_IMAGE_PROTOCOL *LoadedImage; + EFI_GUID *TempGuid; + INTN Count; + MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *FvFilePath; + + if ((Handle == NULL) || (NameString == NULL) || (BufferSize == 0)) { + return EFI_INVALID_PARAMETER; + } + + // + // Try to get the ModuleGuid and name string form the cached array. + // + if (mCachePairCount > 0) { + for (Count = mCachePairCount - 1; Count >= 0; Count--) { + if (Handle == mCacheHandleGuidTable[Count].Handle) { + CopyGuid (ModuleGuid, &mCacheHandleGuidTable[Count].ModuleGuid); + AsciiStrCpyS (NameString, FPDT_STRING_EVENT_RECORD_NAME_LENGTH, mCacheHandleGuidTable[Count].NameString); + return EFI_SUCCESS; + } + } + } + + Status = EFI_INVALID_PARAMETER; + NameString[0] = 0; + TempGuid = &gZeroGuid; + + LoadedImage = GetLoadedImageProtocol (Handle); + if (LoadedImage != NULL) { + // + // Get Module Guid from DevicePath. + // + if ((LoadedImage->FilePath != NULL) && + (LoadedImage->FilePath->Type == MEDIA_DEVICE_PATH) && + (LoadedImage->FilePath->SubType == MEDIA_PIWG_FW_FILE_DP) + ) + { + // + // Determine GUID associated with module logging performance + // + FvFilePath = (MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *)LoadedImage->FilePath; + TempGuid = &FvFilePath->FvFileName; + } + + if (GetModuleNameFromPdbString (LoadedImage->ImageBase, NameString, BufferSize) == EFI_SUCCESS) { + goto Done; + } + } + + if (!IsZeroGuid (TempGuid)) { + Status = GetNameFromUiSection (TempGuid, NameString, BufferSize); + } + +Done: + // + // Copy Module Guid + // + if (ModuleGuid != NULL) { + CopyGuid (ModuleGuid, TempGuid); + if (IsZeroGuid (TempGuid) && (Handle != NULL)) { + // Handle is GUID + CopyGuid (ModuleGuid, (EFI_GUID *)Handle); + } + } + + // + // Cache the Handle and Guid pairs. + // + if (mCachePairCount < CACHE_HANDLE_GUID_COUNT) { + mCacheHandleGuidTable[mCachePairCount].Handle = Handle; + CopyGuid (&mCacheHandleGuidTable[mCachePairCount].ModuleGuid, ModuleGuid); + AsciiStrCpyS (mCacheHandleGuidTable[mCachePairCount].NameString, FPDT_STRING_EVENT_RECORD_NAME_LENGTH, NameString); + mCachePairCount++; + } + + return Status; +} + +/** + Get the module name from the PDB file name in the image header. + + @param[in] ImageBase The base address of the image. + @param[out] NameString The buffer to store the name string. + @param[in] BufferSize The size of the buffer in bytes. + + @retval EFI_SUCCESS The name string is successfully retrieved. + @retval EFI_INVALID_PARAMETER A pointer argument provided is null. + @retval EFI_NOT_FOUND The module name was not found. + +**/ +EFI_STATUS +GetModuleNameFromPdbString ( + IN VOID *ImageBase, + OUT CHAR8 *NameString, + IN UINTN BufferSize + ) +{ + CHAR8 *PdbFileName; + UINTN Index; + UINTN StartIndex; + + if ((ImageBase == NULL) || (NameString == NULL) || (BufferSize == 0)) { + return EFI_INVALID_PARAMETER; + } + + PdbFileName = PeCoffLoaderGetPdbPointer (ImageBase); + + for (StartIndex = 0, Index = 0; PdbFileName[Index] != 0; Index++) { + if ((PdbFileName[Index] == '\\') || (PdbFileName[Index] == '/')) { + StartIndex = Index + 1; + } + } + + // + // Copy the PDB file name to our temporary string. + // If the length is bigger than BufferSize, trim the redudant characters to avoid overflow in array boundary. + // + for (Index = 0; Index < BufferSize - 1; Index++) { + NameString[Index] = PdbFileName[Index + StartIndex]; + if ((NameString[Index] == 0) || (NameString[Index] == '.')) { + NameString[Index] = 0; + break; + } + } + + if (Index == BufferSize - 1) { + NameString[Index] = 0; + } + + return EFI_SUCCESS; +} + +/** + Return the pointer to the FPDT record in the allocated memory. + + @param RecordSize The size of FPDT record. + @param FpdtRecordPtr Pointer the FPDT record in the allocated memory. + + @retval EFI_SUCCESS Successfully get the pointer to the FPDT record. + @retval EFI_OUT_OF_RESOURCES Ran out of space to store the records. +**/ +EFI_STATUS +GetFpdtRecordPtr ( + IN UINT8 RecordSize, + IN OUT FPDT_RECORD_PTR *FpdtRecordPtr + ) +{ + if (mFpdtDataIsReported) { + // + // Append Boot records after Smm boot performance records have been reported. + // + if (mPerformanceLength + RecordSize > mMaxPerformanceLength) { + if (!mLackSpaceIsReport) { + DEBUG ((DEBUG_ERROR, "[%a] - Not enough space to save boot records\n", __func__)); + mLackSpaceIsReport = TRUE; + } + + return EFI_OUT_OF_RESOURCES; + } else { + // + // Covert buffer to FPDT Ptr Union type. + // + FpdtRecordPtr->RecordHeader = (EFI_ACPI_5_0_FPDT_PERFORMANCE_RECORD_HEADER *)((UINT8 *)mSmmBootPerformanceTable + mSmmBootPerformanceTable->Header.Length); + } + } else { + // + // Check if pre-allocated buffer is full + // + if (mPerformanceLength + RecordSize > mMaxPerformanceLength) { + mSmmBootPerformanceTable = ReallocatePool ( + mPerformanceLength, + mPerformanceLength + RecordSize + FIRMWARE_RECORD_BUFFER, + mSmmBootPerformanceTable + ); + + if (mSmmBootPerformanceTable == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + mSmmBootPerformanceTable->Header.Length = mPerformanceLength; + mMaxPerformanceLength = mPerformanceLength + RecordSize + FIRMWARE_RECORD_BUFFER; + } + + // + // Covert buffer to FPDT Ptr Union type. + // + FpdtRecordPtr->RecordHeader = (EFI_ACPI_5_0_FPDT_PERFORMANCE_RECORD_HEADER *)((UINT8 *)mSmmBootPerformanceTable + mSmmBootPerformanceTable->Header.Length); + } + + FpdtRecordPtr->RecordHeader->Length = 0; + return EFI_SUCCESS; +} + +/** +Check whether the Token is a known one which is uesed by core. + +@param Token Pointer to a Null-terminated ASCII string + +@retval TRUE Is a known one used by core. +@retval FALSE Not a known one. + +**/ +BOOLEAN +IsKnownTokens ( + IN CONST CHAR8 *Token + ) +{ + if (Token == NULL) { + return FALSE; + } + + if ((AsciiStrCmp (Token, SEC_TOK) == 0) || + (AsciiStrCmp (Token, PEI_TOK) == 0) || + (AsciiStrCmp (Token, DXE_TOK) == 0) || + (AsciiStrCmp (Token, BDS_TOK) == 0) || + (AsciiStrCmp (Token, DRIVERBINDING_START_TOK) == 0) || + (AsciiStrCmp (Token, DRIVERBINDING_SUPPORT_TOK) == 0) || + (AsciiStrCmp (Token, DRIVERBINDING_STOP_TOK) == 0) || + (AsciiStrCmp (Token, LOAD_IMAGE_TOK) == 0) || + (AsciiStrCmp (Token, START_IMAGE_TOK) == 0) || + (AsciiStrCmp (Token, PEIM_TOK) == 0)) + { + return TRUE; + } else { + return FALSE; + } +} + +/** +Check whether the ID is a known one which map to the known Token. + +@param Identifier 32-bit identifier. + +@retval TRUE Is a known one used by core. +@retval FALSE Not a known one. + +**/ +BOOLEAN +IsKnownID ( + IN UINT32 Identifier + ) +{ + if ((Identifier == MODULE_START_ID) || + (Identifier == MODULE_END_ID) || + (Identifier == MODULE_LOADIMAGE_START_ID) || + (Identifier == MODULE_LOADIMAGE_END_ID) || + (Identifier == MODULE_DB_START_ID) || + (Identifier == MODULE_DB_END_ID) || + (Identifier == MODULE_DB_SUPPORT_START_ID) || + (Identifier == MODULE_DB_SUPPORT_END_ID) || + (Identifier == MODULE_DB_STOP_START_ID) || + (Identifier == MODULE_DB_STOP_END_ID)) + { + return TRUE; + } else { + return FALSE; + } +} + +/** + Get the FPDT record identifier. + + @param Attribute The attribute of the Record. + PerfStartEntry: Start Record. + PerfEndEntry: End Record. + @param Handle Pointer to environment specific context used to identify the component being measured. + @param String Pointer to a Null-terminated ASCII string that identifies the component being measured. + @param ProgressID On return, pointer to the ProgressID. + + @retval EFI_SUCCESS Get record info successfully. + @retval EFI_INVALID_PARAMETER No matched FPDT record. + +**/ +EFI_STATUS +GetFpdtRecordId ( + IN PERF_MEASUREMENT_ATTRIBUTE Attribute, + IN CONST VOID *Handle, + IN CONST CHAR8 *String, + OUT UINT16 *ProgressID + ) +{ + // + // Token to Id. + // + if (String != NULL) { + if (AsciiStrCmp (String, START_IMAGE_TOK) == 0) { + // "StartImage:" + if (Attribute == PerfStartEntry) { + *ProgressID = MODULE_START_ID; + } else { + *ProgressID = MODULE_END_ID; + } + } else if (AsciiStrCmp (String, LOAD_IMAGE_TOK) == 0) { + // "LoadImage:" + if (Attribute == PerfStartEntry) { + *ProgressID = MODULE_LOADIMAGE_START_ID; + } else { + *ProgressID = MODULE_LOADIMAGE_END_ID; + } + } else { + // Pref used in Modules + if (Attribute == PerfStartEntry) { + *ProgressID = PERF_INMODULE_START_ID; + } else { + *ProgressID = PERF_INMODULE_END_ID; + } + } + } else if (Handle != NULL) { + // Pref used in Modules + if (Attribute == PerfStartEntry) { + *ProgressID = PERF_INMODULE_START_ID; + } else { + *ProgressID = PERF_INMODULE_END_ID; + } + } else { + return EFI_UNSUPPORTED; + } + + return EFI_SUCCESS; +} + +/** + Copies the string from Source into Destination and updates Length with the + size of the string. + + @param Destination - destination of the string copy + @param Source - pointer to the source string which will get copied + @param Length - pointer to a length variable to be updated + +**/ +VOID +CopyStringIntoPerfRecordAndUpdateLength ( + IN OUT CHAR8 *Destination, + IN CONST CHAR8 *Source, + IN OUT UINT8 *Length + ) +{ + UINTN StringLen; + UINTN DestMax; + + ASSERT (Source != NULL); + + if (PcdGetBool (PcdEdkiiFpdtStringRecordEnableOnly)) { + DestMax = STRING_SIZE; + } else { + DestMax = AsciiStrSize (Source); + if (DestMax > STRING_SIZE) { + DestMax = STRING_SIZE; + } + } + + StringLen = AsciiStrLen (Source); + if (StringLen >= DestMax) { + StringLen = DestMax -1; + } + + AsciiStrnCpyS (Destination, DestMax, Source, StringLen); + *Length += (UINT8)DestMax; + + return; +} + +/** + Create performance record with event description and a timestamp. + + @param CallerIdentifier - Image handle or pointer to caller ID GUID. + @param Guid - Pointer to a GUID. + @param String - Pointer to a string describing the measurement. + @param Ticker - 64-bit time stamp. + @param Address - Pointer to a location in memory relevant to the measurement. + @param PerfId - Performance identifier describing the type of measurement. + @param Attribute - The attribute of the measurement. According to attribute can create a start + record for PERF_START/PERF_START_EX, or a end record for PERF_END/PERF_END_EX, + or a general record for other Perf macros. + + @retval EFI_SUCCESS - Successfully created performance record. + @retval EFI_OUT_OF_RESOURCES - Ran out of space to store the records. + @retval EFI_INVALID_PARAMETER - Invalid parameter passed to function - NULL + pointer or invalid PerfId. + + @retval EFI_SUCCESS - Successfully created performance record + @retval EFI_OUT_OF_RESOURCES - Ran out of space to store the records + @retval EFI_INVALID_PARAMETER - Invalid parameter passed to function - NULL + pointer or invalid PerfId + +**/ +EFI_STATUS +InsertFpdtRecord ( + IN CONST VOID *CallerIdentifier OPTIONAL, + IN CONST VOID *Guid OPTIONAL, + IN CONST CHAR8 *String OPTIONAL, + IN UINT64 Ticker, + IN UINT64 Address OPTIONAL, + IN UINT16 PerfId, + IN PERF_MEASUREMENT_ATTRIBUTE Attribute + ) + +{ + EFI_STATUS Status; + EFI_GUID ModuleGuid; + CHAR8 ModuleName[FPDT_STRING_EVENT_RECORD_NAME_LENGTH]; + FPDT_RECORD_PTR FpdtRecordPtr; + FPDT_RECORD_PTR CachedFpdtRecordPtr; + UINT64 TimeStamp; + CONST CHAR8 *StringPtr; + UINTN DestMax; + UINTN StringLen; + UINT16 ProgressId; + + StringPtr = NULL; + ZeroMem (ModuleName, sizeof (ModuleName)); + + // + // 1. Get the Perf Id for records from PERF_START/PERF_END, PERF_START_EX/PERF_END_EX. + // notes: For other Perf macros (Attribute == PerfEntry), their Id is known. + // + if (Attribute != PerfEntry) { + // + // If PERF_START_EX()/PERF_END_EX() have specified the ProgressID,it has high priority. + // !!! Note: If the Perf is not the known Token used in the core but have same + // ID with the core Token, this case will not be supported. + // And in currtnt usage mode, for the unkown ID, there is a general rule: + // If it is start pref: the lower 4 bits of the ID should be 0. + // If it is end pref: the lower 4 bits of the ID should not be 0. + // If input ID doesn't follow the rule, we will adjust it. + // + if ((PerfId != 0) && (IsKnownID (PerfId)) && (!IsKnownTokens (String))) { + return EFI_INVALID_PARAMETER; + } else if ((PerfId != 0) && (!IsKnownID (PerfId)) && (!IsKnownTokens (String))) { + if ((Attribute == PerfStartEntry) && ((PerfId & 0x000F) != 0)) { + PerfId &= 0xFFF0; + } else if ((Attribute == PerfEndEntry) && ((PerfId & 0x000F) == 0)) { + PerfId += 1; + } + } + + if (PerfId == 0) { + // + // Get ProgressID form the String Token. + // + Status = GetFpdtRecordId (Attribute, CallerIdentifier, String, &ProgressId); + if (EFI_ERROR (Status)) { + return Status; + } + + PerfId = ProgressId; + } + } + + // + // 2. Get the buffer to store the FPDT record. + // + Status = GetFpdtRecordPtr (FPDT_MAX_PERF_RECORD_SIZE, &FpdtRecordPtr); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // 3. Get the TimeStamp. + // + if (Ticker == 0) { + Ticker = GetPerformanceCounter (); + TimeStamp = GetTimeInNanoSecond (Ticker); + } else if (Ticker == 1) { + TimeStamp = 0; + } else { + TimeStamp = GetTimeInNanoSecond (Ticker); + } + + // + // 4. Fill in the FPDT record according to different Performance Identifier. + // + switch (PerfId) { + case MODULE_START_ID: + case MODULE_END_ID: + GetModuleInfoFromHandle ((EFI_HANDLE)CallerIdentifier, ModuleName, sizeof (ModuleName), &ModuleGuid); + StringPtr = ModuleName; + // + // Cache the offset of start image start record and use to update the start image end record if needed. + // + if ((PerfId == MODULE_START_ID) && (Attribute == PerfEntry)) { + mCachedLength = mSmmBootPerformanceTable->Header.Length; + } + + if (!PcdGetBool (PcdEdkiiFpdtStringRecordEnableOnly)) { + FpdtRecordPtr.GuidEvent->Header.Type = FPDT_GUID_EVENT_TYPE; + FpdtRecordPtr.GuidEvent->Header.Length = sizeof (FPDT_GUID_EVENT_RECORD); + FpdtRecordPtr.GuidEvent->Header.Revision = FPDT_RECORD_REVISION_1; + FpdtRecordPtr.GuidEvent->ProgressID = PerfId; + FpdtRecordPtr.GuidEvent->Timestamp = TimeStamp; + CopyMem (&FpdtRecordPtr.GuidEvent->Guid, &ModuleGuid, sizeof (FpdtRecordPtr.GuidEvent->Guid)); + if ((CallerIdentifier == NULL) && (PerfId == MODULE_END_ID) && (mCachedLength != 0)) { + CachedFpdtRecordPtr.RecordHeader = (EFI_ACPI_5_0_FPDT_PERFORMANCE_RECORD_HEADER *)((UINT8 *)mSmmBootPerformanceTable + mCachedLength); + CopyMem (&FpdtRecordPtr.GuidEvent->Guid, &CachedFpdtRecordPtr.GuidEvent->Guid, sizeof (FpdtRecordPtr.GuidEvent->Guid)); + mCachedLength = 0; + } + } + + break; + + case MODULE_LOADIMAGE_START_ID: + case MODULE_LOADIMAGE_END_ID: + GetModuleInfoFromHandle ((EFI_HANDLE)CallerIdentifier, ModuleName, sizeof (ModuleName), &ModuleGuid); + StringPtr = ModuleName; + if (PerfId == MODULE_LOADIMAGE_START_ID) { + mLoadImageCount++; + // + // Cache the offset of load image start record and use to be updated by the load image end record if needed. + // + if ((CallerIdentifier == NULL) && (Attribute == PerfEntry)) { + mCachedLength = mSmmBootPerformanceTable->Header.Length; + } + } + + if (!PcdGetBool (PcdEdkiiFpdtStringRecordEnableOnly)) { + FpdtRecordPtr.GuidQwordEvent->Header.Type = FPDT_GUID_QWORD_EVENT_TYPE; + FpdtRecordPtr.GuidQwordEvent->Header.Length = sizeof (FPDT_GUID_QWORD_EVENT_RECORD); + FpdtRecordPtr.GuidQwordEvent->Header.Revision = FPDT_RECORD_REVISION_1; + FpdtRecordPtr.GuidQwordEvent->ProgressID = PerfId; + FpdtRecordPtr.GuidQwordEvent->Timestamp = TimeStamp; + FpdtRecordPtr.GuidQwordEvent->Qword = mLoadImageCount; + CopyMem (&FpdtRecordPtr.GuidQwordEvent->Guid, &ModuleGuid, sizeof (FpdtRecordPtr.GuidQwordEvent->Guid)); + if ((PerfId == MODULE_LOADIMAGE_END_ID) && (mCachedLength != 0)) { + CachedFpdtRecordPtr.RecordHeader = (EFI_ACPI_5_0_FPDT_PERFORMANCE_RECORD_HEADER *)((UINT8 *)mSmmBootPerformanceTable + mCachedLength); + CopyMem (&CachedFpdtRecordPtr.GuidQwordEvent->Guid, &ModuleGuid, sizeof (CachedFpdtRecordPtr.GuidQwordEvent->Guid)); + mCachedLength = 0; + } + } + + break; + + case PERF_EVENTSIGNAL_START_ID: + case PERF_EVENTSIGNAL_END_ID: + case PERF_CALLBACK_START_ID: + case PERF_CALLBACK_END_ID: + if ((String == NULL) || (Guid == NULL)) { + return EFI_INVALID_PARAMETER; + } + + StringPtr = String; + if (AsciiStrLen (String) == 0) { + StringPtr = "unknown name"; + } + + if (!PcdGetBool (PcdEdkiiFpdtStringRecordEnableOnly)) { + FpdtRecordPtr.DualGuidStringEvent->Header.Type = FPDT_DUAL_GUID_STRING_EVENT_TYPE; + FpdtRecordPtr.DualGuidStringEvent->Header.Length = sizeof (FPDT_DUAL_GUID_STRING_EVENT_RECORD); + FpdtRecordPtr.DualGuidStringEvent->Header.Revision = FPDT_RECORD_REVISION_1; + FpdtRecordPtr.DualGuidStringEvent->ProgressID = PerfId; + FpdtRecordPtr.DualGuidStringEvent->Timestamp = TimeStamp; + CopyMem (&FpdtRecordPtr.DualGuidStringEvent->Guid1, CallerIdentifier, sizeof (FpdtRecordPtr.DualGuidStringEvent->Guid1)); + CopyMem (&FpdtRecordPtr.DualGuidStringEvent->Guid2, Guid, sizeof (FpdtRecordPtr.DualGuidStringEvent->Guid2)); + CopyStringIntoPerfRecordAndUpdateLength (FpdtRecordPtr.DualGuidStringEvent->String, StringPtr, &FpdtRecordPtr.DualGuidStringEvent->Header.Length); + } + + break; + + case PERF_EVENT_ID: + case PERF_FUNCTION_START_ID: + case PERF_FUNCTION_END_ID: + case PERF_INMODULE_START_ID: + case PERF_INMODULE_END_ID: + case PERF_CROSSMODULE_START_ID: + case PERF_CROSSMODULE_END_ID: + // MU_CHANGE [BEGIN] - CodeQL change + Status = GetModuleInfoFromHandle ((EFI_HANDLE)CallerIdentifier, ModuleName, sizeof (ModuleName), &ModuleGuid); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Failed to get Module Info from Handle! Status = %r\n", Status)); + } + + StringPtr = NULL; + + if (String != NULL) { + StringPtr = String; + } else if (ModuleName != NULL) { + StringPtr = ModuleName; + } + + if ((StringPtr == NULL) || (AsciiStrLen (StringPtr) == 0)) { + StringPtr = "unknown name"; + } + + // MU_CHANGE [END] - CodeQL change + + if (!PcdGetBool (PcdEdkiiFpdtStringRecordEnableOnly)) { + FpdtRecordPtr.DynamicStringEvent->Header.Type = FPDT_DYNAMIC_STRING_EVENT_TYPE; + FpdtRecordPtr.DynamicStringEvent->Header.Length = sizeof (FPDT_DYNAMIC_STRING_EVENT_RECORD); + FpdtRecordPtr.DynamicStringEvent->Header.Revision = FPDT_RECORD_REVISION_1; + FpdtRecordPtr.DynamicStringEvent->ProgressID = PerfId; + FpdtRecordPtr.DynamicStringEvent->Timestamp = TimeStamp; + CopyMem (&FpdtRecordPtr.DynamicStringEvent->Guid, &ModuleGuid, sizeof (FpdtRecordPtr.DynamicStringEvent->Guid)); + CopyStringIntoPerfRecordAndUpdateLength (FpdtRecordPtr.DynamicStringEvent->String, StringPtr, &FpdtRecordPtr.DynamicStringEvent->Header.Length); + } + + break; + + default: + if (Attribute != PerfEntry) { + // MU_CHANGE [BEGIN] - CodeQL change + Status = GetModuleInfoFromHandle ((EFI_HANDLE)CallerIdentifier, ModuleName, sizeof (ModuleName), &ModuleGuid); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Failed to get Module Info from Handle! Status = %r\n", Status)); + } + + StringPtr = NULL; + + if (String != NULL) { + StringPtr = String; + } else if (ModuleName != NULL) { + StringPtr = ModuleName; + } + + if ((StringPtr == NULL) || (AsciiStrLen (StringPtr) == 0)) { + StringPtr = "unknown name"; + } + + // MU_CHANGE [END] - CodeQL change + + if (!PcdGetBool (PcdEdkiiFpdtStringRecordEnableOnly)) { + FpdtRecordPtr.DynamicStringEvent->Header.Type = FPDT_DYNAMIC_STRING_EVENT_TYPE; + FpdtRecordPtr.DynamicStringEvent->Header.Length = sizeof (FPDT_DYNAMIC_STRING_EVENT_RECORD); + FpdtRecordPtr.DynamicStringEvent->Header.Revision = FPDT_RECORD_REVISION_1; + FpdtRecordPtr.DynamicStringEvent->ProgressID = PerfId; + FpdtRecordPtr.DynamicStringEvent->Timestamp = TimeStamp; + CopyMem (&FpdtRecordPtr.DynamicStringEvent->Guid, &ModuleGuid, sizeof (FpdtRecordPtr.DynamicStringEvent->Guid)); + CopyStringIntoPerfRecordAndUpdateLength (FpdtRecordPtr.DynamicStringEvent->String, StringPtr, &FpdtRecordPtr.DynamicStringEvent->Header.Length); + } + } else { + return EFI_INVALID_PARAMETER; + } + + break; + } + + // + // 4.2 When PcdEdkiiFpdtStringRecordEnableOnly==TRUE, create string record for all Perf entries. + // + if (PcdGetBool (PcdEdkiiFpdtStringRecordEnableOnly)) { + if (StringPtr == NULL) { + return EFI_INVALID_PARAMETER; + } + + FpdtRecordPtr.DynamicStringEvent->Header.Type = FPDT_DYNAMIC_STRING_EVENT_TYPE; + FpdtRecordPtr.DynamicStringEvent->Header.Length = sizeof (FPDT_DYNAMIC_STRING_EVENT_RECORD); + FpdtRecordPtr.DynamicStringEvent->Header.Revision = FPDT_RECORD_REVISION_1; + FpdtRecordPtr.DynamicStringEvent->ProgressID = PerfId; + FpdtRecordPtr.DynamicStringEvent->Timestamp = TimeStamp; + if (Guid != NULL) { + // + // Cache the event guid in string event record. + // + CopyMem (&FpdtRecordPtr.DynamicStringEvent->Guid, Guid, sizeof (FpdtRecordPtr.DynamicStringEvent->Guid)); + } else { + CopyMem (&FpdtRecordPtr.DynamicStringEvent->Guid, &ModuleGuid, sizeof (FpdtRecordPtr.DynamicStringEvent->Guid)); + } + + if (AsciiStrLen (StringPtr) == 0) { + StringPtr = "unknown name"; + } + + CopyStringIntoPerfRecordAndUpdateLength (FpdtRecordPtr.DynamicStringEvent->String, StringPtr, &FpdtRecordPtr.DynamicStringEvent->Header.Length); + + if ((PerfId == MODULE_LOADIMAGE_START_ID) || (PerfId == MODULE_END_ID)) { + FpdtRecordPtr.DynamicStringEvent->Header.Length = (UINT8)(sizeof (FPDT_DYNAMIC_STRING_EVENT_RECORD)+ STRING_SIZE); + } + + if (((PerfId == MODULE_LOADIMAGE_END_ID) || (PerfId == MODULE_END_ID)) && (mCachedLength != 0)) { + CachedFpdtRecordPtr.RecordHeader = (EFI_ACPI_5_0_FPDT_PERFORMANCE_RECORD_HEADER *)((UINT8 *)mSmmBootPerformanceTable + mCachedLength); + if (PerfId == MODULE_LOADIMAGE_END_ID) { + DestMax = CachedFpdtRecordPtr.DynamicStringEvent->Header.Length - sizeof (FPDT_DYNAMIC_STRING_EVENT_RECORD); + StringLen = AsciiStrLen (StringPtr); + if (StringLen >= DestMax) { + StringLen = DestMax -1; + } + + CopyMem (&CachedFpdtRecordPtr.DynamicStringEvent->Guid, &ModuleGuid, sizeof (CachedFpdtRecordPtr.DynamicStringEvent->Guid)); + AsciiStrnCpyS (CachedFpdtRecordPtr.DynamicStringEvent->String, DestMax, StringPtr, StringLen); + } else if (PerfId == MODULE_END_ID) { + DestMax = FpdtRecordPtr.DynamicStringEvent->Header.Length - sizeof (FPDT_DYNAMIC_STRING_EVENT_RECORD); + StringLen = AsciiStrLen (CachedFpdtRecordPtr.DynamicStringEvent->String); + if (StringLen >= DestMax) { + StringLen = DestMax -1; + } + + CopyMem (&FpdtRecordPtr.DynamicStringEvent->Guid, &CachedFpdtRecordPtr.DynamicStringEvent->Guid, sizeof (CachedFpdtRecordPtr.DynamicStringEvent->Guid)); + AsciiStrnCpyS (FpdtRecordPtr.DynamicStringEvent->String, DestMax, CachedFpdtRecordPtr.DynamicStringEvent->String, StringLen); + } + + mCachedLength = 0; + } + } + + // + // 5. Update the length of the used buffer after fill in the record. + // + mPerformanceLength += FpdtRecordPtr.RecordHeader->Length; + mSmmBootPerformanceTable->Header.Length += FpdtRecordPtr.RecordHeader->Length; + + return EFI_SUCCESS; +} + +/** + Communication service SMI Handler entry. + + This SMI handler provides services for report MM boot records. + + Caution: This function may receive untrusted input. + Communicate buffer and buffer size are external input, so this function will do basic validation. + + @param[in] DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister(). + @param[in] RegisterContext Points to an optional handler context which was specified when the + handler was registered. + @param[in, out] CommBuffer A pointer to a collection of data in memory that will + be conveyed from a non-MM environment into an MM environment. + @param[in, out] CommBufferSize The size of the CommBuffer. + + @retval EFI_SUCCESS The interrupt was handled and quiesced. No other handlers + should still be called. + @retval EFI_WARN_INTERRUPT_SOURCE_QUIESCED The interrupt has been quiesced but other handlers should + still be called. + @retval EFI_WARN_INTERRUPT_SOURCE_PENDING The interrupt is still pending and other handlers should still + be called. + @retval EFI_INTERRUPT_PENDING The interrupt could not be quiesced. + +**/ +EFI_STATUS +EFIAPI +FpdtSmiHandler ( + IN EFI_HANDLE DispatchHandle, + IN CONST VOID *RegisterContext, + IN OUT VOID *CommBuffer, + IN OUT UINTN *CommBufferSize + ) +{ + EFI_STATUS Status; + SMM_BOOT_RECORD_COMMUNICATE *SmmCommData; + UINTN BootRecordOffset; + UINTN BootRecordSize; + VOID *BootRecordData; + UINTN TempCommBufferSize; + UINT8 *BootRecordBuffer; + + // + // If input is invalid, stop processing this SMI + // + if ((CommBuffer == NULL) || (CommBufferSize == NULL)) { + return EFI_SUCCESS; + } + + TempCommBufferSize = *CommBufferSize; + + if (TempCommBufferSize < sizeof (SMM_BOOT_RECORD_COMMUNICATE)) { + return EFI_SUCCESS; + } + + if (!IsCommBufferValidInternal ((UINTN)CommBuffer, TempCommBufferSize)) { + ASSERT (FALSE); + return EFI_SUCCESS; + } + + SmmCommData = (SMM_BOOT_RECORD_COMMUNICATE *)CommBuffer; + + Status = EFI_SUCCESS; + + switch (SmmCommData->Function) { + case SMM_FPDT_FUNCTION_GET_BOOT_RECORD_SIZE: + DEBUG ((DEBUG_VERBOSE, "[%a] - SMM_FPDT_FUNCTION_GET_BOOT_RECORD_SIZE entry.\n", __func__)); + if (mSmmBootPerformanceTable != NULL) { + mBootRecordSize = mSmmBootPerformanceTable->Header.Length - sizeof (SMM_BOOT_PERFORMANCE_TABLE); + } + + SmmCommData->BootRecordSize = mBootRecordSize; + break; + + case SMM_FPDT_FUNCTION_GET_BOOT_RECORD_DATA: + DEBUG ((DEBUG_VERBOSE, "[%a] - SMM_FPDT_FUNCTION_GET_BOOT_RECORD_DATA entry.\n", __func__)); + Status = EFI_UNSUPPORTED; + break; + + case SMM_FPDT_FUNCTION_GET_BOOT_RECORD_DATA_BY_OFFSET: + DEBUG ((DEBUG_VERBOSE, "[%a] - SMM_FPDT_FUNCTION_GET_BOOT_RECORD_DATA_BY_OFFSET entry.\n", __func__)); + BootRecordOffset = SmmCommData->BootRecordOffset; + BootRecordData = SmmCommData->BootRecordData; + BootRecordSize = SmmCommData->BootRecordSize; + + if (BootRecordOffset >= mBootRecordSize) { + Status = EFI_INVALID_PARAMETER; + break; + } + + if (BootRecordData == NULL) { + BootRecordData = (UINT8 *)SmmCommData + sizeof (SMM_BOOT_RECORD_COMMUNICATE); + } else if (!IsBufferOutsideMmValidInternal ((UINTN)BootRecordData, BootRecordSize)) { + Status = EFI_ACCESS_DENIED; + break; + } + + // + // Sanity check + // + if (BootRecordSize > mBootRecordSize - BootRecordOffset) { + BootRecordSize = mBootRecordSize - BootRecordOffset; + } + + // Note: Comm size passed to this handler already has OFFSET_OF (EFI_MM_COMMUNICATE_HEADER, Data) removed. + if ((SmmCommData->BootRecordData == NULL) && (BootRecordSize - BootRecordOffset > TempCommBufferSize - sizeof (SMM_BOOT_RECORD_COMMUNICATE))) { + Status = EFI_BUFFER_TOO_SMALL; + break; + } + + SmmCommData->BootRecordSize = BootRecordSize; + BootRecordBuffer = ((UINT8 *)(mSmmBootPerformanceTable)) + sizeof (SMM_BOOT_PERFORMANCE_TABLE); + CopyMem ( + (UINT8 *)BootRecordData, + BootRecordBuffer + BootRecordOffset, + BootRecordSize + ); + mFpdtDataIsReported = TRUE; + break; + + default: + Status = EFI_UNSUPPORTED; + } + + SmmCommData->ReturnStatus = Status; + + return EFI_SUCCESS; +} + +/** + This is the Event call back function is triggered in SMM to notify the Library + the system is entering runtime phase. + + @param[in] Protocol Points to the protocol's unique identifier + @param[in] Interface Points to the interface instance + @param[in] Handle The handle on which the interface was installed + + @retval EFI_SUCCESS SmmAtRuntimeCallBack runs successfully + **/ +EFI_STATUS +EFIAPI +SmmCorePerformanceLibExitBootServicesCallback ( + IN CONST EFI_GUID *Protocol, + IN VOID *Interface, + IN EFI_HANDLE Handle + ) +{ + // + // Disable performance measurement after ExitBootServices because + // 1. Performance measurement might impact SMI latency at runtime; + // 2. Performance log is copied to non SMRAM at ReadyToBoot so runtime performance + // log is not useful. + // + mPerformanceMeasurementEnabled = FALSE; + + return EFI_SUCCESS; +} + +VOID +InitializeMmCorePerformanceLibCommon ( + VOID + ) +{ + EFI_STATUS Status; + EFI_HANDLE Handle; + EFI_HANDLE MmiHandle; + VOID *Registration; + + // + // Initialize spin lock + // + InitializeSpinLock (&mSmmFpdtLock); + + // + // Install the protocol interfaces for MM performance library instance. + // + Handle = NULL; + Status = gMmst->MmInstallProtocolInterface ( + &Handle, + &gEdkiiSmmPerformanceMeasurementProtocolGuid, + EFI_NATIVE_INTERFACE, + &mPerformanceMeasurementInterface + ); + ASSERT_EFI_ERROR (Status); + + // + // Register MMI handler. + // + MmiHandle = NULL; + Status = gMmst->MmiHandlerRegister (FpdtSmiHandler, &gEfiFirmwarePerformanceGuid, &MmiHandle); + ASSERT_EFI_ERROR (Status); + + // + // Register callback function for ExitBootServices event. + // + Status = gMmst->MmRegisterProtocolNotify ( + &gEdkiiSmmExitBootServicesProtocolGuid, + SmmCorePerformanceLibExitBootServicesCallback, + &Registration + ); + ASSERT_EFI_ERROR (Status); +} + +/** + Create performance record with event description and a timestamp. + + @param CallerIdentifier - Image handle or pointer to caller ID GUID. + @param Guid - Pointer to a GUID. + @param String - Pointer to a string describing the measurement. + @param TimeStamp - 64-bit time stamp. + @param Address - Pointer to a location in memory relevant to the measurement. + @param Identifier - Performance identifier describing the type of measurement. + @param Attribute - The attribute of the measurement. According to attribute can create a start + record for PERF_START/PERF_START_EX, or a end record for PERF_END/PERF_END_EX, + or a general record for other Perf macros. + + @retval EFI_SUCCESS - Successfully created performance record. + @retval EFI_OUT_OF_RESOURCES - Ran out of space to store the records. + @retval EFI_INVALID_PARAMETER - Invalid parameter passed to function - NULL + pointer or invalid PerfId. +**/ +EFI_STATUS +EFIAPI +CreatePerformanceMeasurement ( + IN CONST VOID *CallerIdentifier OPTIONAL, + IN CONST VOID *Guid OPTIONAL, + IN CONST CHAR8 *String OPTIONAL, + IN UINT64 TimeStamp OPTIONAL, + IN UINT64 Address OPTIONAL, + IN UINT32 Identifier, + IN PERF_MEASUREMENT_ATTRIBUTE Attribute + ) +{ + EFI_STATUS Status; + + Status = EFI_SUCCESS; + + AcquireSpinLock (&mSmmFpdtLock); + Status = InsertFpdtRecord (CallerIdentifier, Guid, String, TimeStamp, Address, (UINT16)Identifier, Attribute); + ReleaseSpinLock (&mSmmFpdtLock); + return Status; +} + +/** + Adds a record at the end of the performance measurement log + that records the start time of a performance measurement. + + Adds a record to the end of the performance measurement log + that contains the Handle, Token, Module and Identifier. + The end time of the new record must be set to zero. + If TimeStamp is not zero, then TimeStamp is used to fill in the start time in the record. + If TimeStamp is zero, the start time in the record is filled in with the value + read from the current time stamp. + + @param Handle Pointer to environment specific context used + to identify the component being measured. + @param Token Pointer to a Null-terminated ASCII string + that identifies the component being measured. + @param Module Pointer to a Null-terminated ASCII string + that identifies the module being measured. + @param TimeStamp 64-bit time stamp. + @param Identifier 32-bit identifier. If the value is 0, the created record + is same as the one created by StartPerformanceMeasurement. + + @retval RETURN_SUCCESS The start of the measurement was recorded. + @retval RETURN_OUT_OF_RESOURCES There are not enough resources to record the measurement. + +**/ +RETURN_STATUS +EFIAPI +StartPerformanceMeasurementEx ( + IN CONST VOID *Handle OPTIONAL, + IN CONST CHAR8 *Token OPTIONAL, + IN CONST CHAR8 *Module OPTIONAL, + IN UINT64 TimeStamp, + IN UINT32 Identifier + ) +{ + CONST CHAR8 *String; + + if (Token != NULL) { + String = Token; + } else if (Module != NULL) { + String = Module; + } else { + String = NULL; + } + + return (RETURN_STATUS)CreatePerformanceMeasurement (Handle, NULL, String, TimeStamp, 0, Identifier, PerfStartEntry); +} + +/** + Searches the performance measurement log from the beginning of the log + for the first matching record that contains a zero end time and fills in a valid end time. + + Searches the performance measurement log from the beginning of the log + for the first record that matches Handle, Token, Module and Identifier and has an end time value of zero. + If the record can not be found then return RETURN_NOT_FOUND. + If the record is found and TimeStamp is not zero, + then the end time in the record is filled in with the value specified by TimeStamp. + If the record is found and TimeStamp is zero, then the end time in the matching record + is filled in with the current time stamp value. + + @param Handle Pointer to environment specific context used + to identify the component being measured. + @param Token Pointer to a Null-terminated ASCII string + that identifies the component being measured. + @param Module Pointer to a Null-terminated ASCII string + that identifies the module being measured. + @param TimeStamp 64-bit time stamp. + @param Identifier 32-bit identifier. If the value is 0, the found record + is same as the one found by EndPerformanceMeasurement. + + @retval RETURN_SUCCESS The end of the measurement was recorded. + @retval RETURN_NOT_FOUND The specified measurement record could not be found. + +**/ +RETURN_STATUS +EFIAPI +EndPerformanceMeasurementEx ( + IN CONST VOID *Handle OPTIONAL, + IN CONST CHAR8 *Token OPTIONAL, + IN CONST CHAR8 *Module OPTIONAL, + IN UINT64 TimeStamp, + IN UINT32 Identifier + ) +{ + CONST CHAR8 *String; + + if (Token != NULL) { + String = Token; + } else if (Module != NULL) { + String = Module; + } else { + String = NULL; + } + + return (RETURN_STATUS)CreatePerformanceMeasurement (Handle, NULL, String, TimeStamp, 0, Identifier, PerfEndEntry); +} + +/** + Attempts to retrieve a performance measurement log entry from the performance measurement log. + It can also retrieve the log created by StartPerformanceMeasurement and EndPerformanceMeasurement, + and then assign the Identifier with 0. + + !!! Not Support!!! + + Attempts to retrieve the performance log entry specified by LogEntryKey. If LogEntryKey is + zero on entry, then an attempt is made to retrieve the first entry from the performance log, + and the key for the second entry in the log is returned. If the performance log is empty, + then no entry is retrieved and zero is returned. If LogEntryKey is not zero, then the performance + log entry associated with LogEntryKey is retrieved, and the key for the next entry in the log is + returned. If LogEntryKey is the key for the last entry in the log, then the last log entry is + retrieved and an implementation specific non-zero key value that specifies the end of the performance + log is returned. If LogEntryKey is equal this implementation specific non-zero key value, then no entry + is retrieved and zero is returned. In the cases where a performance log entry can be returned, + the log entry is returned in Handle, Token, Module, StartTimeStamp, EndTimeStamp and Identifier. + If LogEntryKey is not a valid log entry key for the performance measurement log, then ASSERT(). + If Handle is NULL, then ASSERT(). + If Token is NULL, then ASSERT(). + If Module is NULL, then ASSERT(). + If StartTimeStamp is NULL, then ASSERT(). + If EndTimeStamp is NULL, then ASSERT(). + If Identifier is NULL, then ASSERT(). + + @param LogEntryKey On entry, the key of the performance measurement log entry to retrieve. + 0, then the first performance measurement log entry is retrieved. + On exit, the key of the next performance log entry. + @param Handle Pointer to environment specific context used to identify the component + being measured. + @param Token Pointer to a Null-terminated ASCII string that identifies the component + being measured. + @param Module Pointer to a Null-terminated ASCII string that identifies the module + being measured. + @param StartTimeStamp Pointer to the 64-bit time stamp that was recorded when the measurement + was started. + @param EndTimeStamp Pointer to the 64-bit time stamp that was recorded when the measurement + was ended. + @param Identifier Pointer to the 32-bit identifier that was recorded. + + @return The key for the next performance log entry (in general case). + +**/ +UINTN +EFIAPI +GetPerformanceMeasurementEx ( + IN UINTN LogEntryKey, + OUT CONST VOID **Handle, + OUT CONST CHAR8 **Token, + OUT CONST CHAR8 **Module, + OUT UINT64 *StartTimeStamp, + OUT UINT64 *EndTimeStamp, + OUT UINT32 *Identifier + ) +{ + return 0; +} + +/** + Adds a record at the end of the performance measurement log + that records the start time of a performance measurement. + + Adds a record to the end of the performance measurement log + that contains the Handle, Token, and Module. + The end time of the new record must be set to zero. + If TimeStamp is not zero, then TimeStamp is used to fill in the start time in the record. + If TimeStamp is zero, the start time in the record is filled in with the value + read from the current time stamp. + + @param Handle Pointer to environment specific context used + to identify the component being measured. + @param Token Pointer to a Null-terminated ASCII string + that identifies the component being measured. + @param Module Pointer to a Null-terminated ASCII string + that identifies the module being measured. + @param TimeStamp 64-bit time stamp. + + @retval RETURN_SUCCESS The start of the measurement was recorded. + @retval RETURN_OUT_OF_RESOURCES There are not enough resources to record the measurement. + +**/ +RETURN_STATUS +EFIAPI +StartPerformanceMeasurement ( + IN CONST VOID *Handle OPTIONAL, + IN CONST CHAR8 *Token OPTIONAL, + IN CONST CHAR8 *Module OPTIONAL, + IN UINT64 TimeStamp + ) +{ + return StartPerformanceMeasurementEx (Handle, Token, Module, TimeStamp, 0); +} + +/** + Searches the performance measurement log from the beginning of the log + for the first matching record that contains a zero end time and fills in a valid end time. + + Searches the performance measurement log from the beginning of the log + for the first record that matches Handle, Token, and Module and has an end time value of zero. + If the record can not be found then return RETURN_NOT_FOUND. + If the record is found and TimeStamp is not zero, + then the end time in the record is filled in with the value specified by TimeStamp. + If the record is found and TimeStamp is zero, then the end time in the matching record + is filled in with the current time stamp value. + + @param Handle Pointer to environment specific context used + to identify the component being measured. + @param Token Pointer to a Null-terminated ASCII string + that identifies the component being measured. + @param Module Pointer to a Null-terminated ASCII string + that identifies the module being measured. + @param TimeStamp 64-bit time stamp. + + @retval RETURN_SUCCESS The end of the measurement was recorded. + @retval RETURN_NOT_FOUND The specified measurement record could not be found. + +**/ +RETURN_STATUS +EFIAPI +EndPerformanceMeasurement ( + IN CONST VOID *Handle OPTIONAL, + IN CONST CHAR8 *Token OPTIONAL, + IN CONST CHAR8 *Module OPTIONAL, + IN UINT64 TimeStamp + ) +{ + return EndPerformanceMeasurementEx (Handle, Token, Module, TimeStamp, 0); +} + +/** + Attempts to retrieve a performance measurement log entry from the performance measurement log. + It can also retrieve the log created by StartPerformanceMeasurementEx and EndPerformanceMeasurementEx, + and then eliminate the Identifier. + + !!! Not Support!!! + + Attempts to retrieve the performance log entry specified by LogEntryKey. If LogEntryKey is + zero on entry, then an attempt is made to retrieve the first entry from the performance log, + and the key for the second entry in the log is returned. If the performance log is empty, + then no entry is retrieved and zero is returned. If LogEntryKey is not zero, then the performance + log entry associated with LogEntryKey is retrieved, and the key for the next entry in the log is + returned. If LogEntryKey is the key for the last entry in the log, then the last log entry is + retrieved and an implementation specific non-zero key value that specifies the end of the performance + log is returned. If LogEntryKey is equal this implementation specific non-zero key value, then no entry + is retrieved and zero is returned. In the cases where a performance log entry can be returned, + the log entry is returned in Handle, Token, Module, StartTimeStamp, and EndTimeStamp. + If LogEntryKey is not a valid log entry key for the performance measurement log, then ASSERT(). + If Handle is NULL, then ASSERT(). + If Token is NULL, then ASSERT(). + If Module is NULL, then ASSERT(). + If StartTimeStamp is NULL, then ASSERT(). + If EndTimeStamp is NULL, then ASSERT(). + + @param LogEntryKey On entry, the key of the performance measurement log entry to retrieve. + 0, then the first performance measurement log entry is retrieved. + On exit, the key of the next performance log entry. + @param Handle Pointer to environment specific context used to identify the component + being measured. + @param Token Pointer to a Null-terminated ASCII string that identifies the component + being measured. + @param Module Pointer to a Null-terminated ASCII string that identifies the module + being measured. + @param StartTimeStamp Pointer to the 64-bit time stamp that was recorded when the measurement + was started. + @param EndTimeStamp Pointer to the 64-bit time stamp that was recorded when the measurement + was ended. + + @return The key for the next performance log entry (in general case). + +**/ +UINTN +EFIAPI +GetPerformanceMeasurement ( + IN UINTN LogEntryKey, + OUT CONST VOID **Handle, + OUT CONST CHAR8 **Token, + OUT CONST CHAR8 **Module, + OUT UINT64 *StartTimeStamp, + OUT UINT64 *EndTimeStamp + ) +{ + return 0; +} + +/** + Returns TRUE if the performance measurement macros are enabled. + + This function returns TRUE if the PERFORMANCE_LIBRARY_PROPERTY_MEASUREMENT_ENABLED bit of + PcdPerformanceLibraryPropertyMask is set. Otherwise FALSE is returned. + + @retval TRUE The PERFORMANCE_LIBRARY_PROPERTY_MEASUREMENT_ENABLED bit of + PcdPerformanceLibraryPropertyMask is set. + @retval FALSE The PERFORMANCE_LIBRARY_PROPERTY_MEASUREMENT_ENABLED bit of + PcdPerformanceLibraryPropertyMask is clear. + +**/ +BOOLEAN +EFIAPI +PerformanceMeasurementEnabled ( + VOID + ) +{ + return mPerformanceMeasurementEnabled; +} + +/** + Create performance record with event description and a timestamp. + + @param CallerIdentifier - Image handle or pointer to caller ID GUID + @param Guid - Pointer to a GUID + @param String - Pointer to a string describing the measurement + @param Address - Pointer to a location in memory relevant to the measurement + @param Identifier - Performance identifier describing the type of measurement + + @retval RETURN_SUCCESS - Successfully created performance record + @retval RETURN_OUT_OF_RESOURCES - Ran out of space to store the records + @retval RETURN_INVALID_PARAMETER - Invalid parameter passed to function - NULL + pointer or invalid PerfId + +**/ +RETURN_STATUS +EFIAPI +LogPerformanceMeasurement ( + IN CONST VOID *CallerIdentifier, + IN CONST VOID *Guid OPTIONAL, + IN CONST CHAR8 *String OPTIONAL, + IN UINT64 Address OPTIONAL, + IN UINT32 Identifier + ) +{ + return (RETURN_STATUS)CreatePerformanceMeasurement (CallerIdentifier, Guid, String, 0, Address, Identifier, PerfEntry); +} + +/** + Check whether the specified performance measurement can be logged. + + This function returns TRUE when the PERFORMANCE_LIBRARY_PROPERTY_MEASUREMENT_ENABLED bit of PcdPerformanceLibraryPropertyMask is set + and the Type disable bit in PcdPerformanceLibraryPropertyMask is not set. + + @param Type - Type of the performance measurement entry. + + @retval TRUE The performance measurement can be logged. + @retval FALSE The performance measurement can NOT be logged. + +**/ +BOOLEAN +EFIAPI +LogPerformanceMeasurementEnabled ( + IN CONST UINTN Type + ) +{ + // + // When Performance measurement is enabled and the type is not filtered, the performance can be logged. + // + if (PerformanceMeasurementEnabled () && ((PcdGet8 (PcdPerformanceLibraryPropertyMask) & Type) == 0)) { + return TRUE; + } + + return FALSE; +} diff --git a/MdeModulePkg/Library/SmmCorePerformanceLib/SmmCorePerformanceLib.c b/MdeModulePkg/Library/SmmCorePerformanceLib/SmmCorePerformanceLib.c index bb96fdcd7c..f6f9e01b7a 100644 --- a/MdeModulePkg/Library/SmmCorePerformanceLib/SmmCorePerformanceLib.c +++ b/MdeModulePkg/Library/SmmCorePerformanceLib/SmmCorePerformanceLib.c @@ -17,972 +17,172 @@ SmmPerformanceHandlerEx(), SmmPerformanceHandler() will receive untrusted input and do basic validation. Copyright (c) 2011 - 2023, Intel Corporation. All rights reserved.
+Copyright (c) Microsoft Corporation. SPDX-License-Identifier: BSD-2-Clause-Patent +MU_CHANGE [WHOLE FILE] - Standalone MM Perf Support + **/ #include "SmmCorePerformanceLibInternal.h" -#define STRING_SIZE (FPDT_STRING_EVENT_RECORD_NAME_LENGTH * sizeof (CHAR8)) -#define FIRMWARE_RECORD_BUFFER 0x1000 -#define CACHE_HANDLE_GUID_COUNT 0x100 - -SMM_BOOT_PERFORMANCE_TABLE *mSmmBootPerformanceTable = NULL; - -typedef struct { - EFI_HANDLE Handle; - CHAR8 NameString[FPDT_STRING_EVENT_RECORD_NAME_LENGTH]; - EFI_GUID ModuleGuid; -} HANDLE_GUID_MAP; - -HANDLE_GUID_MAP mCacheHandleGuidTable[CACHE_HANDLE_GUID_COUNT]; -UINTN mCachePairCount = 0; +#include +#include +#include +#include +#include -UINT32 mPerformanceLength = sizeof (SMM_BOOT_PERFORMANCE_TABLE); -UINT32 mMaxPerformanceLength = 0; -UINT32 mLoadImageCount = 0; -BOOLEAN mFpdtDataIsReported = FALSE; -BOOLEAN mLackSpaceIsReport = FALSE; -CHAR8 *mPlatformLanguage = NULL; -SPIN_LOCK mSmmFpdtLock; PERFORMANCE_PROPERTY mPerformanceProperty; -UINT32 mCachedLength = 0; -UINT32 mBootRecordSize = 0; -BOOLEAN mPerformanceMeasurementEnabled; - -// -// Interfaces for SMM PerformanceMeasurement Protocol. -// -EDKII_PERFORMANCE_MEASUREMENT_PROTOCOL mPerformanceMeasurementInterface = { - CreatePerformanceMeasurement, -}; - -/** - Return the pointer to the FPDT record in the allocated memory. - - @param RecordSize The size of FPDT record. - @param FpdtRecordPtr Pointer the FPDT record in the allocated memory. - - @retval EFI_SUCCESS Successfully get the pointer to the FPDT record. - @retval EFI_OUT_OF_RESOURCES Ran out of space to store the records. -**/ -EFI_STATUS -GetFpdtRecordPtr ( - IN UINT8 RecordSize, - IN OUT FPDT_RECORD_PTR *FpdtRecordPtr - ) -{ - if (mFpdtDataIsReported) { - // - // Append Boot records after Smm boot performance records have been reported. - // - if (mPerformanceLength + RecordSize > mMaxPerformanceLength) { - if (!mLackSpaceIsReport) { - DEBUG ((DEBUG_INFO, "SmmCorePerformanceLib: No enough space to save boot records\n")); - mLackSpaceIsReport = TRUE; - } - - return EFI_OUT_OF_RESOURCES; - } else { - // - // Covert buffer to FPDT Ptr Union type. - // - FpdtRecordPtr->RecordHeader = (EFI_ACPI_5_0_FPDT_PERFORMANCE_RECORD_HEADER *)((UINT8 *)mSmmBootPerformanceTable + mSmmBootPerformanceTable->Header.Length); - } - } else { - // - // Check if pre-allocated buffer is full - // - if (mPerformanceLength + RecordSize > mMaxPerformanceLength) { - mSmmBootPerformanceTable = ReallocatePool ( - mPerformanceLength, - mPerformanceLength + RecordSize + FIRMWARE_RECORD_BUFFER, - mSmmBootPerformanceTable - ); - - if (mSmmBootPerformanceTable == NULL) { - return EFI_OUT_OF_RESOURCES; - } - - mSmmBootPerformanceTable->Header.Length = mPerformanceLength; - mMaxPerformanceLength = mPerformanceLength + RecordSize + FIRMWARE_RECORD_BUFFER; - } - - // - // Covert buffer to FPDT Ptr Union type. - // - FpdtRecordPtr->RecordHeader = (EFI_ACPI_5_0_FPDT_PERFORMANCE_RECORD_HEADER *)((UINT8 *)mSmmBootPerformanceTable + mSmmBootPerformanceTable->Header.Length); - } - - FpdtRecordPtr->RecordHeader->Length = 0; - return EFI_SUCCESS; -} +extern BOOLEAN mPerformanceMeasurementEnabled; /** -Check whether the Token is a known one which is uesed by core. + A library internal MM-instance specific implementation to check if a buffer outside MM is valid. -@param Token Pointer to a Null-terminated ASCII string + This function is provided so Standalone MM and Traditional MM may use a different implementation + of data buffer check logic. -@retval TRUE Is a known one used by core. -@retval FALSE Not a known one. + @param[in] Buffer The buffer start address to be checked. + @param[in] Length The buffer length to be checked. + @retval TRUE This buffer is valid per processor architecture. + @retval FALSE This buffer is not valid per processor architecture. **/ BOOLEAN -IsKnownTokens ( - IN CONST CHAR8 *Token +IsBufferOutsideMmValidInternal ( + IN EFI_PHYSICAL_ADDRESS Buffer, + IN UINT64 Length ) { - if (Token == NULL) { - return FALSE; - } - - if ((AsciiStrCmp (Token, SEC_TOK) == 0) || - (AsciiStrCmp (Token, PEI_TOK) == 0) || - (AsciiStrCmp (Token, DXE_TOK) == 0) || - (AsciiStrCmp (Token, BDS_TOK) == 0) || - (AsciiStrCmp (Token, DRIVERBINDING_START_TOK) == 0) || - (AsciiStrCmp (Token, DRIVERBINDING_SUPPORT_TOK) == 0) || - (AsciiStrCmp (Token, DRIVERBINDING_STOP_TOK) == 0) || - (AsciiStrCmp (Token, LOAD_IMAGE_TOK) == 0) || - (AsciiStrCmp (Token, START_IMAGE_TOK) == 0) || - (AsciiStrCmp (Token, PEIM_TOK) == 0)) - { - return TRUE; - } else { - return FALSE; - } + return SmmIsBufferOutsideSmmValid (Buffer, Length); } /** -Check whether the ID is a known one which map to the known Token. + A library internal MM-instance specific implementation to check if a comm buffer is valid. -@param Identifier 32-bit identifier. + This function is provided so Standalone MM and Traditional MM may use a different implementation + of comm buffer check logic. -@retval TRUE Is a known one used by core. -@retval FALSE Not a known one. + @param[in] Buffer The buffer start address to be checked. + @param[in] Length The buffer length to be checked. + @retval TRUE This communicate buffer is valid per processor architecture. + @retval FALSE This communicate buffer is not valid per processor architecture. **/ BOOLEAN -IsKnownID ( - IN UINT32 Identifier +IsCommBufferValidInternal ( + IN EFI_PHYSICAL_ADDRESS Buffer, + IN UINT64 Length ) { - if ((Identifier == MODULE_START_ID) || - (Identifier == MODULE_END_ID) || - (Identifier == MODULE_LOADIMAGE_START_ID) || - (Identifier == MODULE_LOADIMAGE_END_ID) || - (Identifier == MODULE_DB_START_ID) || - (Identifier == MODULE_DB_END_ID) || - (Identifier == MODULE_DB_SUPPORT_START_ID) || - (Identifier == MODULE_DB_SUPPORT_END_ID) || - (Identifier == MODULE_DB_STOP_START_ID) || - (Identifier == MODULE_DB_STOP_END_ID)) - { - return TRUE; - } else { - return FALSE; - } + return SmmIsBufferOutsideSmmValid (Buffer, Length); } /** - Get the FPDT record identifier. + Return a pointer to the loaded image protocol for the given handle. - @param Attribute The attribute of the Record. - PerfStartEntry: Start Record. - PerfEndEntry: End Record. - @param Handle Pointer to environment specific context used to identify the component being measured. - @param String Pointer to a Null-terminated ASCII string that identifies the component being measured. - @param ProgressID On return, pointer to the ProgressID. - - @retval EFI_SUCCESS Get record info successfully. - @retval EFI_INVALID_PARAMETER No matched FPDT record. + @param[in] Handle A handle to query for the loaded image protocol. + @return A pointer to a loaded image protocol instance or null if the handle does not support load image protocol. **/ -EFI_STATUS -GetFpdtRecordId ( - IN PERF_MEASUREMENT_ATTRIBUTE Attribute, - IN CONST VOID *Handle, - IN CONST CHAR8 *String, - OUT UINT16 *ProgressID +EFI_LOADED_IMAGE_PROTOCOL * +GetLoadedImageProtocol ( + IN EFI_HANDLE Handle ) { - // - // Token to Id. - // - if (String != NULL) { - if (AsciiStrCmp (String, START_IMAGE_TOK) == 0) { - // "StartImage:" - if (Attribute == PerfStartEntry) { - *ProgressID = MODULE_START_ID; - } else { - *ProgressID = MODULE_END_ID; - } - } else if (AsciiStrCmp (String, LOAD_IMAGE_TOK) == 0) { - // "LoadImage:" - if (Attribute == PerfStartEntry) { - *ProgressID = MODULE_LOADIMAGE_START_ID; - } else { - *ProgressID = MODULE_LOADIMAGE_END_ID; - } - } else { - // Pref used in Modules - if (Attribute == PerfStartEntry) { - *ProgressID = PERF_INMODULE_START_ID; - } else { - *ProgressID = PERF_INMODULE_END_ID; - } - } - } else if (Handle != NULL) { - // Pref used in Modules - if (Attribute == PerfStartEntry) { - *ProgressID = PERF_INMODULE_START_ID; - } else { - *ProgressID = PERF_INMODULE_END_ID; - } - } else { - return EFI_UNSUPPORTED; - } - - return EFI_SUCCESS; -} - -/** - Get a human readable module name and module guid for the given image handle. - If module name can't be found, "" string will return. - If module guid can't be found, Zero Guid will return. - - @param Handle Image handle or Controller handle. - @param NameString The ascii string will be filled into it. If not found, null string will return. - @param BufferSize Size of the input NameString buffer. - @param ModuleGuid Point to the guid buffer to store the got module guid value. - - @retval EFI_SUCCESS Successfully get module name and guid. - @retval EFI_INVALID_PARAMETER The input parameter NameString is NULL. - @retval other value Module Name can't be got. -**/ -EFI_STATUS -EFIAPI -GetModuleInfoFromHandle ( - IN EFI_HANDLE Handle, - OUT CHAR8 *NameString, - IN UINTN BufferSize, - OUT EFI_GUID *ModuleGuid OPTIONAL - ) -{ - EFI_STATUS Status; - EFI_LOADED_IMAGE_PROTOCOL *LoadedImage; - EFI_DRIVER_BINDING_PROTOCOL *DriverBinding; - CHAR8 *PdbFileName; - EFI_GUID *TempGuid; - UINTN StartIndex; - UINTN Index; - INTN Count; - BOOLEAN ModuleGuidIsGet; - UINTN StringSize; - CHAR16 *StringPtr; - MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *FvFilePath; + EFI_STATUS Status; + EFI_DRIVER_BINDING_PROTOCOL *DriverBinding; + EFI_LOADED_IMAGE_PROTOCOL *LoadedImage; - if ((NameString == NULL) || (BufferSize == 0)) { - return EFI_INVALID_PARAMETER; + if ((Handle == NULL) || (LoadedImage == NULL)) { + return NULL; } - // - // Try to get the ModuleGuid and name string form the caached array. - // - if (mCachePairCount > 0) { - for (Count = mCachePairCount - 1; Count >= 0; Count--) { - if (Handle == mCacheHandleGuidTable[Count].Handle) { - CopyGuid (ModuleGuid, &mCacheHandleGuidTable[Count].ModuleGuid); - AsciiStrCpyS (NameString, FPDT_STRING_EVENT_RECORD_NAME_LENGTH, mCacheHandleGuidTable[Count].NameString); - return EFI_SUCCESS; - } - } - } - - Status = EFI_INVALID_PARAMETER; - LoadedImage = NULL; - ModuleGuidIsGet = FALSE; + LoadedImage = NULL; // - // Initialize GUID as zero value. + // Try Handle as ImageHandle. // - TempGuid = &gZeroGuid; - // - // Initialize it as "" string. - // - NameString[0] = 0; - - if (Handle != NULL) { + Status = gBS->HandleProtocol ( + Handle, + &gEfiLoadedImageProtocolGuid, + (VOID **)&LoadedImage + ); + if (EFI_ERROR (Status)) { // - // Try Handle as ImageHandle. + // Try Handle as Controller Handle // - Status = gBS->HandleProtocol ( + Status = gBS->OpenProtocol ( Handle, - &gEfiLoadedImageProtocolGuid, - (VOID **)&LoadedImage + &gEfiDriverBindingProtocolGuid, + (VOID **)&DriverBinding, + NULL, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL ); - - if (EFI_ERROR (Status)) { - // - // Try Handle as Controller Handle - // - Status = gBS->OpenProtocol ( - Handle, - &gEfiDriverBindingProtocolGuid, - (VOID **)&DriverBinding, - NULL, - NULL, - EFI_OPEN_PROTOCOL_GET_PROTOCOL - ); - if (!EFI_ERROR (Status)) { - // - // Get Image protocol from ImageHandle - // - Status = gBS->HandleProtocol ( - DriverBinding->ImageHandle, - &gEfiLoadedImageProtocolGuid, - (VOID **)&LoadedImage - ); - } - } - } - - if (!EFI_ERROR (Status) && (LoadedImage != NULL)) { - // - // Get Module Guid from DevicePath. - // - if ((LoadedImage->FilePath != NULL) && - (LoadedImage->FilePath->Type == MEDIA_DEVICE_PATH) && - (LoadedImage->FilePath->SubType == MEDIA_PIWG_FW_FILE_DP) - ) - { - // - // Determine GUID associated with module logging performance - // - ModuleGuidIsGet = TRUE; - FvFilePath = (MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *)LoadedImage->FilePath; - TempGuid = &FvFilePath->FvFileName; - } - - // - // Method 1 Get Module Name from PDB string. - // - PdbFileName = PeCoffLoaderGetPdbPointer (LoadedImage->ImageBase); - if ((PdbFileName != NULL) && (BufferSize > 0)) { - StartIndex = 0; - for (Index = 0; PdbFileName[Index] != 0; Index++) { - if ((PdbFileName[Index] == '\\') || (PdbFileName[Index] == '/')) { - StartIndex = Index + 1; - } - } - - // - // Copy the PDB file name to our temporary string. - // If the length is bigger than BufferSize, trim the redudant characters to avoid overflow in array boundary. - // - for (Index = 0; Index < BufferSize - 1; Index++) { - NameString[Index] = PdbFileName[Index + StartIndex]; - if ((NameString[Index] == 0) || (NameString[Index] == '.')) { - NameString[Index] = 0; - break; - } - } - - if (Index == BufferSize - 1) { - NameString[Index] = 0; - } - - // - // Module Name is got. - // - goto Done; - } - } - - if (ModuleGuidIsGet) { - // - // Method 2 Try to get the image's FFS UI section by image GUID - // - StringPtr = NULL; - StringSize = 0; - Status = GetSectionFromAnyFv ( - TempGuid, - EFI_SECTION_USER_INTERFACE, - 0, - (VOID **)&StringPtr, - &StringSize - ); - if (!EFI_ERROR (Status)) { // - // Method 3. Get the name string from FFS UI section + // Get Image protocol from ImageHandle // - for (Index = 0; Index < BufferSize - 1 && StringPtr[Index] != 0; Index++) { - NameString[Index] = (CHAR8)StringPtr[Index]; - } - - NameString[Index] = 0; - FreePool (StringPtr); - } - } - -Done: - // - // Copy Module Guid - // - if (ModuleGuid != NULL) { - CopyGuid (ModuleGuid, TempGuid); - if (IsZeroGuid (TempGuid) && (Handle != NULL) && !ModuleGuidIsGet) { - // Handle is GUID - CopyGuid (ModuleGuid, (EFI_GUID *)Handle); - } - } - - // - // Cache the Handle and Guid pairs. - // - if (mCachePairCount < CACHE_HANDLE_GUID_COUNT) { - mCacheHandleGuidTable[mCachePairCount].Handle = Handle; - CopyGuid (&mCacheHandleGuidTable[mCachePairCount].ModuleGuid, ModuleGuid); - AsciiStrCpyS (mCacheHandleGuidTable[mCachePairCount].NameString, FPDT_STRING_EVENT_RECORD_NAME_LENGTH, NameString); - mCachePairCount++; - } - - return Status; -} - -/** - Copies the string from Source into Destination and updates Length with the - size of the string. - - @param Destination - destination of the string copy - @param Source - pointer to the source string which will get copied - @param Length - pointer to a length variable to be updated - -**/ -VOID -CopyStringIntoPerfRecordAndUpdateLength ( - IN OUT CHAR8 *Destination, - IN CONST CHAR8 *Source, - IN OUT UINT8 *Length - ) -{ - UINTN StringLen; - UINTN DestMax; - - ASSERT (Source != NULL); - - if (PcdGetBool (PcdEdkiiFpdtStringRecordEnableOnly)) { - DestMax = STRING_SIZE; - } else { - DestMax = AsciiStrSize (Source); - if (DestMax > STRING_SIZE) { - DestMax = STRING_SIZE; + Status = gBS->HandleProtocol ( + DriverBinding->ImageHandle, + &gEfiLoadedImageProtocolGuid, + (VOID **)&LoadedImage + ); } } - StringLen = AsciiStrLen (Source); - if (StringLen >= DestMax) { - StringLen = DestMax -1; - } - - AsciiStrnCpyS (Destination, DestMax, Source, StringLen); - *Length += (UINT8)DestMax; - - return; + return LoadedImage; } /** - Create performance record with event description and a timestamp. - - @param CallerIdentifier - Image handle or pointer to caller ID GUID. - @param Guid - Pointer to a GUID. - @param String - Pointer to a string describing the measurement. - @param Ticker - 64-bit time stamp. - @param Address - Pointer to a location in memory relevant to the measurement. - @param PerfId - Performance identifier describing the type of measurement. - @param Attribute - The attribute of the measurement. According to attribute can create a start - record for PERF_START/PERF_START_EX, or a end record for PERF_END/PERF_END_EX, - or a general record for other Perf macros. + Get the module name from the PDB file name in the image header. - @retval EFI_SUCCESS - Successfully created performance record. - @retval EFI_OUT_OF_RESOURCES - Ran out of space to store the records. - @retval EFI_INVALID_PARAMETER - Invalid parameter passed to function - NULL - pointer or invalid PerfId. + @param[in] ModuleGuid The GUID of the module. + @param[out] NameString The buffer to store the name string. + @param[in] BufferSize The size of the buffer in bytes. - @retval EFI_SUCCESS - Successfully created performance record - @retval EFI_OUT_OF_RESOURCES - Ran out of space to store the records - @retval EFI_INVALID_PARAMETER - Invalid parameter passed to function - NULL - pointer or invalid PerfId + @retval EFI_SUCCESS The name string is successfully retrieved. + @retval EFI_NOT_FOUND The module name was not found. **/ EFI_STATUS -InsertFpdtRecord ( - IN CONST VOID *CallerIdentifier OPTIONAL, - IN CONST VOID *Guid OPTIONAL, - IN CONST CHAR8 *String OPTIONAL, - IN UINT64 Ticker, - IN UINT64 Address OPTIONAL, - IN UINT16 PerfId, - IN PERF_MEASUREMENT_ATTRIBUTE Attribute +GetNameFromUiSection ( + IN EFI_GUID *ModuleGuid, + OUT CHAR8 *NameString, + IN UINTN BufferSize ) - { - EFI_STATUS Status; - EFI_GUID ModuleGuid; - CHAR8 ModuleName[FPDT_STRING_EVENT_RECORD_NAME_LENGTH]; - FPDT_RECORD_PTR FpdtRecordPtr; - FPDT_RECORD_PTR CachedFpdtRecordPtr; - UINT64 TimeStamp; - CONST CHAR8 *StringPtr; - UINTN DestMax; - UINTN StringLen; - UINT16 ProgressId; - - StringPtr = NULL; - ZeroMem (ModuleName, sizeof (ModuleName)); - - // - // 1. Get the Perf Id for records from PERF_START/PERF_END, PERF_START_EX/PERF_END_EX. - // notes: For other Perf macros (Attribute == PerfEntry), their Id is known. - // - if (Attribute != PerfEntry) { - // - // If PERF_START_EX()/PERF_END_EX() have specified the ProgressID,it has high priority. - // !!! Note: If the Perf is not the known Token used in the core but have same - // ID with the core Token, this case will not be supported. - // And in currtnt usage mode, for the unkown ID, there is a general rule: - // If it is start pref: the lower 4 bits of the ID should be 0. - // If it is end pref: the lower 4 bits of the ID should not be 0. - // If input ID doesn't follow the rule, we will adjust it. - // - if ((PerfId != 0) && (IsKnownID (PerfId)) && (!IsKnownTokens (String))) { - return EFI_INVALID_PARAMETER; - } else if ((PerfId != 0) && (!IsKnownID (PerfId)) && (!IsKnownTokens (String))) { - if ((Attribute == PerfStartEntry) && ((PerfId & 0x000F) != 0)) { - PerfId &= 0xFFF0; - } else if ((Attribute == PerfEndEntry) && ((PerfId & 0x000F) == 0)) { - PerfId += 1; - } - } - - if (PerfId == 0) { - // - // Get ProgressID form the String Token. - // - Status = GetFpdtRecordId (Attribute, CallerIdentifier, String, &ProgressId); - if (EFI_ERROR (Status)) { - return Status; - } - - PerfId = ProgressId; - } - } - - // - // 2. Get the buffer to store the FPDT record. - // - Status = GetFpdtRecordPtr (FPDT_MAX_PERF_RECORD_SIZE, &FpdtRecordPtr); - if (EFI_ERROR (Status)) { - return Status; - } - - // - // 3. Get the TimeStamp. - // - if (Ticker == 0) { - Ticker = GetPerformanceCounter (); - TimeStamp = GetTimeInNanoSecond (Ticker); - } else if (Ticker == 1) { - TimeStamp = 0; - } else { - TimeStamp = GetTimeInNanoSecond (Ticker); - } - - // - // 4. Fill in the FPDT record according to different Performance Identifier. - // - switch (PerfId) { - case MODULE_START_ID: - case MODULE_END_ID: - GetModuleInfoFromHandle ((EFI_HANDLE)CallerIdentifier, ModuleName, sizeof (ModuleName), &ModuleGuid); - StringPtr = ModuleName; - // - // Cache the offset of start image start record and use to update the start image end record if needed. - // - if ((PerfId == MODULE_START_ID) && (Attribute == PerfEntry)) { - mCachedLength = mSmmBootPerformanceTable->Header.Length; - } - - if (!PcdGetBool (PcdEdkiiFpdtStringRecordEnableOnly)) { - FpdtRecordPtr.GuidEvent->Header.Type = FPDT_GUID_EVENT_TYPE; - FpdtRecordPtr.GuidEvent->Header.Length = sizeof (FPDT_GUID_EVENT_RECORD); - FpdtRecordPtr.GuidEvent->Header.Revision = FPDT_RECORD_REVISION_1; - FpdtRecordPtr.GuidEvent->ProgressID = PerfId; - FpdtRecordPtr.GuidEvent->Timestamp = TimeStamp; - CopyMem (&FpdtRecordPtr.GuidEvent->Guid, &ModuleGuid, sizeof (FpdtRecordPtr.GuidEvent->Guid)); - if ((CallerIdentifier == NULL) && (PerfId == MODULE_END_ID) && (mCachedLength != 0)) { - CachedFpdtRecordPtr.RecordHeader = (EFI_ACPI_5_0_FPDT_PERFORMANCE_RECORD_HEADER *)((UINT8 *)mSmmBootPerformanceTable + mCachedLength); - CopyMem (&FpdtRecordPtr.GuidEvent->Guid, &CachedFpdtRecordPtr.GuidEvent->Guid, sizeof (FpdtRecordPtr.GuidEvent->Guid)); - mCachedLength = 0; - } - } - - break; - - case MODULE_LOADIMAGE_START_ID: - case MODULE_LOADIMAGE_END_ID: - GetModuleInfoFromHandle ((EFI_HANDLE)CallerIdentifier, ModuleName, sizeof (ModuleName), &ModuleGuid); - StringPtr = ModuleName; - if (PerfId == MODULE_LOADIMAGE_START_ID) { - mLoadImageCount++; - // - // Cache the offset of load image start record and use to be updated by the load image end record if needed. - // - if ((CallerIdentifier == NULL) && (Attribute == PerfEntry)) { - mCachedLength = mSmmBootPerformanceTable->Header.Length; - } - } - - if (!PcdGetBool (PcdEdkiiFpdtStringRecordEnableOnly)) { - FpdtRecordPtr.GuidQwordEvent->Header.Type = FPDT_GUID_QWORD_EVENT_TYPE; - FpdtRecordPtr.GuidQwordEvent->Header.Length = sizeof (FPDT_GUID_QWORD_EVENT_RECORD); - FpdtRecordPtr.GuidQwordEvent->Header.Revision = FPDT_RECORD_REVISION_1; - FpdtRecordPtr.GuidQwordEvent->ProgressID = PerfId; - FpdtRecordPtr.GuidQwordEvent->Timestamp = TimeStamp; - FpdtRecordPtr.GuidQwordEvent->Qword = mLoadImageCount; - CopyMem (&FpdtRecordPtr.GuidQwordEvent->Guid, &ModuleGuid, sizeof (FpdtRecordPtr.GuidQwordEvent->Guid)); - if ((PerfId == MODULE_LOADIMAGE_END_ID) && (mCachedLength != 0)) { - CachedFpdtRecordPtr.RecordHeader = (EFI_ACPI_5_0_FPDT_PERFORMANCE_RECORD_HEADER *)((UINT8 *)mSmmBootPerformanceTable + mCachedLength); - CopyMem (&CachedFpdtRecordPtr.GuidQwordEvent->Guid, &ModuleGuid, sizeof (CachedFpdtRecordPtr.GuidQwordEvent->Guid)); - mCachedLength = 0; - } - } - - break; - - case PERF_EVENTSIGNAL_START_ID: - case PERF_EVENTSIGNAL_END_ID: - case PERF_CALLBACK_START_ID: - case PERF_CALLBACK_END_ID: - if ((String == NULL) || (Guid == NULL)) { - return EFI_INVALID_PARAMETER; - } - - StringPtr = String; - if (AsciiStrLen (String) == 0) { - StringPtr = "unknown name"; - } - - if (!PcdGetBool (PcdEdkiiFpdtStringRecordEnableOnly)) { - FpdtRecordPtr.DualGuidStringEvent->Header.Type = FPDT_DUAL_GUID_STRING_EVENT_TYPE; - FpdtRecordPtr.DualGuidStringEvent->Header.Length = sizeof (FPDT_DUAL_GUID_STRING_EVENT_RECORD); - FpdtRecordPtr.DualGuidStringEvent->Header.Revision = FPDT_RECORD_REVISION_1; - FpdtRecordPtr.DualGuidStringEvent->ProgressID = PerfId; - FpdtRecordPtr.DualGuidStringEvent->Timestamp = TimeStamp; - CopyMem (&FpdtRecordPtr.DualGuidStringEvent->Guid1, CallerIdentifier, sizeof (FpdtRecordPtr.DualGuidStringEvent->Guid1)); - CopyMem (&FpdtRecordPtr.DualGuidStringEvent->Guid2, Guid, sizeof (FpdtRecordPtr.DualGuidStringEvent->Guid2)); - CopyStringIntoPerfRecordAndUpdateLength (FpdtRecordPtr.DualGuidStringEvent->String, StringPtr, &FpdtRecordPtr.DualGuidStringEvent->Header.Length); - } - - break; - - case PERF_EVENT_ID: - case PERF_FUNCTION_START_ID: - case PERF_FUNCTION_END_ID: - case PERF_INMODULE_START_ID: - case PERF_INMODULE_END_ID: - case PERF_CROSSMODULE_START_ID: - case PERF_CROSSMODULE_END_ID: - // MU_CHANGE [BEGIN] - CodeQL change - Status = GetModuleInfoFromHandle ((EFI_HANDLE)CallerIdentifier, ModuleName, sizeof (ModuleName), &ModuleGuid); - if (EFI_ERROR (Status)) { - DEBUG ((DEBUG_ERROR, "Failed to get Module Info from Handle! Status = %r\n", Status)); - } - - StringPtr = NULL; - - if (String != NULL) { - StringPtr = String; - } else if (ModuleName != NULL) { - StringPtr = ModuleName; - } - - if ((StringPtr == NULL) || (AsciiStrLen (StringPtr) == 0)) { - StringPtr = "unknown name"; - } - - // MU_CHANGE [END] - CodeQL change - - if (!PcdGetBool (PcdEdkiiFpdtStringRecordEnableOnly)) { - FpdtRecordPtr.DynamicStringEvent->Header.Type = FPDT_DYNAMIC_STRING_EVENT_TYPE; - FpdtRecordPtr.DynamicStringEvent->Header.Length = sizeof (FPDT_DYNAMIC_STRING_EVENT_RECORD); - FpdtRecordPtr.DynamicStringEvent->Header.Revision = FPDT_RECORD_REVISION_1; - FpdtRecordPtr.DynamicStringEvent->ProgressID = PerfId; - FpdtRecordPtr.DynamicStringEvent->Timestamp = TimeStamp; - CopyMem (&FpdtRecordPtr.DynamicStringEvent->Guid, &ModuleGuid, sizeof (FpdtRecordPtr.DynamicStringEvent->Guid)); - CopyStringIntoPerfRecordAndUpdateLength (FpdtRecordPtr.DynamicStringEvent->String, StringPtr, &FpdtRecordPtr.DynamicStringEvent->Header.Length); - } - - break; - - default: - if (Attribute != PerfEntry) { - // MU_CHANGE [BEGIN] - CodeQL change - Status = GetModuleInfoFromHandle ((EFI_HANDLE)CallerIdentifier, ModuleName, sizeof (ModuleName), &ModuleGuid); - if (EFI_ERROR (Status)) { - DEBUG ((DEBUG_ERROR, "Failed to get Module Info from Handle! Status = %r\n", Status)); - } - - StringPtr = NULL; - - if (String != NULL) { - StringPtr = String; - } else if (ModuleName != NULL) { - StringPtr = ModuleName; - } - - if ((StringPtr == NULL) || (AsciiStrLen (StringPtr) == 0)) { - StringPtr = "unknown name"; - } - - // MU_CHANGE [END] - CodeQL change - - if (!PcdGetBool (PcdEdkiiFpdtStringRecordEnableOnly)) { - FpdtRecordPtr.DynamicStringEvent->Header.Type = FPDT_DYNAMIC_STRING_EVENT_TYPE; - FpdtRecordPtr.DynamicStringEvent->Header.Length = sizeof (FPDT_DYNAMIC_STRING_EVENT_RECORD); - FpdtRecordPtr.DynamicStringEvent->Header.Revision = FPDT_RECORD_REVISION_1; - FpdtRecordPtr.DynamicStringEvent->ProgressID = PerfId; - FpdtRecordPtr.DynamicStringEvent->Timestamp = TimeStamp; - CopyMem (&FpdtRecordPtr.DynamicStringEvent->Guid, &ModuleGuid, sizeof (FpdtRecordPtr.DynamicStringEvent->Guid)); - CopyStringIntoPerfRecordAndUpdateLength (FpdtRecordPtr.DynamicStringEvent->String, StringPtr, &FpdtRecordPtr.DynamicStringEvent->Header.Length); - } - } else { - return EFI_INVALID_PARAMETER; - } - - break; - } - - // - // 4.2 When PcdEdkiiFpdtStringRecordEnableOnly==TRUE, create string record for all Perf entries. - // - if (PcdGetBool (PcdEdkiiFpdtStringRecordEnableOnly)) { - if (StringPtr == NULL) { - return EFI_INVALID_PARAMETER; - } - - FpdtRecordPtr.DynamicStringEvent->Header.Type = FPDT_DYNAMIC_STRING_EVENT_TYPE; - FpdtRecordPtr.DynamicStringEvent->Header.Length = sizeof (FPDT_DYNAMIC_STRING_EVENT_RECORD); - FpdtRecordPtr.DynamicStringEvent->Header.Revision = FPDT_RECORD_REVISION_1; - FpdtRecordPtr.DynamicStringEvent->ProgressID = PerfId; - FpdtRecordPtr.DynamicStringEvent->Timestamp = TimeStamp; - if (Guid != NULL) { - // - // Cache the event guid in string event record. - // - CopyMem (&FpdtRecordPtr.DynamicStringEvent->Guid, Guid, sizeof (FpdtRecordPtr.DynamicStringEvent->Guid)); - } else { - CopyMem (&FpdtRecordPtr.DynamicStringEvent->Guid, &ModuleGuid, sizeof (FpdtRecordPtr.DynamicStringEvent->Guid)); - } - - if (AsciiStrLen (StringPtr) == 0) { - StringPtr = "unknown name"; - } - - CopyStringIntoPerfRecordAndUpdateLength (FpdtRecordPtr.DynamicStringEvent->String, StringPtr, &FpdtRecordPtr.DynamicStringEvent->Header.Length); - - if ((PerfId == MODULE_LOADIMAGE_START_ID) || (PerfId == MODULE_END_ID)) { - FpdtRecordPtr.DynamicStringEvent->Header.Length = (UINT8)(sizeof (FPDT_DYNAMIC_STRING_EVENT_RECORD)+ STRING_SIZE); + EFI_STATUS Status; + CHAR16 *StringPtr; + UINTN Index; + UINTN StringSize; + + StringPtr = NULL; + StringSize = 0; + Status = GetSectionFromAnyFv ( + ModuleGuid, + EFI_SECTION_USER_INTERFACE, + 0, + (VOID **)&StringPtr, + &StringSize + ); + if (!EFI_ERROR (Status)) { + for (Index = 0; Index < BufferSize - 1 && StringPtr[Index] != 0; Index++) { + NameString[Index] = (CHAR8)StringPtr[Index]; } - if (((PerfId == MODULE_LOADIMAGE_END_ID) || (PerfId == MODULE_END_ID)) && (mCachedLength != 0)) { - CachedFpdtRecordPtr.RecordHeader = (EFI_ACPI_5_0_FPDT_PERFORMANCE_RECORD_HEADER *)((UINT8 *)mSmmBootPerformanceTable + mCachedLength); - if (PerfId == MODULE_LOADIMAGE_END_ID) { - DestMax = CachedFpdtRecordPtr.DynamicStringEvent->Header.Length - sizeof (FPDT_DYNAMIC_STRING_EVENT_RECORD); - StringLen = AsciiStrLen (StringPtr); - if (StringLen >= DestMax) { - StringLen = DestMax -1; - } - - CopyMem (&CachedFpdtRecordPtr.DynamicStringEvent->Guid, &ModuleGuid, sizeof (CachedFpdtRecordPtr.DynamicStringEvent->Guid)); - AsciiStrnCpyS (CachedFpdtRecordPtr.DynamicStringEvent->String, DestMax, StringPtr, StringLen); - } else if (PerfId == MODULE_END_ID) { - DestMax = FpdtRecordPtr.DynamicStringEvent->Header.Length - sizeof (FPDT_DYNAMIC_STRING_EVENT_RECORD); - StringLen = AsciiStrLen (CachedFpdtRecordPtr.DynamicStringEvent->String); - if (StringLen >= DestMax) { - StringLen = DestMax -1; - } - - CopyMem (&FpdtRecordPtr.DynamicStringEvent->Guid, &CachedFpdtRecordPtr.DynamicStringEvent->Guid, sizeof (CachedFpdtRecordPtr.DynamicStringEvent->Guid)); - AsciiStrnCpyS (FpdtRecordPtr.DynamicStringEvent->String, DestMax, CachedFpdtRecordPtr.DynamicStringEvent->String, StringLen); - } - - mCachedLength = 0; - } + NameString[Index] = 0; + FreePool (StringPtr); } - // - // 5. Update the length of the used buffer after fill in the record. - // - mPerformanceLength += FpdtRecordPtr.RecordHeader->Length; - mSmmBootPerformanceTable->Header.Length += FpdtRecordPtr.RecordHeader->Length; - - return EFI_SUCCESS; -} - -/** - Communication service SMI Handler entry. - - This SMI handler provides services for report MM boot records. - - Caution: This function may receive untrusted input. - Communicate buffer and buffer size are external input, so this function will do basic validation. - - @param[in] DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister(). - @param[in] RegisterContext Points to an optional handler context which was specified when the - handler was registered. - @param[in, out] CommBuffer A pointer to a collection of data in memory that will - be conveyed from a non-MM environment into an MM environment. - @param[in, out] CommBufferSize The size of the CommBuffer. - - @retval EFI_SUCCESS The interrupt was handled and quiesced. No other handlers - should still be called. - @retval EFI_WARN_INTERRUPT_SOURCE_QUIESCED The interrupt has been quiesced but other handlers should - still be called. - @retval EFI_WARN_INTERRUPT_SOURCE_PENDING The interrupt is still pending and other handlers should still - be called. - @retval EFI_INTERRUPT_PENDING The interrupt could not be quiesced. - -**/ -EFI_STATUS -EFIAPI -FpdtSmiHandler ( - IN EFI_HANDLE DispatchHandle, - IN CONST VOID *RegisterContext, - IN OUT VOID *CommBuffer, - IN OUT UINTN *CommBufferSize - ) -{ - EFI_STATUS Status; - SMM_BOOT_RECORD_COMMUNICATE *SmmCommData; - UINTN BootRecordOffset; - UINTN BootRecordSize; - VOID *BootRecordData; - UINTN TempCommBufferSize; - UINT8 *BootRecordBuffer; - - // - // If input is invalid, stop processing this SMI - // - if ((CommBuffer == NULL) || (CommBufferSize == NULL)) { - return EFI_SUCCESS; - } - - TempCommBufferSize = *CommBufferSize; - - if (TempCommBufferSize < sizeof (SMM_BOOT_RECORD_COMMUNICATE)) { - return EFI_SUCCESS; - } - - if (!SmmIsBufferOutsideSmmValid ((UINTN)CommBuffer, TempCommBufferSize)) { - DEBUG ((DEBUG_ERROR, "FpdtSmiHandler: MM communication data buffer in MMRAM or overflow!\n")); - return EFI_SUCCESS; - } - - SmmCommData = (SMM_BOOT_RECORD_COMMUNICATE *)CommBuffer; - - Status = EFI_SUCCESS; - - switch (SmmCommData->Function) { - case SMM_FPDT_FUNCTION_GET_BOOT_RECORD_SIZE: - if (mSmmBootPerformanceTable != NULL) { - mBootRecordSize = mSmmBootPerformanceTable->Header.Length - sizeof (SMM_BOOT_PERFORMANCE_TABLE); - } - - SmmCommData->BootRecordSize = mBootRecordSize; - break; - - case SMM_FPDT_FUNCTION_GET_BOOT_RECORD_DATA: - Status = EFI_UNSUPPORTED; - break; - - case SMM_FPDT_FUNCTION_GET_BOOT_RECORD_DATA_BY_OFFSET: - BootRecordOffset = SmmCommData->BootRecordOffset; - BootRecordData = SmmCommData->BootRecordData; - BootRecordSize = SmmCommData->BootRecordSize; - if ((BootRecordData == NULL) || (BootRecordOffset >= mBootRecordSize)) { - Status = EFI_INVALID_PARAMETER; - break; - } - - // - // Sanity check - // - if (BootRecordSize > mBootRecordSize - BootRecordOffset) { - BootRecordSize = mBootRecordSize - BootRecordOffset; - } - - SmmCommData->BootRecordSize = BootRecordSize; - if (!SmmIsBufferOutsideSmmValid ((UINTN)BootRecordData, BootRecordSize)) { - DEBUG ((DEBUG_ERROR, "FpdtSmiHandler: MM Data buffer in MMRAM or overflow!\n")); - Status = EFI_ACCESS_DENIED; - break; - } - - BootRecordBuffer = ((UINT8 *)(mSmmBootPerformanceTable)) + sizeof (SMM_BOOT_PERFORMANCE_TABLE); - CopyMem ( - (UINT8 *)BootRecordData, - BootRecordBuffer + BootRecordOffset, - BootRecordSize - ); - mFpdtDataIsReported = TRUE; - break; - - default: - Status = EFI_UNSUPPORTED; - } - - SmmCommData->ReturnStatus = Status; - - return EFI_SUCCESS; -} - -/** - This is the Event call back function is triggered in SMM to notify the Library - the system is entering runtime phase. - - @param[in] Protocol Points to the protocol's unique identifier - @param[in] Interface Points to the interface instance - @param[in] Handle The handle on which the interface was installed - - @retval EFI_SUCCESS SmmAtRuntimeCallBack runs successfully - **/ -EFI_STATUS -EFIAPI -SmmCorePerformanceLibExitBootServicesCallback ( - IN CONST EFI_GUID *Protocol, - IN VOID *Interface, - IN EFI_HANDLE Handle - ) -{ - // - // Disable performance measurement after ExitBootServices because - // 1. Performance measurement might impact SMI latency at runtime; - // 2. Performance log is copied to non SMRAM at ReadyToBoot so runtime performance - // log is not useful. - // - mPerformanceMeasurementEnabled = FALSE; - - return EFI_SUCCESS; + return Status; } /** - SmmBase2 protocol notify callback function, when SMST and SMM memory service get initialized - this function is callbacked to initialize the Smm Performance Lib + Initializes the MM Core Performance library after MM services are available. - @param Event The event of notify protocol. - @param Context Notify event context. + @param[in] Event The event of notify protocol. + @param[in] Context Notify event context. **/ VOID @@ -992,35 +192,10 @@ InitializeSmmCorePerformanceLib ( IN VOID *Context ) { - EFI_HANDLE Handle; - EFI_HANDLE SmiHandle; EFI_STATUS Status; PERFORMANCE_PROPERTY *PerformanceProperty; - VOID *Registration; - // - // Initialize spin lock - // - InitializeSpinLock (&mSmmFpdtLock); - - // - // Install the protocol interfaces for SMM performance library instance. - // - Handle = NULL; - Status = gSmst->SmmInstallProtocolInterface ( - &Handle, - &gEdkiiSmmPerformanceMeasurementProtocolGuid, - EFI_NATIVE_INTERFACE, - &mPerformanceMeasurementInterface - ); - ASSERT_EFI_ERROR (Status); - - // - // Register SMI handler. - // - SmiHandle = NULL; - Status = gSmst->SmiHandlerRegister (FpdtSmiHandler, &gEfiFirmwarePerformanceGuid, &SmiHandle); - ASSERT_EFI_ERROR (Status); + InitializeMmCorePerformanceLibCommon (); Status = EfiGetSystemConfigurationTable (&gPerformanceProtocolGuid, (VOID **)&PerformanceProperty); if (EFI_ERROR (Status)) { @@ -1036,16 +211,6 @@ InitializeSmmCorePerformanceLib ( Status = gBS->InstallConfigurationTable (&gPerformanceProtocolGuid, &mPerformanceProperty); ASSERT_EFI_ERROR (Status); } - - // - // Register callback function for ExitBootServices event. - // - Status = gSmst->SmmRegisterProtocolNotify ( - &gEdkiiSmmExitBootServicesProtocolGuid, - SmmCorePerformanceLibExitBootServicesCallback, - &Registration - ); - ASSERT_EFI_ERROR (Status); } /** @@ -1073,6 +238,7 @@ SmmCorePerformanceLibConstructor ( mPerformanceMeasurementEnabled = (BOOLEAN)((PcdGet8 (PcdPerformanceLibraryPropertyMask) & PERFORMANCE_LIBRARY_PROPERTY_MEASUREMENT_ENABLED) != 0); if (!PerformanceMeasurementEnabled ()) { + DEBUG ((DEBUG_WARN, "[%a] - Performance library is linked but performance tracing is not enabled.\n", __func__)); // // Do not initialize performance infrastructure if not required. // @@ -1104,401 +270,3 @@ SmmCorePerformanceLibConstructor ( return EFI_SUCCESS; } - -/** - Create performance record with event description and a timestamp. - - @param CallerIdentifier - Image handle or pointer to caller ID GUID. - @param Guid - Pointer to a GUID. - @param String - Pointer to a string describing the measurement. - @param TimeStamp - 64-bit time stamp. - @param Address - Pointer to a location in memory relevant to the measurement. - @param Identifier - Performance identifier describing the type of measurement. - @param Attribute - The attribute of the measurement. According to attribute can create a start - record for PERF_START/PERF_START_EX, or a end record for PERF_END/PERF_END_EX, - or a general record for other Perf macros. - - @retval EFI_SUCCESS - Successfully created performance record. - @retval EFI_OUT_OF_RESOURCES - Ran out of space to store the records. - @retval EFI_INVALID_PARAMETER - Invalid parameter passed to function - NULL - pointer or invalid PerfId. -**/ -EFI_STATUS -EFIAPI -CreatePerformanceMeasurement ( - IN CONST VOID *CallerIdentifier OPTIONAL, - IN CONST VOID *Guid OPTIONAL, - IN CONST CHAR8 *String OPTIONAL, - IN UINT64 TimeStamp OPTIONAL, - IN UINT64 Address OPTIONAL, - IN UINT32 Identifier, - IN PERF_MEASUREMENT_ATTRIBUTE Attribute - ) -{ - EFI_STATUS Status; - - Status = EFI_SUCCESS; - - AcquireSpinLock (&mSmmFpdtLock); - Status = InsertFpdtRecord (CallerIdentifier, Guid, String, TimeStamp, Address, (UINT16)Identifier, Attribute); - ReleaseSpinLock (&mSmmFpdtLock); - return Status; -} - -/** - Adds a record at the end of the performance measurement log - that records the start time of a performance measurement. - - Adds a record to the end of the performance measurement log - that contains the Handle, Token, Module and Identifier. - The end time of the new record must be set to zero. - If TimeStamp is not zero, then TimeStamp is used to fill in the start time in the record. - If TimeStamp is zero, the start time in the record is filled in with the value - read from the current time stamp. - - @param Handle Pointer to environment specific context used - to identify the component being measured. - @param Token Pointer to a Null-terminated ASCII string - that identifies the component being measured. - @param Module Pointer to a Null-terminated ASCII string - that identifies the module being measured. - @param TimeStamp 64-bit time stamp. - @param Identifier 32-bit identifier. If the value is 0, the created record - is same as the one created by StartPerformanceMeasurement. - - @retval RETURN_SUCCESS The start of the measurement was recorded. - @retval RETURN_OUT_OF_RESOURCES There are not enough resources to record the measurement. - -**/ -RETURN_STATUS -EFIAPI -StartPerformanceMeasurementEx ( - IN CONST VOID *Handle OPTIONAL, - IN CONST CHAR8 *Token OPTIONAL, - IN CONST CHAR8 *Module OPTIONAL, - IN UINT64 TimeStamp, - IN UINT32 Identifier - ) -{ - CONST CHAR8 *String; - - if (Token != NULL) { - String = Token; - } else if (Module != NULL) { - String = Module; - } else { - String = NULL; - } - - return (RETURN_STATUS)CreatePerformanceMeasurement (Handle, NULL, String, TimeStamp, 0, Identifier, PerfStartEntry); -} - -/** - Searches the performance measurement log from the beginning of the log - for the first matching record that contains a zero end time and fills in a valid end time. - - Searches the performance measurement log from the beginning of the log - for the first record that matches Handle, Token, Module and Identifier and has an end time value of zero. - If the record can not be found then return RETURN_NOT_FOUND. - If the record is found and TimeStamp is not zero, - then the end time in the record is filled in with the value specified by TimeStamp. - If the record is found and TimeStamp is zero, then the end time in the matching record - is filled in with the current time stamp value. - - @param Handle Pointer to environment specific context used - to identify the component being measured. - @param Token Pointer to a Null-terminated ASCII string - that identifies the component being measured. - @param Module Pointer to a Null-terminated ASCII string - that identifies the module being measured. - @param TimeStamp 64-bit time stamp. - @param Identifier 32-bit identifier. If the value is 0, the found record - is same as the one found by EndPerformanceMeasurement. - - @retval RETURN_SUCCESS The end of the measurement was recorded. - @retval RETURN_NOT_FOUND The specified measurement record could not be found. - -**/ -RETURN_STATUS -EFIAPI -EndPerformanceMeasurementEx ( - IN CONST VOID *Handle OPTIONAL, - IN CONST CHAR8 *Token OPTIONAL, - IN CONST CHAR8 *Module OPTIONAL, - IN UINT64 TimeStamp, - IN UINT32 Identifier - ) -{ - CONST CHAR8 *String; - - if (Token != NULL) { - String = Token; - } else if (Module != NULL) { - String = Module; - } else { - String = NULL; - } - - return (RETURN_STATUS)CreatePerformanceMeasurement (Handle, NULL, String, TimeStamp, 0, Identifier, PerfEndEntry); -} - -/** - Attempts to retrieve a performance measurement log entry from the performance measurement log. - It can also retrieve the log created by StartPerformanceMeasurement and EndPerformanceMeasurement, - and then assign the Identifier with 0. - - !!! Not Support!!! - - Attempts to retrieve the performance log entry specified by LogEntryKey. If LogEntryKey is - zero on entry, then an attempt is made to retrieve the first entry from the performance log, - and the key for the second entry in the log is returned. If the performance log is empty, - then no entry is retrieved and zero is returned. If LogEntryKey is not zero, then the performance - log entry associated with LogEntryKey is retrieved, and the key for the next entry in the log is - returned. If LogEntryKey is the key for the last entry in the log, then the last log entry is - retrieved and an implementation specific non-zero key value that specifies the end of the performance - log is returned. If LogEntryKey is equal this implementation specific non-zero key value, then no entry - is retrieved and zero is returned. In the cases where a performance log entry can be returned, - the log entry is returned in Handle, Token, Module, StartTimeStamp, EndTimeStamp and Identifier. - If LogEntryKey is not a valid log entry key for the performance measurement log, then ASSERT(). - If Handle is NULL, then ASSERT(). - If Token is NULL, then ASSERT(). - If Module is NULL, then ASSERT(). - If StartTimeStamp is NULL, then ASSERT(). - If EndTimeStamp is NULL, then ASSERT(). - If Identifier is NULL, then ASSERT(). - - @param LogEntryKey On entry, the key of the performance measurement log entry to retrieve. - 0, then the first performance measurement log entry is retrieved. - On exit, the key of the next performance log entry. - @param Handle Pointer to environment specific context used to identify the component - being measured. - @param Token Pointer to a Null-terminated ASCII string that identifies the component - being measured. - @param Module Pointer to a Null-terminated ASCII string that identifies the module - being measured. - @param StartTimeStamp Pointer to the 64-bit time stamp that was recorded when the measurement - was started. - @param EndTimeStamp Pointer to the 64-bit time stamp that was recorded when the measurement - was ended. - @param Identifier Pointer to the 32-bit identifier that was recorded. - - @return The key for the next performance log entry (in general case). - -**/ -UINTN -EFIAPI -GetPerformanceMeasurementEx ( - IN UINTN LogEntryKey, - OUT CONST VOID **Handle, - OUT CONST CHAR8 **Token, - OUT CONST CHAR8 **Module, - OUT UINT64 *StartTimeStamp, - OUT UINT64 *EndTimeStamp, - OUT UINT32 *Identifier - ) -{ - return 0; -} - -/** - Adds a record at the end of the performance measurement log - that records the start time of a performance measurement. - - Adds a record to the end of the performance measurement log - that contains the Handle, Token, and Module. - The end time of the new record must be set to zero. - If TimeStamp is not zero, then TimeStamp is used to fill in the start time in the record. - If TimeStamp is zero, the start time in the record is filled in with the value - read from the current time stamp. - - @param Handle Pointer to environment specific context used - to identify the component being measured. - @param Token Pointer to a Null-terminated ASCII string - that identifies the component being measured. - @param Module Pointer to a Null-terminated ASCII string - that identifies the module being measured. - @param TimeStamp 64-bit time stamp. - - @retval RETURN_SUCCESS The start of the measurement was recorded. - @retval RETURN_OUT_OF_RESOURCES There are not enough resources to record the measurement. - -**/ -RETURN_STATUS -EFIAPI -StartPerformanceMeasurement ( - IN CONST VOID *Handle OPTIONAL, - IN CONST CHAR8 *Token OPTIONAL, - IN CONST CHAR8 *Module OPTIONAL, - IN UINT64 TimeStamp - ) -{ - return StartPerformanceMeasurementEx (Handle, Token, Module, TimeStamp, 0); -} - -/** - Searches the performance measurement log from the beginning of the log - for the first matching record that contains a zero end time and fills in a valid end time. - - Searches the performance measurement log from the beginning of the log - for the first record that matches Handle, Token, and Module and has an end time value of zero. - If the record can not be found then return RETURN_NOT_FOUND. - If the record is found and TimeStamp is not zero, - then the end time in the record is filled in with the value specified by TimeStamp. - If the record is found and TimeStamp is zero, then the end time in the matching record - is filled in with the current time stamp value. - - @param Handle Pointer to environment specific context used - to identify the component being measured. - @param Token Pointer to a Null-terminated ASCII string - that identifies the component being measured. - @param Module Pointer to a Null-terminated ASCII string - that identifies the module being measured. - @param TimeStamp 64-bit time stamp. - - @retval RETURN_SUCCESS The end of the measurement was recorded. - @retval RETURN_NOT_FOUND The specified measurement record could not be found. - -**/ -RETURN_STATUS -EFIAPI -EndPerformanceMeasurement ( - IN CONST VOID *Handle OPTIONAL, - IN CONST CHAR8 *Token OPTIONAL, - IN CONST CHAR8 *Module OPTIONAL, - IN UINT64 TimeStamp - ) -{ - return EndPerformanceMeasurementEx (Handle, Token, Module, TimeStamp, 0); -} - -/** - Attempts to retrieve a performance measurement log entry from the performance measurement log. - It can also retrieve the log created by StartPerformanceMeasurementEx and EndPerformanceMeasurementEx, - and then eliminate the Identifier. - - !!! Not Support!!! - - Attempts to retrieve the performance log entry specified by LogEntryKey. If LogEntryKey is - zero on entry, then an attempt is made to retrieve the first entry from the performance log, - and the key for the second entry in the log is returned. If the performance log is empty, - then no entry is retrieved and zero is returned. If LogEntryKey is not zero, then the performance - log entry associated with LogEntryKey is retrieved, and the key for the next entry in the log is - returned. If LogEntryKey is the key for the last entry in the log, then the last log entry is - retrieved and an implementation specific non-zero key value that specifies the end of the performance - log is returned. If LogEntryKey is equal this implementation specific non-zero key value, then no entry - is retrieved and zero is returned. In the cases where a performance log entry can be returned, - the log entry is returned in Handle, Token, Module, StartTimeStamp, and EndTimeStamp. - If LogEntryKey is not a valid log entry key for the performance measurement log, then ASSERT(). - If Handle is NULL, then ASSERT(). - If Token is NULL, then ASSERT(). - If Module is NULL, then ASSERT(). - If StartTimeStamp is NULL, then ASSERT(). - If EndTimeStamp is NULL, then ASSERT(). - - @param LogEntryKey On entry, the key of the performance measurement log entry to retrieve. - 0, then the first performance measurement log entry is retrieved. - On exit, the key of the next performance log entry. - @param Handle Pointer to environment specific context used to identify the component - being measured. - @param Token Pointer to a Null-terminated ASCII string that identifies the component - being measured. - @param Module Pointer to a Null-terminated ASCII string that identifies the module - being measured. - @param StartTimeStamp Pointer to the 64-bit time stamp that was recorded when the measurement - was started. - @param EndTimeStamp Pointer to the 64-bit time stamp that was recorded when the measurement - was ended. - - @return The key for the next performance log entry (in general case). - -**/ -UINTN -EFIAPI -GetPerformanceMeasurement ( - IN UINTN LogEntryKey, - OUT CONST VOID **Handle, - OUT CONST CHAR8 **Token, - OUT CONST CHAR8 **Module, - OUT UINT64 *StartTimeStamp, - OUT UINT64 *EndTimeStamp - ) -{ - return 0; -} - -/** - Returns TRUE if the performance measurement macros are enabled. - - This function returns TRUE if the PERFORMANCE_LIBRARY_PROPERTY_MEASUREMENT_ENABLED bit of - PcdPerformanceLibraryPropertyMask is set. Otherwise FALSE is returned. - - @retval TRUE The PERFORMANCE_LIBRARY_PROPERTY_MEASUREMENT_ENABLED bit of - PcdPerformanceLibraryPropertyMask is set. - @retval FALSE The PERFORMANCE_LIBRARY_PROPERTY_MEASUREMENT_ENABLED bit of - PcdPerformanceLibraryPropertyMask is clear. - -**/ -BOOLEAN -EFIAPI -PerformanceMeasurementEnabled ( - VOID - ) -{ - return mPerformanceMeasurementEnabled; -} - -/** - Create performance record with event description and a timestamp. - - @param CallerIdentifier - Image handle or pointer to caller ID GUID - @param Guid - Pointer to a GUID - @param String - Pointer to a string describing the measurement - @param Address - Pointer to a location in memory relevant to the measurement - @param Identifier - Performance identifier describing the type of measurement - - @retval RETURN_SUCCESS - Successfully created performance record - @retval RETURN_OUT_OF_RESOURCES - Ran out of space to store the records - @retval RETURN_INVALID_PARAMETER - Invalid parameter passed to function - NULL - pointer or invalid PerfId - -**/ -RETURN_STATUS -EFIAPI -LogPerformanceMeasurement ( - IN CONST VOID *CallerIdentifier, - IN CONST VOID *Guid OPTIONAL, - IN CONST CHAR8 *String OPTIONAL, - IN UINT64 Address OPTIONAL, - IN UINT32 Identifier - ) -{ - return (RETURN_STATUS)CreatePerformanceMeasurement (CallerIdentifier, Guid, String, 0, Address, Identifier, PerfEntry); -} - -/** - Check whether the specified performance measurement can be logged. - - This function returns TRUE when the PERFORMANCE_LIBRARY_PROPERTY_MEASUREMENT_ENABLED bit of PcdPerformanceLibraryPropertyMask is set - and the Type disable bit in PcdPerformanceLibraryPropertyMask is not set. - - @param Type - Type of the performance measurement entry. - - @retval TRUE The performance measurement can be logged. - @retval FALSE The performance measurement can NOT be logged. - -**/ -BOOLEAN -EFIAPI -LogPerformanceMeasurementEnabled ( - IN CONST UINTN Type - ) -{ - // - // When Performance measurement is enabled and the type is not filtered, the performance can be logged. - // - if (PerformanceMeasurementEnabled () && ((PcdGet8 (PcdPerformanceLibraryPropertyMask) & Type) == 0)) { - return TRUE; - } - - return FALSE; -} diff --git a/MdeModulePkg/Library/SmmCorePerformanceLib/SmmCorePerformanceLib.inf b/MdeModulePkg/Library/SmmCorePerformanceLib/SmmCorePerformanceLib.inf index 9a7e14e80c..0a2b6009c1 100644 --- a/MdeModulePkg/Library/SmmCorePerformanceLib/SmmCorePerformanceLib.inf +++ b/MdeModulePkg/Library/SmmCorePerformanceLib/SmmCorePerformanceLib.inf @@ -9,20 +9,20 @@ # SMM Performance and PerformanceEx Protocol are installed at the very beginning of SMM phase. # # Copyright (c) 2011 - 2023, Intel Corporation. All rights reserved.
+# Copyright (c) Microsoft Corporation. MU_CHANGE - Standalone MM Perf Support # SPDX-License-Identifier: BSD-2-Clause-Patent # ## [Defines] - INF_VERSION = 0x00010005 + INF_VERSION = 0x0001001B # MU_CHANGE - Standalone MM Perf Support BASE_NAME = SmmCorePerformanceLib MODULE_UNI_FILE = SmmCorePerformanceLib.uni FILE_GUID = 36290D10-0F47-42c1-BBCE-E191C7928DCF MODULE_TYPE = SMM_CORE VERSION_STRING = 1.0 - PI_SPECIFICATION_VERSION = 0x0001000A + PI_SPECIFICATION_VERSION = 0x00010032 # MU_CHANGE - Standalone MM Perf Support LIBRARY_CLASS = PerformanceLib|SMM_CORE - CONSTRUCTOR = SmmCorePerformanceLibConstructor # @@ -32,33 +32,35 @@ # [Sources] - SmmCorePerformanceLib.c + MmCorePerformanceLib.c # MU_CHANGE - Standalone MM Perf Support + SmmCorePerformanceLibInternal.h SmmCorePerformanceLibInternal.h [Packages] MdePkg/MdePkg.dec MdeModulePkg/MdeModulePkg.dec - +# MU_CHANGE [BEGIN] - Standalone MM Perf Support [LibraryClasses] - MemoryAllocationLib - UefiBootServicesTableLib - PcdLib - TimerLib - BaseMemoryLib BaseLib + BaseMemoryLib DebugLib - SynchronizationLib - SmmServicesTableLib + DxeServicesLib + MemoryAllocationLib + MmServicesTableLib + PcdLib + PeCoffGetEntryPointLib SmmMemLib + SynchronizationLib + TimerLib + UefiBootServicesTableLib UefiLib - ReportStatusCodeLib - PeCoffGetEntryPointLib - DxeServicesLib +# MU_CHANGE [END] - Standalone MM Perf Support [Protocols] gEfiSmmBase2ProtocolGuid ## CONSUMES - gEdkiiSmmExitBootServicesProtocolGuid ## CONSUMES + gEdkiiSmmExitBootServicesProtocolGuid ## CONSUMES ## NOTIFY # MU_CHANGE - Standalone MM Perf Support + gEfiLoadedImageProtocolGuid ## CONSUMES # MU_CHANGE - Standalone MM Perf Support [Guids] ## PRODUCES ## SystemTable diff --git a/MdeModulePkg/Library/SmmCorePerformanceLib/SmmCorePerformanceLibInternal.h b/MdeModulePkg/Library/SmmCorePerformanceLib/SmmCorePerformanceLibInternal.h index 62b496484c..9e95e857a4 100644 --- a/MdeModulePkg/Library/SmmCorePerformanceLib/SmmCorePerformanceLibInternal.h +++ b/MdeModulePkg/Library/SmmCorePerformanceLib/SmmCorePerformanceLibInternal.h @@ -5,6 +5,7 @@ library instance at its constructor. Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.
+Copyright (c) Microsoft Corporation. MU_CHANGE - Standalone MM Perf Support SPDX-License-Identifier: BSD-2-Clause-Patent **/ @@ -12,32 +13,133 @@ SPDX-License-Identifier: BSD-2-Clause-Patent #ifndef _SMM_CORE_PERFORMANCE_LIB_INTERNAL_H_ #define _SMM_CORE_PERFORMANCE_LIB_INTERNAL_H_ -#include -#include +// MU_CHANGE [BEGIN] - Standalone MM Perf Support +#include #include #include +#include +#include #include -#include -#include -#include -#include #include #include -#include -#include -#include -#include +#include #include -#include -#include -#include -#include +#include +#include #include +#include +#include +#include +#include -#include -#include #include +#include + +#define STRING_SIZE (FPDT_STRING_EVENT_RECORD_NAME_LENGTH * sizeof (CHAR8)) +#define FIRMWARE_RECORD_BUFFER 0x1000 +#define CACHE_HANDLE_GUID_COUNT 0x100 + +// +// Library internal function declarations +// + +/** + Return a pointer to the loaded image protocol for the given handle. + + @param[in] Handle A handle to query for the loaded image protocol. + + @return A pointer to a loaded image protocol instance or null if the handle does not support load image protocol. +**/ +EFI_LOADED_IMAGE_PROTOCOL * +GetLoadedImageProtocol ( + IN EFI_HANDLE Handle + ); + +/** + Get the module name from the PDB file name in the image header. + + @param[in] ImageBase The base address of the image. + @param[out] NameString The buffer to store the name string. + @param[in] BufferSize The size of the buffer in bytes. + + @retval EFI_SUCCESS The name string is successfully retrieved. + @retval EFI_INVALID_PARAMETER A pointer argument provided is null. + @retval EFI_NOT_FOUND The module name was not found. + +**/ +EFI_STATUS +GetModuleNameFromPdbString ( + IN VOID *ImageBase, + OUT CHAR8 *NameString, + IN UINTN BufferSize + ); + +/** + Get the module name from the PDB file name in the image header. + + @param[in] ModuleGuid The GUID of the module. + @param[out] NameString The buffer to store the name string. + @param[in] BufferSize The size of the buffer in bytes. + + @retval EFI_SUCCESS The name string is successfully retrieved. + @retval EFI_NOT_FOUND The module name was not found. + +**/ +EFI_STATUS +GetNameFromUiSection ( + IN EFI_GUID *ModuleGuid, + OUT CHAR8 *NameString, + IN UINTN BufferSize + ); + +/** + Perform library initialization common to all MM instances. + + Note: MM Services must be available when this function is called. +**/ +VOID +InitializeMmCorePerformanceLibCommon ( + VOID + ); + +/** + A library internal MM-instance specific implementation to check if a buffer outside MM is valid. + + This function is provided so Standalone MM and Traditional MM may use a different implementation + of data buffer check logic. + + @param[in] Buffer The buffer start address to be checked. + @param[in] Length The buffer length to be checked. + + @retval TRUE This buffer is valid per processor architecture. + @retval FALSE This buffer is not valid per processor architecture. +**/ +BOOLEAN +IsBufferOutsideMmValidInternal ( + IN EFI_PHYSICAL_ADDRESS Buffer, + IN UINT64 Length + ); + +/** + A library internal MM-instance specific implementation to check if a comm buffer is valid. + + This function is provided so Standalone MM and Traditional MM may use a different implementation + of comm buffer check logic. + + @param[in] Buffer The buffer start address to be checked. + @param[in] Length The buffer length to be checked. + + @retval TRUE This communicate buffer is valid per processor architecture. + @retval FALSE This communicate buffer is not valid per processor architecture. +**/ +BOOLEAN +IsCommBufferValidInternal ( + IN EFI_PHYSICAL_ADDRESS Buffer, + IN UINT64 Length + ); + +// MU_CHANGE [END] - Standalone MM Perf Support // // Interface declarations for SMM PerformanceMeasurement Protocol. diff --git a/MdeModulePkg/Library/SmmCorePerformanceLib/StandaloneMmCorePerformanceLib.c b/MdeModulePkg/Library/SmmCorePerformanceLib/StandaloneMmCorePerformanceLib.c new file mode 100644 index 0000000000..c31e67683c --- /dev/null +++ b/MdeModulePkg/Library/SmmCorePerformanceLib/StandaloneMmCorePerformanceLib.c @@ -0,0 +1,147 @@ +/** @file + This library is mainly used by the Standalone MM Core to start performance logging to ensure that + Standalone MM Performance and PerformanceEx Protocol are installed as early as possible in the MM phase. + + Caution: This module requires additional review when modified. + - This driver will have external input - performance data and communicate buffer in MM mode. + - This external input must be validated carefully to avoid security issue like + buffer overflow, integer overflow. + + SmmPerformanceHandlerEx(), SmmPerformanceHandler() will receive untrusted input and do basic validation. + + Copyright (c) Microsoft Corporation. + SPDX-License-Identifier: BSD-2-Clause-Patent + + MU_CHANGE [WHOLE FILE] - Standalone MM Perf Support + +**/ + +#include "SmmCorePerformanceLibInternal.h" + +#include + +extern BOOLEAN mPerformanceMeasurementEnabled; + +/** + A library internal MM-instance specific implementation to check if a buffer outside MM is valid. + + This function is provided so Standalone MM and Traditional MM may use a different implementation + of data buffer check logic. + + @param[in] Buffer The buffer start address to be checked. + @param[in] Length The buffer length to be checked. + + @retval TRUE This buffer is valid per processor architecture. + @retval FALSE This buffer is not valid per processor architecture. +**/ +BOOLEAN +IsBufferOutsideMmValidInternal ( + IN EFI_PHYSICAL_ADDRESS Buffer, + IN UINT64 Length + ) +{ + return MmIsBufferOutsideMmValid (Buffer, Length); +} + +/** + A library internal MM-instance specific implementation to check if a comm buffer is valid. + + This function is provided so Standalone MM and Traditional MM may use a different implementation + of comm buffer check logic. + + @param[in] Buffer The buffer start address to be checked. + @param[in] Length The buffer length to be checked. + + @retval TRUE This communicate buffer is valid per processor architecture. + @retval FALSE This communicate buffer is not valid per processor architecture. +**/ +BOOLEAN +IsCommBufferValidInternal ( + IN EFI_PHYSICAL_ADDRESS Buffer, + IN UINT64 Length + ) +{ + return MmCommBufferValid (Buffer, Length); +} + +/** + Return a pointer to the loaded image protocol for the given handle. + + @param[in] Handle A handle to query for the loaded image protocol. + + @return A pointer to a loaded image protocol instance or null if the handle does not support load image protocol. +**/ +EFI_LOADED_IMAGE_PROTOCOL * +GetLoadedImageProtocol ( + IN EFI_HANDLE Handle + ) +{ + EFI_LOADED_IMAGE_PROTOCOL *LoadedImage; + + if (Handle == NULL) { + return NULL; + } + + LoadedImage = NULL; + gMmst->MmHandleProtocol ( + Handle, + &gEfiLoadedImageProtocolGuid, + (VOID **)&LoadedImage + ); + return LoadedImage; +} + +/** + Get the module name from the PDB file name in the image header. + + @param[in] ModuleGuid The GUID of the module. + @param[out] NameString The buffer to store the name string. + @param[in] BufferSize The size of the buffer in bytes. + + @retval EFI_SUCCESS The name string is successfully retrieved. + @retval EFI_NOT_FOUND The module name was not found. + +**/ +EFI_STATUS +GetNameFromUiSection ( + IN EFI_GUID *ModuleGuid, + OUT CHAR8 *NameString, + IN UINTN BufferSize + ) +{ + return EFI_NOT_FOUND; +} + +/** + The constructor function initializes the Standalone MM Core performance library. + + It will ASSERT() if one of these operations fails and it will always return EFI_SUCCESS. + + @param[in] ImageHandle The firmware allocated handle for the image. + @param[in] MmSystemTable A pointer to the MM System Table. + + @retval EFI_SUCCESS The constructor successfully gets HobList. + @retval Other value The constructor can't get HobList. + +**/ +EFI_STATUS +EFIAPI +StandaloneMmCorePerformanceLibConstructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_MM_SYSTEM_TABLE *MmSystemTable + ) +{ + mPerformanceMeasurementEnabled = (BOOLEAN)((FixedPcdGet8 (PcdPerformanceLibraryPropertyMask) & PERFORMANCE_LIBRARY_PROPERTY_MEASUREMENT_ENABLED) != 0); + + if (!PerformanceMeasurementEnabled ()) { + DEBUG ((DEBUG_WARN, "[%a] - Performance library is linked but performance tracing is not enabled.\n", __func__)); + // + // Do not initialize performance infrastructure if not required. + // + return EFI_SUCCESS; + } + + InitializeMmCorePerformanceLibCommon (); + + return EFI_SUCCESS; +} diff --git a/MdeModulePkg/Library/SmmCorePerformanceLib/StandaloneMmCorePerformanceLib.inf b/MdeModulePkg/Library/SmmCorePerformanceLib/StandaloneMmCorePerformanceLib.inf new file mode 100644 index 0000000000..5482ead294 --- /dev/null +++ b/MdeModulePkg/Library/SmmCorePerformanceLib/StandaloneMmCorePerformanceLib.inf @@ -0,0 +1,55 @@ +## @file +# Performance library instance used by a Standalone MM Core or very early Standlaone MM module. +# +# Installs the MM performance measurement protocol and returns MM performance data via MM communicate. +# +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# MU_CHANGE [WHOLE FILE] - Standalone MM Perf Support +## + +[Defines] + INF_VERSION = 0x0001001B + BASE_NAME = StandaloneMmCorePerformanceLib + FILE_GUID = 8585D462-DE63-4080-882A-A73974CE5609 + MODULE_TYPE = MM_CORE_STANDALONE + VERSION_STRING = 1.0 + PI_SPECIFICATION_VERSION = 0x00010032 + LIBRARY_CLASS = PerformanceLib|MM_CORE_STANDALONE MM_STANDALONE + CONSTRUCTOR = StandaloneMmCorePerformanceLibConstructor + +[Sources] + MmCorePerformanceLib.c + SmmCorePerformanceLibInternal.h + StandaloneMmCorePerformanceLib.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + StandaloneMmPkg/StandaloneMmPkg.dec + +[LibraryClasses] + BaseLib + BaseMemoryLib + DebugLib + MemoryAllocationLib + MemLib + MmServicesTableLib + PcdLib + PeCoffGetEntryPointLib + SynchronizationLib + TimerLib + +[Protocols] + gEdkiiSmmExitBootServicesProtocolGuid ## CONSUMES ## NOTIFY + gEfiLoadedImageProtocolGuid ## CONSUMES + +[Guids] + gEfiFirmwarePerformanceGuid ## SOMETIMES_PRODUCES # SmiHandlerRegister + gEdkiiSmmPerformanceMeasurementProtocolGuid ## PRODUCES # Install protocol + gZeroGuid ## SOMETIMES_CONSUMES ## GUID + +[FixedPcd] + gEfiMdePkgTokenSpaceGuid.PcdPerformanceLibraryPropertyMask ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdEdkiiFpdtStringRecordEnableOnly ## CONSUMES diff --git a/MdeModulePkg/MdeModulePkg.dsc b/MdeModulePkg/MdeModulePkg.dsc index b300f61023..30790c0021 100644 --- a/MdeModulePkg/MdeModulePkg.dsc +++ b/MdeModulePkg/MdeModulePkg.dsc @@ -574,6 +574,7 @@ MdeModulePkg/Library/PiSmmCoreMemoryAllocationLib/PiSmmCoreMemoryAllocationProfileLib.inf MdeModulePkg/Library/PiSmmCoreMemoryAllocationLib/PiSmmCoreMemoryAllocationLib.inf MdeModulePkg/Library/SmmCorePerformanceLib/SmmCorePerformanceLib.inf + MdeModulePkg/Library/SmmCorePerformanceLib/StandaloneMmCorePerformanceLib.inf # MU_CHANGE - Standalone MM Perf Support MdeModulePkg/Library/SmmPerformanceLib/SmmPerformanceLib.inf MdeModulePkg/Library/SmmLockBoxLib/SmmLockBoxPeiLib.inf MdeModulePkg/Library/SmmLockBoxLib/SmmLockBoxDxeLib.inf