]> git.proxmox.com Git - mirror_edk2.git/blobdiff - UefiCpuPkg/Library/MpInitLib/MpLib.c
UefiCpuPkg/MpInitLib: honor the platform's boot CPU count in AP detection
[mirror_edk2.git] / UefiCpuPkg / Library / MpInitLib / MpLib.c
index 7f4d6e60bd9af92fd6ed765108f8dcf70cfd0e37..622b70ca3c4ecd60c031c3afd2c86edb149356cd 100644 (file)
@@ -1,14 +1,8 @@
 /** @file\r
   CPU MP Initialize Library common functions.\r
 \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
-  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) 2016 - 2019, Intel Corporation. All rights reserved.<BR>\r
+  SPDX-License-Identifier: BSD-2-Clause-Patent\r
 \r
 **/\r
 \r
@@ -625,6 +619,8 @@ ApWakeupFunction (
       RestoreVolatileRegisters (&CpuMpData->CpuData[0].VolatileRegisters, FALSE);\r
       InitializeApData (CpuMpData, ProcessorNumber, BistData, ApTopOfStack);\r
       ApStartupSignalBuffer = CpuMpData->CpuData[ProcessorNumber].StartupApSignal;\r
+\r
+      InterlockedDecrement ((UINT32 *) &CpuMpData->MpCpuExchangeInfo->NumApsExecuting);\r
     } else {\r
       //\r
       // Execute AP function if AP is ready\r
@@ -704,7 +700,6 @@ ApWakeupFunction (
     // AP finished executing C code\r
     //\r
     InterlockedIncrement ((UINT32 *) &CpuMpData->FinishedCount);\r
-    InterlockedDecrement ((UINT32 *) &CpuMpData->MpCpuExchangeInfo->NumApsExecuting);\r
 \r
     //\r
     // Place AP is specified loop mode\r
@@ -795,6 +790,7 @@ FillExchangeInfoData (
   volatile MP_CPU_EXCHANGE_INFO    *ExchangeInfo;\r
   UINTN                            Size;\r
   IA32_SEGMENT_DESCRIPTOR          *Selector;\r
+  IA32_CR4                         Cr4;\r
 \r
   ExchangeInfo                  = CpuMpData->MpCpuExchangeInfo;\r
   ExchangeInfo->Lock            = 0;\r
@@ -819,6 +815,18 @@ FillExchangeInfoData (
 \r
   ExchangeInfo->InitializeFloatingPointUnitsAddress = (UINTN)InitializeFloatingPointUnits;\r
 \r
+  //\r
+  // We can check either CPUID(7).ECX[bit16] or check CR4.LA57[bit12]\r
+  //  to determin whether 5-Level Paging is enabled.\r
+  // CPUID(7).ECX[bit16] shows CPU's capability, CR4.LA57[bit12] shows\r
+  // current system setting.\r
+  // Using latter way is simpler because it also eliminates the needs to\r
+  //  check whether platform wants to enable it.\r
+  //\r
+  Cr4.UintN = AsmReadCr4 ();\r
+  ExchangeInfo->Enable5LevelPaging = (BOOLEAN) (Cr4.Bits.LA57 == 1);\r
+  DEBUG ((DEBUG_INFO, "%a: 5-Level Paging = %d\n", gEfiCallerBaseName, ExchangeInfo->Enable5LevelPaging));\r
+\r
   //\r
   // Get the BSP's data of GDT and IDT\r
   //\r
@@ -1036,24 +1044,67 @@ WakeUpAP (
       SendInitSipiSipiAllExcludingSelf ((UINT32) ExchangeInfo->BufferStart);\r
     }\r
     if (CpuMpData->InitFlag == ApInitConfig) {\r
-      //\r
-      // Here support two methods to collect AP count through adjust\r
-      // PcdCpuApInitTimeOutInMicroSeconds values.\r
-      //\r
-      // one way is set a value to just let the first AP to start the\r
-      // initialization, then through the later while loop to wait all Aps\r
-      // finsh the initialization.\r
-      // The other way is set a value to let all APs finished the initialzation.\r
-      // In this case, the later while loop is useless.\r
-      //\r
-      TimedWaitForApFinish (\r
-        CpuMpData,\r
-        PcdGet32 (PcdCpuMaxLogicalProcessorNumber) - 1,\r
-        PcdGet32 (PcdCpuApInitTimeOutInMicroSeconds)\r
-        );\r
+      if (PcdGet32 (PcdCpuBootLogicalProcessorNumber) > 0) {\r
+        //\r
+        // The AP enumeration algorithm below is suitable only when the\r
+        // platform can tell us the *exact* boot CPU count in advance.\r
+        //\r
+        // The wait below finishes only when the detected AP count reaches\r
+        // (PcdCpuBootLogicalProcessorNumber - 1), regardless of how long that\r
+        // takes. If at least one AP fails to check in (meaning a platform\r
+        // hardware bug), the detection hangs forever, by design. If the actual\r
+        // boot CPU count in the system is higher than\r
+        // PcdCpuBootLogicalProcessorNumber (meaning a platform\r
+        // misconfiguration), then some APs may complete initialization after\r
+        // the wait finishes, and cause undefined behavior.\r
+        //\r
+        TimedWaitForApFinish (\r
+          CpuMpData,\r
+          PcdGet32 (PcdCpuBootLogicalProcessorNumber) - 1,\r
+          MAX_UINT32 // approx. 71 minutes\r
+          );\r
+      } else {\r
+        //\r
+        // The AP enumeration algorithm below is suitable for two use cases.\r
+        //\r
+        // (1) The check-in time for an individual AP is bounded, and APs run\r
+        //     through their initialization routines strongly concurrently. In\r
+        //     particular, the number of concurrently running APs\r
+        //     ("NumApsExecuting") is never expected to fall to zero\r
+        //     *temporarily* -- it is expected to fall to zero only when all\r
+        //     APs have checked-in.\r
+        //\r
+        //     In this case, the platform is supposed to set\r
+        //     PcdCpuApInitTimeOutInMicroSeconds to a low-ish value (just long\r
+        //     enough for one AP to start initialization). The timeout will be\r
+        //     reached soon, and remaining APs are collected by watching\r
+        //     NumApsExecuting fall to zero. If NumApsExecuting falls to zero\r
+        //     mid-process, while some APs have not completed initialization,\r
+        //     the behavior is undefined.\r
+        //\r
+        // (2) The check-in time for an individual AP is unbounded, and/or APs\r
+        //     may complete their initializations widely spread out. In\r
+        //     particular, some APs may finish initialization before some APs\r
+        //     even start.\r
+        //\r
+        //     In this case, the platform is supposed to set\r
+        //     PcdCpuApInitTimeOutInMicroSeconds to a high-ish value. The AP\r
+        //     enumeration will always take that long (except when the boot CPU\r
+        //     count happens to be maximal, that is,\r
+        //     PcdCpuMaxLogicalProcessorNumber). All APs are expected to\r
+        //     check-in before the timeout, and NumApsExecuting is assumed zero\r
+        //     at timeout. APs that miss the time-out may cause undefined\r
+        //     behavior.\r
+        //\r
+        TimedWaitForApFinish (\r
+          CpuMpData,\r
+          PcdGet32 (PcdCpuMaxLogicalProcessorNumber) - 1,\r
+          PcdGet32 (PcdCpuApInitTimeOutInMicroSeconds)\r
+          );\r
 \r
-      while (CpuMpData->MpCpuExchangeInfo->NumApsExecuting != 0) {\r
-        CpuPause();\r
+        while (CpuMpData->MpCpuExchangeInfo->NumApsExecuting != 0) {\r
+          CpuPause();\r
+        }\r
       }\r
     } else {\r
       //\r
@@ -1612,38 +1663,42 @@ MpInitLibInitialize (
   CpuMpData->SwitchBspFlag    = FALSE;\r
   CpuMpData->CpuData          = (CPU_AP_DATA *) (CpuMpData + 1);\r
   CpuMpData->CpuInfoInHob     = (UINT64) (UINTN) (CpuMpData->CpuData + MaxLogicalProcessorNumber);\r
-  CpuMpData->MicrocodePatchRegionSize = PcdGet64 (PcdCpuMicrocodePatchRegionSize);\r
-  //\r
-  // If platform has more than one CPU, relocate microcode to memory to reduce\r
-  // loading microcode time.\r
-  //\r
-  MicrocodePatchInRam = NULL;\r
-  if (MaxLogicalProcessorNumber > 1) {\r
-    MicrocodePatchInRam = AllocatePages (\r
-                            EFI_SIZE_TO_PAGES (\r
-                              (UINTN)CpuMpData->MicrocodePatchRegionSize\r
-                              )\r
-                            );\r
-  }\r
-  if (MicrocodePatchInRam == NULL) {\r
-    //\r
-    // there is only one processor, or no microcode patch is available, or\r
-    // memory allocation failed\r
-    //\r
-    CpuMpData->MicrocodePatchAddress = PcdGet64 (PcdCpuMicrocodePatchAddress);\r
-  } else {\r
+  if (OldCpuMpData == NULL) {\r
+    CpuMpData->MicrocodePatchRegionSize = PcdGet64 (PcdCpuMicrocodePatchRegionSize);\r
     //\r
-    // there are multiple processors, and a microcode patch is available, and\r
-    // memory allocation succeeded\r
+    // If platform has more than one CPU, relocate microcode to memory to reduce\r
+    // loading microcode time.\r
     //\r
-    CopyMem (\r
-      MicrocodePatchInRam,\r
-      (VOID *)(UINTN)PcdGet64 (PcdCpuMicrocodePatchAddress),\r
-      (UINTN)CpuMpData->MicrocodePatchRegionSize\r
-      );\r
-    CpuMpData->MicrocodePatchAddress = (UINTN)MicrocodePatchInRam;\r
+    MicrocodePatchInRam = NULL;\r
+    if (MaxLogicalProcessorNumber > 1) {\r
+      MicrocodePatchInRam = AllocatePages (\r
+                              EFI_SIZE_TO_PAGES (\r
+                                (UINTN)CpuMpData->MicrocodePatchRegionSize\r
+                                )\r
+                              );\r
+    }\r
+    if (MicrocodePatchInRam == NULL) {\r
+      //\r
+      // there is only one processor, or no microcode patch is available, or\r
+      // memory allocation failed\r
+      //\r
+      CpuMpData->MicrocodePatchAddress = PcdGet64 (PcdCpuMicrocodePatchAddress);\r
+    } else {\r
+      //\r
+      // there are multiple processors, and a microcode patch is available, and\r
+      // memory allocation succeeded\r
+      //\r
+      CopyMem (\r
+        MicrocodePatchInRam,\r
+        (VOID *)(UINTN)PcdGet64 (PcdCpuMicrocodePatchAddress),\r
+        (UINTN)CpuMpData->MicrocodePatchRegionSize\r
+        );\r
+      CpuMpData->MicrocodePatchAddress = (UINTN)MicrocodePatchInRam;\r
+    }\r
+  }else {\r
+    CpuMpData->MicrocodePatchRegionSize = OldCpuMpData->MicrocodePatchRegionSize;\r
+    CpuMpData->MicrocodePatchAddress    = OldCpuMpData->MicrocodePatchAddress;\r
   }\r
-\r
   InitializeSpinLock(&CpuMpData->MpLock);\r
 \r
   //\r
@@ -2135,6 +2190,7 @@ MpInitLibGetNumberOfProcessors (
                                       number.  If FALSE, then all the enabled APs\r
                                       execute the function specified by Procedure\r
                                       simultaneously.\r
+  @param[in]  ExcludeBsp              Whether let BSP also trig this task.\r
   @param[in]  WaitEvent               The event created by the caller with CreateEvent()\r
                                       service.\r
   @param[in]  TimeoutInMicroseconds   Indicates the time limit in microseconds for\r
@@ -2156,9 +2212,10 @@ MpInitLibGetNumberOfProcessors (
 \r
 **/\r
 EFI_STATUS\r
-StartupAllAPsWorker (\r
+StartupAllCPUsWorker (\r
   IN  EFI_AP_PROCEDURE          Procedure,\r
   IN  BOOLEAN                   SingleThread,\r
+  IN  BOOLEAN                   ExcludeBsp,\r
   IN  EFI_EVENT                 WaitEvent               OPTIONAL,\r
   IN  UINTN                     TimeoutInMicroseconds,\r
   IN  VOID                      *ProcedureArgument      OPTIONAL,\r
@@ -2180,7 +2237,7 @@ StartupAllAPsWorker (
     *FailedCpuList = NULL;\r
   }\r
 \r
-  if (CpuMpData->CpuCount == 1) {\r
+  if (CpuMpData->CpuCount == 1 && ExcludeBsp) {\r
     return EFI_NOT_STARTED;\r
   }\r
 \r
@@ -2223,9 +2280,9 @@ StartupAllAPsWorker (
     }\r
   }\r
 \r
-  if (!HasEnabledAp) {\r
+  if (!HasEnabledAp && ExcludeBsp) {\r
     //\r
-    // If no enabled AP exists, return EFI_NOT_STARTED.\r
+    // If no enabled AP exists and not include Bsp to do the procedure, return EFI_NOT_STARTED.\r
     //\r
     return EFI_NOT_STARTED;\r
   }\r
@@ -2271,6 +2328,13 @@ StartupAllAPsWorker (
     }\r
   }\r
 \r
+  if (!ExcludeBsp) {\r
+    //\r
+    // Start BSP.\r
+    //\r
+    Procedure (ProcedureArgument);\r
+  }\r
+\r
   Status = EFI_SUCCESS;\r
   if (WaitEvent == NULL) {\r
     do {\r
@@ -2416,3 +2480,47 @@ GetCpuMpDataFromGuidedHob (
   return CpuMpData;\r
 }\r
 \r
+/**\r
+  This service executes a caller provided function on all enabled CPUs.\r
+\r
+  @param[in]  Procedure               A pointer to the function to be run on\r
+                                      enabled APs of the system. See type\r
+                                      EFI_AP_PROCEDURE.\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. TimeoutInMicroseconds is ignored\r
+                                      for BSP.\r
+  @param[in]  ProcedureArgument       The parameter passed into Procedure for\r
+                                      all APs.\r
+\r
+  @retval EFI_SUCCESS             In blocking mode, all CPUs have finished before\r
+                                  the timeout expired.\r
+  @retval EFI_SUCCESS             In non-blocking mode, function has been dispatched\r
+                                  to all enabled CPUs.\r
+  @retval EFI_DEVICE_ERROR        Caller processor is AP.\r
+  @retval EFI_NOT_READY           Any enabled APs are busy.\r
+  @retval EFI_NOT_READY           MP Initialize Library is not initialized.\r
+  @retval EFI_TIMEOUT             In blocking mode, the timeout expired before\r
+                                  all enabled APs have finished.\r
+  @retval EFI_INVALID_PARAMETER   Procedure is NULL.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+MpInitLibStartupAllCPUs (\r
+  IN  EFI_AP_PROCEDURE          Procedure,\r
+  IN  UINTN                     TimeoutInMicroseconds,\r
+  IN  VOID                      *ProcedureArgument      OPTIONAL\r
+  )\r
+{\r
+  return StartupAllCPUsWorker (\r
+           Procedure,\r
+           FALSE,\r
+           FALSE,\r
+           NULL,\r
+           TimeoutInMicroseconds,\r
+           ProcedureArgument,\r
+           NULL\r
+           );\r
+}\r