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