blob: 1f7ad68c31423be2492cc3c3034ce7b516ddee71 [file] [log] [blame]
/** @file
Support routines for UEFI memory profile.
Copyright (c) 2014 - 2018, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "DxeMain.h"
#include "Imem.h"
#define IS_UEFI_MEMORY_PROFILE_ENABLED ((PcdGet8 (PcdMemoryProfilePropertyMask) & BIT0) != 0)
#define GET_OCCUPIED_SIZE(ActualSize, Alignment) \
((ActualSize) + (((Alignment) - ((ActualSize) & ((Alignment) - 1))) & ((Alignment) - 1)))
typedef struct {
UINT32 Signature;
MEMORY_PROFILE_CONTEXT Context;
LIST_ENTRY *DriverInfoList;
} MEMORY_PROFILE_CONTEXT_DATA;
typedef struct {
UINT32 Signature;
MEMORY_PROFILE_DRIVER_INFO DriverInfo;
LIST_ENTRY *AllocInfoList;
CHAR8 *PdbString;
LIST_ENTRY Link;
} MEMORY_PROFILE_DRIVER_INFO_DATA;
typedef struct {
UINT32 Signature;
MEMORY_PROFILE_ALLOC_INFO AllocInfo;
CHAR8 *ActionString;
LIST_ENTRY Link;
} MEMORY_PROFILE_ALLOC_INFO_DATA;
GLOBAL_REMOVE_IF_UNREFERENCED LIST_ENTRY mImageQueue = INITIALIZE_LIST_HEAD_VARIABLE (mImageQueue);
GLOBAL_REMOVE_IF_UNREFERENCED MEMORY_PROFILE_CONTEXT_DATA mMemoryProfileContext = {
MEMORY_PROFILE_CONTEXT_SIGNATURE,
{
{
MEMORY_PROFILE_CONTEXT_SIGNATURE,
sizeof (MEMORY_PROFILE_CONTEXT),
MEMORY_PROFILE_CONTEXT_REVISION
},
0,
0,
{ 0 },
{ 0 },
0,
0,
0
},
&mImageQueue,
};
GLOBAL_REMOVE_IF_UNREFERENCED MEMORY_PROFILE_CONTEXT_DATA *mMemoryProfileContextPtr = NULL;
GLOBAL_REMOVE_IF_UNREFERENCED EFI_LOCK mMemoryProfileLock = EFI_INITIALIZE_LOCK_VARIABLE (TPL_NOTIFY);
GLOBAL_REMOVE_IF_UNREFERENCED BOOLEAN mMemoryProfileGettingStatus = FALSE;
GLOBAL_REMOVE_IF_UNREFERENCED BOOLEAN mMemoryProfileRecordingEnable = MEMORY_PROFILE_RECORDING_DISABLE;
GLOBAL_REMOVE_IF_UNREFERENCED EFI_DEVICE_PATH_PROTOCOL *mMemoryProfileDriverPath;
GLOBAL_REMOVE_IF_UNREFERENCED UINTN mMemoryProfileDriverPathSize;
/**
Get memory profile data.
@param[in] This The EDKII_MEMORY_PROFILE_PROTOCOL instance.
@param[in, out] ProfileSize On entry, points to the size in bytes of the ProfileBuffer.
On return, points to the size of the data returned in ProfileBuffer.
@param[out] ProfileBuffer Profile buffer.
@return EFI_SUCCESS Get the memory profile data successfully.
@return EFI_UNSUPPORTED Memory profile is unsupported.
@return EFI_BUFFER_TO_SMALL The ProfileSize is too small for the resulting data.
ProfileSize is updated with the size required.
**/
EFI_STATUS
EFIAPI
ProfileProtocolGetData (
IN EDKII_MEMORY_PROFILE_PROTOCOL *This,
IN OUT UINT64 *ProfileSize,
OUT VOID *ProfileBuffer
);
/**
Register image to memory profile.
@param[in] This The EDKII_MEMORY_PROFILE_PROTOCOL instance.
@param[in] FilePath File path of the image.
@param[in] ImageBase Image base address.
@param[in] ImageSize Image size.
@param[in] FileType File type of the image.
@return EFI_SUCCESS Register successfully.
@return EFI_UNSUPPORTED Memory profile is unsupported,
or memory profile for the image is not required.
@return EFI_OUT_OF_RESOURCE No enough resource for this register.
**/
EFI_STATUS
EFIAPI
ProfileProtocolRegisterImage (
IN EDKII_MEMORY_PROFILE_PROTOCOL *This,
IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
IN PHYSICAL_ADDRESS ImageBase,
IN UINT64 ImageSize,
IN EFI_FV_FILETYPE FileType
);
/**
Unregister image from memory profile.
@param[in] This The EDKII_MEMORY_PROFILE_PROTOCOL instance.
@param[in] FilePath File path of the image.
@param[in] ImageBase Image base address.
@param[in] ImageSize Image size.
@return EFI_SUCCESS Unregister successfully.
@return EFI_UNSUPPORTED Memory profile is unsupported,
or memory profile for the image is not required.
@return EFI_NOT_FOUND The image is not found.
**/
EFI_STATUS
EFIAPI
ProfileProtocolUnregisterImage (
IN EDKII_MEMORY_PROFILE_PROTOCOL *This,
IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
IN PHYSICAL_ADDRESS ImageBase,
IN UINT64 ImageSize
);
/**
Get memory profile recording state.
@param[in] This The EDKII_MEMORY_PROFILE_PROTOCOL instance.
@param[out] RecordingState Recording state.
@return EFI_SUCCESS Memory profile recording state is returned.
@return EFI_UNSUPPORTED Memory profile is unsupported.
@return EFI_INVALID_PARAMETER RecordingState is NULL.
**/
EFI_STATUS
EFIAPI
ProfileProtocolGetRecordingState (
IN EDKII_MEMORY_PROFILE_PROTOCOL *This,
OUT BOOLEAN *RecordingState
);
/**
Set memory profile recording state.
@param[in] This The EDKII_MEMORY_PROFILE_PROTOCOL instance.
@param[in] RecordingState Recording state.
@return EFI_SUCCESS Set memory profile recording state successfully.
@return EFI_UNSUPPORTED Memory profile is unsupported.
**/
EFI_STATUS
EFIAPI
ProfileProtocolSetRecordingState (
IN EDKII_MEMORY_PROFILE_PROTOCOL *This,
IN BOOLEAN RecordingState
);
/**
Record memory profile of multilevel caller.
@param[in] This The EDKII_MEMORY_PROFILE_PROTOCOL instance.
@param[in] CallerAddress Address of caller.
@param[in] Action Memory profile action.
@param[in] MemoryType Memory type.
EfiMaxMemoryType means the MemoryType is unknown.
@param[in] Buffer Buffer address.
@param[in] Size Buffer size.
@param[in] ActionString String for memory profile action.
Only needed for user defined allocate action.
@return EFI_SUCCESS Memory profile is updated.
@return EFI_UNSUPPORTED Memory profile is unsupported,
or memory profile for the image is not required,
or memory profile for the memory type is not required.
@return EFI_ACCESS_DENIED It is during memory profile data getting.
@return EFI_ABORTED Memory profile recording is not enabled.
@return EFI_OUT_OF_RESOURCES No enough resource to update memory profile for allocate action.
@return EFI_NOT_FOUND No matched allocate info found for free action.
**/
EFI_STATUS
EFIAPI
ProfileProtocolRecord (
IN EDKII_MEMORY_PROFILE_PROTOCOL *This,
IN PHYSICAL_ADDRESS CallerAddress,
IN MEMORY_PROFILE_ACTION Action,
IN EFI_MEMORY_TYPE MemoryType,
IN VOID *Buffer,
IN UINTN Size,
IN CHAR8 *ActionString OPTIONAL
);
GLOBAL_REMOVE_IF_UNREFERENCED EDKII_MEMORY_PROFILE_PROTOCOL mProfileProtocol = {
ProfileProtocolGetData,
ProfileProtocolRegisterImage,
ProfileProtocolUnregisterImage,
ProfileProtocolGetRecordingState,
ProfileProtocolSetRecordingState,
ProfileProtocolRecord,
};
/**
Acquire lock on mMemoryProfileLock.
**/
VOID
CoreAcquireMemoryProfileLock (
VOID
)
{
CoreAcquireLock (&mMemoryProfileLock);
}
/**
Release lock on mMemoryProfileLock.
**/
VOID
CoreReleaseMemoryProfileLock (
VOID
)
{
CoreReleaseLock (&mMemoryProfileLock);
}
/**
Return memory profile context.
@return Memory profile context.
**/
MEMORY_PROFILE_CONTEXT_DATA *
GetMemoryProfileContext (
VOID
)
{
return mMemoryProfileContextPtr;
}
/**
Retrieves and returns the Subsystem of a PE/COFF image that has been loaded into system memory.
If Pe32Data is NULL, then ASSERT().
@param Pe32Data The pointer to the PE/COFF image that is loaded in system memory.
@return The Subsystem of the PE/COFF image.
**/
UINT16
InternalPeCoffGetSubsystem (
IN VOID *Pe32Data
)
{
EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr;
EFI_IMAGE_DOS_HEADER *DosHdr;
UINT16 Magic;
ASSERT (Pe32Data != NULL);
DosHdr = (EFI_IMAGE_DOS_HEADER *)Pe32Data;
if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) {
//
// DOS image header is present, so read the PE header after the DOS image header.
//
Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)((UINTN)Pe32Data + (UINTN)((DosHdr->e_lfanew) & 0x0ffff));
} else {
//
// DOS image header is not present, so PE header is at the image base.
//
Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)Pe32Data;
}
if (Hdr.Te->Signature == EFI_TE_IMAGE_HEADER_SIGNATURE) {
return Hdr.Te->Subsystem;
} else if (Hdr.Pe32->Signature == EFI_IMAGE_NT_SIGNATURE) {
Magic = Hdr.Pe32->OptionalHeader.Magic;
if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
return Hdr.Pe32->OptionalHeader.Subsystem;
} else if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
return Hdr.Pe32Plus->OptionalHeader.Subsystem;
}
}
return 0x0000;
}
/**
Retrieves and returns a pointer to the entry point to a PE/COFF image that has been loaded
into system memory with the PE/COFF Loader Library functions.
Retrieves the entry point to the PE/COFF image specified by Pe32Data and returns this entry
point in EntryPoint. If the entry point could not be retrieved from the PE/COFF image, then
return RETURN_INVALID_PARAMETER. Otherwise return RETURN_SUCCESS.
If Pe32Data is NULL, then ASSERT().
If EntryPoint is NULL, then ASSERT().
@param Pe32Data The pointer to the PE/COFF image that is loaded in system memory.
@param EntryPoint The pointer to entry point to the PE/COFF image to return.
@retval RETURN_SUCCESS EntryPoint was returned.
@retval RETURN_INVALID_PARAMETER The entry point could not be found in the PE/COFF image.
**/
RETURN_STATUS
InternalPeCoffGetEntryPoint (
IN VOID *Pe32Data,
OUT VOID **EntryPoint
)
{
EFI_IMAGE_DOS_HEADER *DosHdr;
EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr;
ASSERT (Pe32Data != NULL);
ASSERT (EntryPoint != NULL);
DosHdr = (EFI_IMAGE_DOS_HEADER *)Pe32Data;
if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) {
//
// DOS image header is present, so read the PE header after the DOS image header.
//
Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)((UINTN)Pe32Data + (UINTN)((DosHdr->e_lfanew) & 0x0ffff));
} else {
//
// DOS image header is not present, so PE header is at the image base.
//
Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)Pe32Data;
}
//
// Calculate the entry point relative to the start of the image.
// AddressOfEntryPoint is common for PE32 & PE32+
//
if (Hdr.Te->Signature == EFI_TE_IMAGE_HEADER_SIGNATURE) {
*EntryPoint = (VOID *)((UINTN)Pe32Data + (UINTN)(Hdr.Te->AddressOfEntryPoint & 0x0ffffffff) + sizeof (EFI_TE_IMAGE_HEADER) - Hdr.Te->StrippedSize);
return RETURN_SUCCESS;
} else if (Hdr.Pe32->Signature == EFI_IMAGE_NT_SIGNATURE) {
*EntryPoint = (VOID *)((UINTN)Pe32Data + (UINTN)(Hdr.Pe32->OptionalHeader.AddressOfEntryPoint & 0x0ffffffff));
return RETURN_SUCCESS;
}
return RETURN_UNSUPPORTED;
}
/**
Build driver info.
@param ContextData Memory profile context.
@param FileName File name of the image.
@param ImageBase Image base address.
@param ImageSize Image size.
@param EntryPoint Entry point of the image.
@param ImageSubsystem Image subsystem of the image.
@param FileType File type of the image.
@return Pointer to memory profile driver info.
**/
MEMORY_PROFILE_DRIVER_INFO_DATA *
BuildDriverInfo (
IN MEMORY_PROFILE_CONTEXT_DATA *ContextData,
IN EFI_GUID *FileName,
IN PHYSICAL_ADDRESS ImageBase,
IN UINT64 ImageSize,
IN PHYSICAL_ADDRESS EntryPoint,
IN UINT16 ImageSubsystem,
IN EFI_FV_FILETYPE FileType
)
{
EFI_STATUS Status;
MEMORY_PROFILE_DRIVER_INFO *DriverInfo;
MEMORY_PROFILE_DRIVER_INFO_DATA *DriverInfoData;
VOID *EntryPointInImage;
CHAR8 *PdbString;
UINTN PdbSize;
UINTN PdbOccupiedSize;
PdbSize = 0;
PdbOccupiedSize = 0;
PdbString = NULL;
if (ImageBase != 0) {
PdbString = PeCoffLoaderGetPdbPointer ((VOID *)(UINTN)ImageBase);
if (PdbString != NULL) {
PdbSize = AsciiStrSize (PdbString);
PdbOccupiedSize = GET_OCCUPIED_SIZE (PdbSize, sizeof (UINT64));
}
}
//
// Use CoreInternalAllocatePool() that will not update profile for this AllocatePool action.
//
Status = CoreInternalAllocatePool (
EfiBootServicesData,
sizeof (*DriverInfoData) + sizeof (LIST_ENTRY) + PdbSize,
(VOID **)&DriverInfoData
);
if (EFI_ERROR (Status)) {
return NULL;
}
ASSERT (DriverInfoData != NULL);
ZeroMem (DriverInfoData, sizeof (*DriverInfoData));
DriverInfo = &DriverInfoData->DriverInfo;
DriverInfoData->Signature = MEMORY_PROFILE_DRIVER_INFO_SIGNATURE;
DriverInfo->Header.Signature = MEMORY_PROFILE_DRIVER_INFO_SIGNATURE;
DriverInfo->Header.Length = (UINT16)(sizeof (MEMORY_PROFILE_DRIVER_INFO) + PdbOccupiedSize);
DriverInfo->Header.Revision = MEMORY_PROFILE_DRIVER_INFO_REVISION;
if (FileName != NULL) {
CopyMem (&DriverInfo->FileName, FileName, sizeof (EFI_GUID));
}
DriverInfo->ImageBase = ImageBase;
DriverInfo->ImageSize = ImageSize;
DriverInfo->EntryPoint = EntryPoint;
DriverInfo->ImageSubsystem = ImageSubsystem;
if ((EntryPoint != 0) && ((EntryPoint < ImageBase) || (EntryPoint >= (ImageBase + ImageSize)))) {
//
// If the EntryPoint is not in the range of image buffer, it should come from emulation environment.
// So patch ImageBuffer here to align the EntryPoint.
//
Status = InternalPeCoffGetEntryPoint ((VOID *)(UINTN)ImageBase, &EntryPointInImage);
ASSERT_EFI_ERROR (Status);
DriverInfo->ImageBase = ImageBase + EntryPoint - (PHYSICAL_ADDRESS)(UINTN)EntryPointInImage;
}
DriverInfo->FileType = FileType;
DriverInfoData->AllocInfoList = (LIST_ENTRY *)(DriverInfoData + 1);
InitializeListHead (DriverInfoData->AllocInfoList);
DriverInfo->CurrentUsage = 0;
DriverInfo->PeakUsage = 0;
DriverInfo->AllocRecordCount = 0;
if (PdbSize != 0) {
DriverInfo->PdbStringOffset = (UINT16)sizeof (MEMORY_PROFILE_DRIVER_INFO);
DriverInfoData->PdbString = (CHAR8 *)(DriverInfoData->AllocInfoList + 1);
CopyMem (DriverInfoData->PdbString, PdbString, PdbSize);
} else {
DriverInfo->PdbStringOffset = 0;
DriverInfoData->PdbString = NULL;
}
InsertTailList (ContextData->DriverInfoList, &DriverInfoData->Link);
ContextData->Context.ImageCount++;
ContextData->Context.TotalImageSize += DriverInfo->ImageSize;
return DriverInfoData;
}
/**
Return if record for this driver is needed..
@param DriverFilePath Driver file path.
@retval TRUE Record for this driver is needed.
@retval FALSE Record for this driver is not needed.
**/
BOOLEAN
NeedRecordThisDriver (
IN EFI_DEVICE_PATH_PROTOCOL *DriverFilePath
)
{
EFI_DEVICE_PATH_PROTOCOL *TmpDevicePath;
EFI_DEVICE_PATH_PROTOCOL *DevicePathInstance;
UINTN DevicePathSize;
UINTN FilePathSize;
if (!IsDevicePathValid (mMemoryProfileDriverPath, mMemoryProfileDriverPathSize)) {
//
// Invalid Device Path means record all.
//
return TRUE;
}
//
// Record FilePath without END node.
//
FilePathSize = GetDevicePathSize (DriverFilePath) - sizeof (EFI_DEVICE_PATH_PROTOCOL);
DevicePathInstance = mMemoryProfileDriverPath;
do {
//
// Find END node (it might be END_ENTIRE or END_INSTANCE).
//
TmpDevicePath = DevicePathInstance;
while (!IsDevicePathEndType (TmpDevicePath)) {
TmpDevicePath = NextDevicePathNode (TmpDevicePath);
}
//
// Do not compare END node.
//
DevicePathSize = (UINTN)TmpDevicePath - (UINTN)DevicePathInstance;
if ((FilePathSize == DevicePathSize) &&
(CompareMem (DriverFilePath, DevicePathInstance, DevicePathSize) == 0))
{
return TRUE;
}
//
// Get next instance.
//
DevicePathInstance = (EFI_DEVICE_PATH_PROTOCOL *)((UINTN)DevicePathInstance + DevicePathSize + DevicePathNodeLength (TmpDevicePath));
} while (DevicePathSubType (TmpDevicePath) != END_ENTIRE_DEVICE_PATH_SUBTYPE);
return FALSE;
}
/**
Register DXE Core to memory profile.
@param HobStart The start address of the HOB.
@param ContextData Memory profile context.
@retval TRUE Register success.
@retval FALSE Register fail.
**/
BOOLEAN
RegisterDxeCore (
IN VOID *HobStart,
IN MEMORY_PROFILE_CONTEXT_DATA *ContextData
)
{
EFI_PEI_HOB_POINTERS DxeCoreHob;
MEMORY_PROFILE_DRIVER_INFO_DATA *DriverInfoData;
PHYSICAL_ADDRESS ImageBase;
UINT8 TempBuffer[sizeof (MEDIA_FW_VOL_FILEPATH_DEVICE_PATH) + sizeof (EFI_DEVICE_PATH_PROTOCOL)];
MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *FilePath;
ASSERT (ContextData != NULL);
//
// Searching for image hob
//
DxeCoreHob.Raw = HobStart;
while ((DxeCoreHob.Raw = GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION, DxeCoreHob.Raw)) != NULL) {
if (CompareGuid (&DxeCoreHob.MemoryAllocationModule->MemoryAllocationHeader.Name, &gEfiHobMemoryAllocModuleGuid)) {
//
// Find Dxe Core HOB
//
if (CompareGuid (&DxeCoreHob.MemoryAllocationModule->ModuleName, &gEfiCallerIdGuid)) {
break;
}
}
DxeCoreHob.Raw = GET_NEXT_HOB (DxeCoreHob);
}
ASSERT (DxeCoreHob.Raw != NULL);
FilePath = (MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *)TempBuffer;
EfiInitializeFwVolDevicepathNode (FilePath, &DxeCoreHob.MemoryAllocationModule->ModuleName);
SetDevicePathEndNode (FilePath + 1);
if (!NeedRecordThisDriver ((EFI_DEVICE_PATH_PROTOCOL *)FilePath)) {
return FALSE;
}
ImageBase = DxeCoreHob.MemoryAllocationModule->MemoryAllocationHeader.MemoryBaseAddress;
DriverInfoData = BuildDriverInfo (
ContextData,
&DxeCoreHob.MemoryAllocationModule->ModuleName,
ImageBase,
DxeCoreHob.MemoryAllocationModule->MemoryAllocationHeader.MemoryLength,
DxeCoreHob.MemoryAllocationModule->EntryPoint,
InternalPeCoffGetSubsystem ((VOID *)(UINTN)ImageBase),
EFI_FV_FILETYPE_DXE_CORE
);
if (DriverInfoData == NULL) {
return FALSE;
}
return TRUE;
}
/**
Initialize memory profile.
@param HobStart The start address of the HOB.
**/
VOID
MemoryProfileInit (
IN VOID *HobStart
)
{
MEMORY_PROFILE_CONTEXT_DATA *ContextData;
if (!IS_UEFI_MEMORY_PROFILE_ENABLED) {
return;
}
ContextData = GetMemoryProfileContext ();
if (ContextData != NULL) {
return;
}
mMemoryProfileGettingStatus = FALSE;
if ((PcdGet8 (PcdMemoryProfilePropertyMask) & BIT7) != 0) {
mMemoryProfileRecordingEnable = MEMORY_PROFILE_RECORDING_DISABLE;
} else {
mMemoryProfileRecordingEnable = MEMORY_PROFILE_RECORDING_ENABLE;
}
mMemoryProfileDriverPathSize = PcdGetSize (PcdMemoryProfileDriverPath);
mMemoryProfileDriverPath = AllocateCopyPool (mMemoryProfileDriverPathSize, PcdGetPtr (PcdMemoryProfileDriverPath));
mMemoryProfileContextPtr = &mMemoryProfileContext;
RegisterDxeCore (HobStart, &mMemoryProfileContext);
DEBUG ((DEBUG_INFO, "MemoryProfileInit MemoryProfileContext - 0x%x\n", &mMemoryProfileContext));
}
/**
Install memory profile protocol.
**/
VOID
MemoryProfileInstallProtocol (
VOID
)
{
EFI_HANDLE Handle;
EFI_STATUS Status;
if (!IS_UEFI_MEMORY_PROFILE_ENABLED) {
return;
}
Handle = NULL;
Status = CoreInstallMultipleProtocolInterfaces (
&Handle,
&gEdkiiMemoryProfileGuid,
&mProfileProtocol,
NULL
);
ASSERT_EFI_ERROR (Status);
}
/**
Get the GUID file name from the file path.
@param FilePath File path.
@return The GUID file name from the file path.
**/
EFI_GUID *
GetFileNameFromFilePath (
IN EFI_DEVICE_PATH_PROTOCOL *FilePath
)
{
MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *ThisFilePath;
EFI_GUID *FileName;
FileName = NULL;
if (FilePath != NULL) {
ThisFilePath = (MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *)FilePath;
while (!IsDevicePathEnd (ThisFilePath)) {
FileName = EfiGetNameGuidFromFwVolDevicePathNode (ThisFilePath);
if (FileName != NULL) {
break;
}
ThisFilePath = (MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *)NextDevicePathNode (ThisFilePath);
}
}
return FileName;
}
/**
Register image to memory profile.
@param DriverEntry Image info.
@param FileType Image file type.
@return EFI_SUCCESS Register successfully.
@return EFI_UNSUPPORTED Memory profile is unsupported,
or memory profile for the image is not required.
@return EFI_OUT_OF_RESOURCES No enough resource for this register.
**/
EFI_STATUS
RegisterMemoryProfileImage (
IN LOADED_IMAGE_PRIVATE_DATA *DriverEntry,
IN EFI_FV_FILETYPE FileType
)
{
MEMORY_PROFILE_CONTEXT_DATA *ContextData;
MEMORY_PROFILE_DRIVER_INFO_DATA *DriverInfoData;
if (!IS_UEFI_MEMORY_PROFILE_ENABLED) {
return EFI_UNSUPPORTED;
}
if (!NeedRecordThisDriver (DriverEntry->Info.FilePath)) {
return EFI_UNSUPPORTED;
}
ContextData = GetMemoryProfileContext ();
if (ContextData == NULL) {
return EFI_UNSUPPORTED;
}
DriverInfoData = BuildDriverInfo (
ContextData,
GetFileNameFromFilePath (DriverEntry->Info.FilePath),
DriverEntry->ImageContext.ImageAddress,
DriverEntry->ImageContext.ImageSize,
DriverEntry->ImageContext.EntryPoint,
DriverEntry->ImageContext.ImageType,
FileType
);
if (DriverInfoData == NULL) {
return EFI_OUT_OF_RESOURCES;
}
return EFI_SUCCESS;
}
/**
Search image from memory profile.
@param ContextData Memory profile context.
@param FileName Image file name.
@param Address Image Address.
@return Pointer to memory profile driver info.
**/
MEMORY_PROFILE_DRIVER_INFO_DATA *
GetMemoryProfileDriverInfoByFileNameAndAddress (
IN MEMORY_PROFILE_CONTEXT_DATA *ContextData,
IN EFI_GUID *FileName,
IN PHYSICAL_ADDRESS Address
)
{
MEMORY_PROFILE_DRIVER_INFO *DriverInfo;
MEMORY_PROFILE_DRIVER_INFO_DATA *DriverInfoData;
LIST_ENTRY *DriverLink;
LIST_ENTRY *DriverInfoList;
DriverInfoList = ContextData->DriverInfoList;
for (DriverLink = DriverInfoList->ForwardLink;
DriverLink != DriverInfoList;
DriverLink = DriverLink->ForwardLink)
{
DriverInfoData = CR (
DriverLink,
MEMORY_PROFILE_DRIVER_INFO_DATA,
Link,
MEMORY_PROFILE_DRIVER_INFO_SIGNATURE
);
DriverInfo = &DriverInfoData->DriverInfo;
if ((CompareGuid (&DriverInfo->FileName, FileName)) &&
(Address >= DriverInfo->ImageBase) &&
(Address < (DriverInfo->ImageBase + DriverInfo->ImageSize)))
{
return DriverInfoData;
}
}
return NULL;
}
/**
Search image from memory profile.
It will return image, if (Address >= ImageBuffer) AND (Address < ImageBuffer + ImageSize).
@param ContextData Memory profile context.
@param Address Image or Function address.
@return Pointer to memory profile driver info.
**/
MEMORY_PROFILE_DRIVER_INFO_DATA *
GetMemoryProfileDriverInfoFromAddress (
IN MEMORY_PROFILE_CONTEXT_DATA *ContextData,
IN PHYSICAL_ADDRESS Address
)
{
MEMORY_PROFILE_DRIVER_INFO *DriverInfo;
MEMORY_PROFILE_DRIVER_INFO_DATA *DriverInfoData;
LIST_ENTRY *DriverLink;
LIST_ENTRY *DriverInfoList;
DriverInfoList = ContextData->DriverInfoList;
for (DriverLink = DriverInfoList->ForwardLink;
DriverLink != DriverInfoList;
DriverLink = DriverLink->ForwardLink)
{
DriverInfoData = CR (
DriverLink,
MEMORY_PROFILE_DRIVER_INFO_DATA,
Link,
MEMORY_PROFILE_DRIVER_INFO_SIGNATURE
);
DriverInfo = &DriverInfoData->DriverInfo;
if ((Address >= DriverInfo->ImageBase) &&
(Address < (DriverInfo->ImageBase + DriverInfo->ImageSize)))
{
return DriverInfoData;
}
}
return NULL;
}
/**
Unregister image from memory profile.
@param DriverEntry Image info.
@return EFI_SUCCESS Unregister successfully.
@return EFI_UNSUPPORTED Memory profile is unsupported,
or memory profile for the image is not required.
@return EFI_NOT_FOUND The image is not found.
**/
EFI_STATUS
UnregisterMemoryProfileImage (
IN LOADED_IMAGE_PRIVATE_DATA *DriverEntry
)
{
EFI_STATUS Status;
MEMORY_PROFILE_CONTEXT_DATA *ContextData;
MEMORY_PROFILE_DRIVER_INFO_DATA *DriverInfoData;
EFI_GUID *FileName;
PHYSICAL_ADDRESS ImageAddress;
VOID *EntryPointInImage;
if (!IS_UEFI_MEMORY_PROFILE_ENABLED) {
return EFI_UNSUPPORTED;
}
if (!NeedRecordThisDriver (DriverEntry->Info.FilePath)) {
return EFI_UNSUPPORTED;
}
ContextData = GetMemoryProfileContext ();
if (ContextData == NULL) {
return EFI_UNSUPPORTED;
}
DriverInfoData = NULL;
FileName = GetFileNameFromFilePath (DriverEntry->Info.FilePath);
ImageAddress = DriverEntry->ImageContext.ImageAddress;
if ((DriverEntry->ImageContext.EntryPoint < ImageAddress) || (DriverEntry->ImageContext.EntryPoint >= (ImageAddress + DriverEntry->ImageContext.ImageSize))) {
//
// If the EntryPoint is not in the range of image buffer, it should come from emulation environment.
// So patch ImageAddress here to align the EntryPoint.
//
Status = InternalPeCoffGetEntryPoint ((VOID *)(UINTN)ImageAddress, &EntryPointInImage);
ASSERT_EFI_ERROR (Status);
ImageAddress = ImageAddress + (UINTN)DriverEntry->ImageContext.EntryPoint - (UINTN)EntryPointInImage;
}
if (FileName != NULL) {
DriverInfoData = GetMemoryProfileDriverInfoByFileNameAndAddress (ContextData, FileName, ImageAddress);
}
if (DriverInfoData == NULL) {
DriverInfoData = GetMemoryProfileDriverInfoFromAddress (ContextData, ImageAddress);
}
if (DriverInfoData == NULL) {
return EFI_NOT_FOUND;
}
ContextData->Context.TotalImageSize -= DriverInfoData->DriverInfo.ImageSize;
// Keep the ImageBase for RVA calculation in Application.
// DriverInfoData->DriverInfo.ImageBase = 0;
DriverInfoData->DriverInfo.ImageSize = 0;
if (DriverInfoData->DriverInfo.PeakUsage == 0) {
ContextData->Context.ImageCount--;
RemoveEntryList (&DriverInfoData->Link);
//
// Use CoreInternalFreePool() that will not update profile for this FreePool action.
//
CoreInternalFreePool (DriverInfoData, NULL);
}
return EFI_SUCCESS;
}
/**
Return if this memory type needs to be recorded into memory profile.
If BIOS memory type (0 ~ EfiMaxMemoryType - 1), it checks bit (1 << MemoryType).
If OS memory type (0x80000000 ~ 0xFFFFFFFF), it checks bit63 - 0x8000000000000000.
If OEM memory type (0x70000000 ~ 0x7FFFFFFF), it checks bit62 - 0x4000000000000000.
@param MemoryType Memory type.
@retval TRUE This memory type need to be recorded.
@retval FALSE This memory type need not to be recorded.
**/
BOOLEAN
CoreNeedRecordProfile (
IN EFI_MEMORY_TYPE MemoryType
)
{
UINT64 TestBit;
if ((UINT32)MemoryType >= MEMORY_TYPE_OS_RESERVED_MIN) {
TestBit = BIT63;
} else if ((UINT32)MemoryType >= MEMORY_TYPE_OEM_RESERVED_MIN) {
TestBit = BIT62;
} else {
TestBit = LShiftU64 (1, MemoryType);
}
if ((PcdGet64 (PcdMemoryProfileMemoryType) & TestBit) != 0) {
return TRUE;
} else {
return FALSE;
}
}
/**
Convert EFI memory type to profile memory index. The rule is:
If BIOS memory type (0 ~ EfiMaxMemoryType - 1), ProfileMemoryIndex = MemoryType.
If OS memory type (0x80000000 ~ 0xFFFFFFFF), ProfileMemoryIndex = EfiMaxMemoryType.
If OEM memory type (0x70000000 ~ 0x7FFFFFFF), ProfileMemoryIndex = EfiMaxMemoryType + 1.
@param MemoryType Memory type.
@return Profile memory index.
**/
UINTN
GetProfileMemoryIndex (
IN EFI_MEMORY_TYPE MemoryType
)
{
if ((UINT32)MemoryType >= MEMORY_TYPE_OS_RESERVED_MIN) {
return EfiMaxMemoryType;
} else if ((UINT32)MemoryType >= MEMORY_TYPE_OEM_RESERVED_MIN) {
return EfiMaxMemoryType + 1;
} else {
return MemoryType;
}
}
/**
Update memory profile Allocate information.
@param CallerAddress Address of caller who call Allocate.
@param Action This Allocate action.
@param MemoryType Memory type.
@param Size Buffer size.
@param Buffer Buffer address.
@param ActionString String for memory profile action.
@return EFI_SUCCESS Memory profile is updated.
@return EFI_UNSUPPORTED Memory profile is unsupported,
or memory profile for the image is not required.
@return EFI_OUT_OF_RESOURCES No enough resource to update memory profile for allocate action.
**/
EFI_STATUS
CoreUpdateProfileAllocate (
IN PHYSICAL_ADDRESS CallerAddress,
IN MEMORY_PROFILE_ACTION Action,
IN EFI_MEMORY_TYPE MemoryType,
IN UINTN Size,
IN VOID *Buffer,
IN CHAR8 *ActionString OPTIONAL
)
{
EFI_STATUS Status;
MEMORY_PROFILE_CONTEXT *Context;
MEMORY_PROFILE_DRIVER_INFO *DriverInfo;
MEMORY_PROFILE_ALLOC_INFO *AllocInfo;
MEMORY_PROFILE_CONTEXT_DATA *ContextData;
MEMORY_PROFILE_DRIVER_INFO_DATA *DriverInfoData;
MEMORY_PROFILE_ALLOC_INFO_DATA *AllocInfoData;
UINTN ProfileMemoryIndex;
MEMORY_PROFILE_ACTION BasicAction;
UINTN ActionStringSize;
UINTN ActionStringOccupiedSize;
BasicAction = Action & MEMORY_PROFILE_ACTION_BASIC_MASK;
ContextData = GetMemoryProfileContext ();
if (ContextData == NULL) {
return EFI_UNSUPPORTED;
}
DriverInfoData = GetMemoryProfileDriverInfoFromAddress (ContextData, CallerAddress);
if (DriverInfoData == NULL) {
return EFI_UNSUPPORTED;
}
ActionStringSize = 0;
ActionStringOccupiedSize = 0;
if (ActionString != NULL) {
ActionStringSize = AsciiStrSize (ActionString);
ActionStringOccupiedSize = GET_OCCUPIED_SIZE (ActionStringSize, sizeof (UINT64));
}
//
// Use CoreInternalAllocatePool() that will not update profile for this AllocatePool action.
//
AllocInfoData = NULL;
Status = CoreInternalAllocatePool (
EfiBootServicesData,
sizeof (*AllocInfoData) + ActionStringSize,
(VOID **)&AllocInfoData
);
if (EFI_ERROR (Status)) {
return EFI_OUT_OF_RESOURCES;
}
ASSERT (AllocInfoData != NULL);
//
// Only update SequenceCount if and only if it is basic action.
//
if (Action == BasicAction) {
ContextData->Context.SequenceCount++;
}
AllocInfo = &AllocInfoData->AllocInfo;
AllocInfoData->Signature = MEMORY_PROFILE_ALLOC_INFO_SIGNATURE;
AllocInfo->Header.Signature = MEMORY_PROFILE_ALLOC_INFO_SIGNATURE;
AllocInfo->Header.Length = (UINT16)(sizeof (MEMORY_PROFILE_ALLOC_INFO) + ActionStringOccupiedSize);
AllocInfo->Header.Revision = MEMORY_PROFILE_ALLOC_INFO_REVISION;
AllocInfo->CallerAddress = CallerAddress;
AllocInfo->SequenceId = ContextData->Context.SequenceCount;
AllocInfo->Action = Action;
AllocInfo->MemoryType = MemoryType;
AllocInfo->Buffer = (PHYSICAL_ADDRESS)(UINTN)Buffer;
AllocInfo->Size = Size;
if (ActionString != NULL) {
AllocInfo->ActionStringOffset = (UINT16)sizeof (MEMORY_PROFILE_ALLOC_INFO);
AllocInfoData->ActionString = (CHAR8 *)(AllocInfoData + 1);
CopyMem (AllocInfoData->ActionString, ActionString, ActionStringSize);
} else {
AllocInfo->ActionStringOffset = 0;
AllocInfoData->ActionString = NULL;
}
InsertTailList (DriverInfoData->AllocInfoList, &AllocInfoData->Link);
Context = &ContextData->Context;
DriverInfo = &DriverInfoData->DriverInfo;
DriverInfo->AllocRecordCount++;
//
// Update summary if and only if it is basic action.
//
if (Action == BasicAction) {
ProfileMemoryIndex = GetProfileMemoryIndex (MemoryType);
DriverInfo->CurrentUsage += Size;
if (DriverInfo->PeakUsage < DriverInfo->CurrentUsage) {
DriverInfo->PeakUsage = DriverInfo->CurrentUsage;
}
DriverInfo->CurrentUsageByType[ProfileMemoryIndex] += Size;
if (DriverInfo->PeakUsageByType[ProfileMemoryIndex] < DriverInfo->CurrentUsageByType[ProfileMemoryIndex]) {
DriverInfo->PeakUsageByType[ProfileMemoryIndex] = DriverInfo->CurrentUsageByType[ProfileMemoryIndex];
}
Context->CurrentTotalUsage += Size;
if (Context->PeakTotalUsage < Context->CurrentTotalUsage) {
Context->PeakTotalUsage = Context->CurrentTotalUsage;
}
Context->CurrentTotalUsageByType[ProfileMemoryIndex] += Size;
if (Context->PeakTotalUsageByType[ProfileMemoryIndex] < Context->CurrentTotalUsageByType[ProfileMemoryIndex]) {
Context->PeakTotalUsageByType[ProfileMemoryIndex] = Context->CurrentTotalUsageByType[ProfileMemoryIndex];
}
}
return EFI_SUCCESS;
}
/**
Get memory profile alloc info from memory profile.
@param DriverInfoData Driver info.
@param BasicAction This Free basic action.
@param Size Buffer size.
@param Buffer Buffer address.
@return Pointer to memory profile alloc info.
**/
MEMORY_PROFILE_ALLOC_INFO_DATA *
GetMemoryProfileAllocInfoFromAddress (
IN MEMORY_PROFILE_DRIVER_INFO_DATA *DriverInfoData,
IN MEMORY_PROFILE_ACTION BasicAction,
IN UINTN Size,
IN VOID *Buffer
)
{
LIST_ENTRY *AllocInfoList;
LIST_ENTRY *AllocLink;
MEMORY_PROFILE_ALLOC_INFO *AllocInfo;
MEMORY_PROFILE_ALLOC_INFO_DATA *AllocInfoData;
AllocInfoList = DriverInfoData->AllocInfoList;
for (AllocLink = AllocInfoList->ForwardLink;
AllocLink != AllocInfoList;
AllocLink = AllocLink->ForwardLink)
{
AllocInfoData = CR (
AllocLink,
MEMORY_PROFILE_ALLOC_INFO_DATA,
Link,
MEMORY_PROFILE_ALLOC_INFO_SIGNATURE
);
AllocInfo = &AllocInfoData->AllocInfo;
if ((AllocInfo->Action & MEMORY_PROFILE_ACTION_BASIC_MASK) != BasicAction) {
continue;
}
switch (BasicAction) {
case MemoryProfileActionAllocatePages:
if ((AllocInfo->Buffer <= (PHYSICAL_ADDRESS)(UINTN)Buffer) &&
((AllocInfo->Buffer + AllocInfo->Size) >= ((PHYSICAL_ADDRESS)(UINTN)Buffer + Size)))
{
return AllocInfoData;
}
break;
case MemoryProfileActionAllocatePool:
if (AllocInfo->Buffer == (PHYSICAL_ADDRESS)(UINTN)Buffer) {
return AllocInfoData;
}
break;
default:
ASSERT (FALSE);
break;
}
}
return NULL;
}
/**
Update memory profile Free information.
@param CallerAddress Address of caller who call Free.
@param Action This Free action.
@param Size Buffer size.
@param Buffer Buffer address.
@return EFI_SUCCESS Memory profile is updated.
@return EFI_UNSUPPORTED Memory profile is unsupported.
@return EFI_NOT_FOUND No matched allocate info found for free action.
**/
EFI_STATUS
CoreUpdateProfileFree (
IN PHYSICAL_ADDRESS CallerAddress,
IN MEMORY_PROFILE_ACTION Action,
IN UINTN Size,
IN VOID *Buffer
)
{
MEMORY_PROFILE_CONTEXT *Context;
MEMORY_PROFILE_DRIVER_INFO *DriverInfo;
MEMORY_PROFILE_ALLOC_INFO *AllocInfo;
MEMORY_PROFILE_CONTEXT_DATA *ContextData;
MEMORY_PROFILE_DRIVER_INFO_DATA *DriverInfoData;
LIST_ENTRY *DriverLink;
LIST_ENTRY *DriverInfoList;
MEMORY_PROFILE_DRIVER_INFO_DATA *ThisDriverInfoData;
MEMORY_PROFILE_ALLOC_INFO_DATA *AllocInfoData;
UINTN ProfileMemoryIndex;
MEMORY_PROFILE_ACTION BasicAction;
BOOLEAN Found;
BasicAction = Action & MEMORY_PROFILE_ACTION_BASIC_MASK;
ContextData = GetMemoryProfileContext ();
if (ContextData == NULL) {
return EFI_UNSUPPORTED;
}
DriverInfoData = GetMemoryProfileDriverInfoFromAddress (ContextData, CallerAddress);
//
// Do not return if DriverInfoData == NULL here,
// because driver A might free memory allocated by driver B.
//
//
// Need use do-while loop to find all possible records,
// because one address might be recorded multiple times.
//
Found = FALSE;
AllocInfoData = NULL;
do {
if (DriverInfoData != NULL) {
switch (BasicAction) {
case MemoryProfileActionFreePages:
AllocInfoData = GetMemoryProfileAllocInfoFromAddress (DriverInfoData, MemoryProfileActionAllocatePages, Size, Buffer);
break;
case MemoryProfileActionFreePool:
AllocInfoData = GetMemoryProfileAllocInfoFromAddress (DriverInfoData, MemoryProfileActionAllocatePool, 0, Buffer);
break;
default:
ASSERT (FALSE);
AllocInfoData = NULL;
break;
}
}
if (AllocInfoData == NULL) {
//
// Legal case, because driver A might free memory allocated by driver B, by some protocol.
//
DriverInfoList = ContextData->DriverInfoList;
for (DriverLink = DriverInfoList->ForwardLink;
DriverLink != DriverInfoList;
DriverLink = DriverLink->ForwardLink)
{
ThisDriverInfoData = CR (
DriverLink,
MEMORY_PROFILE_DRIVER_INFO_DATA,
Link,
MEMORY_PROFILE_DRIVER_INFO_SIGNATURE
);
switch (BasicAction) {
case MemoryProfileActionFreePages:
AllocInfoData = GetMemoryProfileAllocInfoFromAddress (ThisDriverInfoData, MemoryProfileActionAllocatePages, Size, Buffer);
break;
case MemoryProfileActionFreePool:
AllocInfoData = GetMemoryProfileAllocInfoFromAddress (ThisDriverInfoData, MemoryProfileActionAllocatePool, 0, Buffer);
break;
default:
ASSERT (FALSE);
AllocInfoData = NULL;
break;
}
if (AllocInfoData != NULL) {
DriverInfoData = ThisDriverInfoData;
break;
}
}
if (AllocInfoData == NULL) {
//
// If (!Found), no matched allocate info is found for this free action.
// It is because the specified memory type allocate actions have been filtered by
// CoreNeedRecordProfile(), but free actions may have no memory type information,
// they can not be filtered by CoreNeedRecordProfile(). Then, they will be
// filtered here.
//
// If (Found), it is normal exit path.
return (Found ? EFI_SUCCESS : EFI_NOT_FOUND);
}
}
if ((DriverInfoData == NULL) || (AllocInfoData == NULL)) {
ASSERT (DriverInfoData != NULL);
ASSERT (AllocInfoData != NULL);
return EFI_NOT_FOUND;
}
Found = TRUE;
Context = &ContextData->Context;
DriverInfo = &DriverInfoData->DriverInfo;
AllocInfo = &AllocInfoData->AllocInfo;
DriverInfo->AllocRecordCount--;
//
// Update summary if and only if it is basic action.
//
if (AllocInfo->Action == (AllocInfo->Action & MEMORY_PROFILE_ACTION_BASIC_MASK)) {
ProfileMemoryIndex = GetProfileMemoryIndex (AllocInfo->MemoryType);
Context->CurrentTotalUsage -= AllocInfo->Size;
Context->CurrentTotalUsageByType[ProfileMemoryIndex] -= AllocInfo->Size;
DriverInfo->CurrentUsage -= AllocInfo->Size;
DriverInfo->CurrentUsageByType[ProfileMemoryIndex] -= AllocInfo->Size;
}
RemoveEntryList (&AllocInfoData->Link);
if (BasicAction == MemoryProfileActionFreePages) {
if (AllocInfo->Buffer != (PHYSICAL_ADDRESS)(UINTN)Buffer) {
CoreUpdateProfileAllocate (
AllocInfo->CallerAddress,
AllocInfo->Action,
AllocInfo->MemoryType,
(UINTN)((PHYSICAL_ADDRESS)(UINTN)Buffer - AllocInfo->Buffer),
(VOID *)(UINTN)AllocInfo->Buffer,
AllocInfoData->ActionString
);
}
if (AllocInfo->Buffer + AllocInfo->Size != ((PHYSICAL_ADDRESS)(UINTN)Buffer + Size)) {
CoreUpdateProfileAllocate (
AllocInfo->CallerAddress,
AllocInfo->Action,
AllocInfo->MemoryType,
(UINTN)((AllocInfo->Buffer + AllocInfo->Size) - ((PHYSICAL_ADDRESS)(UINTN)Buffer + Size)),
(VOID *)((UINTN)Buffer + Size),
AllocInfoData->ActionString
);
}
}
//
// Use CoreInternalFreePool() that will not update profile for this FreePool action.
//
CoreInternalFreePool (AllocInfoData, NULL);
} while (TRUE);
}
/**
Update memory profile information.
@param CallerAddress Address of caller who call Allocate or Free.
@param Action This Allocate or Free action.
@param MemoryType Memory type.
EfiMaxMemoryType means the MemoryType is unknown.
@param Size Buffer size.
@param Buffer Buffer address.
@param ActionString String for memory profile action.
Only needed for user defined allocate action.
@return EFI_SUCCESS Memory profile is updated.
@return EFI_UNSUPPORTED Memory profile is unsupported,
or memory profile for the image is not required,
or memory profile for the memory type is not required.
@return EFI_ACCESS_DENIED It is during memory profile data getting.
@return EFI_ABORTED Memory profile recording is not enabled.
@return EFI_OUT_OF_RESOURCES No enough resource to update memory profile for allocate action.
@return EFI_NOT_FOUND No matched allocate info found for free action.
**/
EFI_STATUS
EFIAPI
CoreUpdateProfile (
IN PHYSICAL_ADDRESS CallerAddress,
IN MEMORY_PROFILE_ACTION Action,
IN EFI_MEMORY_TYPE MemoryType,
IN UINTN Size, // Valid for AllocatePages/FreePages/AllocatePool
IN VOID *Buffer,
IN CHAR8 *ActionString OPTIONAL
)
{
EFI_STATUS Status;
MEMORY_PROFILE_CONTEXT_DATA *ContextData;
MEMORY_PROFILE_ACTION BasicAction;
if (!IS_UEFI_MEMORY_PROFILE_ENABLED) {
return EFI_UNSUPPORTED;
}
if (mMemoryProfileGettingStatus) {
return EFI_ACCESS_DENIED;
}
if (!mMemoryProfileRecordingEnable) {
return EFI_ABORTED;
}
//
// Get the basic action to know how to process the record
//
BasicAction = Action & MEMORY_PROFILE_ACTION_BASIC_MASK;
//
// EfiMaxMemoryType means the MemoryType is unknown.
//
if (MemoryType != EfiMaxMemoryType) {
//
// Only record limited MemoryType.
//
if (!CoreNeedRecordProfile (MemoryType)) {
return EFI_UNSUPPORTED;
}
}
ContextData = GetMemoryProfileContext ();
if (ContextData == NULL) {
return EFI_UNSUPPORTED;
}
CoreAcquireMemoryProfileLock ();
switch (BasicAction) {
case MemoryProfileActionAllocatePages:
Status = CoreUpdateProfileAllocate (CallerAddress, Action, MemoryType, Size, Buffer, ActionString);
break;
case MemoryProfileActionFreePages:
Status = CoreUpdateProfileFree (CallerAddress, Action, Size, Buffer);
break;
case MemoryProfileActionAllocatePool:
Status = CoreUpdateProfileAllocate (CallerAddress, Action, MemoryType, Size, Buffer, ActionString);
break;
case MemoryProfileActionFreePool:
Status = CoreUpdateProfileFree (CallerAddress, Action, 0, Buffer);
break;
default:
ASSERT (FALSE);
Status = EFI_UNSUPPORTED;
break;
}
CoreReleaseMemoryProfileLock ();
return Status;
}
////////////////////
/**
Get memory profile data size.
@return Memory profile data size.
**/
UINTN
MemoryProfileGetDataSize (
VOID
)
{
MEMORY_PROFILE_CONTEXT_DATA *ContextData;
MEMORY_PROFILE_DRIVER_INFO_DATA *DriverInfoData;
MEMORY_PROFILE_ALLOC_INFO_DATA *AllocInfoData;
LIST_ENTRY *DriverInfoList;
LIST_ENTRY *DriverLink;
LIST_ENTRY *AllocInfoList;
LIST_ENTRY *AllocLink;
UINTN TotalSize;
ContextData = GetMemoryProfileContext ();
if (ContextData == NULL) {
return 0;
}
TotalSize = sizeof (MEMORY_PROFILE_CONTEXT);
DriverInfoList = ContextData->DriverInfoList;
for (DriverLink = DriverInfoList->ForwardLink;
DriverLink != DriverInfoList;
DriverLink = DriverLink->ForwardLink)
{
DriverInfoData = CR (
DriverLink,
MEMORY_PROFILE_DRIVER_INFO_DATA,
Link,
MEMORY_PROFILE_DRIVER_INFO_SIGNATURE
);
TotalSize += DriverInfoData->DriverInfo.Header.Length;
AllocInfoList = DriverInfoData->AllocInfoList;
for (AllocLink = AllocInfoList->ForwardLink;
AllocLink != AllocInfoList;
AllocLink = AllocLink->ForwardLink)
{
AllocInfoData = CR (
AllocLink,
MEMORY_PROFILE_ALLOC_INFO_DATA,
Link,
MEMORY_PROFILE_ALLOC_INFO_SIGNATURE
);
TotalSize += AllocInfoData->AllocInfo.Header.Length;
}
}
return TotalSize;
}
/**
Copy memory profile data.
@param ProfileBuffer The buffer to hold memory profile data.
**/
VOID
MemoryProfileCopyData (
IN VOID *ProfileBuffer
)
{
MEMORY_PROFILE_CONTEXT *Context;
MEMORY_PROFILE_DRIVER_INFO *DriverInfo;
MEMORY_PROFILE_ALLOC_INFO *AllocInfo;
MEMORY_PROFILE_CONTEXT_DATA *ContextData;
MEMORY_PROFILE_DRIVER_INFO_DATA *DriverInfoData;
MEMORY_PROFILE_ALLOC_INFO_DATA *AllocInfoData;
LIST_ENTRY *DriverInfoList;
LIST_ENTRY *DriverLink;
LIST_ENTRY *AllocInfoList;
LIST_ENTRY *AllocLink;
UINTN PdbSize;
UINTN ActionStringSize;
ContextData = GetMemoryProfileContext ();
if (ContextData == NULL) {
return;
}
Context = ProfileBuffer;
CopyMem (Context, &ContextData->Context, sizeof (MEMORY_PROFILE_CONTEXT));
DriverInfo = (MEMORY_PROFILE_DRIVER_INFO *)(Context + 1);
DriverInfoList = ContextData->DriverInfoList;
for (DriverLink = DriverInfoList->ForwardLink;
DriverLink != DriverInfoList;
DriverLink = DriverLink->ForwardLink)
{
DriverInfoData = CR (
DriverLink,
MEMORY_PROFILE_DRIVER_INFO_DATA,
Link,
MEMORY_PROFILE_DRIVER_INFO_SIGNATURE
);
CopyMem (DriverInfo, &DriverInfoData->DriverInfo, sizeof (MEMORY_PROFILE_DRIVER_INFO));
if (DriverInfo->PdbStringOffset != 0) {
PdbSize = AsciiStrSize (DriverInfoData->PdbString);
CopyMem ((VOID *)((UINTN)DriverInfo + DriverInfo->PdbStringOffset), DriverInfoData->PdbString, PdbSize);
}
AllocInfo = (MEMORY_PROFILE_ALLOC_INFO *)((UINTN)DriverInfo + DriverInfo->Header.Length);
AllocInfoList = DriverInfoData->AllocInfoList;
for (AllocLink = AllocInfoList->ForwardLink;
AllocLink != AllocInfoList;
AllocLink = AllocLink->ForwardLink)
{
AllocInfoData = CR (
AllocLink,
MEMORY_PROFILE_ALLOC_INFO_DATA,
Link,
MEMORY_PROFILE_ALLOC_INFO_SIGNATURE
);
CopyMem (AllocInfo, &AllocInfoData->AllocInfo, sizeof (MEMORY_PROFILE_ALLOC_INFO));
if (AllocInfo->ActionStringOffset != 0) {
ActionStringSize = AsciiStrSize (AllocInfoData->ActionString);
CopyMem ((VOID *)((UINTN)AllocInfo + AllocInfo->ActionStringOffset), AllocInfoData->ActionString, ActionStringSize);
}
AllocInfo = (MEMORY_PROFILE_ALLOC_INFO *)((UINTN)AllocInfo + AllocInfo->Header.Length);
}
DriverInfo = (MEMORY_PROFILE_DRIVER_INFO *)AllocInfo;
}
}
/**
Get memory profile data.
@param[in] This The EDKII_MEMORY_PROFILE_PROTOCOL instance.
@param[in, out] ProfileSize On entry, points to the size in bytes of the ProfileBuffer.
On return, points to the size of the data returned in ProfileBuffer.
@param[out] ProfileBuffer Profile buffer.
@return EFI_SUCCESS Get the memory profile data successfully.
@return EFI_UNSUPPORTED Memory profile is unsupported.
@return EFI_BUFFER_TO_SMALL The ProfileSize is too small for the resulting data.
ProfileSize is updated with the size required.
**/
EFI_STATUS
EFIAPI
ProfileProtocolGetData (
IN EDKII_MEMORY_PROFILE_PROTOCOL *This,
IN OUT UINT64 *ProfileSize,
OUT VOID *ProfileBuffer
)
{
UINTN Size;
MEMORY_PROFILE_CONTEXT_DATA *ContextData;
BOOLEAN MemoryProfileGettingStatus;
ContextData = GetMemoryProfileContext ();
if (ContextData == NULL) {
return EFI_UNSUPPORTED;
}
MemoryProfileGettingStatus = mMemoryProfileGettingStatus;
mMemoryProfileGettingStatus = TRUE;
Size = MemoryProfileGetDataSize ();
if (*ProfileSize < Size) {
*ProfileSize = Size;
mMemoryProfileGettingStatus = MemoryProfileGettingStatus;
return EFI_BUFFER_TOO_SMALL;
}
*ProfileSize = Size;
MemoryProfileCopyData (ProfileBuffer);
mMemoryProfileGettingStatus = MemoryProfileGettingStatus;
return EFI_SUCCESS;
}
/**
Register image to memory profile.
@param[in] This The EDKII_MEMORY_PROFILE_PROTOCOL instance.
@param[in] FilePath File path of the image.
@param[in] ImageBase Image base address.
@param[in] ImageSize Image size.
@param[in] FileType File type of the image.
@return EFI_SUCCESS Register successfully.
@return EFI_UNSUPPORTED Memory profile is unsupported,
or memory profile for the image is not required.
@return EFI_OUT_OF_RESOURCES No enough resource for this register.
**/
EFI_STATUS
EFIAPI
ProfileProtocolRegisterImage (
IN EDKII_MEMORY_PROFILE_PROTOCOL *This,
IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
IN PHYSICAL_ADDRESS ImageBase,
IN UINT64 ImageSize,
IN EFI_FV_FILETYPE FileType
)
{
EFI_STATUS Status;
LOADED_IMAGE_PRIVATE_DATA DriverEntry;
VOID *EntryPointInImage;
ZeroMem (&DriverEntry, sizeof (DriverEntry));
DriverEntry.Info.FilePath = FilePath;
DriverEntry.ImageContext.ImageAddress = ImageBase;
DriverEntry.ImageContext.ImageSize = ImageSize;
Status = InternalPeCoffGetEntryPoint ((VOID *)(UINTN)ImageBase, &EntryPointInImage);
ASSERT_EFI_ERROR (Status);
DriverEntry.ImageContext.EntryPoint = (PHYSICAL_ADDRESS)(UINTN)EntryPointInImage;
DriverEntry.ImageContext.ImageType = InternalPeCoffGetSubsystem ((VOID *)(UINTN)ImageBase);
return RegisterMemoryProfileImage (&DriverEntry, FileType);
}
/**
Unregister image from memory profile.
@param[in] This The EDKII_MEMORY_PROFILE_PROTOCOL instance.
@param[in] FilePath File path of the image.
@param[in] ImageBase Image base address.
@param[in] ImageSize Image size.
@return EFI_SUCCESS Unregister successfully.
@return EFI_UNSUPPORTED Memory profile is unsupported,
or memory profile for the image is not required.
@return EFI_NOT_FOUND The image is not found.
**/
EFI_STATUS
EFIAPI
ProfileProtocolUnregisterImage (
IN EDKII_MEMORY_PROFILE_PROTOCOL *This,
IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
IN PHYSICAL_ADDRESS ImageBase,
IN UINT64 ImageSize
)
{
EFI_STATUS Status;
LOADED_IMAGE_PRIVATE_DATA DriverEntry;
VOID *EntryPointInImage;
ZeroMem (&DriverEntry, sizeof (DriverEntry));
DriverEntry.Info.FilePath = FilePath;
DriverEntry.ImageContext.ImageAddress = ImageBase;
DriverEntry.ImageContext.ImageSize = ImageSize;
Status = InternalPeCoffGetEntryPoint ((VOID *)(UINTN)ImageBase, &EntryPointInImage);
ASSERT_EFI_ERROR (Status);
DriverEntry.ImageContext.EntryPoint = (PHYSICAL_ADDRESS)(UINTN)EntryPointInImage;
return UnregisterMemoryProfileImage (&DriverEntry);
}
/**
Get memory profile recording state.
@param[in] This The EDKII_MEMORY_PROFILE_PROTOCOL instance.
@param[out] RecordingState Recording state.
@return EFI_SUCCESS Memory profile recording state is returned.
@return EFI_UNSUPPORTED Memory profile is unsupported.
@return EFI_INVALID_PARAMETER RecordingState is NULL.
**/
EFI_STATUS
EFIAPI
ProfileProtocolGetRecordingState (
IN EDKII_MEMORY_PROFILE_PROTOCOL *This,
OUT BOOLEAN *RecordingState
)
{
MEMORY_PROFILE_CONTEXT_DATA *ContextData;
ContextData = GetMemoryProfileContext ();
if (ContextData == NULL) {
return EFI_UNSUPPORTED;
}
if (RecordingState == NULL) {
return EFI_INVALID_PARAMETER;
}
*RecordingState = mMemoryProfileRecordingEnable;
return EFI_SUCCESS;
}
/**
Set memory profile recording state.
@param[in] This The EDKII_MEMORY_PROFILE_PROTOCOL instance.
@param[in] RecordingState Recording state.
@return EFI_SUCCESS Set memory profile recording state successfully.
@return EFI_UNSUPPORTED Memory profile is unsupported.
**/
EFI_STATUS
EFIAPI
ProfileProtocolSetRecordingState (
IN EDKII_MEMORY_PROFILE_PROTOCOL *This,
IN BOOLEAN RecordingState
)
{
MEMORY_PROFILE_CONTEXT_DATA *ContextData;
ContextData = GetMemoryProfileContext ();
if (ContextData == NULL) {
return EFI_UNSUPPORTED;
}
mMemoryProfileRecordingEnable = RecordingState;
return EFI_SUCCESS;
}
/**
Record memory profile of multilevel caller.
@param[in] This The EDKII_MEMORY_PROFILE_PROTOCOL instance.
@param[in] CallerAddress Address of caller.
@param[in] Action Memory profile action.
@param[in] MemoryType Memory type.
EfiMaxMemoryType means the MemoryType is unknown.
@param[in] Buffer Buffer address.
@param[in] Size Buffer size.
@param[in] ActionString String for memory profile action.
Only needed for user defined allocate action.
@return EFI_SUCCESS Memory profile is updated.
@return EFI_UNSUPPORTED Memory profile is unsupported,
or memory profile for the image is not required,
or memory profile for the memory type is not required.
@return EFI_ACCESS_DENIED It is during memory profile data getting.
@return EFI_ABORTED Memory profile recording is not enabled.
@return EFI_OUT_OF_RESOURCES No enough resource to update memory profile for allocate action.
@return EFI_NOT_FOUND No matched allocate info found for free action.
**/
EFI_STATUS
EFIAPI
ProfileProtocolRecord (
IN EDKII_MEMORY_PROFILE_PROTOCOL *This,
IN PHYSICAL_ADDRESS CallerAddress,
IN MEMORY_PROFILE_ACTION Action,
IN EFI_MEMORY_TYPE MemoryType,
IN VOID *Buffer,
IN UINTN Size,
IN CHAR8 *ActionString OPTIONAL
)
{
return CoreUpdateProfile (CallerAddress, Action, MemoryType, Size, Buffer, ActionString);
}
////////////////////