From e91797885aee58ae65d7935332e580dc8517e8f6 Mon Sep 17 00:00:00 2001 From: Star Zeng Date: Wed, 28 Mar 2018 16:52:12 +0800 Subject: [PATCH] IntelSiliconPkg MicrocodeUpdateDxe: Honor FIT table It is the second step for https://bugzilla.tianocore.org/show_bug.cgi?id=540. V2: Use error handling instead of ASSERT for FIT table checking result. Cc: Jiewen Yao Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Star Zeng Reviewed-by: Jiewen Yao --- .../Capsule/MicrocodeUpdateDxe/MicrocodeFmp.c | 251 +++++++++++- .../MicrocodeUpdateDxe/MicrocodeUpdate.c | 363 +++++++++++++++++- .../MicrocodeUpdateDxe/MicrocodeUpdate.h | 13 +- .../MicrocodeUpdateDxe/MicrocodeUpdateDxe.inf | 3 +- 4 files changed, 601 insertions(+), 29 deletions(-) diff --git a/IntelSiliconPkg/Feature/Capsule/MicrocodeUpdateDxe/MicrocodeFmp.c b/IntelSiliconPkg/Feature/Capsule/MicrocodeUpdateDxe/MicrocodeFmp.c index ef5e630caf..bc1387b3dd 100644 --- a/IntelSiliconPkg/Feature/Capsule/MicrocodeUpdateDxe/MicrocodeFmp.c +++ b/IntelSiliconPkg/Feature/Capsule/MicrocodeUpdateDxe/MicrocodeFmp.c @@ -272,7 +272,7 @@ FmpSetImage ( } Status = MicrocodeWrite(MicrocodeFmpPrivate, (VOID *)Image, ImageSize, &MicrocodeFmpPrivate->LastAttempt.LastAttemptVersion, &MicrocodeFmpPrivate->LastAttempt.LastAttemptStatus, AbortReason); - DEBUG((DEBUG_INFO, "SetImage - LastAttemp Version - 0x%x, State - 0x%x\n", MicrocodeFmpPrivate->LastAttempt.LastAttemptVersion, MicrocodeFmpPrivate->LastAttempt.LastAttemptStatus)); + DEBUG((DEBUG_INFO, "SetImage - LastAttempt Version - 0x%x, Status - 0x%x\n", MicrocodeFmpPrivate->LastAttempt.LastAttemptVersion, MicrocodeFmpPrivate->LastAttempt.LastAttemptStatus)); VarStatus = gRT->SetVariable( MICROCODE_FMP_LAST_ATTEMPT_VARIABLE_NAME, &gEfiCallerIdGuid, @@ -280,7 +280,7 @@ FmpSetImage ( sizeof(MicrocodeFmpPrivate->LastAttempt), &MicrocodeFmpPrivate->LastAttempt ); - DEBUG((DEBUG_INFO, "SetLastAttemp - %r\n", VarStatus)); + DEBUG((DEBUG_INFO, "SetLastAttempt - %r\n", VarStatus)); if (!EFI_ERROR(Status)) { InitializeMicrocodeDescriptor(MicrocodeFmpPrivate); @@ -414,6 +414,212 @@ FmpSetPackageInfo ( return EFI_UNSUPPORTED; } +/** + Sort FIT microcode entries based upon MicrocodeEntryPoint, from low to high. + + @param[in] MicrocodeFmpPrivate private data structure to be initialized. + +**/ +VOID +SortFitMicrocodeInfo ( + IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate + ) +{ + FIT_MICROCODE_INFO *FitMicrocodeEntry; + FIT_MICROCODE_INFO *NextFitMicrocodeEntry; + FIT_MICROCODE_INFO TempFitMicrocodeEntry; + FIT_MICROCODE_INFO *FitMicrocodeEntryEnd; + + FitMicrocodeEntry = MicrocodeFmpPrivate->FitMicrocodeInfo; + NextFitMicrocodeEntry = FitMicrocodeEntry + 1; + FitMicrocodeEntryEnd = MicrocodeFmpPrivate->FitMicrocodeInfo + MicrocodeFmpPrivate->FitMicrocodeEntryCount; + while (FitMicrocodeEntry < FitMicrocodeEntryEnd) { + while (NextFitMicrocodeEntry < FitMicrocodeEntryEnd) { + if (FitMicrocodeEntry->MicrocodeEntryPoint > NextFitMicrocodeEntry->MicrocodeEntryPoint) { + CopyMem (&TempFitMicrocodeEntry, FitMicrocodeEntry, sizeof (FIT_MICROCODE_INFO)); + CopyMem (FitMicrocodeEntry, NextFitMicrocodeEntry, sizeof (FIT_MICROCODE_INFO)); + CopyMem (NextFitMicrocodeEntry, &TempFitMicrocodeEntry, sizeof (FIT_MICROCODE_INFO)); + } + + NextFitMicrocodeEntry = NextFitMicrocodeEntry + 1; + } + + FitMicrocodeEntry = FitMicrocodeEntry + 1; + NextFitMicrocodeEntry = FitMicrocodeEntry + 1; + } +} + +/** + Initialize FIT microcode information. + + @param[in] MicrocodeFmpPrivate private data structure to be initialized. + + @return EFI_SUCCESS FIT microcode information is initialized. + @return EFI_OUT_OF_RESOURCES No enough resource for the initialization. + @return EFI_DEVICE_ERROR There is something wrong in FIT microcode entry. +**/ +EFI_STATUS +InitializeFitMicrocodeInfo ( + IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate + ) +{ + UINT64 FitPointer; + FIRMWARE_INTERFACE_TABLE_ENTRY *FitEntry; + UINT32 EntryNum; + UINT32 MicrocodeEntryNum; + UINT32 Index; + UINTN Address; + VOID *MicrocodePatchAddress; + UINTN MicrocodePatchRegionSize; + FIT_MICROCODE_INFO *FitMicrocodeInfo; + FIT_MICROCODE_INFO *FitMicrocodeInfoNext; + CPU_MICROCODE_HEADER *MicrocodeEntryPoint; + CPU_MICROCODE_HEADER *MicrocodeEntryPointNext; + UINTN FitMicrocodeIndex; + MICROCODE_INFO *MicrocodeInfo; + UINTN MicrocodeIndex; + + if (MicrocodeFmpPrivate->FitMicrocodeInfo != NULL) { + FreePool (MicrocodeFmpPrivate->FitMicrocodeInfo); + MicrocodeFmpPrivate->FitMicrocodeInfo = NULL; + MicrocodeFmpPrivate->FitMicrocodeEntryCount = 0; + } + + FitPointer = *(UINT64 *) (UINTN) FIT_POINTER_ADDRESS; + if ((FitPointer == 0) || + (FitPointer == 0xFFFFFFFFFFFFFFFF) || + (FitPointer == 0xEEEEEEEEEEEEEEEE)) { + // + // No FIT table. + // + return EFI_SUCCESS; + } + FitEntry = (FIRMWARE_INTERFACE_TABLE_ENTRY *) (UINTN) FitPointer; + if ((FitEntry[0].Type != FIT_TYPE_00_HEADER) || + (FitEntry[0].Address != FIT_TYPE_00_SIGNATURE)) { + // + // Invalid FIT table, treat it as no FIT table. + // + return EFI_SUCCESS; + } + + EntryNum = *(UINT32 *)(&FitEntry[0].Size[0]) & 0xFFFFFF; + + // + // Calculate microcode entry number. + // + MicrocodeEntryNum = 0; + for (Index = 0; Index < EntryNum; Index++) { + if (FitEntry[Index].Type == FIT_TYPE_01_MICROCODE) { + MicrocodeEntryNum++; + } + } + if (MicrocodeEntryNum == 0) { + // + // No FIT microcode entry. + // + return EFI_SUCCESS; + } + + // + // Allocate buffer. + // + MicrocodeFmpPrivate->FitMicrocodeInfo = AllocateZeroPool (MicrocodeEntryNum * sizeof (FIT_MICROCODE_INFO)); + if (MicrocodeFmpPrivate->FitMicrocodeInfo == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + MicrocodeFmpPrivate->FitMicrocodeEntryCount = MicrocodeEntryNum; + + MicrocodePatchAddress = MicrocodeFmpPrivate->MicrocodePatchAddress; + MicrocodePatchRegionSize = MicrocodeFmpPrivate->MicrocodePatchRegionSize; + + // + // Collect microcode entry info. + // + MicrocodeEntryNum = 0; + for (Index = 0; Index < EntryNum; Index++) { + if (FitEntry[Index].Type == FIT_TYPE_01_MICROCODE) { + Address = (UINTN) FitEntry[Index].Address; + if ((Address < (UINTN) MicrocodePatchAddress) || + (Address >= ((UINTN) MicrocodePatchAddress + MicrocodePatchRegionSize))) { + DEBUG (( + DEBUG_ERROR, + "InitializeFitMicrocodeInfo - Address (0x%x) is not in Microcode Region\n", + Address + )); + goto ErrorExit; + } + FitMicrocodeInfo = &MicrocodeFmpPrivate->FitMicrocodeInfo[MicrocodeEntryNum]; + FitMicrocodeInfo->MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *) Address; + if ((*(UINT32 *) Address) == 0xFFFFFFFF) { + // + // It is the empty slot as long as the first dword is 0xFFFF_FFFF. + // + FitMicrocodeInfo->Empty = TRUE; + } else { + FitMicrocodeInfo->Empty = FALSE; + } + MicrocodeEntryNum++; + } + } + + // + // Every microcode should have a FIT microcode entry. + // + for (MicrocodeIndex = 0; MicrocodeIndex < MicrocodeFmpPrivate->DescriptorCount; MicrocodeIndex++) { + MicrocodeInfo = &MicrocodeFmpPrivate->MicrocodeInfo[MicrocodeIndex]; + for (FitMicrocodeIndex = 0; FitMicrocodeIndex < MicrocodeFmpPrivate->FitMicrocodeEntryCount; FitMicrocodeIndex++) { + FitMicrocodeInfo = &MicrocodeFmpPrivate->FitMicrocodeInfo[FitMicrocodeIndex]; + if (MicrocodeInfo->MicrocodeEntryPoint == FitMicrocodeInfo->MicrocodeEntryPoint) { + FitMicrocodeInfo->TotalSize = MicrocodeInfo->TotalSize; + FitMicrocodeInfo->InUse = MicrocodeInfo->InUse; + break; + } + } + if (FitMicrocodeIndex >= MicrocodeFmpPrivate->FitMicrocodeEntryCount) { + DEBUG (( + DEBUG_ERROR, + "InitializeFitMicrocodeInfo - There is no FIT microcode entry for Microcode (0x%x)\n", + MicrocodeInfo->MicrocodeEntryPoint + )); + goto ErrorExit; + } + } + + SortFitMicrocodeInfo (MicrocodeFmpPrivate); + + // + // Check overlap. + // + for (FitMicrocodeIndex = 0; FitMicrocodeIndex < MicrocodeFmpPrivate->FitMicrocodeEntryCount - 1; FitMicrocodeIndex++) { + FitMicrocodeInfo = &MicrocodeFmpPrivate->FitMicrocodeInfo[FitMicrocodeIndex]; + MicrocodeEntryPoint = FitMicrocodeInfo->MicrocodeEntryPoint; + FitMicrocodeInfoNext = &MicrocodeFmpPrivate->FitMicrocodeInfo[FitMicrocodeIndex + 1]; + MicrocodeEntryPointNext = FitMicrocodeInfoNext->MicrocodeEntryPoint; + if ((MicrocodeEntryPoint >= MicrocodeEntryPointNext) || + ((FitMicrocodeInfo->TotalSize != 0) && + ((UINTN) MicrocodeEntryPoint + FitMicrocodeInfo->TotalSize) > + (UINTN) MicrocodeEntryPointNext)) { + DEBUG (( + DEBUG_ERROR, + "InitializeFitMicrocodeInfo - There is overlap between FIT microcode entries (0x%x 0x%x)\n", + MicrocodeEntryPoint, + MicrocodeEntryPointNext + )); + goto ErrorExit; + } + } + + return EFI_SUCCESS; + +ErrorExit: + FreePool (MicrocodeFmpPrivate->FitMicrocodeInfo); + MicrocodeFmpPrivate->FitMicrocodeInfo = NULL; + MicrocodeFmpPrivate->FitMicrocodeEntryCount = 0; + return EFI_DEVICE_ERROR; +} + /** Initialize Processor Microcode Index. @@ -460,14 +666,16 @@ InitializedProcessorMicrocodeIndex ( @param[in] MicrocodeFmpPrivate private data structure to be initialized. - @return EFI_SUCCESS Microcode Descriptor is initialized. + @return EFI_SUCCESS Microcode Descriptor is initialized. + @return EFI_OUT_OF_RESOURCES No enough resource for the initialization. **/ EFI_STATUS InitializeMicrocodeDescriptor ( IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate ) { - UINT8 CurrentMicrocodeCount; + EFI_STATUS Status; + UINT8 CurrentMicrocodeCount; CurrentMicrocodeCount = (UINT8)GetMicrocodeInfo (MicrocodeFmpPrivate, 0, NULL, NULL); @@ -496,6 +704,7 @@ InitializeMicrocodeDescriptor ( if (MicrocodeFmpPrivate->MicrocodeInfo == NULL) { MicrocodeFmpPrivate->MicrocodeInfo = AllocateZeroPool(MicrocodeFmpPrivate->DescriptorCount * sizeof(MICROCODE_INFO)); if (MicrocodeFmpPrivate->MicrocodeInfo == NULL) { + FreePool (MicrocodeFmpPrivate->ImageDescriptor); return EFI_OUT_OF_RESOURCES; } } @@ -505,6 +714,14 @@ InitializeMicrocodeDescriptor ( InitializedProcessorMicrocodeIndex (MicrocodeFmpPrivate); + Status = InitializeFitMicrocodeInfo (MicrocodeFmpPrivate); + if (EFI_ERROR(Status)) { + FreePool (MicrocodeFmpPrivate->ImageDescriptor); + FreePool (MicrocodeFmpPrivate->MicrocodeInfo); + DEBUG((DEBUG_ERROR, "InitializeFitMicrocodeInfo - %r\n", Status)); + return Status; + } + return EFI_SUCCESS; } @@ -513,7 +730,8 @@ InitializeMicrocodeDescriptor ( @param[in] MicrocodeFmpPrivate private data structure to be initialized. - @return EFI_SUCCESS private data is initialized. + @return EFI_SUCCESS Processor information is initialized. + @return EFI_OUT_OF_RESOURCES No enough resource for the initialization. **/ EFI_STATUS InitializeProcessorInfo ( @@ -583,6 +801,7 @@ DumpPrivateInfo ( PROCESSOR_INFO *ProcessorInfo; MICROCODE_INFO *MicrocodeInfo; EFI_FIRMWARE_IMAGE_DESCRIPTOR *ImageDescriptor; + FIT_MICROCODE_INFO *FitMicrocodeInfo; DEBUG ((DEBUG_INFO, "ProcessorInfo:\n")); DEBUG ((DEBUG_INFO, " ProcessorCount - 0x%x\n", MicrocodeFmpPrivate->ProcessorCount)); @@ -635,6 +854,23 @@ DumpPrivateInfo ( DEBUG((DEBUG_VERBOSE, " LastAttemptStatus - 0x%x\n", ImageDescriptor[Index].LastAttemptStatus)); DEBUG((DEBUG_VERBOSE, " HardwareInstance - 0x%lx\n", ImageDescriptor[Index].HardwareInstance)); } + + if (MicrocodeFmpPrivate->FitMicrocodeInfo != NULL) { + DEBUG ((DEBUG_INFO, "FitMicrocodeInfo:\n")); + FitMicrocodeInfo = MicrocodeFmpPrivate->FitMicrocodeInfo; + DEBUG ((DEBUG_INFO, " FitMicrocodeEntryCount - 0x%x\n", MicrocodeFmpPrivate->FitMicrocodeEntryCount)); + for (Index = 0; Index < MicrocodeFmpPrivate->FitMicrocodeEntryCount; Index++) { + DEBUG (( + DEBUG_INFO, + " FitMicrocodeInfo[0x%x] - 0x%08x, 0x%08x, (0x%x, 0x%x)\n", + Index, + FitMicrocodeInfo[Index].MicrocodeEntryPoint, + FitMicrocodeInfo[Index].TotalSize, + FitMicrocodeInfo[Index].InUse, + FitMicrocodeInfo[Index].Empty + )); + } + } } /** @@ -671,8 +907,8 @@ InitializePrivateData ( &VarSize, &MicrocodeFmpPrivate->LastAttempt ); - DEBUG((DEBUG_INFO, "GetLastAttemp - %r\n", VarStatus)); - DEBUG((DEBUG_INFO, "GetLastAttemp Version - 0x%x, State - 0x%x\n", MicrocodeFmpPrivate->LastAttempt.LastAttemptVersion, MicrocodeFmpPrivate->LastAttempt.LastAttemptStatus)); + DEBUG((DEBUG_INFO, "GetLastAttempt - %r\n", VarStatus)); + DEBUG((DEBUG_INFO, "GetLastAttempt Version - 0x%x, State - 0x%x\n", MicrocodeFmpPrivate->LastAttempt.LastAttemptVersion, MicrocodeFmpPrivate->LastAttempt.LastAttemptStatus)); Result = GetMicrocodeRegion(&MicrocodeFmpPrivate->MicrocodePatchAddress, &MicrocodeFmpPrivate->MicrocodePatchRegionSize); if (!Result) { @@ -688,6 +924,7 @@ InitializePrivateData ( Status = InitializeMicrocodeDescriptor(MicrocodeFmpPrivate); if (EFI_ERROR(Status)) { + FreePool (MicrocodeFmpPrivate->ProcessorInfo); DEBUG((DEBUG_ERROR, "InitializeMicrocodeDescriptor - %r\n", Status)); return Status; } diff --git a/IntelSiliconPkg/Feature/Capsule/MicrocodeUpdateDxe/MicrocodeUpdate.c b/IntelSiliconPkg/Feature/Capsule/MicrocodeUpdateDxe/MicrocodeUpdate.c index 2cb0adbc44..9098712c2f 100644 --- a/IntelSiliconPkg/Feature/Capsule/MicrocodeUpdateDxe/MicrocodeUpdate.c +++ b/IntelSiliconPkg/Feature/Capsule/MicrocodeUpdateDxe/MicrocodeUpdate.c @@ -368,7 +368,7 @@ GetMatchedProcessor ( On output, the index of target CPU which matches the Microcode. @retval EFI_SUCCESS The Microcode image passes verification. - @retval EFI_VOLUME_CORRUPTED The Microcode image is corrupt. + @retval EFI_VOLUME_CORRUPTED The Microcode image is corrupted. @retval EFI_INCOMPATIBLE_VERSION The Microcode image version is incorrect. @retval EFI_UNSUPPORTED The Microcode ProcessorSignature or ProcessorFlags is incorrect. @retval EFI_SECURITY_VIOLATION The Microcode image fails to load. @@ -550,7 +550,7 @@ VerifyMicrocode ( } *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INCORRECT_VERSION; if (AbortReason != NULL) { - *AbortReason = AllocateCopyPool(sizeof(L"UnsupportedProcessSignature/ProcessorFlags"), L"UnsupportedProcessSignature/ProcessorFlags"); + *AbortReason = AllocateCopyPool(sizeof(L"UnsupportedProcessorSignature/ProcessorFlags"), L"UnsupportedProcessorSignature/ProcessorFlags"); } return EFI_UNSUPPORTED; } @@ -622,6 +622,124 @@ GetNextMicrocode ( return NULL; } +/** + Get next FIT Microcode entrypoint. + + @param[in] MicrocodeFmpPrivate The Microcode driver private data + @param[in] MicrocodeEntryPoint Current Microcode entrypoint + + @return next FIT Microcode entrypoint. +**/ +CPU_MICROCODE_HEADER * +GetNextFitMicrocode ( + IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate, + IN CPU_MICROCODE_HEADER *MicrocodeEntryPoint + ) +{ + UINTN Index; + + for (Index = 0; Index < MicrocodeFmpPrivate->FitMicrocodeEntryCount; Index++) { + if (MicrocodeEntryPoint == MicrocodeFmpPrivate->FitMicrocodeInfo[Index].MicrocodeEntryPoint) { + if (Index == (UINTN) MicrocodeFmpPrivate->FitMicrocodeEntryCount - 1) { + // it is last one + return NULL; + } else { + // return next one + return MicrocodeFmpPrivate->FitMicrocodeInfo[Index + 1].MicrocodeEntryPoint; + } + } + } + + ASSERT(FALSE); + return NULL; +} + +/** + Find empty FIT Microcode entrypoint. + + @param[in] MicrocodeFmpPrivate The Microcode driver private data + @param[in] ImageSize The size of Microcode image buffer in bytes. + @param[out] AvailableSize Available size of the empty FIT Microcode entrypoint. + + @return Empty FIT Microcode entrypoint. +**/ +CPU_MICROCODE_HEADER * +FindEmptyFitMicrocode ( + IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate, + IN UINTN ImageSize, + OUT UINTN *AvailableSize + ) +{ + UINTN Index; + CPU_MICROCODE_HEADER *MicrocodeEntryPoint; + CPU_MICROCODE_HEADER *NextMicrocodeEntryPoint; + VOID *MicrocodePatchAddress; + UINTN MicrocodePatchRegionSize; + + MicrocodePatchAddress = MicrocodeFmpPrivate->MicrocodePatchAddress; + MicrocodePatchRegionSize = MicrocodeFmpPrivate->MicrocodePatchRegionSize; + + for (Index = 0; Index < MicrocodeFmpPrivate->FitMicrocodeEntryCount; Index++) { + if (MicrocodeFmpPrivate->FitMicrocodeInfo[Index].Empty) { + MicrocodeEntryPoint = MicrocodeFmpPrivate->FitMicrocodeInfo[Index].MicrocodeEntryPoint; + NextMicrocodeEntryPoint = GetNextFitMicrocode (MicrocodeFmpPrivate, MicrocodeEntryPoint); + if (NextMicrocodeEntryPoint != NULL) { + *AvailableSize = (UINTN) NextMicrocodeEntryPoint - (UINTN) MicrocodeEntryPoint; + } else { + *AvailableSize = (UINTN) MicrocodePatchAddress + MicrocodePatchRegionSize - (UINTN) MicrocodeEntryPoint; + } + if (*AvailableSize >= ImageSize) { + return MicrocodeEntryPoint; + } + } + } + + return NULL; +} + +/** + Find unused FIT Microcode entrypoint. + + @param[in] MicrocodeFmpPrivate The Microcode driver private data + @param[in] ImageSize The size of Microcode image buffer in bytes. + @param[out] AvailableSize Available size of the unused FIT Microcode entrypoint. + + @return Unused FIT Microcode entrypoint. +**/ +CPU_MICROCODE_HEADER * +FindUnusedFitMicrocode ( + IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate, + IN UINTN ImageSize, + OUT UINTN *AvailableSize + ) +{ + UINTN Index; + CPU_MICROCODE_HEADER *MicrocodeEntryPoint; + CPU_MICROCODE_HEADER *NextMicrocodeEntryPoint; + VOID *MicrocodePatchAddress; + UINTN MicrocodePatchRegionSize; + + MicrocodePatchAddress = MicrocodeFmpPrivate->MicrocodePatchAddress; + MicrocodePatchRegionSize = MicrocodeFmpPrivate->MicrocodePatchRegionSize; + + for (Index = 0; Index < MicrocodeFmpPrivate->FitMicrocodeEntryCount; Index++) { + if (!MicrocodeFmpPrivate->FitMicrocodeInfo[Index].InUse) { + MicrocodeEntryPoint = MicrocodeFmpPrivate->FitMicrocodeInfo[Index].MicrocodeEntryPoint; + NextMicrocodeEntryPoint = GetNextFitMicrocode (MicrocodeFmpPrivate, MicrocodeEntryPoint); + if (NextMicrocodeEntryPoint != NULL) { + *AvailableSize = (UINTN) NextMicrocodeEntryPoint - (UINTN) MicrocodeEntryPoint; + } else { + *AvailableSize = (UINTN) MicrocodePatchAddress + MicrocodePatchRegionSize - (UINTN) MicrocodeEntryPoint; + } + if (*AvailableSize >= ImageSize) { + return MicrocodeEntryPoint; + } + } + } + + return NULL; +} + /** Get current Microcode used region size. @@ -666,7 +784,7 @@ UpdateMicrocode ( DEBUG((DEBUG_INFO, "PlatformUpdate:")); DEBUG((DEBUG_INFO, " Address - 0x%lx,", Address)); - DEBUG((DEBUG_INFO, " Legnth - 0x%x\n", ImageSize)); + DEBUG((DEBUG_INFO, " Length - 0x%x\n", ImageSize)); Status = MicrocodeFlashWrite ( Address, @@ -681,6 +799,201 @@ UpdateMicrocode ( return Status; } +/** + Update Microcode flash region with FIT. + + @param[in] MicrocodeFmpPrivate The Microcode driver private data + @param[in] TargetMicrocodeEntryPoint Target Microcode entrypoint to be updated + @param[in] Image The Microcode image buffer. + @param[in] ImageSize The size of Microcode image buffer in bytes. + @param[out] LastAttemptStatus The last attempt status, which will be recorded in ESRT and FMP EFI_FIRMWARE_IMAGE_DESCRIPTOR. + + @retval EFI_SUCCESS The Microcode image is written. + @retval EFI_WRITE_PROTECTED The flash device is read only. +**/ +EFI_STATUS +UpdateMicrocodeFlashRegionWithFit ( + IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate, + IN CPU_MICROCODE_HEADER *TargetMicrocodeEntryPoint, + IN VOID *Image, + IN UINTN ImageSize, + OUT UINT32 *LastAttemptStatus + ) +{ + VOID *MicrocodePatchAddress; + UINTN MicrocodePatchRegionSize; + UINTN TargetTotalSize; + EFI_STATUS Status; + VOID *MicrocodePatchScratchBuffer; + UINT8 *ScratchBufferPtr; + UINTN ScratchBufferSize; + UINTN RestSize; + UINTN AvailableSize; + VOID *NextMicrocodeEntryPoint; + VOID *EmptyFitMicrocodeEntry; + VOID *UnusedFitMicrocodeEntry; + + DEBUG((DEBUG_INFO, "UpdateMicrocodeFlashRegionWithFit: Image - 0x%x, size - 0x%x\n", Image, ImageSize)); + + MicrocodePatchAddress = MicrocodeFmpPrivate->MicrocodePatchAddress; + MicrocodePatchRegionSize = MicrocodeFmpPrivate->MicrocodePatchRegionSize; + + MicrocodePatchScratchBuffer = AllocateZeroPool (MicrocodePatchRegionSize); + if (MicrocodePatchScratchBuffer == NULL) { + DEBUG((DEBUG_ERROR, "Fail to allocate Microcode Scratch buffer\n")); + *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INSUFFICIENT_RESOURCES; + return EFI_OUT_OF_RESOURCES; + } + ScratchBufferPtr = MicrocodePatchScratchBuffer; + ScratchBufferSize = 0; + + // + // Target data collection + // + TargetTotalSize = 0; + AvailableSize = 0; + if (TargetMicrocodeEntryPoint != NULL) { + if (TargetMicrocodeEntryPoint->DataSize == 0) { + TargetTotalSize = 2048; + } else { + TargetTotalSize = TargetMicrocodeEntryPoint->TotalSize; + } + DEBUG((DEBUG_INFO, " TargetTotalSize - 0x%x\n", TargetTotalSize)); + NextMicrocodeEntryPoint = GetNextFitMicrocode (MicrocodeFmpPrivate, TargetMicrocodeEntryPoint); + DEBUG((DEBUG_INFO, " NextMicrocodeEntryPoint - 0x%x\n", NextMicrocodeEntryPoint)); + if (NextMicrocodeEntryPoint != NULL) { + ASSERT ((UINTN) NextMicrocodeEntryPoint >= ((UINTN) TargetMicrocodeEntryPoint + TargetTotalSize)); + AvailableSize = (UINTN) NextMicrocodeEntryPoint - (UINTN) TargetMicrocodeEntryPoint; + } else { + AvailableSize = (UINTN) MicrocodePatchAddress + MicrocodePatchRegionSize - (UINTN) TargetMicrocodeEntryPoint; + } + DEBUG((DEBUG_INFO, " AvailableSize - 0x%x\n", AvailableSize)); + ASSERT (AvailableSize >= TargetTotalSize); + } + // + // Total Size means the Microcode size. + // Available Size means the Microcode size plus the pad till (1) next Microcode or (2) the end. + // + // (1) + // +------+-----------+-----+------+===================+ + // | MCU1 | Microcode | PAD | MCU2 | Empty | + // +------+-----------+-----+------+===================+ + // | TotalSize | + // |<-AvailableSize->| + // + // (2) + // +------+-----------+===================+ + // | MCU | Microcode | Empty | + // +------+-----------+===================+ + // | TotalSize | + // |<- AvailableSize ->| + // + + // + // Update based on policy + // + + // + // 1. If there is enough space to update old one in situ, replace old microcode in situ. + // + if (AvailableSize >= ImageSize) { + DEBUG((DEBUG_INFO, "Replace old microcode in situ\n")); + // + // +------+------------+------+===================+ + // |Other | Old Image | ... | Empty | + // +------+------------+------+===================+ + // + // +------+---------+--+------+===================+ + // |Other |New Image|FF| ... | Empty | + // +------+---------+--+------+===================+ + // + // 1.1. Copy new image + CopyMem (ScratchBufferPtr, Image, ImageSize); + ScratchBufferSize += ImageSize; + ScratchBufferPtr = (UINT8 *)MicrocodePatchScratchBuffer + ScratchBufferSize; + // 1.2. Pad 0xFF + RestSize = AvailableSize - ImageSize; + if (RestSize > 0) { + SetMem (ScratchBufferPtr, RestSize, 0xFF); + ScratchBufferSize += RestSize; + ScratchBufferPtr = (UINT8 *)MicrocodePatchScratchBuffer + ScratchBufferSize; + } + Status = UpdateMicrocode((UINTN)TargetMicrocodeEntryPoint, MicrocodePatchScratchBuffer, ScratchBufferSize, LastAttemptStatus); + return Status; + } + + // + // 2. If there is empty FIT microcode entry with enough space, use it. + // + EmptyFitMicrocodeEntry = FindEmptyFitMicrocode (MicrocodeFmpPrivate, ImageSize, &AvailableSize); + if (EmptyFitMicrocodeEntry != NULL) { + DEBUG((DEBUG_INFO, "Use empty FIT microcode entry\n")); + // 2.1. Copy new image + CopyMem (ScratchBufferPtr, Image, ImageSize); + ScratchBufferSize += ImageSize; + ScratchBufferPtr = (UINT8 *)MicrocodePatchScratchBuffer + ScratchBufferSize; + // 2.2. Pad 0xFF + RestSize = AvailableSize - ImageSize; + if (RestSize > 0) { + SetMem (ScratchBufferPtr, RestSize, 0xFF); + ScratchBufferSize += RestSize; + ScratchBufferPtr = (UINT8 *)MicrocodePatchScratchBuffer + ScratchBufferSize; + } + Status = UpdateMicrocode ((UINTN) EmptyFitMicrocodeEntry, MicrocodePatchScratchBuffer, ScratchBufferSize, LastAttemptStatus); + if (!EFI_ERROR (Status) && (TargetMicrocodeEntryPoint != NULL)) { + // + // Empty old microcode. + // + ScratchBufferPtr = MicrocodePatchScratchBuffer; + SetMem (ScratchBufferPtr, TargetTotalSize, 0xFF); + ScratchBufferSize = TargetTotalSize; + ScratchBufferPtr = (UINT8 *) MicrocodePatchScratchBuffer + ScratchBufferSize; + UpdateMicrocode ((UINTN) TargetMicrocodeEntryPoint, MicrocodePatchScratchBuffer, ScratchBufferSize, LastAttemptStatus); + } + return Status; + } + + // + // 3. If there is unused microcode entry with enough space, use it. + // + UnusedFitMicrocodeEntry = FindUnusedFitMicrocode (MicrocodeFmpPrivate, ImageSize, &AvailableSize); + if (UnusedFitMicrocodeEntry != NULL) { + DEBUG((DEBUG_INFO, "Use unused FIT microcode entry\n")); + // 3.1. Copy new image + CopyMem (ScratchBufferPtr, Image, ImageSize); + ScratchBufferSize += ImageSize; + ScratchBufferPtr = (UINT8 *)MicrocodePatchScratchBuffer + ScratchBufferSize; + // 3.2. Pad 0xFF + RestSize = AvailableSize - ImageSize; + if (RestSize > 0) { + SetMem (ScratchBufferPtr, RestSize, 0xFF); + ScratchBufferSize += RestSize; + ScratchBufferPtr = (UINT8 *)MicrocodePatchScratchBuffer + ScratchBufferSize; + } + Status = UpdateMicrocode ((UINTN) UnusedFitMicrocodeEntry, MicrocodePatchScratchBuffer, ScratchBufferSize, LastAttemptStatus); + if (!EFI_ERROR (Status) && (TargetMicrocodeEntryPoint != NULL)) { + // + // Empty old microcode. + // + ScratchBufferPtr = MicrocodePatchScratchBuffer; + SetMem (ScratchBufferPtr, TargetTotalSize, 0xFF); + ScratchBufferSize = TargetTotalSize; + ScratchBufferPtr = (UINT8 *) MicrocodePatchScratchBuffer + ScratchBufferSize; + UpdateMicrocode ((UINTN) TargetMicrocodeEntryPoint, MicrocodePatchScratchBuffer, ScratchBufferSize, LastAttemptStatus); + } + return Status; + } + + // + // 4. No usable FIT microcode entry. + // + DEBUG((DEBUG_ERROR, "No usable FIT microcode entry\n")); + *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INSUFFICIENT_RESOURCES; + Status = EFI_OUT_OF_RESOURCES; + + return Status; +} + /** Update Microcode flash region. @@ -753,8 +1066,8 @@ UpdateMicrocodeFlashRegion ( AvailableSize = (UINTN)MicrocodePatchAddress + MicrocodePatchRegionSize - (UINTN)TargetMicrocodeEntryPoint; } DEBUG((DEBUG_INFO, " AvailableSize - 0x%x\n", AvailableSize)); + ASSERT (AvailableSize >= TargetTotalSize); } - ASSERT (AvailableSize >= TargetTotalSize); UsedRegionSize = GetCurrentMicrocodeUsedRegionSize(MicrocodeFmpPrivate); DEBUG((DEBUG_INFO, " UsedRegionSize - 0x%x\n", UsedRegionSize)); ASSERT (UsedRegionSize >= TargetTotalSize); @@ -762,8 +1075,8 @@ UpdateMicrocodeFlashRegion ( ASSERT ((UINTN)MicrocodePatchAddress + UsedRegionSize >= ((UINTN)TargetMicrocodeEntryPoint + TargetTotalSize)); } // - // Total Size means the Microcode data size. - // Available Size means the Microcode data size plus the pad till (1) next Microcode or (2) the end. + // Total Size means the Microcode size. + // Available Size means the Microcode size plus the pad till (1) next Microcode or (2) the end. // // (1) // +------+-----------+-----+------+===================+ @@ -793,11 +1106,11 @@ UpdateMicrocodeFlashRegion ( DEBUG((DEBUG_INFO, "Replace old microcode in situ\n")); // // +------+------------+------+===================+ - // |Other1| Old Image |Other2| Empty | + // |Other | Old Image | ... | Empty | // +------+------------+------+===================+ // // +------+---------+--+------+===================+ - // |Other1|New Image|FF|Other2| Empty | + // |Other |New Image|FF| ... | Empty | // +------+---------+--+------+===================+ // // 1.1. Copy new image @@ -835,11 +1148,11 @@ UpdateMicrocodeFlashRegion ( DEBUG((DEBUG_INFO, "Reorg and replace old microcode\n")); // // +------+------------+------+===================+ - // |Other1| Old Image |Other2| Empty | + // |Other | Old Image | ... | Empty | // +------+------------+------+===================+ // // +------+---------------+------+================+ - // |Other1| New Image |Other2| Empty | + // |Other | New Image | ... | Empty | // +------+---------------+------+================+ // // 2.1. Copy new image @@ -849,7 +1162,7 @@ UpdateMicrocodeFlashRegion ( // 2.2. Copy rest images after the old image. if (NextMicrocodeEntryPoint != 0) { RestSize = (UINTN)MicrocodePatchAddress + UsedRegionSize - ((UINTN)NextMicrocodeEntryPoint); - CopyMem (ScratchBufferPtr, (UINT8 *)TargetMicrocodeEntryPoint + TargetTotalSize, RestSize); + CopyMem (ScratchBufferPtr, NextMicrocodeEntryPoint, RestSize); ScratchBufferSize += RestSize; ScratchBufferPtr = (UINT8 *)MicrocodePatchScratchBuffer + ScratchBufferSize; } @@ -932,7 +1245,7 @@ UpdateMicrocodeFlashRegion ( call to FreePool(). @retval EFI_SUCCESS The Microcode image is written. - @retval EFI_VOLUME_CORRUPTED The Microcode image is corrupt. + @retval EFI_VOLUME_CORRUPTED The Microcode image is corrupted. @retval EFI_INCOMPATIBLE_VERSION The Microcode image version is incorrect. @retval EFI_SECURITY_VIOLATION The Microcode image fails to load. @retval EFI_WRITE_PROTECTED The flash device is read only. @@ -963,7 +1276,6 @@ MicrocodeWrite ( return EFI_OUT_OF_RESOURCES; } - *LastAttemptVersion = ((CPU_MICROCODE_HEADER *)Image)->UpdateRevision; TargetCpuIndex = (UINTN)-1; Status = VerifyMicrocode(MicrocodeFmpPrivate, AlignedImage, ImageSize, TRUE, LastAttemptStatus, AbortReason, &TargetCpuIndex); if (EFI_ERROR(Status)) { @@ -972,6 +1284,7 @@ MicrocodeWrite ( return Status; } DEBUG((DEBUG_INFO, "Pass VerifyMicrocode\n")); + *LastAttemptVersion = ((CPU_MICROCODE_HEADER *)Image)->UpdateRevision; DEBUG((DEBUG_INFO, " TargetCpuIndex - 0x%x\n", TargetCpuIndex)); ASSERT (TargetCpuIndex < MicrocodeFmpPrivate->ProcessorCount); @@ -985,13 +1298,23 @@ MicrocodeWrite ( } DEBUG((DEBUG_INFO, " TargetMicrocodeEntryPoint - 0x%x\n", TargetMicrocodeEntryPoint)); - Status = UpdateMicrocodeFlashRegion( - MicrocodeFmpPrivate, - TargetMicrocodeEntryPoint, - AlignedImage, - ImageSize, - LastAttemptStatus - ); + if (MicrocodeFmpPrivate->FitMicrocodeInfo != NULL) { + Status = UpdateMicrocodeFlashRegionWithFit ( + MicrocodeFmpPrivate, + TargetMicrocodeEntryPoint, + AlignedImage, + ImageSize, + LastAttemptStatus + ); + } else { + Status = UpdateMicrocodeFlashRegion ( + MicrocodeFmpPrivate, + TargetMicrocodeEntryPoint, + AlignedImage, + ImageSize, + LastAttemptStatus + ); + } FreePool(AlignedImage); diff --git a/IntelSiliconPkg/Feature/Capsule/MicrocodeUpdateDxe/MicrocodeUpdate.h b/IntelSiliconPkg/Feature/Capsule/MicrocodeUpdateDxe/MicrocodeUpdate.h index 4442032eb6..3f92c517a9 100644 --- a/IntelSiliconPkg/Feature/Capsule/MicrocodeUpdateDxe/MicrocodeUpdate.h +++ b/IntelSiliconPkg/Feature/Capsule/MicrocodeUpdateDxe/MicrocodeUpdate.h @@ -20,6 +20,8 @@ #include #include +#include + #include #include @@ -57,6 +59,13 @@ typedef struct { BOOLEAN InUse; } MICROCODE_INFO; +typedef struct { + CPU_MICROCODE_HEADER *MicrocodeEntryPoint; + UINTN TotalSize; + BOOLEAN InUse; + BOOLEAN Empty; +} FIT_MICROCODE_INFO; + typedef struct { UINTN CpuIndex; UINT32 ProcessorSignature; @@ -86,11 +95,13 @@ struct _MICROCODE_FMP_PRIVATE_DATA { UINTN BspIndex; UINTN ProcessorCount; PROCESSOR_INFO *ProcessorInfo; + UINT32 FitMicrocodeEntryCount; + FIT_MICROCODE_INFO *FitMicrocodeInfo; }; typedef struct _MICROCODE_FMP_PRIVATE_DATA MICROCODE_FMP_PRIVATE_DATA; -#define MICROCODE_FMP_LAST_ATTEMPT_VARIABLE_NAME L"MicrocodeLastAttempVar" +#define MICROCODE_FMP_LAST_ATTEMPT_VARIABLE_NAME L"MicrocodeLastAttemptVar" /** Returns a pointer to the MICROCODE_FMP_PRIVATE_DATA structure from the input a as Fmp. diff --git a/IntelSiliconPkg/Feature/Capsule/MicrocodeUpdateDxe/MicrocodeUpdateDxe.inf b/IntelSiliconPkg/Feature/Capsule/MicrocodeUpdateDxe/MicrocodeUpdateDxe.inf index dbc90857a0..24f06c23d4 100644 --- a/IntelSiliconPkg/Feature/Capsule/MicrocodeUpdateDxe/MicrocodeUpdateDxe.inf +++ b/IntelSiliconPkg/Feature/Capsule/MicrocodeUpdateDxe/MicrocodeUpdateDxe.inf @@ -3,7 +3,7 @@ # # Produce FMP instance to update Microcode. # -# Copyright (c) 2016 - 2017, Intel Corporation. All rights reserved.
+# Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.
# This program and the accompanying materials # are licensed and made available under the terms and conditions of the BSD License # which accompanies this distribution. The full text of the license may be found at @@ -65,6 +65,7 @@ [Depex] gEfiVariableArchProtocolGuid AND + gEfiVariableWriteArchProtocolGuid AND gEfiMpServiceProtocolGuid [UserExtensions.TianoCore."ExtraFiles"] -- 2.39.2