]> git.proxmox.com Git - mirror_edk2.git/blobdiff - UefiCpuPkg/CpuDxe/CpuMp.c
BaseTools: Library hashing fix and optimization for --hash feature
[mirror_edk2.git] / UefiCpuPkg / CpuDxe / CpuMp.c
index 8f7a56c40daa2bbb1559061f9871d5859571f124..de6c6a5c330c97fadc23df164ad0f3af98bfa0e4 100644 (file)
@@ -1,34 +1,16 @@
 /** @file\r
   CPU DXE Module to produce CPU MP Protocol.\r
 \r
-  Copyright (c) 2008 - 2016, Intel Corporation. All rights reserved.<BR>\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
+  Copyright (c) 2008 - 2017, Intel Corporation. All rights reserved.<BR>\r
+  SPDX-License-Identifier: BSD-2-Clause-Patent\r
 \r
 **/\r
 \r
 #include "CpuDxe.h"\r
 #include "CpuMp.h"\r
 \r
-UINTN gMaxLogicalProcessorNumber;\r
-UINTN gApStackSize;\r
-UINTN gPollInterval = 100; // 100 microseconds\r
-\r
-MP_SYSTEM_DATA mMpSystemData;\r
 EFI_HANDLE     mMpServiceHandle       = NULL;\r
 UINTN          mNumberOfProcessors    = 1;\r
-EFI_EVENT      mExitBootServicesEvent = (EFI_EVENT)NULL;\r
-\r
-VOID *mCommonStack = 0;\r
-VOID *mTopOfApCommonStack = 0;\r
-VOID *mApStackStart = 0;\r
-\r
-volatile BOOLEAN mAPsAlreadyInitFinished = FALSE;\r
 \r
 EFI_MP_SERVICES_PROTOCOL  mMpServicesTemplate = {\r
   GetNumberOfProcessors,\r
@@ -40,372 +22,6 @@ EFI_MP_SERVICES_PROTOCOL  mMpServicesTemplate = {
   WhoAmI\r
 };\r
 \r
-/**\r
-   Get Mp Service Lock.\r
-\r
-  @param   CpuData    the pointer to CPU_DATA_BLOCK of specified processor\r
-\r
-**/\r
-VOID\r
-GetMpSpinLock (\r
-  IN  CPU_DATA_BLOCK  *CpuData\r
-  )\r
-{\r
-  while (!AcquireSpinLockOrFail (&CpuData->CpuDataLock)) {\r
-    CpuPause ();\r
-  }\r
-  CpuData->LockSelf = GetApicId ();\r
-}\r
-\r
-/**\r
-   Release Mp Service Lock.\r
-\r
-  @param   CpuData    the pointer to CPU_DATA_BLOCK of specified processor\r
-\r
-**/\r
-VOID\r
-ReleaseMpSpinLock (\r
-  IN  CPU_DATA_BLOCK  *CpuData\r
-  )\r
-{\r
-  ReleaseSpinLock (&CpuData->CpuDataLock);\r
-}\r
-\r
-/**\r
-  Check whether caller processor is BSP.\r
-\r
-  @retval  TRUE       the caller is BSP\r
-  @retval  FALSE      the caller is AP\r
-\r
-**/\r
-BOOLEAN\r
-IsBSP (\r
-  VOID\r
-  )\r
-{\r
-  UINTN           CpuIndex;\r
-  CPU_DATA_BLOCK  *CpuData;\r
-\r
-  CpuData = NULL;\r
-\r
-  WhoAmI (&mMpServicesTemplate, &CpuIndex);\r
-  CpuData = &mMpSystemData.CpuDatas[CpuIndex];\r
-\r
-  return CpuData->Info.StatusFlag & PROCESSOR_AS_BSP_BIT ? TRUE : FALSE;\r
-}\r
-\r
-/**\r
-  Get the Application Processors state.\r
-\r
-  @param   CpuData    the pointer to CPU_DATA_BLOCK of specified AP\r
-\r
-  @retval  CPU_STATE  the AP status\r
-\r
-**/\r
-STATIC\r
-CPU_STATE\r
-GetApState (\r
-  IN  CPU_DATA_BLOCK  *CpuData\r
-  )\r
-{\r
-  CPU_STATE State;\r
-\r
-  GetMpSpinLock (CpuData);\r
-  State = CpuData->State;\r
-  ReleaseMpSpinLock (CpuData);\r
-\r
-  return State;\r
-}\r
-\r
-/**\r
-  Set the Application Processors state.\r
-\r
-  @param   CpuData    The pointer to CPU_DATA_BLOCK of specified AP\r
-  @param   State      The AP status\r
-\r
-**/\r
-STATIC\r
-VOID\r
-SetApState (\r
-  IN  CPU_DATA_BLOCK   *CpuData,\r
-  IN  CPU_STATE        State\r
-  )\r
-{\r
-  GetMpSpinLock (CpuData);\r
-  CpuData->State = State;\r
-  ReleaseMpSpinLock (CpuData);\r
-}\r
-\r
-/**\r
-  Set the Application Processor prepare to run a function specified\r
-  by Params.\r
-\r
-  @param CpuData           the pointer to CPU_DATA_BLOCK of specified AP\r
-  @param Procedure         A pointer to the function to be run on enabled APs of the system\r
-  @param ProcedureArgument Pointer to the optional parameter of the assigned function\r
-\r
-**/\r
-VOID\r
-SetApProcedure (\r
-  IN   CPU_DATA_BLOCK        *CpuData,\r
-  IN   EFI_AP_PROCEDURE      Procedure,\r
-  IN   VOID                  *ProcedureArgument\r
-  )\r
-{\r
-  GetMpSpinLock (CpuData);\r
-  CpuData->Parameter  = ProcedureArgument;\r
-  CpuData->Procedure  = Procedure;\r
-  ReleaseMpSpinLock (CpuData);\r
-}\r
-\r
-/**\r
-  Check the Application Processors Status whether contains the Flags.\r
-\r
-  @param     CpuData  the pointer to CPU_DATA_BLOCK of specified AP\r
-  @param     Flags    the StatusFlag describing in EFI_PROCESSOR_INFORMATION\r
-\r
-  @retval    TRUE     the AP status includes the StatusFlag\r
-  @retval    FALSE    the AP status excludes the StatusFlag\r
-\r
-**/\r
-BOOLEAN\r
-TestCpuStatusFlag (\r
-  IN  CPU_DATA_BLOCK  *CpuData,\r
-  IN  UINT32          Flags\r
-  )\r
-{\r
-  UINT32 Ret;\r
-\r
-  GetMpSpinLock (CpuData);\r
-  Ret = CpuData->Info.StatusFlag & Flags;\r
-  ReleaseMpSpinLock (CpuData);\r
-\r
-  return (BOOLEAN) (Ret != 0);\r
-}\r
-\r
-/**\r
-  Bitwise-Or of the Application Processors Status with the Flags.\r
-\r
-  @param     CpuData  the pointer to CPU_DATA_BLOCK of specified AP\r
-  @param     Flags    the StatusFlag describing in EFI_PROCESSOR_INFORMATION\r
-\r
-**/\r
-VOID\r
-CpuStatusFlagOr (\r
-  IN  CPU_DATA_BLOCK  *CpuData,\r
-  IN  UINT32          Flags\r
-  )\r
-{\r
-  GetMpSpinLock (CpuData);\r
-  CpuData->Info.StatusFlag |= Flags;\r
-  ReleaseMpSpinLock (CpuData);\r
-}\r
-\r
-/**\r
-  Bitwise-AndNot of the Application Processors Status with the Flags.\r
-\r
-  @param     CpuData  the pointer to CPU_DATA_BLOCK of specified AP\r
-  @param     Flags    the StatusFlag describing in EFI_PROCESSOR_INFORMATION\r
-\r
-**/\r
-VOID\r
-CpuStatusFlagAndNot (\r
-  IN  CPU_DATA_BLOCK  *CpuData,\r
-  IN  UINT32          Flags\r
-  )\r
-{\r
-  GetMpSpinLock (CpuData);\r
-  CpuData->Info.StatusFlag &= ~Flags;\r
-  ReleaseMpSpinLock (CpuData);\r
-}\r
-\r
-/**\r
-  Searches for the next blocking AP.\r
-\r
-  Search for the next AP that is put in blocking state by single-threaded StartupAllAPs().\r
-\r
-  @param  NextNumber           Pointer to the processor number of the next blocking AP.\r
-\r
-  @retval EFI_SUCCESS          The next blocking AP has been found.\r
-  @retval EFI_NOT_FOUND        No blocking AP exists.\r
-\r
-**/\r
-EFI_STATUS\r
-GetNextBlockedNumber (\r
-  OUT UINTN  *NextNumber\r
-  )\r
-{\r
-  UINTN                 Number;\r
-  CPU_STATE             CpuState;\r
-  CPU_DATA_BLOCK        *CpuData;\r
-\r
-  for (Number = 0; Number < mMpSystemData.NumberOfProcessors; Number++) {\r
-    CpuData = &mMpSystemData.CpuDatas[Number];\r
-    if (TestCpuStatusFlag (CpuData, PROCESSOR_AS_BSP_BIT)) {\r
-      //\r
-      // Skip BSP\r
-      //\r
-      continue;\r
-    }\r
-\r
-    CpuState = GetApState (CpuData);\r
-    if (CpuState == CpuStateBlocked) {\r
-      *NextNumber = Number;\r
-      return EFI_SUCCESS;\r
-    }\r
-  }\r
-\r
-  return EFI_NOT_FOUND;\r
-}\r
-\r
-/**\r
-  Check if the APs state are finished, and update them to idle state\r
-  by StartupAllAPs().\r
-\r
-**/\r
-VOID\r
-CheckAndUpdateAllAPsToIdleState (\r
-  VOID\r
-  )\r
-{\r
-  UINTN                 ProcessorNumber;\r
-  UINTN                 NextNumber;\r
-  CPU_DATA_BLOCK        *CpuData;\r
-  EFI_STATUS            Status;\r
-  CPU_STATE             CpuState;\r
-\r
-  for (ProcessorNumber = 0; ProcessorNumber < mMpSystemData.NumberOfProcessors; ProcessorNumber++) {\r
-    CpuData = &mMpSystemData.CpuDatas[ProcessorNumber];\r
-    if (TestCpuStatusFlag (CpuData, PROCESSOR_AS_BSP_BIT)) {\r
-      //\r
-      // Skip BSP\r
-      //\r
-      continue;\r
-    }\r
-\r
-    if (!TestCpuStatusFlag (CpuData, PROCESSOR_ENABLED_BIT)) {\r
-      //\r
-      // Skip Disabled processors\r
-      //\r
-      continue;\r
-    }\r
-\r
-    CpuState = GetApState (CpuData);\r
-    if (CpuState == CpuStateFinished) {\r
-      mMpSystemData.FinishCount++;\r
-      if (mMpSystemData.SingleThread) {\r
-        Status = GetNextBlockedNumber (&NextNumber);\r
-        if (!EFI_ERROR (Status)) {\r
-          SetApState (&mMpSystemData.CpuDatas[NextNumber], CpuStateReady);\r
-          SetApProcedure (&mMpSystemData.CpuDatas[NextNumber],\r
-                          mMpSystemData.Procedure,\r
-                          mMpSystemData.ProcedureArgument);\r
-          //\r
-          // If this AP previous state is blocked, we should\r
-          // wake up this AP by sent a SIPI. and avoid\r
-          // re-involve the sleeping state. we must call\r
-          // SetApProcedure() first.\r
-          //\r
-          ResetProcessorToIdleState (&mMpSystemData.CpuDatas[NextNumber]);\r
-        }\r
-      }\r
-      SetApState (CpuData, CpuStateIdle);\r
-    }\r
-  }\r
-}\r
-\r
-/**\r
-  Check if all APs are in state CpuStateSleeping.\r
-\r
-  Return TRUE if all APs are in the CpuStateSleeping state.  Do not\r
-  check the state of the BSP or any disabled APs.\r
-\r
-  @retval TRUE   All APs are in CpuStateSleeping state.\r
-  @retval FALSE  One or more APs are not in CpuStateSleeping state.\r
-\r
-**/\r
-BOOLEAN\r
-CheckAllAPsSleeping (\r
-  VOID\r
-  )\r
-{\r
-  UINTN           ProcessorNumber;\r
-  CPU_DATA_BLOCK  *CpuData;\r
-\r
-  for (ProcessorNumber = 0; ProcessorNumber < mMpSystemData.NumberOfProcessors; ProcessorNumber++) {\r
-    CpuData = &mMpSystemData.CpuDatas[ProcessorNumber];\r
-    if (TestCpuStatusFlag (CpuData, PROCESSOR_AS_BSP_BIT)) {\r
-      //\r
-      // Skip BSP\r
-      //\r
-      continue;\r
-    }\r
-\r
-    if (!TestCpuStatusFlag (CpuData, PROCESSOR_ENABLED_BIT)) {\r
-      //\r
-      // Skip Disabled processors\r
-      //\r
-      continue;\r
-    }\r
-\r
-    if (GetApState (CpuData) != CpuStateSleeping) {\r
-      return FALSE;\r
-    }\r
-  }\r
-  return TRUE;\r
-}\r
-\r
-/**\r
-  If the timeout expires before all APs returns from Procedure,\r
-  we should forcibly terminate the executing AP and fill FailedList back\r
-  by StartupAllAPs().\r
-\r
-**/\r
-VOID\r
-ResetAllFailedAPs (\r
-  VOID\r
-  )\r
-{\r
-  CPU_DATA_BLOCK        *CpuData;\r
-  UINTN                 Number;\r
-  CPU_STATE             CpuState;\r
-\r
-  if (mMpSystemData.FailedList != NULL) {\r
-     *mMpSystemData.FailedList = AllocatePool ((mMpSystemData.StartCount - mMpSystemData.FinishCount + 1) * sizeof(UINTN));\r
-     ASSERT (*mMpSystemData.FailedList != NULL);\r
-  }\r
-\r
-  for (Number = 0; Number < mMpSystemData.NumberOfProcessors; Number++) {\r
-    CpuData = &mMpSystemData.CpuDatas[Number];\r
-    if (TestCpuStatusFlag (CpuData,  PROCESSOR_AS_BSP_BIT)) {\r
-      //\r
-      // Skip BSP\r
-      //\r
-      continue;\r
-    }\r
-\r
-    if (!TestCpuStatusFlag (CpuData, PROCESSOR_ENABLED_BIT)) {\r
-      //\r
-      // Skip Disabled processors\r
-      //\r
-      continue;\r
-    }\r
-\r
-    CpuState = GetApState (CpuData);\r
-    if (CpuState != CpuStateIdle &&\r
-        CpuState != CpuStateSleeping) {\r
-      if (mMpSystemData.FailedList != NULL) {\r
-        (*mMpSystemData.FailedList)[mMpSystemData.FailedListIndex++] = Number;\r
-      }\r
-      ResetProcessorToIdleState (CpuData);\r
-    }\r
-  }\r
-\r
-  if (mMpSystemData.FailedList != NULL) {\r
-    (*mMpSystemData.FailedList)[mMpSystemData.FailedListIndex] = END_OF_CPU_LIST;\r
-  }\r
-}\r
-\r
 /**\r
   This service retrieves the number of logical processor in the platform\r
   and the number of those logical processors that are enabled on this boot.\r
@@ -677,8 +293,8 @@ StartupAllAPs (
 \r
   @param[in]  This                    A pointer to the EFI_MP_SERVICES_PROTOCOL\r
                                       instance.\r
-  @param[in]  Procedure               A pointer to the function to be run on\r
-                                      enabled APs of the system. See type\r
+  @param[in]  Procedure               A pointer to the function to be run on the\r
+                                      designated AP of the system. See type\r
                                       EFI_AP_PROCEDURE.\r
   @param[in]  ProcessorNumber         The handle number of the AP. The range is\r
                                       from 0 to the total number of logical\r
@@ -687,13 +303,13 @@ StartupAllAPs (
                                       EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors().\r
   @param[in]  WaitEvent               The event created by the caller with CreateEvent()\r
                                       service.  If it is NULL, then execute in\r
-                                      blocking mode. BSP waits until all APs finish\r
-                                      or TimeoutInMicroseconds expires.  If it's\r
+                                      blocking mode. BSP waits until this AP finish\r
+                                      or TimeoutInMicroSeconds expires.  If it's\r
                                       not NULL, then execute in non-blocking mode.\r
                                       BSP requests the function specified by\r
-                                      Procedure to be started on all the enabled\r
-                                      APs, and go on executing immediately. If\r
-                                      all return from Procedure or TimeoutInMicroseconds\r
+                                      Procedure to be started on this AP,\r
+                                      and go on executing immediately. If this AP\r
+                                      return from Procedure or TimeoutInMicroSeconds\r
                                       expires, this event is signaled. The BSP\r
                                       can use the CheckEvent() or WaitForEvent()\r
                                       services to check the state of event.  Type\r
@@ -701,20 +317,20 @@ StartupAllAPs (
                                       the Unified Extensible Firmware Interface\r
                                       Specification.\r
   @param[in]  TimeoutInMicroseconds   Indicates the time limit in microseconds for\r
-                                      APs to return from Procedure, either for\r
+                                      this AP to finish this Procedure, either for\r
                                       blocking or non-blocking mode. Zero means\r
                                       infinity.  If the timeout expires before\r
-                                      all APs return from Procedure, then Procedure\r
-                                      on the failed APs is terminated. All enabled\r
-                                      APs are available for next function assigned\r
+                                      this AP returns from Procedure, then Procedure\r
+                                      on the AP is terminated. The\r
+                                      AP is available for next function assigned\r
                                       by EFI_MP_SERVICES_PROTOCOL.StartupAllAPs()\r
                                       or EFI_MP_SERVICES_PROTOCOL.StartupThisAP().\r
                                       If the timeout expires in blocking mode,\r
                                       BSP returns EFI_TIMEOUT.  If the timeout\r
                                       expires in non-blocking mode, WaitEvent\r
                                       is signaled with SignalEvent().\r
-  @param[in]  ProcedureArgument       The parameter passed into Procedure for\r
-                                      all APs.\r
+  @param[in]  ProcedureArgument       The parameter passed into Procedure on the\r
+                                      specified AP.\r
   @param[out] Finished                If NULL, this parameter is ignored.  In\r
                                       blocking mode, this parameter is ignored.\r
                                       In non-blocking mode, if AP returns from\r
@@ -790,7 +406,7 @@ StartupThisAP (
   @retval EFI_UNSUPPORTED         Switching the BSP cannot be completed prior to\r
                                   this service returning.\r
   @retval EFI_UNSUPPORTED         Switching the BSP is not supported.\r
-  @retval EFI_SUCCESS             The calling processor is an AP.\r
+  @retval EFI_DEVICE_ERROR        The calling processor is an AP.\r
   @retval EFI_NOT_FOUND           The processor with the handle specified by\r
                                   ProcessorNumber does not exist.\r
   @retval EFI_INVALID_PARAMETER   ProcessorNumber specifies the current BSP or\r
@@ -825,8 +441,8 @@ SwitchBSP (
   from this service, then EFI_UNSUPPORTED must be returned.\r
 \r
   @param[in] This              A pointer to the EFI_MP_SERVICES_PROTOCOL instance.\r
-  @param[in] ProcessorNumber   The handle number of AP that is to become the new\r
-                               BSP. The range is from 0 to the total number of\r
+  @param[in] ProcessorNumber   The handle number of AP.\r
+                               The range is from 0 to the total number of\r
                                logical processors minus 1. The total number of\r
                                logical processors can be retrieved by\r
                                EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors().\r
@@ -875,8 +491,8 @@ EnableDisableAP (
   ProcessorNumber, and EFI_SUCCESS is returned.\r
 \r
   @param[in]  This             A pointer to the EFI_MP_SERVICES_PROTOCOL instance.\r
-  @param[out] ProcessorNumber  The handle number of AP that is to become the new\r
-                               BSP. The range is from 0 to the total number of\r
+  @param[out] ProcessorNumber  Pointer to the handle number of AP.\r
+                               The range is from 0 to the total number of\r
                                logical processors minus 1. The total number of\r
                                logical processors can be retrieved by\r
                                EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors().\r
@@ -917,6 +533,7 @@ CollectBistDataFromHob (
   UINTN                                 ProcessorNumber;\r
   EFI_PROCESSOR_INFORMATION             ProcessorInfo;\r
   EFI_HEALTH_FLAGS                      BistData;\r
+  UINTN                                 CpuInstanceNumber;\r
 \r
   SecPlatformInformation2 = NULL;\r
   SecPlatformInformation  = NULL;\r
@@ -948,7 +565,7 @@ CollectBistDataFromHob (
       BspCpuInstance.InfoRecord.IA32HealthFlags.Uint32  = SecPlatformInformation->IA32HealthFlags.Uint32;\r
       CpuInstance = &BspCpuInstance;\r
     } else {\r
-      DEBUG ((EFI_D_INFO, "Does not find any HOB stored CPU BIST information!\n"));\r
+      DEBUG ((DEBUG_INFO, "Does not find any HOB stored CPU BIST information!\n"));\r
       //\r
       // Does not find any HOB stored BIST information\r
       //\r
@@ -956,48 +573,237 @@ CollectBistDataFromHob (
     }\r
   }\r
 \r
-  while ((NumberOfData--) > 0) {\r
-    for (ProcessorNumber = 0; ProcessorNumber < mNumberOfProcessors; ProcessorNumber++) {\r
-      MpInitLibGetProcessorInfo (ProcessorNumber, &ProcessorInfo, &BistData);\r
-      if (ProcessorInfo.ProcessorId == CpuInstance[NumberOfData].CpuLocation) {\r
+  for (ProcessorNumber = 0; ProcessorNumber < mNumberOfProcessors; ProcessorNumber++) {\r
+    MpInitLibGetProcessorInfo (ProcessorNumber, &ProcessorInfo, &BistData);\r
+    for (CpuInstanceNumber = 0; CpuInstanceNumber < NumberOfData; CpuInstanceNumber++) {\r
+      if (ProcessorInfo.ProcessorId == CpuInstance[CpuInstanceNumber].CpuLocation) {\r
         //\r
         // Update CPU health status for MP Services Protocol according to BIST data.\r
         //\r
-        BistData = CpuInstance[NumberOfData].InfoRecord.IA32HealthFlags;\r
-      }\r
-      if (BistData.Uint32 != 0) {\r
-        //\r
-        // Report Status Code that self test is failed\r
-        //\r
-        REPORT_STATUS_CODE (\r
-          EFI_ERROR_CODE | EFI_ERROR_MAJOR,\r
-          (EFI_COMPUTING_UNIT_HOST_PROCESSOR | EFI_CU_HP_EC_SELF_TEST)\r
-          );\r
+        BistData = CpuInstance[CpuInstanceNumber].InfoRecord.IA32HealthFlags;\r
       }\r
     }\r
+    if (BistData.Uint32 != 0) {\r
+      //\r
+      // Report Status Code that self test is failed\r
+      //\r
+      REPORT_STATUS_CODE (\r
+        EFI_ERROR_CODE | EFI_ERROR_MAJOR,\r
+        (EFI_COMPUTING_UNIT_HOST_PROCESSOR | EFI_CU_HP_EC_SELF_TEST)\r
+        );\r
+    }\r
   }\r
 }\r
 \r
 /**\r
-  Callback function for ExitBootServices.\r
+  Get GDT register value.\r
+\r
+  This function is mainly for AP purpose because AP may have different GDT\r
+  table than BSP.\r
 \r
-  @param  Event                 Event whose notification function is being invoked.\r
-  @param  Context               The pointer to the notification function's context,\r
-                                which is implementation-dependent.\r
+  @param[in,out] Buffer  The pointer to private data buffer.\r
 \r
 **/\r
 VOID\r
 EFIAPI\r
-ExitBootServicesCallback (\r
-  IN EFI_EVENT                Event,\r
-  IN VOID                     *Context\r
+GetGdtr (\r
+  IN OUT VOID *Buffer\r
   )\r
 {\r
+  AsmReadGdtr ((IA32_DESCRIPTOR *)Buffer);\r
+}\r
+\r
+/**\r
+  Initializes CPU exceptions handlers for the sake of stack switch requirement.\r
+\r
+  This function is a wrapper of InitializeCpuExceptionHandlersEx. It's mainly\r
+  for the sake of AP's init because of EFI_AP_PROCEDURE API requirement.\r
+\r
+  @param[in,out] Buffer  The pointer to private data buffer.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+InitializeExceptionStackSwitchHandlers (\r
+  IN OUT VOID *Buffer\r
+  )\r
+{\r
+  CPU_EXCEPTION_INIT_DATA           *EssData;\r
+  IA32_DESCRIPTOR                   Idtr;\r
+  EFI_STATUS                        Status;\r
+\r
+  EssData = Buffer;\r
   //\r
-  // Avoid APs access invalid buff datas which allocated by BootServices,\r
-  // so we send INIT IPI to APs to let them wait for SIPI state.\r
+  // We don't plan to replace IDT table with a new one, but we should not assume\r
+  // the AP's IDT is the same as BSP's IDT either.\r
   //\r
-  SendInitIpiAllExcludingSelf ();\r
+  AsmReadIdtr (&Idtr);\r
+  EssData->Ia32.IdtTable = (VOID *)Idtr.Base;\r
+  EssData->Ia32.IdtTableSize = Idtr.Limit + 1;\r
+  Status = InitializeCpuExceptionHandlersEx (NULL, EssData);\r
+  ASSERT_EFI_ERROR (Status);\r
+}\r
+\r
+/**\r
+  Initializes MP exceptions handlers for the sake of stack switch requirement.\r
+\r
+  This function will allocate required resources required to setup stack switch\r
+  and pass them through CPU_EXCEPTION_INIT_DATA to each logic processor.\r
+\r
+**/\r
+VOID\r
+InitializeMpExceptionStackSwitchHandlers (\r
+  VOID\r
+  )\r
+{\r
+  UINTN                           Index;\r
+  UINTN                           Bsp;\r
+  UINTN                           ExceptionNumber;\r
+  UINTN                           OldGdtSize;\r
+  UINTN                           NewGdtSize;\r
+  UINTN                           NewStackSize;\r
+  IA32_DESCRIPTOR                 Gdtr;\r
+  CPU_EXCEPTION_INIT_DATA         EssData;\r
+  UINT8                           *GdtBuffer;\r
+  UINT8                           *StackTop;\r
+\r
+  ExceptionNumber = FixedPcdGetSize (PcdCpuStackSwitchExceptionList);\r
+  NewStackSize = FixedPcdGet32 (PcdCpuKnownGoodStackSize) * ExceptionNumber;\r
+\r
+  StackTop = AllocateRuntimeZeroPool (NewStackSize * mNumberOfProcessors);\r
+  ASSERT (StackTop != NULL);\r
+  StackTop += NewStackSize  * mNumberOfProcessors;\r
+\r
+  //\r
+  // The default exception handlers must have been initialized. Let's just skip\r
+  // it in this method.\r
+  //\r
+  EssData.Ia32.Revision = CPU_EXCEPTION_INIT_DATA_REV;\r
+  EssData.Ia32.InitDefaultHandlers = FALSE;\r
+\r
+  EssData.Ia32.StackSwitchExceptions = FixedPcdGetPtr(PcdCpuStackSwitchExceptionList);\r
+  EssData.Ia32.StackSwitchExceptionNumber = ExceptionNumber;\r
+  EssData.Ia32.KnownGoodStackSize = FixedPcdGet32(PcdCpuKnownGoodStackSize);\r
+\r
+  //\r
+  // Initialize Gdtr to suppress incorrect compiler/analyzer warnings.\r
+  //\r
+  Gdtr.Base = 0;\r
+  Gdtr.Limit = 0;\r
+  MpInitLibWhoAmI (&Bsp);\r
+  for (Index = 0; Index < mNumberOfProcessors; ++Index) {\r
+    //\r
+    // To support stack switch, we need to re-construct GDT but not IDT.\r
+    //\r
+    if (Index == Bsp) {\r
+      GetGdtr (&Gdtr);\r
+    } else {\r
+      //\r
+      // AP might have different size of GDT from BSP.\r
+      //\r
+      MpInitLibStartupThisAP (GetGdtr, Index, NULL, 0, (VOID *)&Gdtr, NULL);\r
+    }\r
+\r
+    //\r
+    // X64 needs only one TSS of current task working for all exceptions\r
+    // because of its IST feature. IA32 needs one TSS for each exception\r
+    // in addition to current task. Since AP is not supposed to allocate\r
+    // memory, we have to do it in BSP. To simplify the code, we allocate\r
+    // memory for IA32 case to cover both IA32 and X64 exception stack\r
+    // switch.\r
+    //\r
+    // Layout of memory to allocate for each processor:\r
+    //    --------------------------------\r
+    //    |            Alignment         |  (just in case)\r
+    //    --------------------------------\r
+    //    |                              |\r
+    //    |        Original GDT          |\r
+    //    |                              |\r
+    //    --------------------------------\r
+    //    |    Current task descriptor   |\r
+    //    --------------------------------\r
+    //    |                              |\r
+    //    |  Exception task descriptors  |  X ExceptionNumber\r
+    //    |                              |\r
+    //    --------------------------------\r
+    //    |  Current task-state segment  |\r
+    //    --------------------------------\r
+    //    |                              |\r
+    //    | Exception task-state segment |  X ExceptionNumber\r
+    //    |                              |\r
+    //    --------------------------------\r
+    //\r
+    OldGdtSize = Gdtr.Limit + 1;\r
+    EssData.Ia32.ExceptionTssDescSize = sizeof (IA32_TSS_DESCRIPTOR) *\r
+                                        (ExceptionNumber + 1);\r
+    EssData.Ia32.ExceptionTssSize = sizeof (IA32_TASK_STATE_SEGMENT) *\r
+                                    (ExceptionNumber + 1);\r
+    NewGdtSize = sizeof (IA32_TSS_DESCRIPTOR) +\r
+                 OldGdtSize +\r
+                 EssData.Ia32.ExceptionTssDescSize +\r
+                 EssData.Ia32.ExceptionTssSize;\r
+\r
+    GdtBuffer = AllocateRuntimeZeroPool (NewGdtSize);\r
+    ASSERT (GdtBuffer != NULL);\r
+\r
+    //\r
+    // Make sure GDT table alignment\r
+    //\r
+    EssData.Ia32.GdtTable = ALIGN_POINTER(GdtBuffer, sizeof (IA32_TSS_DESCRIPTOR));\r
+    NewGdtSize -= ((UINT8 *)EssData.Ia32.GdtTable - GdtBuffer);\r
+    EssData.Ia32.GdtTableSize = NewGdtSize;\r
+\r
+    EssData.Ia32.ExceptionTssDesc = ((UINT8 *)EssData.Ia32.GdtTable + OldGdtSize);\r
+    EssData.Ia32.ExceptionTss = ((UINT8 *)EssData.Ia32.GdtTable + OldGdtSize +\r
+                                 EssData.Ia32.ExceptionTssDescSize);\r
+\r
+    EssData.Ia32.KnownGoodStackTop = (UINTN)StackTop;\r
+    DEBUG ((DEBUG_INFO,\r
+            "Exception stack top[cpu%lu]: 0x%lX\n",\r
+            (UINT64)(UINTN)Index,\r
+            (UINT64)(UINTN)StackTop));\r
+\r
+    if (Index == Bsp) {\r
+      InitializeExceptionStackSwitchHandlers (&EssData);\r
+    } else {\r
+      MpInitLibStartupThisAP (\r
+        InitializeExceptionStackSwitchHandlers,\r
+        Index,\r
+        NULL,\r
+        0,\r
+        (VOID *)&EssData,\r
+        NULL\r
+        );\r
+    }\r
+\r
+    StackTop  -= NewStackSize;\r
+  }\r
+}\r
+\r
+/**\r
+  Initializes MP exceptions handlers for special features, such as Heap Guard\r
+  and Stack Guard.\r
+**/\r
+VOID\r
+InitializeMpExceptionHandlers (\r
+  VOID\r
+  )\r
+{\r
+  //\r
+  // Enable non-stop mode for #PF triggered by Heap Guard or NULL Pointer\r
+  // Detection.\r
+  //\r
+  if (HEAP_GUARD_NONSTOP_MODE || NULL_DETECTION_NONSTOP_MODE) {\r
+    RegisterCpuInterruptHandler (EXCEPT_IA32_DEBUG, DebugExceptionHandler);\r
+    RegisterCpuInterruptHandler (EXCEPT_IA32_PAGE_FAULT, PageFaultExceptionHandler);\r
+  }\r
+\r
+  //\r
+  // Setup stack switch for Stack Guard feature.\r
+  //\r
+  if (PcdGetBool (PcdCpuStackGuard)) {\r
+    InitializeMpExceptionStackSwitchHandlers ();\r
+  }\r
 }\r
 \r
 /**\r
@@ -1013,23 +819,20 @@ InitializeMpSupport (
   UINTN          NumberOfProcessors;\r
   UINTN          NumberOfEnabledProcessors;\r
 \r
-  NumberOfProcessors = (UINTN) PcdGet32 (PcdCpuMaxLogicalProcessorNumber);\r
-  if (NumberOfProcessors < 1) {\r
-    DEBUG ((DEBUG_ERROR, "Setting PcdCpuMaxLogicalProcessorNumber should be more than zero.\n"));\r
-    return;\r
-  }\r
-\r
   //\r
-  // Only perform AP detection if PcdCpuMaxLogicalProcessorNumber is greater than 1\r
+  // Wakeup APs to do initialization\r
   //\r
-  if (NumberOfProcessors > 1) {\r
-    Status = MpInitLibInitialize ();\r
-    ASSERT_EFI_ERROR (Status);\r
+  Status = MpInitLibInitialize ();\r
+  ASSERT_EFI_ERROR (Status);\r
 \r
-    MpInitLibGetNumberOfProcessors (&NumberOfProcessors, &NumberOfEnabledProcessors);\r
-    mNumberOfProcessors = NumberOfProcessors;\r
-  }\r
-  DEBUG ((EFI_D_ERROR, "Detect CPU count: %d\n", mNumberOfProcessors));\r
+  MpInitLibGetNumberOfProcessors (&NumberOfProcessors, &NumberOfEnabledProcessors);\r
+  mNumberOfProcessors = NumberOfProcessors;\r
+  DEBUG ((DEBUG_INFO, "Detect CPU count: %d\n", mNumberOfProcessors));\r
+\r
+  //\r
+  // Initialize special exception handlers for each logic processor.\r
+  //\r
+  InitializeMpExceptionHandlers ();\r
 \r
   //\r
   // Update CPU healthy information from Guided HOB\r