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.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#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.
+# +# 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.
+ Copyright (c) Microsoft Corporation + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef PRMT_ACPI_TABLE_H_ +#define PRMT_ACPI_TABLE_H_ + +#include +#include + +#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 +#include + +#include + +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +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