]> git.proxmox.com Git - mirror_edk2.git/blobdiff - UefiCpuPkg/Library/MpInitLib/Microcode.c
UefiCpuPkg/MpInitLib: Reduce the size when loading microcode patches
[mirror_edk2.git] / UefiCpuPkg / Library / MpInitLib / Microcode.c
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