]> git.proxmox.com Git - mirror_edk2.git/blobdiff - UefiCpuPkg/Library/MpInitLib/DxeMpLib.c
UefiCpuPkg/MpInitLib: Fix MemTest86 failure.
[mirror_edk2.git] / UefiCpuPkg / Library / MpInitLib / DxeMpLib.c
index 383eec9c6b584a5c80517f0e163ffd9d54fdf8ca..cef5b49dde4be0225b2b1fec4935c5032b13b206 100644 (file)
@@ -1,7 +1,7 @@
 /** @file\r
   MP initialize support functions for DXE phase.\r
 \r
-  Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>\r
+  Copyright (c) 2016 - 2018, 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
 \r
 #include <Library/UefiLib.h>\r
 #include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/DebugAgentLib.h>\r
+#include <Library/DxeServicesTableLib.h>\r
+\r
+#include <Protocol/Timer.h>\r
 \r
 #define  AP_CHECK_INTERVAL     (EFI_TIMER_PERIOD_MILLISECONDS (100))\r
+#define  AP_SAFE_STACK_SIZE    128\r
 \r
 CPU_MP_DATA      *mCpuMpData = NULL;\r
 EFI_EVENT        mCheckAllApsEvent = NULL;\r
 EFI_EVENT        mMpInitExitBootServicesEvent = NULL;\r
+EFI_EVENT        mLegacyBootEvent = NULL;\r
 volatile BOOLEAN mStopCheckAllApsStatus = TRUE;\r
 VOID             *mReservedApLoopFunc = NULL;\r
+UINTN            mReservedTopOfApStack;\r
+volatile UINT32  mNumberToFinish = 0;\r
+\r
+/**\r
+  Enable Debug Agent to support source debugging on AP function.\r
+\r
+**/\r
+VOID\r
+EnableDebugAgent (\r
+  VOID\r
+  )\r
+{\r
+  //\r
+  // Initialize Debug Agent to support source level debug in DXE phase\r
+  //\r
+  InitializeDebugAgent (DEBUG_AGENT_INIT_DXE_AP, NULL, NULL);\r
+}\r
 \r
 /**\r
   Get the pointer to CPU MP Data structure.\r
@@ -53,63 +76,79 @@ SaveCpuMpData (
 }\r
 \r
 /**\r
-  Allocate reset vector buffer.\r
+  Get available system memory below 0x88000 by specified size.\r
 \r
-  @param[in, out]  CpuMpData  The pointer to CPU MP Data structure.\r
+  @param[in] WakeupBufferSize   Wakeup buffer size required\r
+\r
+  @retval other   Return wakeup buffer address below 1MB.\r
+  @retval -1      Cannot find free memory below 1MB.\r
 **/\r
-VOID\r
-AllocateResetVector (\r
-  IN OUT CPU_MP_DATA          *CpuMpData\r
+UINTN\r
+GetWakeupBuffer (\r
+  IN UINTN                WakeupBufferSize\r
   )\r
 {\r
-  EFI_STATUS            Status;\r
-  UINTN                 ApResetVectorSize;\r
-  EFI_PHYSICAL_ADDRESS  StartAddress;\r
-\r
-  ApResetVectorSize = CpuMpData->AddressMap.RendezvousFunnelSize +\r
-                      sizeof (MP_CPU_EXCHANGE_INFO);\r
+  EFI_STATUS              Status;\r
+  EFI_PHYSICAL_ADDRESS    StartAddress;\r
 \r
-  StartAddress = BASE_1MB;\r
+  //\r
+  // Try to allocate buffer below 1M for waking vector.\r
+  // LegacyBios driver only reports warning when page allocation in range\r
+  // [0x60000, 0x88000) fails.\r
+  // This library is consumed by CpuDxe driver to produce CPU Arch protocol.\r
+  // LagacyBios driver depends on CPU Arch protocol which guarantees below\r
+  // allocation runs earlier than LegacyBios driver.\r
+  //\r
+  StartAddress = 0x88000;\r
   Status = gBS->AllocatePages (\r
                   AllocateMaxAddress,\r
-                  EfiACPIMemoryNVS,\r
-                  EFI_SIZE_TO_PAGES (ApResetVectorSize),\r
+                  EfiBootServicesData,\r
+                  EFI_SIZE_TO_PAGES (WakeupBufferSize),\r
                   &StartAddress\r
                   );\r
   ASSERT_EFI_ERROR (Status);\r
+  if (EFI_ERROR (Status)) {\r
+    StartAddress = (EFI_PHYSICAL_ADDRESS) -1;\r
+  }\r
 \r
-  CpuMpData->WakeupBuffer      = (UINTN) StartAddress;\r
-  CpuMpData->MpCpuExchangeInfo = (MP_CPU_EXCHANGE_INFO *) (UINTN)\r
-                  (CpuMpData->WakeupBuffer + CpuMpData->AddressMap.RendezvousFunnelSize);\r
-  //\r
-  // copy AP reset code in it\r
-  //\r
-  CopyMem (\r
-    (VOID *) CpuMpData->WakeupBuffer,\r
-    (VOID *) CpuMpData->AddressMap.RendezvousFunnelAddress,\r
-    CpuMpData->AddressMap.RendezvousFunnelSize\r
-    );\r
+  DEBUG ((DEBUG_INFO, "WakeupBufferStart = %x, WakeupBufferSize = %x\n",\r
+                      (UINTN) StartAddress, WakeupBufferSize));\r
+\r
+  return (UINTN) StartAddress;\r
 }\r
 \r
 /**\r
-  Free AP reset vector buffer.\r
+  Get available EfiBootServicesCode memory below 4GB by specified size.\r
+\r
+  This buffer is required to safely transfer AP from real address mode to\r
+  protected mode or long mode, due to the fact that the buffer returned by\r
+  GetWakeupBuffer() may be marked as non-executable.\r
+\r
+  @param[in] BufferSize   Wakeup transition buffer size.\r
 \r
-  @param[in]  CpuMpData  The pointer to CPU MP Data structure.\r
+  @retval other   Return wakeup transition buffer address below 4GB.\r
+  @retval 0       Cannot find free memory below 4GB.\r
 **/\r
-VOID\r
-FreeResetVector (\r
-  IN CPU_MP_DATA              *CpuMpData\r
+UINTN\r
+GetModeTransitionBuffer (\r
+  IN UINTN                BufferSize\r
   )\r
 {\r
-  EFI_STATUS            Status;\r
-  UINTN                 ApResetVectorSize;\r
-  ApResetVectorSize = CpuMpData->AddressMap.RendezvousFunnelSize +\r
-                      sizeof (MP_CPU_EXCHANGE_INFO);\r
-  Status = gBS->FreePages(\r
-             (EFI_PHYSICAL_ADDRESS)CpuMpData->WakeupBuffer,\r
-             EFI_SIZE_TO_PAGES (ApResetVectorSize)\r
-             );\r
-  ASSERT_EFI_ERROR (Status);\r
+  EFI_STATUS              Status;\r
+  EFI_PHYSICAL_ADDRESS    StartAddress;\r
+\r
+  StartAddress = BASE_4GB - 1;\r
+  Status = gBS->AllocatePages (\r
+                  AllocateMaxAddress,\r
+                  EfiBootServicesCode,\r
+                  EFI_SIZE_TO_PAGES (BufferSize),\r
+                  &StartAddress\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    StartAddress = 0;\r
+  }\r
+\r
+  return (UINTN)StartAddress;\r
 }\r
 \r
 /**\r
@@ -163,7 +202,7 @@ CheckAndUpdateApsStatus (
 /**\r
   Checks APs' status periodically.\r
 \r
-  This function is triggerred by timer perodically to check the\r
+  This function is triggered by timer periodically to check the\r
   state of APs for StartupAllAPs() and StartupThisAP() executed\r
   in non-blocking mode.\r
 \r
@@ -189,7 +228,7 @@ CheckApsStatus (
 /**\r
   Get Protected mode code segment from current GDT table.\r
 \r
-  @returen  Protected mode code segment value.\r
+  @return  Protected mode code segment value.\r
 **/\r
 UINT16\r
 GetProtectedModeCS (\r
@@ -201,7 +240,6 @@ GetProtectedModeCS (
   UINTN                    GdtEntryCount;\r
   UINT16                   Index;\r
 \r
-  Index = (UINT16) -1;\r
   AsmReadGdtr (&GdtrDesc);\r
   GdtEntryCount = (GdtrDesc.Limit + 1) / sizeof (IA32_SEGMENT_DESCRIPTOR);\r
   GdtEntry = (IA32_SEGMENT_DESCRIPTOR *) GdtrDesc.Base;\r
@@ -213,7 +251,7 @@ GetProtectedModeCS (
     }\r
     GdtEntry++;\r
   }\r
-  ASSERT (Index != -1);\r
+  ASSERT (Index != GdtEntryCount);\r
   return Index * 8;\r
 }\r
 \r
@@ -231,11 +269,19 @@ RelocateApLoop (
   CPU_MP_DATA            *CpuMpData;\r
   BOOLEAN                MwaitSupport;\r
   ASM_RELOCATE_AP_LOOP   AsmRelocateApLoopFunc;\r
+  UINTN                  ProcessorNumber;\r
 \r
+  MpInitLibWhoAmI (&ProcessorNumber);\r
   CpuMpData    = GetCpuMpData ();\r
   MwaitSupport = IsMwaitSupport ();\r
-  AsmRelocateApLoopFunc = (ASM_RELOCATE_AP_LOOP) (UINTN) Buffer;\r
-  AsmRelocateApLoopFunc (MwaitSupport, CpuMpData->ApTargetCState, CpuMpData->PmCodeSegment);\r
+  AsmRelocateApLoopFunc = (ASM_RELOCATE_AP_LOOP) (UINTN) mReservedApLoopFunc;\r
+  AsmRelocateApLoopFunc (\r
+    MwaitSupport,\r
+    CpuMpData->ApTargetCState,\r
+    CpuMpData->PmCodeSegment,\r
+    mReservedTopOfApStack - ProcessorNumber * AP_SAFE_STACK_SIZE,\r
+    (UINTN) &mNumberToFinish\r
+    );\r
   //\r
   // It should never reach here\r
   //\r
@@ -252,7 +298,7 @@ RelocateApLoop (
 **/\r
 VOID\r
 EFIAPI\r
-MpInitExitBootServicesCallback (\r
+MpInitChangeApLoopCallback (\r
   IN EFI_EVENT                Event,\r
   IN VOID                     *Context\r
   )\r
@@ -262,8 +308,12 @@ MpInitExitBootServicesCallback (
   CpuMpData = GetCpuMpData ();\r
   CpuMpData->PmCodeSegment = GetProtectedModeCS ();\r
   CpuMpData->ApLoopMode = PcdGet8 (PcdCpuApLoopMode);\r
-  WakeUpAP (CpuMpData, TRUE, 0, RelocateApLoop, mReservedApLoopFunc);\r
-  DEBUG ((DEBUG_INFO, "MpInitExitBootServicesCallback() done!\n"));\r
+  mNumberToFinish = CpuMpData->CpuCount - 1;\r
+  WakeUpAP (CpuMpData, TRUE, 0, RelocateApLoop, NULL, TRUE);\r
+  while (mNumberToFinish > 0) {\r
+    CpuPause ();\r
+  }\r
+  DEBUG ((DEBUG_INFO, "%a() done!\n", __FUNCTION__));\r
 }\r
 \r
 /**\r
@@ -276,22 +326,122 @@ InitMpGlobalData (
   IN CPU_MP_DATA               *CpuMpData\r
   )\r
 {\r
-  EFI_STATUS     Status;\r
+  EFI_STATUS                          Status;\r
+  EFI_PHYSICAL_ADDRESS                Address;\r
+  UINTN                               ApSafeBufferSize;\r
+  UINTN                               Index;\r
+  EFI_GCD_MEMORY_SPACE_DESCRIPTOR     MemDesc;\r
+  UINTN                               StackBase;\r
+  CPU_INFO_IN_HOB                     *CpuInfoInHob;\r
 \r
   SaveCpuMpData (CpuMpData);\r
 \r
+  if (CpuMpData->CpuCount == 1) {\r
+    //\r
+    // If only BSP exists, return\r
+    //\r
+    return;\r
+  }\r
+\r
+  if (PcdGetBool (PcdCpuStackGuard)) {\r
+    //\r
+    // One extra page at the bottom of the stack is needed for Guard page.\r
+    //\r
+    if (CpuMpData->CpuApStackSize <= EFI_PAGE_SIZE) {\r
+      DEBUG ((DEBUG_ERROR, "PcdCpuApStackSize is not big enough for Stack Guard!\n"));\r
+      ASSERT (FALSE);\r
+    }\r
+\r
+    //\r
+    // DXE will reuse stack allocated for APs at PEI phase if it's available.\r
+    // Let's check it here.\r
+    //\r
+    // Note: BSP's stack guard is set at DxeIpl phase. But for the sake of\r
+    // BSP/AP exchange, stack guard for ApTopOfStack of cpu 0 will still be\r
+    // set here.\r
+    //\r
+    CpuInfoInHob = (CPU_INFO_IN_HOB *)(UINTN)CpuMpData->CpuInfoInHob;\r
+    for (Index = 0; Index < CpuMpData->CpuCount; ++Index) {\r
+      if (CpuInfoInHob != NULL && CpuInfoInHob[Index].ApTopOfStack != 0) {\r
+        StackBase = (UINTN)CpuInfoInHob[Index].ApTopOfStack - CpuMpData->CpuApStackSize;\r
+      } else {\r
+        StackBase = CpuMpData->Buffer + Index * CpuMpData->CpuApStackSize;\r
+      }\r
+\r
+      Status = gDS->GetMemorySpaceDescriptor (StackBase, &MemDesc);\r
+      ASSERT_EFI_ERROR (Status);\r
+\r
+      Status = gDS->SetMemorySpaceAttributes (\r
+                      StackBase,\r
+                      EFI_PAGES_TO_SIZE (1),\r
+                      MemDesc.Attributes | EFI_MEMORY_RP\r
+                      );\r
+      ASSERT_EFI_ERROR (Status);\r
+\r
+      DEBUG ((DEBUG_INFO, "Stack Guard set at %lx [cpu%lu]!\n",\r
+              (UINT64)StackBase, (UINT64)Index));\r
+    }\r
+  }\r
+\r
   //\r
-  // Avoid APs access invalid buff data which allocated by BootServices,\r
-  // so we will allocate reserved data for AP loop code.\r
+  // Avoid APs access invalid buffer data which allocated by BootServices,\r
+  // so we will allocate reserved data for AP loop code. We also need to\r
+  // allocate this buffer below 4GB due to APs may be transferred to 32bit\r
+  // protected mode on long mode DXE.\r
   // Allocating it in advance since memory services are not available in\r
   // Exit Boot Services callback function.\r
   //\r
-  mReservedApLoopFunc = AllocateReservedCopyPool (\r
-                          CpuMpData->AddressMap.RelocateApLoopFuncSize,\r
-                          CpuMpData->AddressMap.RelocateApLoopFuncAddress\r
-                          );\r
+  ApSafeBufferSize  = EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (\r
+                        CpuMpData->AddressMap.RelocateApLoopFuncSize\r
+                        ));\r
+  Address = BASE_4GB - 1;\r
+  Status  = gBS->AllocatePages (\r
+                   AllocateMaxAddress,\r
+                   EfiReservedMemoryType,\r
+                   EFI_SIZE_TO_PAGES (ApSafeBufferSize),\r
+                   &Address\r
+                   );\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  mReservedApLoopFunc = (VOID *) (UINTN) Address;\r
   ASSERT (mReservedApLoopFunc != NULL);\r
 \r
+  //\r
+  // Make sure that the buffer memory is executable if NX protection is enabled\r
+  // for EfiReservedMemoryType.\r
+  //\r
+  // TODO: Check EFI_MEMORY_XP bit set or not once it's available in DXE GCD\r
+  //       service.\r
+  //\r
+  Status = gDS->GetMemorySpaceDescriptor (Address, &MemDesc);\r
+  if (!EFI_ERROR (Status)) {\r
+    gDS->SetMemorySpaceAttributes (\r
+           Address,\r
+           ApSafeBufferSize,\r
+           MemDesc.Attributes & (~EFI_MEMORY_XP)\r
+           );\r
+  }\r
+\r
+  ApSafeBufferSize = EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (\r
+                       CpuMpData->CpuCount * AP_SAFE_STACK_SIZE\r
+                       ));\r
+  Address = BASE_4GB - 1;\r
+  Status  = gBS->AllocatePages (\r
+                   AllocateMaxAddress,\r
+                   EfiReservedMemoryType,\r
+                   EFI_SIZE_TO_PAGES (ApSafeBufferSize),\r
+                   &Address\r
+                   );\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  mReservedTopOfApStack = (UINTN) Address + ApSafeBufferSize;\r
+  ASSERT ((mReservedTopOfApStack & (UINTN)(CPU_STACK_ALIGNMENT - 1)) == 0);\r
+  CopyMem (\r
+    mReservedApLoopFunc,\r
+    CpuMpData->AddressMap.RelocateApLoopFuncAddress,\r
+    CpuMpData->AddressMap.RelocateApLoopFuncSize\r
+    );\r
+\r
   Status = gBS->CreateEvent (\r
                   EVT_TIMER | EVT_NOTIFY_SIGNAL,\r
                   TPL_NOTIFY,\r
@@ -310,14 +460,25 @@ InitMpGlobalData (
                   AP_CHECK_INTERVAL\r
                   );\r
   ASSERT_EFI_ERROR (Status);\r
+\r
   Status = gBS->CreateEvent (\r
                   EVT_SIGNAL_EXIT_BOOT_SERVICES,\r
                   TPL_CALLBACK,\r
-                  MpInitExitBootServicesCallback,\r
+                  MpInitChangeApLoopCallback,\r
                   NULL,\r
                   &mMpInitExitBootServicesEvent\r
                   );\r
   ASSERT_EFI_ERROR (Status);\r
+\r
+  Status = gBS->CreateEventEx (\r
+                  EVT_NOTIFY_SIGNAL,\r
+                  TPL_CALLBACK,\r
+                  MpInitChangeApLoopCallback,\r
+                  NULL,\r
+                  &gEfiEventLegacyBootGuid,\r
+                  &mLegacyBootEvent\r
+                  );\r
+  ASSERT_EFI_ERROR (Status);\r
 }\r
 \r
 /**\r
@@ -347,7 +508,7 @@ InitMpGlobalData (
                                       EFI_EVENT is defined in CreateEvent() in\r
                                       the Unified Extensible Firmware Interface\r
                                       Specification.\r
-  @param[in]  TimeoutInMicrosecsond   Indicates the time limit in microseconds for\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. Zero means\r
                                       infinity.  If the timeout expires before\r
@@ -457,7 +618,7 @@ MpInitLibStartupAllAPs (
                                       EFI_EVENT is defined in CreateEvent() in\r
                                       the Unified Extensible Firmware Interface\r
                                       Specification.\r
-  @param[in]  TimeoutInMicrosecsond   Indicates the time limit in microseconds for\r
+  @param[in]  TimeoutInMicroseconds   Indicates the time limit in microseconds 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
@@ -566,29 +727,38 @@ MpInitLibSwitchBSP (
   IN BOOLEAN                   EnableOldBSP\r
   )\r
 {\r
-  EFI_STATUS            Status;\r
-  BOOLEAN               OldInterruptState;\r
+  EFI_STATUS                   Status;\r
+  EFI_TIMER_ARCH_PROTOCOL      *Timer;\r
+  UINT64                       TimerPeriod;\r
 \r
+  TimerPeriod = 0;\r
   //\r
-  // Before send both BSP and AP to a procedure to exchange their roles,\r
-  // interrupt must be disabled. This is because during the exchange role\r
-  // process, 2 CPU may use 1 stack. If interrupt happens, the stack will\r
-  // be corrupted, since interrupt return address will be pushed to stack\r
-  // by hardware.\r
+  // Locate Timer Arch Protocol\r
   //\r
-  OldInterruptState = SaveAndDisableInterrupts ();\r
+  Status = gBS->LocateProtocol (&gEfiTimerArchProtocolGuid, NULL, (VOID **) &Timer);\r
+  if (EFI_ERROR (Status)) {\r
+    Timer = NULL;\r
+  }\r
 \r
-  //\r
-  // Mask LINT0 & LINT1 for the old BSP\r
-  //\r
-  DisableLvtInterrupts ();\r
+  if (Timer != NULL) {\r
+    //\r
+    // Save current rate of DXE Timer\r
+    //\r
+    Timer->GetTimerPeriod (Timer, &TimerPeriod);\r
+    //\r
+    // Disable DXE Timer and drain pending interrupts\r
+    //\r
+    Timer->SetTimerPeriod (Timer, 0);\r
+  }\r
 \r
   Status = SwitchBSPWorker (ProcessorNumber, EnableOldBSP);\r
 \r
-  //\r
-  // Restore interrupt state.\r
-  //\r
-  SetInterruptState (OldInterruptState);\r
+  if (Timer != NULL) {\r
+    //\r
+    // Enable and restore rate of DXE Timer\r
+    //\r
+    Timer->SetTimerPeriod (Timer, TimerPeriod);\r
+  }\r
 \r
   return Status;\r
 }\r