--- /dev/null
+/** @file\r
+\r
+ This file contains the implementation for a Platform Runtime Mechanism (PRM) configuration driver.\r
+\r
+ Copyright (c) Microsoft Corporation\r
+ Copyright (c) 2020, Intel Corporation. All rights reserved.<BR>\r
+ SPDX-License-Identifier: BSD-2-Clause-Patent\r
+\r
+**/\r
+\r
+#include <Library/BaseLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/DxeServicesTableLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/UefiRuntimeServicesTableLib.h>\r
+#include <Library/UefiLib.h>\r
+\r
+#include <PiDxe.h>\r
+#include <PrmContextBuffer.h>\r
+#include <PrmDataBuffer.h>\r
+#include <PrmMmio.h>\r
+#include <Protocol/PrmConfig.h>\r
+\r
+#define _DBGMSGID_ "[PRMCONFIG]"\r
+\r
+STATIC UINTN mMaxRuntimeMmioRangeCount;\r
+STATIC UINTN mMaxStaticDataBufferCount;\r
+\r
+STATIC PRM_RUNTIME_MMIO_RANGES **mRuntimeMmioRanges;\r
+STATIC PRM_DATA_BUFFER ***mStaticDataBuffers;\r
+\r
+/**\r
+ Converts the runtime memory range physical addresses to virtual addresses.\r
+\r
+ @param[in] RuntimeMmioRanges A pointer to a PRM_RUNTIME_MMIO_RANGES buffer.\r
+\r
+**/\r
+VOID\r
+ConvertRuntimeMemoryRangeAddresses (\r
+ IN PRM_RUNTIME_MMIO_RANGES *RuntimeMmioRanges\r
+ )\r
+{\r
+ UINTN Index;\r
+\r
+ if (RuntimeMmioRanges == NULL || RuntimeMmioRanges->Count == 0) {\r
+ return;\r
+ }\r
+\r
+ for (Index = 0; Index < (UINTN) RuntimeMmioRanges->Count; Index++) {\r
+ RuntimeMmioRanges->Range[Index].VirtualBaseAddress = RuntimeMmioRanges->Range[Index].PhysicalBaseAddress;\r
+ gRT->ConvertPointer (0x0, (VOID **) &(RuntimeMmioRanges->Range[Index].VirtualBaseAddress));\r
+ }\r
+}\r
+\r
+/**\r
+ Sets the runtime memory range attributes.\r
+\r
+ The EFI_MEMORY_RUNTIME attribute is set for each PRM_RUNTIME_MMIO_RANGE present\r
+ in the buffer provided.\r
+\r
+ @param[in] RuntimeMmioRanges A pointer to a PRM_RUNTIME_MMIO_RANGES buffer.\r
+\r
+**/\r
+VOID\r
+SetRuntimeMemoryRangeAttributes (\r
+ IN PRM_RUNTIME_MMIO_RANGES *RuntimeMmioRanges\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_STATUS Status2;\r
+ UINTN Index;\r
+ EFI_GCD_MEMORY_SPACE_DESCRIPTOR Descriptor;\r
+\r
+ DEBUG ((DEBUG_INFO, "%a %a - Entry.\n", _DBGMSGID_, __FUNCTION__));\r
+\r
+ if (RuntimeMmioRanges == NULL || RuntimeMmioRanges->Count == 0) {\r
+ return;\r
+ }\r
+\r
+ for (Index = 0; Index < (UINTN) RuntimeMmioRanges->Count; Index++) {\r
+ DEBUG ((\r
+ DEBUG_INFO, " %a %a: Runtime MMIO Range [%d].\n", _DBGMSGID_, __FUNCTION__, Index));\r
+ DEBUG ((\r
+ DEBUG_INFO,\r
+ " %a %a: Physical address = 0x%016x. Length = 0x%x.\n",\r
+ _DBGMSGID_,\r
+ __FUNCTION__,\r
+ RuntimeMmioRanges->Range[Index].PhysicalBaseAddress,\r
+ RuntimeMmioRanges->Range[Index].Length\r
+ ));\r
+\r
+ // Runtime memory ranges should cover ranges on a page boundary\r
+ ASSERT ((RuntimeMmioRanges->Range[Index].PhysicalBaseAddress & EFI_PAGE_MASK) == 0);\r
+ ASSERT ((RuntimeMmioRanges->Range[Index].Length & EFI_PAGE_MASK) == 0);\r
+\r
+ Status2 = EFI_NOT_FOUND;\r
+ Status = gDS->GetMemorySpaceDescriptor (RuntimeMmioRanges->Range[Index].PhysicalBaseAddress, &Descriptor);\r
+ if (!EFI_ERROR (Status) &&\r
+ (\r
+ (Descriptor.GcdMemoryType != EfiGcdMemoryTypeMemoryMappedIo && Descriptor.GcdMemoryType != EfiGcdMemoryTypeReserved) ||\r
+ ((Descriptor.Length & EFI_PAGE_MASK) != 0)\r
+ )\r
+ ) {\r
+ Status2 = gDS->RemoveMemorySpace (\r
+ RuntimeMmioRanges->Range[Index].PhysicalBaseAddress,\r
+ Descriptor.Length\r
+ );\r
+ }\r
+\r
+ if (Status == EFI_NOT_FOUND || !EFI_ERROR (Status2)) {\r
+ Status = gDS->AddMemorySpace (\r
+ EfiGcdMemoryTypeMemoryMappedIo,\r
+ RuntimeMmioRanges->Range[Index].PhysicalBaseAddress,\r
+ (UINT64) RuntimeMmioRanges->Range[Index].Length,\r
+ EFI_MEMORY_UC | EFI_MEMORY_RUNTIME\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ Status = gDS->AllocateMemorySpace (\r
+ EfiGcdAllocateAddress,\r
+ EfiGcdMemoryTypeMemoryMappedIo,\r
+ 0,\r
+ (UINT64) RuntimeMmioRanges->Range[Index].Length,\r
+ &RuntimeMmioRanges->Range[Index].PhysicalBaseAddress,\r
+ gImageHandle,\r
+ NULL\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+ }\r
+\r
+ Status = gDS->GetMemorySpaceDescriptor (RuntimeMmioRanges->Range[Index].PhysicalBaseAddress, &Descriptor);\r
+ ASSERT_EFI_ERROR (Status);\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((\r
+ DEBUG_ERROR,\r
+ " %a %a: Error [%r] finding descriptor for runtime memory range 0x%016x.\n",\r
+ _DBGMSGID_,\r
+ __FUNCTION__,\r
+ Status,\r
+ RuntimeMmioRanges->Range[Index].PhysicalBaseAddress\r
+ ));\r
+ continue;\r
+ }\r
+ if ((Descriptor.Attributes & EFI_MEMORY_RUNTIME) != 0) {\r
+ continue;\r
+ }\r
+\r
+ Status = gDS->SetMemorySpaceAttributes (\r
+ RuntimeMmioRanges->Range[Index].PhysicalBaseAddress,\r
+ (UINT64) RuntimeMmioRanges->Range[Index].Length,\r
+ Descriptor.Attributes | EFI_MEMORY_RUNTIME\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((\r
+ DEBUG_ERROR,\r
+ " %a %a: Error [%r] setting descriptor for runtime memory range 0x%016x.\n",\r
+ _DBGMSGID_,\r
+ __FUNCTION__,\r
+ Status,\r
+ RuntimeMmioRanges->Range[Index].PhysicalBaseAddress\r
+ ));\r
+ } else {\r
+ DEBUG ((DEBUG_INFO, " %a %a: Successfully set runtime attribute for the MMIO range.\n", _DBGMSGID_, __FUNCTION__));\r
+ }\r
+ }\r
+}\r
+\r
+/**\r
+ Stores pointers or pointer to resources that should be converted in the virtual address change event.\r
+\r
+**/\r
+VOID\r
+StoreVirtualMemoryAddressChangePointers (\r
+ VOID\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINTN BufferIndex;\r
+ UINTN HandleCount;\r
+ UINTN HandleIndex;\r
+ UINTN RangeIndex;\r
+ UINTN StaticDataBufferIndex;\r
+ EFI_HANDLE *HandleBuffer;\r
+ PRM_CONFIG_PROTOCOL *PrmConfigProtocol;\r
+ PRM_CONTEXT_BUFFER *CurrentContextBuffer;\r
+\r
+ DEBUG ((DEBUG_INFO, "%a %a - Entry.\n", _DBGMSGID_, __FUNCTION__));\r
+\r
+ RangeIndex = 0;\r
+ StaticDataBufferIndex = 0;\r
+\r
+ mRuntimeMmioRanges = AllocateRuntimeZeroPool (sizeof (*mRuntimeMmioRanges) * mMaxRuntimeMmioRangeCount);\r
+ if (mRuntimeMmioRanges == NULL && mMaxRuntimeMmioRangeCount > 0) {\r
+ DEBUG ((\r
+ DEBUG_ERROR,\r
+ " %a %a: Memory allocation for runtime MMIO pointer array failed.\n",\r
+ _DBGMSGID_,\r
+ __FUNCTION__\r
+ ));\r
+ ASSERT (FALSE);\r
+ return;\r
+ }\r
+\r
+ mStaticDataBuffers = AllocateRuntimeZeroPool (sizeof (*mStaticDataBuffers) * mMaxStaticDataBufferCount);\r
+ if (mStaticDataBuffers == NULL && mMaxStaticDataBufferCount > 0) {\r
+ DEBUG ((\r
+ DEBUG_ERROR,\r
+ " %a %a: Memory allocation for PRM static data buffer pointer array failed.\n",\r
+ _DBGMSGID_,\r
+ __FUNCTION__\r
+ ));\r
+ ASSERT (FALSE);\r
+ return;\r
+ }\r
+\r
+ HandleBuffer = NULL;\r
+ Status = gBS->LocateHandleBuffer (\r
+ ByProtocol,\r
+ &gPrmConfigProtocolGuid,\r
+ NULL,\r
+ &HandleCount,\r
+ &HandleBuffer\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ for (HandleIndex = 0; HandleIndex < HandleCount; HandleIndex++) {\r
+ Status = gBS->HandleProtocol (\r
+ HandleBuffer[HandleIndex],\r
+ &gPrmConfigProtocolGuid,\r
+ (VOID **) &PrmConfigProtocol\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+ if (EFI_ERROR (Status) || PrmConfigProtocol == NULL) {\r
+ continue;\r
+ }\r
+\r
+ for (BufferIndex = 0; BufferIndex < PrmConfigProtocol->ModuleContextBuffers.BufferCount; BufferIndex++) {\r
+ CurrentContextBuffer = &(PrmConfigProtocol->ModuleContextBuffers.Buffer[BufferIndex]);\r
+\r
+ if (CurrentContextBuffer->StaticDataBuffer != NULL) {\r
+ if (StaticDataBufferIndex >= mMaxStaticDataBufferCount) {\r
+ Status = EFI_BUFFER_TOO_SMALL;\r
+ DEBUG ((\r
+ DEBUG_ERROR,\r
+ " %a %a: Index out of bounds - Actual count (%d) of PRM data buffers exceeds maximum count (%d).\n",\r
+ _DBGMSGID_,\r
+ __FUNCTION__,\r
+ StaticDataBufferIndex + 1,\r
+ mMaxStaticDataBufferCount\r
+ ));\r
+ ASSERT_EFI_ERROR (Status);\r
+ return;\r
+ }\r
+ mStaticDataBuffers[StaticDataBufferIndex++] = &CurrentContextBuffer->StaticDataBuffer;\r
+ }\r
+ }\r
+ if (PrmConfigProtocol->ModuleContextBuffers.RuntimeMmioRanges != NULL) {\r
+ if (RangeIndex >= mMaxRuntimeMmioRangeCount) {\r
+ Status = EFI_BUFFER_TOO_SMALL;\r
+ DEBUG ((\r
+ DEBUG_ERROR,\r
+ " %a %a: Index out of bounds - Actual count (%d) of runtime MMIO ranges exceeds maximum count (%d).\n",\r
+ _DBGMSGID_,\r
+ __FUNCTION__,\r
+ RangeIndex + 1,\r
+ mMaxRuntimeMmioRangeCount\r
+ ));\r
+ ASSERT_EFI_ERROR (Status);\r
+ return;\r
+ }\r
+ mRuntimeMmioRanges[RangeIndex++] = PrmConfigProtocol->ModuleContextBuffers.RuntimeMmioRanges;\r
+ }\r
+ }\r
+ DEBUG ((\r
+ DEBUG_INFO,\r
+ " %a %a: %d MMIO ranges buffers saved for future virtual memory conversion.\n",\r
+ _DBGMSGID_,\r
+ __FUNCTION__,\r
+ RangeIndex\r
+ ));\r
+ DEBUG ((\r
+ DEBUG_INFO,\r
+ " %a %a: %d static buffers saved for future virtual memory conversion.\n",\r
+ _DBGMSGID_,\r
+ __FUNCTION__,\r
+ StaticDataBufferIndex\r
+ ));\r
+ }\r
+}\r
+\r
+/**\r
+ Validates a data buffer for a PRM module.\r
+\r
+ Verifies the buffer header signature is valid and the length meets the minimum size.\r
+\r
+ @param[in] PrmDataBuffer A pointer to the data buffer for this PRM module.\r
+\r
+ @retval EFI_SUCCESS The data buffer was validated successfully.\r
+ @retval EFI_INVALID_PARAMETER The pointer given for PrmDataBuffer is NULL.\r
+ @retval EFI_NOT_FOUND The data buffer signature is not valid.\r
+ @retval EFI_BUFFER_TOO_SMALL The buffer size is too small.\r
+\r
+**/\r
+EFI_STATUS\r
+ValidatePrmDataBuffer (\r
+ IN CONST PRM_DATA_BUFFER *PrmDataBuffer\r
+ )\r
+{\r
+ if (PrmDataBuffer == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (PrmDataBuffer->Header.Signature != PRM_DATA_BUFFER_HEADER_SIGNATURE) {\r
+ DEBUG ((DEBUG_ERROR, " %a %a: The PRM data buffer signature is invalid. PRM module.\n", _DBGMSGID_, __FUNCTION__));\r
+ return EFI_NOT_FOUND;\r
+ }\r
+ if (PrmDataBuffer->Header.Length < sizeof (PRM_DATA_BUFFER_HEADER)) {\r
+ DEBUG ((DEBUG_ERROR, " %a %a: The PRM data buffer length is invalid.\n", _DBGMSGID_, __FUNCTION__));\r
+ return EFI_BUFFER_TOO_SMALL;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Validates a PRM context buffer.\r
+\r
+ Verifies the buffer header signature is valid and the GUID is set to a non-zero value.\r
+\r
+ @param[in] PrmContextBuffer A pointer to the context buffer for this PRM handler.\r
+\r
+ @retval EFI_SUCCESS The context buffer was validated successfully.\r
+ @retval EFI_INVALID_PARAMETER The pointer given for ContextBuffer is NULL.\r
+ @retval EFI_NOT_FOUND The proper value for a field was not found.\r
+\r
+**/\r
+EFI_STATUS\r
+ValidatePrmContextBuffer (\r
+ IN CONST PRM_CONTEXT_BUFFER *PrmContextBuffer\r
+ )\r
+{\r
+ if (PrmContextBuffer == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (PrmContextBuffer->Signature != PRM_CONTEXT_BUFFER_SIGNATURE) {\r
+ DEBUG ((DEBUG_ERROR, " %a %a: The PRM context buffer signature is invalid.\n", _DBGMSGID_, __FUNCTION__));\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ if (IsZeroGuid (&PrmContextBuffer->HandlerGuid)) {\r
+ DEBUG ((DEBUG_ERROR, " %a %a: The PRM context buffer GUID is zero.\n", _DBGMSGID_, __FUNCTION__));\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ if (PrmContextBuffer->StaticDataBuffer != NULL && EFI_ERROR (ValidatePrmDataBuffer (PrmContextBuffer->StaticDataBuffer))) {\r
+ DEBUG ((\r
+ DEBUG_ERROR,\r
+ " %a %a: Error in static buffer for PRM handler %g.\n",\r
+ _DBGMSGID_,\r
+ __FUNCTION__,\r
+ &PrmContextBuffer->HandlerGuid\r
+ ));\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Notification function of EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE.\r
+\r
+ This is notification function converts any registered PRM_RUNTIME_MMIO_RANGE\r
+ addresses to a virtual address.\r
+\r
+ @param[in] Event Event whose notification function is being invoked.\r
+ @param[in] Context Pointer to the notification function's context.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+PrmConfigVirtualAddressChangeEvent (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ UINTN Index;\r
+\r
+ //\r
+ // Convert static data buffer pointers\r
+ //\r
+ for (Index = 0; Index < mMaxStaticDataBufferCount; Index++) {\r
+ gRT->ConvertPointer (0x0, (VOID **) mStaticDataBuffers[Index]);\r
+ }\r
+\r
+ //\r
+ // Convert runtime MMIO ranges\r
+ //\r
+ for (Index = 0; Index < mMaxRuntimeMmioRangeCount; Index++) {\r
+ ConvertRuntimeMemoryRangeAddresses (mRuntimeMmioRanges[Index]);\r
+ }\r
+}\r
+\r
+/**\r
+ The PRM Config END_OF_DXE protocol notification event handler.\r
+\r
+ Finds all of the PRM_CONFIG_PROTOCOL instances installed at end of DXE and\r
+ marks all PRM_RUNTIME_MMIO_RANGE entries as EFI_MEMORY_RUNTIME.\r
+\r
+ @param[in] Event Event whose notification function is being invoked.\r
+ @param[in] Context The pointer to the notification function's context,\r
+ which is implementation-dependent.\r
+\r
+ @retval EFI_SUCCESS The function executed successfully\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+PrmConfigEndOfDxeNotification (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINTN HandleCount;\r
+ UINTN BufferIndex;\r
+ UINTN HandleIndex;\r
+ EFI_HANDLE *HandleBuffer;\r
+ PRM_CONTEXT_BUFFER *CurrentContextBuffer;\r
+ PRM_CONFIG_PROTOCOL *PrmConfigProtocol;\r
+\r
+ DEBUG ((DEBUG_INFO, "%a %a - Entry.\n", _DBGMSGID_, __FUNCTION__));\r
+\r
+ HandleBuffer = NULL;\r
+ Status = gBS->LocateHandleBuffer (\r
+ ByProtocol,\r
+ &gPrmConfigProtocolGuid,\r
+ NULL,\r
+ &HandleCount,\r
+ &HandleBuffer\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ for (HandleIndex = 0; HandleIndex < HandleCount; HandleIndex++) {\r
+ Status = gBS->HandleProtocol (\r
+ HandleBuffer[HandleIndex],\r
+ &gPrmConfigProtocolGuid,\r
+ (VOID **) &PrmConfigProtocol\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+ if (EFI_ERROR (Status) || PrmConfigProtocol == NULL) {\r
+ continue;\r
+ }\r
+\r
+ DEBUG ((\r
+ DEBUG_INFO,\r
+ " %a %a: Found PRM configuration protocol for PRM module %g.\n",\r
+ _DBGMSGID_,\r
+ __FUNCTION__,\r
+ &PrmConfigProtocol->ModuleContextBuffers.ModuleGuid\r
+ ));\r
+\r
+ DEBUG ((DEBUG_INFO, " %a %a: Validating module context buffers...\n", _DBGMSGID_, __FUNCTION__));\r
+ for (BufferIndex = 0; BufferIndex < PrmConfigProtocol->ModuleContextBuffers.BufferCount; BufferIndex++) {\r
+ CurrentContextBuffer = &(PrmConfigProtocol->ModuleContextBuffers.Buffer[BufferIndex]);\r
+\r
+ Status = ValidatePrmContextBuffer (CurrentContextBuffer);\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((\r
+ DEBUG_ERROR,\r
+ " %a %a: Context buffer validation failed for PRM handler %g.\n",\r
+ _DBGMSGID_,\r
+ __FUNCTION__,\r
+ CurrentContextBuffer->HandlerGuid\r
+ ));\r
+ }\r
+ if (CurrentContextBuffer->StaticDataBuffer != NULL) {\r
+ mMaxStaticDataBufferCount++;\r
+ }\r
+ }\r
+ DEBUG ((DEBUG_INFO, " %a %a: Module context buffer validation complete.\n", _DBGMSGID_, __FUNCTION__));\r
+\r
+ if (PrmConfigProtocol->ModuleContextBuffers.RuntimeMmioRanges != NULL) {\r
+ DEBUG ((\r
+ DEBUG_INFO,\r
+ " %a %a: Found %d PRM runtime MMIO ranges to convert.\n",\r
+ _DBGMSGID_,\r
+ __FUNCTION__,\r
+ PrmConfigProtocol->ModuleContextBuffers.RuntimeMmioRanges->Count\r
+ ));\r
+ SetRuntimeMemoryRangeAttributes (PrmConfigProtocol->ModuleContextBuffers.RuntimeMmioRanges);\r
+ mMaxRuntimeMmioRangeCount++;\r
+ }\r
+ }\r
+\r
+ StoreVirtualMemoryAddressChangePointers ();\r
+ }\r
+\r
+ if (HandleBuffer != NULL) {\r
+ gBS->FreePool (HandleBuffer);\r
+ }\r
+ gBS->CloseEvent(Event);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ The entry point for this module.\r
+\r
+ @param[in] ImageHandle The firmware allocated handle for the EFI image.\r
+ @param[in] SystemTable A pointer to the EFI System Table.\r
+\r
+ @retval EFI_SUCCESS The entry point is executed successfully.\r
+ @retval Others An error occurred when executing this entry point.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+PrmConfigEntryPoint (\r
+ IN EFI_HANDLE ImageHandle,\r
+ IN EFI_SYSTEM_TABLE *SystemTable\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_EVENT Event;\r
+\r
+ DEBUG ((DEBUG_INFO, "%a %a - Entry.\n", _DBGMSGID_, __FUNCTION__));\r
+\r
+ //\r
+ // Register a notification function to change memory attributes at end of DXE\r
+ //\r
+ Event = NULL;\r
+ Status = gBS->CreateEventEx(\r
+ EVT_NOTIFY_SIGNAL,\r
+ TPL_CALLBACK,\r
+ PrmConfigEndOfDxeNotification,\r
+ NULL,\r
+ &gEfiEndOfDxeEventGroupGuid,\r
+ &Event\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ //\r
+ // Register a notification function for virtual address change\r
+ //\r
+ Event = NULL;\r
+ Status = gBS->CreateEventEx (\r
+ EVT_NOTIFY_SIGNAL,\r
+ TPL_NOTIFY,\r
+ PrmConfigVirtualAddressChangeEvent,\r
+ NULL,\r
+ &gEfiEventVirtualAddressChangeGuid,\r
+ &Event\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
--- /dev/null
+/** @file\r
+\r
+ This file contains the implementation for a Platform Runtime Mechanism (PRM)\r
+ loader driver.\r
+\r
+ Copyright (c) Microsoft Corporation\r
+ SPDX-License-Identifier: BSD-2-Clause-Patent\r
+\r
+**/\r
+\r
+#include "PrmAcpiTable.h"\r
+#include "PrmLoader.h"\r
+\r
+#include <IndustryStandard/Acpi.h>\r
+#include <Library/BaseLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/PrmContextBufferLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/UefiLib.h>\r
+#include <Protocol/AcpiTable.h>\r
+#include <Protocol/LoadedImage.h>\r
+#include <Protocol/PrmConfig.h>\r
+\r
+#include <PrmContextBuffer.h>\r
+#include <PrmMmio.h>\r
+#include <PrmModuleUpdate.h>\r
+\r
+LIST_ENTRY mPrmModuleList;\r
+\r
+// Todo: Potentially refactor mPrmHandlerCount and mPrmModuleCount into localized structures\r
+// in the future.\r
+UINT32 mPrmHandlerCount;\r
+UINT32 mPrmModuleCount;\r
+\r
+/**\r
+ Gets a pointer to the export directory in a given PE/COFF image.\r
+\r
+ @param[in] ImageExportDirectory A pointer to an export directory table in a PE/COFF image.\r
+ @param[in] PeCoffLoaderImageContext A pointer to a PE_COFF_LOADER_IMAGE_CONTEXT structure that contains the\r
+ PE/COFF image context for the Image containing the PRM Module Export\r
+ Descriptor table.\r
+ @param[out] ExportDescriptor A pointer to a pointer to the PRM Module Export Descriptor table found\r
+ in the ImageExportDirectory given.\r
+\r
+ @retval EFI_SUCCESS The PRM Module Export Descriptor table was found successfully.\r
+ @retval EFI_INVALID_PARAMETER A required parameter is NULL.\r
+ @retval EFI_NOT_FOUND The PRM Module Export Descriptor table was not found in the given\r
+ ImageExportDirectory.\r
+\r
+**/\r
+EFI_STATUS\r
+GetPrmModuleExportDescriptorTable (\r
+ IN EFI_IMAGE_EXPORT_DIRECTORY *ImageExportDirectory,\r
+ IN PE_COFF_LOADER_IMAGE_CONTEXT *PeCoffLoaderImageContext,\r
+ OUT PRM_MODULE_EXPORT_DESCRIPTOR_STRUCT **ExportDescriptor\r
+ )\r
+{\r
+ UINTN Index;\r
+ EFI_PHYSICAL_ADDRESS CurrentImageAddress;\r
+ UINT16 PrmModuleExportDescriptorOrdinal;\r
+ CONST CHAR8 *CurrentExportName;\r
+ UINT16 *OrdinalTable;\r
+ UINT32 *ExportNamePointerTable;\r
+ UINT32 *ExportAddressTable;\r
+ PRM_MODULE_EXPORT_DESCRIPTOR_STRUCT *TempExportDescriptor;\r
+\r
+ DEBUG ((DEBUG_INFO, "%a %a - Entry.\n", _DBGMSGID_, __FUNCTION__));\r
+\r
+ *ExportDescriptor = NULL;\r
+\r
+ if (ImageExportDirectory == NULL ||\r
+ PeCoffLoaderImageContext == NULL ||\r
+ PeCoffLoaderImageContext->ImageAddress == 0 ||\r
+ ExportDescriptor == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ DEBUG ((\r
+ DEBUG_INFO,\r
+ " %a %a: %d exported names found in this image.\n",\r
+ _DBGMSGID_,\r
+ __FUNCTION__,\r
+ ImageExportDirectory->NumberOfNames\r
+ ));\r
+\r
+ //\r
+ // The export name pointer table and export ordinal table form two parallel arrays associated by index.\r
+ //\r
+ CurrentImageAddress = PeCoffLoaderImageContext->ImageAddress;\r
+ ExportAddressTable = (UINT32 *) ((UINTN) CurrentImageAddress + ImageExportDirectory->AddressOfFunctions);\r
+ ExportNamePointerTable = (UINT32 *) ((UINTN) CurrentImageAddress + ImageExportDirectory->AddressOfNames);\r
+ OrdinalTable = (UINT16 *) ((UINTN) CurrentImageAddress + ImageExportDirectory->AddressOfNameOrdinals);\r
+\r
+ for (Index = 0; Index < ImageExportDirectory->NumberOfNames; Index++) {\r
+ CurrentExportName = (CONST CHAR8 *) ((UINTN) CurrentImageAddress + ExportNamePointerTable[Index]);\r
+ DEBUG ((\r
+ DEBUG_INFO,\r
+ " %a %a: Export Name[0x%x] - %a.\n",\r
+ _DBGMSGID_,\r
+ __FUNCTION__,\r
+ Index,\r
+ CurrentExportName\r
+ ));\r
+ if (\r
+ AsciiStrnCmp (\r
+ PRM_STRING(PRM_MODULE_EXPORT_DESCRIPTOR_NAME),\r
+ CurrentExportName,\r
+ AsciiStrLen (PRM_STRING(PRM_MODULE_EXPORT_DESCRIPTOR_NAME))\r
+ ) == 0) {\r
+ PrmModuleExportDescriptorOrdinal = OrdinalTable[Index];\r
+ DEBUG ((\r
+ DEBUG_INFO,\r
+ " %a %a: PRM Module Export Descriptor found. Ordinal = %d.\n",\r
+ _DBGMSGID_,\r
+ __FUNCTION__,\r
+ PrmModuleExportDescriptorOrdinal\r
+ ));\r
+ if (PrmModuleExportDescriptorOrdinal >= ImageExportDirectory->NumberOfFunctions) {\r
+ DEBUG ((DEBUG_ERROR, "%a %a: The PRM Module Export Descriptor ordinal value is invalid.\n", _DBGMSGID_, __FUNCTION__));\r
+ return EFI_NOT_FOUND;\r
+ }\r
+ TempExportDescriptor = (PRM_MODULE_EXPORT_DESCRIPTOR_STRUCT *) ((UINTN) CurrentImageAddress + ExportAddressTable[PrmModuleExportDescriptorOrdinal]);\r
+ if (TempExportDescriptor->Signature == PRM_MODULE_EXPORT_DESCRIPTOR_SIGNATURE) {\r
+ *ExportDescriptor = TempExportDescriptor;\r
+ DEBUG ((DEBUG_INFO, " %a %a: PRM Module Export Descriptor found at 0x%x.\n", _DBGMSGID_, __FUNCTION__, (UINTN) ExportDescriptor));\r
+ } else {\r
+ DEBUG ((\r
+ DEBUG_INFO,\r
+ " %a %a: PRM Module Export Descriptor found at 0x%x but signature check failed.\n",\r
+ _DBGMSGID_,\r
+ __FUNCTION__,\r
+ (UINTN) TempExportDescriptor\r
+ ));\r
+ }\r
+ DEBUG ((DEBUG_INFO, " %a %a: Exiting export iteration since export descriptor found.\n", _DBGMSGID_, __FUNCTION__));\r
+ return EFI_SUCCESS;\r
+ }\r
+ }\r
+\r
+ return EFI_NOT_FOUND;\r
+}\r
+\r
+/**\r
+ Gets a pointer to the export directory in a given PE/COFF image.\r
+\r
+ @param[in] Image A pointer to a PE32/COFF image base address that is loaded into memory\r
+ and already relocated to the memory base address. RVAs in the image given\r
+ should be valid.\r
+ @param[in] PeCoffLoaderImageContext A pointer to a PE_COFF_LOADER_IMAGE_CONTEXT structure that contains the\r
+ PE/COFF image context for the Image given.\r
+ @param[out] ImageExportDirectory A pointer to a pointer to the export directory found in the Image given.\r
+\r
+ @retval EFI_SUCCESS The export directory was found successfully.\r
+ @retval EFI_INVALID_PARAMETER A required parameter is NULL.\r
+ @retval EFI_UNSUPPORTED The PE/COFF image given is not supported as a PRM Module.\r
+ @retval EFI_NOT_FOUND The image export directory could not be found for this image.\r
+\r
+**/\r
+EFI_STATUS\r
+GetExportDirectoryInPeCoffImage (\r
+ IN VOID *Image,\r
+ IN PE_COFF_LOADER_IMAGE_CONTEXT *PeCoffLoaderImageContext,\r
+ OUT EFI_IMAGE_EXPORT_DIRECTORY **ImageExportDirectory\r
+ )\r
+{\r
+ UINT16 Magic;\r
+ UINT32 NumberOfRvaAndSizes;\r
+ EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION OptionalHeaderPtrUnion;\r
+ EFI_IMAGE_DATA_DIRECTORY *DirectoryEntry;\r
+ EFI_IMAGE_EXPORT_DIRECTORY *ExportDirectory;\r
+ EFI_IMAGE_SECTION_HEADER *SectionHeader;\r
+\r
+ if (Image == NULL || PeCoffLoaderImageContext == NULL || ImageExportDirectory == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ DirectoryEntry = NULL;\r
+ ExportDirectory = NULL;\r
+\r
+ //\r
+ // NOTE: For backward compatibility, use the Machine field to identify a PE32/PE32+\r
+ // image instead of using the Magic field. Some systems might generate a PE32+\r
+ // image with PE32 magic.\r
+ //\r
+ switch (PeCoffLoaderImageContext->Machine) {\r
+ case EFI_IMAGE_MACHINE_IA32:\r
+ // Todo: Add EFI_IMAGE_MACHINE_ARMT\r
+ //\r
+ // Assume PE32 image with IA32 Machine field.\r
+ //\r
+ Magic = EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC;\r
+ break;\r
+ case EFI_IMAGE_MACHINE_X64:\r
+ //\r
+ // Assume PE32+ image with X64 Machine field\r
+ //\r
+ Magic = EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC;\r
+ break;\r
+ default:\r
+ //\r
+ // For unknown Machine field, use Magic in optional header\r
+ //\r
+ DEBUG ((\r
+ DEBUG_WARN,\r
+ "%a %a: The machine type for this image is not valid for a PRM module.\n",\r
+ _DBGMSGID_,\r
+ __FUNCTION__\r
+ ));\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ OptionalHeaderPtrUnion.Pe32 = (EFI_IMAGE_NT_HEADERS32 *) (\r
+ (UINTN) Image +\r
+ PeCoffLoaderImageContext->PeCoffHeaderOffset\r
+ );\r
+\r
+ //\r
+ // Check the PE/COFF Header Signature. Determine if the image is valid and/or a TE image.\r
+ //\r
+ if (OptionalHeaderPtrUnion.Pe32->Signature != EFI_IMAGE_NT_SIGNATURE) {\r
+ DEBUG ((DEBUG_ERROR, "%a %a: The PE signature is not valid for the current image.\n", _DBGMSGID_, __FUNCTION__));\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ SectionHeader = (EFI_IMAGE_SECTION_HEADER *) (\r
+ (UINTN) Image +\r
+ PeCoffLoaderImageContext->PeCoffHeaderOffset +\r
+ sizeof (UINT32) +\r
+ sizeof (EFI_IMAGE_FILE_HEADER) +\r
+ PeCoffLoaderImageContext->SizeOfHeaders\r
+ );\r
+ if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {\r
+ //\r
+ // Use the PE32 offset to get the Export Directory Entry\r
+ //\r
+ NumberOfRvaAndSizes = OptionalHeaderPtrUnion.Pe32->OptionalHeader.NumberOfRvaAndSizes;\r
+ DirectoryEntry = (EFI_IMAGE_DATA_DIRECTORY *) &(OptionalHeaderPtrUnion.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_EXPORT]);\r
+ } else if (OptionalHeaderPtrUnion.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) {\r
+ //\r
+ // Use the PE32+ offset get the Export Directory Entry\r
+ //\r
+ NumberOfRvaAndSizes = OptionalHeaderPtrUnion.Pe32Plus->OptionalHeader.NumberOfRvaAndSizes;\r
+ DirectoryEntry = (EFI_IMAGE_DATA_DIRECTORY *) &(OptionalHeaderPtrUnion.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_EXPORT]);\r
+ } else {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ if (NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY_EXPORT || DirectoryEntry->VirtualAddress == 0) {\r
+ //\r
+ // The export directory is not present\r
+ //\r
+ return EFI_NOT_FOUND;\r
+ } else if (((UINT32) (~0) - DirectoryEntry->VirtualAddress) < DirectoryEntry->Size) {\r
+ //\r
+ // The directory address overflows\r
+ //\r
+ DEBUG ((DEBUG_ERROR, "%a %a: The export directory entry in this image results in overflow.\n", _DBGMSGID_, __FUNCTION__));\r
+ return EFI_UNSUPPORTED;\r
+ } else {\r
+ DEBUG ((DEBUG_INFO, "%a %a: Export Directory Entry found in the image at 0x%x.\n", _DBGMSGID_, __FUNCTION__, (UINTN) OptionalHeaderPtrUnion.Pe32));\r
+ DEBUG ((DEBUG_INFO, " %a %a: Directory Entry Virtual Address = 0x%x.\n", _DBGMSGID_, __FUNCTION__, DirectoryEntry->VirtualAddress));\r
+\r
+ ExportDirectory = (EFI_IMAGE_EXPORT_DIRECTORY *) ((UINTN) Image + DirectoryEntry->VirtualAddress);\r
+ DEBUG ((\r
+ DEBUG_INFO,\r
+ " %a %a: Export Directory Table found successfully at 0x%x. Name address = 0x%x. Name = %a.\n",\r
+ _DBGMSGID_,\r
+ __FUNCTION__,\r
+ (UINTN) ExportDirectory,\r
+ ((UINTN) Image + ExportDirectory->Name),\r
+ (CHAR8 *) ((UINTN) Image + ExportDirectory->Name)\r
+ ));\r
+ }\r
+ *ImageExportDirectory = ExportDirectory;\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Returns the image major and image minor version in a given PE/COFF image.\r
+\r
+ @param[in] Image A pointer to a PE32/COFF image base address that is loaded into memory\r
+ and already relocated to the memory base address. RVAs in the image given\r
+ should be valid.\r
+ @param[in] PeCoffLoaderImageContext A pointer to a PE_COFF_LOADER_IMAGE_CONTEXT structure that contains the\r
+ PE/COFF image context for the Image given.\r
+ @param[out] ImageMajorVersion A pointer to a UINT16 buffer to hold the image major version.\r
+ @param[out] ImageMinorVersion A pointer to a UINT16 buffer to hold the image minor version.\r
+\r
+ @retval EFI_SUCCESS The image version was read successfully.\r
+ @retval EFI_INVALID_PARAMETER A required parameter is NULL.\r
+ @retval EFI_UNSUPPORTED The PE/COFF image given is not supported.\r
+\r
+**/\r
+EFI_STATUS\r
+GetImageVersionInPeCoffImage (\r
+ IN VOID *Image,\r
+ IN PE_COFF_LOADER_IMAGE_CONTEXT *PeCoffLoaderImageContext,\r
+ OUT UINT16 *ImageMajorVersion,\r
+ OUT UINT16 *ImageMinorVersion\r
+ )\r
+{\r
+ EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION OptionalHeaderPtrUnion;\r
+ UINT16 Magic;\r
+\r
+ DEBUG ((DEBUG_INFO, " %a %a - Entry.\n", _DBGMSGID_, __FUNCTION__));\r
+\r
+ if (Image == NULL || PeCoffLoaderImageContext == NULL || ImageMajorVersion == NULL || ImageMinorVersion == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // NOTE: For backward compatibility, use the Machine field to identify a PE32/PE32+\r
+ // image instead of using the Magic field. Some systems might generate a PE32+\r
+ // image with PE32 magic.\r
+ //\r
+ switch (PeCoffLoaderImageContext->Machine) {\r
+ case EFI_IMAGE_MACHINE_IA32:\r
+ // Todo: Add EFI_IMAGE_MACHINE_ARMT\r
+ //\r
+ // Assume PE32 image with IA32 Machine field.\r
+ //\r
+ Magic = EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC;\r
+ break;\r
+ case EFI_IMAGE_MACHINE_X64:\r
+ //\r
+ // Assume PE32+ image with X64 Machine field\r
+ //\r
+ Magic = EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC;\r
+ break;\r
+ default:\r
+ //\r
+ // For unknown Machine field, use Magic in optional header\r
+ //\r
+ DEBUG ((\r
+ DEBUG_WARN,\r
+ "%a %a: The machine type for this image is not valid for a PRM module.\n",\r
+ _DBGMSGID_,\r
+ __FUNCTION__\r
+ ));\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ OptionalHeaderPtrUnion.Pe32 = (EFI_IMAGE_NT_HEADERS32 *) (\r
+ (UINTN) Image +\r
+ PeCoffLoaderImageContext->PeCoffHeaderOffset\r
+ );\r
+ //\r
+ // Check the PE/COFF Header Signature. Determine if the image is valid and/or a TE image.\r
+ //\r
+ if (OptionalHeaderPtrUnion.Pe32->Signature != EFI_IMAGE_NT_SIGNATURE) {\r
+ DEBUG ((DEBUG_ERROR, "%a %a: The PE signature is not valid for the current image.\n", _DBGMSGID_, __FUNCTION__));\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {\r
+ //\r
+ // Use the PE32 offset to get the Export Directory Entry\r
+ //\r
+ *ImageMajorVersion = OptionalHeaderPtrUnion.Pe32->OptionalHeader.MajorImageVersion;\r
+ *ImageMinorVersion = OptionalHeaderPtrUnion.Pe32->OptionalHeader.MinorImageVersion;\r
+ } else {\r
+ //\r
+ // Use the PE32+ offset to get the Export Directory Entry\r
+ //\r
+ *ImageMajorVersion = OptionalHeaderPtrUnion.Pe32Plus->OptionalHeader.MajorImageVersion;\r
+ *ImageMinorVersion = OptionalHeaderPtrUnion.Pe32Plus->OptionalHeader.MinorImageVersion;\r
+ }\r
+\r
+ DEBUG ((DEBUG_INFO, " %a %a - Image Major Version: 0x%02x.\n", _DBGMSGID_, __FUNCTION__, *ImageMajorVersion));\r
+ DEBUG ((DEBUG_INFO, " %a %a - Image Minor Version: 0x%02x.\n", _DBGMSGID_, __FUNCTION__, *ImageMinorVersion));\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Creates a new PRM Module Image Context linked list entry.\r
+\r
+ @retval PrmModuleImageContextListEntry If successful, a pointer a PRM Module Image Context linked list entry\r
+ otherwise, NULL is returned.\r
+\r
+**/\r
+STATIC\r
+PRM_MODULE_IMAGE_CONTEXT_LIST_ENTRY *\r
+CreateNewPrmModuleImageContextListEntry (\r
+ VOID\r
+ )\r
+{\r
+ PRM_MODULE_IMAGE_CONTEXT *PrmModuleImageContext;\r
+ PRM_MODULE_IMAGE_CONTEXT_LIST_ENTRY *PrmModuleImageContextListEntry;\r
+\r
+ DEBUG ((DEBUG_INFO, "%a %a - Entry.\n", _DBGMSGID_, __FUNCTION__));\r
+\r
+ PrmModuleImageContext = AllocateZeroPool (sizeof (*PrmModuleImageContext));\r
+ if (PrmModuleImageContext == NULL) {\r
+ return NULL;\r
+ }\r
+ DEBUG ((\r
+ DEBUG_INFO,\r
+ " %a %a: Allocated PrmModuleImageContext at 0x%x of size 0x%x bytes.\n",\r
+ _DBGMSGID_,\r
+ __FUNCTION__,\r
+ (UINTN) PrmModuleImageContext,\r
+ sizeof (*PrmModuleImageContext)\r
+ ));\r
+\r
+ PrmModuleImageContextListEntry = AllocateZeroPool (sizeof (*PrmModuleImageContextListEntry));\r
+ if (PrmModuleImageContextListEntry == NULL) {\r
+ FreePool (PrmModuleImageContext);\r
+ return NULL;\r
+ }\r
+ DEBUG ((\r
+ DEBUG_INFO,\r
+ " %a %a: Allocated PrmModuleImageContextListEntry at 0x%x of size 0x%x bytes.\n",\r
+ _DBGMSGID_,\r
+ __FUNCTION__,\r
+ (UINTN) PrmModuleImageContextListEntry,\r
+ sizeof (*PrmModuleImageContextListEntry)\r
+ ));\r
+\r
+ PrmModuleImageContextListEntry->Signature = PRM_MODULE_IMAGE_CONTEXT_LIST_ENTRY_SIGNATURE;\r
+ PrmModuleImageContextListEntry->Context = PrmModuleImageContext;\r
+\r
+ return PrmModuleImageContextListEntry;\r
+}\r
+\r
+/**\r
+ Discovers all PRM Modules loaded during the DXE boot phase.\r
+\r
+ Each PRM Module discovered is placed into a linked list so the list can br processsed in the future.\r
+\r
+ @retval EFI_SUCCESS All PRM Modules were discovered successfully.\r
+ @retval EFI_NOT_FOUND The gEfiLoadedImageProtocolGuid protocol could not be found.\r
+ @retval EFI_OUT_OF_RESOURCES Insufficient memory resources to allocate the new PRM Context\r
+ linked list nodes.\r
+\r
+**/\r
+EFI_STATUS\r
+DiscoverPrmModules (\r
+ VOID\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ PRM_MODULE_IMAGE_CONTEXT TempPrmModuleImageContext;\r
+ PRM_MODULE_IMAGE_CONTEXT_LIST_ENTRY *PrmModuleImageContextListEntry;\r
+ EFI_LOADED_IMAGE_PROTOCOL *LoadedImageProtocol;\r
+ EFI_HANDLE *HandleBuffer;\r
+ UINTN HandleCount;\r
+ UINTN Index;\r
+\r
+ DEBUG ((DEBUG_INFO, "%a %a - Entry.\n", _DBGMSGID_, __FUNCTION__));\r
+\r
+ Status = gBS->LocateHandleBuffer (\r
+ ByProtocol,\r
+ &gEfiLoadedImageProtocolGuid,\r
+ NULL,\r
+ &HandleCount,\r
+ &HandleBuffer\r
+ );\r
+ if (EFI_ERROR (Status) && (HandleCount == 0)) {\r
+ DEBUG ((DEBUG_ERROR, "%a %a: No LoadedImageProtocol instances found!\n", _DBGMSGID_, __FUNCTION__));\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ for (Index = 0; Index < HandleCount; Index++) {\r
+ Status = gBS->HandleProtocol (\r
+ HandleBuffer[Index],\r
+ &gEfiLoadedImageProtocolGuid,\r
+ (VOID **) &LoadedImageProtocol\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ continue;\r
+ }\r
+\r
+ ZeroMem (&TempPrmModuleImageContext, sizeof (TempPrmModuleImageContext));\r
+ TempPrmModuleImageContext.PeCoffImageContext.Handle = LoadedImageProtocol->ImageBase;\r
+ TempPrmModuleImageContext.PeCoffImageContext.ImageRead = PeCoffLoaderImageReadFromMemory;\r
+\r
+ Status = PeCoffLoaderGetImageInfo (&TempPrmModuleImageContext.PeCoffImageContext);\r
+ if (EFI_ERROR (Status) || TempPrmModuleImageContext.PeCoffImageContext.ImageError != IMAGE_ERROR_SUCCESS) {\r
+ DEBUG ((\r
+ DEBUG_WARN,\r
+ "%a %a: ImageHandle 0x%016lx is not a valid PE/COFF image. It cannot be considered a PRM module.\n",\r
+ _DBGMSGID_,\r
+ __FUNCTION__,\r
+ (EFI_PHYSICAL_ADDRESS) (UINTN) LoadedImageProtocol->ImageBase\r
+ ));\r
+ continue;\r
+ }\r
+ if (TempPrmModuleImageContext.PeCoffImageContext.IsTeImage) {\r
+ // A PRM Module is not allowed to be a TE image\r
+ continue;\r
+ }\r
+\r
+ // Attempt to find an export table in this image\r
+ Status = GetExportDirectoryInPeCoffImage (\r
+ LoadedImageProtocol->ImageBase,\r
+ &TempPrmModuleImageContext.PeCoffImageContext,\r
+ &TempPrmModuleImageContext.ExportDirectory\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ continue;\r
+ }\r
+\r
+ // Attempt to find the PRM Module Export Descriptor in the export table\r
+ Status = GetPrmModuleExportDescriptorTable (\r
+ TempPrmModuleImageContext.ExportDirectory,\r
+ &TempPrmModuleImageContext.PeCoffImageContext,\r
+ &TempPrmModuleImageContext.ExportDescriptor\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ continue;\r
+ }\r
+ // A PRM Module Export Descriptor was successfully found, this is considered a PRM Module.\r
+\r
+ //\r
+ // Create a new PRM Module image context node\r
+ //\r
+ PrmModuleImageContextListEntry = CreateNewPrmModuleImageContextListEntry ();\r
+ if (PrmModuleImageContextListEntry == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ CopyMem (\r
+ PrmModuleImageContextListEntry->Context,\r
+ &TempPrmModuleImageContext,\r
+ sizeof (*(PrmModuleImageContextListEntry->Context))\r
+ );\r
+ InsertTailList (&mPrmModuleList, &PrmModuleImageContextListEntry->Link);\r
+ mPrmHandlerCount += TempPrmModuleImageContext.ExportDescriptor->NumberPrmHandlers;\r
+ mPrmModuleCount++; // Todo: Match with global variable refactor change in the future\r
+ DEBUG ((DEBUG_INFO, "%a %a: New PRM Module inserted into list to be processed.\n", _DBGMSGID_, __FUNCTION__));\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Gets the address of an entry in an image export table by ASCII name.\r
+\r
+ @param[in] ExportName A pointer to an ASCII name string of the entry name.\r
+ @param[in] ImageBaseAddress The base address of the PE/COFF image.\r
+ @param[in] ImageExportDirectory A pointer to the export directory in the image.\r
+ @param[out] ExportPhysicalAddress A pointer that will be updated with the address of the address of the\r
+ export entry if found.\r
+\r
+ @retval EFI_SUCCESS The export entry was found successfully.\r
+ @retval EFI_INVALID_PARAMETER A required pointer argument is NULL.\r
+ @retval EFI_NOT_FOUND An entry with the given ExportName was not found.\r
+\r
+**/\r
+EFI_STATUS\r
+GetExportEntryAddress (\r
+ IN CONST CHAR8 *ExportName,\r
+ IN EFI_PHYSICAL_ADDRESS ImageBaseAddress,\r
+ IN EFI_IMAGE_EXPORT_DIRECTORY *ImageExportDirectory,\r
+ OUT EFI_PHYSICAL_ADDRESS *ExportPhysicalAddress\r
+ )\r
+{\r
+ UINTN ExportNameIndex;\r
+ UINT16 CurrentExportOrdinal;\r
+ UINT32 *ExportAddressTable;\r
+ UINT32 *ExportNamePointerTable;\r
+ UINT16 *OrdinalTable;\r
+ CONST CHAR8 *ExportNameTablePointerName;\r
+\r
+ if (ExportName == NULL || ImageBaseAddress == 0 || ImageExportDirectory == NULL || ExportPhysicalAddress == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ *ExportPhysicalAddress = 0;\r
+\r
+ ExportAddressTable = (UINT32 *) ((UINTN) ImageBaseAddress + ImageExportDirectory->AddressOfFunctions);\r
+ ExportNamePointerTable = (UINT32 *) ((UINTN) ImageBaseAddress + ImageExportDirectory->AddressOfNames);\r
+ OrdinalTable = (UINT16 *) ((UINTN) ImageBaseAddress + ImageExportDirectory->AddressOfNameOrdinals);\r
+\r
+ for (ExportNameIndex = 0; ExportNameIndex < ImageExportDirectory->NumberOfNames; ExportNameIndex++) {\r
+ ExportNameTablePointerName = (CONST CHAR8 *) ((UINTN) ImageBaseAddress + ExportNamePointerTable[ExportNameIndex]);\r
+\r
+ if (AsciiStrnCmp (ExportName, ExportNameTablePointerName, PRM_HANDLER_NAME_MAXIMUM_LENGTH) == 0) {\r
+ CurrentExportOrdinal = OrdinalTable[ExportNameIndex];\r
+\r
+ ASSERT (CurrentExportOrdinal < ImageExportDirectory->NumberOfFunctions);\r
+ if (CurrentExportOrdinal >= ImageExportDirectory->NumberOfFunctions) {\r
+ DEBUG ((DEBUG_ERROR, " %a %a: The export ordinal value is invalid.\n", _DBGMSGID_, __FUNCTION__));\r
+ break;\r
+ }\r
+\r
+ *ExportPhysicalAddress = (EFI_PHYSICAL_ADDRESS) ((UINTN) ImageBaseAddress + ExportAddressTable[CurrentExportOrdinal]);\r
+ return EFI_SUCCESS;\r
+ }\r
+ }\r
+\r
+ return EFI_NOT_FOUND;\r
+}\r
+\r
+/**\r
+ Processes a list of PRM context entries to build a PRM ACPI table.\r
+\r
+ The ACPI table buffer is allocated and the table structure is built inside this function.\r
+\r
+ @param[out] PrmAcpiDescriptionTable A pointer to a pointer to a buffer that is allocated within this function\r
+ and will contain the PRM ACPI table. In case of an error in this function,\r
+ *PrmAcpiDescriptorTable will be NULL.\r
+\r
+ @retval EFI_SUCCESS All PRM Modules were processed to construct the PRM ACPI table successfully.\r
+ @retval EFI_INVALID_PARAMETER THe parameter PrmAcpiDescriptionTable is NULL.\r
+ @retval EFI_OUT_OF_RESOURCES Insufficient memory resources to allocate the PRM ACPI table boot services\r
+ memory data buffer.\r
+\r
+**/\r
+EFI_STATUS\r
+ProcessPrmModules (\r
+ OUT PRM_ACPI_DESCRIPTION_TABLE **PrmAcpiDescriptionTable\r
+ )\r
+{\r
+ EFI_IMAGE_EXPORT_DIRECTORY *CurrentImageExportDirectory;\r
+ PRM_MODULE_EXPORT_DESCRIPTOR_STRUCT *CurrentExportDescriptorStruct;\r
+ LIST_ENTRY *Link;\r
+ PRM_ACPI_DESCRIPTION_TABLE *PrmAcpiTable;\r
+ PRM_MODULE_IMAGE_CONTEXT_LIST_ENTRY *TempListEntry;\r
+ CONST CHAR8 *CurrentExportDescriptorHandlerName;\r
+\r
+ PRM_CONTEXT_BUFFER *CurrentContextBuffer;\r
+ PRM_MODULE_CONTEXT_BUFFERS *CurrentModuleContextBuffers;\r
+ PRM_MODULE_INFORMATION_STRUCT *CurrentModuleInfoStruct;\r
+ PRM_HANDLER_INFORMATION_STRUCT *CurrentHandlerInfoStruct;\r
+\r
+ EFI_STATUS Status;\r
+ EFI_PHYSICAL_ADDRESS CurrentImageAddress;\r
+ UINTN HandlerIndex;\r
+ UINT32 PrmAcpiDescriptionTableBufferSize;\r
+\r
+ UINT64 HandlerPhysicalAddress;\r
+\r
+ DEBUG ((DEBUG_INFO, "%a %a - Entry.\n", _DBGMSGID_, __FUNCTION__));\r
+\r
+ if (PrmAcpiDescriptionTable == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ Link = NULL;\r
+ *PrmAcpiDescriptionTable = NULL;\r
+\r
+ DEBUG ((DEBUG_INFO, " %a %a: %d total PRM modules to process.\n", _DBGMSGID_, __FUNCTION__, mPrmModuleCount));\r
+ DEBUG ((DEBUG_INFO, " %a %a: %d total PRM handlers to process.\n", _DBGMSGID_, __FUNCTION__, mPrmHandlerCount));\r
+\r
+ PrmAcpiDescriptionTableBufferSize = (OFFSET_OF (PRM_ACPI_DESCRIPTION_TABLE, PrmModuleInfoStructure) +\r
+ (OFFSET_OF (PRM_MODULE_INFORMATION_STRUCT, HandlerInfoStructure) * mPrmModuleCount) +\r
+ (sizeof (PRM_HANDLER_INFORMATION_STRUCT) * mPrmHandlerCount)\r
+ );\r
+ DEBUG ((DEBUG_INFO, " %a %a: Total PRM ACPI table size: 0x%x.\n", _DBGMSGID_, __FUNCTION__, PrmAcpiDescriptionTableBufferSize));\r
+\r
+ PrmAcpiTable = AllocateZeroPool ((UINTN) PrmAcpiDescriptionTableBufferSize);\r
+ if (PrmAcpiTable == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ PrmAcpiTable->Header.Signature = PRM_TABLE_SIGNATURE;\r
+ PrmAcpiTable->Header.Length = PrmAcpiDescriptionTableBufferSize;\r
+ PrmAcpiTable->Header.Revision = PRM_TABLE_REVISION;\r
+ PrmAcpiTable->Header.Checksum = 0x0;\r
+ CopyMem (&PrmAcpiTable->Header.OemId, PcdGetPtr (PcdAcpiDefaultOemId), sizeof (PrmAcpiTable->Header.OemId));\r
+ PrmAcpiTable->Header.OemTableId = PcdGet64 (PcdAcpiDefaultOemTableId);\r
+ PrmAcpiTable->Header.OemRevision = PcdGet32 (PcdAcpiDefaultOemRevision);\r
+ PrmAcpiTable->Header.CreatorId = PcdGet32 (PcdAcpiDefaultCreatorId);\r
+ PrmAcpiTable->Header.CreatorRevision = PcdGet32 (PcdAcpiDefaultCreatorRevision);\r
+ PrmAcpiTable->PrmModuleInfoOffset = OFFSET_OF (PRM_ACPI_DESCRIPTION_TABLE, PrmModuleInfoStructure);\r
+ PrmAcpiTable->PrmModuleInfoCount = mPrmModuleCount;\r
+\r
+ //\r
+ // Iterate across all PRM Modules on the list\r
+ //\r
+ CurrentModuleInfoStruct = &PrmAcpiTable->PrmModuleInfoStructure[0];\r
+ EFI_LIST_FOR_EACH(Link, &mPrmModuleList)\r
+ {\r
+ TempListEntry = CR(Link, PRM_MODULE_IMAGE_CONTEXT_LIST_ENTRY, Link, PRM_MODULE_IMAGE_CONTEXT_LIST_ENTRY_SIGNATURE);\r
+ CurrentImageAddress = TempListEntry->Context->PeCoffImageContext.ImageAddress;\r
+ CurrentImageExportDirectory = TempListEntry->Context->ExportDirectory;\r
+ CurrentExportDescriptorStruct = TempListEntry->Context->ExportDescriptor;\r
+\r
+ DEBUG ((\r
+ DEBUG_INFO,\r
+ " %a %a: PRM Module - %a with %d handlers.\n",\r
+ _DBGMSGID_,\r
+ __FUNCTION__,\r
+ (CHAR8 *) ((UINTN) CurrentImageAddress + CurrentImageExportDirectory->Name),\r
+ CurrentExportDescriptorStruct->NumberPrmHandlers\r
+ ));\r
+\r
+ CurrentModuleInfoStruct->StructureRevision = PRM_MODULE_INFORMATION_STRUCT_REVISION;\r
+ CurrentModuleInfoStruct->StructureLength = (\r
+ OFFSET_OF (PRM_MODULE_INFORMATION_STRUCT, HandlerInfoStructure) +\r
+ (CurrentExportDescriptorStruct->NumberPrmHandlers * sizeof (PRM_HANDLER_INFORMATION_STRUCT))\r
+ );\r
+ CopyGuid (&CurrentModuleInfoStruct->Identifier, &CurrentExportDescriptorStruct->ModuleGuid);\r
+ CurrentModuleInfoStruct->HandlerCount = (UINT32) CurrentExportDescriptorStruct->NumberPrmHandlers;\r
+ CurrentModuleInfoStruct->HandlerInfoOffset = OFFSET_OF (PRM_MODULE_INFORMATION_STRUCT, HandlerInfoStructure);\r
+\r
+ CurrentModuleInfoStruct->MajorRevision = 0;\r
+ CurrentModuleInfoStruct->MinorRevision = 0;\r
+ Status = GetImageVersionInPeCoffImage (\r
+ (VOID *) (UINTN) CurrentImageAddress,\r
+ &TempListEntry->Context->PeCoffImageContext,\r
+ &CurrentModuleInfoStruct->MajorRevision,\r
+ &CurrentModuleInfoStruct->MinorRevision\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ Status = GetExportEntryAddress (\r
+ PRM_STRING (PRM_MODULE_UPDATE_LOCK_DESCRIPTOR_NAME),\r
+ CurrentImageAddress,\r
+ CurrentImageExportDirectory,\r
+ (EFI_PHYSICAL_ADDRESS *) &(CurrentModuleInfoStruct->ModuleUpdateLock)\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+ if (!EFI_ERROR (Status)) {\r
+ DEBUG ((\r
+ DEBUG_INFO,\r
+ " %a %a: Found PRM module update lock physical address at 0x%016x.\n",\r
+ _DBGMSGID_,\r
+ __FUNCTION__,\r
+ CurrentModuleInfoStruct->ModuleUpdateLock\r
+ ));\r
+ }\r
+\r
+ // It is currently valid for a PRM module not to use a context buffer\r
+ Status = GetModuleContextBuffers (\r
+ ByModuleGuid,\r
+ &CurrentModuleInfoStruct->Identifier,\r
+ &CurrentModuleContextBuffers\r
+ );\r
+ ASSERT (!EFI_ERROR (Status) || Status == EFI_NOT_FOUND);\r
+ if (!EFI_ERROR (Status) && CurrentModuleContextBuffers != NULL) {\r
+ CurrentModuleInfoStruct->RuntimeMmioRanges = (UINT64) (UINTN) CurrentModuleContextBuffers->RuntimeMmioRanges;\r
+ }\r
+\r
+ //\r
+ // Iterate across all PRM handlers in the PRM Module\r
+ //\r
+ for (HandlerIndex = 0; HandlerIndex < CurrentExportDescriptorStruct->NumberPrmHandlers; HandlerIndex++) {\r
+ CurrentHandlerInfoStruct = &(CurrentModuleInfoStruct->HandlerInfoStructure[HandlerIndex]);\r
+\r
+ CurrentHandlerInfoStruct->StructureRevision = PRM_HANDLER_INFORMATION_STRUCT_REVISION;\r
+ CurrentHandlerInfoStruct->StructureLength = sizeof (PRM_HANDLER_INFORMATION_STRUCT);\r
+ CopyGuid (\r
+ &CurrentHandlerInfoStruct->Identifier,\r
+ &CurrentExportDescriptorStruct->PrmHandlerExportDescriptors[HandlerIndex].PrmHandlerGuid\r
+ );\r
+\r
+ CurrentExportDescriptorHandlerName = (CONST CHAR8 *) CurrentExportDescriptorStruct->PrmHandlerExportDescriptors[HandlerIndex].PrmHandlerName;\r
+\r
+ Status = GetContextBuffer (\r
+ &CurrentHandlerInfoStruct->Identifier,\r
+ CurrentModuleContextBuffers,\r
+ &CurrentContextBuffer\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ CurrentHandlerInfoStruct->PrmContextBuffer = (UINT64) CurrentContextBuffer;\r
+ }\r
+\r
+ Status = GetExportEntryAddress (\r
+ CurrentExportDescriptorHandlerName,\r
+ CurrentImageAddress,\r
+ CurrentImageExportDirectory,\r
+ &HandlerPhysicalAddress\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+ if (!EFI_ERROR (Status)) {\r
+ CurrentHandlerInfoStruct->PhysicalAddress = HandlerPhysicalAddress;\r
+ DEBUG ((\r
+ DEBUG_INFO,\r
+ " %a %a: Found %a handler physical address at 0x%016x.\n",\r
+ _DBGMSGID_,\r
+ __FUNCTION__,\r
+ CurrentExportDescriptorHandlerName,\r
+ CurrentHandlerInfoStruct->PhysicalAddress\r
+ ));\r
+ }\r
+ }\r
+ CurrentModuleInfoStruct = (PRM_MODULE_INFORMATION_STRUCT *) ((UINTN) CurrentModuleInfoStruct + CurrentModuleInfoStruct->StructureLength);\r
+ }\r
+ *PrmAcpiDescriptionTable = PrmAcpiTable;\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Publishes the PRM ACPI table (PRMT).\r
+\r
+ @param[in] PrmAcpiDescriptionTable A pointer to a buffer with a completely populated and valid PRM\r
+ ACPI description table.\r
+\r
+ @retval EFI_SUCCESS The PRM ACPI was installed successfully.\r
+ @retval EFI_INVALID_PARAMETER THe parameter PrmAcpiDescriptionTable is NULL or the table signature\r
+ in the table provided is invalid.\r
+ @retval EFI_NOT_FOUND The protocol gEfiAcpiTableProtocolGuid could not be found.\r
+ @retval EFI_OUT_OF_RESOURCES Insufficient memory resources to allocate the PRM ACPI table buffer.\r
+\r
+**/\r
+EFI_STATUS\r
+PublishPrmAcpiTable (\r
+ IN PRM_ACPI_DESCRIPTION_TABLE *PrmAcpiDescriptionTable\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_ACPI_TABLE_PROTOCOL *AcpiTableProtocol;\r
+ UINTN TableKey;\r
+\r
+ if (PrmAcpiDescriptionTable == NULL || PrmAcpiDescriptionTable->Header.Signature != PRM_TABLE_SIGNATURE) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Status = gBS->LocateProtocol (&gEfiAcpiTableProtocolGuid, NULL, (VOID **) &AcpiTableProtocol);\r
+ if (!EFI_ERROR (Status)) {\r
+ TableKey = 0;\r
+ //\r
+ // Publish the PRM ACPI table. The table checksum will be computed during installation.\r
+ //\r
+ Status = AcpiTableProtocol->InstallAcpiTable (\r
+ AcpiTableProtocol,\r
+ PrmAcpiDescriptionTable,\r
+ PrmAcpiDescriptionTable->Header.Length,\r
+ &TableKey\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ DEBUG ((DEBUG_INFO, "%a %a: The PRMT ACPI table was installed successfully.\n", _DBGMSGID_, __FUNCTION__));\r
+ }\r
+ }\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ The PRM Loader END_OF_DXE protocol notification event handler.\r
+\r
+ All PRM Modules that are eligible for dispatch should have been loaded the DXE Dispatcher at the\r
+ time of this function invocation.\r
+\r
+ The main responsibilities of the PRM Loader are executed from this function which include 3 phases:\r
+ 1.) Disover PRM Modules - Find all PRM modules loaded during DXE dispatch and insert a PRM Module\r
+ Context entry into a linked list to be handed off to phase 2.\r
+ 2.) Process PRM Modules - Build a GUID to PRM handler mapping for each module that is described in the\r
+ PRM ACPI table so the OS can resolve a PRM Handler GUID to the corresponding PRM Handler physical address.\r
+ 3.) Publish PRM ACPI Table - Publish the PRM ACPI table with the information gathered in the phase 2.\r
+\r
+ @param[in] Event Event whose notification function is being invoked.\r
+ @param[in] Context The pointer to the notification function's context,\r
+ which is implementation-dependent.\r
+\r
+ @retval EFI_SUCCESS The function executed successfully\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+PrmLoaderEndOfDxeNotification (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ PRM_ACPI_DESCRIPTION_TABLE *PrmAcpiDescriptionTable;\r
+\r
+ DEBUG ((DEBUG_INFO, "%a %a - Entry.\n", _DBGMSGID_, __FUNCTION__));\r
+\r
+ InitializeListHead (&mPrmModuleList);\r
+\r
+ Status = DiscoverPrmModules ();\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ Status = ProcessPrmModules (&PrmAcpiDescriptionTable);\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ Status = PublishPrmAcpiTable (PrmAcpiDescriptionTable);\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ if (PrmAcpiDescriptionTable != NULL) {\r
+ FreePool (PrmAcpiDescriptionTable);\r
+ }\r
+ gBS->CloseEvent (Event);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ The entry point for this module.\r
+\r
+ @param ImageHandle The firmware allocated handle for the EFI image.\r
+ @param SystemTable A pointer to the EFI System Table.\r
+\r
+ @retval EFI_SUCCESS The entry point is executed successfully.\r
+ @retval Others An error occurred when executing this entry point.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+PrmLoaderEntryPoint (\r
+ IN EFI_HANDLE ImageHandle,\r
+ IN EFI_SYSTEM_TABLE *SystemTable\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_EVENT EndOfDxeEvent;\r
+\r
+ DEBUG ((DEBUG_INFO, "%a %a - Entry.\n", _DBGMSGID_, __FUNCTION__));\r
+\r
+ //\r
+ // Discover and process installed PRM modules at the End of DXE\r
+ // The PRM ACPI table is published if one or PRM modules are discovered\r
+ //\r
+ Status = gBS->CreateEventEx(\r
+ EVT_NOTIFY_SIGNAL,\r
+ TPL_CALLBACK,\r
+ PrmLoaderEndOfDxeNotification,\r
+ NULL,\r
+ &gEfiEndOfDxeEventGroupGuid,\r
+ &EndOfDxeEvent\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((DEBUG_ERROR, "%a %a: EndOfDxe callback registration failed! %r.\n", _DBGMSGID_, __FUNCTION__, Status));\r
+ ASSERT_EFI_ERROR (Status);\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r