| /** @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; | |
| } |