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 <ray.ni@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Eric Dong <eric.dong@intel.com>
Reviewed-by: Ray Ni <ray.ni@intel.com>
Regression-tested-by: Laszlo Ersek <lersek@redhat.com>
This commit is contained in:
Eric Dong 2019-07-10 15:49:11 +08:00
parent 18f169a95c
commit 51dd408ae1
6 changed files with 1391 additions and 25 deletions

View File

@ -136,11 +136,9 @@ ReleaseAllAPs (
) )
{ {
UINTN Index; UINTN Index;
UINTN BspIndex;
BspIndex = mSmmMpSyncData->BspIndex;
for (Index = mMaxNumberOfCpus; Index-- > 0;) { for (Index = mMaxNumberOfCpus; Index-- > 0;) {
if (Index != BspIndex && *(mSmmMpSyncData->CpuData[Index].Present)) { if (IsPresentAp (Index)) {
ReleaseSemaphore (mSmmMpSyncData->CpuData[Index].Run); ReleaseSemaphore (mSmmMpSyncData->CpuData[Index].Run);
} }
} }
@ -347,6 +345,165 @@ ReplaceOSMtrrs (
MtrrSetAllMtrrs (&gSmiMtrrs); 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. SMI handler for BSP.
@ -476,12 +633,7 @@ BSPHandler (
// //
// Make sure all APs have completed their pending none-block tasks // Make sure all APs have completed their pending none-block tasks
// //
for (Index = mMaxNumberOfCpus; Index-- > 0;) { WaitForAllAPsNotBusy (TRUE);
if (Index != CpuIndex && *(mSmmMpSyncData->CpuData[Index].Present)) {
AcquireSpinLock (mSmmMpSyncData->CpuData[Index].Busy);
ReleaseSpinLock (mSmmMpSyncData->CpuData[Index].Busy);
}
}
// //
// Perform the remaining tasks // Perform the remaining tasks
@ -572,6 +724,11 @@ BSPHandler (
// //
WaitForAllAPs (ApCount); WaitForAllAPs (ApCount);
//
// Clean the tokens buffer.
//
FreeTokens ();
// //
// Reset BspIndex to -1, meaning BSP has not been elected. // Reset BspIndex to -1, meaning BSP has not been elected.
// //
@ -604,6 +761,7 @@ APHandler (
UINT64 Timer; UINT64 Timer;
UINTN BspIndex; UINTN BspIndex;
MTRR_SETTINGS Mtrrs; MTRR_SETTINGS Mtrrs;
EFI_STATUS ProcedureStatus;
// //
// Timeout BSP // Timeout BSP
@ -730,14 +888,19 @@ APHandler (
// //
// Invoke the scheduled procedure // Invoke the scheduled procedure
// //
(*mSmmMpSyncData->CpuData[CpuIndex].Procedure) ( ProcedureStatus = (*mSmmMpSyncData->CpuData[CpuIndex].Procedure) (
(VOID*)mSmmMpSyncData->CpuData[CpuIndex].Parameter (VOID*)mSmmMpSyncData->CpuData[CpuIndex].Parameter
); );
if (mSmmMpSyncData->CpuData[CpuIndex].Status != NULL) {
*mSmmMpSyncData->CpuData[CpuIndex].Status = ProcedureStatus;
}
// //
// Release BUSY // Release BUSY
// //
ReleaseSpinLock (mSmmMpSyncData->CpuData[CpuIndex].Busy); ReleaseSpinLock (mSmmMpSyncData->CpuData[CpuIndex].Busy);
ReleaseToken (CpuIndex);
} }
if (SmmCpuFeaturesNeedConfigureMtrrs()) { if (SmmCpuFeaturesNeedConfigureMtrrs()) {
@ -906,13 +1069,124 @@ Gen4GPageTable (
return (UINT32)(UINTN)PageTable; 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. Schedule a procedure to run on the specified CPU.
@param[in] Procedure The address of the procedure to run @param[in] Procedure The address of the procedure to run
@param[in] CpuIndex Target CPU Index @param[in] CpuIndex Target CPU Index
@param[in, out] ProcArguments The parameter to pass to the procedure @param[in,out] ProcArguments The parameter to pass to the procedure
@param[in] BlockingMode Startup AP in blocking mode or not @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 not valid
@retval EFI_INVALID_PARAMETER CpuNumber specifying BSP @retval EFI_INVALID_PARAMETER CpuNumber specifying BSP
@ -923,10 +1197,12 @@ Gen4GPageTable (
**/ **/
EFI_STATUS EFI_STATUS
InternalSmmStartupThisAp ( InternalSmmStartupThisAp (
IN EFI_AP_PROCEDURE Procedure, IN EFI_AP_PROCEDURE2 Procedure,
IN UINTN CpuIndex, IN UINTN CpuIndex,
IN OUT VOID *ProcArguments OPTIONAL, IN OUT VOID *ProcArguments OPTIONAL,
IN BOOLEAN BlockingMode IN MM_COMPLETION *Token,
IN UINTN TimeoutInMicroseconds,
IN OUT EFI_STATUS *CpuStatus
) )
{ {
if (CpuIndex >= gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus) { if (CpuIndex >= gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus) {
@ -952,24 +1228,190 @@ InternalSmmStartupThisAp (
} }
return EFI_INVALID_PARAMETER; 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); AcquireSpinLock (mSmmMpSyncData->CpuData[CpuIndex].Busy);
} else { } else {
if (!AcquireSpinLockOrFail (mSmmMpSyncData->CpuData[CpuIndex].Busy)) { if (!AcquireSpinLockOrFail (mSmmMpSyncData->CpuData[CpuIndex].Busy)) {
DEBUG((DEBUG_ERROR, "mSmmMpSyncData->CpuData[%d].Busy\n", CpuIndex)); DEBUG((DEBUG_ERROR, "Can't acquire mSmmMpSyncData->CpuData[%d].Busy\n", CpuIndex));
return EFI_INVALID_PARAMETER; return EFI_NOT_READY;
} }
*Token = (MM_COMPLETION) CreateToken ();
} }
mSmmMpSyncData->CpuData[CpuIndex].Procedure = Procedure; mSmmMpSyncData->CpuData[CpuIndex].Procedure = Procedure;
mSmmMpSyncData->CpuData[CpuIndex].Parameter = ProcArguments; 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); ReleaseSemaphore (mSmmMpSyncData->CpuData[CpuIndex].Run);
if (BlockingMode) { if (Token == NULL) {
AcquireSpinLock (mSmmMpSyncData->CpuData[CpuIndex].Busy); AcquireSpinLock (mSmmMpSyncData->CpuData[CpuIndex].Busy);
ReleaseSpinLock (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; return EFI_SUCCESS;
} }
@ -995,7 +1437,15 @@ SmmBlockingStartupThisAp (
IN OUT VOID *ProcArguments OPTIONAL 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 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; Cr2 = 0;
SaveCr2 (&Cr2); SaveCr2 (&Cr2);
//
// Call the user register Startup function first.
//
if (mSmmMpSyncData->StartupProcedure != NULL) {
mSmmMpSyncData->StartupProcedure (mSmmMpSyncData->StartupProcArgs);
}
// //
// Perform CPU specific entry hooks // Perform CPU specific entry hooks
// //
@ -1256,6 +1728,21 @@ Exit:
RestoreCr2 (Cr2); 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. Allocate buffer for all semaphores and spin locks.
@ -1469,3 +1956,40 @@ RegisterSmmEntry (
gSmmCpuPrivate->SmmCoreEntry = SmmEntryPoint; gSmmCpuPrivate->SmmCoreEntry = SmmEntryPoint;
return EFI_SUCCESS; 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;
}

View File

@ -34,6 +34,8 @@ SMM_CPU_PRIVATE_DATA mSmmCpuPrivateData = {
mSmmCpuPrivateData.SmmReservedSmramRegion, // SmmConfiguration.SmramReservedRegions mSmmCpuPrivateData.SmmReservedSmramRegion, // SmmConfiguration.SmramReservedRegions
RegisterSmmEntry // SmmConfiguration.RegisterSmmEntry RegisterSmmEntry // SmmConfiguration.RegisterSmmEntry
}, },
NULL, // pointer to Ap Wrapper Func array
{NULL, NULL}, // List_Entry for Tokens.
}; };
CPU_HOT_PLUG_DATA mCpuHotPlugData = { CPU_HOT_PLUG_DATA mCpuHotPlugData = {
@ -996,6 +998,22 @@ PiCpuSmmEntry (
); );
ASSERT_EFI_ERROR (Status); 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. // Expose address of CPU Hot Plug Data structure if CPU hot plug is supported.
// //

View File

@ -20,6 +20,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
#include <Protocol/SmmReadyToLock.h> #include <Protocol/SmmReadyToLock.h>
#include <Protocol/SmmCpuService.h> #include <Protocol/SmmCpuService.h>
#include <Protocol/SmmMemoryAttribute.h> #include <Protocol/SmmMemoryAttribute.h>
#include <Protocol/MmMp.h>
#include <Guid/AcpiS3Context.h> #include <Guid/AcpiS3Context.h>
#include <Guid/MemoryAttributesTable.h> #include <Guid/MemoryAttributesTable.h>
@ -197,6 +198,25 @@ typedef UINT32 SMM_CPU_ARRIVAL_EXCEPTIONS;
#define ARRIVAL_EXCEPTION_DELAYED 0x2 #define ARRIVAL_EXCEPTION_DELAYED 0x2
#define ARRIVAL_EXCEPTION_SMI_DISABLED 0x4 #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 // Private structure for the SMM CPU module that is stored in DXE Runtime memory
// Contains the SMM Configuration Protocols that is produced. // Contains the SMM Configuration Protocols that is produced.
@ -219,6 +239,10 @@ typedef struct {
EFI_SMM_ENTRY_POINT SmmCoreEntry; EFI_SMM_ENTRY_POINT SmmCoreEntry;
EFI_SMM_CONFIGURATION_PROTOCOL SmmConfiguration; EFI_SMM_CONFIGURATION_PROTOCOL SmmConfiguration;
PROCEDURE_WRAPPER *ApWrapperFunc;
LIST_ENTRY TokenList;
} SMM_CPU_PRIVATE_DATA; } SMM_CPU_PRIVATE_DATA;
extern SMM_CPU_PRIVATE_DATA *gSmmCpuPrivate; extern SMM_CPU_PRIVATE_DATA *gSmmCpuPrivate;
@ -226,6 +250,7 @@ extern CPU_HOT_PLUG_DATA mCpuHotPlugData;
extern UINTN mMaxNumberOfCpus; extern UINTN mMaxNumberOfCpus;
extern UINTN mNumberOfCpus; extern UINTN mNumberOfCpus;
extern EFI_SMM_CPU_PROTOCOL mSmmCpu; extern EFI_SMM_CPU_PROTOCOL mSmmCpu;
extern EFI_MM_MP_PROTOCOL mSmmMp;
/// ///
/// The mode of the CPU at the time an SMI occurs /// The mode of the CPU at the time an SMI occurs
@ -363,10 +388,12 @@ SmmRelocationSemaphoreComplete (
/// ///
typedef struct { typedef struct {
SPIN_LOCK *Busy; SPIN_LOCK *Busy;
volatile EFI_AP_PROCEDURE Procedure; volatile EFI_AP_PROCEDURE2 Procedure;
volatile VOID *Parameter; volatile VOID *Parameter;
volatile UINT32 *Run; volatile UINT32 *Run;
volatile BOOLEAN *Present; volatile BOOLEAN *Present;
SPIN_LOCK *Token;
EFI_STATUS *Status;
} SMM_CPU_DATA_BLOCK; } SMM_CPU_DATA_BLOCK;
typedef enum { typedef enum {
@ -388,6 +415,8 @@ typedef struct {
volatile SMM_CPU_SYNC_MODE EffectiveSyncMode; volatile SMM_CPU_SYNC_MODE EffectiveSyncMode;
volatile BOOLEAN SwitchBsp; volatile BOOLEAN SwitchBsp;
volatile BOOLEAN *CandidateBsp; volatile BOOLEAN *CandidateBsp;
EFI_AP_PROCEDURE StartupProcedure;
VOID *StartupProcArgs;
} SMM_DISPATCHER_MP_SYNC_DATA; } SMM_DISPATCHER_MP_SYNC_DATA;
#define SMM_PSD_OFFSET 0xfb00 #define SMM_PSD_OFFSET 0xfb00
@ -410,6 +439,7 @@ typedef struct {
SPIN_LOCK *Busy; SPIN_LOCK *Busy;
volatile UINT32 *Run; volatile UINT32 *Run;
volatile BOOLEAN *Present; volatile BOOLEAN *Present;
SPIN_LOCK *Token;
} SMM_CPU_SEMAPHORE_CPU; } SMM_CPU_SEMAPHORE_CPU;
/// ///
@ -1259,4 +1289,165 @@ RestoreCr2 (
IN UINTN Cr2 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 #endif

View File

@ -40,6 +40,8 @@
SmmProfileInternal.h SmmProfileInternal.h
SmramSaveState.c SmramSaveState.c
SmmCpuMemoryManagement.c SmmCpuMemoryManagement.c
SmmMp.h
SmmMp.c
[Sources.Ia32] [Sources.Ia32]
Ia32/Semaphore.c Ia32/Semaphore.c
@ -105,6 +107,7 @@
gEfiSmmReadyToLockProtocolGuid ## NOTIFY gEfiSmmReadyToLockProtocolGuid ## NOTIFY
gEfiSmmCpuServiceProtocolGuid ## PRODUCES gEfiSmmCpuServiceProtocolGuid ## PRODUCES
gEdkiiSmmMemoryAttributeProtocolGuid ## PRODUCES gEdkiiSmmMemoryAttributeProtocolGuid ## PRODUCES
gEfiMmMpProtocolGuid ## PRODUCES
[Guids] [Guids]
gEfiAcpiVariableGuid ## SOMETIMES_CONSUMES ## HOB # it is used for S3 boot. gEfiAcpiVariableGuid ## SOMETIMES_CONSUMES ## HOB # it is used for S3 boot.

View File

@ -0,0 +1,344 @@
/** @file
SMM MP protocol implementation
Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
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;
}

View File

@ -0,0 +1,286 @@
/** @file
Include file for SMM MP protocol implementation.
Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
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