]> git.proxmox.com Git - mirror_edk2.git/commitdiff
UefiCpuPkg/MpInitLib: Reduce the size when loading microcode patches
authorHao A Wu <hao.a.wu@intel.com>
Thu, 19 Dec 2019 06:33:44 +0000 (14:33 +0800)
committermergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Thu, 2 Jan 2020 03:10:36 +0000 (03:10 +0000)
REF:https://bugzilla.tianocore.org/show_bug.cgi?id=2429

This commit will attempt to reduce the copy size when loading the
microcode patches data from flash into memory.

Such optimization is done by a pre-process of the microcode patch headers
(on flash). A microcode patch will be loaded into memory only when the
below 3 criteria are met:

A. With a microcode patch header (which means the data is not padding data
   between microcode patches);
B. The 'ProcessorSignature' & 'ProcessorFlags' fields in the header match
   at least one processor within system;
C. If the Extended Signature Table exists in a microcode patch, the
   'ProcessorSignature' & 'ProcessorFlag' fields in the table entries
   match at least one processor within system.

Criterion B and C will require all the processors to be woken up once to
collect their CPUID and Platform ID information. Hence, this commit will
move the copy, detect and apply of microcode patch on BSP and APs after
all the processors have been woken up.

Cc: Eric Dong <eric.dong@intel.com>
Cc: Ray Ni <ray.ni@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Star Zeng <star.zeng@intel.com>
Cc: Siyuan Fu <siyuan.fu@intel.com>
Cc: Michael D Kinney <michael.d.kinney@intel.com>
Signed-off-by: Hao A Wu <hao.a.wu@intel.com>
Reviewed-by: Eric Dong <eric.dong@intel.com>
Reviewed-by: Ray Ni <ray.ni@intel.com>
UefiCpuPkg/Library/MpInitLib/Microcode.c
UefiCpuPkg/Library/MpInitLib/MpLib.c
UefiCpuPkg/Library/MpInitLib/MpLib.h

index 199b1f23ce35c1ae335dafe8d3432160b7b6fad8..330fd99623347ef172542b9bceaa708cd29ef424 100644 (file)
@@ -331,3 +331,291 @@ Done:
        MicroData [0x%08x], Revision [0x%08x]\n", Eax.Uint32, ProcessorFlags, (UINTN) MicrocodeData, LatestRevision));\r
   }\r
 }\r
+\r
+/**\r
+  Determine if a microcode patch will be loaded into memory.\r
+\r
+  @param[in]  CpuMpData             The pointer to CPU MP Data structure.\r
+  @param[in]  ProcessorSignature    The processor signature field value\r
+                                    supported by a microcode patch.\r
+  @param[in]  ProcessorFlags        The prcessor flags field value supported by\r
+                                    a microcode patch.\r
+\r
+  @retval TRUE     The specified microcode patch will be loaded.\r
+  @retval FALSE    The specified microcode patch will not be loaded.\r
+**/\r
+BOOLEAN\r
+IsMicrocodePatchNeedLoad (\r
+  IN CPU_MP_DATA                 *CpuMpData,\r
+  IN UINT32                      ProcessorSignature,\r
+  IN UINT32                      ProcessorFlags\r
+  )\r
+{\r
+  UINTN          Index;\r
+  CPU_AP_DATA    *CpuData;\r
+\r
+  for (Index = 0; Index < CpuMpData->CpuCount; Index++) {\r
+    CpuData = &CpuMpData->CpuData[Index];\r
+    if ((ProcessorSignature == CpuData->ProcessorSignature) &&\r
+        (ProcessorFlags & (1 << CpuData->PlatformId)) != 0) {\r
+      return TRUE;\r
+    }\r
+  }\r
+\r
+  return FALSE;\r
+}\r
+\r
+/**\r
+  Actual worker function that loads the required microcode patches into memory.\r
+\r
+  @param[in, out]  CpuMpData        The pointer to CPU MP Data structure.\r
+  @param[in]       Patches          The pointer to an array of information on\r
+                                    the microcode patches that will be loaded\r
+                                    into memory.\r
+  @param[in]       PatchCount       The number of microcode patches that will\r
+                                    be loaded into memory.\r
+  @param[in]       TotalLoadSize    The total size of all the microcode patches\r
+                                    to be loaded.\r
+**/\r
+VOID\r
+LoadMicrocodePatchWorker (\r
+  IN OUT CPU_MP_DATA             *CpuMpData,\r
+  IN     MICROCODE_PATCH_INFO    *Patches,\r
+  IN     UINTN                   PatchCount,\r
+  IN     UINTN                   TotalLoadSize\r
+  )\r
+{\r
+  UINTN    Index;\r
+  VOID     *MicrocodePatchInRam;\r
+  UINT8    *Walker;\r
+\r
+  ASSERT ((Patches != NULL) && (PatchCount != 0));\r
+\r
+  MicrocodePatchInRam = AllocatePages (EFI_SIZE_TO_PAGES (TotalLoadSize));\r
+  if (MicrocodePatchInRam == NULL) {\r
+    return;\r
+  }\r
+\r
+  //\r
+  // Load all the required microcode patches into memory\r
+  //\r
+  for (Walker = MicrocodePatchInRam, Index = 0; Index < PatchCount; Index++) {\r
+    CopyMem (\r
+      Walker,\r
+      (VOID *) Patches[Index].Address,\r
+      Patches[Index].Size\r
+      );\r
+\r
+    //\r
+    // Zero-fill the padding area\r
+    // Please note that AlignedSize will be no less than Size\r
+    //\r
+    ZeroMem (\r
+      Walker + Patches[Index].Size,\r
+      Patches[Index].AlignedSize - Patches[Index].Size\r
+      );\r
+\r
+    Walker += Patches[Index].AlignedSize;\r
+  }\r
+\r
+  //\r
+  // Update the microcode patch related fields in CpuMpData\r
+  //\r
+  CpuMpData->MicrocodePatchAddress    = (UINTN) MicrocodePatchInRam;\r
+  CpuMpData->MicrocodePatchRegionSize = TotalLoadSize;\r
+\r
+  DEBUG ((\r
+    DEBUG_INFO,\r
+    "%a: Required microcode patches have been loaded at 0x%lx, with size 0x%lx.\n",\r
+    __FUNCTION__, CpuMpData->MicrocodePatchAddress, CpuMpData->MicrocodePatchRegionSize\r
+    ));\r
+\r
+  return;\r
+}\r
+\r
+/**\r
+  Load the required microcode patches data into memory.\r
+\r
+  @param[in, out]  CpuMpData    The pointer to CPU MP Data structure.\r
+**/\r
+VOID\r
+LoadMicrocodePatch (\r
+  IN OUT CPU_MP_DATA             *CpuMpData\r
+  )\r
+{\r
+  CPU_MICROCODE_HEADER                   *MicrocodeEntryPoint;\r
+  UINTN                                  MicrocodeEnd;\r
+  UINTN                                  DataSize;\r
+  UINTN                                  TotalSize;\r
+  CPU_MICROCODE_EXTENDED_TABLE_HEADER    *ExtendedTableHeader;\r
+  UINT32                                 ExtendedTableCount;\r
+  CPU_MICROCODE_EXTENDED_TABLE           *ExtendedTable;\r
+  MICROCODE_PATCH_INFO                   *PatchInfoBuffer;\r
+  UINTN                                  MaxPatchNumber;\r
+  UINTN                                  PatchCount;\r
+  UINTN                                  TotalLoadSize;\r
+  UINTN                                  Index;\r
+  BOOLEAN                                NeedLoad;\r
+\r
+  //\r
+  // Initialize the microcode patch related fields in CpuMpData as the values\r
+  // specified by the PCD pair. If the microcode patches are loaded into memory,\r
+  // these fields will be updated.\r
+  //\r
+  CpuMpData->MicrocodePatchAddress    = PcdGet64 (PcdCpuMicrocodePatchAddress);\r
+  CpuMpData->MicrocodePatchRegionSize = PcdGet64 (PcdCpuMicrocodePatchRegionSize);\r
+\r
+  MicrocodeEntryPoint    = (CPU_MICROCODE_HEADER *) (UINTN) CpuMpData->MicrocodePatchAddress;\r
+  MicrocodeEnd           = (UINTN) MicrocodeEntryPoint +\r
+                           (UINTN) CpuMpData->MicrocodePatchRegionSize;\r
+  if ((MicrocodeEntryPoint == NULL) || ((UINTN) MicrocodeEntryPoint == MicrocodeEnd)) {\r
+    //\r
+    // There is no microcode patches\r
+    //\r
+    return;\r
+  }\r
+\r
+  PatchCount      = 0;\r
+  MaxPatchNumber  = DEFAULT_MAX_MICROCODE_PATCH_NUM;\r
+  TotalLoadSize   = 0;\r
+  PatchInfoBuffer = AllocatePool (MaxPatchNumber * sizeof (MICROCODE_PATCH_INFO));\r
+  if (PatchInfoBuffer == NULL) {\r
+    return;\r
+  }\r
+\r
+  //\r
+  // Process the header of each microcode patch within the region.\r
+  // The purpose is to decide which microcode patch(es) will be loaded into memory.\r
+  //\r
+  do {\r
+    if (MicrocodeEntryPoint->HeaderVersion != 0x1) {\r
+      //\r
+      // Padding data between the microcode patches, skip 1KB to check next entry.\r
+      //\r
+      MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *) (((UINTN) MicrocodeEntryPoint) + SIZE_1KB);\r
+      continue;\r
+    }\r
+\r
+    DataSize  = MicrocodeEntryPoint->DataSize;\r
+    TotalSize = (DataSize == 0) ? 2048 : MicrocodeEntryPoint->TotalSize;\r
+    if ( (UINTN)MicrocodeEntryPoint > (MAX_ADDRESS - TotalSize) ||\r
+         ((UINTN)MicrocodeEntryPoint + TotalSize) > MicrocodeEnd ||\r
+         (DataSize & 0x3) != 0 ||\r
+         (TotalSize & (SIZE_1KB - 1)) != 0 ||\r
+         TotalSize < DataSize\r
+       ) {\r
+      //\r
+      // Not a valid microcode header, skip 1KB to check next entry.\r
+      //\r
+      MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *) (((UINTN) MicrocodeEntryPoint) + SIZE_1KB);\r
+      continue;\r
+    }\r
+\r
+    //\r
+    // Check the 'ProcessorSignature' and 'ProcessorFlags' of the microcode\r
+    // patch header with the CPUID and PlatformID of the processors within\r
+    // system to decide if it will be copied into memory\r
+    //\r
+    NeedLoad = IsMicrocodePatchNeedLoad (\r
+                 CpuMpData,\r
+                 MicrocodeEntryPoint->ProcessorSignature.Uint32,\r
+                 MicrocodeEntryPoint->ProcessorFlags\r
+                 );\r
+\r
+    //\r
+    // If the Extended Signature Table exists, check if the processor is in the\r
+    // support list\r
+    //\r
+    if ((!NeedLoad) && (DataSize != 0) &&\r
+        (TotalSize - DataSize > sizeof (CPU_MICROCODE_HEADER) +\r
+                                sizeof (CPU_MICROCODE_EXTENDED_TABLE_HEADER))) {\r
+      ExtendedTableHeader = (CPU_MICROCODE_EXTENDED_TABLE_HEADER *) ((UINT8 *) (MicrocodeEntryPoint)\r
+                              + DataSize + sizeof (CPU_MICROCODE_HEADER));\r
+      ExtendedTableCount  = ExtendedTableHeader->ExtendedSignatureCount;\r
+      ExtendedTable       = (CPU_MICROCODE_EXTENDED_TABLE *) (ExtendedTableHeader + 1);\r
+\r
+      for (Index = 0; Index < ExtendedTableCount; Index ++) {\r
+        //\r
+        // Avoid access content beyond MicrocodeEnd\r
+        //\r
+        if ((UINTN) ExtendedTable > MicrocodeEnd - sizeof (CPU_MICROCODE_EXTENDED_TABLE)) {\r
+          break;\r
+        }\r
+\r
+        //\r
+        // Check the 'ProcessorSignature' and 'ProcessorFlag' of the Extended\r
+        // Signature Table entry with the CPUID and PlatformID of the processors\r
+        // within system to decide if it will be copied into memory\r
+        //\r
+        NeedLoad = IsMicrocodePatchNeedLoad (\r
+                     CpuMpData,\r
+                     ExtendedTable->ProcessorSignature.Uint32,\r
+                     ExtendedTable->ProcessorFlag\r
+                     );\r
+        if (NeedLoad) {\r
+          break;\r
+        }\r
+        ExtendedTable ++;\r
+      }\r
+    }\r
+\r
+    if (NeedLoad) {\r
+      PatchCount++;\r
+      if (PatchCount > MaxPatchNumber) {\r
+        //\r
+        // Current 'PatchInfoBuffer' cannot hold the information, double the size\r
+        // and allocate a new buffer.\r
+        //\r
+        if (MaxPatchNumber > MAX_UINTN / 2 / sizeof (MICROCODE_PATCH_INFO)) {\r
+          //\r
+          // Overflow check for MaxPatchNumber\r
+          //\r
+          goto OnExit;\r
+        }\r
+\r
+        PatchInfoBuffer = ReallocatePool (\r
+                            MaxPatchNumber * sizeof (MICROCODE_PATCH_INFO),\r
+                            2 * MaxPatchNumber * sizeof (MICROCODE_PATCH_INFO),\r
+                            PatchInfoBuffer\r
+                            );\r
+        if (PatchInfoBuffer == NULL) {\r
+          goto OnExit;\r
+        }\r
+        MaxPatchNumber = MaxPatchNumber * 2;\r
+      }\r
+\r
+      //\r
+      // Store the information of this microcode patch\r
+      //\r
+      if (TotalSize > ALIGN_VALUE (TotalSize, SIZE_1KB) ||\r
+          ALIGN_VALUE (TotalSize, SIZE_1KB) > MAX_UINTN - TotalLoadSize) {\r
+        goto OnExit;\r
+      }\r
+      PatchInfoBuffer[PatchCount - 1].Address     = (UINTN) MicrocodeEntryPoint;\r
+      PatchInfoBuffer[PatchCount - 1].Size        = TotalSize;\r
+      PatchInfoBuffer[PatchCount - 1].AlignedSize = ALIGN_VALUE (TotalSize, SIZE_1KB);\r
+      TotalLoadSize += PatchInfoBuffer[PatchCount - 1].AlignedSize;\r
+    }\r
+\r
+    //\r
+    // Process the next microcode patch\r
+    //\r
+    MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *) (((UINTN) MicrocodeEntryPoint) + TotalSize);\r
+  } while (((UINTN) MicrocodeEntryPoint < MicrocodeEnd));\r
+\r
+  if (PatchCount != 0) {\r
+    DEBUG ((\r
+      DEBUG_INFO,\r
+      "%a: 0x%x microcode patches will be loaded into memory, with size 0x%x.\n",\r
+      __FUNCTION__, PatchCount, TotalLoadSize\r
+      ));\r
+\r
+    LoadMicrocodePatchWorker (CpuMpData, PatchInfoBuffer, PatchCount, TotalLoadSize);\r
+  }\r
+\r
+OnExit:\r
+  if (PatchInfoBuffer != NULL) {\r
+    FreePool (PatchInfoBuffer);\r
+  }\r
+  return;\r
+}\r
index d5077e080e495f0864682ae42e152396c9302a1e..c72bf3c9eece135f09227942e290f48ad5af172a 100644 (file)
@@ -628,10 +628,6 @@ ApWakeupFunction (
       ApTopOfStack  = CpuMpData->Buffer + (ProcessorNumber + 1) * CpuMpData->CpuApStackSize;\r
       BistData = *(UINT32 *) ((UINTN) ApTopOfStack - sizeof (UINTN));\r
       //\r
-      // Do some AP initialize sync\r
-      //\r
-      ApInitializeSync (CpuMpData);\r
-      //\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
@@ -1615,7 +1611,6 @@ MpInitLibInitialize (
   UINTN                    ApResetVectorSize;\r
   UINTN                    BackupBufferAddr;\r
   UINTN                    ApIdtBase;\r
-  VOID                     *MicrocodePatchInRam;\r
 \r
   OldCpuMpData = GetCpuMpDataFromGuidedHob ();\r
   if (OldCpuMpData == NULL) {\r
@@ -1683,39 +1678,7 @@ MpInitLibInitialize (
   CpuMpData->SwitchBspFlag    = FALSE;\r
   CpuMpData->CpuData          = (CPU_AP_DATA *) (CpuMpData + 1);\r
   CpuMpData->CpuInfoInHob     = (UINT64) (UINTN) (CpuMpData->CpuData + MaxLogicalProcessorNumber);\r
-  if (OldCpuMpData == NULL) {\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
-  }else {\r
+  if (OldCpuMpData != NULL) {\r
     CpuMpData->MicrocodePatchRegionSize = OldCpuMpData->MicrocodePatchRegionSize;\r
     CpuMpData->MicrocodePatchAddress    = OldCpuMpData->MicrocodePatchAddress;\r
   }\r
@@ -1762,14 +1725,6 @@ MpInitLibInitialize (
       (UINT32 *)(MonitorBuffer + MonitorFilterSize * Index);\r
   }\r
   //\r
-  // Load Microcode on BSP\r
-  //\r
-  MicrocodeDetect (CpuMpData, TRUE);\r
-  //\r
-  // Store BSP's MTRR setting\r
-  //\r
-  MtrrGetAllMtrrs (&CpuMpData->MtrrTable);\r
-  //\r
   // Enable the local APIC for Virtual Wire Mode.\r
   //\r
   ProgramVirtualWireMode ();\r
@@ -1781,6 +1736,11 @@ MpInitLibInitialize (
       //\r
       CollectProcessorCount (CpuMpData);\r
     }\r
+\r
+    //\r
+    // Load required microcode patches data into memory\r
+    //\r
+    LoadMicrocodePatch (CpuMpData);\r
   } else {\r
     //\r
     // APs have been wakeup before, just get the CPU Information\r
@@ -1788,7 +1748,6 @@ MpInitLibInitialize (
     //\r
     CpuMpData->CpuCount  = OldCpuMpData->CpuCount;\r
     CpuMpData->BspNumber = OldCpuMpData->BspNumber;\r
-    CpuMpData->InitFlag  = ApInitReconfig;\r
     CpuMpData->CpuInfoInHob = OldCpuMpData->CpuInfoInHob;\r
     CpuInfoInHob = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob;\r
     for (Index = 0; Index < CpuMpData->CpuCount; Index++) {\r
@@ -1797,21 +1756,28 @@ MpInitLibInitialize (
       CpuMpData->CpuData[Index].ApFunction = 0;\r
       CopyMem (&CpuMpData->CpuData[Index].VolatileRegisters, &VolatileRegisters, sizeof (CPU_VOLATILE_REGISTERS));\r
     }\r
-    if (MaxLogicalProcessorNumber > 1) {\r
-      //\r
-      // Wakeup APs to do some AP initialize sync\r
-      //\r
-      WakeUpAP (CpuMpData, TRUE, 0, ApInitializeSync, CpuMpData, TRUE);\r
-      //\r
-      // Wait for all APs finished initialization\r
-      //\r
-      while (CpuMpData->FinishedCount < (CpuMpData->CpuCount - 1)) {\r
-        CpuPause ();\r
-      }\r
-      CpuMpData->InitFlag = ApInitDone;\r
-      for (Index = 0; Index < CpuMpData->CpuCount; Index++) {\r
-        SetApState (&CpuMpData->CpuData[Index], CpuStateIdle);\r
-      }\r
+  }\r
+\r
+  //\r
+  // Detect and apply Microcode on BSP\r
+  //\r
+  MicrocodeDetect (CpuMpData, TRUE);\r
+  //\r
+  // Store BSP's MTRR setting\r
+  //\r
+  MtrrGetAllMtrrs (&CpuMpData->MtrrTable);\r
+\r
+  //\r
+  // Wakeup APs to do some AP initialize sync (Microcode & MTRR)\r
+  //\r
+  if (CpuMpData->CpuCount > 1) {\r
+    WakeUpAP (CpuMpData, TRUE, 0, ApInitializeSync, CpuMpData, TRUE);\r
+    while (CpuMpData->FinishedCount < (CpuMpData->CpuCount - 1)) {\r
+      CpuPause ();\r
+    }\r
+\r
+    for (Index = 0; Index < CpuMpData->CpuCount; Index++) {\r
+      SetApState (&CpuMpData->CpuData[Index], CpuStateIdle);\r
     }\r
   }\r
 \r
index 4440dc2701f9b872c96c8844de747def238b523b..56b0df664a7956d13e19e7ab4e154e88d26d9a22 100644 (file)
 #define CPU_SWITCH_STATE_STORED 1\r
 #define CPU_SWITCH_STATE_LOADED 2\r
 \r
+//\r
+// Default maximum number of entries to store the microcode patches information\r
+//\r
+#define DEFAULT_MAX_MICROCODE_PATCH_NUM 8\r
+\r
+//\r
+// Data structure for microcode patch information\r
+//\r
+typedef struct {\r
+  UINTN    Address;\r
+  UINTN    Size;\r
+  UINTN    AlignedSize;\r
+} MICROCODE_PATCH_INFO;\r
+\r
 //\r
 // CPU exchange information for switch BSP\r
 //\r
@@ -575,6 +589,16 @@ MicrocodeDetect (
   IN BOOLEAN                 IsBspCallIn\r
   );\r
 \r
+/**\r
+  Load the required microcode patches data into memory.\r
+\r
+  @param[in, out]  CpuMpData    The pointer to CPU MP Data structure.\r
+**/\r
+VOID\r
+LoadMicrocodePatch (\r
+  IN OUT CPU_MP_DATA             *CpuMpData\r
+  );\r
+\r
 /**\r
   Detect whether Mwait-monitor feature is supported.\r
 \r