+ //\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