]> git.proxmox.com Git - mirror_edk2.git/blobdiff - UefiCpuPkg/PiSmmCpuDxeSmm/MpService.c
UefiCpuPkg: Change use of EFI_D_* to DEBUG_*
[mirror_edk2.git] / UefiCpuPkg / PiSmmCpuDxeSmm / MpService.c
index 3b0b3b52ac21b2b2a2eb6045ad4ca4024ca73381..48f9c330b8cf450b43cd14f9fb5e905b8eef3cf8 100644 (file)
@@ -1,16 +1,10 @@
 /** @file\r
 SMM MP service implementation\r
 \r
-Copyright (c) 2009 - 2019, Intel Corporation. All rights reserved.<BR>\r
+Copyright (c) 2009 - 2021, Intel Corporation. All rights reserved.<BR>\r
 Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR>\r
 \r
-This program and the accompanying materials\r
-are licensed and made available under the terms and conditions of the BSD License\r
-which accompanies this distribution.  The full text of the license may be found at\r
-http://opensource.org/licenses/bsd-license.php\r
-\r
-THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
-WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+SPDX-License-Identifier: BSD-2-Clause-Patent\r
 \r
 **/\r
 \r
@@ -28,6 +22,9 @@ UINTN                                       mSemaphoreSize;
 SPIN_LOCK                                   *mPFLock = NULL;\r
 SMM_CPU_SYNC_MODE                           mCpuSmmSyncMode;\r
 BOOLEAN                                     mMachineCheckSupported = FALSE;\r
+MM_COMPLETION                               mSmmStartupThisApToken;\r
+\r
+extern UINTN mSmmShadowStackSize;\r
 \r
 /**\r
   Performs an atomic compare exchange operation to get semaphore.\r
@@ -46,14 +43,18 @@ WaitForSemaphore (
 {\r
   UINT32                            Value;\r
 \r
-  do {\r
+  for (;;) {\r
     Value = *Sem;\r
-  } while (Value == 0 ||\r
-           InterlockedCompareExchange32 (\r
-             (UINT32*)Sem,\r
-             Value,\r
-             Value - 1\r
-             ) != Value);\r
+    if (Value != 0 &&\r
+        InterlockedCompareExchange32 (\r
+          (UINT32*)Sem,\r
+          Value,\r
+          Value - 1\r
+          ) == Value) {\r
+      break;\r
+    }\r
+    CpuPause ();\r
+  }\r
   return Value - 1;\r
 }\r
 \r
@@ -142,11 +143,9 @@ ReleaseAllAPs (
   )\r
 {\r
   UINTN                             Index;\r
-  UINTN                             BspIndex;\r
 \r
-  BspIndex = mSmmMpSyncData->BspIndex;\r
-  for (Index = mMaxNumberOfCpus; Index-- > 0;) {\r
-    if (Index != BspIndex && *(mSmmMpSyncData->CpuData[Index].Present)) {\r
+  for (Index = 0; Index < mMaxNumberOfCpus; Index++) {\r
+    if (IsPresentAp (Index)) {\r
       ReleaseSemaphore (mSmmMpSyncData->CpuData[Index].Run);\r
     }\r
   }\r
@@ -178,7 +177,7 @@ AllCpusInSmmWithExceptions (
 \r
   CpuData = mSmmMpSyncData->CpuData;\r
   ProcessorInfo = gSmmCpuPrivate->ProcessorInfo;\r
-  for (Index = mMaxNumberOfCpus; Index-- > 0;) {\r
+  for (Index = 0; Index < mMaxNumberOfCpus; Index++) {\r
     if (!(*(CpuData[Index].Present)) && ProcessorInfo[Index].ProcessorId != INVALID_APIC_ID) {\r
       if (((Exceptions & ARRIVAL_EXCEPTION_DELAYED) != 0) && SmmCpuFeaturesGetSmmRegister (Index, SmmRegSmmDelayed) != 0) {\r
         continue;\r
@@ -313,7 +312,7 @@ SmmWaitForApArrival (
     //\r
     // Send SMI IPIs to bring outside processors in\r
     //\r
-    for (Index = mMaxNumberOfCpus; Index-- > 0;) {\r
+    for (Index = 0; Index < mMaxNumberOfCpus; Index++) {\r
       if (!(*(mSmmMpSyncData->CpuData[Index].Present)) && gSmmCpuPrivate->ProcessorInfo[Index].ProcessorId != INVALID_APIC_ID) {\r
         SendSmiIpi ((UINT32)gSmmCpuPrivate->ProcessorInfo[Index].ProcessorId);\r
       }\r
@@ -353,6 +352,100 @@ ReplaceOSMtrrs (
   MtrrSetAllMtrrs (&gSmiMtrrs);\r
 }\r
 \r
+/**\r
+  Wheck whether task has been finished by all APs.\r
+\r
+  @param       BlockMode   Whether did it in block mode or non-block mode.\r
+\r
+  @retval      TRUE        Task has been finished by all APs.\r
+  @retval      FALSE       Task not has been finished by all APs.\r
+\r
+**/\r
+BOOLEAN\r
+WaitForAllAPsNotBusy (\r
+  IN BOOLEAN                        BlockMode\r
+  )\r
+{\r
+  UINTN                             Index;\r
+\r
+  for (Index = 0; Index < mMaxNumberOfCpus; Index++) {\r
+    //\r
+    // Ignore BSP and APs which not call in SMM.\r
+    //\r
+    if (!IsPresentAp(Index)) {\r
+      continue;\r
+    }\r
+\r
+    if (BlockMode) {\r
+      AcquireSpinLock(mSmmMpSyncData->CpuData[Index].Busy);\r
+      ReleaseSpinLock(mSmmMpSyncData->CpuData[Index].Busy);\r
+    } else {\r
+      if (AcquireSpinLockOrFail (mSmmMpSyncData->CpuData[Index].Busy)) {\r
+        ReleaseSpinLock(mSmmMpSyncData->CpuData[Index].Busy);\r
+      } else {\r
+        return FALSE;\r
+      }\r
+    }\r
+  }\r
+\r
+  return TRUE;\r
+}\r
+\r
+/**\r
+  Check whether it is an present AP.\r
+\r
+  @param   CpuIndex      The AP index which calls this function.\r
+\r
+  @retval  TRUE           It's a present AP.\r
+  @retval  TRUE           This is not an AP or it is not present.\r
+\r
+**/\r
+BOOLEAN\r
+IsPresentAp (\r
+  IN UINTN        CpuIndex\r
+  )\r
+{\r
+  return ((CpuIndex != gSmmCpuPrivate->SmmCoreEntryContext.CurrentlyExecutingCpu) &&\r
+    *(mSmmMpSyncData->CpuData[CpuIndex].Present));\r
+}\r
+\r
+/**\r
+  Clean up the status flags used during executing the procedure.\r
+\r
+  @param   CpuIndex      The AP index which calls this function.\r
+\r
+**/\r
+VOID\r
+ReleaseToken (\r
+  IN UINTN                  CpuIndex\r
+  )\r
+{\r
+  PROCEDURE_TOKEN                         *Token;\r
+\r
+  Token = mSmmMpSyncData->CpuData[CpuIndex].Token;\r
+\r
+  if (InterlockedDecrement (&Token->RunningApCount) == 0) {\r
+    ReleaseSpinLock (Token->SpinLock);\r
+  }\r
+\r
+  mSmmMpSyncData->CpuData[CpuIndex].Token = NULL;\r
+}\r
+\r
+/**\r
+  Free the tokens in the maintained list.\r
+\r
+**/\r
+VOID\r
+ResetTokens (\r
+  VOID\r
+  )\r
+{\r
+  //\r
+  // Reset the FirstFreeToken to the beginning of token list upon exiting SMI.\r
+  //\r
+  gSmmCpuPrivate->FirstFreeToken = GetFirstNode (&gSmmCpuPrivate->TokenList);\r
+}\r
+\r
 /**\r
   SMI handler for BSP.\r
 \r
@@ -482,12 +575,7 @@ BSPHandler (
   //\r
   // Make sure all APs have completed their pending none-block tasks\r
   //\r
-  for (Index = mMaxNumberOfCpus; Index-- > 0;) {\r
-    if (Index != CpuIndex && *(mSmmMpSyncData->CpuData[Index].Present)) {\r
-      AcquireSpinLock (mSmmMpSyncData->CpuData[Index].Busy);\r
-      ReleaseSpinLock (mSmmMpSyncData->CpuData[Index].Busy);\r
-    }\r
-  }\r
+  WaitForAllAPsNotBusy (TRUE);\r
 \r
   //\r
   // Perform the remaining tasks\r
@@ -511,7 +599,7 @@ BSPHandler (
     //\r
     while (TRUE) {\r
       PresentCount = 0;\r
-      for (Index = mMaxNumberOfCpus; Index-- > 0;) {\r
+      for (Index = 0; Index < mMaxNumberOfCpus; Index++) {\r
         if (*(mSmmMpSyncData->CpuData[Index].Present)) {\r
           PresentCount ++;\r
         }\r
@@ -578,6 +666,11 @@ BSPHandler (
   //\r
   WaitForAllAPs (ApCount);\r
 \r
+  //\r
+  // Reset the tokens buffer.\r
+  //\r
+  ResetTokens ();\r
+\r
   //\r
   // Reset BspIndex to -1, meaning BSP has not been elected.\r
   //\r
@@ -610,6 +703,7 @@ APHandler (
   UINT64                            Timer;\r
   UINTN                             BspIndex;\r
   MTRR_SETTINGS                     Mtrrs;\r
+  EFI_STATUS                        ProcedureStatus;\r
 \r
   //\r
   // Timeout BSP\r
@@ -736,9 +830,16 @@ APHandler (
     //\r
     // Invoke the scheduled procedure\r
     //\r
-    (*mSmmMpSyncData->CpuData[CpuIndex].Procedure) (\r
-      (VOID*)mSmmMpSyncData->CpuData[CpuIndex].Parameter\r
-      );\r
+    ProcedureStatus = (*mSmmMpSyncData->CpuData[CpuIndex].Procedure) (\r
+                          (VOID*)mSmmMpSyncData->CpuData[CpuIndex].Parameter\r
+                          );\r
+    if (mSmmMpSyncData->CpuData[CpuIndex].Status != NULL) {\r
+      *mSmmMpSyncData->CpuData[CpuIndex].Status = ProcedureStatus;\r
+    }\r
+\r
+    if (mSmmMpSyncData->CpuData[CpuIndex].Token != NULL) {\r
+      ReleaseToken (CpuIndex);\r
+    }\r
 \r
     //\r
     // Release BUSY\r
@@ -822,7 +923,7 @@ Gen4GPageTable (
     // Add two more pages for known good stack and stack guard page,\r
     // then find the lower 2MB aligned address.\r
     //\r
-    High2MBoundary = (mSmmStackArrayEnd - mSmmStackSize + EFI_PAGE_SIZE * 2) & ~(SIZE_2MB-1);\r
+    High2MBoundary = (mSmmStackArrayEnd - mSmmStackSize - mSmmShadowStackSize + EFI_PAGE_SIZE * 2) & ~(SIZE_2MB-1);\r
     PagesNeeded = ((High2MBoundary - Low2MBoundary) / SIZE_2MB) + 1;\r
   }\r
   //\r
@@ -873,7 +974,7 @@ Gen4GPageTable (
           // Mark the guard page as non-present\r
           //\r
           Pte[Index] = PageAddress | mAddressEncMask;\r
-          GuardPage += mSmmStackSize;\r
+          GuardPage += (mSmmStackSize + mSmmShadowStackSize);\r
           if (GuardPage > mSmmStackArrayEnd) {\r
             GuardPage = 0;\r
           }\r
@@ -912,13 +1013,179 @@ Gen4GPageTable (
   return (UINT32)(UINTN)PageTable;\r
 }\r
 \r
+/**\r
+  Checks whether the input token is the current used token.\r
+\r
+  @param[in]  Token      This parameter describes the token that was passed into DispatchProcedure or\r
+                         BroadcastProcedure.\r
+\r
+  @retval TRUE           The input token is the current used token.\r
+  @retval FALSE          The input token is not the current used token.\r
+**/\r
+BOOLEAN\r
+IsTokenInUse (\r
+  IN SPIN_LOCK           *Token\r
+  )\r
+{\r
+  LIST_ENTRY        *Link;\r
+  PROCEDURE_TOKEN   *ProcToken;\r
+\r
+  if (Token == NULL) {\r
+    return FALSE;\r
+  }\r
+\r
+  Link = GetFirstNode (&gSmmCpuPrivate->TokenList);\r
+  //\r
+  // Only search used tokens.\r
+  //\r
+  while (Link != gSmmCpuPrivate->FirstFreeToken) {\r
+    ProcToken = PROCEDURE_TOKEN_FROM_LINK (Link);\r
+\r
+    if (ProcToken->SpinLock == Token) {\r
+      return TRUE;\r
+    }\r
+\r
+    Link = GetNextNode (&gSmmCpuPrivate->TokenList, Link);\r
+  }\r
+\r
+  return FALSE;\r
+}\r
+\r
+/**\r
+  Allocate buffer for the SPIN_LOCK and PROCEDURE_TOKEN.\r
+\r
+  @return First token of the token buffer.\r
+**/\r
+LIST_ENTRY *\r
+AllocateTokenBuffer (\r
+  VOID\r
+  )\r
+{\r
+  UINTN               SpinLockSize;\r
+  UINT32              TokenCountPerChunk;\r
+  UINTN               Index;\r
+  SPIN_LOCK           *SpinLock;\r
+  UINT8               *SpinLockBuffer;\r
+  PROCEDURE_TOKEN     *ProcTokens;\r
+\r
+  SpinLockSize = GetSpinLockProperties ();\r
+\r
+  TokenCountPerChunk = FixedPcdGet32 (PcdCpuSmmMpTokenCountPerChunk);\r
+  ASSERT (TokenCountPerChunk != 0);\r
+  if (TokenCountPerChunk == 0) {\r
+    DEBUG ((DEBUG_ERROR, "PcdCpuSmmMpTokenCountPerChunk should not be Zero!\n"));\r
+    CpuDeadLoop ();\r
+  }\r
+  DEBUG ((DEBUG_INFO, "CpuSmm: SpinLock Size = 0x%x, PcdCpuSmmMpTokenCountPerChunk = 0x%x\n", SpinLockSize, TokenCountPerChunk));\r
+\r
+  //\r
+  // Separate the Spin_lock and Proc_token because the alignment requires by Spin_Lock.\r
+  //\r
+  SpinLockBuffer = AllocatePool (SpinLockSize * TokenCountPerChunk);\r
+  ASSERT (SpinLockBuffer != NULL);\r
+\r
+  ProcTokens = AllocatePool (sizeof (PROCEDURE_TOKEN) * TokenCountPerChunk);\r
+  ASSERT (ProcTokens != NULL);\r
+\r
+  for (Index = 0; Index < TokenCountPerChunk; Index++) {\r
+    SpinLock = (SPIN_LOCK *)(SpinLockBuffer + SpinLockSize * Index);\r
+    InitializeSpinLock (SpinLock);\r
+\r
+    ProcTokens[Index].Signature      = PROCEDURE_TOKEN_SIGNATURE;\r
+    ProcTokens[Index].SpinLock       = SpinLock;\r
+    ProcTokens[Index].RunningApCount = 0;\r
+\r
+    InsertTailList (&gSmmCpuPrivate->TokenList, &ProcTokens[Index].Link);\r
+  }\r
+\r
+  return &ProcTokens[0].Link;\r
+}\r
+\r
+/**\r
+  Get the free token.\r
+\r
+  If no free token, allocate new tokens then return the free one.\r
+\r
+  @param RunningApsCount    The Running Aps count for this token.\r
+\r
+  @retval    return the first free PROCEDURE_TOKEN.\r
+\r
+**/\r
+PROCEDURE_TOKEN *\r
+GetFreeToken (\r
+  IN UINT32       RunningApsCount\r
+  )\r
+{\r
+  PROCEDURE_TOKEN  *NewToken;\r
+\r
+  //\r
+  // If FirstFreeToken meets the end of token list, enlarge the token list.\r
+  // Set FirstFreeToken to the first free token.\r
+  //\r
+  if (gSmmCpuPrivate->FirstFreeToken == &gSmmCpuPrivate->TokenList) {\r
+    gSmmCpuPrivate->FirstFreeToken = AllocateTokenBuffer ();\r
+  }\r
+  NewToken = PROCEDURE_TOKEN_FROM_LINK (gSmmCpuPrivate->FirstFreeToken);\r
+  gSmmCpuPrivate->FirstFreeToken = GetNextNode (&gSmmCpuPrivate->TokenList, gSmmCpuPrivate->FirstFreeToken);\r
+\r
+  NewToken->RunningApCount = RunningApsCount;\r
+  AcquireSpinLock (NewToken->SpinLock);\r
+\r
+  return NewToken;\r
+}\r
+\r
+/**\r
+  Checks status of specified AP.\r
+\r
+  This function checks whether the specified AP has finished the task assigned\r
+  by StartupThisAP(), and whether timeout expires.\r
+\r
+  @param[in]  Token             This parameter describes the token that was passed into DispatchProcedure or\r
+                                BroadcastProcedure.\r
+\r
+  @retval EFI_SUCCESS           Specified AP has finished task assigned by StartupThisAPs().\r
+  @retval EFI_NOT_READY         Specified AP has not finished task and timeout has not expired.\r
+**/\r
+EFI_STATUS\r
+IsApReady (\r
+  IN SPIN_LOCK          *Token\r
+  )\r
+{\r
+  if (AcquireSpinLockOrFail (Token)) {\r
+    ReleaseSpinLock (Token);\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  return EFI_NOT_READY;\r
+}\r
+\r
 /**\r
   Schedule a procedure to run on the specified CPU.\r
 \r
   @param[in]       Procedure                The address of the procedure to run\r
   @param[in]       CpuIndex                 Target CPU Index\r
-  @param[in, out]  ProcArguments            The parameter to pass to the procedure\r
-  @param[in]       BlockingMode             Startup AP in blocking mode or not\r
+  @param[in,out]   ProcArguments            The parameter to pass to the procedure\r
+  @param[in]       Token                    This is an optional parameter that allows the caller to execute the\r
+                                            procedure in a blocking or non-blocking fashion. If it is NULL the\r
+                                            call is blocking, and the call will not return until the AP has\r
+                                            completed the procedure. If the token is not NULL, the call will\r
+                                            return immediately. The caller can check whether the procedure has\r
+                                            completed with CheckOnProcedure or WaitForProcedure.\r
+  @param[in]       TimeoutInMicroseconds    Indicates the time limit in microseconds for the APs to finish\r
+                                            execution of Procedure, either for blocking or non-blocking mode.\r
+                                            Zero means infinity. If the timeout expires before all APs return\r
+                                            from Procedure, then Procedure on the failed APs is terminated. If\r
+                                            the timeout expires in blocking mode, the call returns EFI_TIMEOUT.\r
+                                            If the timeout expires in non-blocking mode, the timeout determined\r
+                                            can be through CheckOnProcedure or WaitForProcedure.\r
+                                            Note that timeout support is optional. Whether an implementation\r
+                                            supports this feature can be determined via the Attributes data\r
+                                            member.\r
+  @param[in,out]   CpuStatus                This optional pointer may be used to get the status code returned\r
+                                            by Procedure when it completes execution on the target AP, or with\r
+                                            EFI_TIMEOUT if the Procedure fails to complete within the optional\r
+                                            timeout. The implementation will update this variable with\r
+                                            EFI_NOT_READY prior to starting Procedure on the target AP.\r
 \r
   @retval EFI_INVALID_PARAMETER    CpuNumber not valid\r
   @retval EFI_INVALID_PARAMETER    CpuNumber specifying BSP\r
@@ -929,12 +1196,16 @@ Gen4GPageTable (
 **/\r
 EFI_STATUS\r
 InternalSmmStartupThisAp (\r
-  IN      EFI_AP_PROCEDURE          Procedure,\r
-  IN      UINTN                     CpuIndex,\r
-  IN OUT  VOID                      *ProcArguments OPTIONAL,\r
-  IN      BOOLEAN                   BlockingMode\r
+  IN      EFI_AP_PROCEDURE2              Procedure,\r
+  IN      UINTN                          CpuIndex,\r
+  IN OUT  VOID                           *ProcArguments OPTIONAL,\r
+  IN      MM_COMPLETION                  *Token,\r
+  IN      UINTN                          TimeoutInMicroseconds,\r
+  IN OUT  EFI_STATUS                     *CpuStatus\r
   )\r
 {\r
+  PROCEDURE_TOKEN    *ProcToken;\r
+\r
   if (CpuIndex >= gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus) {\r
     DEBUG((DEBUG_ERROR, "CpuIndex(%d) >= gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus(%d)\n", CpuIndex, gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus));\r
     return EFI_INVALID_PARAMETER;\r
@@ -958,24 +1229,211 @@ InternalSmmStartupThisAp (
     }\r
     return EFI_INVALID_PARAMETER;\r
   }\r
-\r
-  if (BlockingMode) {\r
-    AcquireSpinLock (mSmmMpSyncData->CpuData[CpuIndex].Busy);\r
-  } else {\r
-    if (!AcquireSpinLockOrFail (mSmmMpSyncData->CpuData[CpuIndex].Busy)) {\r
-      DEBUG((DEBUG_ERROR, "mSmmMpSyncData->CpuData[%d].Busy\n", CpuIndex));\r
-      return EFI_INVALID_PARAMETER;\r
-    }\r
+  if ((TimeoutInMicroseconds != 0) && ((mSmmMp.Attributes & EFI_MM_MP_TIMEOUT_SUPPORTED) == 0)) {\r
+    return EFI_INVALID_PARAMETER;\r
   }\r
+  if (Procedure == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  AcquireSpinLock (mSmmMpSyncData->CpuData[CpuIndex].Busy);\r
 \r
   mSmmMpSyncData->CpuData[CpuIndex].Procedure = Procedure;\r
   mSmmMpSyncData->CpuData[CpuIndex].Parameter = ProcArguments;\r
+  if (Token != NULL) {\r
+    if (Token != &mSmmStartupThisApToken) {\r
+      //\r
+      // When Token points to mSmmStartupThisApToken, this routine is called\r
+      // from SmmStartupThisAp() in non-blocking mode (PcdCpuSmmBlockStartupThisAp == FALSE).\r
+      //\r
+      // In this case, caller wants to startup AP procedure in non-blocking\r
+      // mode and cannot get the completion status from the Token because there\r
+      // is no way to return the Token to caller from SmmStartupThisAp().\r
+      // Caller needs to use its implementation specific way to query the completion status.\r
+      //\r
+      // There is no need to allocate a token for such case so the 3 overheads\r
+      // can be avoided:\r
+      // 1. Call AllocateTokenBuffer() when there is no free token.\r
+      // 2. Get a free token from the token buffer.\r
+      // 3. Call ReleaseToken() in APHandler().\r
+      //\r
+      ProcToken = GetFreeToken (1);\r
+      mSmmMpSyncData->CpuData[CpuIndex].Token = ProcToken;\r
+      *Token = (MM_COMPLETION)ProcToken->SpinLock;\r
+    }\r
+  }\r
+  mSmmMpSyncData->CpuData[CpuIndex].Status    = CpuStatus;\r
+  if (mSmmMpSyncData->CpuData[CpuIndex].Status != NULL) {\r
+    *mSmmMpSyncData->CpuData[CpuIndex].Status = EFI_NOT_READY;\r
+  }\r
+\r
   ReleaseSemaphore (mSmmMpSyncData->CpuData[CpuIndex].Run);\r
 \r
-  if (BlockingMode) {\r
+  if (Token == NULL) {\r
     AcquireSpinLock (mSmmMpSyncData->CpuData[CpuIndex].Busy);\r
     ReleaseSpinLock (mSmmMpSyncData->CpuData[CpuIndex].Busy);\r
   }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Worker function to execute a caller provided function on all enabled APs.\r
+\r
+  @param[in]     Procedure               A pointer to the function to be run on\r
+                                         enabled APs of the system.\r
+  @param[in]     TimeoutInMicroseconds   Indicates the time limit in microseconds for\r
+                                         APs to return from Procedure, either for\r
+                                         blocking or non-blocking mode.\r
+  @param[in,out] ProcedureArguments      The parameter passed into Procedure for\r
+                                         all APs.\r
+  @param[in,out] Token                   This is an optional parameter that allows the caller to execute the\r
+                                         procedure in a blocking or non-blocking fashion. If it is NULL the\r
+                                         call is blocking, and the call will not return until the AP has\r
+                                         completed the procedure. If the token is not NULL, the call will\r
+                                         return immediately. The caller can check whether the procedure has\r
+                                         completed with CheckOnProcedure or WaitForProcedure.\r
+  @param[in,out] CPUStatus               This optional pointer may be used to get the status code returned\r
+                                         by Procedure when it completes execution on the target AP, or with\r
+                                         EFI_TIMEOUT if the Procedure fails to complete within the optional\r
+                                         timeout. The implementation will update this variable with\r
+                                         EFI_NOT_READY prior to starting Procedure on the target AP.\r
+\r
+\r
+  @retval EFI_SUCCESS             In blocking mode, all APs have finished before\r
+                                  the timeout expired.\r
+  @retval EFI_SUCCESS             In non-blocking mode, function has been dispatched\r
+                                  to all enabled APs.\r
+  @retval others                  Failed to Startup all APs.\r
+\r
+**/\r
+EFI_STATUS\r
+InternalSmmStartupAllAPs (\r
+  IN       EFI_AP_PROCEDURE2             Procedure,\r
+  IN       UINTN                         TimeoutInMicroseconds,\r
+  IN OUT   VOID                          *ProcedureArguments OPTIONAL,\r
+  IN OUT   MM_COMPLETION                 *Token,\r
+  IN OUT   EFI_STATUS                    *CPUStatus\r
+  )\r
+{\r
+  UINTN               Index;\r
+  UINTN               CpuCount;\r
+  PROCEDURE_TOKEN     *ProcToken;\r
+\r
+  if ((TimeoutInMicroseconds != 0) && ((mSmmMp.Attributes & EFI_MM_MP_TIMEOUT_SUPPORTED) == 0)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+  if (Procedure == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  CpuCount = 0;\r
+  for (Index = 0; Index < mMaxNumberOfCpus; Index++) {\r
+    if (IsPresentAp (Index)) {\r
+      CpuCount ++;\r
+\r
+      if (gSmmCpuPrivate->Operation[Index] == SmmCpuRemove) {\r
+        return EFI_INVALID_PARAMETER;\r
+      }\r
+\r
+      if (!AcquireSpinLockOrFail(mSmmMpSyncData->CpuData[Index].Busy)) {\r
+        return EFI_NOT_READY;\r
+      }\r
+      ReleaseSpinLock (mSmmMpSyncData->CpuData[Index].Busy);\r
+    }\r
+  }\r
+  if (CpuCount == 0) {\r
+    return EFI_NOT_STARTED;\r
+  }\r
+\r
+  if (Token != NULL) {\r
+    ProcToken = GetFreeToken ((UINT32)mMaxNumberOfCpus);\r
+    *Token = (MM_COMPLETION)ProcToken->SpinLock;\r
+  } else {\r
+    ProcToken = NULL;\r
+  }\r
+\r
+  //\r
+  // Make sure all BUSY should be acquired.\r
+  //\r
+  // Because former code already check mSmmMpSyncData->CpuData[***].Busy for each AP.\r
+  // Here code always use AcquireSpinLock instead of AcquireSpinLockOrFail for not\r
+  // block mode.\r
+  //\r
+  for (Index = 0; Index < mMaxNumberOfCpus; Index++) {\r
+    if (IsPresentAp (Index)) {\r
+      AcquireSpinLock (mSmmMpSyncData->CpuData[Index].Busy);\r
+    }\r
+  }\r
+\r
+  for (Index = 0; Index < mMaxNumberOfCpus; Index++) {\r
+    if (IsPresentAp (Index)) {\r
+      mSmmMpSyncData->CpuData[Index].Procedure = (EFI_AP_PROCEDURE2) Procedure;\r
+      mSmmMpSyncData->CpuData[Index].Parameter = ProcedureArguments;\r
+      if (ProcToken != NULL) {\r
+        mSmmMpSyncData->CpuData[Index].Token   = ProcToken;\r
+      }\r
+      if (CPUStatus != NULL) {\r
+        mSmmMpSyncData->CpuData[Index].Status    = &CPUStatus[Index];\r
+        if (mSmmMpSyncData->CpuData[Index].Status != NULL) {\r
+          *mSmmMpSyncData->CpuData[Index].Status = EFI_NOT_READY;\r
+        }\r
+      }\r
+    } else {\r
+      //\r
+      // PI spec requirement:\r
+      // For every excluded processor, the array entry must contain a value of EFI_NOT_STARTED.\r
+      //\r
+      if (CPUStatus != NULL) {\r
+        CPUStatus[Index] = EFI_NOT_STARTED;\r
+      }\r
+\r
+      //\r
+      // Decrease the count to mark this processor(AP or BSP) as finished.\r
+      //\r
+      if (ProcToken != NULL) {\r
+        WaitForSemaphore (&ProcToken->RunningApCount);\r
+      }\r
+    }\r
+  }\r
+\r
+  ReleaseAllAPs ();\r
+\r
+  if (Token == NULL) {\r
+    //\r
+    // Make sure all APs have completed their tasks.\r
+    //\r
+    WaitForAllAPsNotBusy (TRUE);\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  ISO C99 6.5.2.2 "Function calls", paragraph 9:\r
+  If the function is defined with a type that is not compatible with\r
+  the type (of the expression) pointed to by the expression that\r
+  denotes the called function, the behavior is undefined.\r
+\r
+  So add below wrapper function to convert between EFI_AP_PROCEDURE\r
+  and EFI_AP_PROCEDURE2.\r
+\r
+  Wrapper for Procedures.\r
+\r
+  @param[in]  Buffer              Pointer to PROCEDURE_WRAPPER buffer.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+ProcedureWrapper (\r
+  IN     VOID *Buffer\r
+  )\r
+{\r
+  PROCEDURE_WRAPPER *Wrapper;\r
+\r
+  Wrapper = Buffer;\r
+  Wrapper->Procedure (Wrapper->ProcedureArgument);\r
+\r
   return EFI_SUCCESS;\r
 }\r
 \r
@@ -1001,7 +1459,15 @@ SmmBlockingStartupThisAp (
   IN OUT  VOID                      *ProcArguments OPTIONAL\r
   )\r
 {\r
-  return InternalSmmStartupThisAp(Procedure, CpuIndex, ProcArguments, TRUE);\r
+  PROCEDURE_WRAPPER  Wrapper;\r
+\r
+  Wrapper.Procedure = Procedure;\r
+  Wrapper.ProcedureArgument = ProcArguments;\r
+\r
+  //\r
+  // Use wrapper function to convert EFI_AP_PROCEDURE to EFI_AP_PROCEDURE2.\r
+  //\r
+  return InternalSmmStartupThisAp (ProcedureWrapper, CpuIndex, &Wrapper, NULL, 0, NULL);\r
 }\r
 \r
 /**\r
@@ -1026,7 +1492,20 @@ SmmStartupThisAp (
   IN OUT  VOID                      *ProcArguments OPTIONAL\r
   )\r
 {\r
-  return InternalSmmStartupThisAp(Procedure, CpuIndex, ProcArguments, FeaturePcdGet (PcdCpuSmmBlockStartupThisAp));\r
+  gSmmCpuPrivate->ApWrapperFunc[CpuIndex].Procedure = Procedure;\r
+  gSmmCpuPrivate->ApWrapperFunc[CpuIndex].ProcedureArgument = ProcArguments;\r
+\r
+  //\r
+  // Use wrapper function to convert EFI_AP_PROCEDURE to EFI_AP_PROCEDURE2.\r
+  //\r
+  return InternalSmmStartupThisAp (\r
+    ProcedureWrapper,\r
+    CpuIndex,\r
+    &gSmmCpuPrivate->ApWrapperFunc[CpuIndex],\r
+    FeaturePcdGet (PcdCpuSmmBlockStartupThisAp) ? NULL : &mSmmStartupThisApToken,\r
+    0,\r
+    NULL\r
+    );\r
 }\r
 \r
 /**\r
@@ -1112,9 +1591,18 @@ SmiRendezvous (
   ASSERT(CpuIndex < mMaxNumberOfCpus);\r
 \r
   //\r
-  // Save Cr2 because Page Fault exception in SMM may override its value\r
+  // Save Cr2 because Page Fault exception in SMM may override its value,\r
+  // when using on-demand paging for above 4G memory.\r
+  //\r
+  Cr2 = 0;\r
+  SaveCr2 (&Cr2);\r
+\r
+  //\r
+  // Call the user register Startup function first.\r
   //\r
-  Cr2 = AsmReadCr2 ();\r
+  if (mSmmMpSyncData->StartupProcedure != NULL) {\r
+    mSmmMpSyncData->StartupProcedure (mSmmMpSyncData->StartupProcArgs);\r
+  }\r
 \r
   //\r
   // Perform CPU specific entry hooks\r
@@ -1253,10 +1741,28 @@ SmiRendezvous (
 \r
 Exit:\r
   SmmCpuFeaturesRendezvousExit (CpuIndex);\r
+\r
   //\r
   // Restore Cr2\r
   //\r
-  AsmWriteCr2 (Cr2);\r
+  RestoreCr2 (Cr2);\r
+}\r
+\r
+/**\r
+  Allocate buffer for SpinLock and Wrapper function buffer.\r
+\r
+**/\r
+VOID\r
+InitializeDataForMmMp (\r
+  VOID\r
+  )\r
+{\r
+  gSmmCpuPrivate->ApWrapperFunc = AllocatePool (sizeof (PROCEDURE_WRAPPER) * gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus);\r
+  ASSERT (gSmmCpuPrivate->ApWrapperFunc != NULL);\r
+\r
+  InitializeListHead (&gSmmCpuPrivate->TokenList);\r
+\r
+  gSmmCpuPrivate->FirstFreeToken = AllocateTokenBuffer ();\r
 }\r
 \r
 /**\r
@@ -1282,8 +1788,8 @@ InitializeSmmCpuSemaphores (
   GlobalSemaphoresSize = (sizeof (SMM_CPU_SEMAPHORE_GLOBAL) / sizeof (VOID *)) * SemaphoreSize;\r
   CpuSemaphoresSize    = (sizeof (SMM_CPU_SEMAPHORE_CPU) / sizeof (VOID *)) * ProcessorCount * SemaphoreSize;\r
   TotalSize = GlobalSemaphoresSize + CpuSemaphoresSize;\r
-  DEBUG((EFI_D_INFO, "One Semaphore Size    = 0x%x\n", SemaphoreSize));\r
-  DEBUG((EFI_D_INFO, "Total Semaphores Size = 0x%x\n", TotalSize));\r
+  DEBUG((DEBUG_INFO, "One Semaphore Size    = 0x%x\n", SemaphoreSize));\r
+  DEBUG((DEBUG_INFO, "Total Semaphores Size = 0x%x\n", TotalSize));\r
   Pages = EFI_SIZE_TO_PAGES (TotalSize);\r
   SemaphoreBlock = AllocatePages (Pages);\r
   ASSERT (SemaphoreBlock != NULL);\r
@@ -1381,11 +1887,13 @@ InitializeMpServiceData (
   IN UINTN       ShadowStackSize\r
   )\r
 {\r
-  UINT32                    Cr3;\r
-  UINTN                     Index;\r
-  UINT8                     *GdtTssTables;\r
-  UINTN                     GdtTableStepSize;\r
-  CPUID_VERSION_INFO_EDX    RegEdx;\r
+  UINT32                          Cr3;\r
+  UINTN                           Index;\r
+  UINT8                           *GdtTssTables;\r
+  UINTN                           GdtTableStepSize;\r
+  CPUID_VERSION_INFO_EDX          RegEdx;\r
+  UINT32                          MaxExtendedFunction;\r
+  CPUID_VIR_PHY_ADDRESS_SIZE_EAX  VirPhyAddressSize;\r
 \r
   //\r
   // Determine if this CPU supports machine check\r
@@ -1412,9 +1920,17 @@ InitializeMpServiceData (
   // Initialize physical address mask\r
   // NOTE: Physical memory above virtual address limit is not supported !!!\r
   //\r
-  AsmCpuid (0x80000008, (UINT32*)&Index, NULL, NULL, NULL);\r
-  gPhyMask = LShiftU64 (1, (UINT8)Index) - 1;\r
-  gPhyMask &= (1ull << 48) - EFI_PAGE_SIZE;\r
+  AsmCpuid (CPUID_EXTENDED_FUNCTION, &MaxExtendedFunction, NULL, NULL, NULL);\r
+  if (MaxExtendedFunction >= CPUID_VIR_PHY_ADDRESS_SIZE) {\r
+    AsmCpuid (CPUID_VIR_PHY_ADDRESS_SIZE, &VirPhyAddressSize.Uint32, NULL, NULL, NULL);\r
+  } else {\r
+    VirPhyAddressSize.Bits.PhysicalAddressBits = 36;\r
+  }\r
+  gPhyMask  = LShiftU64 (1, VirPhyAddressSize.Bits.PhysicalAddressBits) - 1;\r
+  //\r
+  // Clear the low 12 bits\r
+  //\r
+  gPhyMask &= 0xfffffffffffff000ULL;\r
 \r
   //\r
   // Create page tables\r
@@ -1472,3 +1988,40 @@ RegisterSmmEntry (
   gSmmCpuPrivate->SmmCoreEntry = SmmEntryPoint;\r
   return EFI_SUCCESS;\r
 }\r
+\r
+/**\r
+\r
+  Register the SMM Foundation entry point.\r
+\r
+  @param[in]      Procedure            A pointer to the code stream to be run on the designated target AP\r
+                                       of the system. Type EFI_AP_PROCEDURE is defined below in Volume 2\r
+                                       with the related definitions of\r
+                                       EFI_MP_SERVICES_PROTOCOL.StartupAllAPs.\r
+                                       If caller may pass a value of NULL to deregister any existing\r
+                                       startup procedure.\r
+  @param[in,out]  ProcedureArguments   Allows the caller to pass a list of parameters to the code that is\r
+                                       run by the AP. It is an optional common mailbox between APs and\r
+                                       the caller to share information\r
+\r
+  @retval EFI_SUCCESS                  The Procedure has been set successfully.\r
+  @retval EFI_INVALID_PARAMETER        The Procedure is NULL but ProcedureArguments not NULL.\r
+\r
+**/\r
+EFI_STATUS\r
+RegisterStartupProcedure (\r
+  IN     EFI_AP_PROCEDURE    Procedure,\r
+  IN OUT VOID                *ProcedureArguments OPTIONAL\r
+  )\r
+{\r
+  if (Procedure == NULL && ProcedureArguments != NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+  if (mSmmMpSyncData == NULL) {\r
+    return EFI_NOT_READY;\r
+  }\r
+\r
+  mSmmMpSyncData->StartupProcedure = Procedure;\r
+  mSmmMpSyncData->StartupProcArgs  = ProcedureArguments;\r
+\r
+  return EFI_SUCCESS;\r
+}\r