/** @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.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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;
}