]> git.proxmox.com Git - mirror_edk2.git/blobdiff - UefiCpuPkg/Library/MpInitLib/MpLib.c
UefiCpuPkg/MpInitLib: expand comment on initial AP enumeration
[mirror_edk2.git] / UefiCpuPkg / Library / MpInitLib / MpLib.c
index 85ca4a2946d9184a841a46a06406c8e9a0c3c32f..594a035d8b927ec031daff272c600d7c81dfd447 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
@@ -696,7 +692,7 @@ ApWakeupFunction (
             }\r
           }\r
         }\r
-        SetApState (&CpuMpData->CpuData[ProcessorNumber], CpuStateIdle);\r
+        SetApState (&CpuMpData->CpuData[ProcessorNumber], CpuStateFinished);\r
       }\r
     }\r
 \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
@@ -1037,14 +1045,36 @@ WakeUpAP (
     }\r
     if (CpuMpData->InitFlag == ApInitConfig) {\r
       //\r
-      // Here support two methods to collect AP count through adjust\r
-      // PcdCpuApInitTimeOutInMicroSeconds values.\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
-      // 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
+      // (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
@@ -1370,10 +1400,11 @@ CheckThisAP (
   //\r
   // If the AP finishes for StartupThisAP(), return EFI_SUCCESS.\r
   //\r
-  if (GetApState(CpuData) == CpuStateIdle) {\r
+  if (GetApState(CpuData) == CpuStateFinished) {\r
     if (CpuData->Finished != NULL) {\r
       *(CpuData->Finished) = TRUE;\r
     }\r
+    SetApState (CpuData, CpuStateIdle);\r
     return EFI_SUCCESS;\r
   } else {\r
     //\r
@@ -1434,9 +1465,10 @@ CheckAllAPs (
     // Only BSP and corresponding AP access this unit of CPU Data. This means the AP will not modify the\r
     // value of state after setting the it to CpuStateIdle, so BSP can safely make use of its value.\r
     //\r
-    if (GetApState(CpuData) == CpuStateIdle) {\r
+    if (GetApState(CpuData) == CpuStateFinished) {\r
       CpuMpData->RunningCount --;\r
       CpuMpData->CpuData[ProcessorNumber].Waiting = FALSE;\r
+      SetApState(CpuData, CpuStateIdle);\r
 \r
       //\r
       // If in Single Thread mode, then search for the next waiting AP for execution.\r
@@ -1610,38 +1642,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
@@ -1937,7 +1973,7 @@ SwitchBSPWorker (
   //\r
   // Wait for old BSP finished AP task\r
   //\r
-  while (GetApState (&CpuMpData->CpuData[CallerNumber]) != CpuStateIdle) {\r
+  while (GetApState (&CpuMpData->CpuData[CallerNumber]) != CpuStateFinished) {\r
     CpuPause ();\r
   }\r
 \r
@@ -2133,6 +2169,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
@@ -2154,9 +2191,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
@@ -2178,7 +2216,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
@@ -2221,9 +2259,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
@@ -2269,6 +2307,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
@@ -2414,3 +2459,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