mirror of https://github.com/acidanthera/audk.git
378 lines
16 KiB
C
378 lines
16 KiB
C
/** @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;
|
|
}
|