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