From 51dd408ae1022e5c9378492451a62b38d5b101c5 Mon Sep 17 00:00:00 2001 From: Eric Dong Date: Wed, 10 Jul 2019 15:49:11 +0800 Subject: [PATCH] UefiCpuPkg/PiSmmCpuDxeSmm: Enable MM MP Protocol REF: https://bugzilla.tianocore.org/show_bug.cgi?id=1937 Add MM Mp Protocol in PiSmmCpuDxeSmm driver. Cc: Ray Ni Cc: Laszlo Ersek Signed-off-by: Eric Dong Reviewed-by: Ray Ni Regression-tested-by: Laszlo Ersek --- UefiCpuPkg/PiSmmCpuDxeSmm/MpService.c | 572 ++++++++++++++++++- UefiCpuPkg/PiSmmCpuDxeSmm/PiSmmCpuDxeSmm.c | 18 + UefiCpuPkg/PiSmmCpuDxeSmm/PiSmmCpuDxeSmm.h | 193 ++++++- UefiCpuPkg/PiSmmCpuDxeSmm/PiSmmCpuDxeSmm.inf | 3 + UefiCpuPkg/PiSmmCpuDxeSmm/SmmMp.c | 344 +++++++++++ UefiCpuPkg/PiSmmCpuDxeSmm/SmmMp.h | 286 ++++++++++ 6 files changed, 1391 insertions(+), 25 deletions(-) create mode 100644 UefiCpuPkg/PiSmmCpuDxeSmm/SmmMp.c create mode 100644 UefiCpuPkg/PiSmmCpuDxeSmm/SmmMp.h diff --git a/UefiCpuPkg/PiSmmCpuDxeSmm/MpService.c b/UefiCpuPkg/PiSmmCpuDxeSmm/MpService.c index 64fb4d6344..ef16997547 100644 --- a/UefiCpuPkg/PiSmmCpuDxeSmm/MpService.c +++ b/UefiCpuPkg/PiSmmCpuDxeSmm/MpService.c @@ -136,11 +136,9 @@ ReleaseAllAPs ( ) { UINTN Index; - UINTN BspIndex; - BspIndex = mSmmMpSyncData->BspIndex; for (Index = mMaxNumberOfCpus; Index-- > 0;) { - if (Index != BspIndex && *(mSmmMpSyncData->CpuData[Index].Present)) { + if (IsPresentAp (Index)) { ReleaseSemaphore (mSmmMpSyncData->CpuData[Index].Run); } } @@ -347,6 +345,165 @@ ReplaceOSMtrrs ( MtrrSetAllMtrrs (&gSmiMtrrs); } +/** + Wheck whether task has been finished by all APs. + + @param BlockMode Whether did it in block mode or non-block mode. + + @retval TRUE Task has been finished by all APs. + @retval FALSE Task not has been finished by all APs. + +**/ +BOOLEAN +WaitForAllAPsNotBusy ( + IN BOOLEAN BlockMode + ) +{ + UINTN Index; + + for (Index = mMaxNumberOfCpus; Index-- > 0;) { + // + // Ignore BSP and APs which not call in SMM. + // + if (!IsPresentAp(Index)) { + continue; + } + + if (BlockMode) { + AcquireSpinLock(mSmmMpSyncData->CpuData[Index].Busy); + ReleaseSpinLock(mSmmMpSyncData->CpuData[Index].Busy); + } else { + if (AcquireSpinLockOrFail (mSmmMpSyncData->CpuData[Index].Busy)) { + ReleaseSpinLock(mSmmMpSyncData->CpuData[Index].Busy); + } else { + return FALSE; + } + } + } + + return TRUE; +} + +/** + Check whether it is an present AP. + + @param CpuIndex The AP index which calls this function. + + @retval TRUE It's a present AP. + @retval TRUE This is not an AP or it is not present. + +**/ +BOOLEAN +IsPresentAp ( + IN UINTN CpuIndex + ) +{ + return ((CpuIndex != gSmmCpuPrivate->SmmCoreEntryContext.CurrentlyExecutingCpu) && + *(mSmmMpSyncData->CpuData[CpuIndex].Present)); +} + +/** + Check whether execute in single AP or all APs. + + Compare two Tokens used by different APs to know whether in StartAllAps call. + + Whether is an valid AP base on AP's Present flag. + + @retval TRUE IN StartAllAps call. + @retval FALSE Not in StartAllAps call. + +**/ +BOOLEAN +InStartAllApsCall ( + VOID + ) +{ + UINTN ApIndex; + UINTN ApIndex2; + + for (ApIndex = mMaxNumberOfCpus; ApIndex-- > 0;) { + if (IsPresentAp (ApIndex) && (mSmmMpSyncData->CpuData[ApIndex].Token != NULL)) { + for (ApIndex2 = ApIndex; ApIndex2-- > 0;) { + if (IsPresentAp (ApIndex2) && (mSmmMpSyncData->CpuData[ApIndex2].Token != NULL)) { + return mSmmMpSyncData->CpuData[ApIndex2].Token == mSmmMpSyncData->CpuData[ApIndex].Token; + } + } + } + } + + return FALSE; +} + +/** + Clean up the status flags used during executing the procedure. + + @param CpuIndex The AP index which calls this function. + +**/ +VOID +ReleaseToken ( + IN UINTN CpuIndex + ) +{ + UINTN Index; + BOOLEAN Released; + + if (InStartAllApsCall ()) { + // + // In Start All APs mode, make sure all APs have finished task. + // + if (WaitForAllAPsNotBusy (FALSE)) { + // + // Clean the flags update in the function call. + // + Released = FALSE; + for (Index = mMaxNumberOfCpus; Index-- > 0;) { + // + // Only In SMM APs need to be clean up. + // + if (mSmmMpSyncData->CpuData[Index].Present && mSmmMpSyncData->CpuData[Index].Token != NULL) { + if (!Released) { + ReleaseSpinLock (mSmmMpSyncData->CpuData[Index].Token); + Released = TRUE; + } + mSmmMpSyncData->CpuData[Index].Token = NULL; + } + } + } + } else { + // + // In single AP mode. + // + if (mSmmMpSyncData->CpuData[CpuIndex].Token != NULL) { + ReleaseSpinLock (mSmmMpSyncData->CpuData[CpuIndex].Token); + mSmmMpSyncData->CpuData[CpuIndex].Token = NULL; + } + } +} + +/** + Free the tokens in the maintained list. + +**/ +VOID +FreeTokens ( + VOID + ) +{ + LIST_ENTRY *Link; + PROCEDURE_TOKEN *ProcToken; + + while (!IsListEmpty (&gSmmCpuPrivate->TokenList)) { + Link = GetFirstNode (&gSmmCpuPrivate->TokenList); + ProcToken = PROCEDURE_TOKEN_FROM_LINK (Link); + + RemoveEntryList (&ProcToken->Link); + + FreePool ((VOID *)ProcToken->ProcedureToken); + FreePool (ProcToken); + } +} + /** SMI handler for BSP. @@ -476,12 +633,7 @@ BSPHandler ( // // Make sure all APs have completed their pending none-block tasks // - for (Index = mMaxNumberOfCpus; Index-- > 0;) { - if (Index != CpuIndex && *(mSmmMpSyncData->CpuData[Index].Present)) { - AcquireSpinLock (mSmmMpSyncData->CpuData[Index].Busy); - ReleaseSpinLock (mSmmMpSyncData->CpuData[Index].Busy); - } - } + WaitForAllAPsNotBusy (TRUE); // // Perform the remaining tasks @@ -572,6 +724,11 @@ BSPHandler ( // WaitForAllAPs (ApCount); + // + // Clean the tokens buffer. + // + FreeTokens (); + // // Reset BspIndex to -1, meaning BSP has not been elected. // @@ -604,6 +761,7 @@ APHandler ( UINT64 Timer; UINTN BspIndex; MTRR_SETTINGS Mtrrs; + EFI_STATUS ProcedureStatus; // // Timeout BSP @@ -730,14 +888,19 @@ APHandler ( // // Invoke the scheduled procedure // - (*mSmmMpSyncData->CpuData[CpuIndex].Procedure) ( - (VOID*)mSmmMpSyncData->CpuData[CpuIndex].Parameter - ); + ProcedureStatus = (*mSmmMpSyncData->CpuData[CpuIndex].Procedure) ( + (VOID*)mSmmMpSyncData->CpuData[CpuIndex].Parameter + ); + if (mSmmMpSyncData->CpuData[CpuIndex].Status != NULL) { + *mSmmMpSyncData->CpuData[CpuIndex].Status = ProcedureStatus; + } // // Release BUSY // ReleaseSpinLock (mSmmMpSyncData->CpuData[CpuIndex].Busy); + + ReleaseToken (CpuIndex); } if (SmmCpuFeaturesNeedConfigureMtrrs()) { @@ -906,13 +1069,124 @@ Gen4GPageTable ( return (UINT32)(UINTN)PageTable; } +/** + Checks whether the input token is the current used token. + + @param[in] Token This parameter describes the token that was passed into DispatchProcedure or + BroadcastProcedure. + + @retval TRUE The input token is the current used token. + @retval FALSE The input token is not the current used token. +**/ +BOOLEAN +IsTokenInUse ( + IN SPIN_LOCK *Token + ) +{ + LIST_ENTRY *Link; + PROCEDURE_TOKEN *ProcToken; + + if (Token == NULL) { + return FALSE; + } + + Link = GetFirstNode (&gSmmCpuPrivate->TokenList); + while (!IsNull (&gSmmCpuPrivate->TokenList, Link)) { + ProcToken = PROCEDURE_TOKEN_FROM_LINK (Link); + + if (ProcToken->ProcedureToken == Token) { + return TRUE; + } + + Link = GetNextNode (&gSmmCpuPrivate->TokenList, Link); + } + + return FALSE; +} + +/** + create token and save it to the maintain list. + + @retval return the spin lock used as token. + +**/ +SPIN_LOCK * +CreateToken ( + VOID + ) +{ + PROCEDURE_TOKEN *ProcToken; + SPIN_LOCK *CpuToken; + UINTN SpinLockSize; + + SpinLockSize = GetSpinLockProperties (); + CpuToken = AllocatePool (SpinLockSize); + ASSERT (CpuToken != NULL); + InitializeSpinLock (CpuToken); + AcquireSpinLock (CpuToken); + + ProcToken = AllocatePool (sizeof (PROCEDURE_TOKEN)); + ASSERT (ProcToken != NULL); + ProcToken->Signature = PROCEDURE_TOKEN_SIGNATURE; + ProcToken->ProcedureToken = CpuToken; + + InsertTailList (&gSmmCpuPrivate->TokenList, &ProcToken->Link); + + return CpuToken; +} + +/** + Checks status of specified AP. + + This function checks whether the specified AP has finished the task assigned + by StartupThisAP(), and whether timeout expires. + + @param[in] Token This parameter describes the token that was passed into DispatchProcedure or + BroadcastProcedure. + + @retval EFI_SUCCESS Specified AP has finished task assigned by StartupThisAPs(). + @retval EFI_NOT_READY Specified AP has not finished task and timeout has not expired. +**/ +EFI_STATUS +IsApReady ( + IN SPIN_LOCK *Token + ) +{ + if (AcquireSpinLockOrFail (Token)) { + ReleaseSpinLock (Token); + return EFI_SUCCESS; + } + + return EFI_NOT_READY; +} + /** Schedule a procedure to run on the specified CPU. @param[in] Procedure The address of the procedure to run @param[in] CpuIndex Target CPU Index - @param[in, out] ProcArguments The parameter to pass to the procedure - @param[in] BlockingMode Startup AP in blocking mode or not + @param[in,out] ProcArguments The parameter to pass to the procedure + @param[in] Token This is an optional parameter that allows the caller to execute the + procedure in a blocking or non-blocking fashion. If it is NULL the + call is blocking, and the call will not return until the AP has + completed the procedure. If the token is not NULL, the call will + return immediately. The caller can check whether the procedure has + completed with CheckOnProcedure or WaitForProcedure. + @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for the APs to finish + execution of Procedure, either for blocking or non-blocking mode. + Zero means infinity. If the timeout expires before all APs return + from Procedure, then Procedure on the failed APs is terminated. If + the timeout expires in blocking mode, the call returns EFI_TIMEOUT. + If the timeout expires in non-blocking mode, the timeout determined + can be through CheckOnProcedure or WaitForProcedure. + Note that timeout support is optional. Whether an implementation + supports this feature can be determined via the Attributes data + member. + @param[in,out] CpuStatus This optional pointer may be used to get the status code returned + by Procedure when it completes execution on the target AP, or with + EFI_TIMEOUT if the Procedure fails to complete within the optional + timeout. The implementation will update this variable with + EFI_NOT_READY prior to starting Procedure on the target AP. @retval EFI_INVALID_PARAMETER CpuNumber not valid @retval EFI_INVALID_PARAMETER CpuNumber specifying BSP @@ -923,10 +1197,12 @@ Gen4GPageTable ( **/ EFI_STATUS InternalSmmStartupThisAp ( - IN EFI_AP_PROCEDURE Procedure, - IN UINTN CpuIndex, - IN OUT VOID *ProcArguments OPTIONAL, - IN BOOLEAN BlockingMode + IN EFI_AP_PROCEDURE2 Procedure, + IN UINTN CpuIndex, + IN OUT VOID *ProcArguments OPTIONAL, + IN MM_COMPLETION *Token, + IN UINTN TimeoutInMicroseconds, + IN OUT EFI_STATUS *CpuStatus ) { if (CpuIndex >= gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus) { @@ -952,24 +1228,190 @@ InternalSmmStartupThisAp ( } return EFI_INVALID_PARAMETER; } + if ((TimeoutInMicroseconds != 0) && ((mSmmMp.Attributes & EFI_MM_MP_TIMEOUT_SUPPORTED) == 0)) { + return EFI_INVALID_PARAMETER; + } + if (Procedure == NULL) { + return EFI_INVALID_PARAMETER; + } - if (BlockingMode) { + if (Token == NULL) { AcquireSpinLock (mSmmMpSyncData->CpuData[CpuIndex].Busy); } else { if (!AcquireSpinLockOrFail (mSmmMpSyncData->CpuData[CpuIndex].Busy)) { - DEBUG((DEBUG_ERROR, "mSmmMpSyncData->CpuData[%d].Busy\n", CpuIndex)); - return EFI_INVALID_PARAMETER; + DEBUG((DEBUG_ERROR, "Can't acquire mSmmMpSyncData->CpuData[%d].Busy\n", CpuIndex)); + return EFI_NOT_READY; } + + *Token = (MM_COMPLETION) CreateToken (); } mSmmMpSyncData->CpuData[CpuIndex].Procedure = Procedure; mSmmMpSyncData->CpuData[CpuIndex].Parameter = ProcArguments; + if (Token != NULL) { + mSmmMpSyncData->CpuData[CpuIndex].Token = (SPIN_LOCK *)(*Token); + } + mSmmMpSyncData->CpuData[CpuIndex].Status = CpuStatus; + if (mSmmMpSyncData->CpuData[CpuIndex].Status != NULL) { + *mSmmMpSyncData->CpuData[CpuIndex].Status = EFI_NOT_READY; + } + ReleaseSemaphore (mSmmMpSyncData->CpuData[CpuIndex].Run); - if (BlockingMode) { + if (Token == NULL) { AcquireSpinLock (mSmmMpSyncData->CpuData[CpuIndex].Busy); ReleaseSpinLock (mSmmMpSyncData->CpuData[CpuIndex].Busy); } + + return EFI_SUCCESS; +} + +/** + Worker function to execute a caller provided function on all enabled APs. + + @param[in] Procedure A pointer to the function to be run on + enabled APs of the system. + @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for + APs to return from Procedure, either for + blocking or non-blocking mode. + @param[in,out] ProcedureArguments The parameter passed into Procedure for + all APs. + @param[in,out] Token This is an optional parameter that allows the caller to execute the + procedure in a blocking or non-blocking fashion. If it is NULL the + call is blocking, and the call will not return until the AP has + completed the procedure. If the token is not NULL, the call will + return immediately. The caller can check whether the procedure has + completed with CheckOnProcedure or WaitForProcedure. + @param[in,out] CPUStatus This optional pointer may be used to get the status code returned + by Procedure when it completes execution on the target AP, or with + EFI_TIMEOUT if the Procedure fails to complete within the optional + timeout. The implementation will update this variable with + EFI_NOT_READY prior to starting Procedure on the target AP. + + + @retval EFI_SUCCESS In blocking mode, all APs have finished before + the timeout expired. + @retval EFI_SUCCESS In non-blocking mode, function has been dispatched + to all enabled APs. + @retval others Failed to Startup all APs. + +**/ +EFI_STATUS +InternalSmmStartupAllAPs ( + IN EFI_AP_PROCEDURE2 Procedure, + IN UINTN TimeoutInMicroseconds, + IN OUT VOID *ProcedureArguments OPTIONAL, + IN OUT MM_COMPLETION *Token, + IN OUT EFI_STATUS *CPUStatus + ) +{ + UINTN Index; + UINTN CpuCount; + + if ((TimeoutInMicroseconds != 0) && ((mSmmMp.Attributes & EFI_MM_MP_TIMEOUT_SUPPORTED) == 0)) { + return EFI_INVALID_PARAMETER; + } + if (Procedure == NULL) { + return EFI_INVALID_PARAMETER; + } + + CpuCount = 0; + for (Index = mMaxNumberOfCpus; Index-- > 0;) { + if (IsPresentAp (Index)) { + CpuCount ++; + + if (gSmmCpuPrivate->Operation[Index] == SmmCpuRemove) { + return EFI_INVALID_PARAMETER; + } + + if (!AcquireSpinLockOrFail(mSmmMpSyncData->CpuData[Index].Busy)) { + return EFI_NOT_READY; + } + ReleaseSpinLock (mSmmMpSyncData->CpuData[Index].Busy); + } + } + if (CpuCount == 0) { + return EFI_NOT_STARTED; + } + + if (Token != NULL) { + *Token = (MM_COMPLETION) CreateToken (); + } + + // + // Make sure all BUSY should be acquired. + // + // Because former code already check mSmmMpSyncData->CpuData[***].Busy for each AP. + // Here code always use AcquireSpinLock instead of AcquireSpinLockOrFail for not + // block mode. + // + for (Index = mMaxNumberOfCpus; Index-- > 0;) { + if (IsPresentAp (Index)) { + AcquireSpinLock (mSmmMpSyncData->CpuData[Index].Busy); + } + } + + for (Index = mMaxNumberOfCpus; Index-- > 0;) { + if (IsPresentAp (Index)) { + mSmmMpSyncData->CpuData[Index].Procedure = (EFI_AP_PROCEDURE2) Procedure; + mSmmMpSyncData->CpuData[Index].Parameter = ProcedureArguments; + if (Token != NULL) { + mSmmMpSyncData->CpuData[Index].Token = (SPIN_LOCK *)(*Token); + } + if (CPUStatus != NULL) { + mSmmMpSyncData->CpuData[Index].Status = &CPUStatus[Index]; + if (mSmmMpSyncData->CpuData[Index].Status != NULL) { + *mSmmMpSyncData->CpuData[Index].Status = EFI_NOT_READY; + } + } + } else { + // + // PI spec requirement: + // For every excluded processor, the array entry must contain a value of EFI_NOT_STARTED. + // + if (CPUStatus != NULL) { + CPUStatus[Index] = EFI_NOT_STARTED; + } + } + } + + ReleaseAllAPs (); + + if (Token == NULL) { + // + // Make sure all APs have completed their tasks. + // + WaitForAllAPsNotBusy (TRUE); + } + + return EFI_SUCCESS; +} + +/** + ISO C99 6.5.2.2 "Function calls", paragraph 9: + If the function is defined with a type that is not compatible with + the type (of the expression) pointed to by the expression that + denotes the called function, the behavior is undefined. + + So add below wrapper function to convert between EFI_AP_PROCEDURE + and EFI_AP_PROCEDURE2. + + Wrapper for Procedures. + + @param[in] Buffer Pointer to PROCEDURE_WRAPPER buffer. + +**/ +EFI_STATUS +EFIAPI +ProcedureWrapper ( + IN OUT VOID *Buffer + ) +{ + PROCEDURE_WRAPPER *Wrapper; + + Wrapper = Buffer; + Wrapper->Procedure (Wrapper->ProcedureArgument); + return EFI_SUCCESS; } @@ -995,7 +1437,15 @@ SmmBlockingStartupThisAp ( IN OUT VOID *ProcArguments OPTIONAL ) { - return InternalSmmStartupThisAp(Procedure, CpuIndex, ProcArguments, TRUE); + PROCEDURE_WRAPPER Wrapper; + + Wrapper.Procedure = Procedure; + Wrapper.ProcedureArgument = ProcArguments; + + // + // Use wrapper function to convert EFI_AP_PROCEDURE to EFI_AP_PROCEDURE2. + // + return InternalSmmStartupThisAp (ProcedureWrapper, CpuIndex, &Wrapper, NULL, 0, NULL); } /** @@ -1020,7 +1470,22 @@ SmmStartupThisAp ( IN OUT VOID *ProcArguments OPTIONAL ) { - return InternalSmmStartupThisAp(Procedure, CpuIndex, ProcArguments, FeaturePcdGet (PcdCpuSmmBlockStartupThisAp)); + MM_COMPLETION Token; + + gSmmCpuPrivate->ApWrapperFunc[CpuIndex].Procedure = Procedure; + gSmmCpuPrivate->ApWrapperFunc[CpuIndex].ProcedureArgument = ProcArguments; + + // + // Use wrapper function to convert EFI_AP_PROCEDURE to EFI_AP_PROCEDURE2. + // + return InternalSmmStartupThisAp ( + ProcedureWrapper, + CpuIndex, + &gSmmCpuPrivate->ApWrapperFunc[CpuIndex], + FeaturePcdGet (PcdCpuSmmBlockStartupThisAp) ? NULL : &Token, + 0, + NULL + ); } /** @@ -1112,6 +1577,13 @@ SmiRendezvous ( Cr2 = 0; SaveCr2 (&Cr2); + // + // Call the user register Startup function first. + // + if (mSmmMpSyncData->StartupProcedure != NULL) { + mSmmMpSyncData->StartupProcedure (mSmmMpSyncData->StartupProcArgs); + } + // // Perform CPU specific entry hooks // @@ -1256,6 +1728,21 @@ Exit: RestoreCr2 (Cr2); } +/** + Allocate buffer for SpinLock and Wrapper function buffer. + +**/ +VOID +InitializeDataForMmMp ( + VOID + ) +{ + gSmmCpuPrivate->ApWrapperFunc = AllocatePool (sizeof (PROCEDURE_WRAPPER) * gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus); + ASSERT (gSmmCpuPrivate->ApWrapperFunc != NULL); + + InitializeListHead (&gSmmCpuPrivate->TokenList); +} + /** Allocate buffer for all semaphores and spin locks. @@ -1469,3 +1956,40 @@ RegisterSmmEntry ( gSmmCpuPrivate->SmmCoreEntry = SmmEntryPoint; return EFI_SUCCESS; } + +/** + + Register the SMM Foundation entry point. + + @param[in] Procedure A pointer to the code stream to be run on the designated target AP + of the system. Type EFI_AP_PROCEDURE is defined below in Volume 2 + with the related definitions of + EFI_MP_SERVICES_PROTOCOL.StartupAllAPs. + If caller may pass a value of NULL to deregister any existing + startup procedure. + @param[in] ProcedureArguments Allows the caller to pass a list of parameters to the code that is + run by the AP. It is an optional common mailbox between APs and + the caller to share information + + @retval EFI_SUCCESS The Procedure has been set successfully. + @retval EFI_INVALID_PARAMETER The Procedure is NULL but ProcedureArguments not NULL. + +**/ +EFI_STATUS +RegisterStartupProcedure ( + IN EFI_AP_PROCEDURE Procedure, + IN VOID *ProcedureArguments OPTIONAL + ) +{ + if (Procedure == NULL && ProcedureArguments != NULL) { + return EFI_INVALID_PARAMETER; + } + if (mSmmMpSyncData == NULL) { + return EFI_NOT_READY; + } + + mSmmMpSyncData->StartupProcedure = Procedure; + mSmmMpSyncData->StartupProcArgs = ProcedureArguments; + + return EFI_SUCCESS; +} diff --git a/UefiCpuPkg/PiSmmCpuDxeSmm/PiSmmCpuDxeSmm.c b/UefiCpuPkg/PiSmmCpuDxeSmm/PiSmmCpuDxeSmm.c index 2f7d777ee7..69a04dfb23 100644 --- a/UefiCpuPkg/PiSmmCpuDxeSmm/PiSmmCpuDxeSmm.c +++ b/UefiCpuPkg/PiSmmCpuDxeSmm/PiSmmCpuDxeSmm.c @@ -34,6 +34,8 @@ SMM_CPU_PRIVATE_DATA mSmmCpuPrivateData = { mSmmCpuPrivateData.SmmReservedSmramRegion, // SmmConfiguration.SmramReservedRegions RegisterSmmEntry // SmmConfiguration.RegisterSmmEntry }, + NULL, // pointer to Ap Wrapper Func array + {NULL, NULL}, // List_Entry for Tokens. }; CPU_HOT_PLUG_DATA mCpuHotPlugData = { @@ -996,6 +998,22 @@ PiCpuSmmEntry ( ); ASSERT_EFI_ERROR (Status); + // + // Initialize global buffer for MM MP. + // + InitializeDataForMmMp (); + + // + // Install the SMM Mp Protocol into SMM protocol database + // + Status = gSmst->SmmInstallProtocolInterface ( + &mSmmCpuHandle, + &gEfiMmMpProtocolGuid, + EFI_NATIVE_INTERFACE, + &mSmmMp + ); + ASSERT_EFI_ERROR (Status); + // // Expose address of CPU Hot Plug Data structure if CPU hot plug is supported. // diff --git a/UefiCpuPkg/PiSmmCpuDxeSmm/PiSmmCpuDxeSmm.h b/UefiCpuPkg/PiSmmCpuDxeSmm/PiSmmCpuDxeSmm.h index 2bb35a424d..186809f431 100644 --- a/UefiCpuPkg/PiSmmCpuDxeSmm/PiSmmCpuDxeSmm.h +++ b/UefiCpuPkg/PiSmmCpuDxeSmm/PiSmmCpuDxeSmm.h @@ -20,6 +20,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent #include #include #include +#include #include #include @@ -197,6 +198,25 @@ typedef UINT32 SMM_CPU_ARRIVAL_EXCEPTIONS; #define ARRIVAL_EXCEPTION_DELAYED 0x2 #define ARRIVAL_EXCEPTION_SMI_DISABLED 0x4 +// +// Wrapper used to convert EFI_AP_PROCEDURE2 and EFI_AP_PROCEDURE. +// +typedef struct { + EFI_AP_PROCEDURE Procedure; + VOID *ProcedureArgument; +} PROCEDURE_WRAPPER; + +#define PROCEDURE_TOKEN_SIGNATURE SIGNATURE_32 ('P', 'R', 'T', 'S') + +typedef struct { + UINTN Signature; + LIST_ENTRY Link; + + SPIN_LOCK *ProcedureToken; +} PROCEDURE_TOKEN; + +#define PROCEDURE_TOKEN_FROM_LINK(a) CR (a, PROCEDURE_TOKEN, Link, PROCEDURE_TOKEN_SIGNATURE) + // // Private structure for the SMM CPU module that is stored in DXE Runtime memory // Contains the SMM Configuration Protocols that is produced. @@ -219,6 +239,10 @@ typedef struct { EFI_SMM_ENTRY_POINT SmmCoreEntry; EFI_SMM_CONFIGURATION_PROTOCOL SmmConfiguration; + + PROCEDURE_WRAPPER *ApWrapperFunc; + LIST_ENTRY TokenList; + } SMM_CPU_PRIVATE_DATA; extern SMM_CPU_PRIVATE_DATA *gSmmCpuPrivate; @@ -226,6 +250,7 @@ extern CPU_HOT_PLUG_DATA mCpuHotPlugData; extern UINTN mMaxNumberOfCpus; extern UINTN mNumberOfCpus; extern EFI_SMM_CPU_PROTOCOL mSmmCpu; +extern EFI_MM_MP_PROTOCOL mSmmMp; /// /// The mode of the CPU at the time an SMI occurs @@ -363,10 +388,12 @@ SmmRelocationSemaphoreComplete ( /// typedef struct { SPIN_LOCK *Busy; - volatile EFI_AP_PROCEDURE Procedure; + volatile EFI_AP_PROCEDURE2 Procedure; volatile VOID *Parameter; volatile UINT32 *Run; volatile BOOLEAN *Present; + SPIN_LOCK *Token; + EFI_STATUS *Status; } SMM_CPU_DATA_BLOCK; typedef enum { @@ -388,6 +415,8 @@ typedef struct { volatile SMM_CPU_SYNC_MODE EffectiveSyncMode; volatile BOOLEAN SwitchBsp; volatile BOOLEAN *CandidateBsp; + EFI_AP_PROCEDURE StartupProcedure; + VOID *StartupProcArgs; } SMM_DISPATCHER_MP_SYNC_DATA; #define SMM_PSD_OFFSET 0xfb00 @@ -410,6 +439,7 @@ typedef struct { SPIN_LOCK *Busy; volatile UINT32 *Run; volatile BOOLEAN *Present; + SPIN_LOCK *Token; } SMM_CPU_SEMAPHORE_CPU; /// @@ -1259,4 +1289,165 @@ RestoreCr2 ( IN UINTN Cr2 ); +/** + Schedule a procedure to run on the specified CPU. + + @param[in] Procedure The address of the procedure to run + @param[in] CpuIndex Target CPU Index + @param[in,out] ProcArguments The parameter to pass to the procedure + @param[in,out] Token This is an optional parameter that allows the caller to execute the + procedure in a blocking or non-blocking fashion. If it is NULL the + call is blocking, and the call will not return until the AP has + completed the procedure. If the token is not NULL, the call will + return immediately. The caller can check whether the procedure has + completed with CheckOnProcedure or WaitForProcedure. + @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for the APs to finish + execution of Procedure, either for blocking or non-blocking mode. + Zero means infinity. If the timeout expires before all APs return + from Procedure, then Procedure on the failed APs is terminated. If + the timeout expires in blocking mode, the call returns EFI_TIMEOUT. + If the timeout expires in non-blocking mode, the timeout determined + can be through CheckOnProcedure or WaitForProcedure. + Note that timeout support is optional. Whether an implementation + supports this feature can be determined via the Attributes data + member. + @param[in,out] CPUStatus This optional pointer may be used to get the status code returned + by Procedure when it completes execution on the target AP, or with + EFI_TIMEOUT if the Procedure fails to complete within the optional + timeout. The implementation will update this variable with + EFI_NOT_READY prior to starting Procedure on the target AP. + + @retval EFI_INVALID_PARAMETER CpuNumber not valid + @retval EFI_INVALID_PARAMETER CpuNumber specifying BSP + @retval EFI_INVALID_PARAMETER The AP specified by CpuNumber did not enter SMM + @retval EFI_INVALID_PARAMETER The AP specified by CpuNumber is busy + @retval EFI_SUCCESS The procedure has been successfully scheduled + +**/ +EFI_STATUS +InternalSmmStartupThisAp ( + IN EFI_AP_PROCEDURE2 Procedure, + IN UINTN CpuIndex, + IN OUT VOID *ProcArguments OPTIONAL, + IN MM_COMPLETION *Token, + IN UINTN TimeoutInMicroseconds, + IN OUT EFI_STATUS *CpuStatus + ); + +/** + Checks whether the input token is the current used token. + + @param[in] Token This parameter describes the token that was passed into DispatchProcedure or + BroadcastProcedure. + + @retval TRUE The input token is the current used token. + @retval FALSE The input token is not the current used token. +**/ +BOOLEAN +IsTokenInUse ( + IN SPIN_LOCK *Token + ); + +/** + Checks status of specified AP. + + This function checks whether the specified AP has finished the task assigned + by StartupThisAP(), and whether timeout expires. + + @param[in] Token This parameter describes the token that was passed into DispatchProcedure or + BroadcastProcedure. + + @retval EFI_SUCCESS Specified AP has finished task assigned by StartupThisAPs(). + @retval EFI_NOT_READY Specified AP has not finished task and timeout has not expired. +**/ +EFI_STATUS +IsApReady ( + IN SPIN_LOCK *Token + ); + +/** + Check whether it is an present AP. + + @param CpuIndex The AP index which calls this function. + + @retval TRUE It's a present AP. + @retval TRUE This is not an AP or it is not present. + +**/ +BOOLEAN +IsPresentAp ( + IN UINTN CpuIndex + ); + +/** + Worker function to execute a caller provided function on all enabled APs. + + @param[in] Procedure A pointer to the function to be run on + enabled APs of the system. + @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for + APs to return from Procedure, either for + blocking or non-blocking mode. + @param[in,out] ProcedureArgument The parameter passed into Procedure for + all APs. + @param[in,out] Token This is an optional parameter that allows the caller to execute the + procedure in a blocking or non-blocking fashion. If it is NULL the + call is blocking, and the call will not return until the AP has + completed the procedure. If the token is not NULL, the call will + return immediately. The caller can check whether the procedure has + completed with CheckOnProcedure or WaitForProcedure. + @param[in,out] CPUStatus This optional pointer may be used to get the status code returned + by Procedure when it completes execution on the target AP, or with + EFI_TIMEOUT if the Procedure fails to complete within the optional + timeout. The implementation will update this variable with + EFI_NOT_READY prior to starting Procedure on the target AP. + + @retval EFI_SUCCESS In blocking mode, all APs have finished before + the timeout expired. + @retval EFI_SUCCESS In non-blocking mode, function has been dispatched + to all enabled APs. + @retval others Failed to Startup all APs. + +**/ +EFI_STATUS +InternalSmmStartupAllAPs ( + IN EFI_AP_PROCEDURE2 Procedure, + IN UINTN TimeoutInMicroseconds, + IN OUT VOID *ProcedureArguments OPTIONAL, + IN OUT MM_COMPLETION *Token, + IN OUT EFI_STATUS *CPUStatus + ); + +/** + + Register the SMM Foundation entry point. + + @param[in] Procedure A pointer to the code stream to be run on the designated target AP + of the system. Type EFI_AP_PROCEDURE is defined below in Volume 2 + with the related definitions of + EFI_MP_SERVICES_PROTOCOL.StartupAllAPs. + If caller may pass a value of NULL to deregister any existing + startup procedure. + @param[in,out] ProcedureArguments Allows the caller to pass a list of parameters to the code that is + run by the AP. It is an optional common mailbox between APs and + the caller to share information + + @retval EFI_SUCCESS The Procedure has been set successfully. + @retval EFI_INVALID_PARAMETER The Procedure is NULL but ProcedureArguments not NULL. + +**/ +EFI_STATUS +RegisterStartupProcedure ( + IN EFI_AP_PROCEDURE Procedure, + IN VOID *ProcedureArguments OPTIONAL + ); + +/** + Allocate buffer for SpinLock and Wrapper function buffer. + +**/ +VOID +InitializeDataForMmMp ( + VOID + ); + #endif diff --git a/UefiCpuPkg/PiSmmCpuDxeSmm/PiSmmCpuDxeSmm.inf b/UefiCpuPkg/PiSmmCpuDxeSmm/PiSmmCpuDxeSmm.inf index 466c568d49..da0308c47f 100644 --- a/UefiCpuPkg/PiSmmCpuDxeSmm/PiSmmCpuDxeSmm.inf +++ b/UefiCpuPkg/PiSmmCpuDxeSmm/PiSmmCpuDxeSmm.inf @@ -40,6 +40,8 @@ SmmProfileInternal.h SmramSaveState.c SmmCpuMemoryManagement.c + SmmMp.h + SmmMp.c [Sources.Ia32] Ia32/Semaphore.c @@ -105,6 +107,7 @@ gEfiSmmReadyToLockProtocolGuid ## NOTIFY gEfiSmmCpuServiceProtocolGuid ## PRODUCES gEdkiiSmmMemoryAttributeProtocolGuid ## PRODUCES + gEfiMmMpProtocolGuid ## PRODUCES [Guids] gEfiAcpiVariableGuid ## SOMETIMES_CONSUMES ## HOB # it is used for S3 boot. diff --git a/UefiCpuPkg/PiSmmCpuDxeSmm/SmmMp.c b/UefiCpuPkg/PiSmmCpuDxeSmm/SmmMp.c new file mode 100644 index 0000000000..9b2b191e03 --- /dev/null +++ b/UefiCpuPkg/PiSmmCpuDxeSmm/SmmMp.c @@ -0,0 +1,344 @@ +/** @file +SMM MP protocol implementation + +Copyright (c) 2019, Intel Corporation. All rights reserved.
+ +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "PiSmmCpuDxeSmm.h" +#include "SmmMp.h" + +/// +/// SMM MP Protocol instance +/// +EFI_MM_MP_PROTOCOL mSmmMp = { + EFI_MM_MP_PROTOCOL_REVISION, + 0, + SmmMpGetNumberOfProcessors, + SmmMpDispatchProcedure, + SmmMpBroadcastProcedure, + SmmMpSetStartupProcedure, + SmmMpCheckForProcedure, + SmmMpWaitForProcedure +}; + +/** + Service to retrieves the number of logical processor in the platform. + + @param[in] This The EFI_MM_MP_PROTOCOL instance. + @param[out] NumberOfProcessors Pointer to the total number of logical processors in the system, + including the BSP and all APs. + + @retval EFI_SUCCESS The number of processors was retrieved successfully + @retval EFI_INVALID_PARAMETER NumberOfProcessors is NULL +**/ +EFI_STATUS +EFIAPI +SmmMpGetNumberOfProcessors ( + IN CONST EFI_MM_MP_PROTOCOL *This, + OUT UINTN *NumberOfProcessors + ) +{ + if (NumberOfProcessors == NULL) { + return EFI_INVALID_PARAMETER; + } + + *NumberOfProcessors = gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus; + + return EFI_SUCCESS; +} + +/** + This service allows the caller to invoke a procedure one of the application processors (AP). This + function uses an optional token parameter to support blocking and non-blocking modes. If the token + is passed into the call, the function will operate in a non-blocking fashion and the caller can + check for completion with CheckOnProcedure or WaitForProcedure. + + @param[in] This The EFI_MM_MP_PROTOCOL instance. + @param[in] Procedure A pointer to the procedure to be run on the designated target + AP of the system. Type EFI_AP_PROCEDURE2 is defined below in + related definitions. + @param[in] CpuNumber The zero-based index of the processor number of the target + AP, on which the code stream is supposed to run. If the number + points to the calling processor then it will not run the + supplied code. + @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for this AP to + finish execution of Procedure, either for blocking or + non-blocking mode. Zero means infinity. If the timeout + expires before this AP returns from Procedure, then Procedure + on the AP is terminated. If the timeout expires in blocking + mode, the call returns EFI_TIMEOUT. If the timeout expires + in non-blocking mode, the timeout determined can be through + CheckOnProcedure or WaitForProcedure. + Note that timeout support is optional. Whether an + implementation supports this feature, can be determined via + the Attributes data member. + @param[in,out] ProcedureArguments Allows the caller to pass a list of parameters to the code + that is run by the AP. It is an optional common mailbox + between APs and the caller to share information. + @param[in,out] Token This is parameter is broken into two components: + 1.Token->Completion is an optional parameter that allows the + caller to execute the procedure in a blocking or non-blocking + fashion. If it is NULL the call is blocking, and the call will + not return until the AP has completed the procedure. If the + token is not NULL, the call will return immediately. The caller + can check whether the procedure has completed with + CheckOnProcedure or WaitForProcedure. + 2.Token->Status The implementation updates the address pointed + at by this variable with the status code returned by Procedure + when it completes execution on the target AP, or with EFI_TIMEOUT + if the Procedure fails to complete within the optional timeout. + The implementation will update this variable with EFI_NOT_READY + prior to starting Procedure on the target AP + @param[in,out] CPUStatus This optional pointer may be used to get the status code returned + by Procedure when it completes execution on the target AP, or with + EFI_TIMEOUT if the Procedure fails to complete within the optional + timeout. The implementation will update this variable with + EFI_NOT_READY prior to starting Procedure on the target AP. + + @retval EFI_SUCCESS In the blocking case, this indicates that Procedure has completed + execution on the target AP. + In the non-blocking case this indicates that the procedure has + been successfully scheduled for execution on the target AP. + @retval EFI_INVALID_PARAMETER The input arguments are out of range. Either the target AP is the + caller of the function, or the Procedure or Token is NULL + @retval EFI_NOT_READY If the target AP is busy executing another procedure + @retval EFI_ALREADY_STARTED Token is already in use for another procedure + @retval EFI_TIMEOUT In blocking mode, the timeout expired before the specified AP + has finished + @retval EFI_OUT_OF_RESOURCES Could not allocate a required resource. + +**/ +EFI_STATUS +EFIAPI +SmmMpDispatchProcedure ( + IN CONST EFI_MM_MP_PROTOCOL *This, + IN EFI_AP_PROCEDURE2 Procedure, + IN UINTN CpuNumber, + IN UINTN TimeoutInMicroseconds, + IN OUT VOID *ProcedureArguments OPTIONAL, + IN OUT MM_COMPLETION *Token, + IN OUT EFI_STATUS *CPUStatus + ) +{ + return InternalSmmStartupThisAp ( + Procedure, + CpuNumber, + ProcedureArguments, + Token, + TimeoutInMicroseconds, + CPUStatus + ); +} + +/** + This service allows the caller to invoke a procedure on all running application processors (AP) + except the caller. This function uses an optional token parameter to support blocking and + nonblocking modes. If the token is passed into the call, the function will operate in a non-blocking + fashion and the caller can check for completion with CheckOnProcedure or WaitForProcedure. + + It is not necessary for the implementation to run the procedure on every processor on the platform. + Processors that are powered down in such a way that they cannot respond to interrupts, may be + excluded from the broadcast. + + + @param[in] This The EFI_MM_MP_PROTOCOL instance. + @param[in] Procedure A pointer to the code stream to be run on the APs that have + entered MM. Type EFI_AP_PROCEDURE is defined below in related + definitions. + @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for the APs to finish + execution of Procedure, either for blocking or non-blocking mode. + Zero means infinity. If the timeout expires before all APs return + from Procedure, then Procedure on the failed APs is terminated. If + the timeout expires in blocking mode, the call returns EFI_TIMEOUT. + If the timeout expires in non-blocking mode, the timeout determined + can be through CheckOnProcedure or WaitForProcedure. + Note that timeout support is optional. Whether an implementation + supports this feature can be determined via the Attributes data + member. + @param[in,out] ProcedureArguments Allows the caller to pass a list of parameters to the code + that is run by the AP. It is an optional common mailbox + between APs and the caller to share information. + @param[in,out] Token This is parameter is broken into two components: + 1.Token->Completion is an optional parameter that allows the + caller to execute the procedure in a blocking or non-blocking + fashion. If it is NULL the call is blocking, and the call will + not return until the AP has completed the procedure. If the + token is not NULL, the call will return immediately. The caller + can check whether the procedure has completed with + CheckOnProcedure or WaitForProcedure. + 2.Token->Status The implementation updates the address pointed + at by this variable with the status code returned by Procedure + when it completes execution on the target AP, or with EFI_TIMEOUT + if the Procedure fails to complete within the optional timeout. + The implementation will update this variable with EFI_NOT_READY + prior to starting Procedure on the target AP + @param[in,out] CPUStatus This optional pointer may be used to get the individual status + returned by every AP that participated in the broadcast. This + parameter if used provides the base address of an array to hold + the EFI_STATUS value of each AP in the system. The size of the + array can be ascertained by the GetNumberOfProcessors function. + As mentioned above, the broadcast may not include every processor + in the system. Some implementations may exclude processors that + have been powered down in such a way that they are not responsive + to interrupts. Additionally the broadcast excludes the processor + which is making the BroadcastProcedure call. For every excluded + processor, the array entry must contain a value of EFI_NOT_STARTED + + @retval EFI_SUCCESS In the blocking case, this indicates that Procedure has completed + execution on the APs. + In the non-blocking case this indicates that the procedure has + been successfully scheduled for execution on the APs. + @retval EFI_INVALID_PARAMETER The Procedure or Token is NULL + @retval EFI_NOT_READY If the target AP is busy executing another procedure + @retval EFI_ALREADY_STARTED Token is already in use for another procedure + @retval EFI_TIMEOUT In blocking mode, the timeout expired before the specified AP + has finished. + @retval EFI_OUT_OF_RESOURCES Could not allocate a required resource. + +**/ +EFI_STATUS +EFIAPI +SmmMpBroadcastProcedure ( + IN CONST EFI_MM_MP_PROTOCOL *This, + IN EFI_AP_PROCEDURE2 Procedure, + IN UINTN TimeoutInMicroseconds, + IN OUT VOID *ProcedureArguments OPTIONAL, + IN OUT MM_COMPLETION *Token, + IN OUT EFI_STATUS *CPUStatus + ) +{ + return InternalSmmStartupAllAPs( + Procedure, + TimeoutInMicroseconds, + ProcedureArguments, + Token, + CPUStatus + ); +} + +/** + This service allows the caller to set a startup procedure that will be executed when an AP powers + up from a state where core configuration and context is lost. The procedure is execution has the + following properties: + 1. The procedure executes before the processor is handed over to the operating system. + 2. All processors execute the same startup procedure. + 3. The procedure may run in parallel with other procedures invoked through the functions in this + protocol, or with processors that are executing an MM handler or running in the operating system. + + + @param[in] This The EFI_MM_MP_PROTOCOL instance. + @param[in] Procedure A pointer to the code stream to be run on the designated target AP + of the system. Type EFI_AP_PROCEDURE is defined below in Volume 2 + with the related definitions of + EFI_MP_SERVICES_PROTOCOL.StartupAllAPs. + If caller may pass a value of NULL to deregister any existing + startup procedure. + @param[in,out] ProcedureArguments Allows the caller to pass a list of parameters to the code that is + run by the AP. It is an optional common mailbox between APs and + the caller to share information + + @retval EFI_SUCCESS The Procedure has been set successfully. + @retval EFI_INVALID_PARAMETER The Procedure is NULL but ProcedureArguments not NULL. + +**/ +EFI_STATUS +EFIAPI +SmmMpSetStartupProcedure ( + IN CONST EFI_MM_MP_PROTOCOL *This, + IN EFI_AP_PROCEDURE Procedure, + IN OUT VOID *ProcedureArguments OPTIONAL + ) +{ + return RegisterStartupProcedure (Procedure, ProcedureArguments); +} + +/** + When non-blocking execution of a procedure on an AP is invoked with DispatchProcedure, + via the use of a token, this function can be used to check for completion of the procedure on the AP. + The function takes the token that was passed into the DispatchProcedure call. If the procedure + is complete, and therefore it is now possible to run another procedure on the same AP, this function + returns EFI_SUCESS. In this case the status returned by the procedure that executed on the AP is + returned in the token's Status field. If the procedure has not yet completed, then this function + returns EFI_NOT_READY. + + When a non-blocking execution of a procedure is invoked with BroadcastProcedure, via the + use of a token, this function can be used to check for completion of the procedure on all the + broadcast APs. The function takes the token that was passed into the BroadcastProcedure + call. If the procedure is complete on all broadcast APs this function returns EFI_SUCESS. In this + case the Status field in the token passed into the function reflects the overall result of the + invocation, which may be EFI_SUCCESS, if all executions succeeded, or the first observed failure. + If the procedure has not yet completed on the broadcast APs, the function returns + EFI_NOT_READY. + + @param[in] This The EFI_MM_MP_PROTOCOL instance. + @param[in] Token This parameter describes the token that was passed into + DispatchProcedure or BroadcastProcedure. + + @retval EFI_SUCCESS Procedure has completed. + @retval EFI_NOT_READY The Procedure has not completed. + @retval EFI_INVALID_PARAMETER Token or Token->Completion is NULL + @retval EFI_NOT_FOUND Token is not currently in use for a non-blocking call + +**/ +EFI_STATUS +EFIAPI +SmmMpCheckForProcedure ( + IN CONST EFI_MM_MP_PROTOCOL *This, + IN MM_COMPLETION Token + ) +{ + if (Token == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (!IsTokenInUse ((SPIN_LOCK *)Token)) { + return EFI_NOT_FOUND; + } + + return IsApReady ((SPIN_LOCK *)Token); +} + +/** + When a non-blocking execution of a procedure on an AP is invoked via DispatchProcedure, + this function will block the caller until the remote procedure has completed on the designated AP. + The non-blocking procedure invocation is identified by the Token parameter, which must match the + token that used when DispatchProcedure was called. Upon completion the status returned by + the procedure that executed on the AP is used to update the token's Status field. + + When a non-blocking execution of a procedure on an AP is invoked via BroadcastProcedure + this function will block the caller until the remote procedure has completed on all of the APs that + entered MM. The non-blocking procedure invocation is identified by the Token parameter, which + must match the token that used when BroadcastProcedure was called. Upon completion the + overall status returned by the procedures that executed on the broadcast AP is used to update the + token's Status field. The overall status may be EFI_SUCCESS, if all executions succeeded, or the + first observed failure. + + + @param[in] This The EFI_MM_MP_PROTOCOL instance. + @param[in] Token This parameter describes the token that was passed into + DispatchProcedure or BroadcastProcedure. + + @retval EFI_SUCCESS Procedure has completed. + @retval EFI_INVALID_PARAMETER Token or Token->Completion is NULL + @retval EFI_NOT_FOUND Token is not currently in use for a non-blocking call + +**/ +EFI_STATUS +EFIAPI +SmmMpWaitForProcedure ( + IN CONST EFI_MM_MP_PROTOCOL *This, + IN MM_COMPLETION Token + ) +{ + EFI_STATUS Status; + + do { + Status = SmmMpCheckForProcedure (This, Token); + } while (Status == EFI_NOT_READY); + + return Status; +} + diff --git a/UefiCpuPkg/PiSmmCpuDxeSmm/SmmMp.h b/UefiCpuPkg/PiSmmCpuDxeSmm/SmmMp.h new file mode 100644 index 0000000000..e0d823a4b1 --- /dev/null +++ b/UefiCpuPkg/PiSmmCpuDxeSmm/SmmMp.h @@ -0,0 +1,286 @@ +/** @file +Include file for SMM MP protocol implementation. + +Copyright (c) 2019, Intel Corporation. All rights reserved.
+ +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _SMM_MP_PROTOCOL_H_ +#define _SMM_MP_PROTOCOL_H_ + +// +// SMM MP Protocol function prototypes. +// + +/** + Service to retrieves the number of logical processor in the platform. + + @param[in] This The EFI_MM_MP_PROTOCOL instance. + @param[out] NumberOfProcessors Pointer to the total number of logical processors in the system, + including the BSP and all APs. + + @retval EFI_SUCCESS The number of processors was retrieved successfully + @retval EFI_INVALID_PARAMETER NumberOfProcessors is NULL +**/ + +EFI_STATUS +EFIAPI +SmmMpGetNumberOfProcessors ( + IN CONST EFI_MM_MP_PROTOCOL *This, + OUT UINTN *NumberOfProcessors + ); + + +/** + This service allows the caller to invoke a procedure one of the application processors (AP). This + function uses an optional token parameter to support blocking and non-blocking modes. If the token + is passed into the call, the function will operate in a non-blocking fashion and the caller can + check for completion with CheckOnProcedure or WaitForProcedure. + + @param[in] This The EFI_MM_MP_PROTOCOL instance. + @param[in] Procedure A pointer to the procedure to be run on the designated target + AP of the system. Type EFI_AP_PROCEDURE2 is defined below in + related definitions. + @param[in] CpuNumber The zero-based index of the processor number of the target + AP, on which the code stream is supposed to run. If the number + points to the calling processor then it will not run the + supplied code. + @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for this AP to + finish execution of Procedure, either for blocking or + non-blocking mode. Zero means infinity. If the timeout + expires before this AP returns from Procedure, then Procedure + on the AP is terminated. If the timeout expires in blocking + mode, the call returns EFI_TIMEOUT. If the timeout expires + in non-blocking mode, the timeout determined can be through + CheckOnProcedure or WaitForProcedure. + Note that timeout support is optional. Whether an + implementation supports this feature, can be determined via + the Attributes data member. + @param[in,out] ProcedureArguments Allows the caller to pass a list of parameters to the code + that is run by the AP. It is an optional common mailbox + between APs and the caller to share information. + @param[in,out] Token This is parameter is broken into two components: + 1.Token->Completion is an optional parameter that allows the + caller to execute the procedure in a blocking or non-blocking + fashion. If it is NULL the call is blocking, and the call will + not return until the AP has completed the procedure. If the + token is not NULL, the call will return immediately. The caller + can check whether the procedure has completed with + CheckOnProcedure or WaitForProcedure. + 2.Token->Status The implementation updates the address pointed + at by this variable with the status code returned by Procedure + when it completes execution on the target AP, or with EFI_TIMEOUT + if the Procedure fails to complete within the optional timeout. + The implementation will update this variable with EFI_NOT_READY + prior to starting Procedure on the target AP + @param[in,out] CPUStatus This optional pointer may be used to get the status code returned + by Procedure when it completes execution on the target AP, or with + EFI_TIMEOUT if the Procedure fails to complete within the optional + timeout. The implementation will update this variable with + EFI_NOT_READY prior to starting Procedure on the target AP. + + @retval EFI_SUCCESS In the blocking case, this indicates that Procedure has completed + execution on the target AP. + In the non-blocking case this indicates that the procedure has + been successfully scheduled for execution on the target AP. + @retval EFI_INVALID_PARAMETER The input arguments are out of range. Either the target AP is the + caller of the function, or the Procedure or Token is NULL + @retval EFI_NOT_READY If the target AP is busy executing another procedure + @retval EFI_ALREADY_STARTED Token is already in use for another procedure + @retval EFI_TIMEOUT In blocking mode, the timeout expired before the specified AP + has finished + @retval EFI_OUT_OF_RESOURCES Could not allocate a required resource. + +**/ +EFI_STATUS +EFIAPI +SmmMpDispatchProcedure ( + IN CONST EFI_MM_MP_PROTOCOL *This, + IN EFI_AP_PROCEDURE2 Procedure, + IN UINTN CpuNumber, + IN UINTN TimeoutInMicroseconds, + IN OUT VOID *ProcedureArguments OPTIONAL, + IN OUT MM_COMPLETION *Token, + IN OUT EFI_STATUS *CPUStatus + ); + +/** + This service allows the caller to invoke a procedure on all running application processors (AP) + except the caller. This function uses an optional token parameter to support blocking and + nonblocking modes. If the token is passed into the call, the function will operate in a non-blocking + fashion and the caller can check for completion with CheckOnProcedure or WaitForProcedure. + + It is not necessary for the implementation to run the procedure on every processor on the platform. + Processors that are powered down in such a way that they cannot respond to interrupts, may be + excluded from the broadcast. + + + @param[in] This The EFI_MM_MP_PROTOCOL instance. + @param[in] Procedure A pointer to the code stream to be run on the APs that have + entered MM. Type EFI_AP_PROCEDURE is defined below in related + definitions. + @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for the APs to finish + execution of Procedure, either for blocking or non-blocking mode. + Zero means infinity. If the timeout expires before all APs return + from Procedure, then Procedure on the failed APs is terminated. If + the timeout expires in blocking mode, the call returns EFI_TIMEOUT. + If the timeout expires in non-blocking mode, the timeout determined + can be through CheckOnProcedure or WaitForProcedure. + Note that timeout support is optional. Whether an implementation + supports this feature can be determined via the Attributes data + member. + @param[in,out] ProcedureArguments Allows the caller to pass a list of parameters to the code + that is run by the AP. It is an optional common mailbox + between APs and the caller to share information. + @param[in,out] Token This is parameter is broken into two components: + 1.Token->Completion is an optional parameter that allows the + caller to execute the procedure in a blocking or non-blocking + fashion. If it is NULL the call is blocking, and the call will + not return until the AP has completed the procedure. If the + token is not NULL, the call will return immediately. The caller + can check whether the procedure has completed with + CheckOnProcedure or WaitForProcedure. + 2.Token->Status The implementation updates the address pointed + at by this variable with the status code returned by Procedure + when it completes execution on the target AP, or with EFI_TIMEOUT + if the Procedure fails to complete within the optional timeout. + The implementation will update this variable with EFI_NOT_READY + prior to starting Procedure on the target AP + @param[in,out] CPUStatus This optional pointer may be used to get the individual status + returned by every AP that participated in the broadcast. This + parameter if used provides the base address of an array to hold + the EFI_STATUS value of each AP in the system. The size of the + array can be ascertained by the GetNumberOfProcessors function. + As mentioned above, the broadcast may not include every processor + in the system. Some implementations may exclude processors that + have been powered down in such a way that they are not responsive + to interrupts. Additionally the broadcast excludes the processor + which is making the BroadcastProcedure call. For every excluded + processor, the array entry must contain a value of EFI_NOT_STARTED + + @retval EFI_SUCCESS In the blocking case, this indicates that Procedure has completed + execution on the APs. + In the non-blocking case this indicates that the procedure has + been successfully scheduled for execution on the APs. + @retval EFI_INVALID_PARAMETER The Procedure or Token is NULL + @retval EFI_NOT_READY If the target AP is busy executing another procedure + @retval EFI_ALREADY_STARTED Token is already in use for another procedure + @retval EFI_TIMEOUT In blocking mode, the timeout expired before the specified AP + has finished + @retval EFI_OUT_OF_RESOURCES Could not allocate a required resource. + +**/ +EFI_STATUS +EFIAPI +SmmMpBroadcastProcedure ( + IN CONST EFI_MM_MP_PROTOCOL *This, + IN EFI_AP_PROCEDURE2 Procedure, + IN UINTN TimeoutInMicroseconds, + IN OUT VOID *ProcedureArguments OPTIONAL, + IN OUT MM_COMPLETION *Token, + IN OUT EFI_STATUS *CPUStatus + ); + + +/** + This service allows the caller to set a startup procedure that will be executed when an AP powers + up from a state where core configuration and context is lost. The procedure is execution has the + following properties: + 1. The procedure executes before the processor is handed over to the operating system. + 2. All processors execute the same startup procedure. + 3. The procedure may run in parallel with other procedures invoked through the functions in this + protocol, or with processors that are executing an MM handler or running in the operating system. + + + @param[in] This The EFI_MM_MP_PROTOCOL instance. + @param[in] Procedure A pointer to the code stream to be run on the designated target AP + of the system. Type EFI_AP_PROCEDURE is defined below in Volume 2 + with the related definitions of + EFI_MP_SERVICES_PROTOCOL.StartupAllAPs. + If caller may pass a value of NULL to deregister any existing + startup procedure. + @param[in,out] ProcedureArguments Allows the caller to pass a list of parameters to the code that is + run by the AP. It is an optional common mailbox between APs and + the caller to share information + + @retval EFI_SUCCESS The Procedure has been set successfully. + @retval EFI_INVALID_PARAMETER The Procedure is NULL but ProcedureArguments not NULL. +**/ +EFI_STATUS +EFIAPI +SmmMpSetStartupProcedure ( + IN CONST EFI_MM_MP_PROTOCOL *This, + IN EFI_AP_PROCEDURE Procedure, + IN OUT VOID *ProcedureArguments OPTIONAL + ); + +/** + When non-blocking execution of a procedure on an AP is invoked with DispatchProcedure, + via the use of a token, this function can be used to check for completion of the procedure on the AP. + The function takes the token that was passed into the DispatchProcedure call. If the procedure + is complete, and therefore it is now possible to run another procedure on the same AP, this function + returns EFI_SUCESS. In this case the status returned by the procedure that executed on the AP is + returned in the token's Status field. If the procedure has not yet completed, then this function + returns EFI_NOT_READY. + + When a non-blocking execution of a procedure is invoked with BroadcastProcedure, via the + use of a token, this function can be used to check for completion of the procedure on all the + broadcast APs. The function takes the token that was passed into the BroadcastProcedure + call. If the procedure is complete on all broadcast APs this function returns EFI_SUCESS. In this + case the Status field in the token passed into the function reflects the overall result of the + invocation, which may be EFI_SUCCESS, if all executions succeeded, or the first observed failure. + If the procedure has not yet completed on the broadcast APs, the function returns + EFI_NOT_READY. + + @param[in] This The EFI_MM_MP_PROTOCOL instance. + @param[in] Token This parameter describes the token that was passed into + DispatchProcedure or BroadcastProcedure. + + @retval EFI_SUCCESS Procedure has completed. + @retval EFI_NOT_READY The Procedure has not completed. + @retval EFI_INVALID_PARAMETER Token or Token->Completion is NULL + @retval EFI_NOT_FOUND Token is not currently in use for a non-blocking call + +**/ +EFI_STATUS +EFIAPI +SmmMpCheckForProcedure ( + IN CONST EFI_MM_MP_PROTOCOL *This, + IN MM_COMPLETION Token + ); + +/** + When a non-blocking execution of a procedure on an AP is invoked via DispatchProcedure, + this function will block the caller until the remote procedure has completed on the designated AP. + The non-blocking procedure invocation is identified by the Token parameter, which must match the + token that used when DispatchProcedure was called. Upon completion the status returned by + the procedure that executed on the AP is used to update the token's Status field. + + When a non-blocking execution of a procedure on an AP is invoked via BroadcastProcedure + this function will block the caller until the remote procedure has completed on all of the APs that + entered MM. The non-blocking procedure invocation is identified by the Token parameter, which + must match the token that used when BroadcastProcedure was called. Upon completion the + overall status returned by the procedures that executed on the broadcast AP is used to update the + token's Status field. The overall status may be EFI_SUCCESS, if all executions succeeded, or the + first observed failure. + + + @param[in] This The EFI_MM_MP_PROTOCOL instance. + @param[in] Token This parameter describes the token that was passed into + DispatchProcedure or BroadcastProcedure. + + @retval EFI_SUCCESS Procedure has completed. + @retval EFI_INVALID_PARAMETER Token or Token->Completion is NULL + @retval EFI_NOT_FOUND Token is not currently in use for a non-blocking call + +**/ +EFI_STATUS +EFIAPI +SmmMpWaitForProcedure ( + IN CONST EFI_MM_MP_PROTOCOL *This, + IN MM_COMPLETION Token + ); + +#endif -- 2.39.2