]> git.proxmox.com Git - mirror_edk2.git/blobdiff - UefiCpuPkg/Feature/Capsule/MicrocodeUpdateDxe/MicrocodeUpdate.c
UefiCpuPkg/MicrocodeUpdate: enhance flash write logic
[mirror_edk2.git] / UefiCpuPkg / Feature / Capsule / MicrocodeUpdateDxe / MicrocodeUpdate.c
index 2eb4ae4fbaab816399cabd1e699329dac478bd1f..46739ba57a2ca5de333cb3bb6e462c2e47703c9a 100644 (file)
 \r
 #include "MicrocodeUpdate.h"\r
 \r
+\r
+/**\r
+  Verify Microcode.\r
+\r
+  Caution: This function may receive untrusted input.\r
+\r
+  @param[in]  Image              The Microcode image buffer.\r
+  @param[in]  ImageSize          The size of Microcode image buffer in bytes.\r
+  @param[in]  TryLoad            Try to load Microcode or not.\r
+  @param[out] LastAttemptStatus  The last attempt status, which will be recorded in ESRT and FMP EFI_FIRMWARE_IMAGE_DESCRIPTOR.\r
+  @param[out] AbortReason        A pointer to a pointer to a null-terminated string providing more\r
+                                 details for the aborted operation. The buffer is allocated by this function\r
+                                 with AllocatePool(), and it is the caller's responsibility to free it with a\r
+                                 call to FreePool().\r
+\r
+  @retval EFI_SUCCESS               The Microcode image passes verification.\r
+  @retval EFI_VOLUME_CORRUPTED      The Microcode image is corrupt.\r
+  @retval EFI_INCOMPATIBLE_VERSION  The Microcode image version is incorrect.\r
+  @retval EFI_UNSUPPORTED           The Microcode ProcessorSignature or ProcessorFlags is incorrect.\r
+  @retval EFI_SECURITY_VIOLATION    The Microcode image fails to load.\r
+**/\r
+EFI_STATUS\r
+VerifyMicrocode (\r
+  IN VOID    *Image,\r
+  IN UINTN   ImageSize,\r
+  IN BOOLEAN TryLoad,\r
+  OUT UINT32 *LastAttemptStatus,\r
+  OUT CHAR16 **AbortReason\r
+  );\r
+\r
 /**\r
   Get Microcode Region.\r
 \r
 **/\r
 BOOLEAN\r
 GetMicrocodeRegion (\r
-  OUT UINT64   *MicrocodePatchAddress,\r
-  OUT UINT64   *MicrocodePatchRegionSize\r
+  OUT VOID     **MicrocodePatchAddress,\r
+  OUT UINT   *MicrocodePatchRegionSize\r
   )\r
 {\r
-  *MicrocodePatchAddress = PcdGet64(PcdCpuMicrocodePatchAddress);\r
-  *MicrocodePatchRegionSize = PcdGet64(PcdCpuMicrocodePatchRegionSize);\r
+  *MicrocodePatchAddress = (VOID *)(UINTN)PcdGet64(PcdCpuMicrocodePatchAddress);\r
+  *MicrocodePatchRegionSize = (UINTN)PcdGet64(PcdCpuMicrocodePatchRegionSize);\r
 \r
-  if ((*MicrocodePatchAddress == 0) || (*MicrocodePatchRegionSize == 0)) {\r
+  if ((*MicrocodePatchAddress == NULL) || (*MicrocodePatchRegionSize == 0)) {\r
     return FALSE;\r
   }\r
 \r
@@ -113,41 +143,80 @@ LoadMicrocode (
   return GetCurrentMicrocodeSignature();\r
 }\r
 \r
+/**\r
+  If the Microcode is used by current processor.\r
+\r
+  @param[in]  MicrocodeEntryPoint  The Microcode buffer\r
+\r
+  @retval TRUE  The Microcode is used by current processor.\r
+  @retval FALSE The Microcode is NOT used by current processor.\r
+**/\r
+BOOLEAN\r
+IsMicrocodeInUse (\r
+  IN CPU_MICROCODE_HEADER                    *MicrocodeEntryPoint\r
+  )\r
+{\r
+  UINT32      AttemptStatus;\r
+  UINTN       TotalSize;\r
+  EFI_STATUS  Status;\r
+\r
+  if (MicrocodeEntryPoint->HeaderVersion == 0x1 && MicrocodeEntryPoint->LoaderRevision == 0x1) {\r
+    //\r
+    // It is the microcode header. It is not the padding data between microcode patches\r
+    // becasue the padding data should not include 0x00000001 and it should be the repeated\r
+    // byte format (like 0xXYXYXYXY....).\r
+    //\r
+    if (MicrocodeEntryPoint->DataSize == 0) {\r
+      TotalSize = 2048;\r
+    } else {\r
+      TotalSize = MicrocodeEntryPoint->TotalSize;\r
+    }\r
+    Status = VerifyMicrocode(MicrocodeEntryPoint, TotalSize, FALSE, &AttemptStatus, NULL);\r
+    if (!EFI_ERROR(Status)) {\r
+      return TRUE;\r
+    }\r
+  }\r
+  return FALSE;\r
+}\r
+\r
 /**\r
   Get current Microcode information.\r
 \r
-  @param[out]  ImageDescriptor  Microcode ImageDescriptor\r
-  @param[in]   DescriptorCount  The count of Microcode ImageDescriptor allocated.\r
+  NOTE: The DescriptorCount/ImageDescriptor/MicrocodeInfo in MicrocodeFmpPrivate\r
+  are not avaiable in this function.\r
+\r
+  @param[in]   MicrocodeFmpPrivate        The Microcode driver private data\r
+  @param[in]   DescriptorCount            The count of Microcode ImageDescriptor allocated.\r
+  @param[out]  ImageDescriptor            Microcode ImageDescriptor\r
+  @param[out]  MicrocodeInfo              Microcode information\r
 \r
   @return Microcode count\r
 **/\r
 UINTN\r
 GetMicrocodeInfo (\r
+  IN  MICROCODE_FMP_PRIVATE_DATA     *MicrocodeFmpPrivate,\r
+  IN  UINTN                          DescriptorCount,  OPTIONAL\r
   OUT EFI_FIRMWARE_IMAGE_DESCRIPTOR  *ImageDescriptor, OPTIONAL\r
-  IN  UINTN                          DescriptorCount   OPTIONAL\r
+  OUT MICROCODE_INFO                 *MicrocodeInfo    OPTIONAL\r
   )\r
 {\r
-  BOOLEAN                                 Result;\r
-  UINT64                                  MicrocodePatchAddress;\r
-  UINT64                                  MicrocodePatchRegionSize;\r
+  VOID                                    *MicrocodePatchAddress;\r
+  UINTN                                   MicrocodePatchRegionSize;\r
   CPU_MICROCODE_HEADER                    *MicrocodeEntryPoint;\r
   UINTN                                   MicrocodeEnd;\r
   UINTN                                   TotalSize;\r
   UINTN                                   Count;\r
   UINT64                                  ImageAttributes;\r
-  UINT32                                  CurrentRevision;\r
+  BOOLEAN                                 IsInUse;\r
 \r
-  Result = GetMicrocodeRegion(&MicrocodePatchAddress, &MicrocodePatchRegionSize);\r
-  if (!Result) {\r
-    DEBUG((DEBUG_ERROR, "Fail to get Microcode Region\n"));\r
-    return 0;\r
-  }\r
-  DEBUG((DEBUG_INFO, "Microcode Region - 0x%lx - 0x%lx\n", MicrocodePatchAddress, MicrocodePatchRegionSize));\r
+  MicrocodePatchAddress = MicrocodeFmpPrivate->MicrocodePatchAddress;\r
+  MicrocodePatchRegionSize = MicrocodeFmpPrivate->MicrocodePatchRegionSize;\r
+\r
+  DEBUG((DEBUG_INFO, "Microcode Region - 0x%x - 0x%x\n", MicrocodePatchAddress, MicrocodePatchRegionSize));\r
 \r
   Count = 0;\r
-  CurrentRevision = GetCurrentMicrocodeSignature();\r
 \r
-  MicrocodeEnd = (UINTN)(MicrocodePatchAddress + MicrocodePatchRegionSize);\r
+  MicrocodeEnd = (UINTN)MicrocodePatchAddress + MicrocodePatchRegionSize;\r
   MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *) (UINTN) MicrocodePatchAddress;\r
   do {\r
     if (MicrocodeEntryPoint->HeaderVersion == 0x1 && MicrocodeEntryPoint->LoaderRevision == 0x1) {\r
@@ -162,6 +231,8 @@ GetMicrocodeInfo (
         TotalSize = MicrocodeEntryPoint->TotalSize;\r
       }\r
 \r
+      IsInUse = IsMicrocodeInUse (MicrocodeEntryPoint);\r
+\r
       if (ImageDescriptor != NULL && DescriptorCount > Count) {\r
         ImageDescriptor[Count].ImageIndex = (UINT8)(Count + 1);\r
         CopyGuid (&ImageDescriptor[Count].ImageTypeId, &gMicrocodeFmpImageTypeIdGuid);\r
@@ -171,7 +242,7 @@ GetMicrocodeInfo (
         ImageDescriptor[Count].VersionName = NULL;\r
         ImageDescriptor[Count].Size = TotalSize;\r
         ImageAttributes = IMAGE_ATTRIBUTE_IMAGE_UPDATABLE | IMAGE_ATTRIBUTE_RESET_REQUIRED;\r
-        if (CurrentRevision == MicrocodeEntryPoint->UpdateRevision) {\r
+        if (IsInUse) {\r
           ImageAttributes |= IMAGE_ATTRIBUTE_IN_USE;\r
         }\r
         ImageDescriptor[Count].AttributesSupported = ImageAttributes | IMAGE_ATTRIBUTE_IN_USE;\r
@@ -182,6 +253,11 @@ GetMicrocodeInfo (
         ImageDescriptor[Count].LastAttemptStatus = 0;\r
         ImageDescriptor[Count].HardwareInstance = 0;\r
       }\r
+      if (MicrocodeInfo != NULL && DescriptorCount > Count) {\r
+        MicrocodeInfo[Count].MicrocodeEntryPoint = MicrocodeEntryPoint;\r
+        MicrocodeInfo[Count].TotalSize = TotalSize;\r
+        MicrocodeInfo[Count].InUse = IsInUse;\r
+      }\r
     } else {\r
       //\r
       // It is the padding data between the microcode patches for microcode patches alignment.\r
@@ -206,89 +282,6 @@ GetMicrocodeInfo (
   return Count;\r
 }\r
 \r
-/**\r
-  Read Microcode.\r
-\r
-  @param[in]       ImageIndex The index of Microcode image.\r
-  @param[in, out]  Image      The Microcode image buffer.\r
-  @param[in, out]  ImageSize  The size of Microcode image buffer in bytes.\r
-\r
-  @retval EFI_SUCCESS    The Microcode image is read.\r
-  @retval EFI_NOT_FOUND  The Microcode image is not found.\r
-**/\r
-EFI_STATUS\r
-MicrocodeRead (\r
-  IN UINTN      ImageIndex,\r
-  IN OUT VOID   *Image,\r
-  IN OUT UINTN  *ImageSize\r
-  )\r
-{\r
-  BOOLEAN                                 Result;\r
-  UINT64                                  MicrocodePatchAddress;\r
-  UINT64                                  MicrocodePatchRegionSize;\r
-  CPU_MICROCODE_HEADER                    *MicrocodeEntryPoint;\r
-  UINTN                                   MicrocodeEnd;\r
-  UINTN                                   TotalSize;\r
-  UINTN                                   Count;\r
-\r
-  Result = GetMicrocodeRegion(&MicrocodePatchAddress, &MicrocodePatchRegionSize);\r
-  if (!Result) {\r
-    DEBUG((DEBUG_ERROR, "Fail to get Microcode Region\n"));\r
-    return EFI_NOT_FOUND;\r
-  }\r
-  DEBUG((DEBUG_INFO, "Microcode Region - 0x%lx - 0x%lx\n", MicrocodePatchAddress, MicrocodePatchRegionSize));\r
-\r
-  Count = 0;\r
-\r
-  MicrocodeEnd = (UINTN)(MicrocodePatchAddress + MicrocodePatchRegionSize);\r
-  MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *)(UINTN)MicrocodePatchAddress;\r
-  do {\r
-    if (MicrocodeEntryPoint->HeaderVersion == 0x1 && MicrocodeEntryPoint->LoaderRevision == 0x1) {\r
-      //\r
-      // It is the microcode header. It is not the padding data between microcode patches\r
-      // becasue the padding data should not include 0x00000001 and it should be the repeated\r
-      // byte format (like 0xXYXYXYXY....).\r
-      //\r
-      if (MicrocodeEntryPoint->DataSize == 0) {\r
-        TotalSize = 2048;\r
-      } else {\r
-        TotalSize = MicrocodeEntryPoint->TotalSize;\r
-      }\r
-\r
-      if (ImageIndex == Count + 1) {\r
-        if (*ImageSize < TotalSize) {\r
-          *ImageSize = TotalSize;\r
-          return EFI_BUFFER_TOO_SMALL;\r
-        }\r
-        *ImageSize = TotalSize;\r
-        CopyMem (Image, MicrocodeEntryPoint, TotalSize);\r
-        return EFI_SUCCESS;\r
-      }\r
-\r
-    } else {\r
-      //\r
-      // It is the padding data between the microcode patches for microcode patches alignment.\r
-      // Because the microcode patch is the multiple of 1-KByte, the padding data should not\r
-      // exist if the microcode patch alignment value is not larger than 1-KByte. So, the microcode\r
-      // alignment value should be larger than 1-KByte. We could skip SIZE_1KB padding data to\r
-      // find the next possible microcode patch header.\r
-      //\r
-      MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *)(((UINTN)MicrocodeEntryPoint) + SIZE_1KB);\r
-      continue;\r
-    }\r
-\r
-    Count++;\r
-    ASSERT(Count < 0xFF);\r
-\r
-    //\r
-    // Get the next patch.\r
-    //\r
-    MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *)(((UINTN)MicrocodeEntryPoint) + TotalSize);\r
-  } while (((UINTN)MicrocodeEntryPoint < MicrocodeEnd));\r
-\r
-  return EFI_NOT_FOUND;\r
-}\r
-\r
 /**\r
   Verify Microcode.\r
 \r
@@ -461,7 +454,9 @@ VerifyMicrocode (
       }\r
     }\r
     if (!CorrectMicrocode) {\r
-      DEBUG((DEBUG_ERROR, "VerifyMicrocode - fail on CurrentProcessorSignature/ProcessorFlags\n"));\r
+      if (TryLoad) {\r
+        DEBUG((DEBUG_ERROR, "VerifyMicrocode - fail on CurrentProcessorSignature/ProcessorFlags\n"));\r
+      }\r
       *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INCORRECT_VERSION;\r
       if (AbortReason != NULL) {\r
         if (MicrocodeEntryPoint->ProcessorSignature.Uint32 != CurrentProcessorSignature) {\r
@@ -479,7 +474,9 @@ VerifyMicrocode (
   //\r
   CurrentRevision = GetCurrentMicrocodeSignature();\r
   if (MicrocodeEntryPoint->UpdateRevision < CurrentRevision) {\r
-    DEBUG((DEBUG_ERROR, "VerifyMicrocode - fail on UpdateRevision\n"));\r
+    if (TryLoad) {\r
+      DEBUG((DEBUG_ERROR, "VerifyMicrocode - fail on UpdateRevision\n"));\r
+    }\r
     *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INCORRECT_VERSION;\r
     if (AbortReason != NULL) {\r
       *AbortReason = AllocateCopyPool(sizeof(L"IncorrectRevision"), L"IncorrectRevision");\r
@@ -508,142 +505,79 @@ VerifyMicrocode (
 /**\r
   Get current Microcode in used.\r
 \r
+  @param[in]  MicrocodeFmpPrivate        The Microcode driver private data\r
+\r
   @return current Microcode in used.\r
 **/\r
 VOID *\r
 GetCurrentMicrocodeInUse (\r
-  VOID\r
+  IN MICROCODE_FMP_PRIVATE_DATA              *MicrocodeFmpPrivate\r
   )\r
 {\r
-  BOOLEAN                                 Result;\r
-  EFI_STATUS                              Status;\r
-  UINT64                                  MicrocodePatchAddress;\r
-  UINT64                                  MicrocodePatchRegionSize;\r
-  CPU_MICROCODE_HEADER                    *MicrocodeEntryPoint;\r
-  UINTN                                   MicrocodeEnd;\r
-  UINTN                                   TotalSize;\r
-  UINTN                                   Count;\r
-  UINT32                                  AttemptStatus;\r
+  UINTN                                   Index;\r
 \r
-  Result = GetMicrocodeRegion(&MicrocodePatchAddress, &MicrocodePatchRegionSize);\r
-  if (!Result) {\r
-    DEBUG((DEBUG_ERROR, "Fail to get Microcode Region\n"));\r
-    return NULL;\r
+  for (Index = 0; Index < MicrocodeFmpPrivate->DescriptorCount; Index++) {\r
+    if (!MicrocodeFmpPrivate->MicrocodeInfo[Index].InUse) {\r
+      continue;\r
+    }\r
+    if (IsMicrocodeInUse (MicrocodeFmpPrivate->MicrocodeInfo[Index].MicrocodeEntryPoint)) {\r
+      return MicrocodeFmpPrivate->MicrocodeInfo[Index].MicrocodeEntryPoint;\r
+    }\r
   }\r
-  DEBUG((DEBUG_INFO, "Microcode Region - 0x%lx - 0x%lx\n", MicrocodePatchAddress, MicrocodePatchRegionSize));\r
+  return NULL;\r
+}\r
 \r
-  Count = 0;\r
+/**\r
+  Get next Microcode entrypoint.\r
 \r
-  MicrocodeEnd = (UINTN)(MicrocodePatchAddress + MicrocodePatchRegionSize);\r
-  MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *)(UINTN)MicrocodePatchAddress;\r
-  do {\r
-    if (MicrocodeEntryPoint->HeaderVersion == 0x1 && MicrocodeEntryPoint->LoaderRevision == 0x1) {\r
-      //\r
-      // It is the microcode header. It is not the padding data between microcode patches\r
-      // becasue the padding data should not include 0x00000001 and it should be the repeated\r
-      // byte format (like 0xXYXYXYXY....).\r
-      //\r
-      if (MicrocodeEntryPoint->DataSize == 0) {\r
-        TotalSize = 2048;\r
+  @param[in]  MicrocodeFmpPrivate        The Microcode driver private data\r
+  @param[in]  MicrocodeEntryPoint        Current Microcode entrypoint\r
+\r
+  @return next Microcode entrypoint.\r
+**/\r
+CPU_MICROCODE_HEADER *\r
+GetNextMicrocode (\r
+  IN MICROCODE_FMP_PRIVATE_DATA              *MicrocodeFmpPrivate,\r
+  IN CPU_MICROCODE_HEADER                    *MicrocodeEntryPoint\r
+  )\r
+{\r
+  UINTN                                   Index;\r
+\r
+  for (Index = 0; Index < MicrocodeFmpPrivate->DescriptorCount; Index++) {\r
+    if (MicrocodeEntryPoint == MicrocodeFmpPrivate->MicrocodeInfo[Index].MicrocodeEntryPoint) {\r
+      if (Index == (UINTN)MicrocodeFmpPrivate->DescriptorCount - 1) {\r
+        // it is last one\r
+        return NULL;\r
       } else {\r
-        TotalSize = MicrocodeEntryPoint->TotalSize;\r
+        // return next one\r
+        return MicrocodeFmpPrivate->MicrocodeInfo[Index + 1].MicrocodeEntryPoint;\r
       }\r
-      Status = VerifyMicrocode(MicrocodeEntryPoint, TotalSize, FALSE, &AttemptStatus, NULL);\r
-      if (!EFI_ERROR(Status)) {\r
-        return MicrocodeEntryPoint;\r
-      }\r
-\r
-    } else {\r
-      //\r
-      // It is the padding data between the microcode patches for microcode patches alignment.\r
-      // Because the microcode patch is the multiple of 1-KByte, the padding data should not\r
-      // exist if the microcode patch alignment value is not larger than 1-KByte. So, the microcode\r
-      // alignment value should be larger than 1-KByte. We could skip SIZE_1KB padding data to\r
-      // find the next possible microcode patch header.\r
-      //\r
-      MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *)(((UINTN)MicrocodeEntryPoint) + SIZE_1KB);\r
-      continue;\r
     }\r
+  }\r
 \r
-    Count++;\r
-    ASSERT(Count < 0xFF);\r
-\r
-    //\r
-    // Get the next patch.\r
-    //\r
-    MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *)(((UINTN)MicrocodeEntryPoint) + TotalSize);\r
-  } while (((UINTN)MicrocodeEntryPoint < MicrocodeEnd));\r
-\r
+  ASSERT(FALSE);\r
   return NULL;\r
 }\r
 \r
 /**\r
   Get current Microcode used region size.\r
 \r
+  @param[in]  MicrocodeFmpPrivate        The Microcode driver private data\r
+\r
   @return current Microcode used region size.\r
 **/\r
 UINTN\r
 GetCurrentMicrocodeUsedRegionSize (\r
-  VOID\r
+  IN MICROCODE_FMP_PRIVATE_DATA              *MicrocodeFmpPrivate\r
   )\r
 {\r
-  BOOLEAN                                 Result;\r
-  UINT64                                  MicrocodePatchAddress;\r
-  UINT64                                  MicrocodePatchRegionSize;\r
-  CPU_MICROCODE_HEADER                    *MicrocodeEntryPoint;\r
-  UINTN                                   MicrocodeEnd;\r
-  UINTN                                   TotalSize;\r
-  UINTN                                   Count;\r
-  UINTN                                   MicrocodeUsedEnd;\r
-\r
-  Result = GetMicrocodeRegion(&MicrocodePatchAddress, &MicrocodePatchRegionSize);\r
-  if (!Result) {\r
-    DEBUG((DEBUG_ERROR, "Fail to get Microcode Region\n"));\r
+  if (MicrocodeFmpPrivate->DescriptorCount == 0) {\r
     return 0;\r
   }\r
-  DEBUG((DEBUG_INFO, "Microcode Region - 0x%lx - 0x%lx\n", MicrocodePatchAddress, MicrocodePatchRegionSize));\r
-\r
-  MicrocodeUsedEnd = (UINTN)MicrocodePatchAddress;\r
-  Count = 0;\r
-\r
-  MicrocodeEnd = (UINTN)(MicrocodePatchAddress + MicrocodePatchRegionSize);\r
-  MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *)(UINTN)MicrocodePatchAddress;\r
-  do {\r
-    if (MicrocodeEntryPoint->HeaderVersion == 0x1 && MicrocodeEntryPoint->LoaderRevision == 0x1) {\r
-      //\r
-      // It is the microcode header. It is not the padding data between microcode patches\r
-      // becasue the padding data should not include 0x00000001 and it should be the repeated\r
-      // byte format (like 0xXYXYXYXY....).\r
-      //\r
-      if (MicrocodeEntryPoint->DataSize == 0) {\r
-        TotalSize = 2048;\r
-      } else {\r
-        TotalSize = MicrocodeEntryPoint->TotalSize;\r
-      }\r
-\r
-    } else {\r
-      //\r
-      // It is the padding data between the microcode patches for microcode patches alignment.\r
-      // Because the microcode patch is the multiple of 1-KByte, the padding data should not\r
-      // exist if the microcode patch alignment value is not larger than 1-KByte. So, the microcode\r
-      // alignment value should be larger than 1-KByte. We could skip SIZE_1KB padding data to\r
-      // find the next possible microcode patch header.\r
-      //\r
-      MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *)(((UINTN)MicrocodeEntryPoint) + SIZE_1KB);\r
-      continue;\r
-    }\r
-\r
-    Count++;\r
-    ASSERT(Count < 0xFF);\r
-    MicrocodeUsedEnd = (UINTN)MicrocodeEntryPoint;\r
 \r
-    //\r
-    // Get the next patch.\r
-    //\r
-    MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *)(((UINTN)MicrocodeEntryPoint) + TotalSize);\r
-  } while (((UINTN)MicrocodeEntryPoint < MicrocodeEnd));\r
-\r
-  return MicrocodeUsedEnd - (UINTN)MicrocodePatchAddress;\r
+  return (UINTN)MicrocodeFmpPrivate->MicrocodeInfo[MicrocodeFmpPrivate->DescriptorCount - 1].MicrocodeEntryPoint\r
+         + (UINTN)MicrocodeFmpPrivate->MicrocodeInfo[MicrocodeFmpPrivate->DescriptorCount - 1].TotalSize\r
+         - (UINTN)MicrocodeFmpPrivate->MicrocodePatchAddress;\r
 }\r
 \r
 /**\r
@@ -685,61 +619,282 @@ UpdateMicrocode (
 }\r
 \r
 /**\r
-  Write Microcode.\r
+  Update Microcode flash region.\r
 \r
-  Caution: This function may receive untrusted input.\r
-\r
-  @param[in]   ImageIndex         The index of Microcode image.\r
-  @param[in]   Image              The Microcode image buffer.\r
-  @param[in]   ImageSize          The size of Microcode image buffer in bytes.\r
-  @param[out]  LastAttemptVersion The last attempt version, which will be recorded in ESRT and FMP EFI_FIRMWARE_IMAGE_DESCRIPTOR.\r
-  @param[out]  LastAttemptStatus  The last attempt status, which will be recorded in ESRT and FMP EFI_FIRMWARE_IMAGE_DESCRIPTOR.\r
-  @param[out]  AbortReason        A pointer to a pointer to a null-terminated string providing more\r
-                             details for the aborted operation. The buffer is allocated by this function\r
-                             with AllocatePool(), and it is the caller's responsibility to free it with a\r
-                             call to FreePool().\r
+  @param[in]  MicrocodeFmpPrivate        The Microcode driver private data\r
+  @param[in]  CurrentMicrocodeEntryPoint Current Microcode entrypoint\r
+  @param[in]  Image                      The Microcode image buffer.\r
+  @param[in]  ImageSize                  The size of Microcode image buffer in bytes.\r
+  @param[out] LastAttemptStatus          The last attempt status, which will be recorded in ESRT and FMP EFI_FIRMWARE_IMAGE_DESCRIPTOR.\r
 \r
-  @retval EFI_SUCCESS               The Microcode image is written.\r
-  @retval EFI_VOLUME_CORRUPTED      The Microcode image is corrupt.\r
-  @retval EFI_INCOMPATIBLE_VERSION  The Microcode image version is incorrect.\r
-  @retval EFI_SECURITY_VIOLATION    The Microcode image fails to load.\r
-  @retval EFI_WRITE_PROTECTED   The flash device is read only.\r
+  @retval EFI_SUCCESS             The Microcode image is written.\r
+  @retval EFI_WRITE_PROTECTED     The flash device is read only.\r
 **/\r
 EFI_STATUS\r
-MicrocodeWrite (\r
-  IN  UINTN   ImageIndex,\r
-  IN  VOID    *Image,\r
-  IN  UINTN   ImageSize,\r
-  OUT UINT32  *LastAttemptVersion,\r
-  OUT UINT32  *LastAttemptStatus,\r
-  OUT CHAR16  **AbortReason\r
+UpdateMicrocodeFlashRegion (\r
+  IN  MICROCODE_FMP_PRIVATE_DATA              *MicrocodeFmpPrivate,\r
+  IN  CPU_MICROCODE_HEADER                    *CurrentMicrocodeEntryPoint,\r
+  IN  VOID                                    *Image,\r
+  IN  UINTN                                   ImageSize,\r
+  OUT UINT32                                  *LastAttemptStatus\r
   )\r
 {\r
-  BOOLEAN                                 Result;\r
-  EFI_STATUS                              Status;\r
-  UINT64                                  MicrocodePatchAddress;\r
-  UINT64                                  MicrocodePatchRegionSize;\r
-  CPU_MICROCODE_HEADER                    *CurrentMicrocodeEntryPoint;\r
+  VOID                                    *MicrocodePatchAddress;\r
+  UINTN                                   MicrocodePatchRegionSize;\r
   UINTN                                   CurrentTotalSize;\r
   UINTN                                   UsedRegionSize;\r
-  VOID                                    *AlignedImage;\r
+  EFI_STATUS                              Status;\r
+  VOID                                    *MicrocodePatchScratchBuffer;\r
+  UINT8                                   *ScratchBufferPtr;\r
+  UINTN                                   ScratchBufferSize;\r
+  UINTN                                   RestSize;\r
+  UINTN                                   AvailableSize;\r
+  VOID                                    *NextMicrocodeEntryPoint;\r
+  MICROCODE_INFO                          *MicrocodeInfo;\r
+  UINTN                                   MicrocodeCount;\r
+  UINTN                                   Index;\r
+\r
+  DEBUG((DEBUG_INFO, "UpdateMicrocodeFlashRegion: Image - 0x%x, size - 0x%x\n", Image, ImageSize));\r
 \r
-  Result = GetMicrocodeRegion(&MicrocodePatchAddress, &MicrocodePatchRegionSize);\r
-  if (!Result) {\r
-    DEBUG((DEBUG_ERROR, "Fail to get Microcode Region\n"));\r
-    return EFI_NOT_FOUND;\r
+  MicrocodePatchAddress = MicrocodeFmpPrivate->MicrocodePatchAddress;\r
+  MicrocodePatchRegionSize = MicrocodeFmpPrivate->MicrocodePatchRegionSize;\r
+\r
+  MicrocodePatchScratchBuffer = AllocateZeroPool (MicrocodePatchRegionSize);\r
+  if (MicrocodePatchScratchBuffer == NULL) {\r
+    DEBUG((DEBUG_ERROR, "Fail to allocate Microcode Scratch buffer\n"));\r
+    *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INSUFFICIENT_RESOURCES;\r
+    return EFI_OUT_OF_RESOURCES;\r
   }\r
+  ScratchBufferPtr = MicrocodePatchScratchBuffer;\r
+  ScratchBufferSize = 0;\r
 \r
+  //\r
+  // Current data collection\r
+  //\r
   CurrentTotalSize = 0;\r
-  CurrentMicrocodeEntryPoint = GetCurrentMicrocodeInUse();\r
+  AvailableSize = 0;\r
+  NextMicrocodeEntryPoint = NULL;\r
   if (CurrentMicrocodeEntryPoint != NULL) {\r
     if (CurrentMicrocodeEntryPoint->DataSize == 0) {\r
       CurrentTotalSize = 2048;\r
     } else {\r
       CurrentTotalSize = CurrentMicrocodeEntryPoint->TotalSize;\r
     }\r
+    DEBUG((DEBUG_INFO, "  CurrentTotalSize - 0x%x\n", CurrentTotalSize));\r
+    NextMicrocodeEntryPoint = GetNextMicrocode(MicrocodeFmpPrivate, CurrentMicrocodeEntryPoint);\r
+    DEBUG((DEBUG_INFO, "  NextMicrocodeEntryPoint - 0x%x\n", NextMicrocodeEntryPoint));\r
+    if (NextMicrocodeEntryPoint != NULL) {\r
+      ASSERT ((UINTN)NextMicrocodeEntryPoint >= ((UINTN)CurrentMicrocodeEntryPoint + CurrentTotalSize));\r
+      AvailableSize = (UINTN)NextMicrocodeEntryPoint - (UINTN)CurrentMicrocodeEntryPoint;\r
+    } else {\r
+      AvailableSize = (UINTN)MicrocodePatchAddress + MicrocodePatchRegionSize - (UINTN)CurrentMicrocodeEntryPoint;\r
+    }\r
+    DEBUG((DEBUG_INFO, "  AvailableSize - 0x%x\n", AvailableSize));\r
+  }\r
+  ASSERT (AvailableSize >= CurrentTotalSize);\r
+  UsedRegionSize = GetCurrentMicrocodeUsedRegionSize(MicrocodeFmpPrivate);\r
+  DEBUG((DEBUG_INFO, "  UsedRegionSize - 0x%x\n", UsedRegionSize));\r
+  ASSERT (UsedRegionSize >= CurrentTotalSize);\r
+  if (CurrentMicrocodeEntryPoint != NULL) {\r
+    ASSERT ((UINTN)MicrocodePatchAddress + UsedRegionSize >= ((UINTN)CurrentMicrocodeEntryPoint + CurrentTotalSize));\r
+  }\r
+  //\r
+  // Total Size means the Microcode data size.\r
+  // Available Size means the Microcode data size plus the pad till next (1) Microcode or (2) the end.\r
+  //\r
+  // (1)\r
+  // +------+-----------+-----+------+===================+\r
+  // | MCU1 | Microcode | PAD | MCU2 |      Empty        |\r
+  // +------+-----------+-----+------+===================+\r
+  //        | TotalSize |\r
+  //        |<-AvailableSize->|\r
+  // |<-        UsedRegionSize     ->|\r
+  //\r
+  // (2)\r
+  // +------+-----------+===================+\r
+  // | MCU  | Microcode |      Empty        |\r
+  // +------+-----------+===================+\r
+  //        | TotalSize |\r
+  //        |<-      AvailableSize        ->|\r
+  // |<-UsedRegionSize->|\r
+  //\r
+\r
+  //\r
+  // Update based on policy\r
+  //\r
+\r
+  //\r
+  // 1. If there is enough space to update old one in situ, replace old microcode in situ.\r
+  //\r
+  if (AvailableSize >= ImageSize) {\r
+    DEBUG((DEBUG_INFO, "Replace old microcode in situ\n"));\r
+    //\r
+    // +------+------------+------+===================+\r
+    // |Other1| Old Image  |Other2|      Empty        |\r
+    // +------+------------+------+===================+\r
+    //\r
+    // +------+---------+--+------+===================+\r
+    // |Other1|New Image|FF|Other2|      Empty        |\r
+    // +------+---------+--+------+===================+\r
+    //\r
+    // 1.1. Copy new image\r
+    CopyMem (ScratchBufferPtr, Image, ImageSize);\r
+    ScratchBufferSize += ImageSize;\r
+    ScratchBufferPtr = (UINT8 *)ScratchBufferPtr + ScratchBufferSize;\r
+    // 1.2. Pad 0xFF\r
+    RestSize = AvailableSize - ImageSize;\r
+    if (RestSize > 0) {\r
+      SetMem (ScratchBufferPtr, RestSize, 0xFF);\r
+      ScratchBufferSize += RestSize;\r
+      ScratchBufferPtr = (UINT8 *)ScratchBufferPtr + ScratchBufferSize;\r
+    }\r
+    Status = UpdateMicrocode((UINTN)CurrentMicrocodeEntryPoint, MicrocodePatchScratchBuffer, ScratchBufferSize, LastAttemptStatus);\r
+    return Status;\r
   }\r
 \r
+  //\r
+  // 2. If there is enough space to remove old one and add new one, reorg and replace old microcode.\r
+  //\r
+  if (MicrocodePatchRegionSize - (UsedRegionSize - CurrentTotalSize) >= ImageSize) {\r
+    if (CurrentMicrocodeEntryPoint == NULL) {\r
+      DEBUG((DEBUG_INFO, "Append new microcode\n"));\r
+      //\r
+      // +------+------------+------+===================+\r
+      // |Other1|   Other    |Other2|      Empty        |\r
+      // +------+------------+------+===================+\r
+      //\r
+      // +------+------------+------+-----------+=======+\r
+      // |Other1|   Other    |Other2| New Image | Empty |\r
+      // +------+------------+------+-----------+=======+\r
+      //\r
+      Status = UpdateMicrocode((UINTN)MicrocodePatchAddress + UsedRegionSize, Image, ImageSize, LastAttemptStatus);\r
+    } else {\r
+      DEBUG((DEBUG_INFO, "Reorg and replace old microcode\n"));\r
+      //\r
+      // +------+------------+------+===================+\r
+      // |Other1| Old Image  |Other2|      Empty        |\r
+      // +------+------------+------+===================+\r
+      //\r
+      // +------+---------------+------+================+\r
+      // |Other1|   New Image   |Other2|      Empty     |\r
+      // +------+---------------+------+================+\r
+      //\r
+      // 2.1. Copy new image\r
+      CopyMem (MicrocodePatchScratchBuffer, Image, ImageSize);\r
+      ScratchBufferSize += ImageSize;\r
+      ScratchBufferPtr = (UINT8 *)ScratchBufferPtr + ScratchBufferSize;\r
+      // 2.2. Copy rest images after the old image.\r
+      if (NextMicrocodeEntryPoint != 0) {\r
+        RestSize = (UINTN)MicrocodePatchAddress + UsedRegionSize - ((UINTN)NextMicrocodeEntryPoint);\r
+        CopyMem (ScratchBufferPtr, (UINT8 *)CurrentMicrocodeEntryPoint + CurrentTotalSize, RestSize);\r
+        ScratchBufferSize += RestSize;\r
+        ScratchBufferPtr = (UINT8 *)ScratchBufferPtr + ScratchBufferSize;\r
+      }\r
+      Status = UpdateMicrocode((UINTN)CurrentMicrocodeEntryPoint, MicrocodePatchScratchBuffer, ScratchBufferSize, LastAttemptStatus);\r
+    }\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // 3. The new image can be put in MCU region, but not all others can be put.\r
+  //    So all the unused MCU is removed.\r
+  //\r
+  if (MicrocodePatchRegionSize >= ImageSize) {\r
+    //\r
+    // +------+------------+------+===================+\r
+    // |Other1| Old Image  |Other2|      Empty        |\r
+    // +------+------------+------+===================+\r
+    //\r
+    // +-------------------------------------+--------+\r
+    // |        New Image                    | Other  |\r
+    // +-------------------------------------+--------+\r
+    //\r
+    DEBUG((DEBUG_INFO, "Add new microcode from beginning\n"));\r
+\r
+    MicrocodeCount = MicrocodeFmpPrivate->DescriptorCount;\r
+    MicrocodeInfo = MicrocodeFmpPrivate->MicrocodeInfo;\r
+\r
+    // 3.1. Copy new image\r
+    CopyMem (MicrocodePatchScratchBuffer, Image, ImageSize);\r
+    ScratchBufferSize += ImageSize;\r
+    ScratchBufferPtr = (UINT8 *)ScratchBufferPtr + ScratchBufferSize;\r
+    // 3.2. Copy some others to rest buffer\r
+    for (Index = 0; Index < MicrocodeCount; Index++) {\r
+      if (!MicrocodeInfo[Index].InUse) {\r
+        continue;\r
+      }\r
+      if (MicrocodeInfo[Index].MicrocodeEntryPoint == CurrentMicrocodeEntryPoint) {\r
+        continue;\r
+      }\r
+      if (MicrocodeInfo[Index].TotalSize <= MicrocodePatchRegionSize - ScratchBufferSize) {\r
+        CopyMem (ScratchBufferPtr, MicrocodeInfo[Index].MicrocodeEntryPoint, MicrocodeInfo[Index].TotalSize);\r
+        ScratchBufferSize += MicrocodeInfo[Index].TotalSize;\r
+        ScratchBufferPtr = (UINT8 *)ScratchBufferPtr + ScratchBufferSize;\r
+      }\r
+    }\r
+    // 3.3. Pad 0xFF\r
+    RestSize = MicrocodePatchRegionSize - ScratchBufferSize;\r
+    if (RestSize > 0) {\r
+      SetMem (ScratchBufferPtr, RestSize, 0xFF);\r
+      ScratchBufferSize += RestSize;\r
+      ScratchBufferPtr = (UINT8 *)ScratchBufferPtr + ScratchBufferSize;\r
+    }\r
+    Status = UpdateMicrocode((UINTN)MicrocodePatchAddress, MicrocodePatchScratchBuffer, ScratchBufferSize, LastAttemptStatus);\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // 4. The new image size is bigger than the whole MCU region.\r
+  //\r
+  DEBUG((DEBUG_ERROR, "Microcode too big\n"));\r
+  *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INSUFFICIENT_RESOURCES;\r
+  Status = EFI_OUT_OF_RESOURCES;\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Write Microcode.\r
+\r
+  Caution: This function may receive untrusted input.\r
+\r
+  @param[in]   MicrocodeFmpPrivate The Microcode driver private data\r
+  @param[in]   Image               The Microcode image buffer.\r
+  @param[in]   ImageSize           The size of Microcode image buffer in bytes.\r
+  @param[out]  LastAttemptVersion  The last attempt version, which will be recorded in ESRT and FMP EFI_FIRMWARE_IMAGE_DESCRIPTOR.\r
+  @param[out]  LastAttemptStatus   The last attempt status, which will be recorded in ESRT and FMP EFI_FIRMWARE_IMAGE_DESCRIPTOR.\r
+  @param[out]  AbortReason         A pointer to a pointer to a null-terminated string providing more\r
+                                   details for the aborted operation. The buffer is allocated by this function\r
+                                   with AllocatePool(), and it is the caller's responsibility to free it with a\r
+                                   call to FreePool().\r
+\r
+  @retval EFI_SUCCESS               The Microcode image is written.\r
+  @retval EFI_VOLUME_CORRUPTED      The Microcode image is corrupt.\r
+  @retval EFI_INCOMPATIBLE_VERSION  The Microcode image version is incorrect.\r
+  @retval EFI_SECURITY_VIOLATION    The Microcode image fails to load.\r
+  @retval EFI_WRITE_PROTECTED       The flash device is read only.\r
+**/\r
+EFI_STATUS\r
+MicrocodeWrite (\r
+  IN  MICROCODE_FMP_PRIVATE_DATA   *MicrocodeFmpPrivate,\r
+  IN  VOID                         *Image,\r
+  IN  UINTN                        ImageSize,\r
+  OUT UINT32                       *LastAttemptVersion,\r
+  OUT UINT32                       *LastAttemptStatus,\r
+  OUT CHAR16                       **AbortReason\r
+  )\r
+{\r
+  EFI_STATUS                              Status;\r
+  VOID                                    *AlignedImage;\r
+  CPU_MICROCODE_HEADER                    *CurrentMicrocodeEntryPoint;\r
+\r
+  //\r
+  // We must get Current MicrocodeEntrypoint *before* VerifyMicrocode,\r
+  // because the MicrocodeSignature might be updated in VerifyMicrocode.\r
+  //\r
+  CurrentMicrocodeEntryPoint = GetCurrentMicrocodeInUse(MicrocodeFmpPrivate);\r
+  DEBUG((DEBUG_INFO, "  CurrentMicrocodeEntryPoint - 0x%x\n", CurrentMicrocodeEntryPoint));\r
+\r
   //\r
   // MCU must be 16 bytes aligned\r
   //\r
@@ -759,32 +914,13 @@ MicrocodeWrite (
   }\r
   DEBUG((DEBUG_INFO, "Pass VerifyMicrocode\n"));\r
 \r
-  if (CurrentTotalSize < ImageSize) {\r
-    UsedRegionSize = GetCurrentMicrocodeUsedRegionSize();\r
-    if (MicrocodePatchRegionSize - UsedRegionSize >= ImageSize) {\r
-      //\r
-      // Append\r
-      //\r
-      DEBUG((DEBUG_INFO, "Append new microcode\n"));\r
-      Status = UpdateMicrocode(MicrocodePatchAddress + UsedRegionSize, AlignedImage, ImageSize, LastAttemptStatus);\r
-    } else if (MicrocodePatchRegionSize >= ImageSize) {\r
-      //\r
-      // Ignor all others and just add this one from beginning.\r
-      //\r
-      DEBUG((DEBUG_INFO, "Add new microcode from beginning\n"));\r
-      Status = UpdateMicrocode(MicrocodePatchAddress, AlignedImage, ImageSize, LastAttemptStatus);\r
-    } else {\r
-      DEBUG((DEBUG_ERROR, "Microcode too big\n"));\r
-      *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INSUFFICIENT_RESOURCES;\r
-      Status = EFI_OUT_OF_RESOURCES;\r
-    }\r
-  } else {\r
-    //\r
-    // Replace\r
-    //\r
-    DEBUG((DEBUG_INFO, "Replace old microcode\n"));\r
-    Status = UpdateMicrocode((UINTN)CurrentMicrocodeEntryPoint, AlignedImage, ImageSize, LastAttemptStatus);\r
-  }\r
+  Status = UpdateMicrocodeFlashRegion(\r
+             MicrocodeFmpPrivate,\r
+             CurrentMicrocodeEntryPoint,\r
+             AlignedImage,\r
+             ImageSize,\r
+             LastAttemptStatus\r
+             );\r
 \r
   FreePool(AlignedImage);\r
 \r