diff options
Diffstat (limited to 'MdeModulePkg')
-rw-r--r-- | MdeModulePkg/Core/Pei/Dispatcher/Dispatcher.c | 403 | ||||
-rw-r--r-- | MdeModulePkg/Core/Pei/PeiMain.h | 70 | ||||
-rw-r--r-- | MdeModulePkg/Core/Pei/PeiMain.inf | 7 | ||||
-rw-r--r-- | MdeModulePkg/Core/Pei/PeiMain/PeiMain.c | 3 | ||||
-rw-r--r-- | MdeModulePkg/Include/Guid/DelayedDispatch.h | 47 | ||||
-rw-r--r-- | MdeModulePkg/MdeModulePkg.dec | 11 |
6 files changed, 540 insertions, 1 deletions
diff --git a/MdeModulePkg/Core/Pei/Dispatcher/Dispatcher.c b/MdeModulePkg/Core/Pei/Dispatcher/Dispatcher.c index 79ff8d1cf9..6778b2d3e3 100644 --- a/MdeModulePkg/Core/Pei/Dispatcher/Dispatcher.c +++ b/MdeModulePkg/Core/Pei/Dispatcher/Dispatcher.c @@ -3,14 +3,382 @@ Copyright (c) 2006 - 2024, Intel Corporation. All rights reserved.<BR>
(C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>
+Copyright (c) Microsoft Corporation.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "PeiMain.h"
+//
+// Utility global variables
+//
+
+/**
+ DelayedDispatchDispatcher
+
+ Delayed Dispach cycle (ie one pass) through each entry, calling functions when their
+ time has expired. When DelayedGroupId is specified, if there are any of the specified entries
+ in the dispatch queue during dispatch, repeat the DelayedDispatch cycle.
+
+ @param DelayedDispatchTable Pointer to dispatch table
+ @param OPTIONAL DelayedGroupId used to insure particular time is met.
+
+ @return BOOLEAN
+**/
+BOOLEAN
+DelayedDispatchDispatcher (
+ IN DELAYED_DISPATCH_TABLE *DelayedDispatchTable,
+ IN EFI_GUID *DelayedGroupId OPTIONAL
+ );
+
+/**
+ DelayedDispatch End of PEI callback function. Insure that all of the delayed dispatch
+ entries are complete before exiting PEI.
+
+ @param[in] PeiServices - Pointer to PEI Services Table.
+ @param[in] NotifyDesc - Pointer to the descriptor for the Notification event that
+ caused this function to execute.
+ @param[in] Ppi - Pointer to the PPI data associated with this function.
+
+ @retval EFI_STATUS - Always return EFI_SUCCESS
+**/
+EFI_STATUS
+EFIAPI
+PeiDelayedDispatchOnEndOfPei (
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDesc,
+ IN VOID *Ppi
+ );
+
+EFI_DELAYED_DISPATCH_PPI mDelayedDispatchPpi = { PeiDelayedDispatchRegister, PeiDelayedDispatchWaitOnEvent };
+EFI_PEI_PPI_DESCRIPTOR mDelayedDispatchDesc = {
+ (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
+ &gEfiPeiDelayedDispatchPpiGuid,
+ &mDelayedDispatchPpi
+};
+
+EFI_PEI_NOTIFY_DESCRIPTOR mDelayedDispatchNotifyDesc = {
+ EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST,
+ &gEfiEndOfPeiSignalPpiGuid,
+ PeiDelayedDispatchOnEndOfPei
+};
+
+/**
+ Helper function to look up DELAYED_DISPATCH_TABLE published in HOB.
+
+ @return Pointer to DELAYED_DISPATCH_TABLE from HOB
+**/
+DELAYED_DISPATCH_TABLE *
+GetDelayedDispatchTable (
+ VOID
+ )
+{
+ EFI_HOB_GUID_TYPE *GuidHob;
+
+ GuidHob = GetFirstGuidHob (&gEfiDelayedDispatchTableGuid);
+ if (GuidHob == NULL) {
+ // There is something off about the build if this happens. We do want to
+ // assert here to catch it during development.
+ DEBUG ((DEBUG_ERROR, "%a - Delayed Dispatch Hob not available.\n", __func__));
+ ASSERT (FALSE);
+ return NULL;
+ }
+
+ return (DELAYED_DISPATCH_TABLE *)GET_GUID_HOB_DATA (GuidHob);
+}
+
+/**
+ Register a callback to be called after a minimum delay has occurred.
+
+ This service is the single member function of the EFI_DELAYED_DISPATCH_PPI
+
+ @param[in] This Pointer to the EFI_DELAYED_DISPATCH_PPI instance
+ @param[in] Function Function to call back
+ @param[in] Context Context data
+ @param[in] DelayedGroupId GUID for this Delayed Dispatch request.
+ @param[in] Delay Delay interval
+
+ @retval EFI_SUCCESS Function successfully loaded
+ @retval EFI_INVALID_PARAMETER One of the Arguments is not supported
+ @retval EFI_OUT_OF_RESOURCES No more entries
+
+**/
+EFI_STATUS
+EFIAPI
+PeiDelayedDispatchRegister (
+ IN EFI_DELAYED_DISPATCH_PPI *This,
+ IN EFI_DELAYED_DISPATCH_FUNCTION Function,
+ IN UINT64 Context,
+ IN EFI_GUID *DelayedGroupId OPTIONAL,
+ IN UINT32 Delay
+ )
+{
+ DELAYED_DISPATCH_TABLE *DelayedDispatchTable;
+ DELAYED_DISPATCH_ENTRY *Entry;
+ EFI_STATUS Status;
+
+ // Check input parameters
+ if ((Function == NULL) || (Delay > FixedPcdGet32 (PcdDelayedDispatchMaxDelayUs)) || (This == NULL)) {
+ DEBUG ((DEBUG_ERROR, "%a Invalid parameter. Function: %Lx, Delay: %u, This: %p\n", __func__, (UINT64)(UINTN)Function, Delay, This));
+ Status = EFI_INVALID_PARAMETER;
+ goto Exit;
+ }
+
+ // Get delayed dispatch table
+ DelayedDispatchTable = GetDelayedDispatchTable ();
+ if (DelayedDispatchTable == NULL) {
+ DEBUG ((DEBUG_ERROR, "%a Unable to locate dispatch table\n", __func__));
+ Status = EFI_UNSUPPORTED;
+ goto Exit;
+ }
+
+ // Check for available entry slots
+ ASSERT (DelayedDispatchTable->Count <= DELAYED_DISPATCH_MAX_ENTRIES);
+ if (DelayedDispatchTable->Count == DELAYED_DISPATCH_MAX_ENTRIES) {
+ DEBUG ((DEBUG_ERROR, "%a Too many entries requested\n", __func__));
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Exit;
+ }
+
+ Entry = &DelayedDispatchTable->Entry[DelayedDispatchTable->Count];
+ Entry->Function = Function;
+ Entry->Context = Context;
+ Status = SafeUint64Add (GET_TIME_IN_US (), Delay, &Entry->DispatchTime);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a Delay overflow\n", __func__));
+ Status = EFI_INVALID_PARAMETER;
+ goto Exit;
+ }
+
+ if (DelayedGroupId == NULL) {
+ ZeroMem (&Entry->DelayedGroupId, sizeof (EFI_GUID));
+ } else {
+ CopyGuid (&Entry->DelayedGroupId, DelayedGroupId);
+ }
+
+ Entry->MicrosecondDelay = Delay;
+ DelayedDispatchTable->Count++;
+
+ DEBUG ((DEBUG_INFO, "%a Adding dispatch Entry\n", __func__));
+ DEBUG ((DEBUG_INFO, " Requested Delay = %d\n", Delay));
+ DEBUG ((DEBUG_INFO, " Trigger Time = %d\n", Entry->DispatchTime));
+ DEBUG ((DEBUG_INFO, " Context = 0x%016lx\n", Entry->Context));
+ DEBUG ((DEBUG_INFO, " Function = %Lx\n", (UINT64)(UINTN)Entry->Function));
+ DEBUG ((DEBUG_INFO, " DelayedGroupId = %g\n", &Entry->DelayedGroupId));
+
+ if (Delay == 0) {
+ // Force early dispatch point
+ DelayedDispatchDispatcher (DelayedDispatchTable, NULL);
+ }
+
+ Status = EFI_SUCCESS;
+
+Exit:
+ return Status;
+}
+
+/**
+ DelayedDispatchDispatcher
+
+ Delayed Dispach cycle (ie one pass) through each entry, calling functions when their
+ time has expired. When DelayedGroupId is specified, if there are any of the specified entries
+ in the dispatch queue during dispatch, repeat the DelayedDispatch cycle.
+
+ @param DelayedDispatchTable Pointer to dispatch table
+ @param OPTIONAL DelayedGroupId used to insure particular time is met.
+
+ @return BOOLEAN
+**/
+BOOLEAN
+DelayedDispatchDispatcher (
+ IN DELAYED_DISPATCH_TABLE *DelayedDispatchTable,
+ IN EFI_GUID *DelayedGroupId OPTIONAL
+ )
+{
+ BOOLEAN Dispatched;
+ UINT64 TimeCurrent;
+ UINT64 MaxDispatchTime;
+ UINTN Index1;
+ BOOLEAN DelayedGroupIdPresent;
+ DELAYED_DISPATCH_ENTRY *Entry;
+ EFI_STATUS Status;
+
+ Dispatched = FALSE;
+ DelayedGroupIdPresent = TRUE;
+ Status = SafeUint64Add (GET_TIME_IN_US (), FixedPcdGet32 (PcdDelayedDispatchCompletionTimeoutUs), &MaxDispatchTime);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a Delay overflow\n", __func__));
+ return FALSE;
+ }
+
+ while ((DelayedDispatchTable->Count > 0) && (DelayedGroupIdPresent)) {
+ DelayedGroupIdPresent = FALSE;
+ DelayedDispatchTable->DispCount++;
+
+ // If dispatching is messed up, clear DelayedDispatchTable and exit.
+ TimeCurrent = GET_TIME_IN_US ();
+ if (TimeCurrent > MaxDispatchTime) {
+ DEBUG ((DEBUG_ERROR, "%a - DelayedDispatch Completion timeout!\n", __func__));
+ ReportStatusCode ((EFI_ERROR_MAJOR | EFI_ERROR_CODE), (EFI_SOFTWARE_PEI_CORE | EFI_SW_EC_ABORTED));
+ ASSERT (FALSE);
+ DelayedDispatchTable->Count = 0;
+ break;
+ }
+
+ // Check each entry in the table for possible dispatch
+ for (Index1 = 0; Index1 < DelayedDispatchTable->Count;) {
+ Entry = &DelayedDispatchTable->Entry[Index1];
+ // If DelayedGroupId is present, insure there is an additional check of the table.
+ if (DelayedGroupId != NULL) {
+ if (CompareGuid (DelayedGroupId, &Entry->DelayedGroupId)) {
+ DelayedGroupIdPresent = TRUE;
+ }
+ }
+
+ TimeCurrent = GET_TIME_IN_US ();
+ if (TimeCurrent >= Entry->DispatchTime) {
+ // Time expired, invoked the function
+ DEBUG ((
+ DEBUG_ERROR,
+ "Delayed dispatch entry %d @ %p, Target=%d, Act=%d Disp=%d\n",
+ Index1,
+ Entry->Function,
+ Entry->DispatchTime,
+ TimeCurrent,
+ DelayedDispatchTable->DispCount
+ ));
+ Dispatched = TRUE;
+ Entry->MicrosecondDelay = 0;
+ Entry->Function (
+ &Entry->Context,
+ &Entry->MicrosecondDelay
+ );
+ DEBUG ((DEBUG_ERROR, "Delayed dispatch Function returned delay=%d\n", Entry->MicrosecondDelay));
+ if (Entry->MicrosecondDelay == 0) {
+ // NewTime = 0 = delete this entry from the table
+ DelayedDispatchTable->Count--;
+ CopyMem (Entry, Entry+1, sizeof (DELAYED_DISPATCH_ENTRY) * (DelayedDispatchTable->Count - Index1));
+ } else {
+ if (Entry->MicrosecondDelay > FixedPcdGet32 (PcdDelayedDispatchMaxDelayUs)) {
+ DEBUG ((DEBUG_ERROR, "%a Illegal new delay %d requested\n", __func__, Entry->MicrosecondDelay));
+ ASSERT (FALSE);
+ Entry->MicrosecondDelay = FixedPcdGet32 (PcdDelayedDispatchMaxDelayUs);
+ }
+
+ // NewTime != 0 - update the time from us to Dispatch time
+ Status = SafeUint64Add (GET_TIME_IN_US (), Entry->MicrosecondDelay, &Entry->DispatchTime);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a Delay overflow, this event will likely never be fired...\n", __func__));
+ Entry->DispatchTime = MAX_UINT64;
+ }
+
+ Index1++;
+ }
+ } else {
+ Index1++;
+ }
+ }
+ }
+
+ return Dispatched;
+}
+
+/**
+ Wait on a registered Delayed Dispatch unit that has a DelayedGroupId. Continue
+ to dispatch all registered delayed dispatch entries until *ALL* entries with
+ DelayedGroupId have completed.
+
+ Example usage:
+ 1. Register a Delayed Dispatch entry with a DelayedGroupId.
+ 2. Call this function with the DelayedGroupId
+ 3. The registered function in #1 will be called after the specified delay.
+ 4. This function will wait until all entries with the DelayedGroupId have completed.
+
+ @param[in] This The Delayed Dispatch PPI pointer.
+ @param[in] DelayedGroupId Delayed dispatch request ID the caller will wait on
+
+ @retval EFI_SUCCESS The operation succeeds.
+ @retval EFI_INVALID_PARAMETER The parameters are invalid.
+
+**/
+EFI_STATUS
+EFIAPI
+PeiDelayedDispatchWaitOnEvent (
+ IN EFI_DELAYED_DISPATCH_PPI *This,
+ IN EFI_GUID DelayedGroupId
+ )
+{
+ PERF_FUNCTION_BEGIN ();
+ EFI_STATUS Status;
+ DELAYED_DISPATCH_TABLE *DelayedDispatchTable;
+
+ // Get delayed dispatch table
+ DelayedDispatchTable = GetDelayedDispatchTable ();
+ if (DelayedDispatchTable == NULL) {
+ DEBUG ((DEBUG_ERROR, "%a Unable to locate dispatch table\n", __func__));
+ Status = EFI_UNSUPPORTED;
+ goto Exit;
+ }
+
+ if (IsZeroGuid (&DelayedGroupId)) {
+ DEBUG ((DEBUG_ERROR, "%a Delayed Group ID is a null GUID\n", __func__));
+ Status = EFI_UNSUPPORTED;
+ goto Exit;
+ }
+
+ DEBUG ((DEBUG_INFO, "Delayed dispatch on %g. Count=%d, DispatchCount=%d\n", &DelayedGroupId, DelayedDispatchTable->Count, DelayedDispatchTable->DispCount));
+ PERF_EVENT_SIGNAL_BEGIN (&DelayedGroupId);
+ DelayedDispatchDispatcher (DelayedDispatchTable, &DelayedGroupId);
+ PERF_EVENT_SIGNAL_END (&DelayedGroupId);
+
+ Status = EFI_SUCCESS;
+
+Exit:
+ PERF_FUNCTION_END ();
+ return Status;
+}
+
/**
+ DelayedDispatch End of PEI callback function. Insure that all of the delayed dispatch
+ entries are complete before exiting PEI.
+
+ @param[in] PeiServices - Pointer to PEI Services Table.
+ @param[in] NotifyDesc - Pointer to the descriptor for the Notification event that
+ caused this function to execute.
+ @param[in] Ppi - Pointer to the PPI data associated with this function.
+
+ @retval EFI_STATUS - Always return EFI_SUCCESS
+**/
+EFI_STATUS
+EFIAPI
+PeiDelayedDispatchOnEndOfPei (
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDesc,
+ IN VOID *Ppi
+ )
+{
+ DELAYED_DISPATCH_TABLE *DelayedDispatchTable;
+
+ // Get delayed dispatch table
+ DelayedDispatchTable = GetDelayedDispatchTable ();
+ if (DelayedDispatchTable == NULL) {
+ DEBUG ((DEBUG_ERROR, "%a Unable to locate dispatch table\n", __func__));
+ return EFI_UNSUPPORTED;
+ }
+
+ PERF_INMODULE_BEGIN ("PerfDelayedDispatchEndOfPei");
+ while (DelayedDispatchTable->Count > 0) {
+ DelayedDispatchDispatcher (DelayedDispatchTable, NULL);
+ }
+
+ DEBUG ((DEBUG_ERROR, "%a Count of dispatch cycles is %d\n", __func__, DelayedDispatchTable->DispCount));
+ PERF_INMODULE_END ("PerfDelayedDispatchEndOfPei");
+ return EFI_SUCCESS;
+}
+
+/**
Discover all PEIMs and optional Apriori file in one FV. There is at most one
Apriori file in one FV.
@@ -347,7 +715,7 @@ PeiLoadFixAddressHook ( // Note: Here is a assumption that system memory should always be healthy even without test.
//
if ((NextResourceHob->ResourceType == EFI_RESOURCE_SYSTEM_MEMORY) &&
- (((NextResourceHob->ResourceAttribute^ResourceHob->ResourceAttribute)&(~EFI_RESOURCE_ATTRIBUTE_TESTED)) == 0))
+ (((NextResourceHob->ResourceAttribute^ResourceHob->ResourceAttribute) & (~EFI_RESOURCE_ATTRIBUTE_TESTED)) == 0))
{
//
// See if the memory range described in ResourceHob and NextResourceHob is adjacent
@@ -1426,12 +1794,38 @@ PeiDispatcher ( EFI_PEI_FILE_HANDLE SaveCurrentFileHandle;
EFI_FV_FILE_INFO FvFileInfo;
PEI_CORE_FV_HANDLE *CoreFvHandle;
+ EFI_HOB_GUID_TYPE *GuidHob;
+ UINT32 TableSize;
PeiServices = (CONST EFI_PEI_SERVICES **)&Private->Ps;
PeimEntryPoint = NULL;
PeimFileHandle = NULL;
EntryPoint = 0;
+ if (Private->DelayedDispatchTable == NULL) {
+ GuidHob = GetFirstGuidHob (&gEfiDelayedDispatchTableGuid);
+ if (GuidHob != NULL) {
+ Private->DelayedDispatchTable = (DELAYED_DISPATCH_TABLE *)(GET_GUID_HOB_DATA (GuidHob));
+ } else {
+ TableSize = sizeof (DELAYED_DISPATCH_TABLE) + ((DELAYED_DISPATCH_MAX_ENTRIES - 1) * sizeof (DELAYED_DISPATCH_ENTRY));
+ Private->DelayedDispatchTable = BuildGuidHob (&gEfiDelayedDispatchTableGuid, TableSize);
+ if (Private->DelayedDispatchTable != NULL) {
+ ZeroMem (Private->DelayedDispatchTable, TableSize);
+ Status = PeiServicesInstallPpi (&mDelayedDispatchDesc);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a Failed to install Delayed Dispatch PPI: %r!\n", __func__, Status));
+ ASSERT_EFI_ERROR (Status);
+ } else {
+ Status = PeiServicesNotifyPpi (&mDelayedDispatchNotifyDesc);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a Failed to notify Delayed Dispatch on End of Pei: %r!\n", __func__, Status));
+ ASSERT_EFI_ERROR (Status);
+ }
+ }
+ }
+ }
+ }
+
if ((Private->PeiMemoryInstalled) &&
(PcdGetBool (PcdMigrateTemporaryRamFirmwareVolumes) ||
(Private->HobList.HandoffInformationTable->BootMode != BOOT_ON_S3_RESUME) ||
@@ -1682,6 +2076,13 @@ PeiDispatcher ( }
}
}
+
+ // Dispatch pending delalyed dispatch requests
+ if (Private->DelayedDispatchTable != NULL) {
+ if (DelayedDispatchDispatcher (Private->DelayedDispatchTable, NULL)) {
+ ProcessDispatchNotifyList (Private);
+ }
+ }
}
//
diff --git a/MdeModulePkg/Core/Pei/PeiMain.h b/MdeModulePkg/Core/Pei/PeiMain.h index 8df0c2d561..3f73da01f4 100644 --- a/MdeModulePkg/Core/Pei/PeiMain.h +++ b/MdeModulePkg/Core/Pei/PeiMain.h @@ -11,6 +11,8 @@ SPDX-License-Identifier: BSD-2-Clause-Patent #include <PiPei.h>
#include <Ppi/DxeIpl.h>
+#include <Ppi/DelayedDispatch.h>
+#include <Ppi/EndOfPeiPhase.h>
#include <Ppi/MemoryDiscovered.h>
#include <Ppi/StatusCode.h>
#include <Ppi/Reset.h>
@@ -42,10 +44,13 @@ SPDX-License-Identifier: BSD-2-Clause-Patent #include <IndustryStandard/PeImage.h>
#include <Library/PeiServicesTablePointerLib.h>
#include <Library/MemoryAllocationLib.h>
+#include <Library/TimerLib.h>
+#include <Library/SafeIntLib.h>
#include <Guid/FirmwareFileSystem2.h>
#include <Guid/FirmwareFileSystem3.h>
#include <Guid/AprioriFileName.h>
#include <Guid/MigratedFvInfo.h>
+#include <Guid/DelayedDispatch.h>
///
/// It is an FFS type extension used for PeiFindFileEx. It indicates current
@@ -208,6 +213,23 @@ EFI_STATUS #define PEI_CORE_HANDLE_SIGNATURE SIGNATURE_32('P','e','i','C')
+//
+// Converts elapsed ticks of performance counter to time in microseconds.
+// This macro converts the elapsed ticks of running performance counter to
+// time value in unit of microseconds.
+//
+// NOTE: To support Delayed Dispatch functionality, the timer ticks are required
+// to be:
+// 1. A 64bit register;
+// 2. Guaranteed to be monotonically increasing from 0;
+// 3. Not wrapped throughout the duration of a boot;
+//
+// The requirement above is set to avoid the timer overflow issue to keep the
+// Delayed Dispatch meet the PI specification with minimal change (instead of
+// implementing a control-yielding multi-threaded PEI core).
+//
+#define GET_TIME_IN_US() ((UINT32)DivU64x32(GetTimeInNanoSecond(GetPerformanceCounter ()), 1000))
+
///
/// Pei Core private data structure instance
///
@@ -308,6 +330,11 @@ struct _PEI_CORE_INSTANCE { // Those Memory Range will be migrated into physical memory.
//
HOLE_MEMORY_DATA HoleData[HOLE_MAX_NUMBER];
+
+ //
+ // Table of delayed dispatch requests
+ //
+ DELAYED_DISPATCH_TABLE *DelayedDispatchTable;
};
///
@@ -2028,4 +2055,47 @@ PeiReinitializeFv ( IN PEI_CORE_INSTANCE *PrivateData
);
+/**
+ Register a callback to be called after a minimum delay has occurred.
+
+ @param[in] This Pointer to the EFI_DELAYED_DISPATCH_PPI instance
+ @param[in] Function Function to call back
+ @param[in] Context Context data
+ @param[in] DelayedGroupId Delayed dispatch request ID the caller will wait on
+ @param[in] Delay Delay interval
+
+ @retval EFI_SUCCESS Function successfully loaded
+ @retval EFI_INVALID_PARAMETER One of the Arguments is not supported
+ @retval EFI_OUT_OF_RESOURCES No more entries
+
+**/
+EFI_STATUS
+EFIAPI
+PeiDelayedDispatchRegister (
+ IN EFI_DELAYED_DISPATCH_PPI *This,
+ IN EFI_DELAYED_DISPATCH_FUNCTION Function,
+ IN UINT64 Context,
+ IN EFI_GUID *DelayedGroupId OPTIONAL,
+ IN UINT32 Delay
+ );
+
+/**
+ Wait on a registered Delayed Dispatch unit that has a DelayedGroupId. Continue
+ to dispatch all registered delayed dispatch entries until *ALL* entries with
+ DelayedGroupId have completed.
+
+ @param[in] This The Delayed Dispatch PPI pointer.
+ @param[in] DelayedGroupId Delayed dispatch request ID the caller will wait on
+
+ @retval EFI_SUCCESS Function successfully invoked
+ @retval EFI_INVALID_PARAMETER One of the Arguments is not supported
+
+**/
+EFI_STATUS
+EFIAPI
+PeiDelayedDispatchWaitOnEvent (
+ IN EFI_DELAYED_DISPATCH_PPI *This,
+ IN EFI_GUID DelayedGroupId
+ );
+
#endif
diff --git a/MdeModulePkg/Core/Pei/PeiMain.inf b/MdeModulePkg/Core/Pei/PeiMain.inf index 4e545ddab2..4cd58ee3aa 100644 --- a/MdeModulePkg/Core/Pei/PeiMain.inf +++ b/MdeModulePkg/Core/Pei/PeiMain.inf @@ -66,6 +66,8 @@ PeCoffLib
PeiServicesTablePointerLib
PcdLib
+ TimerLib
+ SafeIntLib
[Guids]
gPeiAprioriFileNameGuid ## SOMETIMES_CONSUMES ## File
@@ -79,6 +81,7 @@ gStatusCodeCallbackGuid
gEdkiiMigratedFvInfoGuid ## SOMETIMES_PRODUCES ## HOB
gEdkiiMigrationInfoGuid ## SOMETIMES_CONSUMES ## HOB
+ gEfiDelayedDispatchTableGuid ## SOMETIMES_PRODUCES ## HOB
[Ppis]
gEfiPeiStatusCodePpiGuid ## SOMETIMES_CONSUMES # PeiReportStatusService is not ready if this PPI doesn't exist
@@ -102,6 +105,8 @@ gEfiSecHobDataPpiGuid ## SOMETIMES_CONSUMES
gEfiPeiCoreFvLocationPpiGuid ## SOMETIMES_CONSUMES
gEdkiiPeiMigrateTempRamPpiGuid ## PRODUCES
+ gEfiPeiDelayedDispatchPpiGuid ## PRODUCES
+ gEfiEndOfPeiSignalPpiGuid ## CONSUMES
[Pcd]
gEfiMdeModulePkgTokenSpaceGuid.PcdPeiCoreMaxPeiStackSize ## CONSUMES
@@ -114,6 +119,8 @@ gEfiMdeModulePkgTokenSpaceGuid.PcdShadowPeimOnBoot ## CONSUMES
gEfiMdeModulePkgTokenSpaceGuid.PcdInitValueInTempStack ## CONSUMES
gEfiMdeModulePkgTokenSpaceGuid.PcdMigrateTemporaryRamFirmwareVolumes ## CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdDelayedDispatchMaxDelayUs ## CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdDelayedDispatchCompletionTimeoutUs ## CONSUMES
# [BootMode]
# S3_RESUME ## SOMETIMES_CONSUMES
diff --git a/MdeModulePkg/Core/Pei/PeiMain/PeiMain.c b/MdeModulePkg/Core/Pei/PeiMain/PeiMain.c index 61f5699e1f..ff65e69f52 100644 --- a/MdeModulePkg/Core/Pei/PeiMain/PeiMain.c +++ b/MdeModulePkg/Core/Pei/PeiMain/PeiMain.c @@ -282,6 +282,9 @@ PeiCore ( OldCoreData->TempFileHandles = (EFI_PEI_FILE_HANDLE *)((UINT8 *)OldCoreData->TempFileHandles - OldCoreData->HeapOffset);
}
+ // Force relocating the dispatch table
+ OldCoreData->DelayedDispatchTable = NULL;
+
//
// Fixup for PeiService's address
//
diff --git a/MdeModulePkg/Include/Guid/DelayedDispatch.h b/MdeModulePkg/Include/Guid/DelayedDispatch.h new file mode 100644 index 0000000000..9be5444e36 --- /dev/null +++ b/MdeModulePkg/Include/Guid/DelayedDispatch.h @@ -0,0 +1,47 @@ +/** @file
+ Definition for structure & defines exported by Delayed Dispatch PPI
+
+ Copyright (c), Microsoft Corporation.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef DELAYED_DISPATCH_H_
+#define DELAYED_DISPATCH_H_
+
+// Delayed Dispatch table GUID
+#define EFI_DELAYED_DISPATCH_TABLE_GUID {\
+ 0x4b733449, 0x8eff, 0x488c, { 0x92, 0x1a, 0x15, 0x4a, 0xda, 0x25, 0x18, 0x07 } \
+ }
+
+//
+// Maximal number of Delayed Dispatch entries supported
+//
+#define DELAYED_DISPATCH_MAX_ENTRIES 8
+
+//
+// Internal structure for delayed dispatch entries.
+// Packing the structures here to save space as they will be stored as HOBs.
+//
+#pragma pack (push, 1)
+
+typedef struct {
+ EFI_GUID DelayedGroupId;
+ UINT64 Context;
+ EFI_DELAYED_DISPATCH_FUNCTION Function;
+ UINT64 DispatchTime;
+ UINT32 MicrosecondDelay;
+} DELAYED_DISPATCH_ENTRY;
+
+typedef struct {
+ UINT32 Count;
+ UINT32 DispCount;
+ DELAYED_DISPATCH_ENTRY Entry[DELAYED_DISPATCH_MAX_ENTRIES];
+} DELAYED_DISPATCH_TABLE;
+
+#pragma pack (pop)
+
+extern EFI_GUID gEfiDelayedDispatchTableGuid;
+
+#endif
diff --git a/MdeModulePkg/MdeModulePkg.dec b/MdeModulePkg/MdeModulePkg.dec index 1324b6d100..83e27c58aa 100644 --- a/MdeModulePkg/MdeModulePkg.dec +++ b/MdeModulePkg/MdeModulePkg.dec @@ -488,6 +488,9 @@ ## Include/Guid/MmCommBuffer.h
gMmCommBufferHobGuid = { 0x6c2a2520, 0x0131, 0x4aee, { 0xa7, 0x50, 0xcc, 0x38, 0x4a, 0xac, 0xe8, 0xc6 }}
+ ## Include/Guid/DelayedDispatch.h
+ gEfiDelayedDispatchTableGuid = { 0x4b733449, 0x8eff, 0x488c, { 0x92, 0x1a, 0x15, 0x4a, 0xda, 0x25, 0x18, 0x07 }}
+
[Ppis]
## Include/Ppi/FirmwareVolumeShadowPpi.h
gEdkiiPeiFirmwareVolumeShadowPpiGuid = { 0x7dfe756c, 0xed8d, 0x4d77, {0x9e, 0xc4, 0x39, 0x9a, 0x8a, 0x81, 0x51, 0x16 } }
@@ -1041,6 +1044,14 @@ # @ValidList 0x80000006 | 0x03058002
gEfiMdeModulePkgTokenSpaceGuid.PcdErrorCodeSetVariable|0x03058002|UINT32|0x30001040
+ ## Delayed Dispatch Maximum Delay in us (microseconds)
+ # Maximum delay for any particular delay request - 5 seconds
+ gEfiMdeModulePkgTokenSpaceGuid.PcdDelayedDispatchMaxDelayUs|5000000|UINT32|0x3000104A
+
+ ## Delayed Dispatch timeout in us (microseconds)
+ # Maximum delay when waiting for completion (ie EndOfPei) - 10 seconds
+ gEfiMdeModulePkgTokenSpaceGuid.PcdDelayedDispatchCompletionTimeoutUs|10000000|UINT32|0x3000104B
+
## Mask to control the NULL address detection in code for different phases.
# If enabled, accessing NULL address in UEFI or SMM code can be caught.<BR><BR>
# BIT0 - Enable NULL pointer detection for UEFI.<BR>
|