| /** @file | |
| This file contains the implementation for a Platform Runtime Mechanism (PRM) | |
| loader driver. | |
| Copyright (c) 2020, Intel Corporation. All rights reserved.<BR> | |
| Copyright (c) Microsoft Corporation | |
| SPDX-License-Identifier: BSD-2-Clause-Patent | |
| **/ | |
| #include "PrmAcpiTable.h" | |
| #include <Guid/ZeroGuid.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/PrmModuleDiscoveryLib.h> | |
| #include <Library/PrmPeCoffLib.h> | |
| #include <Library/UefiBootServicesTableLib.h> | |
| #include <Library/UefiLib.h> | |
| #include <Protocol/AcpiTable.h> | |
| #include <Protocol/PrmConfig.h> | |
| #include <PrmContextBuffer.h> | |
| #include <PrmMmio.h> | |
| #define _DBGMSGID_ "[PRMLOADER]" | |
| UINTN mPrmHandlerCount; | |
| UINTN mPrmModuleCount; | |
| /** | |
| 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; | |
| PRM_ACPI_DESCRIPTION_TABLE *PrmAcpiTable; | |
| PRM_MODULE_IMAGE_CONTEXT *CurrentPrmModuleImageContext; | |
| CONST CHAR8 *CurrentExportDescriptorHandlerName; | |
| ACPI_PARAMETER_BUFFER_DESCRIPTOR *CurrentModuleAcpiParamDescriptors; | |
| 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 AcpiParamIndex; | |
| UINTN HandlerIndex; | |
| UINT32 PrmAcpiDescriptionTableBufferSize; | |
| UINT64 HandlerPhysicalAddress; | |
| DEBUG ((DEBUG_INFO, "%a %a - Entry.\n", _DBGMSGID_, __func__)); | |
| if (PrmAcpiDescriptionTable == NULL) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| *PrmAcpiDescriptionTable = NULL; | |
| // | |
| // The platform DSC GUID must be set to a non-zero value | |
| // | |
| if (CompareGuid (&gEdkiiDscPlatformGuid, &gZeroGuid)) { | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| " %a %a: The Platform GUID in the DSC file must be set to a unique non-zero value.\n", | |
| _DBGMSGID_, | |
| __func__ | |
| )); | |
| ASSERT (!CompareGuid (&gEdkiiDscPlatformGuid, &gZeroGuid)); | |
| } | |
| DEBUG ((DEBUG_INFO, " %a %a: %d total PRM modules to process.\n", _DBGMSGID_, __func__, mPrmModuleCount)); | |
| DEBUG ((DEBUG_INFO, " %a %a: %d total PRM handlers to process.\n", _DBGMSGID_, __func__, mPrmHandlerCount)); | |
| PrmAcpiDescriptionTableBufferSize = (UINT32)(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_, __func__, 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); | |
| CopyGuid (&PrmAcpiTable->PrmPlatformGuid, &gEdkiiDscPlatformGuid); | |
| PrmAcpiTable->PrmModuleInfoOffset = OFFSET_OF (PRM_ACPI_DESCRIPTION_TABLE, PrmModuleInfoStructure); | |
| PrmAcpiTable->PrmModuleInfoCount = (UINT32)mPrmModuleCount; | |
| // | |
| // Iterate across all PRM Modules on the list | |
| // | |
| CurrentModuleInfoStruct = &PrmAcpiTable->PrmModuleInfoStructure[0]; | |
| for ( | |
| CurrentPrmModuleImageContext = NULL, Status = GetNextPrmModuleEntry (&CurrentPrmModuleImageContext); | |
| !EFI_ERROR (Status); | |
| Status = GetNextPrmModuleEntry (&CurrentPrmModuleImageContext)) | |
| { | |
| CurrentImageAddress = CurrentPrmModuleImageContext->PeCoffImageContext.ImageAddress; | |
| CurrentImageExportDirectory = CurrentPrmModuleImageContext->ExportDirectory; | |
| CurrentExportDescriptorStruct = CurrentPrmModuleImageContext->ExportDescriptor; | |
| CurrentModuleAcpiParamDescriptors = NULL; | |
| DEBUG (( | |
| DEBUG_INFO, | |
| " %a %a: PRM Module - %a with %d handlers.\n", | |
| _DBGMSGID_, | |
| __func__, | |
| (CHAR8 *)((UINTN)CurrentImageAddress + CurrentImageExportDirectory->Name), | |
| CurrentExportDescriptorStruct->Header.NumberPrmHandlers | |
| )); | |
| CurrentModuleInfoStruct->StructureRevision = PRM_MODULE_INFORMATION_STRUCT_REVISION; | |
| CurrentModuleInfoStruct->StructureLength = ( | |
| OFFSET_OF (PRM_MODULE_INFORMATION_STRUCT, HandlerInfoStructure) + | |
| (CurrentExportDescriptorStruct->Header.NumberPrmHandlers * sizeof (PRM_HANDLER_INFORMATION_STRUCT)) | |
| ); | |
| CopyGuid (&CurrentModuleInfoStruct->Identifier, &CurrentExportDescriptorStruct->Header.ModuleGuid); | |
| CurrentModuleInfoStruct->HandlerCount = (UINT32)CurrentExportDescriptorStruct->Header.NumberPrmHandlers; | |
| CurrentModuleInfoStruct->HandlerInfoOffset = OFFSET_OF (PRM_MODULE_INFORMATION_STRUCT, HandlerInfoStructure); | |
| CurrentModuleInfoStruct->MajorRevision = 0; | |
| CurrentModuleInfoStruct->MinorRevision = 0; | |
| Status = GetImageVersionInPeCoffImage ( | |
| (VOID *)(UINTN)CurrentImageAddress, | |
| &CurrentPrmModuleImageContext->PeCoffImageContext, | |
| &CurrentModuleInfoStruct->MajorRevision, | |
| &CurrentModuleInfoStruct->MinorRevision | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| // It is currently valid for a PRM module not to use a context buffer | |
| Status = GetModuleContextBuffers ( | |
| ByModuleGuid, | |
| &CurrentModuleInfoStruct->Identifier, | |
| (CONST PRM_MODULE_CONTEXT_BUFFERS **)&CurrentModuleContextBuffers | |
| ); | |
| ASSERT (!EFI_ERROR (Status) || Status == EFI_NOT_FOUND); | |
| if (!EFI_ERROR (Status) && (CurrentModuleContextBuffers != NULL)) { | |
| CurrentModuleInfoStruct->RuntimeMmioRanges = (UINT64)(UINTN)CurrentModuleContextBuffers->RuntimeMmioRanges; | |
| CurrentModuleAcpiParamDescriptors = CurrentModuleContextBuffers->AcpiParameterBufferDescriptors; | |
| } | |
| // | |
| // Iterate across all PRM handlers in the PRM Module | |
| // | |
| for (HandlerIndex = 0; HandlerIndex < CurrentExportDescriptorStruct->Header.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, | |
| (CONST PRM_CONTEXT_BUFFER **)&CurrentContextBuffer | |
| ); | |
| if (!EFI_ERROR (Status)) { | |
| CurrentHandlerInfoStruct->StaticDataBuffer = (UINT64)(UINTN)CurrentContextBuffer->StaticDataBuffer; | |
| } | |
| 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_, | |
| __func__, | |
| CurrentExportDescriptorHandlerName, | |
| CurrentHandlerInfoStruct->PhysicalAddress | |
| )); | |
| } | |
| // | |
| // Update the handler ACPI parameter buffer address if applicable | |
| // | |
| if (CurrentModuleAcpiParamDescriptors != NULL) { | |
| for (AcpiParamIndex = 0; AcpiParamIndex < CurrentModuleContextBuffers->AcpiParameterBufferDescriptorCount; AcpiParamIndex++) { | |
| if (CompareGuid (&CurrentModuleAcpiParamDescriptors[AcpiParamIndex].HandlerGuid, &CurrentHandlerInfoStruct->Identifier)) { | |
| CurrentHandlerInfoStruct->AcpiParameterBuffer = (UINT64)(UINTN)( | |
| CurrentModuleAcpiParamDescriptors[AcpiParamIndex].AcpiParameterBufferAddress | |
| ); | |
| } | |
| } | |
| } | |
| } | |
| 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_, __func__)); | |
| } | |
| } | |
| 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. | |
| **/ | |
| VOID | |
| 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_, __func__)); | |
| Status = DiscoverPrmModules (&mPrmModuleCount, &mPrmHandlerCount); | |
| 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); | |
| } | |
| /** | |
| 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_, __func__)); | |
| // | |
| // 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_, __func__, Status)); | |
| ASSERT_EFI_ERROR (Status); | |
| } | |
| return EFI_SUCCESS; | |
| } |