]> git.proxmox.com Git - mirror_edk2.git/blobdiff - UefiCpuPkg/Library/MpInitLib/MpLib.c
UefiCpuPkg/MpInitLib: Fix X64 XCODE5/NASM compatibility issues
[mirror_edk2.git] / UefiCpuPkg / Library / MpInitLib / MpLib.c
index 3c2e6d6b89d98ecc09901a48c4757db3646acd46..735e099b32f28e934ca20603679574bf82a02a30 100644 (file)
@@ -1,7 +1,7 @@
 /** @file\r
   CPU MP Initialize Library common functions.\r
 \r
-  Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>\r
+  Copyright (c) 2016 - 2017, 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
@@ -18,8 +18,11 @@ EFI_GUID mCpuInitMpLibHobGuid = CPU_INIT_MP_LIB_HOB_GUID;
 \r
 /**\r
   The function will check if BSP Execute Disable is enabled.\r
-  DxeIpl may have enabled Execute Disable for BSP,\r
-  APs need to get the status and sync up the settings.\r
+\r
+  DxeIpl may have enabled Execute Disable for BSP, APs need to\r
+  get the status and sync up the settings.\r
+  If BSP's CR0.Paging is not set, BSP execute Disble feature is\r
+  not working actually.\r
 \r
   @retval TRUE      BSP Execute Disable is enabled.\r
   @retval FALSE     BSP Execute Disable is not enabled.\r
@@ -33,23 +36,30 @@ IsBspExecuteDisableEnabled (
   CPUID_EXTENDED_CPU_SIG_EDX  Edx;\r
   MSR_IA32_EFER_REGISTER      EferMsr;\r
   BOOLEAN                     Enabled;\r
+  IA32_CR0                    Cr0;\r
 \r
   Enabled = FALSE;\r
-  AsmCpuid (CPUID_EXTENDED_FUNCTION, &Eax, NULL, NULL, NULL);\r
-  if (Eax >= CPUID_EXTENDED_CPU_SIG) {\r
-    AsmCpuid (CPUID_EXTENDED_CPU_SIG, NULL, NULL, NULL, &Edx.Uint32);\r
+  Cr0.UintN = AsmReadCr0 ();\r
+  if (Cr0.Bits.PG != 0) {\r
     //\r
-    // CPUID 0x80000001\r
-    // Bit 20: Execute Disable Bit available.\r
+    // If CR0 Paging bit is set\r
     //\r
-    if (Edx.Bits.NX != 0) {\r
-      EferMsr.Uint64 = AsmReadMsr64 (MSR_IA32_EFER);\r
+    AsmCpuid (CPUID_EXTENDED_FUNCTION, &Eax, NULL, NULL, NULL);\r
+    if (Eax >= CPUID_EXTENDED_CPU_SIG) {\r
+      AsmCpuid (CPUID_EXTENDED_CPU_SIG, NULL, NULL, NULL, &Edx.Uint32);\r
       //\r
-      // MSR 0xC0000080\r
-      // Bit 11: Execute Disable Bit enable.\r
+      // CPUID 0x80000001\r
+      // Bit 20: Execute Disable Bit available.\r
       //\r
-      if (EferMsr.Bits.NXE != 0) {\r
-        Enabled = TRUE;\r
+      if (Edx.Bits.NX != 0) {\r
+        EferMsr.Uint64 = AsmReadMsr64 (MSR_IA32_EFER);\r
+        //\r
+        // MSR 0xC0000080\r
+        // Bit 11: Execute Disable Bit enable.\r
+        //\r
+        if (EferMsr.Bits.NXE != 0) {\r
+          Enabled = TRUE;\r
+        }\r
       }\r
     }\r
   }\r
@@ -109,6 +119,53 @@ SetApState (
   ReleaseSpinLock (&CpuData->ApLock);\r
 }\r
 \r
+/**\r
+  Save BSP's local APIC timer setting.\r
+\r
+  @param[in] CpuMpData          Pointer to CPU MP Data\r
+**/\r
+VOID\r
+SaveLocalApicTimerSetting (\r
+  IN CPU_MP_DATA   *CpuMpData\r
+  )\r
+{\r
+  //\r
+  // Record the current local APIC timer setting of BSP\r
+  //\r
+  GetApicTimerState (\r
+    &CpuMpData->DivideValue,\r
+    &CpuMpData->PeriodicMode,\r
+    &CpuMpData->Vector\r
+    );\r
+  CpuMpData->CurrentTimerCount   = GetApicTimerCurrentCount ();\r
+  CpuMpData->TimerInterruptState = GetApicTimerInterruptState ();\r
+}\r
+\r
+/**\r
+  Sync local APIC timer setting from BSP to AP.\r
+\r
+  @param[in] CpuMpData          Pointer to CPU MP Data\r
+**/\r
+VOID\r
+SyncLocalApicTimerSetting (\r
+  IN CPU_MP_DATA   *CpuMpData\r
+  )\r
+{\r
+  //\r
+  // Sync local APIC timer setting from BSP to AP\r
+  //\r
+  InitializeApicTimer (\r
+    CpuMpData->DivideValue,\r
+    CpuMpData->CurrentTimerCount,\r
+    CpuMpData->PeriodicMode,\r
+    CpuMpData->Vector\r
+    );\r
+  //\r
+  // Disable AP's local APIC timer interrupt\r
+  //\r
+  DisableApicTimerInterrupt ();\r
+}\r
+\r
 /**\r
   Save the volatile registers required to be restored following INIT IPI.\r
 \r
@@ -325,20 +382,20 @@ ApInitializeSync (
 \r
   CpuMpData = (CPU_MP_DATA *) Buffer;\r
   //\r
-  // Sync BSP's MTRR table to AP\r
-  //\r
-  MtrrSetAllMtrrs (&CpuMpData->MtrrTable);\r
-  //\r
   // Load microcode on AP\r
   //\r
   MicrocodeDetect (CpuMpData);\r
+  //\r
+  // Sync BSP's MTRR table to AP\r
+  //\r
+  MtrrSetAllMtrrs (&CpuMpData->MtrrTable);\r
 }\r
 \r
 /**\r
   Find the current Processor number by APIC ID.\r
 \r
-  @param[in] CpuMpData         Pointer to PEI CPU MP Data\r
-  @param[in] ProcessorNumber   Return the pocessor number found\r
+  @param[in]  CpuMpData         Pointer to PEI CPU MP Data\r
+  @param[out] ProcessorNumber   Return the pocessor number found\r
 \r
   @retval EFI_SUCCESS          ProcessorNumber is found and returned.\r
   @retval EFI_NOT_FOUND        ProcessorNumber is not found.\r
@@ -377,6 +434,8 @@ CollectProcessorCount (
   IN CPU_MP_DATA         *CpuMpData\r
   )\r
 {\r
+  UINTN                  Index;\r
+\r
   //\r
   // Send 1st broadcast IPI to APs to wakeup APs\r
   //\r
@@ -408,6 +467,12 @@ CollectProcessorCount (
     // Enable x2APIC on BSP\r
     //\r
     SetApicMode (LOCAL_APIC_MODE_X2APIC);\r
+    //\r
+    // Set BSP/Aps state to IDLE\r
+    //\r
+    for (Index = 0; Index < CpuMpData->CpuCount; Index++) {\r
+      SetApState (&CpuMpData->CpuData[Index], CpuStateIdle);\r
+    }\r
   }\r
   DEBUG ((DEBUG_INFO, "APIC MODE is %d\n", GetApicMode ()));\r
   //\r
@@ -420,12 +485,13 @@ CollectProcessorCount (
   return CpuMpData->CpuCount;\r
 }\r
 \r
-/*\r
+/**\r
   Initialize CPU AP Data when AP is wakeup at the first time.\r
 \r
   @param[in, out] CpuMpData        Pointer to PEI CPU MP Data\r
   @param[in]      ProcessorNumber  The handle number of processor\r
   @param[in]      BistData         Processor BIST data\r
+  @param[in]      ApTopOfStack     Top of AP stack\r
 \r
 **/\r
 VOID\r
@@ -433,7 +499,7 @@ InitializeApData (
   IN OUT CPU_MP_DATA      *CpuMpData,\r
   IN     UINTN            ProcessorNumber,\r
   IN     UINT32           BistData,\r
-  IN     UINT           ApTopOfStack\r
+  IN     UINT64           ApTopOfStack\r
   )\r
 {\r
   CPU_INFO_IN_HOB          *CpuInfoInHob;\r
@@ -442,7 +508,7 @@ InitializeApData (
   CpuInfoInHob[ProcessorNumber].InitialApicId = GetInitialApicId ();\r
   CpuInfoInHob[ProcessorNumber].ApicId        = GetApicId ();\r
   CpuInfoInHob[ProcessorNumber].Health        = BistData;\r
-  CpuInfoInHob[ProcessorNumber].ApTopOfStack  = (UINT32) ApTopOfStack;\r
+  CpuInfoInHob[ProcessorNumber].ApTopOfStack  = ApTopOfStack;\r
 \r
   CpuMpData->CpuData[ProcessorNumber].Waiting    = FALSE;\r
   CpuMpData->CpuData[ProcessorNumber].CpuHealthy = (BistData == 0) ? TRUE : FALSE;\r
@@ -480,14 +546,19 @@ ApWakeupFunction (
   UINT32                     BistData;\r
   volatile UINT32            *ApStartupSignalBuffer;\r
   CPU_INFO_IN_HOB            *CpuInfoInHob;\r
-  UINT                     ApTopOfStack;\r
+  UINT64                     ApTopOfStack;\r
 \r
   //\r
   // AP finished assembly code and begin to execute C code\r
   //\r
   CpuMpData = ExchangeInfo->CpuMpData;\r
 \r
-  ProgramVirtualWireMode (); \r
+  //\r
+  // AP's local APIC settings will be lost after received INIT IPI\r
+  // We need to re-initialize them at here\r
+  //\r
+  ProgramVirtualWireMode ();\r
+  SyncLocalApicTimerSetting (CpuMpData);\r
 \r
   while (TRUE) {\r
     if (CpuMpData->InitFlag == ApInitConfig) {\r
@@ -500,7 +571,7 @@ ApWakeupFunction (
       // This is first time AP wakeup, get BIST information from AP stack\r
       //\r
       ApTopOfStack  = CpuMpData->Buffer + (ProcessorNumber + 1) * CpuMpData->CpuApStackSize;\r
-      BistData = *(UINT32 *) (ApTopOfStack - sizeof (UINTN));\r
+      BistData = *(UINT32 *) ((UINTN) ApTopOfStack - sizeof (UINTN));\r
       //\r
       // Do some AP initialize sync\r
       //\r
@@ -538,6 +609,10 @@ ApWakeupFunction (
         if (Procedure != NULL) {\r
           SetApState (&CpuMpData->CpuData[ProcessorNumber], CpuStateBusy);\r
           //\r
+          // Enable source debugging on AP function\r
+          //         \r
+          EnableDebugAgent ();\r
+          //\r
           // Invoke AP function here\r
           //\r
           Procedure (Parameter);\r
@@ -676,6 +751,8 @@ FillExchangeInfoData (
 \r
   ExchangeInfo->EnableExecuteDisable = IsBspExecuteDisableEnabled ();\r
 \r
+  ExchangeInfo->InitializeFloatingPointUnitsAddress = (UINTN)InitializeFloatingPointUnits;\r
+\r
   //\r
   // Get the BSP's data of GDT and IDT\r
   //\r
@@ -683,6 +760,21 @@ FillExchangeInfoData (
   AsmReadIdtr ((IA32_DESCRIPTOR *) &ExchangeInfo->IdtrProfile);\r
 }\r
 \r
+/**\r
+  Helper function that waits until the finished AP count reaches the specified\r
+  limit, or the specified timeout elapses (whichever comes first).\r
+\r
+  @param[in] CpuMpData        Pointer to CPU MP Data.\r
+  @param[in] FinishedApLimit  The number of finished APs to wait for.\r
+  @param[in] TimeLimit        The number of microseconds to wait for.\r
+**/\r
+VOID\r
+TimedWaitForApFinish (\r
+  IN CPU_MP_DATA               *CpuMpData,\r
+  IN UINT32                    FinishedApLimit,\r
+  IN UINT32                    TimeLimit\r
+  );\r
+\r
 /**\r
   This function will be called by BSP to wakeup AP.\r
 \r
@@ -716,6 +808,7 @@ WakeUpAP (
     ResetVectorRequired = TRUE;\r
     AllocateResetVector (CpuMpData);\r
     FillExchangeInfoData (CpuMpData);\r
+    SaveLocalApicTimerSetting (CpuMpData);\r
   } else if (CpuMpData->ApLoopMode == ApInMwaitLoop) {\r
     //\r
     // Get AP target C-state each time when waking up AP,\r
@@ -748,7 +841,11 @@ WakeUpAP (
       //\r
       // Wait for all potential APs waken up in one specified period\r
       //\r
-      MicroSecondDelay (PcdGet32(PcdCpuApInitTimeOutInMicroSeconds));\r
+      TimedWaitForApFinish (\r
+        CpuMpData,\r
+        PcdGet32 (PcdCpuMaxLogicalProcessorNumber) - 1,\r
+        PcdGet32 (PcdCpuApInitTimeOutInMicroSeconds)\r
+        );\r
     } else {\r
       //\r
       // Wait all APs waken up if this is not the 1st broadcast of SIPI\r
@@ -894,6 +991,58 @@ CheckTimeout (
   return FALSE;\r
 }\r
 \r
+/**\r
+  Helper function that waits until the finished AP count reaches the specified\r
+  limit, or the specified timeout elapses (whichever comes first).\r
+\r
+  @param[in] CpuMpData        Pointer to CPU MP Data.\r
+  @param[in] FinishedApLimit  The number of finished APs to wait for.\r
+  @param[in] TimeLimit        The number of microseconds to wait for.\r
+**/\r
+VOID\r
+TimedWaitForApFinish (\r
+  IN CPU_MP_DATA               *CpuMpData,\r
+  IN UINT32                    FinishedApLimit,\r
+  IN UINT32                    TimeLimit\r
+  )\r
+{\r
+  //\r
+  // CalculateTimeout() and CheckTimeout() consider a TimeLimit of 0\r
+  // "infinity", so check for (TimeLimit == 0) explicitly.\r
+  //\r
+  if (TimeLimit == 0) {\r
+    return;\r
+  }\r
+\r
+  CpuMpData->TotalTime = 0;\r
+  CpuMpData->ExpectedTime = CalculateTimeout (\r
+                              TimeLimit,\r
+                              &CpuMpData->CurrentTime\r
+                              );\r
+  while (CpuMpData->FinishedCount < FinishedApLimit &&\r
+         !CheckTimeout (\r
+            &CpuMpData->CurrentTime,\r
+            &CpuMpData->TotalTime,\r
+            CpuMpData->ExpectedTime\r
+            )) {\r
+    CpuPause ();\r
+  }\r
+\r
+  if (CpuMpData->FinishedCount >= FinishedApLimit) {\r
+    DEBUG ((\r
+      DEBUG_VERBOSE,\r
+      "%a: reached FinishedApLimit=%u in %Lu microseconds\n",\r
+      __FUNCTION__,\r
+      FinishedApLimit,\r
+      DivU64x64Remainder (\r
+        MultU64x32 (CpuMpData->TotalTime, 1000000),\r
+        GetPerformanceCounterProperties (NULL, NULL),\r
+        NULL\r
+        )\r
+      ));\r
+  }\r
+}\r
+\r
 /**\r
   Reset an AP to Idle state.\r
 \r
@@ -1225,6 +1374,10 @@ MpInitLibInitialize (
   // Store BSP's MTRR setting\r
   //\r
   MtrrGetAllMtrrs (&CpuMpData->MtrrTable);\r
+  //\r
+  // Enable the local APIC for Virtual Wire Mode.\r
+  //\r
+  ProgramVirtualWireMode ();\r
 \r
   if (OldCpuMpData == NULL) {\r
     if (MaxLogicalProcessorNumber > 1) {\r
@@ -1382,6 +1535,27 @@ SwitchBSPWorker (
   UINTN                        CallerNumber;\r
   CPU_STATE                    State;\r
   MSR_IA32_APIC_BASE_REGISTER  ApicBaseMsr;\r
+  BOOLEAN                      OldInterruptState;\r
+  BOOLEAN                      OldTimerInterruptState;\r
+\r
+  //\r
+  // Save and Disable Local APIC timer interrupt\r
+  //\r
+  OldTimerInterruptState = GetApicTimerInterruptState ();\r
+  DisableApicTimerInterrupt ();\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
+  //\r
+  OldInterruptState = SaveAndDisableInterrupts ();\r
+\r
+  //\r
+  // Mask LINT0 & LINT1 for the old BSP\r
+  //\r
+  DisableLvtInterrupts ();\r
 \r
   CpuMpData = GetCpuMpData ();\r
 \r
@@ -1458,12 +1632,23 @@ SwitchBSPWorker (
   //\r
   if (!EnableOldBSP) {\r
     SetApState (&CpuMpData->CpuData[CallerNumber], CpuStateDisabled);\r
+  } else {\r
+    SetApState (&CpuMpData->CpuData[CallerNumber], CpuStateIdle);\r
   }\r
   //\r
   // Save new BSP number\r
   //\r
   CpuMpData->BspNumber = (UINT32) ProcessorNumber;\r
 \r
+  //\r
+  // Restore interrupt state.\r
+  //\r
+  SetInterruptState (OldInterruptState);\r
+\r
+  if (OldTimerInterruptState) {\r
+    EnableApicTimerInterrupt ();\r
+  }\r
+\r
   return EFI_SUCCESS;\r
 }\r
 \r
@@ -1635,7 +1820,7 @@ MpInitLibGetNumberOfProcessors (
                                       simultaneously.\r
   @param[in]  WaitEvent               The event created by the caller with CreateEvent()\r
                                       service.\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.\r
   @param[in]  ProcedureArgument       The parameter passed into Procedure for\r
@@ -1789,7 +1974,7 @@ StartupAllAPsWorker (
   @param[in]  ProcessorNumber         The handle number of the AP.\r
   @param[in]  WaitEvent               The event created by the caller with CreateEvent()\r
                                       service.\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.\r
   @param[in]  ProcedureArgument       The parameter passed into Procedure for\r