/** @file | |
Performance library instance used by SMM Core. | |
This library provides the performance measurement interfaces and initializes performance | |
logging for the SMM phase. | |
It initializes SMM phase performance logging by publishing the SMM Performance and PerformanceEx Protocol, | |
which is consumed by SmmPerformanceLib to logging performance data in SMM phase. | |
This library is mainly used by SMM Core to start performance logging to ensure that | |
SMM Performance and PerformanceEx Protocol are installed at the very beginning of SMM 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) 2011 - 2018, Intel Corporation. All rights reserved.<BR> | |
This program and the accompanying materials | |
are licensed and made available under the terms and conditions of the BSD License | |
which accompanies this distribution. The full text of the license may be found at | |
http://opensource.org/licenses/bsd-license.php | |
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
**/ | |
#include "SmmCorePerformanceLibInternal.h" | |
// | |
// The data structure to hold global performance data. | |
// | |
GAUGE_DATA_HEADER *mGaugeData; | |
// | |
// The current maximum number of logging entries. If current number of | |
// entries exceeds this value, it will re-allocate a larger array and | |
// migration the old data to the larger array. | |
// | |
UINT32 mMaxGaugeRecords; | |
// | |
// The handle to install Performance Protocol instance. | |
// | |
EFI_HANDLE mHandle = NULL; | |
BOOLEAN mPerformanceMeasurementEnabled; | |
SPIN_LOCK mSmmPerfLock; | |
// | |
// Interfaces for SMM Performance Protocol. | |
// | |
PERFORMANCE_PROTOCOL mPerformanceInterface = { | |
StartGauge, | |
EndGauge, | |
GetGauge | |
}; | |
// | |
// Interfaces for SMM PerformanceEx Protocol. | |
// | |
PERFORMANCE_EX_PROTOCOL mPerformanceExInterface = { | |
StartGaugeEx, | |
EndGaugeEx, | |
GetGaugeEx | |
}; | |
/** | |
Searches in the gauge array with keyword Handle, Token, Module and Identfier. | |
This internal function searches for the gauge entry in the gauge array. | |
If there is an entry that exactly matches the given keywords | |
and its end time stamp is zero, then the index of that gauge entry is returned; | |
otherwise, the the number of gauge entries in the array is returned. | |
@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 Identifier 32-bit identifier. | |
@retval The index of gauge entry in the array. | |
**/ | |
UINT32 | |
SmmSearchForGaugeEntry ( | |
IN CONST VOID *Handle, OPTIONAL | |
IN CONST CHAR8 *Token, OPTIONAL | |
IN CONST CHAR8 *Module, OPTIONAL | |
IN CONST UINT32 Identifier | |
) | |
{ | |
UINT32 Index; | |
UINT32 Index2; | |
UINT32 NumberOfEntries; | |
GAUGE_DATA_ENTRY_EX *GaugeEntryExArray; | |
if (Token == NULL) { | |
Token = ""; | |
} | |
if (Module == NULL) { | |
Module = ""; | |
} | |
NumberOfEntries = mGaugeData->NumberOfEntries; | |
GaugeEntryExArray = (GAUGE_DATA_ENTRY_EX *) (mGaugeData + 1); | |
Index2 = 0; | |
for (Index = 0; Index < NumberOfEntries; Index++) { | |
Index2 = NumberOfEntries - 1 - Index; | |
if (GaugeEntryExArray[Index2].EndTimeStamp == 0 && | |
(GaugeEntryExArray[Index2].Handle == (EFI_PHYSICAL_ADDRESS) (UINTN) Handle) && | |
AsciiStrnCmp (GaugeEntryExArray[Index2].Token, Token, SMM_PERFORMANCE_STRING_LENGTH) == 0 && | |
AsciiStrnCmp (GaugeEntryExArray[Index2].Module, Module, SMM_PERFORMANCE_STRING_LENGTH) == 0 && | |
(GaugeEntryExArray[Index2].Identifier == Identifier)) { | |
Index = Index2; | |
break; | |
} | |
} | |
return Index; | |
} | |
/** | |
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 StartGauge of PERFORMANCE_PROTOCOL. | |
@retval EFI_SUCCESS The data was read correctly from the device. | |
@retval EFI_OUT_OF_RESOURCES There are not enough resources to record the measurement. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
StartGaugeEx ( | |
IN CONST VOID *Handle, OPTIONAL | |
IN CONST CHAR8 *Token, OPTIONAL | |
IN CONST CHAR8 *Module, OPTIONAL | |
IN UINT64 TimeStamp, | |
IN UINT32 Identifier | |
) | |
{ | |
GAUGE_DATA_ENTRY_EX *GaugeEntryExArray; | |
UINTN GaugeDataSize; | |
GAUGE_DATA_HEADER *NewGaugeData; | |
UINTN OldGaugeDataSize; | |
GAUGE_DATA_HEADER *OldGaugeData; | |
UINT32 Index; | |
AcquireSpinLock (&mSmmPerfLock); | |
Index = mGaugeData->NumberOfEntries; | |
if (Index >= mMaxGaugeRecords) { | |
// | |
// Try to enlarge the scale of gauge array. | |
// | |
OldGaugeData = mGaugeData; | |
OldGaugeDataSize = sizeof (GAUGE_DATA_HEADER) + sizeof (GAUGE_DATA_ENTRY_EX) * mMaxGaugeRecords; | |
GaugeDataSize = sizeof (GAUGE_DATA_HEADER) + sizeof (GAUGE_DATA_ENTRY_EX) * mMaxGaugeRecords * 2; | |
NewGaugeData = AllocateZeroPool (GaugeDataSize); | |
if (NewGaugeData == NULL) { | |
ReleaseSpinLock (&mSmmPerfLock); | |
return EFI_OUT_OF_RESOURCES; | |
} | |
mGaugeData = NewGaugeData; | |
mMaxGaugeRecords *= 2; | |
// | |
// Initialize new data array and migrate old data one. | |
// | |
mGaugeData = CopyMem (mGaugeData, OldGaugeData, OldGaugeDataSize); | |
FreePool (OldGaugeData); | |
} | |
GaugeEntryExArray = (GAUGE_DATA_ENTRY_EX *) (mGaugeData + 1); | |
GaugeEntryExArray[Index].Handle = (EFI_PHYSICAL_ADDRESS) (UINTN) Handle; | |
if (Token != NULL) { | |
AsciiStrnCpy (GaugeEntryExArray[Index].Token, Token, SMM_PERFORMANCE_STRING_LENGTH); | |
} | |
if (Module != NULL) { | |
AsciiStrnCpy (GaugeEntryExArray[Index].Module, Module, SMM_PERFORMANCE_STRING_LENGTH); | |
} | |
GaugeEntryExArray[Index].EndTimeStamp = 0; | |
GaugeEntryExArray[Index].Identifier = Identifier; | |
if (TimeStamp == 0) { | |
TimeStamp = GetPerformanceCounter (); | |
} | |
GaugeEntryExArray[Index].StartTimeStamp = TimeStamp; | |
mGaugeData->NumberOfEntries++; | |
ReleaseSpinLock (&mSmmPerfLock); | |
return EFI_SUCCESS; | |
} | |
/** | |
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 EFI_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 EndGauge of PERFORMANCE_PROTOCOL. | |
@retval EFI_SUCCESS The end of the measurement was recorded. | |
@retval EFI_NOT_FOUND The specified measurement record could not be found. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
EndGaugeEx ( | |
IN CONST VOID *Handle, OPTIONAL | |
IN CONST CHAR8 *Token, OPTIONAL | |
IN CONST CHAR8 *Module, OPTIONAL | |
IN UINT64 TimeStamp, | |
IN UINT32 Identifier | |
) | |
{ | |
GAUGE_DATA_ENTRY_EX *GaugeEntryExArray; | |
UINT32 Index; | |
AcquireSpinLock (&mSmmPerfLock); | |
if (TimeStamp == 0) { | |
TimeStamp = GetPerformanceCounter (); | |
} | |
Index = SmmSearchForGaugeEntry (Handle, Token, Module, Identifier); | |
if (Index >= mGaugeData->NumberOfEntries) { | |
ReleaseSpinLock (&mSmmPerfLock); | |
return EFI_NOT_FOUND; | |
} | |
GaugeEntryExArray = (GAUGE_DATA_ENTRY_EX *) (mGaugeData + 1); | |
GaugeEntryExArray[Index].EndTimeStamp = TimeStamp; | |
ReleaseSpinLock (&mSmmPerfLock); | |
return EFI_SUCCESS; | |
} | |
/** | |
Retrieves a previously logged performance measurement. | |
It can also retrieve the log created by StartGauge and EndGauge of PERFORMANCE_PROTOCOL, | |
and then assign the Identifier with 0. | |
Retrieves the performance log entry from the performance log specified by LogEntryKey. | |
If it stands for a valid entry, then EFI_SUCCESS is returned and | |
GaugeDataEntryEx stores the pointer to that entry. | |
@param LogEntryKey The key for the previous performance measurement log entry. | |
If 0, then the first performance measurement log entry is retrieved. | |
@param GaugeDataEntryEx The indirect pointer to the extended gauge data entry specified by LogEntryKey | |
if the retrieval is successful. | |
@retval EFI_SUCCESS The GuageDataEntryEx is successfully found based on LogEntryKey. | |
@retval EFI_NOT_FOUND The LogEntryKey is the last entry (equals to the total entry number). | |
@retval EFI_INVALIDE_PARAMETER The LogEntryKey is not a valid entry (greater than the total entry number). | |
@retval EFI_INVALIDE_PARAMETER GaugeDataEntryEx is NULL. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
GetGaugeEx ( | |
IN UINTN LogEntryKey, | |
OUT GAUGE_DATA_ENTRY_EX **GaugeDataEntryEx | |
) | |
{ | |
UINTN NumberOfEntries; | |
GAUGE_DATA_ENTRY_EX *GaugeEntryExArray; | |
NumberOfEntries = (UINTN) (mGaugeData->NumberOfEntries); | |
if (LogEntryKey > NumberOfEntries) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (LogEntryKey == NumberOfEntries) { | |
return EFI_NOT_FOUND; | |
} | |
GaugeEntryExArray = (GAUGE_DATA_ENTRY_EX *) (mGaugeData + 1); | |
if (GaugeDataEntryEx == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
*GaugeDataEntryEx = &GaugeEntryExArray[LogEntryKey]; | |
return EFI_SUCCESS; | |
} | |
/** | |
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 EFI_SUCCESS The data was read correctly from the device. | |
@retval EFI_OUT_OF_RESOURCES There are not enough resources to record the measurement. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
StartGauge ( | |
IN CONST VOID *Handle, OPTIONAL | |
IN CONST CHAR8 *Token, OPTIONAL | |
IN CONST CHAR8 *Module, OPTIONAL | |
IN UINT64 TimeStamp | |
) | |
{ | |
return StartGaugeEx (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 EFI_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 EFI_SUCCESS The end of the measurement was recorded. | |
@retval EFI_NOT_FOUND The specified measurement record could not be found. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
EndGauge ( | |
IN CONST VOID *Handle, OPTIONAL | |
IN CONST CHAR8 *Token, OPTIONAL | |
IN CONST CHAR8 *Module, OPTIONAL | |
IN UINT64 TimeStamp | |
) | |
{ | |
return EndGaugeEx (Handle, Token, Module, TimeStamp, 0); | |
} | |
/** | |
Retrieves a previously logged performance measurement. | |
It can also retrieve the log created by StartGaugeEx and EndGaugeEx of PERFORMANCE_EX_PROTOCOL, | |
and then eliminate the Identifier. | |
Retrieves the performance log entry from the performance log specified by LogEntryKey. | |
If it stands for a valid entry, then EFI_SUCCESS is returned and | |
GaugeDataEntry stores the pointer to that entry. | |
@param LogEntryKey The key for the previous performance measurement log entry. | |
If 0, then the first performance measurement log entry is retrieved. | |
@param GaugeDataEntry The indirect pointer to the gauge data entry specified by LogEntryKey | |
if the retrieval is successful. | |
@retval EFI_SUCCESS The GuageDataEntry is successfully found based on LogEntryKey. | |
@retval EFI_NOT_FOUND The LogEntryKey is the last entry (equals to the total entry number). | |
@retval EFI_INVALIDE_PARAMETER The LogEntryKey is not a valid entry (greater than the total entry number). | |
@retval EFI_INVALIDE_PARAMETER GaugeDataEntry is NULL. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
GetGauge ( | |
IN UINTN LogEntryKey, | |
OUT GAUGE_DATA_ENTRY **GaugeDataEntry | |
) | |
{ | |
EFI_STATUS Status; | |
GAUGE_DATA_ENTRY_EX *GaugeEntryEx; | |
GaugeEntryEx = NULL; | |
Status = GetGaugeEx (LogEntryKey, &GaugeEntryEx); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
if (GaugeDataEntry == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
*GaugeDataEntry = (GAUGE_DATA_ENTRY *) GaugeEntryEx; | |
return EFI_SUCCESS; | |
} | |
/** | |
Communication service SMI Handler entry. | |
This SMI handler provides services for the performance wrapper driver. | |
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-SMM environment into an SMM 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 | |
SmmPerformanceHandlerEx ( | |
IN EFI_HANDLE DispatchHandle, | |
IN CONST VOID *RegisterContext, | |
IN OUT VOID *CommBuffer, | |
IN OUT UINTN *CommBufferSize | |
) | |
{ | |
EFI_STATUS Status; | |
SMM_PERF_COMMUNICATE_EX *SmmPerfCommData; | |
GAUGE_DATA_ENTRY_EX *GaugeEntryExArray; | |
UINT64 DataSize; | |
UINTN Index; | |
GAUGE_DATA_ENTRY_EX *GaugeDataEx; | |
UINTN NumberOfEntries; | |
UINTN LogEntryKey; | |
UINTN TempCommBufferSize; | |
GaugeEntryExArray = NULL; | |
// | |
// If input is invalid, stop processing this SMI | |
// | |
if (CommBuffer == NULL || CommBufferSize == NULL) { | |
return EFI_SUCCESS; | |
} | |
TempCommBufferSize = *CommBufferSize; | |
if(TempCommBufferSize < sizeof (SMM_PERF_COMMUNICATE_EX)) { | |
return EFI_SUCCESS; | |
} | |
if (!SmmIsBufferOutsideSmmValid ((UINTN)CommBuffer, TempCommBufferSize)) { | |
DEBUG ((EFI_D_ERROR, "SmmPerformanceHandlerEx: SMM communcation data buffer in SMRAM or overflow!\n")); | |
return EFI_SUCCESS; | |
} | |
SmmPerfCommData = (SMM_PERF_COMMUNICATE_EX *)CommBuffer; | |
switch (SmmPerfCommData->Function) { | |
case SMM_PERF_FUNCTION_GET_GAUGE_ENTRY_NUMBER : | |
SmmPerfCommData->NumberOfEntries = mGaugeData->NumberOfEntries; | |
Status = EFI_SUCCESS; | |
break; | |
case SMM_PERF_FUNCTION_GET_GAUGE_DATA : | |
GaugeDataEx = SmmPerfCommData->GaugeDataEx; | |
NumberOfEntries = SmmPerfCommData->NumberOfEntries; | |
LogEntryKey = SmmPerfCommData->LogEntryKey; | |
if (GaugeDataEx == NULL || NumberOfEntries == 0 || LogEntryKey > mGaugeData->NumberOfEntries || | |
NumberOfEntries > mGaugeData->NumberOfEntries || LogEntryKey > (mGaugeData->NumberOfEntries - NumberOfEntries)) { | |
Status = EFI_INVALID_PARAMETER; | |
break; | |
} | |
// | |
// Sanity check | |
// | |
DataSize = MultU64x32 (NumberOfEntries, sizeof(GAUGE_DATA_ENTRY_EX)); | |
if (!SmmIsBufferOutsideSmmValid ((UINTN) GaugeDataEx, DataSize)) { | |
DEBUG ((EFI_D_ERROR, "SmmPerformanceHandlerEx: SMM Performance Data buffer in SMRAM or overflow!\n")); | |
Status = EFI_ACCESS_DENIED; | |
break; | |
} | |
// | |
// The AsmLfence() call here is to ensure the previous range/content | |
// checks for the CommBuffer have been completed before calling | |
// CopyMem(). | |
// | |
AsmLfence (); | |
GaugeEntryExArray = (GAUGE_DATA_ENTRY_EX *) (mGaugeData + 1); | |
for (Index = 0; Index < NumberOfEntries; Index++) { | |
CopyMem ( | |
(UINT8 *) &GaugeDataEx[Index], | |
(UINT8 *) &GaugeEntryExArray[LogEntryKey++], | |
sizeof (GAUGE_DATA_ENTRY_EX) | |
); | |
} | |
Status = EFI_SUCCESS; | |
break; | |
default: | |
Status = EFI_UNSUPPORTED; | |
} | |
SmmPerfCommData->ReturnStatus = Status; | |
return EFI_SUCCESS; | |
} | |
/** | |
Communication service SMI Handler entry. | |
This SMI handler provides services for the performance wrapper driver. | |
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-SMM environment into an SMM 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 | |
SmmPerformanceHandler ( | |
IN EFI_HANDLE DispatchHandle, | |
IN CONST VOID *RegisterContext, | |
IN OUT VOID *CommBuffer, | |
IN OUT UINTN *CommBufferSize | |
) | |
{ | |
EFI_STATUS Status; | |
SMM_PERF_COMMUNICATE *SmmPerfCommData; | |
GAUGE_DATA_ENTRY_EX *GaugeEntryExArray; | |
UINT64 DataSize; | |
UINTN Index; | |
GAUGE_DATA_ENTRY *GaugeData; | |
UINTN NumberOfEntries; | |
UINTN LogEntryKey; | |
UINTN TempCommBufferSize; | |
GaugeEntryExArray = NULL; | |
// | |
// If input is invalid, stop processing this SMI | |
// | |
if (CommBuffer == NULL || CommBufferSize == NULL) { | |
return EFI_SUCCESS; | |
} | |
TempCommBufferSize = *CommBufferSize; | |
if(TempCommBufferSize < sizeof (SMM_PERF_COMMUNICATE)) { | |
return EFI_SUCCESS; | |
} | |
if (!SmmIsBufferOutsideSmmValid ((UINTN)CommBuffer, TempCommBufferSize)) { | |
DEBUG ((EFI_D_ERROR, "SmmPerformanceHandler: SMM communcation data buffer in SMRAM or overflow!\n")); | |
return EFI_SUCCESS; | |
} | |
SmmPerfCommData = (SMM_PERF_COMMUNICATE *)CommBuffer; | |
switch (SmmPerfCommData->Function) { | |
case SMM_PERF_FUNCTION_GET_GAUGE_ENTRY_NUMBER : | |
SmmPerfCommData->NumberOfEntries = mGaugeData->NumberOfEntries; | |
Status = EFI_SUCCESS; | |
break; | |
case SMM_PERF_FUNCTION_GET_GAUGE_DATA : | |
GaugeData = SmmPerfCommData->GaugeData; | |
NumberOfEntries = SmmPerfCommData->NumberOfEntries; | |
LogEntryKey = SmmPerfCommData->LogEntryKey; | |
if (GaugeData == NULL || NumberOfEntries == 0 || LogEntryKey > mGaugeData->NumberOfEntries || | |
NumberOfEntries > mGaugeData->NumberOfEntries || LogEntryKey > (mGaugeData->NumberOfEntries - NumberOfEntries)) { | |
Status = EFI_INVALID_PARAMETER; | |
break; | |
} | |
// | |
// Sanity check | |
// | |
DataSize = MultU64x32 (NumberOfEntries, sizeof(GAUGE_DATA_ENTRY)); | |
if (!SmmIsBufferOutsideSmmValid ((UINTN) GaugeData, DataSize)) { | |
DEBUG ((EFI_D_ERROR, "SmmPerformanceHandler: SMM Performance Data buffer in SMRAM or overflow!\n")); | |
Status = EFI_ACCESS_DENIED; | |
break; | |
} | |
// | |
// The AsmLfence() call here is to ensure the previous range/content | |
// checks for the CommBuffer have been completed before calling | |
// CopyMem(). | |
// | |
AsmLfence (); | |
GaugeEntryExArray = (GAUGE_DATA_ENTRY_EX *) (mGaugeData + 1); | |
for (Index = 0; Index < NumberOfEntries; Index++) { | |
CopyMem ( | |
(UINT8 *) &GaugeData[Index], | |
(UINT8 *) &GaugeEntryExArray[LogEntryKey++], | |
sizeof (GAUGE_DATA_ENTRY) | |
); | |
} | |
Status = EFI_SUCCESS; | |
break; | |
default: | |
Status = EFI_UNSUPPORTED; | |
} | |
SmmPerfCommData->ReturnStatus = Status; | |
return EFI_SUCCESS; | |
} | |
/** | |
SmmBase2 protocol notify callback function, when SMST and SMM memory service get initialized | |
this function is callbacked to initialize the Smm Performance Lib | |
@param Event The event of notify protocol. | |
@param Context Notify event context. | |
**/ | |
VOID | |
EFIAPI | |
InitializeSmmCorePerformanceLib ( | |
IN EFI_EVENT Event, | |
IN VOID *Context | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_HANDLE Handle; | |
// | |
// Initialize spin lock | |
// | |
InitializeSpinLock (&mSmmPerfLock); | |
mMaxGaugeRecords = INIT_SMM_GAUGE_DATA_ENTRIES; | |
mGaugeData = AllocateZeroPool (sizeof (GAUGE_DATA_HEADER) + (sizeof (GAUGE_DATA_ENTRY_EX) * mMaxGaugeRecords)); | |
ASSERT (mGaugeData != NULL); | |
// | |
// Install the protocol interfaces. | |
// | |
Status = gSmst->SmmInstallProtocolInterface ( | |
&mHandle, | |
&gSmmPerformanceProtocolGuid, | |
EFI_NATIVE_INTERFACE, | |
&mPerformanceInterface | |
); | |
ASSERT_EFI_ERROR (Status); | |
Status = gSmst->SmmInstallProtocolInterface ( | |
&mHandle, | |
&gSmmPerformanceExProtocolGuid, | |
EFI_NATIVE_INTERFACE, | |
&mPerformanceExInterface | |
); | |
ASSERT_EFI_ERROR (Status); | |
/// | |
/// Register SMM Performance SMI handler | |
/// | |
Handle = NULL; | |
Status = gSmst->SmiHandlerRegister (SmmPerformanceHandler, &gSmmPerformanceProtocolGuid, &Handle); | |
ASSERT_EFI_ERROR (Status); | |
Status = gSmst->SmiHandlerRegister (SmmPerformanceHandlerEx, &gSmmPerformanceExProtocolGuid, &Handle); | |
ASSERT_EFI_ERROR (Status); | |
} | |
/** | |
The constructor function initializes the Performance Measurement Enable flag and | |
registers SmmBase2 protocol notify callback. | |
It will ASSERT() if one of these operations fails and it will always return EFI_SUCCESS. | |
@param ImageHandle The firmware allocated handle for the EFI image. | |
@param SystemTable A pointer to the EFI System Table. | |
@retval EFI_SUCCESS The constructor always returns EFI_SUCCESS. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
SmmCorePerformanceLibConstructor ( | |
IN EFI_HANDLE ImageHandle, | |
IN EFI_SYSTEM_TABLE *SystemTable | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_EVENT Event; | |
VOID *Registration; | |
mPerformanceMeasurementEnabled = (BOOLEAN) ((PcdGet8(PcdPerformanceLibraryPropertyMask) & PERFORMANCE_LIBRARY_PROPERTY_MEASUREMENT_ENABLED) != 0); | |
if (!mPerformanceMeasurementEnabled) { | |
// | |
// Do not initialize performance infrastructure if not required. | |
// | |
return EFI_SUCCESS; | |
} | |
// | |
// Create the events to do the library init. | |
// | |
Status = gBS->CreateEvent ( | |
EVT_NOTIFY_SIGNAL, | |
TPL_CALLBACK, | |
InitializeSmmCorePerformanceLib, | |
NULL, | |
&Event | |
); | |
ASSERT_EFI_ERROR (Status); | |
// | |
// Register for protocol notifications on this event | |
// | |
Status = gBS->RegisterProtocolNotify ( | |
&gEfiSmmBase2ProtocolGuid, | |
Event, | |
&Registration | |
); | |
ASSERT_EFI_ERROR (Status); | |
return EFI_SUCCESS; | |
} | |
/** | |
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 | |
) | |
{ | |
return (RETURN_STATUS) StartGaugeEx (Handle, Token, Module, TimeStamp, Identifier); | |
} | |
/** | |
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 | |
) | |
{ | |
return (RETURN_STATUS) EndGaugeEx (Handle, Token, Module, TimeStamp, Identifier); | |
} | |
/** | |
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. | |
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 | |
) | |
{ | |
EFI_STATUS Status; | |
GAUGE_DATA_ENTRY_EX *GaugeData; | |
GaugeData = NULL; | |
ASSERT (Handle != NULL); | |
ASSERT (Token != NULL); | |
ASSERT (Module != NULL); | |
ASSERT (StartTimeStamp != NULL); | |
ASSERT (EndTimeStamp != NULL); | |
ASSERT (Identifier != NULL); | |
Status = GetGaugeEx (LogEntryKey++, &GaugeData); | |
// | |
// Make sure that LogEntryKey is a valid log entry key, | |
// | |
ASSERT (Status != EFI_INVALID_PARAMETER); | |
if (EFI_ERROR (Status)) { | |
// | |
// The LogEntryKey is the last entry (equals to the total entry number). | |
// | |
return 0; | |
} | |
ASSERT (GaugeData != NULL); | |
*Handle = (VOID *) (UINTN) GaugeData->Handle; | |
*Token = GaugeData->Token; | |
*Module = GaugeData->Module; | |
*StartTimeStamp = GaugeData->StartTimeStamp; | |
*EndTimeStamp = GaugeData->EndTimeStamp; | |
*Identifier = GaugeData->Identifier; | |
return LogEntryKey; | |
} | |
/** | |
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. | |
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 | |
) | |
{ | |
UINT32 Identifier; | |
return GetPerformanceMeasurementEx (LogEntryKey, Handle, Token, Module, StartTimeStamp, EndTimeStamp, &Identifier); | |
} | |
/** | |
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; | |
} |