[edk2-devel] [Patch v3 2/2] UefiCpuPkg/PiSmmCpuDxeSmm: Enable MM MP Protocol.

Dong, Eric eric.dong at intel.com
Mon Jul 8 09:18:32 UTC 2019


Hi Ray,

> -----Original Message-----
> From: Ni, Ray
> Sent: Thursday, July 4, 2019 2:18 PM
> To: devel at edk2.groups.io; Dong, Eric <eric.dong at intel.com>
> Cc: Laszlo Ersek <lersek at redhat.com>
> Subject: RE: [edk2-devel] [Patch v3 2/2] UefiCpuPkg/PiSmmCpuDxeSmm:
> Enable MM MP Protocol.
> 
> 1. mMaxNumberOfCpus and gSmmCpuPrivate-
> >SmmCoreEntryContext.NumberOfCpus are both used. Can you please
> refine the code to use only one?
[Eric] will do it in other patch.
 
> 2. I do think that MM_MP code needs to maintain a internal linked list
> containing all created tokens. Because it's possible that caller is written in
> below way:
>     DispatchProcedure (AP#1, &TokenA);
>     DispatchProcedure (AP#1, &TokenB);
>     DispatchProcedure (AP#1, &TokenC);
>     CheckOnProcedure (&TokenA);
>     Without having a list to contain all created tokens, CheckOnProcedure
> (&TokenA) may return NotFound.
>     The linked list can be freed upon exiting SMM
[Eric] will enable it in my next version change. 
 
> 3. IsCurrentToken() can be changed to look up the token in the linked list and
> the name can be changed to IsTokenInUse().
>     The function name reflects the spec wording "EFI_ALREADY_STARTED -
> Token is already in use for another procedure."	
[Eric]  will enable it in my next version change.

> 4. In InternalSmmStartup[AllAPs|ThisAp], Token can be acquired after Busy is
> acquired. Token is associated with the procedure.
>      Busy is associated with the individual processor.
[Eric]  will enable it in my next version change.

> 5. *(CPUStatus + Index) can be changed to CPUStatus[Index]. Use [] instead
> of address operation.
[Eric]  will enable it in my next version change.

> 6. Below error handling in InternalSmmStartupAllAPs may not be necessary.
> Just AcquireSpinLock (...Busy) should be ok.
>     //
>     // Make sure all BUSY should be acquired.
>     //
>     for (Index = mMaxNumberOfCpus; Index-- > 0;) {
>       if (Index != gSmmCpuPrivate-
> >SmmCoreEntryContext.CurrentlyExecutingCpu && *(mSmmMpSyncData-
> >CpuData[Index].Present)) {
>         if (!AcquireSpinLockOrFail (mSmmMpSyncData->CpuData[Index].Busy)) {
>           DEBUG((DEBUG_ERROR, "Can't acquire mSmmMpSyncData-
> >CpuData[%d].Busy\n", Index));
> 
>           //
>           // Release BUSY accquired before.
>           //
>           for (CpuCount = mMaxNumberOfCpus; CpuCount -- > Index;) {
>             if (CpuCount != gSmmCpuPrivate-
> >SmmCoreEntryContext.CurrentlyExecutingCpu && *(mSmmMpSyncData-
> >CpuData[CpuCount].Present)) {
>               ReleaseSpinLock (mSmmMpSyncData->CpuData[CpuCount].Busy);
>             }
>           }
> 
>           ReleaseSpinLock (Token);
>           return EFI_INVALID_PARAMETER;
>         }
>       }
>     }
[Eric]  will enable it in my next version change.

> 7. IsTaskFinishInAllAPs() can be used by BSPHandler() as well. The function
> name can be WaitForAllAPsNotBusy() or some other name because the
> function
>     has side affect, not just check.
[Eric]  will enable it in my next version change.

> 8. Maybe you don't need to have a mApTokenLock[]. For old style async
> SMM MP call, the token is not returned to caller. But it's still possible to be
> freed
>      as long as it's inserted to the linked list.
[Eric]  will enable it in my next version change.

Thanks,
Eric
> 
> Thanks,
> Ray
> 
> > -----Original Message-----
> > From: devel at edk2.groups.io <devel at edk2.groups.io> On Behalf Of Dong,
> > Eric
> > Sent: Wednesday, July 3, 2019 10:43 AM
> > To: devel at edk2.groups.io
> > Cc: Ni, Ray <ray.ni at intel.com>; Laszlo Ersek <lersek at redhat.com>
> > Subject: [edk2-devel] [Patch v3 2/2] UefiCpuPkg/PiSmmCpuDxeSmm:
> Enable
> > MM MP Protocol.
> >
> > v3 changes:
> > 1. Fix Token clean up too early caused CheckProcedure return error.
> >
> > v2 changes:
> > 1. Remove some duplicated global variables.
> > 2. Enhance token design to support multiple task trig for different APs at
> the
> > same time.
> >
> > V1 changes:
> > REF: https://bugzilla.tianocore.org/show_bug.cgi?id=1937
> >
> > Add MM Mp Protocol in PiSmmCpuDxeSmm driver.
> >
> > Cc: Ray Ni <ray.ni at intel.com>
> > Cc: Laszlo Ersek <lersek at redhat.com>
> > Signed-off-by: Eric Dong <eric.dong at intel.com>
> > ---
> >  UefiCpuPkg/PiSmmCpuDxeSmm/MpService.c        | 536
> > ++++++++++++++++++-
> >  UefiCpuPkg/PiSmmCpuDxeSmm/PiSmmCpuDxeSmm.c   |  11 +
> >  UefiCpuPkg/PiSmmCpuDxeSmm/PiSmmCpuDxeSmm.h   | 160 +++++-
> >  UefiCpuPkg/PiSmmCpuDxeSmm/PiSmmCpuDxeSmm.inf |   3 +
> >  UefiCpuPkg/PiSmmCpuDxeSmm/SmmMp.c            | 372 +++++++++++++
> >  UefiCpuPkg/PiSmmCpuDxeSmm/SmmMp.h            | 286 ++++++++++
> >  6 files changed, 1351 insertions(+), 17 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..13858f5d2d 100644
> > --- a/UefiCpuPkg/PiSmmCpuDxeSmm/MpService.c
> > +++ b/UefiCpuPkg/PiSmmCpuDxeSmm/MpService.c
> > @@ -22,6 +22,7 @@ UINTN                                       mSemaphoreSize;
> >  SPIN_LOCK                                   *mPFLock = NULL;
> >  SMM_CPU_SYNC_MODE                           mCpuSmmSyncMode;
> >  BOOLEAN                                     mMachineCheckSupported = FALSE;
> > +SPIN_LOCK                                   **mApTokenLock;
> >
> >  /**
> >    Performs an atomic compare exchange operation to get semaphore.
> > @@ -146,6 +147,45 @@ ReleaseAllAPs (
> >    }
> >  }
> >
> > +/**
> > +  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
> > +IsTaskFinishInAllAPs (
> > +  IN BOOLEAN                        BlockMode
> > +  )
> > +{
> > +  UINTN                             Index;
> > +
> > +  for (Index = mMaxNumberOfCpus; Index-- > 0;) {
> > +    //
> > +    // Ignore BSP and APs which not call in SMM.
> > +    //
> > +    if ((Index == gSmmCpuPrivate-
> > >SmmCoreEntryContext.CurrentlyExecutingCpu) ||
> (!*(mSmmMpSyncData-
> > >CpuData[Index].Present))) {
> > +      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;
> > +}
> > +
> >  /**
> >    Checks if all CPUs (with certain exceptions) have checked in for this SMI
> run
> >
> > @@ -347,6 +387,92 @@ ReplaceOSMtrrs (
> >    MtrrSetAllMtrrs (&gSmiMtrrs);
> >  }
> >
> > +/**
> > +  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 ((ApIndex != gSmmCpuPrivate-
> > >SmmCoreEntryContext.CurrentlyExecutingCpu) &&
> > +        *(mSmmMpSyncData->CpuData[ApIndex].Present) &&
> > +        (mSmmMpSyncData->CpuData[ApIndex].Token != NULL)) {
> > +      for (ApIndex2 = ApIndex; ApIndex2-- > 0;) {
> > +        if ((ApIndex2 != gSmmCpuPrivate-
> > >SmmCoreEntryContext.CurrentlyExecutingCpu) &&
> > +            *(mSmmMpSyncData->CpuData[ApIndex2].Present) &&
> > +            (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
> > +CleanUpStatusFlags (
> > +  IN UINTN                  CpuIndex
> > +  )
> > +{
> > +  UINTN                             Index;
> > +
> > +  if (InStartAllApsCall ()) {
> > +    //
> > +    // In Start All APs mode, make sure all APs have finished task.
> > +    //
> > +    if (IsTaskFinishInAllAPs (FALSE)) {
> > +      //
> > +      // Clean the flags update in the function call.
> > +      //
> > +      for (Index = mMaxNumberOfCpus; Index-- > 0;) {
> > +        //
> > +        // Only In SMM APs need to be clean up.
> > +        //
> > +        if (mSmmMpSyncData->CpuData[Index].Present) {
> > +          if (mSmmMpSyncData->CpuData[Index].Token != NULL) {
> > +            ReleaseSpinLock (mSmmMpSyncData->CpuData[Index].Token);
> > +            break;
> > +          }
> > +        }
> > +      }
> > +    }
> > +  } else {
> > +    //
> > +    // In single AP mode.
> > +    //
> > +    if (mSmmMpSyncData->CpuData[CpuIndex].Token != NULL) {
> > +      //
> > +      // Free the wrapper buffer in non-block mode used by
> > SmmMpDispatchProcedure function.
> > +      //
> > +      if (mApTokenLock[CpuIndex] == mSmmMpSyncData-
> > >CpuData[CpuIndex].Token) {
> > +        FreePool ((VOID *)mSmmMpSyncData-
> > >CpuData[CpuIndex].Parameter);
> > +      }
> > +
> > +      ReleaseSpinLock (mSmmMpSyncData->CpuData[CpuIndex].Token);
> > +    }
> > +  }
> > +}
> > +
> >  /**
> >    SMI handler for BSP.
> >
> > @@ -604,6 +730,7 @@ APHandler (
> >    UINT64                            Timer;
> >    UINTN                             BspIndex;
> >    MTRR_SETTINGS                     Mtrrs;
> > +  EFI_STATUS                        ProcedureStatus;
> >
> >    //
> >    // Timeout BSP
> > @@ -730,14 +857,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);
> > +
> > +    CleanUpStatusFlags (CpuIndex);
> >    }
> >
> >    if (SmmCpuFeaturesNeedConfigureMtrrs()) {
> > @@ -906,13 +1038,95 @@ Gen4GPageTable (
> >    return (UINT32)(UINTN)PageTable;
> >  }
> >
> > +/**
> > +  Checks whether the input token is the current used token.
> > +
> > +  @param[in]  CpuIndex   Cpu Index.
> > +  @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
> > +IsCurrentToken (
> > +  IN UINTN               CpuIndex,
> > +  IN SPIN_LOCK           *Token
> > +  )
> > +{
> > +  UINTN         Index;
> > +
> > +  if (Token == NULL) {
> > +    return FALSE;
> > +  }
> > +
> > +  if (CpuIndex == (UINTN) -1) {
> > +    for (Index = 0; Index < gSmmCpuPrivate-
> > >SmmCoreEntryContext.NumberOfCpus; Index ++) {
> > +      if (mSmmMpSyncData->CpuData[Index].Present &&
> > +          (mSmmMpSyncData->CpuData[Index].Token != NULL) &&
> > +          (mSmmMpSyncData->CpuData[Index].Token == Token)) {
> > +        return TRUE;
> > +      }
> > +    }
> > +
> > +    return FALSE;
> > +  }
> > +
> > +  return mSmmMpSyncData->CpuData[CpuIndex].Token == 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
> > +  )
> > +{
> > +  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 +1137,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      SPIN_LOCK                      *Token,
> > +  IN      UINTN                          TimeoutInMicroseconds,
> > +  IN OUT  EFI_STATUS                     *CpuStatus
> >    )
> >  {
> >    if (CpuIndex >= gSmmCpuPrivate-
> >SmmCoreEntryContext.NumberOfCpus)
> > {
> > @@ -952,24 +1168,208 @@ 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 (IsCurrentToken (CpuIndex, Token)) {
> > +    return EFI_ALREADY_STARTED;
> > +  }
> >
> > -  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));
> > +    if (!AcquireSpinLockOrFail (Token)) {
> > +      DEBUG((DEBUG_ERROR, "Token can't acquire\n"));
> >        return EFI_INVALID_PARAMETER;
> >      }
> > +    if (!AcquireSpinLockOrFail (mSmmMpSyncData-
> > >CpuData[CpuIndex].Busy)) {
> > +      DEBUG((DEBUG_ERROR, "Can't acquire mSmmMpSyncData-
> > >CpuData[%d].Busy\n", CpuIndex));
> > +      ReleaseSpinLock (Token);
> > +      return EFI_NOT_READY;
> > +    }
> >    }
> >
> >    mSmmMpSyncData->CpuData[CpuIndex].Procedure = Procedure;
> >    mSmmMpSyncData->CpuData[CpuIndex].Parameter = ProcArguments;
> > +  mSmmMpSyncData->CpuData[CpuIndex].Token     = 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   SPIN_LOCK                     *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;
> > +  }
> > +  if (IsCurrentToken ((UINTN)-1, Token)) {
> > +    return EFI_ALREADY_STARTED;
> > +  }
> > +
> > +  CpuCount = 0;
> > +  for (Index = mMaxNumberOfCpus; Index-- > 0;) {
> > +    if (*mSmmMpSyncData->CpuData[Index].Present && (Index !=
> > gSmmCpuPrivate->SmmCoreEntryContext.CurrentlyExecutingCpu)) {
> > +      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) {
> > +    //
> > +    // Make sure all BUSY should be acquired.
> > +    //
> > +    for (Index = mMaxNumberOfCpus; Index-- > 0;) {
> > +      if (Index != gSmmCpuPrivate-
> > >SmmCoreEntryContext.CurrentlyExecutingCpu && *(mSmmMpSyncData-
> > >CpuData[Index].Present)) {
> > +        AcquireSpinLock (mSmmMpSyncData->CpuData[Index].Busy);
> > +      }
> > +    }
> > +  } else {
> > +    if (!AcquireSpinLockOrFail (Token)) {
> > +      return EFI_INVALID_PARAMETER;
> > +    }
> > +
> > +    //
> > +    // Make sure all BUSY should be acquired.
> > +    //
> > +    for (Index = mMaxNumberOfCpus; Index-- > 0;) {
> > +      if (Index != gSmmCpuPrivate-
> > >SmmCoreEntryContext.CurrentlyExecutingCpu && *(mSmmMpSyncData-
> > >CpuData[Index].Present)) {
> > +        if (!AcquireSpinLockOrFail (mSmmMpSyncData-
> >CpuData[Index].Busy))
> > {
> > +          DEBUG((DEBUG_ERROR, "Can't acquire mSmmMpSyncData-
> > >CpuData[%d].Busy\n", Index));
> > +
> > +          //
> > +          // Release BUSY accquired before.
> > +          //
> > +          for (CpuCount = mMaxNumberOfCpus; CpuCount -- > Index;) {
> > +            if (CpuCount != gSmmCpuPrivate-
> > >SmmCoreEntryContext.CurrentlyExecutingCpu && *(mSmmMpSyncData-
> > >CpuData[CpuCount].Present)) {
> > +              ReleaseSpinLock (mSmmMpSyncData->CpuData[CpuCount].Busy);
> > +            }
> > +          }
> > +
> > +          ReleaseSpinLock (Token);
> > +          return EFI_INVALID_PARAMETER;
> > +        }
> > +      }
> > +    }
> > +  }
> > +
> > +  for (Index = mMaxNumberOfCpus; Index-- > 0;) {
> > +    if (Index != gSmmCpuPrivate-
> > >SmmCoreEntryContext.CurrentlyExecutingCpu && *(mSmmMpSyncData-
> > >CpuData[Index].Present)) {
> > +      mSmmMpSyncData->CpuData[Index].Procedure =
> > (EFI_AP_PROCEDURE2) Procedure;
> > +      mSmmMpSyncData->CpuData[Index].Parameter =
> > ProcedureArguments;
> > +      mSmmMpSyncData->CpuData[Index].Token     = 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.
> > +    //
> > +    IsTaskFinishInAllAPs (TRUE);
> > +  }
> > +
> > +  return EFI_SUCCESS;
> > +}
> > +
> > +/**
> > +  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 +1395,24 @@ SmmBlockingStartupThisAp (
> >    IN OUT  VOID                      *ProcArguments OPTIONAL
> >    )
> >  {
> > -  return InternalSmmStartupThisAp(Procedure, CpuIndex, ProcArguments,
> > TRUE);
> > +  PROCEDURE_WRAPPER *Wrapper;
> > +  EFI_STATUS        Status;
> > +
> > +  Wrapper = AllocatePool (sizeof(PROCEDURE_WRAPPER));
> > +  if (Wrapper == NULL) {
> > +    return EFI_OUT_OF_RESOURCES;
> > +  }
> > +  Wrapper->Procedure = Procedure;
> > +  Wrapper->ProcedureArgument = ProcArguments;
> > +
> > +  //
> > +  // Use wrapper function to convert EFI_AP_PROCEDURE to
> > EFI_AP_PROCEDURE2.
> > +  //
> > +  Status = InternalSmmStartupThisAp(ProcedureWrapper, CpuIndex,
> > Wrapper, NULL, 0, NULL);
> > +
> > +  FreePool (Wrapper);
> > +
> > +  return Status;
> >  }
> >
> >  /**
> > @@ -1020,7 +1437,37 @@ SmmStartupThisAp (
> >    IN OUT  VOID                      *ProcArguments OPTIONAL
> >    )
> >  {
> > -  return InternalSmmStartupThisAp(Procedure, CpuIndex, ProcArguments,
> > FeaturePcdGet (PcdCpuSmmBlockStartupThisAp));
> > +  SPIN_LOCK               *CpuToken;
> > +  PROCEDURE_WRAPPER       *Wrapper;
> > +  EFI_STATUS              Status;
> > +
> > +  Wrapper = AllocatePool (sizeof(PROCEDURE_WRAPPER));
> > +  if (Wrapper == NULL) {
> > +    return EFI_OUT_OF_RESOURCES;
> > +  }
> > +  Wrapper->Procedure = Procedure;
> > +  Wrapper->ProcedureArgument = ProcArguments;
> > +
> > +  if (FeaturePcdGet (PcdCpuSmmBlockStartupThisAp)) {
> > +    CpuToken = NULL;
> > +  } else {
> > +    CpuToken = mApTokenLock[CpuIndex];
> > +  }
> > +
> > +  //
> > +  // Use wrapper function to convert EFI_AP_PROCEDURE to
> > EFI_AP_PROCEDURE2.
> > +  //
> > +  Status = InternalSmmStartupThisAp(ProcedureWrapper, CpuIndex,
> > Wrapper, CpuToken, 0, NULL);
> > +
> > +  //
> > +  // Free wrapper buffer for block mode.
> > +  // Non-block mode frees buffer in ApHandler function.
> > +  //
> > +  if (CpuToken == NULL) {
> > +    FreePool (Wrapper);
> > +  }
> > +
> > +  return Status;
> >  }
> >
> >  /**
> > @@ -1112,6 +1559,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
> >    //
> > @@ -1273,13 +1727,17 @@ InitializeSmmCpuSemaphores (
> >    UINTN                      Pages;
> >    UINTN                      *SemaphoreBlock;
> >    UINTN                      SemaphoreAddr;
> > +  UINTN                      ApTokenSize;
> > +  UINTN                      CpuIndex;
> >
> >    SemaphoreSize   = GetSpinLockProperties ();
> >    ProcessorCount = gSmmCpuPrivate-
> > >SmmCoreEntryContext.NumberOfCpus;
> >    GlobalSemaphoresSize = (sizeof (SMM_CPU_SEMAPHORE_GLOBAL) /
> > sizeof (VOID *)) * SemaphoreSize;
> >    CpuSemaphoresSize    = (sizeof (SMM_CPU_SEMAPHORE_CPU) / sizeof
> > (VOID *)) * ProcessorCount * SemaphoreSize;
> > -  TotalSize = GlobalSemaphoresSize + CpuSemaphoresSize;
> > +  ApTokenSize = SemaphoreSize * ProcessorCount;
> > +  TotalSize = GlobalSemaphoresSize + CpuSemaphoresSize + ApTokenSize;
> >    DEBUG((EFI_D_INFO, "One Semaphore Size    = 0x%x\n",
> SemaphoreSize));
> > +  DEBUG((EFI_D_INFO, "Token Spin Lock Size = 0x%x\n", ApTokenSize));
> >    DEBUG((EFI_D_INFO, "Total Semaphores Size = 0x%x\n", TotalSize));
> >    Pages = EFI_SIZE_TO_PAGES (TotalSize);
> >    SemaphoreBlock = AllocatePages (Pages);
> > @@ -1305,10 +1763,19 @@ InitializeSmmCpuSemaphores (
> >    mSmmCpuSemaphores.SemaphoreCpu.Run     = (UINT32
> > *)SemaphoreAddr;
> >    SemaphoreAddr += ProcessorCount * SemaphoreSize;
> >    mSmmCpuSemaphores.SemaphoreCpu.Present = (BOOLEAN
> > *)SemaphoreAddr;
> > +  SemaphoreAddr += ProcessorCount * SemaphoreSize;
> > +  mSmmCpuSemaphores.SemaphoreCpu.Token = (SPIN_LOCK
> > *)SemaphoreAddr;
> >
> >    mPFLock                       = mSmmCpuSemaphores.SemaphoreGlobal.PFLock;
> >    mConfigSmmCodeAccessCheckLock =
> > mSmmCpuSemaphores.SemaphoreGlobal.CodeAccessCheckLock;
> >
> > +  mApTokenLock = AllocatePool (sizeof (SPIN_LOCK *) * gSmmCpuPrivate-
> > >SmmCoreEntryContext.NumberOfCpus);
> > +  ASSERT (mApTokenLock != NULL);
> > +  for (CpuIndex = 0; CpuIndex < gSmmCpuPrivate-
> > >SmmCoreEntryContext.NumberOfCpus; CpuIndex ++) {
> > +    mApTokenLock[CpuIndex] = (SPIN_LOCK
> > *)((UINTN)mSmmCpuSemaphores.SemaphoreCpu.Token +
> SemaphoreSize
> > * CpuIndex);
> > +    InitializeSpinLock (mApTokenLock[CpuIndex]);
> > +  }
> > +
> >    mSemaphoreSize = SemaphoreSize;
> >  }
> >
> > @@ -1469,3 +1936,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..dd1b3be0f5 100644
> > --- a/UefiCpuPkg/PiSmmCpuDxeSmm/PiSmmCpuDxeSmm.c
> > +++ b/UefiCpuPkg/PiSmmCpuDxeSmm/PiSmmCpuDxeSmm.c
> > @@ -996,6 +996,17 @@ PiCpuSmmEntry (
> >                      );
> >    ASSERT_EFI_ERROR (Status);
> >
> > +  //
> > +  // 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..5df09687e1 100644
> > --- a/UefiCpuPkg/PiSmmCpuDxeSmm/PiSmmCpuDxeSmm.h
> > +++ b/UefiCpuPkg/PiSmmCpuDxeSmm/PiSmmCpuDxeSmm.h
> > @@ -20,6 +20,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
> >  #include <Protocol/SmmReadyToLock.h>
> >  #include <Protocol/SmmCpuService.h>
> >  #include <Protocol/SmmMemoryAttribute.h>
> > +#include <Protocol/MmMp.h>
> >
> >  #include <Guid/AcpiS3Context.h>
> >  #include <Guid/MemoryAttributesTable.h>
> > @@ -221,11 +222,20 @@ typedef struct {
> >    EFI_SMM_CONFIGURATION_PROTOCOL  SmmConfiguration;
> >  } SMM_CPU_PRIVATE_DATA;
> >
> > +//
> > +// Wrapper used to convert EFI_AP_PROCEDURE2 and
> EFI_AP_PROCEDURE.
> > +//
> > +typedef struct {
> > +  EFI_AP_PROCEDURE  Procedure;
> > +  VOID              *ProcedureArgument;
> > +} PROCEDURE_WRAPPER;
> > +
> >  extern SMM_CPU_PRIVATE_DATA  *gSmmCpuPrivate;
> >  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 +373,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 +400,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 +424,7 @@ typedef struct {
> >    SPIN_LOCK                         *Busy;
> >    volatile UINT32                   *Run;
> >    volatile BOOLEAN                  *Present;
> > +  SPIN_LOCK                         *Token;
> >  } SMM_CPU_SEMAPHORE_CPU;
> >
> >  ///
> > @@ -439,6 +454,7 @@ extern SPIN_LOCK
> > *mConfigSmmCodeAccessCheckLock;
> >  extern EFI_SMRAM_DESCRIPTOR                *mSmmCpuSmramRanges;
> >  extern UINTN                               mSmmCpuSmramRangeCount;
> >  extern UINT8                               mPhysicalAddressBits;
> > +extern SPIN_LOCK                           **mApTokenLock;
> >
> >  //
> >  // Copy of the PcdPteMemoryEncryptionAddressOrMask
> > @@ -1259,4 +1275,146 @@ 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      SPIN_LOCK                      *Token,
> > +  IN      UINTN                          TimeoutInMicroseconds,
> > +  IN OUT  EFI_STATUS                     *CpuStatus
> > +  );
> > +
> > +/**
> > +  Checks whether the input token is the current used token.
> > +
> > +  @param[in]  CpuIndex   Cpu Index.
> > +  @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
> > +IsCurrentToken (
> > +  IN UINTN               CpuIndex,
> > +  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
> > +  );
> > +
> > +/**
> > +  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   SPIN_LOCK                     *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
> > +  );
> > +
> >  #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..bfbc78691f
> > --- /dev/null
> > +++ b/UefiCpuPkg/PiSmmCpuDxeSmm/SmmMp.c
> > @@ -0,0 +1,372 @@
> > +/** @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
> > +  )
> > +{
> > +  SPIN_LOCK      *CpuToken;
> > +
> > +  if (Token != NULL) {
> > +    CpuToken = AllocatePool (sizeof (SPIN_LOCK));
> > +    ASSERT (CpuToken != NULL);
> > +    if (CpuToken == NULL) {
> > +      return EFI_OUT_OF_RESOURCES;
> > +    }
> > +    InitializeSpinLock ((SPIN_LOCK *)(CpuToken));
> > +
> > +    *Token = (MM_COMPLETION)CpuToken;
> > +  }
> > +
> > +  return InternalSmmStartupThisAp (
> > +    Procedure,
> > +    CpuNumber,
> > +    ProcedureArguments,
> > +    Token != NULL ? CpuToken : NULL,
> > +    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
> > +  )
> > +{
> > +  SPIN_LOCK        *CpuToken;
> > +
> > +  if (Token != NULL) {
> > +    CpuToken = AllocatePool (sizeof (SPIN_LOCK));
> > +    ASSERT (CpuToken != NULL);
> > +    if (CpuToken == NULL) {
> > +      return EFI_OUT_OF_RESOURCES;
> > +    }
> > +    InitializeSpinLock ((SPIN_LOCK *)(CpuToken));
> > +
> > +    *Token = (MM_COMPLETION)CpuToken;
> > +  }
> > +
> > +  return InternalSmmStartupAllAPs(
> > +    Procedure,
> > +    TimeoutInMicroseconds,
> > +    ProcedureArguments,
> > +    Token != NULL ? CpuToken : NULL,
> > +    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 (!IsCurrentToken ((UINTN)-1, (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.<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
> > --
> > 2.21.0.windows.1
> >
> >
> > 


-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.

View/Reply Online (#43397): https://edk2.groups.io/g/devel/message/43397
Mute This Topic: https://groups.io/mt/32293746/1813853
Group Owner: devel+owner at edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub  [edk2-devel-archive at redhat.com]
-=-=-=-=-=-=-=-=-=-=-=-





More information about the edk2-devel-archive mailing list