\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 UINTN *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
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
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
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
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
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
}\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
//\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
/**\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
}\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
}\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