]> git.proxmox.com Git - mirror_edk2.git/blobdiff - UefiCpuPkg/Library/MpInitLib/MpLib.c
UefiCpuPkg/MpInitLib: Use BSP uCode for APs if possible.
[mirror_edk2.git] / UefiCpuPkg / Library / MpInitLib / MpLib.c
index 18060fd0998c0323b37c1a0a8a30bd437ca626a0..722db2a01ffe913cdf0c9809516c311e10559d1e 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
@@ -384,7 +410,7 @@ ApInitializeSync (
   //\r
   // Load microcode on AP\r
   //\r
-  MicrocodeDetect (CpuMpData);\r
+  MicrocodeDetect (CpuMpData, FALSE);\r
   //\r
   // Sync BSP's MTRR table to AP\r
   //\r
@@ -536,7 +562,7 @@ 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
@@ -565,6 +591,10 @@ 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
@@ -585,7 +615,9 @@ ApWakeupFunction (
       //\r
       ApInitializeSync (CpuMpData);\r
       //\r
-      // Sync BSP's Control registers to APs\r
+      // CpuMpData->CpuData[0].VolatileRegisters is initialized based on BSP environment,\r
+      //   to initialize AP in InitConfig path.\r
+      // NOTE: IDTR.BASE stored in CpuMpData->CpuData[0].VolatileRegisters points to a different IDT shared by all APs.\r
       //\r
       RestoreVolatileRegisters (&CpuMpData->CpuData[0].VolatileRegisters, FALSE);\r
       InitializeApData (CpuMpData, ProcessorNumber, BistData, ApTopOfStack);\r
@@ -609,6 +641,13 @@ ApWakeupFunction (
         // Restore AP's volatile registers saved\r
         //\r
         RestoreVolatileRegisters (&CpuMpData->CpuData[ProcessorNumber].VolatileRegisters, TRUE);\r
+      } else {\r
+        //\r
+        // The CPU driver might not flush TLB for APs on spot after updating\r
+        // page attributes. AP in mwait loop mode needs to take care of it when\r
+        // woken up.\r
+        //\r
+        CpuFlushTlb ();\r
       }\r
 \r
       if (GetApState (&CpuMpData->CpuData[ProcessorNumber]) == CpuStateReady) {\r
@@ -618,7 +657,7 @@ ApWakeupFunction (
           SetApState (&CpuMpData->CpuData[ProcessorNumber], CpuStateBusy);\r
           //\r
           // Enable source debugging on AP function\r
-          //         \r
+          //\r
           EnableDebugAgent ();\r
           //\r
           // Invoke AP function here\r
@@ -751,6 +790,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
@@ -780,6 +821,46 @@ FillExchangeInfoData (
   //\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
+  } else {\r
+    ExchangeInfo->ModeTransitionMemory = (UINT32)\r
+      (ExchangeInfo->BufferStart + CpuMpData->AddressMap.ModeTransitionOffset);\r
+  }\r
+\r
+  ExchangeInfo->ModeHighMemory = ExchangeInfo->ModeTransitionMemory +\r
+                         (UINT32)ExchangeInfo->ModeOffset -\r
+                         (UINT32)CpuMpData->AddressMap.ModeTransitionOffset;\r
+  ExchangeInfo->ModeHighSegment = (UINT16)ExchangeInfo->CodeSegment;\r
 }\r
 \r
 /**\r
@@ -855,6 +936,10 @@ AllocateResetVector (
     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
@@ -1032,7 +1117,7 @@ CalculateTimeout (
 \r
   //\r
   // GetPerformanceCounterProperties () returns the timestamp counter's frequency\r
-  // in Hz. \r
+  // in Hz.\r
   //\r
   TimestampCounterFreq = GetPerformanceCounterProperties (NULL, NULL);\r
 \r
@@ -1423,6 +1508,7 @@ MpInitLibInitialize (
   UINT32                   MaxLogicalProcessorNumber;\r
   UINT32                   ApStackSize;\r
   MP_ASSEMBLY_ADDRESS_MAP  AddressMap;\r
+  CPU_VOLATILE_REGISTERS   VolatileRegisters;\r
   UINTN                    BufferSize;\r
   UINT32                   MonitorFilterSize;\r
   VOID                     *MpBuffer;\r
@@ -1433,6 +1519,8 @@ MpInitLibInitialize (
   UINTN                    Index;\r
   UINTN                    ApResetVectorSize;\r
   UINTN                    BackupBufferAddr;\r
+  UINTN                    ApIdtBase;\r
+  VOID                     *MicrocodePatchInRam;\r
 \r
   OldCpuMpData = GetCpuMpDataFromGuidedHob ();\r
   if (OldCpuMpData == NULL) {\r
@@ -1447,19 +1535,48 @@ MpInitLibInitialize (
   ApStackSize = PcdGet32(PcdCpuApStackSize);\r
   ApLoopMode  = GetApLoopMode (&MonitorFilterSize);\r
 \r
+  //\r
+  // Save BSP's Control registers for APs\r
+  //\r
+  SaveVolatileRegisters (&VolatileRegisters);\r
+\r
   BufferSize  = ApStackSize * MaxLogicalProcessorNumber;\r
   BufferSize += MonitorFilterSize * MaxLogicalProcessorNumber;\r
-  BufferSize += sizeof (CPU_MP_DATA);\r
   BufferSize += ApResetVectorSize;\r
+  BufferSize  = ALIGN_VALUE (BufferSize, 8);\r
+  BufferSize += VolatileRegisters.Idtr.Limit + 1;\r
+  BufferSize += sizeof (CPU_MP_DATA);\r
   BufferSize += (sizeof (CPU_AP_DATA) + sizeof (CPU_INFO_IN_HOB))* MaxLogicalProcessorNumber;\r
   MpBuffer    = AllocatePages (EFI_SIZE_TO_PAGES (BufferSize));\r
   ASSERT (MpBuffer != NULL);\r
   ZeroMem (MpBuffer, BufferSize);\r
   Buffer = (UINTN) MpBuffer;\r
 \r
+  //\r
+  //  The layout of the Buffer is as below:\r
+  //\r
+  //    +--------------------+ <-- Buffer\r
+  //        AP Stacks (N)\r
+  //    +--------------------+ <-- MonitorBuffer\r
+  //    AP Monitor Filters (N)\r
+  //    +--------------------+ <-- BackupBufferAddr (CpuMpData->BackupBuffer)\r
+  //         Backup Buffer\r
+  //    +--------------------+\r
+  //           Padding\r
+  //    +--------------------+ <-- ApIdtBase (8-byte boundary)\r
+  //           AP IDT          All APs share one separate IDT. So AP can get address of CPU_MP_DATA from IDT Base.\r
+  //    +--------------------+ <-- CpuMpData\r
+  //         CPU_MP_DATA\r
+  //    +--------------------+ <-- CpuMpData->CpuData\r
+  //        CPU_AP_DATA (N)\r
+  //    +--------------------+ <-- CpuMpData->CpuInfoInHob\r
+  //      CPU_INFO_IN_HOB (N)\r
+  //    +--------------------+\r
+  //\r
   MonitorBuffer    = (UINT8 *) (Buffer + ApStackSize * MaxLogicalProcessorNumber);\r
   BackupBufferAddr = (UINTN) MonitorBuffer + MonitorFilterSize * MaxLogicalProcessorNumber;\r
-  CpuMpData = (CPU_MP_DATA *) (BackupBufferAddr + ApResetVectorSize);\r
+  ApIdtBase        = ALIGN_VALUE (BackupBufferAddr + ApResetVectorSize, 8);\r
+  CpuMpData        = (CPU_MP_DATA *) (ApIdtBase + VolatileRegisters.Idtr.Limit + 1);\r
   CpuMpData->Buffer           = Buffer;\r
   CpuMpData->CpuApStackSize   = ApStackSize;\r
   CpuMpData->BackupBuffer     = BackupBufferAddr;\r
@@ -1471,17 +1588,57 @@ 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
+  //\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
+    //\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
+\r
   InitializeSpinLock(&CpuMpData->MpLock);\r
+\r
+  //\r
+  // Make sure no memory usage outside of the allocated buffer.\r
+  //\r
+  ASSERT ((CpuMpData->CpuInfoInHob + sizeof (CPU_INFO_IN_HOB) * MaxLogicalProcessorNumber) ==\r
+          Buffer + BufferSize);\r
+\r
   //\r
-  // Save BSP's Control registers to APs\r
+  // Duplicate BSP's IDT to APs.\r
+  // All APs share one separate IDT. So AP can get the address of CpuMpData by using IDTR.BASE + IDTR.LIMIT + 1\r
   //\r
-  SaveVolatileRegisters (&CpuMpData->CpuData[0].VolatileRegisters);\r
+  CopyMem ((VOID *)ApIdtBase, (VOID *)VolatileRegisters.Idtr.Base, VolatileRegisters.Idtr.Limit + 1);\r
+  VolatileRegisters.Idtr.Base = ApIdtBase;\r
+  CopyMem (&CpuMpData->CpuData[0].VolatileRegisters, &VolatileRegisters, sizeof (VolatileRegisters));\r
   //\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
@@ -1501,7 +1658,7 @@ MpInitLibInitialize (
   //\r
   // Load Microcode on BSP\r
   //\r
-  MicrocodeDetect (CpuMpData);\r
+  MicrocodeDetect (CpuMpData, TRUE);\r
   //\r
   // Store BSP's MTRR setting\r
   //\r
@@ -1535,11 +1692,7 @@ MpInitLibInitialize (
       }\r
       CpuMpData->CpuData[Index].CpuHealthy = (CpuInfoInHob[Index].Health == 0)? TRUE:FALSE;\r
       CpuMpData->CpuData[Index].ApFunction = 0;\r
-      CopyMem (\r
-        &CpuMpData->CpuData[Index].VolatileRegisters,\r
-        &CpuMpData->CpuData[0].VolatileRegisters,\r
-        sizeof (CPU_VOLATILE_REGISTERS)\r
-        );\r
+      CopyMem (&CpuMpData->CpuData[Index].VolatileRegisters, &VolatileRegisters, sizeof (CPU_VOLATILE_REGISTERS));\r
     }\r
     if (MaxLogicalProcessorNumber > 1) {\r
       //\r
@@ -1654,7 +1807,7 @@ MpInitLibGetProcessorInfo (
                                enabled AP. Otherwise, it will be disabled.\r
 \r
   @retval EFI_SUCCESS          BSP successfully switched.\r
-  @retval others               Failed to switch BSP. \r
+  @retval others               Failed to switch BSP.\r
 \r
 **/\r
 EFI_STATUS\r
@@ -1750,6 +1903,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