diff options
author | Kun Qin <kuqin@microsoft.com> | 2024-09-19 14:11:37 -0700 |
---|---|---|
committer | mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> | 2024-10-17 08:51:15 +0000 |
commit | d64d1e195ceb003c824786bfd817227c7ae81f06 (patch) | |
tree | 7be6ef83aabe2d31e5926c5b598b824ed9bda923 /MdeModulePkg | |
parent | b3f36e151d8b014cac5c6a833e2270c14d998739 (diff) | |
download | edk2-d64d1e195ceb003c824786bfd817227c7ae81f06.tar.gz edk2-d64d1e195ceb003c824786bfd817227c7ae81f06.tar.bz2 edk2-d64d1e195ceb003c824786bfd817227c7ae81f06.zip |
MdeModulePkg: PeiMain: Introduce implementation of delayed dispatch
REF: https://bugzilla.tianocore.org/show_bug.cgi?id=4496
This change adds the implementation that fits the needs and description
of PI spec defined Delayed Dispatch PPI in Pei Core.
The PPI would allow minimal delay for registered callbacks. As well as
allowing other functions to wait for GUIDed delayed dispatch callbacks.
Cc: Jian J Wang <jian.j.wang@intel.com>
Cc: Dandan Bi <dandan.bi@intel.com>
Cc: Liming Gao <gaoliming@byosoft.com.cn>
Cc: Debkumar De <debkumar.de@intel.com>
Cc: Catharine West <catharine.west@intel.com>
Co-authored-by: Mike Turner <mikeyt@pobox.com>
Co-authored-by: Sachin Ganesh <sachinganesh@ami.com>
Signed-off-by: Kun Qin <kuqin12@gmail.com>
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>
|