/** @file | |
The PRM Module Discovery library provides functionality to discover PRM modules installed by platform firmware. | |
Copyright (c) Microsoft Corporation | |
Copyright (c) 2020 - 2022, Intel Corporation. All rights reserved.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include <PiMm.h> | |
#include <Protocol/MmAccess.h> | |
#include <Library/BaseLib.h> | |
#include <Library/BaseMemoryLib.h> | |
#include <Library/DebugLib.h> | |
#include <Library/MemoryAllocationLib.h> | |
#include <Library/PrmModuleDiscoveryLib.h> | |
#include <Library/PrmPeCoffLib.h> | |
#include <Library/UefiBootServicesTableLib.h> | |
#include <Protocol/LoadedImage.h> | |
#include "PrmModuleDiscovery.h" | |
#define _DBGMSGID_ "[PRMMODULEDISCOVERYLIB]" | |
LIST_ENTRY mPrmModuleList; | |
/** | |
Gets the next PRM module discovered after the given PRM module. | |
@param[in,out] ModuleImageContext A pointer to a pointer to a PRM module image context structure. | |
ModuleImageContext should point to a pointer that points to NULL to | |
get the first PRM module discovered. | |
@retval EFI_SUCCESS The next PRM module was found successfully. | |
@retval EFI_INVALID_PARAMETER The given ModuleImageContext structure is invalid or the pointer is NULL. | |
@retval EFI_NOT_FOUND The next PRM module was not found. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
GetNextPrmModuleEntry ( | |
IN OUT PRM_MODULE_IMAGE_CONTEXT **ModuleImageContext | |
) | |
{ | |
LIST_ENTRY *CurrentLink; | |
LIST_ENTRY *ForwardLink; | |
PRM_MODULE_IMAGE_CONTEXT_LIST_ENTRY *CurrentListEntry; | |
PRM_MODULE_IMAGE_CONTEXT_LIST_ENTRY *ForwardListEntry; | |
DEBUG ((DEBUG_INFO, "%a %a - Entry.\n", _DBGMSGID_, __func__)); | |
if (ModuleImageContext == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (*ModuleImageContext == NULL) { | |
ForwardLink = GetFirstNode (&mPrmModuleList); | |
} else { | |
CurrentListEntry = NULL; | |
CurrentListEntry = CR (*ModuleImageContext, PRM_MODULE_IMAGE_CONTEXT_LIST_ENTRY, Context, PRM_MODULE_IMAGE_CONTEXT_LIST_ENTRY_SIGNATURE); | |
if ((CurrentListEntry == NULL) || (CurrentListEntry->Signature != PRM_MODULE_IMAGE_CONTEXT_LIST_ENTRY_SIGNATURE)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
CurrentLink = &CurrentListEntry->Link; | |
ForwardLink = GetNextNode (&mPrmModuleList, CurrentLink); | |
if (ForwardLink == &mPrmModuleList) { | |
return EFI_NOT_FOUND; | |
} | |
} | |
ForwardListEntry = BASE_CR (ForwardLink, PRM_MODULE_IMAGE_CONTEXT_LIST_ENTRY, Link); | |
if (ForwardListEntry->Signature == PRM_MODULE_IMAGE_CONTEXT_LIST_ENTRY_SIGNATURE) { | |
*ModuleImageContext = &ForwardListEntry->Context; | |
return EFI_SUCCESS; | |
} | |
return EFI_NOT_FOUND; | |
} | |
/** | |
Creates a new PRM Module Image Context linked list entry. | |
@retval PrmModuleImageContextListEntry If successful, a pointer a PRM Module Image Context linked list entry | |
otherwise, NULL is returned. | |
**/ | |
PRM_MODULE_IMAGE_CONTEXT_LIST_ENTRY * | |
CreateNewPrmModuleImageContextListEntry ( | |
VOID | |
) | |
{ | |
PRM_MODULE_IMAGE_CONTEXT_LIST_ENTRY *PrmModuleImageContextListEntry; | |
DEBUG ((DEBUG_INFO, "%a %a - Entry.\n", _DBGMSGID_, __func__)); | |
PrmModuleImageContextListEntry = AllocateZeroPool (sizeof (*PrmModuleImageContextListEntry)); | |
if (PrmModuleImageContextListEntry == NULL) { | |
return NULL; | |
} | |
DEBUG (( | |
DEBUG_INFO, | |
" %a %a: Allocated PrmModuleImageContextListEntry at 0x%x of size 0x%x bytes.\n", | |
_DBGMSGID_, | |
__func__, | |
(UINTN)PrmModuleImageContextListEntry, | |
sizeof (*PrmModuleImageContextListEntry) | |
)); | |
PrmModuleImageContextListEntry->Signature = PRM_MODULE_IMAGE_CONTEXT_LIST_ENTRY_SIGNATURE; | |
return PrmModuleImageContextListEntry; | |
} | |
/** | |
Check whether the address is within any of the MMRAM ranges. | |
@param[in] Address The address to be checked. | |
@param[in] MmramRanges Pointer to MMRAM descriptor. | |
@param[in] MmramRangeCount MMRAM range count. | |
@retval TRUE The address is in MMRAM ranges. | |
@retval FALSE The address is out of MMRAM ranges. | |
**/ | |
BOOLEAN | |
EFIAPI | |
IsAddressInMmram ( | |
IN EFI_PHYSICAL_ADDRESS Address, | |
IN EFI_MMRAM_DESCRIPTOR *MmramRanges, | |
IN UINTN MmramRangeCount | |
) | |
{ | |
UINTN Index; | |
for (Index = 0; Index < MmramRangeCount; Index++) { | |
if ((Address >= MmramRanges[Index].CpuStart) && | |
(Address < (MmramRanges[Index].CpuStart + MmramRanges[Index].PhysicalSize))) | |
{ | |
return TRUE; | |
} | |
} | |
return FALSE; | |
} | |
/** | |
Discovers all PRM Modules loaded during boot. | |
Each PRM Module discovered is placed into a linked list so the list can br processsed in the future. | |
@param[out] ModuleCount An optional pointer parameter that, if provided, is set to the number | |
of PRM modules discovered. | |
@param[out] HandlerCount An optional pointer parameter that, if provided, is set to the number | |
of PRM handlers discovered. | |
@retval EFI_SUCCESS All PRM Modules were discovered successfully. | |
@retval EFI_INVALID_PARAMETER An actual pointer parameter was passed as NULL. | |
@retval EFI_NOT_FOUND The gEfiLoadedImageProtocolGuid protocol could not be found. | |
@retval EFI_OUT_OF_RESOURCES Insufficient memory resources to allocate the new PRM Context | |
linked list nodes. | |
@retval EFI_ALREADY_STARTED The function was called previously and already discovered the PRM modules | |
loaded on this boot. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
DiscoverPrmModules ( | |
OUT UINTN *ModuleCount OPTIONAL, | |
OUT UINTN *HandlerCount OPTIONAL | |
) | |
{ | |
EFI_STATUS Status; | |
PRM_MODULE_IMAGE_CONTEXT TempPrmModuleImageContext; | |
PRM_MODULE_IMAGE_CONTEXT_LIST_ENTRY *PrmModuleImageContextListEntry; | |
EFI_LOADED_IMAGE_PROTOCOL *LoadedImageProtocol; | |
EFI_HANDLE *HandleBuffer; | |
UINTN HandleCount; | |
UINTN Index; | |
UINTN PrmHandlerCount; | |
UINTN PrmModuleCount; | |
EFI_MM_ACCESS_PROTOCOL *MmAccess; | |
UINTN Size; | |
EFI_MMRAM_DESCRIPTOR *MmramRanges; | |
UINTN MmramRangeCount; | |
DEBUG ((DEBUG_INFO, "%a %a - Entry.\n", _DBGMSGID_, __func__)); | |
PrmHandlerCount = 0; | |
PrmModuleCount = 0; | |
if (!IsListEmpty (&mPrmModuleList)) { | |
return EFI_ALREADY_STARTED; | |
} | |
Status = gBS->LocateHandleBuffer ( | |
ByProtocol, | |
&gEfiLoadedImageProtocolGuid, | |
NULL, | |
&HandleCount, | |
&HandleBuffer | |
); | |
if (EFI_ERROR (Status) && (HandleCount == 0)) { | |
DEBUG ((DEBUG_ERROR, "%a %a: No LoadedImageProtocol instances found!\n", _DBGMSGID_, __func__)); | |
return EFI_NOT_FOUND; | |
} | |
MmramRanges = NULL; | |
MmramRangeCount = 0; | |
Status = gBS->LocateProtocol ( | |
&gEfiMmAccessProtocolGuid, | |
NULL, | |
(VOID **)&MmAccess | |
); | |
if (Status == EFI_SUCCESS) { | |
// | |
// Get MMRAM range information | |
// | |
Size = 0; | |
Status = MmAccess->GetCapabilities (MmAccess, &Size, NULL); | |
if ((Status == EFI_BUFFER_TOO_SMALL) && (Size != 0)) { | |
MmramRanges = (EFI_MMRAM_DESCRIPTOR *)AllocatePool (Size); | |
if (MmramRanges != NULL) { | |
Status = MmAccess->GetCapabilities (MmAccess, &Size, MmramRanges); | |
if (Status == EFI_SUCCESS) { | |
MmramRangeCount = Size / sizeof (EFI_MMRAM_DESCRIPTOR); | |
} | |
} | |
} | |
} | |
for (Index = 0; Index < HandleCount; Index++) { | |
Status = gBS->HandleProtocol ( | |
HandleBuffer[Index], | |
&gEfiLoadedImageProtocolGuid, | |
(VOID **)&LoadedImageProtocol | |
); | |
if (EFI_ERROR (Status)) { | |
continue; | |
} | |
if (IsAddressInMmram ((EFI_PHYSICAL_ADDRESS)(UINTN)(LoadedImageProtocol->ImageBase), MmramRanges, MmramRangeCount)) { | |
continue; | |
} | |
ZeroMem (&TempPrmModuleImageContext, sizeof (TempPrmModuleImageContext)); | |
TempPrmModuleImageContext.PeCoffImageContext.Handle = LoadedImageProtocol->ImageBase; | |
TempPrmModuleImageContext.PeCoffImageContext.ImageRead = PeCoffLoaderImageReadFromMemory; | |
Status = PeCoffLoaderGetImageInfo (&TempPrmModuleImageContext.PeCoffImageContext); | |
if (EFI_ERROR (Status) || (TempPrmModuleImageContext.PeCoffImageContext.ImageError != IMAGE_ERROR_SUCCESS)) { | |
DEBUG (( | |
DEBUG_WARN, | |
"%a %a: ImageHandle 0x%016lx is not a valid PE/COFF image. It cannot be considered a PRM module.\n", | |
_DBGMSGID_, | |
__func__, | |
(EFI_PHYSICAL_ADDRESS)(UINTN)LoadedImageProtocol->ImageBase | |
)); | |
continue; | |
} | |
if (TempPrmModuleImageContext.PeCoffImageContext.IsTeImage) { | |
// A PRM Module is not allowed to be a TE image | |
continue; | |
} | |
// Attempt to find an export table in this image | |
Status = GetExportDirectoryInPeCoffImage ( | |
LoadedImageProtocol->ImageBase, | |
&TempPrmModuleImageContext.PeCoffImageContext, | |
&TempPrmModuleImageContext.ExportDirectory | |
); | |
if (EFI_ERROR (Status)) { | |
continue; | |
} | |
// Attempt to find the PRM Module Export Descriptor in the export table | |
Status = GetPrmModuleExportDescriptorTable ( | |
TempPrmModuleImageContext.ExportDirectory, | |
&TempPrmModuleImageContext.PeCoffImageContext, | |
&TempPrmModuleImageContext.ExportDescriptor | |
); | |
if (EFI_ERROR (Status) || (TempPrmModuleImageContext.ExportDescriptor == NULL)) { | |
continue; | |
} | |
// A PRM Module Export Descriptor was successfully found, this is considered a PRM Module. | |
// | |
// Create a new PRM Module image context node | |
// | |
PrmModuleImageContextListEntry = CreateNewPrmModuleImageContextListEntry (); | |
if (PrmModuleImageContextListEntry == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
CopyMem ( | |
&PrmModuleImageContextListEntry->Context, | |
&TempPrmModuleImageContext, | |
sizeof (PrmModuleImageContextListEntry->Context) | |
); | |
InsertTailList (&mPrmModuleList, &PrmModuleImageContextListEntry->Link); | |
PrmHandlerCount += TempPrmModuleImageContext.ExportDescriptor->Header.NumberPrmHandlers; | |
PrmModuleCount++; | |
DEBUG ((DEBUG_INFO, "%a %a: New PRM Module inserted into list to be processed.\n", _DBGMSGID_, __func__)); | |
} | |
if (HandlerCount != NULL) { | |
*HandlerCount = PrmHandlerCount; | |
} | |
if (ModuleCount != NULL) { | |
*ModuleCount = PrmModuleCount; | |
} | |
if (MmramRanges != NULL) { | |
FreePool (MmramRanges); | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
The destructor function for this library instance. | |
Frees global resources allocated by this library instance. | |
@param ImageHandle The firmware allocated handle for the EFI image. | |
@param SystemTable A pointer to the EFI System Table. | |
@retval EFI_SUCCESS The destructor always returns EFI_SUCCESS. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
PrmModuleDiscoveryLibDestructor ( | |
IN EFI_HANDLE ImageHandle, | |
IN EFI_SYSTEM_TABLE *SystemTable | |
) | |
{ | |
LIST_ENTRY *Link; | |
LIST_ENTRY *NextLink; | |
PRM_MODULE_IMAGE_CONTEXT_LIST_ENTRY *ListEntry; | |
if (IsListEmpty (&mPrmModuleList)) { | |
return EFI_SUCCESS; | |
} | |
Link = GetFirstNode (&mPrmModuleList); | |
while (!IsNull (&mPrmModuleList, Link)) { | |
ListEntry = CR (Link, PRM_MODULE_IMAGE_CONTEXT_LIST_ENTRY, Link, PRM_MODULE_IMAGE_CONTEXT_LIST_ENTRY_SIGNATURE); | |
NextLink = GetNextNode (&mPrmModuleList, Link); | |
RemoveEntryList (Link); | |
FreePool (ListEntry); | |
Link = NextLink; | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
The constructor function for this library instance. | |
Internally initializes data structures used later during library execution. | |
@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 | |
PrmModuleDiscoveryLibConstructor ( | |
IN EFI_HANDLE ImageHandle, | |
IN EFI_SYSTEM_TABLE *SystemTable | |
) | |
{ | |
InitializeListHead (&mPrmModuleList); | |
return EFI_SUCCESS; | |
} |