+/**\r
+ Update Microcode flash region with FIT.\r
+\r
+ @param[in] MicrocodeFmpPrivate The Microcode driver private data\r
+ @param[in] TargetMicrocodeEntryPoint Target Microcode entrypoint to be updated\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_WRITE_PROTECTED The flash device is read only.\r
+**/\r
+EFI_STATUS\r
+UpdateMicrocodeFlashRegionWithFit (\r
+ IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate,\r
+ IN CPU_MICROCODE_HEADER *TargetMicrocodeEntryPoint,\r
+ IN VOID *Image,\r
+ IN UINTN ImageSize,\r
+ OUT UINT32 *LastAttemptStatus\r
+ )\r
+{\r
+ VOID *MicrocodePatchAddress;\r
+ UINTN MicrocodePatchRegionSize;\r
+ UINTN TargetTotalSize;\r
+ EFI_STATUS Status;\r
+ VOID *MicrocodePatchScratchBuffer;\r
+ UINT8 *ScratchBufferPtr;\r
+ UINTN ScratchBufferSize;\r
+ UINTN RestSize;\r
+ UINTN AvailableSize;\r
+ VOID *NextMicrocodeEntryPoint;\r
+ VOID *EmptyFitMicrocodeEntry;\r
+ VOID *UnusedFitMicrocodeEntry;\r
+\r
+ DEBUG((DEBUG_INFO, "UpdateMicrocodeFlashRegionWithFit: Image - 0x%x, size - 0x%x\n", Image, ImageSize));\r
+\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
+ // Target data collection\r
+ //\r
+ TargetTotalSize = 0;\r
+ AvailableSize = 0;\r
+ if (TargetMicrocodeEntryPoint != NULL) {\r
+ if (TargetMicrocodeEntryPoint->DataSize == 0) {\r
+ TargetTotalSize = 2048;\r
+ } else {\r
+ TargetTotalSize = TargetMicrocodeEntryPoint->TotalSize;\r
+ }\r
+ DEBUG((DEBUG_INFO, " TargetTotalSize - 0x%x\n", TargetTotalSize));\r
+ NextMicrocodeEntryPoint = GetNextFitMicrocode (MicrocodeFmpPrivate, TargetMicrocodeEntryPoint);\r
+ DEBUG((DEBUG_INFO, " NextMicrocodeEntryPoint - 0x%x\n", NextMicrocodeEntryPoint));\r
+ if (NextMicrocodeEntryPoint != NULL) {\r
+ ASSERT ((UINTN) NextMicrocodeEntryPoint >= ((UINTN) TargetMicrocodeEntryPoint + TargetTotalSize));\r
+ AvailableSize = (UINTN) NextMicrocodeEntryPoint - (UINTN) TargetMicrocodeEntryPoint;\r
+ } else {\r
+ AvailableSize = (UINTN) MicrocodePatchAddress + MicrocodePatchRegionSize - (UINTN) TargetMicrocodeEntryPoint;\r
+ }\r
+ DEBUG((DEBUG_INFO, " AvailableSize - 0x%x\n", AvailableSize));\r
+ ASSERT (AvailableSize >= TargetTotalSize);\r
+ }\r
+ //\r
+ // Total Size means the Microcode size.\r
+ // Available Size means the Microcode size plus the pad till (1) next Microcode or (2) the end.\r
+ //\r
+ // (1)\r
+ // +------+-----------+-----+------+===================+\r
+ // | MCU1 | Microcode | PAD | MCU2 | Empty |\r
+ // +------+-----------+-----+------+===================+\r
+ // | TotalSize |\r
+ // |<-AvailableSize->|\r
+ //\r
+ // (2)\r
+ // +------+-----------+===================+\r
+ // | MCU | Microcode | Empty |\r
+ // +------+-----------+===================+\r
+ // | TotalSize |\r
+ // |<- AvailableSize ->|\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
+ // |Other | Old Image | ... | Empty |\r
+ // +------+------------+------+===================+\r
+ //\r
+ // +------+---------+--+------+===================+\r
+ // |Other |New Image|FF| ... | Empty |\r
+ // +------+---------+--+------+===================+\r
+ //\r
+ // 1.1. Copy new image\r
+ CopyMem (ScratchBufferPtr, Image, ImageSize);\r
+ ScratchBufferSize += ImageSize;\r
+ ScratchBufferPtr = (UINT8 *)MicrocodePatchScratchBuffer + 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 *)MicrocodePatchScratchBuffer + ScratchBufferSize;\r
+ }\r
+ Status = UpdateMicrocode((UINTN)TargetMicrocodeEntryPoint, MicrocodePatchScratchBuffer, ScratchBufferSize, LastAttemptStatus);\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // 2. If there is empty FIT microcode entry with enough space, use it.\r
+ //\r
+ EmptyFitMicrocodeEntry = FindEmptyFitMicrocode (MicrocodeFmpPrivate, ImageSize, &AvailableSize);\r
+ if (EmptyFitMicrocodeEntry != NULL) {\r
+ DEBUG((DEBUG_INFO, "Use empty FIT microcode entry\n"));\r
+ // 2.1. Copy new image\r
+ CopyMem (ScratchBufferPtr, Image, ImageSize);\r
+ ScratchBufferSize += ImageSize;\r
+ ScratchBufferPtr = (UINT8 *)MicrocodePatchScratchBuffer + ScratchBufferSize;\r
+ // 2.2. Pad 0xFF\r
+ RestSize = AvailableSize - ImageSize;\r
+ if (RestSize > 0) {\r
+ SetMem (ScratchBufferPtr, RestSize, 0xFF);\r
+ ScratchBufferSize += RestSize;\r
+ ScratchBufferPtr = (UINT8 *)MicrocodePatchScratchBuffer + ScratchBufferSize;\r
+ }\r
+ Status = UpdateMicrocode ((UINTN) EmptyFitMicrocodeEntry, MicrocodePatchScratchBuffer, ScratchBufferSize, LastAttemptStatus);\r
+ if (!EFI_ERROR (Status) && (TargetMicrocodeEntryPoint != NULL)) {\r
+ //\r
+ // Empty old microcode.\r
+ //\r
+ ScratchBufferPtr = MicrocodePatchScratchBuffer;\r
+ SetMem (ScratchBufferPtr, TargetTotalSize, 0xFF);\r
+ ScratchBufferSize = TargetTotalSize;\r
+ ScratchBufferPtr = (UINT8 *) MicrocodePatchScratchBuffer + ScratchBufferSize;\r
+ UpdateMicrocode ((UINTN) TargetMicrocodeEntryPoint, MicrocodePatchScratchBuffer, ScratchBufferSize, LastAttemptStatus);\r
+ }\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // 3. If there is unused microcode entry with enough space, use it.\r
+ //\r
+ UnusedFitMicrocodeEntry = FindUnusedFitMicrocode (MicrocodeFmpPrivate, ImageSize, &AvailableSize);\r
+ if (UnusedFitMicrocodeEntry != NULL) {\r
+ DEBUG((DEBUG_INFO, "Use unused FIT microcode entry\n"));\r
+ // 3.1. Copy new image\r
+ CopyMem (ScratchBufferPtr, Image, ImageSize);\r
+ ScratchBufferSize += ImageSize;\r
+ ScratchBufferPtr = (UINT8 *)MicrocodePatchScratchBuffer + ScratchBufferSize;\r
+ // 3.2. Pad 0xFF\r
+ RestSize = AvailableSize - ImageSize;\r
+ if (RestSize > 0) {\r
+ SetMem (ScratchBufferPtr, RestSize, 0xFF);\r
+ ScratchBufferSize += RestSize;\r
+ ScratchBufferPtr = (UINT8 *)MicrocodePatchScratchBuffer + ScratchBufferSize;\r
+ }\r
+ Status = UpdateMicrocode ((UINTN) UnusedFitMicrocodeEntry, MicrocodePatchScratchBuffer, ScratchBufferSize, LastAttemptStatus);\r
+ if (!EFI_ERROR (Status) && (TargetMicrocodeEntryPoint != NULL)) {\r
+ //\r
+ // Empty old microcode.\r
+ //\r
+ ScratchBufferPtr = MicrocodePatchScratchBuffer;\r
+ SetMem (ScratchBufferPtr, TargetTotalSize, 0xFF);\r
+ ScratchBufferSize = TargetTotalSize;\r
+ ScratchBufferPtr = (UINT8 *) MicrocodePatchScratchBuffer + ScratchBufferSize;\r
+ UpdateMicrocode ((UINTN) TargetMicrocodeEntryPoint, MicrocodePatchScratchBuffer, ScratchBufferSize, LastAttemptStatus);\r
+ }\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // 4. No usable FIT microcode entry.\r
+ //\r
+ DEBUG((DEBUG_ERROR, "No usable FIT microcode entry\n"));\r
+ *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INSUFFICIENT_RESOURCES;\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+\r
+ return Status;\r
+}\r
+\r