]> git.proxmox.com Git - mirror_edk2.git/blobdiff - UefiCpuPkg/Library/MpInitLib/MpLib.c
UefiCpuPkg/MpInitLib: Make sure AP uses correct StartupApSignal
[mirror_edk2.git] / UefiCpuPkg / Library / MpInitLib / MpLib.c
index 407c44c95e623a5befdaf309dd17e2bdf54b7426..742f0c1f5e7b5b7b89e000e3c2695ae5ccbcf096 100644 (file)
@@ -1,7 +1,7 @@
 /** @file\r
   CPU MP Initialize Library common functions.\r
 \r
-  Copyright (c) 2016 - 2017, 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
@@ -195,6 +195,10 @@ SaveVolatileRegisters (
     VolatileRegisters->Dr6 = AsmReadDr6 ();\r
     VolatileRegisters->Dr7 = AsmReadDr7 ();\r
   }\r
+\r
+  AsmReadGdtr (&VolatileRegisters->Gdtr);\r
+  AsmReadIdtr (&VolatileRegisters->Idtr);\r
+  VolatileRegisters->Tr = AsmReadTr ();\r
 }\r
 \r
 /**\r
@@ -211,6 +215,7 @@ RestoreVolatileRegisters (
   )\r
 {\r
   CPUID_VERSION_INFO_EDX        VersionInfoEdx;\r
+  IA32_TSS_DESCRIPTOR           *Tss;\r
 \r
   AsmWriteCr0 (VolatileRegisters->Cr0);\r
   AsmWriteCr3 (VolatileRegisters->Cr3);\r
@@ -231,6 +236,18 @@ RestoreVolatileRegisters (
       AsmWriteDr7 (VolatileRegisters->Dr7);\r
     }\r
   }\r
+\r
+  AsmWriteGdtr (&VolatileRegisters->Gdtr);\r
+  AsmWriteIdtr (&VolatileRegisters->Idtr);\r
+  if (VolatileRegisters->Tr != 0 &&\r
+      VolatileRegisters->Tr < VolatileRegisters->Gdtr.Limit) {\r
+    Tss = (IA32_TSS_DESCRIPTOR *)(VolatileRegisters->Gdtr.Base +\r
+                                  VolatileRegisters->Tr);\r
+    if (Tss->Bits.P == 1) {\r
+      Tss->Bits.Type &= 0xD;  // 1101 - Clear busy bit just in case\r
+      AsmWriteTr (VolatileRegisters->Tr);\r
+    }\r
+  }\r
 }\r
 \r
 /**\r
@@ -313,6 +330,7 @@ SortApicId (
   CPU_INFO_IN_HOB   CpuInfo;\r
   UINT32            ApCount;\r
   CPU_INFO_IN_HOB   *CpuInfoInHob;\r
+  volatile UINT32   *StartupApSignal;\r
 \r
   ApCount = CpuMpData->CpuCount - 1;\r
   CpuInfoInHob = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob;\r
@@ -337,6 +355,14 @@ SortApicId (
           sizeof (CPU_INFO_IN_HOB)\r
           );\r
         CopyMem (&CpuInfoInHob[Index1], &CpuInfo, sizeof (CPU_INFO_IN_HOB));\r
+\r
+        //\r
+        // Also exchange the StartupApSignal.\r
+        //\r
+        StartupApSignal = CpuMpData->CpuData[Index3].StartupApSignal;\r
+        CpuMpData->CpuData[Index3].StartupApSignal =\r
+          CpuMpData->CpuData[Index1].StartupApSignal;\r
+        CpuMpData->CpuData[Index1].StartupApSignal = StartupApSignal;\r
       }\r
     }\r
 \r
@@ -451,6 +477,12 @@ CollectProcessorCount (
     CpuPause ();\r
   }\r
 \r
+  if (CpuMpData->CpuCount > 255) {\r
+    //\r
+    // If there are more than 255 processor found, force to enable X2APIC\r
+    //\r
+    CpuMpData->X2ApicEnable = TRUE;\r
+  }\r
   if (CpuMpData->X2ApicEnable) {\r
     DEBUG ((DEBUG_INFO, "Force x2APIC mode!\n"));\r
     //\r
@@ -530,13 +562,13 @@ InitializeApData (
   This function will be called from AP reset code if BSP uses WakeUpAP.\r
 \r
   @param[in] ExchangeInfo     Pointer to the MP exchange info buffer\r
-  @param[in] NumApsExecuting  Number of current executing AP\r
+  @param[in] ApIndex          Number of current executing AP\r
 **/\r
 VOID\r
 EFIAPI\r
 ApWakeupFunction (\r
   IN MP_CPU_EXCHANGE_INFO      *ExchangeInfo,\r
-  IN UINTN                     NumApsExecuting\r
+  IN UINTN                     ApIndex\r
   )\r
 {\r
   CPU_MP_DATA                *CpuMpData;\r
@@ -547,6 +579,7 @@ ApWakeupFunction (
   volatile UINT32            *ApStartupSignalBuffer;\r
   CPU_INFO_IN_HOB            *CpuInfoInHob;\r
   UINT64                     ApTopOfStack;\r
+  UINTN                      CurrentApicMode;\r
 \r
   //\r
   // AP finished assembly code and begin to execute C code\r
@@ -558,15 +591,20 @@ ApWakeupFunction (
   // We need to re-initialize them at here\r
   //\r
   ProgramVirtualWireMode ();\r
+  //\r
+  // Mask the LINT0 and LINT1 so that AP doesn't enter the system timer interrupt handler.\r
+  //\r
+  DisableLvtInterrupts ();\r
   SyncLocalApicTimerSetting (CpuMpData);\r
 \r
+  CurrentApicMode = GetApicMode ();\r
   while (TRUE) {\r
     if (CpuMpData->InitFlag == ApInitConfig) {\r
       //\r
       // Add CPU number\r
       //\r
       InterlockedIncrement ((UINT32 *) &CpuMpData->CpuCount);\r
-      ProcessorNumber = NumApsExecuting;\r
+      ProcessorNumber = ApIndex;\r
       //\r
       // This is first time AP wakeup, get BIST information from AP stack\r
       //\r
@@ -627,11 +665,23 @@ ApWakeupFunction (
             ApStartupSignalBuffer = CpuMpData->CpuData[ProcessorNumber].StartupApSignal;\r
             CpuInfoInHob[ProcessorNumber].ApTopOfStack = CpuInfoInHob[CpuMpData->NewBspNumber].ApTopOfStack;\r
           } else {\r
-            //\r
-            // Re-get the CPU APICID and Initial APICID\r
-            //\r
-            CpuInfoInHob[ProcessorNumber].ApicId        = GetApicId ();\r
-            CpuInfoInHob[ProcessorNumber].InitialApicId = GetInitialApicId ();\r
+            if (CpuInfoInHob[ProcessorNumber].ApicId != GetApicId () ||\r
+                CpuInfoInHob[ProcessorNumber].InitialApicId != GetInitialApicId ()) {\r
+              if (CurrentApicMode != GetApicMode ()) {\r
+                //\r
+                // If APIC mode change happened during AP function execution,\r
+                // we do not support APIC ID value changed.\r
+                //\r
+                ASSERT (FALSE);\r
+                CpuDeadLoop ();\r
+              } else {\r
+                //\r
+                // Re-get the CPU APICID and Initial APICID if they are changed\r
+                //\r
+                CpuInfoInHob[ProcessorNumber].ApicId        = GetApicId ();\r
+                CpuInfoInHob[ProcessorNumber].InitialApicId = GetInitialApicId ();\r
+              }\r
+            }\r
           }\r
         }\r
         SetApState (&CpuMpData->CpuData[ProcessorNumber], CpuStateFinished);\r
@@ -642,6 +692,7 @@ 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
@@ -730,6 +781,8 @@ FillExchangeInfoData (
   )\r
 {\r
   volatile MP_CPU_EXCHANGE_INFO    *ExchangeInfo;\r
+  UINTN                            Size;\r
+  IA32_SEGMENT_DESCRIPTOR          *Selector;\r
 \r
   ExchangeInfo                  = CpuMpData->MpCpuExchangeInfo;\r
   ExchangeInfo->Lock            = 0;\r
@@ -744,6 +797,7 @@ FillExchangeInfoData (
   ExchangeInfo->Cr3             = AsmReadCr3 ();\r
 \r
   ExchangeInfo->CFunction       = (UINTN) ApWakeupFunction;\r
+  ExchangeInfo->ApIndex         = 0;\r
   ExchangeInfo->NumApsExecuting = 0;\r
   ExchangeInfo->InitFlag        = (UINTN) CpuMpData->InitFlag;\r
   ExchangeInfo->CpuInfo         = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob;\r
@@ -751,11 +805,52 @@ 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
   AsmReadGdtr ((IA32_DESCRIPTOR *) &ExchangeInfo->GdtrProfile);\r
   AsmReadIdtr ((IA32_DESCRIPTOR *) &ExchangeInfo->IdtrProfile);\r
+\r
+  //\r
+  // Find a 32-bit code segment\r
+  //\r
+  Selector = (IA32_SEGMENT_DESCRIPTOR *)ExchangeInfo->GdtrProfile.Base;\r
+  Size = ExchangeInfo->GdtrProfile.Limit + 1;\r
+  while (Size > 0) {\r
+    if (Selector->Bits.L == 0 && Selector->Bits.Type >= 8) {\r
+      ExchangeInfo->ModeTransitionSegment =\r
+        (UINT16)((UINTN)Selector - ExchangeInfo->GdtrProfile.Base);\r
+      break;\r
+    }\r
+    Selector += 1;\r
+    Size -= sizeof (IA32_SEGMENT_DESCRIPTOR);\r
+  }\r
+\r
+  //\r
+  // Copy all 32-bit code and 64-bit code into memory with type of\r
+  // EfiBootServicesCode to avoid page fault if NX memory protection is enabled.\r
+  //\r
+  if (CpuMpData->WakeupBufferHigh != 0) {\r
+    Size = CpuMpData->AddressMap.RendezvousFunnelSize -\r
+           CpuMpData->AddressMap.ModeTransitionOffset;\r
+    CopyMem (\r
+      (VOID *)CpuMpData->WakeupBufferHigh,\r
+      CpuMpData->AddressMap.RendezvousFunnelAddress +\r
+      CpuMpData->AddressMap.ModeTransitionOffset,\r
+      Size\r
+      );\r
+\r
+    ExchangeInfo->ModeTransitionMemory = (UINT32)CpuMpData->WakeupBufferHigh;\r
+    ExchangeInfo->ModeHighMemory = (UINT32)CpuMpData->WakeupBufferHigh +\r
+                                   (UINT32)ExchangeInfo->ModeOffset -\r
+                                   (UINT32)CpuMpData->AddressMap.ModeTransitionOffset;\r
+    ExchangeInfo->ModeHighSegment = (UINT16)ExchangeInfo->CodeSegment;\r
+  } else {\r
+    ExchangeInfo->ModeTransitionMemory = (UINT32)\r
+      (ExchangeInfo->BufferStart + CpuMpData->AddressMap.ModeTransitionOffset);\r
+  }\r
 }\r
 \r
 /**\r
@@ -773,6 +868,85 @@ TimedWaitForApFinish (
   IN UINT32                    TimeLimit\r
   );\r
 \r
+/**\r
+  Get available system memory below 1MB by specified size.\r
+\r
+  @param[in]  CpuMpData  The pointer to CPU MP Data structure.\r
+**/\r
+VOID\r
+BackupAndPrepareWakeupBuffer(\r
+  IN CPU_MP_DATA              *CpuMpData\r
+  )\r
+{\r
+  CopyMem (\r
+    (VOID *) CpuMpData->BackupBuffer,\r
+    (VOID *) CpuMpData->WakeupBuffer,\r
+    CpuMpData->BackupBufferSize\r
+    );\r
+  CopyMem (\r
+    (VOID *) CpuMpData->WakeupBuffer,\r
+    (VOID *) CpuMpData->AddressMap.RendezvousFunnelAddress,\r
+    CpuMpData->AddressMap.RendezvousFunnelSize\r
+    );\r
+}\r
+\r
+/**\r
+  Restore wakeup buffer data.\r
+\r
+  @param[in]  CpuMpData  The pointer to CPU MP Data structure.\r
+**/\r
+VOID\r
+RestoreWakeupBuffer(\r
+  IN CPU_MP_DATA              *CpuMpData\r
+  )\r
+{\r
+  CopyMem (\r
+    (VOID *) CpuMpData->WakeupBuffer,\r
+    (VOID *) CpuMpData->BackupBuffer,\r
+    CpuMpData->BackupBufferSize\r
+    );\r
+}\r
+\r
+/**\r
+  Allocate reset vector buffer.\r
+\r
+  @param[in, out]  CpuMpData  The pointer to CPU MP Data structure.\r
+**/\r
+VOID\r
+AllocateResetVector (\r
+  IN OUT CPU_MP_DATA          *CpuMpData\r
+  )\r
+{\r
+  UINTN           ApResetVectorSize;\r
+\r
+  if (CpuMpData->WakeupBuffer == (UINTN) -1) {\r
+    ApResetVectorSize = CpuMpData->AddressMap.RendezvousFunnelSize +\r
+                          sizeof (MP_CPU_EXCHANGE_INFO);\r
+\r
+    CpuMpData->WakeupBuffer      = GetWakeupBuffer (ApResetVectorSize);\r
+    CpuMpData->MpCpuExchangeInfo = (MP_CPU_EXCHANGE_INFO *) (UINTN)\r
+                    (CpuMpData->WakeupBuffer + CpuMpData->AddressMap.RendezvousFunnelSize);\r
+    CpuMpData->WakeupBufferHigh  = GetModeTransitionBuffer (\r
+                                    CpuMpData->AddressMap.RendezvousFunnelSize -\r
+                                    CpuMpData->AddressMap.ModeTransitionOffset\r
+                                    );\r
+  }\r
+  BackupAndPrepareWakeupBuffer (CpuMpData);\r
+}\r
+\r
+/**\r
+  Free AP reset vector buffer.\r
+\r
+  @param[in]  CpuMpData  The pointer to CPU MP Data structure.\r
+**/\r
+VOID\r
+FreeResetVector (\r
+  IN CPU_MP_DATA              *CpuMpData\r
+  )\r
+{\r
+  RestoreWakeupBuffer (CpuMpData);\r
+}\r
+\r
 /**\r
   This function will be called by BSP to wakeup AP.\r
 \r
@@ -837,13 +1011,24 @@ WakeUpAP (
     }\r
     if (CpuMpData->InitFlag == ApInitConfig) {\r
       //\r
-      // Wait for all potential APs waken up in one specified period\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
+\r
+      while (CpuMpData->MpCpuExchangeInfo->NumApsExecuting != 0) {\r
+        CpuPause();\r
+      }\r
     } else {\r
       //\r
       // Wait all APs waken up if this is not the 1st broadcast of SIPI\r
@@ -904,6 +1089,9 @@ CalculateTimeout (
   OUT UINT64  *CurrentTime\r
   )\r
 {\r
+  UINT64 TimeoutInSeconds;\r
+  UINT64 TimestampCounterFreq;\r
+\r
   //\r
   // Read the current value of the performance counter\r
   //\r
@@ -919,16 +1107,36 @@ CalculateTimeout (
 \r
   //\r
   // GetPerformanceCounterProperties () returns the timestamp counter's frequency\r
-  // in Hz. So multiply the return value with TimeoutInMicroseconds and then divide\r
-  // it by 1,000,000, to get the number of ticks for the timeout value.\r
-  //\r
-  return DivU64x32 (\r
-           MultU64x64 (\r
-             GetPerformanceCounterProperties (NULL, NULL),\r
-             TimeoutInMicroseconds\r
-             ),\r
-           1000000\r
-           );\r
+  // in Hz. \r
+  //\r
+  TimestampCounterFreq = GetPerformanceCounterProperties (NULL, NULL);\r
+\r
+  //\r
+  // Check the potential overflow before calculate the number of ticks for the timeout value.\r
+  //\r
+  if (DivU64x64Remainder (MAX_UINT64, TimeoutInMicroseconds, NULL) < TimestampCounterFreq) {\r
+    //\r
+    // Convert microseconds into seconds if direct multiplication overflows\r
+    //\r
+    TimeoutInSeconds = DivU64x32 (TimeoutInMicroseconds, 1000000);\r
+    //\r
+    // Assertion if the final tick count exceeds MAX_UINT64\r
+    //\r
+    ASSERT (DivU64x64Remainder (MAX_UINT64, TimeoutInSeconds, NULL) >= TimestampCounterFreq);\r
+    return MultU64x64 (TimestampCounterFreq, TimeoutInSeconds);\r
+  } else {\r
+    //\r
+    // No overflow case, multiply the return value with TimeoutInMicroseconds and then divide\r
+    // it by 1,000,000, to get the number of ticks for the timeout value.\r
+    //\r
+    return DivU64x32 (\r
+             MultU64x64 (\r
+               TimestampCounterFreq,\r
+               TimeoutInMicroseconds\r
+               ),\r
+             1000000\r
+             );\r
+  }\r
 }\r
 \r
 /**\r
@@ -1331,7 +1539,6 @@ MpInitLibInitialize (
   CpuMpData->CpuApStackSize   = ApStackSize;\r
   CpuMpData->BackupBuffer     = BackupBufferAddr;\r
   CpuMpData->BackupBufferSize = ApResetVectorSize;\r
-  CpuMpData->SaveRestoreFlag  = FALSE;\r
   CpuMpData->WakeupBuffer     = (UINTN) -1;\r
   CpuMpData->CpuCount         = 1;\r
   CpuMpData->BspNumber        = 0;\r
@@ -1339,6 +1546,8 @@ MpInitLibInitialize (
   CpuMpData->SwitchBspFlag    = FALSE;\r
   CpuMpData->CpuData          = (CPU_AP_DATA *) (CpuMpData + 1);\r
   CpuMpData->CpuInfoInHob     = (UINT64) (UINTN) (CpuMpData->CpuData + MaxLogicalProcessorNumber);\r
+  CpuMpData->MicrocodePatchAddress    = PcdGet64 (PcdCpuMicrocodePatchAddress);\r
+  CpuMpData->MicrocodePatchRegionSize = PcdGet64 (PcdCpuMicrocodePatchRegionSize);\r
   InitializeSpinLock(&CpuMpData->MpLock);\r
   //\r
   // Save BSP's Control registers to APs\r
@@ -1347,7 +1556,7 @@ MpInitLibInitialize (
   //\r
   // Set BSP basic information\r
   //\r
-  InitializeApData (CpuMpData, 0, 0, CpuMpData->Buffer);\r
+  InitializeApData (CpuMpData, 0, 0, CpuMpData->Buffer + ApStackSize);\r
   //\r
   // Save assembly code information\r
   //\r
@@ -1396,7 +1605,7 @@ MpInitLibInitialize (
     CpuInfoInHob = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob;\r
     for (Index = 0; Index < CpuMpData->CpuCount; Index++) {\r
       InitializeSpinLock(&CpuMpData->CpuData[Index].ApLock);\r
-      if (CpuInfoInHob[Index].InitialApicId >= 255) {\r
+      if (CpuInfoInHob[Index].InitialApicId >= 255 || Index > 254) {\r
         CpuMpData->X2ApicEnable = TRUE;\r
       }\r
       CpuMpData->CpuData[Index].CpuHealthy = (CpuInfoInHob[Index].Health == 0)? TRUE:FALSE;\r
@@ -1562,7 +1771,7 @@ SwitchBSPWorker (
   //\r
   MpInitLibWhoAmI (&CallerNumber);\r
   if (CallerNumber != CpuMpData->BspNumber) {\r
-    return EFI_SUCCESS;\r
+    return EFI_DEVICE_ERROR;\r
   }\r
 \r
   if (ProcessorNumber >= CpuMpData->CpuCount) {\r
@@ -1616,6 +1825,7 @@ SwitchBSPWorker (
   ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE);\r
   ApicBaseMsr.Bits.BSP = 1;\r
   AsmWriteMsr64 (MSR_IA32_APIC_BASE, ApicBaseMsr.Uint64);\r
+  ProgramVirtualWireMode ();\r
 \r
   //\r
   // Wait for old BSP finished AP task\r
@@ -1695,7 +1905,7 @@ EnableDisableApWorker (
   if (!EnableAP) {\r
     SetApState (&CpuMpData->CpuData[ProcessorNumber], CpuStateDisabled);\r
   } else {\r
-    SetApState (&CpuMpData->CpuData[ProcessorNumber], CpuStateIdle);\r
+    ResetProcessorToIdleState (ProcessorNumber);\r
   }\r
 \r
   if (HealthFlag != NULL) {\r
@@ -2098,41 +2308,3 @@ GetCpuMpDataFromGuidedHob (
   return CpuMpData;\r
 }\r
 \r
-/**\r
-  Get available system memory below 1MB by specified size.\r
-\r
-  @param[in]  CpuMpData  The pointer to CPU MP Data structure.\r
-**/\r
-VOID\r
-BackupAndPrepareWakeupBuffer(\r
-  IN CPU_MP_DATA              *CpuMpData\r
-  )\r
-{\r
-  CopyMem (\r
-    (VOID *) CpuMpData->BackupBuffer,\r
-    (VOID *) CpuMpData->WakeupBuffer,\r
-    CpuMpData->BackupBufferSize\r
-    );\r
-  CopyMem (\r
-    (VOID *) CpuMpData->WakeupBuffer,\r
-    (VOID *) CpuMpData->AddressMap.RendezvousFunnelAddress,\r
-    CpuMpData->AddressMap.RendezvousFunnelSize\r
-    );\r
-}\r
-\r
-/**\r
-  Restore wakeup buffer data.\r
-\r
-  @param[in]  CpuMpData  The pointer to CPU MP Data structure.\r
-**/\r
-VOID\r
-RestoreWakeupBuffer(\r
-  IN CPU_MP_DATA              *CpuMpData\r
-  )\r
-{\r
-  CopyMem (\r
-    (VOID *) CpuMpData->WakeupBuffer,\r
-    (VOID *) CpuMpData->BackupBuffer,\r
-    CpuMpData->BackupBufferSize\r
-    );\r
-}\r