summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--PrmPkg/PrmConfigDxe/PrmConfigDxe.c559
-rw-r--r--PrmPkg/PrmConfigDxe/PrmConfigDxe.inf48
-rw-r--r--PrmPkg/PrmLoaderDxe/PrmAcpiTable.h97
-rw-r--r--PrmPkg/PrmLoaderDxe/PrmLoader.h51
-rw-r--r--PrmPkg/PrmLoaderDxe/PrmLoaderDxe.c925
-rw-r--r--PrmPkg/PrmLoaderDxe/PrmLoaderDxe.inf59
6 files changed, 1739 insertions, 0 deletions
diff --git a/PrmPkg/PrmConfigDxe/PrmConfigDxe.c b/PrmPkg/PrmConfigDxe/PrmConfigDxe.c
new file mode 100644
index 0000000000..cb38146bc9
--- /dev/null
+++ b/PrmPkg/PrmConfigDxe/PrmConfigDxe.c
@@ -0,0 +1,559 @@
+/** @file
+
+ This file contains the implementation for a Platform Runtime Mechanism (PRM) configuration driver.
+
+ Copyright (c) Microsoft Corporation
+ Copyright (c) 2020, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/DxeServicesTableLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+#include <Library/UefiLib.h>
+
+#include <PiDxe.h>
+#include <PrmContextBuffer.h>
+#include <PrmDataBuffer.h>
+#include <PrmMmio.h>
+#include <Protocol/PrmConfig.h>
+
+#define _DBGMSGID_ "[PRMCONFIG]"
+
+STATIC UINTN mMaxRuntimeMmioRangeCount;
+STATIC UINTN mMaxStaticDataBufferCount;
+
+STATIC PRM_RUNTIME_MMIO_RANGES **mRuntimeMmioRanges;
+STATIC PRM_DATA_BUFFER ***mStaticDataBuffers;
+
+/**
+ Converts the runtime memory range physical addresses to virtual addresses.
+
+ @param[in] RuntimeMmioRanges A pointer to a PRM_RUNTIME_MMIO_RANGES buffer.
+
+**/
+VOID
+ConvertRuntimeMemoryRangeAddresses (
+ IN PRM_RUNTIME_MMIO_RANGES *RuntimeMmioRanges
+ )
+{
+ UINTN Index;
+
+ if (RuntimeMmioRanges == NULL || RuntimeMmioRanges->Count == 0) {
+ return;
+ }
+
+ for (Index = 0; Index < (UINTN) RuntimeMmioRanges->Count; Index++) {
+ RuntimeMmioRanges->Range[Index].VirtualBaseAddress = RuntimeMmioRanges->Range[Index].PhysicalBaseAddress;
+ gRT->ConvertPointer (0x0, (VOID **) &(RuntimeMmioRanges->Range[Index].VirtualBaseAddress));
+ }
+}
+
+/**
+ Sets the runtime memory range attributes.
+
+ The EFI_MEMORY_RUNTIME attribute is set for each PRM_RUNTIME_MMIO_RANGE present
+ in the buffer provided.
+
+ @param[in] RuntimeMmioRanges A pointer to a PRM_RUNTIME_MMIO_RANGES buffer.
+
+**/
+VOID
+SetRuntimeMemoryRangeAttributes (
+ IN PRM_RUNTIME_MMIO_RANGES *RuntimeMmioRanges
+ )
+{
+ EFI_STATUS Status;
+ EFI_STATUS Status2;
+ UINTN Index;
+ EFI_GCD_MEMORY_SPACE_DESCRIPTOR Descriptor;
+
+ DEBUG ((DEBUG_INFO, "%a %a - Entry.\n", _DBGMSGID_, __FUNCTION__));
+
+ if (RuntimeMmioRanges == NULL || RuntimeMmioRanges->Count == 0) {
+ return;
+ }
+
+ for (Index = 0; Index < (UINTN) RuntimeMmioRanges->Count; Index++) {
+ DEBUG ((
+ DEBUG_INFO, " %a %a: Runtime MMIO Range [%d].\n", _DBGMSGID_, __FUNCTION__, Index));
+ DEBUG ((
+ DEBUG_INFO,
+ " %a %a: Physical address = 0x%016x. Length = 0x%x.\n",
+ _DBGMSGID_,
+ __FUNCTION__,
+ RuntimeMmioRanges->Range[Index].PhysicalBaseAddress,
+ RuntimeMmioRanges->Range[Index].Length
+ ));
+
+ // Runtime memory ranges should cover ranges on a page boundary
+ ASSERT ((RuntimeMmioRanges->Range[Index].PhysicalBaseAddress & EFI_PAGE_MASK) == 0);
+ ASSERT ((RuntimeMmioRanges->Range[Index].Length & EFI_PAGE_MASK) == 0);
+
+ Status2 = EFI_NOT_FOUND;
+ Status = gDS->GetMemorySpaceDescriptor (RuntimeMmioRanges->Range[Index].PhysicalBaseAddress, &Descriptor);
+ if (!EFI_ERROR (Status) &&
+ (
+ (Descriptor.GcdMemoryType != EfiGcdMemoryTypeMemoryMappedIo && Descriptor.GcdMemoryType != EfiGcdMemoryTypeReserved) ||
+ ((Descriptor.Length & EFI_PAGE_MASK) != 0)
+ )
+ ) {
+ Status2 = gDS->RemoveMemorySpace (
+ RuntimeMmioRanges->Range[Index].PhysicalBaseAddress,
+ Descriptor.Length
+ );
+ }
+
+ if (Status == EFI_NOT_FOUND || !EFI_ERROR (Status2)) {
+ Status = gDS->AddMemorySpace (
+ EfiGcdMemoryTypeMemoryMappedIo,
+ RuntimeMmioRanges->Range[Index].PhysicalBaseAddress,
+ (UINT64) RuntimeMmioRanges->Range[Index].Length,
+ EFI_MEMORY_UC | EFI_MEMORY_RUNTIME
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ Status = gDS->AllocateMemorySpace (
+ EfiGcdAllocateAddress,
+ EfiGcdMemoryTypeMemoryMappedIo,
+ 0,
+ (UINT64) RuntimeMmioRanges->Range[Index].Length,
+ &RuntimeMmioRanges->Range[Index].PhysicalBaseAddress,
+ gImageHandle,
+ NULL
+ );
+ ASSERT_EFI_ERROR (Status);
+ }
+
+ Status = gDS->GetMemorySpaceDescriptor (RuntimeMmioRanges->Range[Index].PhysicalBaseAddress, &Descriptor);
+ ASSERT_EFI_ERROR (Status);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ " %a %a: Error [%r] finding descriptor for runtime memory range 0x%016x.\n",
+ _DBGMSGID_,
+ __FUNCTION__,
+ Status,
+ RuntimeMmioRanges->Range[Index].PhysicalBaseAddress
+ ));
+ continue;
+ }
+ if ((Descriptor.Attributes & EFI_MEMORY_RUNTIME) != 0) {
+ continue;
+ }
+
+ Status = gDS->SetMemorySpaceAttributes (
+ RuntimeMmioRanges->Range[Index].PhysicalBaseAddress,
+ (UINT64) RuntimeMmioRanges->Range[Index].Length,
+ Descriptor.Attributes | EFI_MEMORY_RUNTIME
+ );
+ ASSERT_EFI_ERROR (Status);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ " %a %a: Error [%r] setting descriptor for runtime memory range 0x%016x.\n",
+ _DBGMSGID_,
+ __FUNCTION__,
+ Status,
+ RuntimeMmioRanges->Range[Index].PhysicalBaseAddress
+ ));
+ } else {
+ DEBUG ((DEBUG_INFO, " %a %a: Successfully set runtime attribute for the MMIO range.\n", _DBGMSGID_, __FUNCTION__));
+ }
+ }
+}
+
+/**
+ Stores pointers or pointer to resources that should be converted in the virtual address change event.
+
+**/
+VOID
+StoreVirtualMemoryAddressChangePointers (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ UINTN BufferIndex;
+ UINTN HandleCount;
+ UINTN HandleIndex;
+ UINTN RangeIndex;
+ UINTN StaticDataBufferIndex;
+ EFI_HANDLE *HandleBuffer;
+ PRM_CONFIG_PROTOCOL *PrmConfigProtocol;
+ PRM_CONTEXT_BUFFER *CurrentContextBuffer;
+
+ DEBUG ((DEBUG_INFO, "%a %a - Entry.\n", _DBGMSGID_, __FUNCTION__));
+
+ RangeIndex = 0;
+ StaticDataBufferIndex = 0;
+
+ mRuntimeMmioRanges = AllocateRuntimeZeroPool (sizeof (*mRuntimeMmioRanges) * mMaxRuntimeMmioRangeCount);
+ if (mRuntimeMmioRanges == NULL && mMaxRuntimeMmioRangeCount > 0) {
+ DEBUG ((
+ DEBUG_ERROR,
+ " %a %a: Memory allocation for runtime MMIO pointer array failed.\n",
+ _DBGMSGID_,
+ __FUNCTION__
+ ));
+ ASSERT (FALSE);
+ return;
+ }
+
+ mStaticDataBuffers = AllocateRuntimeZeroPool (sizeof (*mStaticDataBuffers) * mMaxStaticDataBufferCount);
+ if (mStaticDataBuffers == NULL && mMaxStaticDataBufferCount > 0) {
+ DEBUG ((
+ DEBUG_ERROR,
+ " %a %a: Memory allocation for PRM static data buffer pointer array failed.\n",
+ _DBGMSGID_,
+ __FUNCTION__
+ ));
+ ASSERT (FALSE);
+ return;
+ }
+
+ HandleBuffer = NULL;
+ Status = gBS->LocateHandleBuffer (
+ ByProtocol,
+ &gPrmConfigProtocolGuid,
+ NULL,
+ &HandleCount,
+ &HandleBuffer
+ );
+ if (!EFI_ERROR (Status)) {
+ for (HandleIndex = 0; HandleIndex < HandleCount; HandleIndex++) {
+ Status = gBS->HandleProtocol (
+ HandleBuffer[HandleIndex],
+ &gPrmConfigProtocolGuid,
+ (VOID **) &PrmConfigProtocol
+ );
+ ASSERT_EFI_ERROR (Status);
+ if (EFI_ERROR (Status) || PrmConfigProtocol == NULL) {
+ continue;
+ }
+
+ for (BufferIndex = 0; BufferIndex < PrmConfigProtocol->ModuleContextBuffers.BufferCount; BufferIndex++) {
+ CurrentContextBuffer = &(PrmConfigProtocol->ModuleContextBuffers.Buffer[BufferIndex]);
+
+ if (CurrentContextBuffer->StaticDataBuffer != NULL) {
+ if (StaticDataBufferIndex >= mMaxStaticDataBufferCount) {
+ Status = EFI_BUFFER_TOO_SMALL;
+ DEBUG ((
+ DEBUG_ERROR,
+ " %a %a: Index out of bounds - Actual count (%d) of PRM data buffers exceeds maximum count (%d).\n",
+ _DBGMSGID_,
+ __FUNCTION__,
+ StaticDataBufferIndex + 1,
+ mMaxStaticDataBufferCount
+ ));
+ ASSERT_EFI_ERROR (Status);
+ return;
+ }
+ mStaticDataBuffers[StaticDataBufferIndex++] = &CurrentContextBuffer->StaticDataBuffer;
+ }
+ }
+ if (PrmConfigProtocol->ModuleContextBuffers.RuntimeMmioRanges != NULL) {
+ if (RangeIndex >= mMaxRuntimeMmioRangeCount) {
+ Status = EFI_BUFFER_TOO_SMALL;
+ DEBUG ((
+ DEBUG_ERROR,
+ " %a %a: Index out of bounds - Actual count (%d) of runtime MMIO ranges exceeds maximum count (%d).\n",
+ _DBGMSGID_,
+ __FUNCTION__,
+ RangeIndex + 1,
+ mMaxRuntimeMmioRangeCount
+ ));
+ ASSERT_EFI_ERROR (Status);
+ return;
+ }
+ mRuntimeMmioRanges[RangeIndex++] = PrmConfigProtocol->ModuleContextBuffers.RuntimeMmioRanges;
+ }
+ }
+ DEBUG ((
+ DEBUG_INFO,
+ " %a %a: %d MMIO ranges buffers saved for future virtual memory conversion.\n",
+ _DBGMSGID_,
+ __FUNCTION__,
+ RangeIndex
+ ));
+ DEBUG ((
+ DEBUG_INFO,
+ " %a %a: %d static buffers saved for future virtual memory conversion.\n",
+ _DBGMSGID_,
+ __FUNCTION__,
+ StaticDataBufferIndex
+ ));
+ }
+}
+
+/**
+ Validates a data buffer for a PRM module.
+
+ Verifies the buffer header signature is valid and the length meets the minimum size.
+
+ @param[in] PrmDataBuffer A pointer to the data buffer for this PRM module.
+
+ @retval EFI_SUCCESS The data buffer was validated successfully.
+ @retval EFI_INVALID_PARAMETER The pointer given for PrmDataBuffer is NULL.
+ @retval EFI_NOT_FOUND The data buffer signature is not valid.
+ @retval EFI_BUFFER_TOO_SMALL The buffer size is too small.
+
+**/
+EFI_STATUS
+ValidatePrmDataBuffer (
+ IN CONST PRM_DATA_BUFFER *PrmDataBuffer
+ )
+{
+ if (PrmDataBuffer == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (PrmDataBuffer->Header.Signature != PRM_DATA_BUFFER_HEADER_SIGNATURE) {
+ DEBUG ((DEBUG_ERROR, " %a %a: The PRM data buffer signature is invalid. PRM module.\n", _DBGMSGID_, __FUNCTION__));
+ return EFI_NOT_FOUND;
+ }
+ if (PrmDataBuffer->Header.Length < sizeof (PRM_DATA_BUFFER_HEADER)) {
+ DEBUG ((DEBUG_ERROR, " %a %a: The PRM data buffer length is invalid.\n", _DBGMSGID_, __FUNCTION__));
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Validates a PRM context buffer.
+
+ Verifies the buffer header signature is valid and the GUID is set to a non-zero value.
+
+ @param[in] PrmContextBuffer A pointer to the context buffer for this PRM handler.
+
+ @retval EFI_SUCCESS The context buffer was validated successfully.
+ @retval EFI_INVALID_PARAMETER The pointer given for ContextBuffer is NULL.
+ @retval EFI_NOT_FOUND The proper value for a field was not found.
+
+**/
+EFI_STATUS
+ValidatePrmContextBuffer (
+ IN CONST PRM_CONTEXT_BUFFER *PrmContextBuffer
+ )
+{
+ if (PrmContextBuffer == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (PrmContextBuffer->Signature != PRM_CONTEXT_BUFFER_SIGNATURE) {
+ DEBUG ((DEBUG_ERROR, " %a %a: The PRM context buffer signature is invalid.\n", _DBGMSGID_, __FUNCTION__));
+ return EFI_NOT_FOUND;
+ }
+
+ if (IsZeroGuid (&PrmContextBuffer->HandlerGuid)) {
+ DEBUG ((DEBUG_ERROR, " %a %a: The PRM context buffer GUID is zero.\n", _DBGMSGID_, __FUNCTION__));
+ return EFI_NOT_FOUND;
+ }
+
+ if (PrmContextBuffer->StaticDataBuffer != NULL && EFI_ERROR (ValidatePrmDataBuffer (PrmContextBuffer->StaticDataBuffer))) {
+ DEBUG ((
+ DEBUG_ERROR,
+ " %a %a: Error in static buffer for PRM handler %g.\n",
+ _DBGMSGID_,
+ __FUNCTION__,
+ &PrmContextBuffer->HandlerGuid
+ ));
+ return EFI_NOT_FOUND;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Notification function of EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE.
+
+ This is notification function converts any registered PRM_RUNTIME_MMIO_RANGE
+ addresses to a virtual address.
+
+ @param[in] Event Event whose notification function is being invoked.
+ @param[in] Context Pointer to the notification function's context.
+
+**/
+VOID
+EFIAPI
+PrmConfigVirtualAddressChangeEvent (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ UINTN Index;
+
+ //
+ // Convert static data buffer pointers
+ //
+ for (Index = 0; Index < mMaxStaticDataBufferCount; Index++) {
+ gRT->ConvertPointer (0x0, (VOID **) mStaticDataBuffers[Index]);
+ }
+
+ //
+ // Convert runtime MMIO ranges
+ //
+ for (Index = 0; Index < mMaxRuntimeMmioRangeCount; Index++) {
+ ConvertRuntimeMemoryRangeAddresses (mRuntimeMmioRanges[Index]);
+ }
+}
+
+/**
+ The PRM Config END_OF_DXE protocol notification event handler.
+
+ Finds all of the PRM_CONFIG_PROTOCOL instances installed at end of DXE and
+ marks all PRM_RUNTIME_MMIO_RANGE entries as EFI_MEMORY_RUNTIME.
+
+ @param[in] Event Event whose notification function is being invoked.
+ @param[in] Context The pointer to the notification function's context,
+ which is implementation-dependent.
+
+ @retval EFI_SUCCESS The function executed successfully
+
+**/
+EFI_STATUS
+EFIAPI
+PrmConfigEndOfDxeNotification (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ EFI_STATUS Status;
+ UINTN HandleCount;
+ UINTN BufferIndex;
+ UINTN HandleIndex;
+ EFI_HANDLE *HandleBuffer;
+ PRM_CONTEXT_BUFFER *CurrentContextBuffer;
+ PRM_CONFIG_PROTOCOL *PrmConfigProtocol;
+
+ DEBUG ((DEBUG_INFO, "%a %a - Entry.\n", _DBGMSGID_, __FUNCTION__));
+
+ HandleBuffer = NULL;
+ Status = gBS->LocateHandleBuffer (
+ ByProtocol,
+ &gPrmConfigProtocolGuid,
+ NULL,
+ &HandleCount,
+ &HandleBuffer
+ );
+ if (!EFI_ERROR (Status)) {
+ for (HandleIndex = 0; HandleIndex < HandleCount; HandleIndex++) {
+ Status = gBS->HandleProtocol (
+ HandleBuffer[HandleIndex],
+ &gPrmConfigProtocolGuid,
+ (VOID **) &PrmConfigProtocol
+ );
+ ASSERT_EFI_ERROR (Status);
+ if (EFI_ERROR (Status) || PrmConfigProtocol == NULL) {
+ continue;
+ }
+
+ DEBUG ((
+ DEBUG_INFO,
+ " %a %a: Found PRM configuration protocol for PRM module %g.\n",
+ _DBGMSGID_,
+ __FUNCTION__,
+ &PrmConfigProtocol->ModuleContextBuffers.ModuleGuid
+ ));
+
+ DEBUG ((DEBUG_INFO, " %a %a: Validating module context buffers...\n", _DBGMSGID_, __FUNCTION__));
+ for (BufferIndex = 0; BufferIndex < PrmConfigProtocol->ModuleContextBuffers.BufferCount; BufferIndex++) {
+ CurrentContextBuffer = &(PrmConfigProtocol->ModuleContextBuffers.Buffer[BufferIndex]);
+
+ Status = ValidatePrmContextBuffer (CurrentContextBuffer);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ " %a %a: Context buffer validation failed for PRM handler %g.\n",
+ _DBGMSGID_,
+ __FUNCTION__,
+ CurrentContextBuffer->HandlerGuid
+ ));
+ }
+ if (CurrentContextBuffer->StaticDataBuffer != NULL) {
+ mMaxStaticDataBufferCount++;
+ }
+ }
+ DEBUG ((DEBUG_INFO, " %a %a: Module context buffer validation complete.\n", _DBGMSGID_, __FUNCTION__));
+
+ if (PrmConfigProtocol->ModuleContextBuffers.RuntimeMmioRanges != NULL) {
+ DEBUG ((
+ DEBUG_INFO,
+ " %a %a: Found %d PRM runtime MMIO ranges to convert.\n",
+ _DBGMSGID_,
+ __FUNCTION__,
+ PrmConfigProtocol->ModuleContextBuffers.RuntimeMmioRanges->Count
+ ));
+ SetRuntimeMemoryRangeAttributes (PrmConfigProtocol->ModuleContextBuffers.RuntimeMmioRanges);
+ mMaxRuntimeMmioRangeCount++;
+ }
+ }
+
+ StoreVirtualMemoryAddressChangePointers ();
+ }
+
+ if (HandleBuffer != NULL) {
+ gBS->FreePool (HandleBuffer);
+ }
+ gBS->CloseEvent(Event);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ The entry point for this module.
+
+ @param[in] ImageHandle The firmware allocated handle for the EFI image.
+ @param[in] SystemTable A pointer to the EFI System Table.
+
+ @retval EFI_SUCCESS The entry point is executed successfully.
+ @retval Others An error occurred when executing this entry point.
+
+**/
+EFI_STATUS
+EFIAPI
+PrmConfigEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+ EFI_EVENT Event;
+
+ DEBUG ((DEBUG_INFO, "%a %a - Entry.\n", _DBGMSGID_, __FUNCTION__));
+
+ //
+ // Register a notification function to change memory attributes at end of DXE
+ //
+ Event = NULL;
+ Status = gBS->CreateEventEx(
+ EVT_NOTIFY_SIGNAL,
+ TPL_CALLBACK,
+ PrmConfigEndOfDxeNotification,
+ NULL,
+ &gEfiEndOfDxeEventGroupGuid,
+ &Event
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ //
+ // Register a notification function for virtual address change
+ //
+ Event = NULL;
+ Status = gBS->CreateEventEx (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ PrmConfigVirtualAddressChangeEvent,
+ NULL,
+ &gEfiEventVirtualAddressChangeGuid,
+ &Event
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ return EFI_SUCCESS;
+}
diff --git a/PrmPkg/PrmConfigDxe/PrmConfigDxe.inf b/PrmPkg/PrmConfigDxe/PrmConfigDxe.inf
new file mode 100644
index 0000000000..88613c146a
--- /dev/null
+++ b/PrmPkg/PrmConfigDxe/PrmConfigDxe.inf
@@ -0,0 +1,48 @@
+## @file
+# PRM Configuration Driver
+#
+# This driver configures PRM Module settings during the boot services environment.
+#
+# Copyright (c) Microsoft Corporation
+# Copyright (c) 2020, Intel Corporation. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = PrmConfigDxe
+ FILE_GUID = 18D93D57-0B00-4213-B0A2-A2FF5EC214E4
+ MODULE_TYPE = DXE_RUNTIME_DRIVER
+ VERSION_STRING = 1.0
+ ENTRY_POINT = PrmConfigEntryPoint
+
+[Sources]
+ PrmConfigDxe.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ PrmPkg/PrmPkg.dec
+
+[Guids]
+ gEfiEndOfDxeEventGroupGuid
+ gEfiEventVirtualAddressChangeGuid
+
+[LibraryClasses]
+ BaseLib
+ BaseMemoryLib
+ DebugLib
+ DxeServicesTableLib
+ MemoryAllocationLib
+ UefiBootServicesTableLib
+ UefiDriverEntryPoint
+ UefiRuntimeServicesTableLib
+ UefiLib
+
+[Protocols]
+ gPrmConfigProtocolGuid
+
+[Depex]
+ TRUE
diff --git a/PrmPkg/PrmLoaderDxe/PrmAcpiTable.h b/PrmPkg/PrmLoaderDxe/PrmAcpiTable.h
new file mode 100644
index 0000000000..6b9099ca7b
--- /dev/null
+++ b/PrmPkg/PrmLoaderDxe/PrmAcpiTable.h
@@ -0,0 +1,97 @@
+/** @file
+
+ Definition for the Platform Runtime Mechanism (PRM) ACPI table (PRMT).
+
+ Copyright (c) 2020, Intel Corporation. All rights reserved.<BR>
+ Copyright (c) Microsoft Corporation
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef PRMT_ACPI_TABLE_H_
+#define PRMT_ACPI_TABLE_H_
+
+#include <Base.h>
+#include <IndustryStandard/Acpi10.h>
+
+#define PRM_TABLE_SIGNATURE SIGNATURE_32 ('P', 'R', 'M', 'T')
+
+#define PRM_TABLE_REVISION 0x0
+#define PRM_MODULE_INFORMATION_STRUCT_REVISION 0x00
+#define PRM_HANDLER_INFORMATION_STRUCT_REVISION 0x00
+
+#pragma pack(push, 1)
+
+//
+// Platform Runtime Mechanism (PRM) ACPI Table (PRMT) structures
+//
+typedef struct {
+ UINT16 StructureRevision; ///< Revision of this structure
+ UINT16 StructureLength; ///< Length in bytes of this structure
+ GUID Identifier; ///< GUID of the PRM handler for this structure
+ UINT64 PhysicalAddress; ///< Physical address of this PRM handler
+ UINT64 PrmContextBuffer; ///< Physical address of the context buffer for this
+ ///< PRM handler (PRM_CONTEXT_BUFFER *)
+ UINT64 StaticDataBuffer; ///< Physical address of the static data buffer for
+ ///< this PRM handler (PRM_DATA_BUFFER *)
+ UINT64 AcpiParameterBuffer; ///< Physical address of the parameter buffer
+ ///< for this PRM handler (PRM_DATA_BUFFER *)
+ ///< that is only used in the case of _DSM invocation.
+ ///< If _DSM invocation is not used, this value is
+ ///< ignored.
+} PRM_HANDLER_INFORMATION_STRUCT;
+
+typedef struct {
+ UINT16 StructureRevision; ///< Revision of this structure
+ UINT16 StructureLength; ///< Length in bytes of this structure including the
+ ///< variable length PRM Handler Info array
+ GUID Identifier; ///< GUID of the PRM module for this structure
+ UINT16 MajorRevision; ///< PRM module major revision
+ UINT16 MinorRevision; ///< PRM module minor revision
+ UINT16 HandlerCount; ///< Number of entries in the Handler Info array
+ UINT32 HandlerInfoOffset; ///< Offset in bytes from the beginning of this
+ ///< structure to the Handler Info array
+ UINT64 ModuleUpdateLock; ///< Physical address of the PRM Module Update Lock
+ ///< descriptor (PRM_MODULE_UPDATE_LOCK_DESCRIPTOR *)
+ UINT64 RuntimeMmioRanges; ///< Physical address of the PRM MMIO Ranges
+ ///< structure (PRM_MODULE_RUNTIME_MMIO_RANGES *)
+ PRM_HANDLER_INFORMATION_STRUCT HandlerInfoStructure[1];
+} PRM_MODULE_INFORMATION_STRUCT;
+
+typedef struct {
+ EFI_ACPI_DESCRIPTION_HEADER Header; ///< Standard ACPI description header
+ UINT32 PrmModuleInfoOffset; ///< Offset in bytes from the beginning of this
+ ///< structure to the PRM Module Info array
+ UINT32 PrmModuleInfoCount; ///< Number of entries in the PRM Module Info array
+ PRM_MODULE_INFORMATION_STRUCT PrmModuleInfoStructure[1];
+} PRM_ACPI_DESCRIPTION_TABLE;
+
+#pragma pack(pop)
+
+//
+// Helper macros to build PRM Information structures
+//
+// Todo: Revisit whether to use; currently both macros are not used
+//
+#define PRM_MODULE_INFORMATION_STRUCTURE(ModuleGuid, ModuleRevision, HandlerCount, PrmHanderInfoStructureArray) { \
+ { \
+ PRM_MODULE_INFORMATION_STRUCT_REVISION, /* UINT16 StructureRevision; */ \
+ (OFFSET_OF (PRM_MODULE_INFORMATION_STRUCT, HandlerInfoStructure) + (HandlerCount * sizeof (PRM_HANDLER_INFORMATION_STRUCT))) /* UINT16 StructureLength; */ \
+ ModuleGuid, /* GUID ModuleGuid; */ \
+ ModuleRevision, /* UINT16 ModuleRevision */ \
+ HandlerCount, /* UINT16 HandlerCount */ \
+ OFFSET_OF (PRM_MODULE_INFORMATION_STRUCT, HandlerInfoOffset), /* UINT32 HandlerInfoOffset */ \
+ PrmHanderInfoStructureArray /* PRM_HANDLER_INFORMATION_STRUCT HandlerInfoStructure */ \
+ } \
+ }
+
+#define PRM_HANDLER_INFORMATION_STRUCTURE(HandlerGuid, PhysicalAddress) { \
+ { \
+ PRM_HANDLER_INFORMATION_STRUCT_REVISION, /* UINT16 StructureRevision; */ \
+ sizeof (PRM_HANDLER_INFORMATION_STRUCT), /* UINT16 StructureLength; */ \
+ HandlerGuid, /* GUID HandlerGuid; */ \
+ PhysicalAddress, /* UINT64 PhysicalAddress */ \
+ } \
+ }
+
+#endif // _PRMT_ACPI_TABLE_H_
diff --git a/PrmPkg/PrmLoaderDxe/PrmLoader.h b/PrmPkg/PrmLoaderDxe/PrmLoader.h
new file mode 100644
index 0000000000..1356c7a0c9
--- /dev/null
+++ b/PrmPkg/PrmLoaderDxe/PrmLoader.h
@@ -0,0 +1,51 @@
+/** @file
+
+ Definitions specific to the Platform Runtime Mechanism (PRM) loader.x
+
+ Copyright (c) Microsoft Corporation
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef PRM_LOADER_H_
+#define PRM_LOADER_H_
+
+#include <IndustryStandard/PeImage.h>
+#include <Library/PeCoffLib.h>
+
+#include <PrmExportDescriptor.h>
+
+#define _DBGMSGID_ "[PRMLOADER]"
+#define PRM_MODULE_IMAGE_CONTEXT_LIST_ENTRY_SIGNATURE SIGNATURE_32('P','R','M','E')
+
+#pragma pack(push, 1)
+
+typedef struct {
+ PE_COFF_LOADER_IMAGE_CONTEXT PeCoffImageContext;
+ EFI_IMAGE_EXPORT_DIRECTORY *ExportDirectory;
+ PRM_MODULE_EXPORT_DESCRIPTOR_STRUCT *ExportDescriptor;
+} PRM_MODULE_IMAGE_CONTEXT;
+
+typedef struct {
+ UINTN Signature;
+ LIST_ENTRY Link;
+ PRM_MODULE_IMAGE_CONTEXT *Context;
+} PRM_MODULE_IMAGE_CONTEXT_LIST_ENTRY;
+
+#pragma pack(pop)
+
+//
+// Iterate through the double linked list. NOT delete safe.
+//
+#define EFI_LIST_FOR_EACH(Entry, ListHead) \
+ for(Entry = (ListHead)->ForwardLink; Entry != (ListHead); Entry = Entry->ForwardLink)
+
+//
+// Iterate through the double linked list. This is delete-safe.
+// Don't touch NextEntry.
+//
+#define EFI_LIST_FOR_EACH_SAFE(Entry, NextEntry, ListHead) \
+ for(Entry = (ListHead)->ForwardLink, NextEntry = Entry->ForwardLink;\
+ Entry != (ListHead); Entry = NextEntry, NextEntry = Entry->ForwardLin
+
+#endif
diff --git a/PrmPkg/PrmLoaderDxe/PrmLoaderDxe.c b/PrmPkg/PrmLoaderDxe/PrmLoaderDxe.c
new file mode 100644
index 0000000000..b43e6d6bf0
--- /dev/null
+++ b/PrmPkg/PrmLoaderDxe/PrmLoaderDxe.c
@@ -0,0 +1,925 @@
+/** @file
+
+ This file contains the implementation for a Platform Runtime Mechanism (PRM)
+ loader driver.
+
+ Copyright (c) Microsoft Corporation
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "PrmAcpiTable.h"
+#include "PrmLoader.h"
+
+#include <IndustryStandard/Acpi.h>
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/PrmContextBufferLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Protocol/AcpiTable.h>
+#include <Protocol/LoadedImage.h>
+#include <Protocol/PrmConfig.h>
+
+#include <PrmContextBuffer.h>
+#include <PrmMmio.h>
+#include <PrmModuleUpdate.h>
+
+LIST_ENTRY mPrmModuleList;
+
+// Todo: Potentially refactor mPrmHandlerCount and mPrmModuleCount into localized structures
+// in the future.
+UINT32 mPrmHandlerCount;
+UINT32 mPrmModuleCount;
+
+/**
+ Gets a pointer to the export directory in a given PE/COFF image.
+
+ @param[in] ImageExportDirectory A pointer to an export directory table in a PE/COFF image.
+ @param[in] PeCoffLoaderImageContext A pointer to a PE_COFF_LOADER_IMAGE_CONTEXT structure that contains the
+ PE/COFF image context for the Image containing the PRM Module Export
+ Descriptor table.
+ @param[out] ExportDescriptor A pointer to a pointer to the PRM Module Export Descriptor table found
+ in the ImageExportDirectory given.
+
+ @retval EFI_SUCCESS The PRM Module Export Descriptor table was found successfully.
+ @retval EFI_INVALID_PARAMETER A required parameter is NULL.
+ @retval EFI_NOT_FOUND The PRM Module Export Descriptor table was not found in the given
+ ImageExportDirectory.
+
+**/
+EFI_STATUS
+GetPrmModuleExportDescriptorTable (
+ IN EFI_IMAGE_EXPORT_DIRECTORY *ImageExportDirectory,
+ IN PE_COFF_LOADER_IMAGE_CONTEXT *PeCoffLoaderImageContext,
+ OUT PRM_MODULE_EXPORT_DESCRIPTOR_STRUCT **ExportDescriptor
+ )
+{
+ UINTN Index;
+ EFI_PHYSICAL_ADDRESS CurrentImageAddress;
+ UINT16 PrmModuleExportDescriptorOrdinal;
+ CONST CHAR8 *CurrentExportName;
+ UINT16 *OrdinalTable;
+ UINT32 *ExportNamePointerTable;
+ UINT32 *ExportAddressTable;
+ PRM_MODULE_EXPORT_DESCRIPTOR_STRUCT *TempExportDescriptor;
+
+ DEBUG ((DEBUG_INFO, "%a %a - Entry.\n", _DBGMSGID_, __FUNCTION__));
+
+ *ExportDescriptor = NULL;
+
+ if (ImageExportDirectory == NULL ||
+ PeCoffLoaderImageContext == NULL ||
+ PeCoffLoaderImageContext->ImageAddress == 0 ||
+ ExportDescriptor == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ DEBUG ((
+ DEBUG_INFO,
+ " %a %a: %d exported names found in this image.\n",
+ _DBGMSGID_,
+ __FUNCTION__,
+ ImageExportDirectory->NumberOfNames
+ ));
+
+ //
+ // The export name pointer table and export ordinal table form two parallel arrays associated by index.
+ //
+ CurrentImageAddress = PeCoffLoaderImageContext->ImageAddress;
+ ExportAddressTable = (UINT32 *) ((UINTN) CurrentImageAddress + ImageExportDirectory->AddressOfFunctions);
+ ExportNamePointerTable = (UINT32 *) ((UINTN) CurrentImageAddress + ImageExportDirectory->AddressOfNames);
+ OrdinalTable = (UINT16 *) ((UINTN) CurrentImageAddress + ImageExportDirectory->AddressOfNameOrdinals);
+
+ for (Index = 0; Index < ImageExportDirectory->NumberOfNames; Index++) {
+ CurrentExportName = (CONST CHAR8 *) ((UINTN) CurrentImageAddress + ExportNamePointerTable[Index]);
+ DEBUG ((
+ DEBUG_INFO,
+ " %a %a: Export Name[0x%x] - %a.\n",
+ _DBGMSGID_,
+ __FUNCTION__,
+ Index,
+ CurrentExportName
+ ));
+ if (
+ AsciiStrnCmp (
+ PRM_STRING(PRM_MODULE_EXPORT_DESCRIPTOR_NAME),
+ CurrentExportName,
+ AsciiStrLen (PRM_STRING(PRM_MODULE_EXPORT_DESCRIPTOR_NAME))
+ ) == 0) {
+ PrmModuleExportDescriptorOrdinal = OrdinalTable[Index];
+ DEBUG ((
+ DEBUG_INFO,
+ " %a %a: PRM Module Export Descriptor found. Ordinal = %d.\n",
+ _DBGMSGID_,
+ __FUNCTION__,
+ PrmModuleExportDescriptorOrdinal
+ ));
+ if (PrmModuleExportDescriptorOrdinal >= ImageExportDirectory->NumberOfFunctions) {
+ DEBUG ((DEBUG_ERROR, "%a %a: The PRM Module Export Descriptor ordinal value is invalid.\n", _DBGMSGID_, __FUNCTION__));
+ return EFI_NOT_FOUND;
+ }
+ TempExportDescriptor = (PRM_MODULE_EXPORT_DESCRIPTOR_STRUCT *) ((UINTN) CurrentImageAddress + ExportAddressTable[PrmModuleExportDescriptorOrdinal]);
+ if (TempExportDescriptor->Signature == PRM_MODULE_EXPORT_DESCRIPTOR_SIGNATURE) {
+ *ExportDescriptor = TempExportDescriptor;
+ DEBUG ((DEBUG_INFO, " %a %a: PRM Module Export Descriptor found at 0x%x.\n", _DBGMSGID_, __FUNCTION__, (UINTN) ExportDescriptor));
+ } else {
+ DEBUG ((
+ DEBUG_INFO,
+ " %a %a: PRM Module Export Descriptor found at 0x%x but signature check failed.\n",
+ _DBGMSGID_,
+ __FUNCTION__,
+ (UINTN) TempExportDescriptor
+ ));
+ }
+ DEBUG ((DEBUG_INFO, " %a %a: Exiting export iteration since export descriptor found.\n", _DBGMSGID_, __FUNCTION__));
+ return EFI_SUCCESS;
+ }
+ }
+
+ return EFI_NOT_FOUND;
+}
+
+/**
+ Gets a pointer to the export directory in a given PE/COFF image.
+
+ @param[in] Image A pointer to a PE32/COFF image base address that is loaded into memory
+ and already relocated to the memory base address. RVAs in the image given
+ should be valid.
+ @param[in] PeCoffLoaderImageContext A pointer to a PE_COFF_LOADER_IMAGE_CONTEXT structure that contains the
+ PE/COFF image context for the Image given.
+ @param[out] ImageExportDirectory A pointer to a pointer to the export directory found in the Image given.
+
+ @retval EFI_SUCCESS The export directory was found successfully.
+ @retval EFI_INVALID_PARAMETER A required parameter is NULL.
+ @retval EFI_UNSUPPORTED The PE/COFF image given is not supported as a PRM Module.
+ @retval EFI_NOT_FOUND The image export directory could not be found for this image.
+
+**/
+EFI_STATUS
+GetExportDirectoryInPeCoffImage (
+ IN VOID *Image,
+ IN PE_COFF_LOADER_IMAGE_CONTEXT *PeCoffLoaderImageContext,
+ OUT EFI_IMAGE_EXPORT_DIRECTORY **ImageExportDirectory
+ )
+{
+ UINT16 Magic;
+ UINT32 NumberOfRvaAndSizes;
+ EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION OptionalHeaderPtrUnion;
+ EFI_IMAGE_DATA_DIRECTORY *DirectoryEntry;
+ EFI_IMAGE_EXPORT_DIRECTORY *ExportDirectory;
+ EFI_IMAGE_SECTION_HEADER *SectionHeader;
+
+ if (Image == NULL || PeCoffLoaderImageContext == NULL || ImageExportDirectory == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ DirectoryEntry = NULL;
+ ExportDirectory = NULL;
+
+ //
+ // NOTE: For backward compatibility, use the Machine field to identify a PE32/PE32+
+ // image instead of using the Magic field. Some systems might generate a PE32+
+ // image with PE32 magic.
+ //
+ switch (PeCoffLoaderImageContext->Machine) {
+ case EFI_IMAGE_MACHINE_IA32:
+ // Todo: Add EFI_IMAGE_MACHINE_ARMT
+ //
+ // Assume PE32 image with IA32 Machine field.
+ //
+ Magic = EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC;
+ break;
+ case EFI_IMAGE_MACHINE_X64:
+ //
+ // Assume PE32+ image with X64 Machine field
+ //
+ Magic = EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC;
+ break;
+ default:
+ //
+ // For unknown Machine field, use Magic in optional header
+ //
+ DEBUG ((
+ DEBUG_WARN,
+ "%a %a: The machine type for this image is not valid for a PRM module.\n",
+ _DBGMSGID_,
+ __FUNCTION__
+ ));
+ return EFI_UNSUPPORTED;
+ }
+
+ OptionalHeaderPtrUnion.Pe32 = (EFI_IMAGE_NT_HEADERS32 *) (
+ (UINTN) Image +
+ PeCoffLoaderImageContext->PeCoffHeaderOffset
+ );
+
+ //
+ // Check the PE/COFF Header Signature. Determine if the image is valid and/or a TE image.
+ //
+ if (OptionalHeaderPtrUnion.Pe32->Signature != EFI_IMAGE_NT_SIGNATURE) {
+ DEBUG ((DEBUG_ERROR, "%a %a: The PE signature is not valid for the current image.\n", _DBGMSGID_, __FUNCTION__));
+ return EFI_UNSUPPORTED;
+ }
+
+ SectionHeader = (EFI_IMAGE_SECTION_HEADER *) (
+ (UINTN) Image +
+ PeCoffLoaderImageContext->PeCoffHeaderOffset +
+ sizeof (UINT32) +
+ sizeof (EFI_IMAGE_FILE_HEADER) +
+ PeCoffLoaderImageContext->SizeOfHeaders
+ );
+ if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
+ //
+ // Use the PE32 offset to get the Export Directory Entry
+ //
+ NumberOfRvaAndSizes = OptionalHeaderPtrUnion.Pe32->OptionalHeader.NumberOfRvaAndSizes;
+ DirectoryEntry = (EFI_IMAGE_DATA_DIRECTORY *) &(OptionalHeaderPtrUnion.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_EXPORT]);
+ } else if (OptionalHeaderPtrUnion.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
+ //
+ // Use the PE32+ offset get the Export Directory Entry
+ //
+ NumberOfRvaAndSizes = OptionalHeaderPtrUnion.Pe32Plus->OptionalHeader.NumberOfRvaAndSizes;
+ DirectoryEntry = (EFI_IMAGE_DATA_DIRECTORY *) &(OptionalHeaderPtrUnion.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_EXPORT]);
+ } else {
+ return EFI_UNSUPPORTED;
+ }
+
+ if (NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY_EXPORT || DirectoryEntry->VirtualAddress == 0) {
+ //
+ // The export directory is not present
+ //
+ return EFI_NOT_FOUND;
+ } else if (((UINT32) (~0) - DirectoryEntry->VirtualAddress) < DirectoryEntry->Size) {
+ //
+ // The directory address overflows
+ //
+ DEBUG ((DEBUG_ERROR, "%a %a: The export directory entry in this image results in overflow.\n", _DBGMSGID_, __FUNCTION__));
+ return EFI_UNSUPPORTED;
+ } else {
+ DEBUG ((DEBUG_INFO, "%a %a: Export Directory Entry found in the image at 0x%x.\n", _DBGMSGID_, __FUNCTION__, (UINTN) OptionalHeaderPtrUnion.Pe32));
+ DEBUG ((DEBUG_INFO, " %a %a: Directory Entry Virtual Address = 0x%x.\n", _DBGMSGID_, __FUNCTION__, DirectoryEntry->VirtualAddress));
+
+ ExportDirectory = (EFI_IMAGE_EXPORT_DIRECTORY *) ((UINTN) Image + DirectoryEntry->VirtualAddress);
+ DEBUG ((
+ DEBUG_INFO,
+ " %a %a: Export Directory Table found successfully at 0x%x. Name address = 0x%x. Name = %a.\n",
+ _DBGMSGID_,
+ __FUNCTION__,
+ (UINTN) ExportDirectory,
+ ((UINTN) Image + ExportDirectory->Name),
+ (CHAR8 *) ((UINTN) Image + ExportDirectory->Name)
+ ));
+ }
+ *ImageExportDirectory = ExportDirectory;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Returns the image major and image minor version in a given PE/COFF image.
+
+ @param[in] Image A pointer to a PE32/COFF image base address that is loaded into memory
+ and already relocated to the memory base address. RVAs in the image given
+ should be valid.
+ @param[in] PeCoffLoaderImageContext A pointer to a PE_COFF_LOADER_IMAGE_CONTEXT structure that contains the
+ PE/COFF image context for the Image given.
+ @param[out] ImageMajorVersion A pointer to a UINT16 buffer to hold the image major version.
+ @param[out] ImageMinorVersion A pointer to a UINT16 buffer to hold the image minor version.
+
+ @retval EFI_SUCCESS The image version was read successfully.
+ @retval EFI_INVALID_PARAMETER A required parameter is NULL.
+ @retval EFI_UNSUPPORTED The PE/COFF image given is not supported.
+
+**/
+EFI_STATUS
+GetImageVersionInPeCoffImage (
+ IN VOID *Image,
+ IN PE_COFF_LOADER_IMAGE_CONTEXT *PeCoffLoaderImageContext,
+ OUT UINT16 *ImageMajorVersion,
+ OUT UINT16 *ImageMinorVersion
+ )
+{
+ EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION OptionalHeaderPtrUnion;
+ UINT16 Magic;
+
+ DEBUG ((DEBUG_INFO, " %a %a - Entry.\n", _DBGMSGID_, __FUNCTION__));
+
+ if (Image == NULL || PeCoffLoaderImageContext == NULL || ImageMajorVersion == NULL || ImageMinorVersion == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // NOTE: For backward compatibility, use the Machine field to identify a PE32/PE32+
+ // image instead of using the Magic field. Some systems might generate a PE32+
+ // image with PE32 magic.
+ //
+ switch (PeCoffLoaderImageContext->Machine) {
+ case EFI_IMAGE_MACHINE_IA32:
+ // Todo: Add EFI_IMAGE_MACHINE_ARMT
+ //
+ // Assume PE32 image with IA32 Machine field.
+ //
+ Magic = EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC;
+ break;
+ case EFI_IMAGE_MACHINE_X64:
+ //
+ // Assume PE32+ image with X64 Machine field
+ //
+ Magic = EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC;
+ break;
+ default:
+ //
+ // For unknown Machine field, use Magic in optional header
+ //
+ DEBUG ((
+ DEBUG_WARN,
+ "%a %a: The machine type for this image is not valid for a PRM module.\n",
+ _DBGMSGID_,
+ __FUNCTION__
+ ));
+ return EFI_UNSUPPORTED;
+ }
+
+ OptionalHeaderPtrUnion.Pe32 = (EFI_IMAGE_NT_HEADERS32 *) (
+ (UINTN) Image +
+ PeCoffLoaderImageContext->PeCoffHeaderOffset
+ );
+ //
+ // Check the PE/COFF Header Signature. Determine if the image is valid and/or a TE image.
+ //
+ if (OptionalHeaderPtrUnion.Pe32->Signature != EFI_IMAGE_NT_SIGNATURE) {
+ DEBUG ((DEBUG_ERROR, "%a %a: The PE signature is not valid for the current image.\n", _DBGMSGID_, __FUNCTION__));
+ return EFI_UNSUPPORTED;
+ }
+
+ if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
+ //
+ // Use the PE32 offset to get the Export Directory Entry
+ //
+ *ImageMajorVersion = OptionalHeaderPtrUnion.Pe32->OptionalHeader.MajorImageVersion;
+ *ImageMinorVersion = OptionalHeaderPtrUnion.Pe32->OptionalHeader.MinorImageVersion;
+ } else {
+ //
+ // Use the PE32+ offset to get the Export Directory Entry
+ //
+ *ImageMajorVersion = OptionalHeaderPtrUnion.Pe32Plus->OptionalHeader.MajorImageVersion;
+ *ImageMinorVersion = OptionalHeaderPtrUnion.Pe32Plus->OptionalHeader.MinorImageVersion;
+ }
+
+ DEBUG ((DEBUG_INFO, " %a %a - Image Major Version: 0x%02x.\n", _DBGMSGID_, __FUNCTION__, *ImageMajorVersion));
+ DEBUG ((DEBUG_INFO, " %a %a - Image Minor Version: 0x%02x.\n", _DBGMSGID_, __FUNCTION__, *ImageMinorVersion));
+
+ return EFI_SUCCESS;
+}
+
+/**
+ 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.
+
+**/
+STATIC
+PRM_MODULE_IMAGE_CONTEXT_LIST_ENTRY *
+CreateNewPrmModuleImageContextListEntry (
+ VOID
+ )
+{
+ PRM_MODULE_IMAGE_CONTEXT *PrmModuleImageContext;
+ PRM_MODULE_IMAGE_CONTEXT_LIST_ENTRY *PrmModuleImageContextListEntry;
+
+ DEBUG ((DEBUG_INFO, "%a %a - Entry.\n", _DBGMSGID_, __FUNCTION__));
+
+ PrmModuleImageContext = AllocateZeroPool (sizeof (*PrmModuleImageContext));
+ if (PrmModuleImageContext == NULL) {
+ return NULL;
+ }
+ DEBUG ((
+ DEBUG_INFO,
+ " %a %a: Allocated PrmModuleImageContext at 0x%x of size 0x%x bytes.\n",
+ _DBGMSGID_,
+ __FUNCTION__,
+ (UINTN) PrmModuleImageContext,
+ sizeof (*PrmModuleImageContext)
+ ));
+
+ PrmModuleImageContextListEntry = AllocateZeroPool (sizeof (*PrmModuleImageContextListEntry));
+ if (PrmModuleImageContextListEntry == NULL) {
+ FreePool (PrmModuleImageContext);
+ return NULL;
+ }
+ DEBUG ((
+ DEBUG_INFO,
+ " %a %a: Allocated PrmModuleImageContextListEntry at 0x%x of size 0x%x bytes.\n",
+ _DBGMSGID_,
+ __FUNCTION__,
+ (UINTN) PrmModuleImageContextListEntry,
+ sizeof (*PrmModuleImageContextListEntry)
+ ));
+
+ PrmModuleImageContextListEntry->Signature = PRM_MODULE_IMAGE_CONTEXT_LIST_ENTRY_SIGNATURE;
+ PrmModuleImageContextListEntry->Context = PrmModuleImageContext;
+
+ return PrmModuleImageContextListEntry;
+}
+
+/**
+ Discovers all PRM Modules loaded during the DXE boot phase.
+
+ Each PRM Module discovered is placed into a linked list so the list can br processsed in the future.
+
+ @retval EFI_SUCCESS All PRM Modules were discovered successfully.
+ @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.
+
+**/
+EFI_STATUS
+DiscoverPrmModules (
+ VOID
+ )
+{
+ 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;
+
+ DEBUG ((DEBUG_INFO, "%a %a - Entry.\n", _DBGMSGID_, __FUNCTION__));
+
+ 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_, __FUNCTION__));
+ return EFI_NOT_FOUND;
+ }
+
+ for (Index = 0; Index < HandleCount; Index++) {
+ Status = gBS->HandleProtocol (
+ HandleBuffer[Index],
+ &gEfiLoadedImageProtocolGuid,
+ (VOID **) &LoadedImageProtocol
+ );
+ if (EFI_ERROR (Status)) {
+ 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_,
+ __FUNCTION__,
+ (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)) {
+ 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);
+ mPrmHandlerCount += TempPrmModuleImageContext.ExportDescriptor->NumberPrmHandlers;
+ mPrmModuleCount++; // Todo: Match with global variable refactor change in the future
+ DEBUG ((DEBUG_INFO, "%a %a: New PRM Module inserted into list to be processed.\n", _DBGMSGID_, __FUNCTION__));
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Gets the address of an entry in an image export table by ASCII name.
+
+ @param[in] ExportName A pointer to an ASCII name string of the entry name.
+ @param[in] ImageBaseAddress The base address of the PE/COFF image.
+ @param[in] ImageExportDirectory A pointer to the export directory in the image.
+ @param[out] ExportPhysicalAddress A pointer that will be updated with the address of the address of the
+ export entry if found.
+
+ @retval EFI_SUCCESS The export entry was found successfully.
+ @retval EFI_INVALID_PARAMETER A required pointer argument is NULL.
+ @retval EFI_NOT_FOUND An entry with the given ExportName was not found.
+
+**/
+EFI_STATUS
+GetExportEntryAddress (
+ IN CONST CHAR8 *ExportName,
+ IN EFI_PHYSICAL_ADDRESS ImageBaseAddress,
+ IN EFI_IMAGE_EXPORT_DIRECTORY *ImageExportDirectory,
+ OUT EFI_PHYSICAL_ADDRESS *ExportPhysicalAddress
+ )
+{
+ UINTN ExportNameIndex;
+ UINT16 CurrentExportOrdinal;
+ UINT32 *ExportAddressTable;
+ UINT32 *ExportNamePointerTable;
+ UINT16 *OrdinalTable;
+ CONST CHAR8 *ExportNameTablePointerName;
+
+ if (ExportName == NULL || ImageBaseAddress == 0 || ImageExportDirectory == NULL || ExportPhysicalAddress == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+ *ExportPhysicalAddress = 0;
+
+ ExportAddressTable = (UINT32 *) ((UINTN) ImageBaseAddress + ImageExportDirectory->AddressOfFunctions);
+ ExportNamePointerTable = (UINT32 *) ((UINTN) ImageBaseAddress + ImageExportDirectory->AddressOfNames);
+ OrdinalTable = (UINT16 *) ((UINTN) ImageBaseAddress + ImageExportDirectory->AddressOfNameOrdinals);
+
+ for (ExportNameIndex = 0; ExportNameIndex < ImageExportDirectory->NumberOfNames; ExportNameIndex++) {
+ ExportNameTablePointerName = (CONST CHAR8 *) ((UINTN) ImageBaseAddress + ExportNamePointerTable[ExportNameIndex]);
+
+ if (AsciiStrnCmp (ExportName, ExportNameTablePointerName, PRM_HANDLER_NAME_MAXIMUM_LENGTH) == 0) {
+ CurrentExportOrdinal = OrdinalTable[ExportNameIndex];
+
+ ASSERT (CurrentExportOrdinal < ImageExportDirectory->NumberOfFunctions);
+ if (CurrentExportOrdinal >= ImageExportDirectory->NumberOfFunctions) {
+ DEBUG ((DEBUG_ERROR, " %a %a: The export ordinal value is invalid.\n", _DBGMSGID_, __FUNCTION__));
+ break;
+ }
+
+ *ExportPhysicalAddress = (EFI_PHYSICAL_ADDRESS) ((UINTN) ImageBaseAddress + ExportAddressTable[CurrentExportOrdinal]);
+ return EFI_SUCCESS;
+ }
+ }
+
+ return EFI_NOT_FOUND;
+}
+
+/**
+ Processes a list of PRM context entries to build a PRM ACPI table.
+
+ The ACPI table buffer is allocated and the table structure is built inside this function.
+
+ @param[out] PrmAcpiDescriptionTable A pointer to a pointer to a buffer that is allocated within this function
+ and will contain the PRM ACPI table. In case of an error in this function,
+ *PrmAcpiDescriptorTable will be NULL.
+
+ @retval EFI_SUCCESS All PRM Modules were processed to construct the PRM ACPI table successfully.
+ @retval EFI_INVALID_PARAMETER THe parameter PrmAcpiDescriptionTable is NULL.
+ @retval EFI_OUT_OF_RESOURCES Insufficient memory resources to allocate the PRM ACPI table boot services
+ memory data buffer.
+
+**/
+EFI_STATUS
+ProcessPrmModules (
+ OUT PRM_ACPI_DESCRIPTION_TABLE **PrmAcpiDescriptionTable
+ )
+{
+ EFI_IMAGE_EXPORT_DIRECTORY *CurrentImageExportDirectory;
+ PRM_MODULE_EXPORT_DESCRIPTOR_STRUCT *CurrentExportDescriptorStruct;
+ LIST_ENTRY *Link;
+ PRM_ACPI_DESCRIPTION_TABLE *PrmAcpiTable;
+ PRM_MODULE_IMAGE_CONTEXT_LIST_ENTRY *TempListEntry;
+ CONST CHAR8 *CurrentExportDescriptorHandlerName;
+
+ PRM_CONTEXT_BUFFER *CurrentContextBuffer;
+ PRM_MODULE_CONTEXT_BUFFERS *CurrentModuleContextBuffers;
+ PRM_MODULE_INFORMATION_STRUCT *CurrentModuleInfoStruct;
+ PRM_HANDLER_INFORMATION_STRUCT *CurrentHandlerInfoStruct;
+
+ EFI_STATUS Status;
+ EFI_PHYSICAL_ADDRESS CurrentImageAddress;
+ UINTN HandlerIndex;
+ UINT32 PrmAcpiDescriptionTableBufferSize;
+
+ UINT64 HandlerPhysicalAddress;
+
+ DEBUG ((DEBUG_INFO, "%a %a - Entry.\n", _DBGMSGID_, __FUNCTION__));
+
+ if (PrmAcpiDescriptionTable == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+ Link = NULL;
+ *PrmAcpiDescriptionTable = NULL;
+
+ DEBUG ((DEBUG_INFO, " %a %a: %d total PRM modules to process.\n", _DBGMSGID_, __FUNCTION__, mPrmModuleCount));
+ DEBUG ((DEBUG_INFO, " %a %a: %d total PRM handlers to process.\n", _DBGMSGID_, __FUNCTION__, mPrmHandlerCount));
+
+ PrmAcpiDescriptionTableBufferSize = (OFFSET_OF (PRM_ACPI_DESCRIPTION_TABLE, PrmModuleInfoStructure) +
+ (OFFSET_OF (PRM_MODULE_INFORMATION_STRUCT, HandlerInfoStructure) * mPrmModuleCount) +
+ (sizeof (PRM_HANDLER_INFORMATION_STRUCT) * mPrmHandlerCount)
+ );
+ DEBUG ((DEBUG_INFO, " %a %a: Total PRM ACPI table size: 0x%x.\n", _DBGMSGID_, __FUNCTION__, PrmAcpiDescriptionTableBufferSize));
+
+ PrmAcpiTable = AllocateZeroPool ((UINTN) PrmAcpiDescriptionTableBufferSize);
+ if (PrmAcpiTable == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ PrmAcpiTable->Header.Signature = PRM_TABLE_SIGNATURE;
+ PrmAcpiTable->Header.Length = PrmAcpiDescriptionTableBufferSize;
+ PrmAcpiTable->Header.Revision = PRM_TABLE_REVISION;
+ PrmAcpiTable->Header.Checksum = 0x0;
+ CopyMem (&PrmAcpiTable->Header.OemId, PcdGetPtr (PcdAcpiDefaultOemId), sizeof (PrmAcpiTable->Header.OemId));
+ PrmAcpiTable->Header.OemTableId = PcdGet64 (PcdAcpiDefaultOemTableId);
+ PrmAcpiTable->Header.OemRevision = PcdGet32 (PcdAcpiDefaultOemRevision);
+ PrmAcpiTable->Header.CreatorId = PcdGet32 (PcdAcpiDefaultCreatorId);
+ PrmAcpiTable->Header.CreatorRevision = PcdGet32 (PcdAcpiDefaultCreatorRevision);
+ PrmAcpiTable->PrmModuleInfoOffset = OFFSET_OF (PRM_ACPI_DESCRIPTION_TABLE, PrmModuleInfoStructure);
+ PrmAcpiTable->PrmModuleInfoCount = mPrmModuleCount;
+
+ //
+ // Iterate across all PRM Modules on the list
+ //
+ CurrentModuleInfoStruct = &PrmAcpiTable->PrmModuleInfoStructure[0];
+ EFI_LIST_FOR_EACH(Link, &mPrmModuleList)
+ {
+ TempListEntry = CR(Link, PRM_MODULE_IMAGE_CONTEXT_LIST_ENTRY, Link, PRM_MODULE_IMAGE_CONTEXT_LIST_ENTRY_SIGNATURE);
+ CurrentImageAddress = TempListEntry->Context->PeCoffImageContext.ImageAddress;
+ CurrentImageExportDirectory = TempListEntry->Context->ExportDirectory;
+ CurrentExportDescriptorStruct = TempListEntry->Context->ExportDescriptor;
+
+ DEBUG ((
+ DEBUG_INFO,
+ " %a %a: PRM Module - %a with %d handlers.\n",
+ _DBGMSGID_,
+ __FUNCTION__,
+ (CHAR8 *) ((UINTN) CurrentImageAddress + CurrentImageExportDirectory->Name),
+ CurrentExportDescriptorStruct->NumberPrmHandlers
+ ));
+
+ CurrentModuleInfoStruct->StructureRevision = PRM_MODULE_INFORMATION_STRUCT_REVISION;
+ CurrentModuleInfoStruct->StructureLength = (
+ OFFSET_OF (PRM_MODULE_INFORMATION_STRUCT, HandlerInfoStructure) +
+ (CurrentExportDescriptorStruct->NumberPrmHandlers * sizeof (PRM_HANDLER_INFORMATION_STRUCT))
+ );
+ CopyGuid (&CurrentModuleInfoStruct->Identifier, &CurrentExportDescriptorStruct->ModuleGuid);
+ CurrentModuleInfoStruct->HandlerCount = (UINT32) CurrentExportDescriptorStruct->NumberPrmHandlers;
+ CurrentModuleInfoStruct->HandlerInfoOffset = OFFSET_OF (PRM_MODULE_INFORMATION_STRUCT, HandlerInfoStructure);
+
+ CurrentModuleInfoStruct->MajorRevision = 0;
+ CurrentModuleInfoStruct->MinorRevision = 0;
+ Status = GetImageVersionInPeCoffImage (
+ (VOID *) (UINTN) CurrentImageAddress,
+ &TempListEntry->Context->PeCoffImageContext,
+ &CurrentModuleInfoStruct->MajorRevision,
+ &CurrentModuleInfoStruct->MinorRevision
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ Status = GetExportEntryAddress (
+ PRM_STRING (PRM_MODULE_UPDATE_LOCK_DESCRIPTOR_NAME),
+ CurrentImageAddress,
+ CurrentImageExportDirectory,
+ (EFI_PHYSICAL_ADDRESS *) &(CurrentModuleInfoStruct->ModuleUpdateLock)
+ );
+ ASSERT_EFI_ERROR (Status);
+ if (!EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_INFO,
+ " %a %a: Found PRM module update lock physical address at 0x%016x.\n",
+ _DBGMSGID_,
+ __FUNCTION__,
+ CurrentModuleInfoStruct->ModuleUpdateLock
+ ));
+ }
+
+ // It is currently valid for a PRM module not to use a context buffer
+ Status = GetModuleContextBuffers (
+ ByModuleGuid,
+ &CurrentModuleInfoStruct->Identifier,
+ &CurrentModuleContextBuffers
+ );
+ ASSERT (!EFI_ERROR (Status) || Status == EFI_NOT_FOUND);
+ if (!EFI_ERROR (Status) && CurrentModuleContextBuffers != NULL) {
+ CurrentModuleInfoStruct->RuntimeMmioRanges = (UINT64) (UINTN) CurrentModuleContextBuffers->RuntimeMmioRanges;
+ }
+
+ //
+ // Iterate across all PRM handlers in the PRM Module
+ //
+ for (HandlerIndex = 0; HandlerIndex < CurrentExportDescriptorStruct->NumberPrmHandlers; HandlerIndex++) {
+ CurrentHandlerInfoStruct = &(CurrentModuleInfoStruct->HandlerInfoStructure[HandlerIndex]);
+
+ CurrentHandlerInfoStruct->StructureRevision = PRM_HANDLER_INFORMATION_STRUCT_REVISION;
+ CurrentHandlerInfoStruct->StructureLength = sizeof (PRM_HANDLER_INFORMATION_STRUCT);
+ CopyGuid (
+ &CurrentHandlerInfoStruct->Identifier,
+ &CurrentExportDescriptorStruct->PrmHandlerExportDescriptors[HandlerIndex].PrmHandlerGuid
+ );
+
+ CurrentExportDescriptorHandlerName = (CONST CHAR8 *) CurrentExportDescriptorStruct->PrmHandlerExportDescriptors[HandlerIndex].PrmHandlerName;
+
+ Status = GetContextBuffer (
+ &CurrentHandlerInfoStruct->Identifier,
+ CurrentModuleContextBuffers,
+ &CurrentContextBuffer
+ );
+ if (!EFI_ERROR (Status)) {
+ CurrentHandlerInfoStruct->PrmContextBuffer = (UINT64) CurrentContextBuffer;
+ }
+
+ Status = GetExportEntryAddress (
+ CurrentExportDescriptorHandlerName,
+ CurrentImageAddress,
+ CurrentImageExportDirectory,
+ &HandlerPhysicalAddress
+ );
+ ASSERT_EFI_ERROR (Status);
+ if (!EFI_ERROR (Status)) {
+ CurrentHandlerInfoStruct->PhysicalAddress = HandlerPhysicalAddress;
+ DEBUG ((
+ DEBUG_INFO,
+ " %a %a: Found %a handler physical address at 0x%016x.\n",
+ _DBGMSGID_,
+ __FUNCTION__,
+ CurrentExportDescriptorHandlerName,
+ CurrentHandlerInfoStruct->PhysicalAddress
+ ));
+ }
+ }
+ CurrentModuleInfoStruct = (PRM_MODULE_INFORMATION_STRUCT *) ((UINTN) CurrentModuleInfoStruct + CurrentModuleInfoStruct->StructureLength);
+ }
+ *PrmAcpiDescriptionTable = PrmAcpiTable;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Publishes the PRM ACPI table (PRMT).
+
+ @param[in] PrmAcpiDescriptionTable A pointer to a buffer with a completely populated and valid PRM
+ ACPI description table.
+
+ @retval EFI_SUCCESS The PRM ACPI was installed successfully.
+ @retval EFI_INVALID_PARAMETER THe parameter PrmAcpiDescriptionTable is NULL or the table signature
+ in the table provided is invalid.
+ @retval EFI_NOT_FOUND The protocol gEfiAcpiTableProtocolGuid could not be found.
+ @retval EFI_OUT_OF_RESOURCES Insufficient memory resources to allocate the PRM ACPI table buffer.
+
+**/
+EFI_STATUS
+PublishPrmAcpiTable (
+ IN PRM_ACPI_DESCRIPTION_TABLE *PrmAcpiDescriptionTable
+ )
+{
+ EFI_STATUS Status;
+ EFI_ACPI_TABLE_PROTOCOL *AcpiTableProtocol;
+ UINTN TableKey;
+
+ if (PrmAcpiDescriptionTable == NULL || PrmAcpiDescriptionTable->Header.Signature != PRM_TABLE_SIGNATURE) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = gBS->LocateProtocol (&gEfiAcpiTableProtocolGuid, NULL, (VOID **) &AcpiTableProtocol);
+ if (!EFI_ERROR (Status)) {
+ TableKey = 0;
+ //
+ // Publish the PRM ACPI table. The table checksum will be computed during installation.
+ //
+ Status = AcpiTableProtocol->InstallAcpiTable (
+ AcpiTableProtocol,
+ PrmAcpiDescriptionTable,
+ PrmAcpiDescriptionTable->Header.Length,
+ &TableKey
+ );
+ if (!EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_INFO, "%a %a: The PRMT ACPI table was installed successfully.\n", _DBGMSGID_, __FUNCTION__));
+ }
+ }
+ ASSERT_EFI_ERROR (Status);
+
+ return Status;
+}
+
+/**
+ The PRM Loader END_OF_DXE protocol notification event handler.
+
+ All PRM Modules that are eligible for dispatch should have been loaded the DXE Dispatcher at the
+ time of this function invocation.
+
+ The main responsibilities of the PRM Loader are executed from this function which include 3 phases:
+ 1.) Disover PRM Modules - Find all PRM modules loaded during DXE dispatch and insert a PRM Module
+ Context entry into a linked list to be handed off to phase 2.
+ 2.) Process PRM Modules - Build a GUID to PRM handler mapping for each module that is described in the
+ PRM ACPI table so the OS can resolve a PRM Handler GUID to the corresponding PRM Handler physical address.
+ 3.) Publish PRM ACPI Table - Publish the PRM ACPI table with the information gathered in the phase 2.
+
+ @param[in] Event Event whose notification function is being invoked.
+ @param[in] Context The pointer to the notification function's context,
+ which is implementation-dependent.
+
+ @retval EFI_SUCCESS The function executed successfully
+
+**/
+EFI_STATUS
+EFIAPI
+PrmLoaderEndOfDxeNotification (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ EFI_STATUS Status;
+ PRM_ACPI_DESCRIPTION_TABLE *PrmAcpiDescriptionTable;
+
+ DEBUG ((DEBUG_INFO, "%a %a - Entry.\n", _DBGMSGID_, __FUNCTION__));
+
+ InitializeListHead (&mPrmModuleList);
+
+ Status = DiscoverPrmModules ();
+ ASSERT_EFI_ERROR (Status);
+
+ Status = ProcessPrmModules (&PrmAcpiDescriptionTable);
+ ASSERT_EFI_ERROR (Status);
+
+ Status = PublishPrmAcpiTable (PrmAcpiDescriptionTable);
+ ASSERT_EFI_ERROR (Status);
+
+ if (PrmAcpiDescriptionTable != NULL) {
+ FreePool (PrmAcpiDescriptionTable);
+ }
+ gBS->CloseEvent (Event);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ The entry point for this module.
+
+ @param ImageHandle The firmware allocated handle for the EFI image.
+ @param SystemTable A pointer to the EFI System Table.
+
+ @retval EFI_SUCCESS The entry point is executed successfully.
+ @retval Others An error occurred when executing this entry point.
+
+**/
+EFI_STATUS
+EFIAPI
+PrmLoaderEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+ EFI_EVENT EndOfDxeEvent;
+
+ DEBUG ((DEBUG_INFO, "%a %a - Entry.\n", _DBGMSGID_, __FUNCTION__));
+
+ //
+ // Discover and process installed PRM modules at the End of DXE
+ // The PRM ACPI table is published if one or PRM modules are discovered
+ //
+ Status = gBS->CreateEventEx(
+ EVT_NOTIFY_SIGNAL,
+ TPL_CALLBACK,
+ PrmLoaderEndOfDxeNotification,
+ NULL,
+ &gEfiEndOfDxeEventGroupGuid,
+ &EndOfDxeEvent
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a %a: EndOfDxe callback registration failed! %r.\n", _DBGMSGID_, __FUNCTION__, Status));
+ ASSERT_EFI_ERROR (Status);
+ }
+
+ return EFI_SUCCESS;
+}
diff --git a/PrmPkg/PrmLoaderDxe/PrmLoaderDxe.inf b/PrmPkg/PrmLoaderDxe/PrmLoaderDxe.inf
new file mode 100644
index 0000000000..643e1a7989
--- /dev/null
+++ b/PrmPkg/PrmLoaderDxe/PrmLoaderDxe.inf
@@ -0,0 +1,59 @@
+## @file
+# PRM Loader Driver
+#
+# This driver discovers PRM Modules loaded in memory and places those modules and the
+# PRM handlers within those modules into a PRMT ACPI table such that the handlers are
+# made available for invocation in the OS.
+#
+# Copyright (c) Microsoft Corporation
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = PrmLoaderDxe
+ FILE_GUID = 226A500A-E14F-414A-A956-40E5762D3D1E
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+ ENTRY_POINT = PrmLoaderEntryPoint
+
+[Sources]
+ PrmAcpiTable.h
+ PrmLoader.h
+ PrmLoaderDxe.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ PrmPkg/PrmPkg.dec
+
+[Guids]
+ gEfiEndOfDxeEventGroupGuid
+
+[LibraryClasses]
+ BaseLib
+ BaseMemoryLib
+ DebugLib
+ MemoryAllocationLib
+ PeCoffLib
+ PrmContextBufferLib
+ UefiBootServicesTableLib
+ UefiDriverEntryPoint
+ UefiLib
+
+[Pcd]
+ gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiDefaultOemId ## CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiDefaultOemTableId ## CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiDefaultOemRevision ## CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiDefaultCreatorId ## CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiDefaultCreatorRevision ## CONSUMES
+
+[Protocols]
+ gEfiAcpiTableProtocolGuid
+ gEfiLoadedImageProtocolGuid
+ gPrmConfigProtocolGuid
+
+[Depex]
+ TRUE