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

Ni, Ray ray.ni at intel.com
Thu Jul 4 06:18:12 UTC 2019


1. mMaxNumberOfCpus and gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus are both used. Can you please refine the code to use only one?
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
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."
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.
5. *(CPUStatus + Index) can be changed to CPUStatus[Index]. Use [] instead of address operation.
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;
        }
      }
    }
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.
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.

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 (#43249): https://edk2.groups.io/g/devel/message/43249
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