diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:17:27 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:17:27 +0000 |
commit | f215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch) | |
tree | 6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleLib.c | |
parent | Initial commit. (diff) | |
download | virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.tar.xz virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.zip |
Adding upstream version 7.0.14-dfsg.upstream/7.0.14-dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleLib.c')
-rw-r--r-- | src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleLib.c | 1650 |
1 files changed, 1650 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleLib.c b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleLib.c new file mode 100644 index 00000000..68a53da6 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleLib.c @@ -0,0 +1,1650 @@ +/** @file + DXE capsule library. + + Caution: This module requires additional review when modified. + This module will have external input - capsule image. + This external input must be validated carefully to avoid security issue like + buffer overflow, integer overflow. + + SupportCapsuleImage(), ProcessCapsuleImage(), IsValidCapsuleHeader(), + ValidateFmpCapsule(), and DisplayCapsuleImage() receives untrusted input and + performs basic validation. + + Copyright (c) 2016 - 2019, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <PiDxe.h> + +#include <IndustryStandard/WindowsUxCapsule.h> + +#include <Guid/FmpCapsule.h> +#include <Guid/SystemResourceTable.h> +#include <Guid/EventGroup.h> + +#include <Library/BaseLib.h> +#include <Library/DebugLib.h> +#include <Library/BaseMemoryLib.h> +#include <Library/DxeServicesTableLib.h> +#include <Library/UefiBootServicesTableLib.h> +#include <Library/UefiRuntimeServicesTableLib.h> +#include <Library/MemoryAllocationLib.h> +#include <Library/CapsuleLib.h> +#include <Library/DevicePathLib.h> +#include <Library/UefiLib.h> +#include <Library/BmpSupportLib.h> + +#include <Protocol/GraphicsOutput.h> +#include <Protocol/EsrtManagement.h> +#include <Protocol/FirmwareManagement.h> +#include <Protocol/FirmwareManagementProgress.h> +#include <Protocol/DevicePath.h> + +EFI_SYSTEM_RESOURCE_TABLE *mEsrtTable = NULL; +BOOLEAN mIsVirtualAddrConverted = FALSE; + +BOOLEAN mDxeCapsuleLibEndOfDxe = FALSE; +EFI_EVENT mDxeCapsuleLibEndOfDxeEvent = NULL; + +EDKII_FIRMWARE_MANAGEMENT_PROGRESS_PROTOCOL *mFmpProgress = NULL; + +/** + Initialize capsule related variables. +**/ +VOID +InitCapsuleVariable ( + VOID + ); + +/** + Record capsule status variable. + + @param[in] CapsuleHeader The capsule image header + @param[in] CapsuleStatus The capsule process stauts + + @retval EFI_SUCCESS The capsule status variable is recorded. + @retval EFI_OUT_OF_RESOURCES No resource to record the capsule status variable. +**/ +EFI_STATUS +RecordCapsuleStatusVariable ( + IN EFI_CAPSULE_HEADER *CapsuleHeader, + IN EFI_STATUS CapsuleStatus + ); + +/** + Record FMP capsule status variable. + + @param[in] CapsuleHeader The capsule image header + @param[in] CapsuleStatus The capsule process stauts + @param[in] PayloadIndex FMP payload index + @param[in] ImageHeader FMP image header + @param[in] FmpDevicePath DevicePath associated with the FMP producer + @param[in] CapFileName Capsule file name + + @retval EFI_SUCCESS The capsule status variable is recorded. + @retval EFI_OUT_OF_RESOURCES No resource to record the capsule status variable. +**/ +EFI_STATUS +RecordFmpCapsuleStatusVariable ( + IN EFI_CAPSULE_HEADER *CapsuleHeader, + IN EFI_STATUS CapsuleStatus, + IN UINTN PayloadIndex, + IN EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *ImageHeader, + IN EFI_DEVICE_PATH_PROTOCOL *FmpDevicePath, OPTIONAL + IN CHAR16 *CapFileName OPTIONAL + ); + +/** + Function indicate the current completion progress of the firmware + update. Platform may override with own specific progress function. + + @param[in] Completion A value between 1 and 100 indicating the current + completion progress of the firmware update + + @retval EFI_SUCESS The capsule update progress was updated. + @retval EFI_INVALID_PARAMETER Completion is greater than 100%. +**/ +EFI_STATUS +EFIAPI +UpdateImageProgress ( + IN UINTN Completion + ); + +/** + Return if this capsule is a capsule name capsule, based upon CapsuleHeader. + + @param[in] CapsuleHeader A pointer to EFI_CAPSULE_HEADER + + @retval TRUE It is a capsule name capsule. + @retval FALSE It is not a capsule name capsule. +**/ +BOOLEAN +IsCapsuleNameCapsule ( + IN EFI_CAPSULE_HEADER *CapsuleHeader + ) +{ + return CompareGuid (&CapsuleHeader->CapsuleGuid, &gEdkiiCapsuleOnDiskNameGuid); +} + +/** + Return if this CapsuleGuid is a FMP capsule GUID or not. + + @param[in] CapsuleGuid A pointer to EFI_GUID + + @retval TRUE It is a FMP capsule GUID. + @retval FALSE It is not a FMP capsule GUID. +**/ +BOOLEAN +IsFmpCapsuleGuid ( + IN EFI_GUID *CapsuleGuid + ) +{ + if (CompareGuid(&gEfiFmpCapsuleGuid, CapsuleGuid)) { + return TRUE; + } + + return FALSE; +} + +/** + Validate if it is valid capsule header + + Caution: This function may receive untrusted input. + + This function assumes the caller provided correct CapsuleHeader pointer + and CapsuleSize. + + This function validates the fields in EFI_CAPSULE_HEADER. + + @param[in] CapsuleHeader Points to a capsule header. + @param[in] CapsuleSize Size of the whole capsule image. + +**/ +BOOLEAN +IsValidCapsuleHeader ( + IN EFI_CAPSULE_HEADER *CapsuleHeader, + IN UINT64 CapsuleSize + ) +{ + if (CapsuleHeader->CapsuleImageSize != CapsuleSize) { + return FALSE; + } + if (CapsuleHeader->HeaderSize >= CapsuleHeader->CapsuleImageSize) { + return FALSE; + } + return TRUE; +} + +/** + Validate Fmp capsules layout. + + Caution: This function may receive untrusted input. + + This function assumes the caller validated the capsule by using + IsValidCapsuleHeader(), so that all fields in EFI_CAPSULE_HEADER are correct. + The capsule buffer size is CapsuleHeader->CapsuleImageSize. + + This function validates the fields in EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER + and EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER. + + This function need support nested FMP capsule. + + @param[in] CapsuleHeader Points to a capsule header. + @param[out] EmbeddedDriverCount The EmbeddedDriverCount in the FMP capsule. + + @retval EFI_SUCESS Input capsule is a correct FMP capsule. + @retval EFI_INVALID_PARAMETER Input capsule is not a correct FMP capsule. +**/ +EFI_STATUS +ValidateFmpCapsule ( + IN EFI_CAPSULE_HEADER *CapsuleHeader, + OUT UINT16 *EmbeddedDriverCount OPTIONAL + ) +{ + EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER *FmpCapsuleHeader; + UINT8 *EndOfCapsule; + EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *ImageHeader; + UINT8 *EndOfPayload; + UINT64 *ItemOffsetList; + UINT32 ItemNum; + UINTN Index; + UINTN FmpCapsuleSize; + UINTN FmpCapsuleHeaderSize; + UINT64 FmpImageSize; + UINTN FmpImageHeaderSize; + + if (!IsFmpCapsuleGuid(&CapsuleHeader->CapsuleGuid)) { + return ValidateFmpCapsule ((EFI_CAPSULE_HEADER *)((UINTN)CapsuleHeader + CapsuleHeader->HeaderSize), EmbeddedDriverCount); + } + + if (CapsuleHeader->HeaderSize >= CapsuleHeader->CapsuleImageSize) { + DEBUG((DEBUG_ERROR, "HeaderSize(0x%x) >= CapsuleImageSize(0x%x)\n", CapsuleHeader->HeaderSize, CapsuleHeader->CapsuleImageSize)); + return EFI_INVALID_PARAMETER; + } + + FmpCapsuleHeader = (EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER *) ((UINT8 *) CapsuleHeader + CapsuleHeader->HeaderSize); + EndOfCapsule = (UINT8 *) CapsuleHeader + CapsuleHeader->CapsuleImageSize; + FmpCapsuleSize = (UINTN)EndOfCapsule - (UINTN)FmpCapsuleHeader; + + if (FmpCapsuleSize < sizeof(EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER)) { + DEBUG((DEBUG_ERROR, "FmpCapsuleSize(0x%x) < EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER\n", FmpCapsuleSize)); + return EFI_INVALID_PARAMETER; + } + + // Check EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER + if (FmpCapsuleHeader->Version != EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER_INIT_VERSION) { + DEBUG((DEBUG_ERROR, "FmpCapsuleHeader->Version(0x%x) != EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER_INIT_VERSION\n", FmpCapsuleHeader->Version)); + return EFI_INVALID_PARAMETER; + } + ItemOffsetList = (UINT64 *)(FmpCapsuleHeader + 1); + + // No overflow + ItemNum = FmpCapsuleHeader->EmbeddedDriverCount + FmpCapsuleHeader->PayloadItemCount; + + if ((FmpCapsuleSize - sizeof(EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER))/sizeof(UINT64) < ItemNum) { + DEBUG((DEBUG_ERROR, "ItemNum(0x%x) too big\n", ItemNum)); + return EFI_INVALID_PARAMETER; + } + FmpCapsuleHeaderSize = sizeof(EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER) + sizeof(UINT64)*ItemNum; + + // Check ItemOffsetList + for (Index = 0; Index < ItemNum; Index++) { + if (ItemOffsetList[Index] >= FmpCapsuleSize) { + DEBUG((DEBUG_ERROR, "ItemOffsetList[%d](0x%lx) >= FmpCapsuleSize(0x%x)\n", Index, ItemOffsetList[Index], FmpCapsuleSize)); + return EFI_INVALID_PARAMETER; + } + if (ItemOffsetList[Index] < FmpCapsuleHeaderSize) { + DEBUG((DEBUG_ERROR, "ItemOffsetList[%d](0x%lx) < FmpCapsuleHeaderSize(0x%x)\n", Index, ItemOffsetList[Index], FmpCapsuleHeaderSize)); + return EFI_INVALID_PARAMETER; + } + // + // All the address in ItemOffsetList must be stored in ascending order + // + if (Index > 0) { + if (ItemOffsetList[Index] <= ItemOffsetList[Index - 1]) { + DEBUG((DEBUG_ERROR, "ItemOffsetList[%d](0x%lx) < ItemOffsetList[%d](0x%x)\n", Index, ItemOffsetList[Index], Index - 1, ItemOffsetList[Index - 1])); + return EFI_INVALID_PARAMETER; + } + } + } + + // Check EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER + for (Index = FmpCapsuleHeader->EmbeddedDriverCount; Index < ItemNum; Index++) { + ImageHeader = (EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *)((UINT8 *)FmpCapsuleHeader + ItemOffsetList[Index]); + if (Index == ItemNum - 1) { + EndOfPayload = (UINT8 *)((UINTN)EndOfCapsule - (UINTN)FmpCapsuleHeader); + } else { + EndOfPayload = (UINT8 *)(UINTN)ItemOffsetList[Index+1]; + } + FmpImageSize = (UINTN)EndOfPayload - ItemOffsetList[Index]; + + FmpImageHeaderSize = sizeof(EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER); + if ((ImageHeader->Version > EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER_INIT_VERSION) || + (ImageHeader->Version < 1)) { + DEBUG((DEBUG_ERROR, "ImageHeader->Version(0x%x) Unknown\n", ImageHeader->Version)); + return EFI_INVALID_PARAMETER; + } + if (ImageHeader->Version == 1) { + FmpImageHeaderSize = OFFSET_OF(EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER, UpdateHardwareInstance); + } else if (ImageHeader->Version == 2) { + FmpImageHeaderSize = OFFSET_OF(EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER, ImageCapsuleSupport); + } + if (FmpImageSize < FmpImageHeaderSize) { + DEBUG((DEBUG_ERROR, "FmpImageSize(0x%lx) < FmpImageHeaderSize(0x%x)\n", FmpImageSize, FmpImageHeaderSize)); + return EFI_INVALID_PARAMETER; + } + + // No overflow + if (FmpImageSize != (UINT64)FmpImageHeaderSize + (UINT64)ImageHeader->UpdateImageSize + (UINT64)ImageHeader->UpdateVendorCodeSize) { + DEBUG((DEBUG_ERROR, "FmpImageSize(0x%lx) mismatch, UpdateImageSize(0x%x) UpdateVendorCodeSize(0x%x)\n", FmpImageSize, ImageHeader->UpdateImageSize, ImageHeader->UpdateVendorCodeSize)); + return EFI_INVALID_PARAMETER; + } + } + + if (ItemNum == 0) { + // + // No driver & payload element in FMP + // + EndOfPayload = (UINT8 *)(FmpCapsuleHeader + 1); + if (EndOfPayload != EndOfCapsule) { + DEBUG((DEBUG_ERROR, "EndOfPayload(0x%x) mismatch, EndOfCapsule(0x%x)\n", EndOfPayload, EndOfCapsule)); + return EFI_INVALID_PARAMETER; + } + return EFI_UNSUPPORTED; + } + + if (EmbeddedDriverCount != NULL) { + *EmbeddedDriverCount = FmpCapsuleHeader->EmbeddedDriverCount; + } + + return EFI_SUCCESS; +} + +/** + Those capsules supported by the firmwares. + + Caution: This function may receive untrusted input. + + @param[in] CapsuleHeader Points to a capsule header. + + @retval EFI_SUCESS Input capsule is supported by firmware. + @retval EFI_UNSUPPORTED Input capsule is not supported by the firmware. +**/ +EFI_STATUS +DisplayCapsuleImage ( + IN EFI_CAPSULE_HEADER *CapsuleHeader + ) +{ + DISPLAY_DISPLAY_PAYLOAD *ImagePayload; + UINTN PayloadSize; + EFI_STATUS Status; + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *Blt; + UINTN BltSize; + UINTN Height; + UINTN Width; + EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput; + + // + // UX capsule doesn't have extended header entries. + // + if (CapsuleHeader->HeaderSize != sizeof (EFI_CAPSULE_HEADER)) { + return EFI_UNSUPPORTED; + } + ImagePayload = (DISPLAY_DISPLAY_PAYLOAD *)((UINTN) CapsuleHeader + CapsuleHeader->HeaderSize); + // + // (CapsuleImageSize > HeaderSize) is guaranteed by IsValidCapsuleHeader(). + // + PayloadSize = CapsuleHeader->CapsuleImageSize - CapsuleHeader->HeaderSize; + + // + // Make sure the image payload at least contain the DISPLAY_DISPLAY_PAYLOAD header. + // Further size check is performed by the logic translating BMP to GOP BLT. + // + if (PayloadSize <= sizeof (DISPLAY_DISPLAY_PAYLOAD)) { + return EFI_INVALID_PARAMETER; + } + + if (ImagePayload->Version != 1) { + return EFI_UNSUPPORTED; + } + if (CalculateCheckSum8((UINT8 *)CapsuleHeader, CapsuleHeader->CapsuleImageSize) != 0) { + return EFI_UNSUPPORTED; + } + // + // Only Support Bitmap by now + // + if (ImagePayload->ImageType != 0) { + return EFI_UNSUPPORTED; + } + + // + // Try to open GOP + // + Status = gBS->HandleProtocol (gST->ConsoleOutHandle, &gEfiGraphicsOutputProtocolGuid, (VOID **)&GraphicsOutput); + if (EFI_ERROR (Status)) { + Status = gBS->LocateProtocol(&gEfiGraphicsOutputProtocolGuid, NULL, (VOID **)&GraphicsOutput); + if (EFI_ERROR(Status)) { + return EFI_UNSUPPORTED; + } + } + + if (GraphicsOutput->Mode->Mode != ImagePayload->Mode) { + return EFI_UNSUPPORTED; + } + + Blt = NULL; + Width = 0; + Height = 0; + Status = TranslateBmpToGopBlt ( + ImagePayload + 1, + PayloadSize - sizeof(DISPLAY_DISPLAY_PAYLOAD), + &Blt, + &BltSize, + &Height, + &Width + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + Status = GraphicsOutput->Blt ( + GraphicsOutput, + Blt, + EfiBltBufferToVideo, + 0, + 0, + (UINTN) ImagePayload->OffsetX, + (UINTN) ImagePayload->OffsetY, + Width, + Height, + Width * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL) + ); + + FreePool(Blt); + + return Status; +} + +/** + Dump FMP information. + + @param[in] ImageInfoSize The size of ImageInfo, in bytes. + @param[in] ImageInfo A pointer to EFI_FIRMWARE_IMAGE_DESCRIPTOR. + @param[in] DescriptorVersion The version of EFI_FIRMWARE_IMAGE_DESCRIPTOR. + @param[in] DescriptorCount The count of EFI_FIRMWARE_IMAGE_DESCRIPTOR. + @param[in] DescriptorSize The size of an individual EFI_FIRMWARE_IMAGE_DESCRIPTOR, in bytes. + @param[in] PackageVersion The version of package. + @param[in] PackageVersionName The version name of package. +**/ +VOID +DumpFmpImageInfo ( + IN UINTN ImageInfoSize, + IN EFI_FIRMWARE_IMAGE_DESCRIPTOR *ImageInfo, + IN UINT32 DescriptorVersion, + IN UINT8 DescriptorCount, + IN UINTN DescriptorSize, + IN UINT32 PackageVersion, + IN CHAR16 *PackageVersionName + ) +{ + EFI_FIRMWARE_IMAGE_DESCRIPTOR *CurrentImageInfo; + UINTN Index; + + DEBUG((DEBUG_VERBOSE, " DescriptorVersion - 0x%x\n", DescriptorVersion)); + DEBUG((DEBUG_VERBOSE, " DescriptorCount - 0x%x\n", DescriptorCount)); + DEBUG((DEBUG_VERBOSE, " DescriptorSize - 0x%x\n", DescriptorSize)); + DEBUG((DEBUG_VERBOSE, " PackageVersion - 0x%x\n", PackageVersion)); + DEBUG((DEBUG_VERBOSE, " PackageVersionName - %s\n\n", PackageVersionName)); + CurrentImageInfo = ImageInfo; + for (Index = 0; Index < DescriptorCount; Index++) { + DEBUG((DEBUG_VERBOSE, " ImageDescriptor (%d)\n", Index)); + DEBUG((DEBUG_VERBOSE, " ImageIndex - 0x%x\n", CurrentImageInfo->ImageIndex)); + DEBUG((DEBUG_VERBOSE, " ImageTypeId - %g\n", &CurrentImageInfo->ImageTypeId)); + DEBUG((DEBUG_VERBOSE, " ImageId - 0x%lx\n", CurrentImageInfo->ImageId)); + DEBUG((DEBUG_VERBOSE, " ImageIdName - %s\n", CurrentImageInfo->ImageIdName)); + DEBUG((DEBUG_VERBOSE, " Version - 0x%x\n", CurrentImageInfo->Version)); + DEBUG((DEBUG_VERBOSE, " VersionName - %s\n", CurrentImageInfo->VersionName)); + DEBUG((DEBUG_VERBOSE, " Size - 0x%x\n", CurrentImageInfo->Size)); + DEBUG((DEBUG_VERBOSE, " AttributesSupported - 0x%lx\n", CurrentImageInfo->AttributesSupported)); + DEBUG((DEBUG_VERBOSE, " AttributesSetting - 0x%lx\n", CurrentImageInfo->AttributesSetting)); + DEBUG((DEBUG_VERBOSE, " Compatibilities - 0x%lx\n", CurrentImageInfo->Compatibilities)); + if (DescriptorVersion > 1) { + DEBUG((DEBUG_VERBOSE, " LowestSupportedImageVersion - 0x%x\n", CurrentImageInfo->LowestSupportedImageVersion)); + if (DescriptorVersion > 2) { + DEBUG((DEBUG_VERBOSE, " LastAttemptVersion - 0x%x\n", CurrentImageInfo->LastAttemptVersion)); + DEBUG((DEBUG_VERBOSE, " LastAttemptStatus - 0x%x\n", CurrentImageInfo->LastAttemptStatus)); + DEBUG((DEBUG_VERBOSE, " HardwareInstance - 0x%lx\n", CurrentImageInfo->HardwareInstance)); + } + } + // + // Use DescriptorSize to move ImageInfo Pointer to stay compatible with different ImageInfo version + // + CurrentImageInfo = (EFI_FIRMWARE_IMAGE_DESCRIPTOR *)((UINT8 *)CurrentImageInfo + DescriptorSize); + } +} + +/** + Dump a non-nested FMP capsule. + + @param[in] CapsuleHeader A pointer to CapsuleHeader +**/ +VOID +DumpFmpCapsule ( + IN EFI_CAPSULE_HEADER *CapsuleHeader + ) +{ + EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER *FmpCapsuleHeader; + EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *ImageHeader; + UINTN Index; + UINT64 *ItemOffsetList; + + FmpCapsuleHeader = (EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER *)((UINT8 *)CapsuleHeader + CapsuleHeader->HeaderSize); + + DEBUG((DEBUG_VERBOSE, "FmpCapsule:\n")); + DEBUG((DEBUG_VERBOSE, " Version - 0x%x\n", FmpCapsuleHeader->Version)); + DEBUG((DEBUG_VERBOSE, " EmbeddedDriverCount - 0x%x\n", FmpCapsuleHeader->EmbeddedDriverCount)); + DEBUG((DEBUG_VERBOSE, " PayloadItemCount - 0x%x\n", FmpCapsuleHeader->PayloadItemCount)); + + ItemOffsetList = (UINT64 *)(FmpCapsuleHeader + 1); + for (Index = 0; Index < FmpCapsuleHeader->EmbeddedDriverCount; Index++) { + DEBUG((DEBUG_VERBOSE, " ItemOffsetList[%d] - 0x%lx\n", Index, ItemOffsetList[Index])); + } + for (; Index < (UINT32)FmpCapsuleHeader->EmbeddedDriverCount + FmpCapsuleHeader->PayloadItemCount; Index++) { + DEBUG((DEBUG_VERBOSE, " ItemOffsetList[%d] - 0x%lx\n", Index, ItemOffsetList[Index])); + ImageHeader = (EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *)((UINT8 *)FmpCapsuleHeader + ItemOffsetList[Index]); + + DEBUG((DEBUG_VERBOSE, " ImageHeader:\n")); + DEBUG((DEBUG_VERBOSE, " Version - 0x%x\n", ImageHeader->Version)); + DEBUG((DEBUG_VERBOSE, " UpdateImageTypeId - %g\n", &ImageHeader->UpdateImageTypeId)); + DEBUG((DEBUG_VERBOSE, " UpdateImageIndex - 0x%x\n", ImageHeader->UpdateImageIndex)); + DEBUG((DEBUG_VERBOSE, " UpdateImageSize - 0x%x\n", ImageHeader->UpdateImageSize)); + DEBUG((DEBUG_VERBOSE, " UpdateVendorCodeSize - 0x%x\n", ImageHeader->UpdateVendorCodeSize)); + if (ImageHeader->Version >= 2) { + DEBUG((DEBUG_VERBOSE, " UpdateHardwareInstance - 0x%lx\n", ImageHeader->UpdateHardwareInstance)); + if (ImageHeader->Version >= EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER_INIT_VERSION) { + DEBUG((DEBUG_VERBOSE, " ImageCapsuleSupport - 0x%lx\n", ImageHeader->ImageCapsuleSupport)); + } + } + } +} + +/** + Dump all FMP information. +**/ +VOID +DumpAllFmpInfo ( + VOID + ) +{ + EFI_STATUS Status; + EFI_HANDLE *HandleBuffer; + UINTN NumberOfHandles; + EFI_FIRMWARE_MANAGEMENT_PROTOCOL *Fmp; + UINTN Index; + UINTN ImageInfoSize; + EFI_FIRMWARE_IMAGE_DESCRIPTOR *FmpImageInfoBuf; + UINT32 FmpImageInfoDescriptorVer; + UINT8 FmpImageInfoCount; + UINTN DescriptorSize; + UINT32 PackageVersion; + CHAR16 *PackageVersionName; + + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiFirmwareManagementProtocolGuid, + NULL, + &NumberOfHandles, + &HandleBuffer + ); + if (EFI_ERROR(Status)) { + return ; + } + + for (Index = 0; Index < NumberOfHandles; Index++) { + Status = gBS->HandleProtocol( + HandleBuffer[Index], + &gEfiFirmwareManagementProtocolGuid, + (VOID **)&Fmp + ); + if (EFI_ERROR(Status)) { + continue; + } + + ImageInfoSize = 0; + Status = Fmp->GetImageInfo ( + Fmp, + &ImageInfoSize, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL + ); + if (Status != EFI_BUFFER_TOO_SMALL) { + continue; + } + + FmpImageInfoBuf = AllocateZeroPool (ImageInfoSize); + if (FmpImageInfoBuf == NULL) { + continue; + } + + PackageVersionName = NULL; + Status = Fmp->GetImageInfo ( + Fmp, + &ImageInfoSize, // ImageInfoSize + FmpImageInfoBuf, // ImageInfo + &FmpImageInfoDescriptorVer, // DescriptorVersion + &FmpImageInfoCount, // DescriptorCount + &DescriptorSize, // DescriptorSize + &PackageVersion, // PackageVersion + &PackageVersionName // PackageVersionName + ); + if (EFI_ERROR(Status)) { + FreePool(FmpImageInfoBuf); + continue; + } + + DEBUG((DEBUG_INFO, "FMP (%d) ImageInfo:\n", Index)); + DumpFmpImageInfo( + ImageInfoSize, // ImageInfoSize + FmpImageInfoBuf, // ImageInfo + FmpImageInfoDescriptorVer, // DescriptorVersion + FmpImageInfoCount, // DescriptorCount + DescriptorSize, // DescriptorSize + PackageVersion, // PackageVersion + PackageVersionName // PackageVersionName + ); + + if (PackageVersionName != NULL) { + FreePool(PackageVersionName); + } + + FreePool(FmpImageInfoBuf); + } + + FreePool (HandleBuffer); + + return ; +} + +/** + Get FMP handle by ImageTypeId and HardwareInstance. + + @param[in] UpdateImageTypeId Used to identify device firmware targeted by this update. + @param[in] UpdateHardwareInstance The HardwareInstance to target with this update. + @param[out] NoHandles The number of handles returned in HandleBuf. + @param[out] HandleBuf A pointer to the buffer to return the requested array of handles. + @param[out] ResetRequiredBuf A pointer to the buffer to return reset required flag for + the requested array of handles. + + @retval EFI_SUCCESS The array of handles and their reset required flag were returned in + HandleBuf and ResetRequiredBuf, and the number of handles in HandleBuf + was returned in NoHandles. + @retval EFI_NOT_FOUND No handles match the search. + @retval EFI_OUT_OF_RESOURCES There is not enough pool memory to store the matching results. +**/ +EFI_STATUS +GetFmpHandleBufferByType ( + IN EFI_GUID *UpdateImageTypeId, + IN UINT64 UpdateHardwareInstance, + OUT UINTN *NoHandles, OPTIONAL + OUT EFI_HANDLE **HandleBuf, OPTIONAL + OUT BOOLEAN **ResetRequiredBuf OPTIONAL + ) +{ + EFI_STATUS Status; + EFI_HANDLE *HandleBuffer; + UINTN NumberOfHandles; + EFI_HANDLE *MatchedHandleBuffer; + BOOLEAN *MatchedResetRequiredBuffer; + UINTN MatchedNumberOfHandles; + EFI_FIRMWARE_MANAGEMENT_PROTOCOL *Fmp; + UINTN Index; + UINTN ImageInfoSize; + EFI_FIRMWARE_IMAGE_DESCRIPTOR *FmpImageInfoBuf; + UINT32 FmpImageInfoDescriptorVer; + UINT8 FmpImageInfoCount; + UINTN DescriptorSize; + UINT32 PackageVersion; + CHAR16 *PackageVersionName; + UINTN Index2; + EFI_FIRMWARE_IMAGE_DESCRIPTOR *TempFmpImageInfo; + + if (NoHandles != NULL) { + *NoHandles = 0; + } + if (HandleBuf != NULL) { + *HandleBuf = NULL; + } + if (ResetRequiredBuf != NULL) { + *ResetRequiredBuf = NULL; + } + + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiFirmwareManagementProtocolGuid, + NULL, + &NumberOfHandles, + &HandleBuffer + ); + if (EFI_ERROR(Status)) { + return Status; + } + + MatchedNumberOfHandles = 0; + + MatchedHandleBuffer = NULL; + if (HandleBuf != NULL) { + MatchedHandleBuffer = AllocateZeroPool (sizeof(EFI_HANDLE) * NumberOfHandles); + if (MatchedHandleBuffer == NULL) { + FreePool (HandleBuffer); + return EFI_OUT_OF_RESOURCES; + } + } + + MatchedResetRequiredBuffer = NULL; + if (ResetRequiredBuf != NULL) { + MatchedResetRequiredBuffer = AllocateZeroPool (sizeof(BOOLEAN) * NumberOfHandles); + if (MatchedResetRequiredBuffer == NULL) { + if (MatchedHandleBuffer != NULL) { + FreePool (MatchedHandleBuffer); + } + FreePool (HandleBuffer); + return EFI_OUT_OF_RESOURCES; + } + } + + for (Index = 0; Index < NumberOfHandles; Index++) { + Status = gBS->HandleProtocol( + HandleBuffer[Index], + &gEfiFirmwareManagementProtocolGuid, + (VOID **)&Fmp + ); + if (EFI_ERROR(Status)) { + continue; + } + + ImageInfoSize = 0; + Status = Fmp->GetImageInfo ( + Fmp, + &ImageInfoSize, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL + ); + if (Status != EFI_BUFFER_TOO_SMALL) { + continue; + } + + FmpImageInfoBuf = AllocateZeroPool (ImageInfoSize); + if (FmpImageInfoBuf == NULL) { + continue; + } + + PackageVersionName = NULL; + Status = Fmp->GetImageInfo ( + Fmp, + &ImageInfoSize, // ImageInfoSize + FmpImageInfoBuf, // ImageInfo + &FmpImageInfoDescriptorVer, // DescriptorVersion + &FmpImageInfoCount, // DescriptorCount + &DescriptorSize, // DescriptorSize + &PackageVersion, // PackageVersion + &PackageVersionName // PackageVersionName + ); + if (EFI_ERROR(Status)) { + FreePool(FmpImageInfoBuf); + continue; + } + + if (PackageVersionName != NULL) { + FreePool(PackageVersionName); + } + + TempFmpImageInfo = FmpImageInfoBuf; + for (Index2 = 0; Index2 < FmpImageInfoCount; Index2++) { + // + // Check if this FMP instance matches + // + if (CompareGuid(UpdateImageTypeId, &TempFmpImageInfo->ImageTypeId)) { + if ((UpdateHardwareInstance == 0) || + ((FmpImageInfoDescriptorVer >= EFI_FIRMWARE_IMAGE_DESCRIPTOR_VERSION) && + (UpdateHardwareInstance == TempFmpImageInfo->HardwareInstance))) { + if (MatchedHandleBuffer != NULL) { + MatchedHandleBuffer[MatchedNumberOfHandles] = HandleBuffer[Index]; + } + if (MatchedResetRequiredBuffer != NULL) { + MatchedResetRequiredBuffer[MatchedNumberOfHandles] = (((TempFmpImageInfo->AttributesSupported & + IMAGE_ATTRIBUTE_RESET_REQUIRED) != 0) && + ((TempFmpImageInfo->AttributesSetting & + IMAGE_ATTRIBUTE_RESET_REQUIRED) != 0)); + } + MatchedNumberOfHandles++; + break; + } + } + TempFmpImageInfo = (EFI_FIRMWARE_IMAGE_DESCRIPTOR *)((UINT8 *)TempFmpImageInfo + DescriptorSize); + } + FreePool(FmpImageInfoBuf); + } + + FreePool (HandleBuffer); + + if (MatchedNumberOfHandles == 0) { + return EFI_NOT_FOUND; + } + + if (NoHandles != NULL) { + *NoHandles = MatchedNumberOfHandles; + } + if (HandleBuf != NULL) { + *HandleBuf = MatchedHandleBuffer; + } + if (ResetRequiredBuf != NULL) { + *ResetRequiredBuf = MatchedResetRequiredBuffer; + } + + return EFI_SUCCESS; +} + +/** + Return FmpImageInfoDescriptorVer by an FMP handle. + + @param[in] Handle A FMP handle. + + @return FmpImageInfoDescriptorVer associated with the FMP. +**/ +UINT32 +GetFmpImageInfoDescriptorVer ( + IN EFI_HANDLE Handle + ) +{ + EFI_STATUS Status; + EFI_FIRMWARE_MANAGEMENT_PROTOCOL *Fmp; + UINTN ImageInfoSize; + EFI_FIRMWARE_IMAGE_DESCRIPTOR *FmpImageInfoBuf; + UINT32 FmpImageInfoDescriptorVer; + UINT8 FmpImageInfoCount; + UINTN DescriptorSize; + UINT32 PackageVersion; + CHAR16 *PackageVersionName; + + Status = gBS->HandleProtocol( + Handle, + &gEfiFirmwareManagementProtocolGuid, + (VOID **)&Fmp + ); + if (EFI_ERROR(Status)) { + return 0; + } + + ImageInfoSize = 0; + Status = Fmp->GetImageInfo ( + Fmp, + &ImageInfoSize, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL + ); + if (Status != EFI_BUFFER_TOO_SMALL) { + return 0; + } + + FmpImageInfoBuf = AllocateZeroPool (ImageInfoSize); + if (FmpImageInfoBuf == NULL) { + return 0; + } + + PackageVersionName = NULL; + Status = Fmp->GetImageInfo ( + Fmp, + &ImageInfoSize, // ImageInfoSize + FmpImageInfoBuf, // ImageInfo + &FmpImageInfoDescriptorVer, // DescriptorVersion + &FmpImageInfoCount, // DescriptorCount + &DescriptorSize, // DescriptorSize + &PackageVersion, // PackageVersion + &PackageVersionName // PackageVersionName + ); + if (EFI_ERROR(Status)) { + FreePool(FmpImageInfoBuf); + return 0; + } + return FmpImageInfoDescriptorVer; +} + +/** + Set FMP image data. + + @param[in] Handle A FMP handle. + @param[in] ImageHeader The payload image header. + @param[in] PayloadIndex The index of the payload. + + @return The status of FMP->SetImage. +**/ +EFI_STATUS +SetFmpImageData ( + IN EFI_HANDLE Handle, + IN EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *ImageHeader, + IN UINTN PayloadIndex + ) +{ + EFI_STATUS Status; + EFI_FIRMWARE_MANAGEMENT_PROTOCOL *Fmp; + UINT8 *Image; + VOID *VendorCode; + CHAR16 *AbortReason; + EFI_FIRMWARE_MANAGEMENT_UPDATE_IMAGE_PROGRESS ProgressCallback; + + Status = gBS->HandleProtocol( + Handle, + &gEfiFirmwareManagementProtocolGuid, + (VOID **)&Fmp + ); + if (EFI_ERROR(Status)) { + return Status; + } + + // + // Lookup Firmware Management Progress Protocol before SetImage() is called + // This is an optional protocol that may not be present on Handle. + // + Status = gBS->HandleProtocol ( + Handle, + &gEdkiiFirmwareManagementProgressProtocolGuid, + (VOID **)&mFmpProgress + ); + if (EFI_ERROR (Status)) { + mFmpProgress = NULL; + } + + if (ImageHeader->Version >= EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER_INIT_VERSION) { + Image = (UINT8 *)(ImageHeader + 1); + } else { + // + // If the EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER is version 1, + // Header should exclude UpdateHardwareInstance field, and + // ImageCapsuleSupport field if version is 2. + // + if (ImageHeader->Version == 1) { + Image = (UINT8 *)ImageHeader + OFFSET_OF(EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER, UpdateHardwareInstance); + } else { + Image = (UINT8 *)ImageHeader + OFFSET_OF(EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER, ImageCapsuleSupport); + } + } + + if (ImageHeader->UpdateVendorCodeSize == 0) { + VendorCode = NULL; + } else { + VendorCode = Image + ImageHeader->UpdateImageSize; + } + AbortReason = NULL; + DEBUG((DEBUG_INFO, "Fmp->SetImage ...\n")); + DEBUG((DEBUG_INFO, "ImageTypeId - %g, ", &ImageHeader->UpdateImageTypeId)); + DEBUG((DEBUG_INFO, "PayloadIndex - 0x%x, ", PayloadIndex)); + DEBUG((DEBUG_INFO, "ImageIndex - 0x%x ", ImageHeader->UpdateImageIndex)); + if (ImageHeader->Version >= 2) { + DEBUG((DEBUG_INFO, "(UpdateHardwareInstance - 0x%x)", ImageHeader->UpdateHardwareInstance)); + if (ImageHeader->Version >= EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER_INIT_VERSION) { + DEBUG((DEBUG_INFO, "(ImageCapsuleSupport - 0x%x)", ImageHeader->ImageCapsuleSupport)); + } + } + DEBUG((DEBUG_INFO, "\n")); + + // + // Before calling SetImage(), reset the progress bar to 0% + // + ProgressCallback = UpdateImageProgress; + Status = UpdateImageProgress (0); + if (EFI_ERROR (Status)) { + ProgressCallback = NULL; + } + + Status = Fmp->SetImage( + Fmp, + ImageHeader->UpdateImageIndex, // ImageIndex + Image, // Image + ImageHeader->UpdateImageSize, // ImageSize + VendorCode, // VendorCode + ProgressCallback, // Progress + &AbortReason // AbortReason + ); + // + // Set the progress bar to 100% after returning from SetImage() + // + if (ProgressCallback != NULL) { + UpdateImageProgress (100); + } + + DEBUG((DEBUG_INFO, "Fmp->SetImage - %r\n", Status)); + if (AbortReason != NULL) { + DEBUG ((DEBUG_ERROR, "%s\n", AbortReason)); + FreePool(AbortReason); + } + + // + // Clear mFmpProgress after SetImage() returns + // + mFmpProgress = NULL; + + return Status; +} + +/** + Start a UEFI image in the FMP payload. + + @param[in] ImageBuffer A pointer to the memory location containing a copy of the image to be loaded.. + @param[in] ImageSize The size in bytes of ImageBuffer. + + @return The status of gBS->LoadImage and gBS->StartImage. +**/ +EFI_STATUS +StartFmpImage ( + IN VOID *ImageBuffer, + IN UINTN ImageSize + ) +{ + MEMMAP_DEVICE_PATH MemMapNode; + EFI_STATUS Status; + EFI_HANDLE ImageHandle; + EFI_DEVICE_PATH_PROTOCOL *DriverDevicePath; + UINTN ExitDataSize; + + SetDevicePathNodeLength (&MemMapNode.Header, sizeof (MemMapNode)); + MemMapNode.Header.Type = HARDWARE_DEVICE_PATH; + MemMapNode.Header.SubType = HW_MEMMAP_DP; + MemMapNode.MemoryType = EfiBootServicesCode; + MemMapNode.StartingAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)ImageBuffer; + MemMapNode.EndingAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)((UINT8 *)ImageBuffer + ImageSize - 1); + + DriverDevicePath = AppendDevicePathNode (NULL, &MemMapNode.Header); + if (DriverDevicePath == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + DEBUG((DEBUG_INFO, "FmpCapsule: LoadImage ...\n")); + Status = gBS->LoadImage( + FALSE, + gImageHandle, + DriverDevicePath, + ImageBuffer, + ImageSize, + &ImageHandle + ); + DEBUG((DEBUG_INFO, "FmpCapsule: LoadImage - %r\n", Status)); + if (EFI_ERROR(Status)) { + // + // With EFI_SECURITY_VIOLATION retval, the Image was loaded and an ImageHandle was created + // with a valid EFI_LOADED_IMAGE_PROTOCOL, but the image can not be started right now. + // If the caller doesn't have the option to defer the execution of an image, we should + // unload image for the EFI_SECURITY_VIOLATION to avoid resource leak. + // + if (Status == EFI_SECURITY_VIOLATION) { + gBS->UnloadImage (ImageHandle); + } + FreePool(DriverDevicePath); + return Status; + } + + DEBUG((DEBUG_INFO, "FmpCapsule: StartImage ...\n")); + Status = gBS->StartImage( + ImageHandle, + &ExitDataSize, + NULL + ); + DEBUG((DEBUG_INFO, "FmpCapsule: StartImage - %r\n", Status)); + if (EFI_ERROR(Status)) { + DEBUG ((DEBUG_ERROR, "Driver Return Status = %r\n", Status)); + } + + FreePool(DriverDevicePath); + return Status; +} + +/** + Record FMP capsule status. + + @param[in] Handle A FMP handle. + @param[in] CapsuleHeader The capsule image header + @param[in] CapsuleStatus The capsule process stauts + @param[in] PayloadIndex FMP payload index + @param[in] ImageHeader FMP image header + @param[in] CapFileName Capsule file name +**/ +VOID +RecordFmpCapsuleStatus ( + IN EFI_HANDLE Handle, OPTIONAL + IN EFI_CAPSULE_HEADER *CapsuleHeader, + IN EFI_STATUS CapsuleStatus, + IN UINTN PayloadIndex, + IN EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *ImageHeader, + IN CHAR16 *CapFileName OPTIONAL + ) +{ + EFI_STATUS Status; + EFI_DEVICE_PATH_PROTOCOL *FmpDevicePath; + UINT32 FmpImageInfoDescriptorVer; + EFI_STATUS StatusEsrt; + ESRT_MANAGEMENT_PROTOCOL *EsrtProtocol; + EFI_SYSTEM_RESOURCE_ENTRY EsrtEntry; + + FmpDevicePath = NULL; + if (Handle != NULL) { + gBS->HandleProtocol( + Handle, + &gEfiDevicePathProtocolGuid, + (VOID **)&FmpDevicePath + ); + } + + RecordFmpCapsuleStatusVariable ( + CapsuleHeader, + CapsuleStatus, + PayloadIndex, + ImageHeader, + FmpDevicePath, + CapFileName + ); + + // + // Update corresponding ESRT entry LastAttemp Status + // + Status = gBS->LocateProtocol(&gEsrtManagementProtocolGuid, NULL, (VOID **)&EsrtProtocol); + if (EFI_ERROR (Status)) { + return ; + } + + if (Handle == NULL) { + return ; + } + + // + // Update EsrtEntry For V1, V2 FMP instance. + // V3 FMP ESRT cache will be synced up through SyncEsrtFmp interface + // + FmpImageInfoDescriptorVer = GetFmpImageInfoDescriptorVer (Handle); + if (FmpImageInfoDescriptorVer < EFI_FIRMWARE_IMAGE_DESCRIPTOR_VERSION) { + StatusEsrt = EsrtProtocol->GetEsrtEntry(&ImageHeader->UpdateImageTypeId, &EsrtEntry); + if (!EFI_ERROR(StatusEsrt)){ + if (!EFI_ERROR(CapsuleStatus)) { + EsrtEntry.LastAttemptStatus = LAST_ATTEMPT_STATUS_SUCCESS; + } else { + EsrtEntry.LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_UNSUCCESSFUL; + } + EsrtEntry.LastAttemptVersion = 0; + EsrtProtocol->UpdateEsrtEntry(&EsrtEntry); + } + } +} + +/** + Process Firmware management protocol data capsule. + + This function assumes the caller validated the capsule by using + ValidateFmpCapsule(), so that all fields in EFI_CAPSULE_HEADER, + EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER and + EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER are correct. + + This function need support nested FMP capsule. + + @param[in] CapsuleHeader Points to a capsule header. + @param[in] CapFileName Capsule file name. + @param[out] ResetRequired Indicates whether reset is required or not. + + @retval EFI_SUCESS Process Capsule Image successfully. + @retval EFI_UNSUPPORTED Capsule image is not supported by the firmware. + @retval EFI_VOLUME_CORRUPTED FV volume in the capsule is corrupted. + @retval EFI_OUT_OF_RESOURCES Not enough memory. + @retval EFI_NOT_READY No FMP protocol to handle this FMP capsule. +**/ +EFI_STATUS +ProcessFmpCapsuleImage ( + IN EFI_CAPSULE_HEADER *CapsuleHeader, + IN CHAR16 *CapFileName, OPTIONAL + OUT BOOLEAN *ResetRequired OPTIONAL + ) +{ + EFI_STATUS Status; + EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER *FmpCapsuleHeader; + EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *ImageHeader; + UINT64 *ItemOffsetList; + UINT32 ItemNum; + UINTN Index; + EFI_HANDLE *HandleBuffer; + BOOLEAN *ResetRequiredBuffer; + UINTN NumberOfHandles; + UINTN DriverLen; + UINT64 UpdateHardwareInstance; + UINTN Index2; + BOOLEAN NotReady; + BOOLEAN Abort; + + if (!IsFmpCapsuleGuid(&CapsuleHeader->CapsuleGuid)) { + return ProcessFmpCapsuleImage ((EFI_CAPSULE_HEADER *)((UINTN)CapsuleHeader + CapsuleHeader->HeaderSize), CapFileName, ResetRequired); + } + + NotReady = FALSE; + Abort = FALSE; + + DumpFmpCapsule(CapsuleHeader); + + FmpCapsuleHeader = (EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER *) ((UINT8 *) CapsuleHeader + CapsuleHeader->HeaderSize); + + if (FmpCapsuleHeader->Version > EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER_INIT_VERSION) { + return EFI_INVALID_PARAMETER; + } + ItemOffsetList = (UINT64 *)(FmpCapsuleHeader + 1); + + ItemNum = FmpCapsuleHeader->EmbeddedDriverCount + FmpCapsuleHeader->PayloadItemCount; + + // + // capsule in which driver count and payload count are both zero is not processed. + // + if (ItemNum == 0) { + return EFI_SUCCESS; + } + + // + // 1. Try to load & start all the drivers within capsule + // + for (Index = 0; Index < FmpCapsuleHeader->EmbeddedDriverCount; Index++) { + if ((FmpCapsuleHeader->PayloadItemCount == 0) && + (Index == (UINTN)FmpCapsuleHeader->EmbeddedDriverCount - 1)) { + // + // When driver is last element in the ItemOffsetList array, the driver size is calculated by reference CapsuleImageSize in EFI_CAPSULE_HEADER + // + DriverLen = CapsuleHeader->CapsuleImageSize - CapsuleHeader->HeaderSize - (UINTN)ItemOffsetList[Index]; + } else { + DriverLen = (UINTN)ItemOffsetList[Index + 1] - (UINTN)ItemOffsetList[Index]; + } + + Status = StartFmpImage ( + (UINT8 *)FmpCapsuleHeader + ItemOffsetList[Index], + DriverLen + ); + if (EFI_ERROR(Status)) { + DEBUG ((DEBUG_ERROR, "Driver Return Status = %r\n", Status)); + return Status; + } + } + + // + // 2. Route payload to right FMP instance + // + DEBUG((DEBUG_INFO, "FmpCapsule: route payload to right FMP instance ...\n")); + + DumpAllFmpInfo (); + + // + // Check all the payload entry in capsule payload list + // + for (Index = FmpCapsuleHeader->EmbeddedDriverCount; Index < ItemNum; Index++) { + ImageHeader = (EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *)((UINT8 *)FmpCapsuleHeader + ItemOffsetList[Index]); + + UpdateHardwareInstance = 0; + /// + /// UpdateHardwareInstance field was added in Version 2 + /// + if (ImageHeader->Version >= 2) { + UpdateHardwareInstance = ImageHeader->UpdateHardwareInstance; + } + + Status = GetFmpHandleBufferByType ( + &ImageHeader->UpdateImageTypeId, + UpdateHardwareInstance, + &NumberOfHandles, + &HandleBuffer, + &ResetRequiredBuffer + ); + if (EFI_ERROR(Status) || + (HandleBuffer == NULL) || + (ResetRequiredBuffer == NULL)) { + NotReady = TRUE; + RecordFmpCapsuleStatus ( + NULL, + CapsuleHeader, + EFI_NOT_READY, + Index - FmpCapsuleHeader->EmbeddedDriverCount, + ImageHeader, + CapFileName + ); + continue; + } + + for (Index2 = 0; Index2 < NumberOfHandles; Index2++) { + if (Abort) { + RecordFmpCapsuleStatus ( + HandleBuffer[Index2], + CapsuleHeader, + EFI_ABORTED, + Index - FmpCapsuleHeader->EmbeddedDriverCount, + ImageHeader, + CapFileName + ); + continue; + } + + Status = SetFmpImageData ( + HandleBuffer[Index2], + ImageHeader, + Index - FmpCapsuleHeader->EmbeddedDriverCount + ); + if (Status != EFI_SUCCESS) { + Abort = TRUE; + } else { + if (ResetRequired != NULL) { + *ResetRequired |= ResetRequiredBuffer[Index2]; + } + } + + RecordFmpCapsuleStatus ( + HandleBuffer[Index2], + CapsuleHeader, + Status, + Index - FmpCapsuleHeader->EmbeddedDriverCount, + ImageHeader, + CapFileName + ); + } + if (HandleBuffer != NULL) { + FreePool(HandleBuffer); + } + if (ResetRequiredBuffer != NULL) { + FreePool(ResetRequiredBuffer); + } + } + + if (NotReady) { + return EFI_NOT_READY; + } + + // + // always return SUCCESS to indicate this capsule is processed. + // The status of SetImage is recorded in capsule result variable. + // + return EFI_SUCCESS; +} + +/** + Return if there is a FMP header below capsule header. + + @param[in] CapsuleHeader A pointer to EFI_CAPSULE_HEADER + + @retval TRUE There is a FMP header below capsule header. + @retval FALSE There is not a FMP header below capsule header +**/ +BOOLEAN +IsNestedFmpCapsule ( + IN EFI_CAPSULE_HEADER *CapsuleHeader + ) +{ + EFI_STATUS Status; + EFI_SYSTEM_RESOURCE_ENTRY *EsrtEntry; + UINTN Index; + BOOLEAN EsrtGuidFound; + EFI_CAPSULE_HEADER *NestedCapsuleHeader; + UINTN NestedCapsuleSize; + ESRT_MANAGEMENT_PROTOCOL *EsrtProtocol; + EFI_SYSTEM_RESOURCE_ENTRY Entry; + + EsrtGuidFound = FALSE; + if (mIsVirtualAddrConverted) { + if(mEsrtTable != NULL) { + EsrtEntry = (EFI_SYSTEM_RESOURCE_ENTRY *)(mEsrtTable + 1); + for (Index = 0; Index < mEsrtTable->FwResourceCount ; Index++, EsrtEntry++) { + if (CompareGuid(&EsrtEntry->FwClass, &CapsuleHeader->CapsuleGuid)) { + EsrtGuidFound = TRUE; + break; + } + } + } + } else { + // + // Check ESRT protocol + // + Status = gBS->LocateProtocol(&gEsrtManagementProtocolGuid, NULL, (VOID **)&EsrtProtocol); + if (!EFI_ERROR(Status)) { + Status = EsrtProtocol->GetEsrtEntry(&CapsuleHeader->CapsuleGuid, &Entry); + if (!EFI_ERROR(Status)) { + EsrtGuidFound = TRUE; + } + } + + // + // Check Firmware Management Protocols + // + if (!EsrtGuidFound) { + Status = GetFmpHandleBufferByType ( + &CapsuleHeader->CapsuleGuid, + 0, + NULL, + NULL, + NULL + ); + if (!EFI_ERROR(Status)) { + EsrtGuidFound = TRUE; + } + } + } + if (!EsrtGuidFound) { + return FALSE; + } + + // + // Check nested capsule header + // FMP GUID after ESRT one + // + NestedCapsuleHeader = (EFI_CAPSULE_HEADER *)((UINT8 *)CapsuleHeader + CapsuleHeader->HeaderSize); + NestedCapsuleSize = (UINTN)CapsuleHeader + CapsuleHeader->CapsuleImageSize - (UINTN)NestedCapsuleHeader; + if (NestedCapsuleSize < sizeof(EFI_CAPSULE_HEADER)) { + return FALSE; + } + if (!IsValidCapsuleHeader(NestedCapsuleHeader, NestedCapsuleSize)) { + return FALSE; + } + if (!IsFmpCapsuleGuid(&NestedCapsuleHeader->CapsuleGuid)) { + return FALSE; + } + DEBUG ((DEBUG_INFO, "IsNestedFmpCapsule\n")); + return TRUE; +} + +/** + Return if this FMP is a system FMP or a device FMP, based upon CapsuleHeader. + + @param[in] CapsuleHeader A pointer to EFI_CAPSULE_HEADER + + @retval TRUE It is a system FMP. + @retval FALSE It is a device FMP. +**/ +BOOLEAN +IsFmpCapsule ( + IN EFI_CAPSULE_HEADER *CapsuleHeader + ) +{ + if (IsFmpCapsuleGuid(&CapsuleHeader->CapsuleGuid)) { + return TRUE; + } + if (IsNestedFmpCapsule(CapsuleHeader)) { + return TRUE; + } + return FALSE; +} + +/** + Those capsules supported by the firmwares. + + Caution: This function may receive untrusted input. + + @param[in] CapsuleHeader Points to a capsule header. + + @retval EFI_SUCESS Input capsule is supported by firmware. + @retval EFI_UNSUPPORTED Input capsule is not supported by the firmware. + @retval EFI_INVALID_PARAMETER Input capsule layout is not correct +**/ +EFI_STATUS +EFIAPI +SupportCapsuleImage ( + IN EFI_CAPSULE_HEADER *CapsuleHeader + ) +{ + // + // check Display Capsule Guid + // + if (CompareGuid (&gWindowsUxCapsuleGuid, &CapsuleHeader->CapsuleGuid)) { + return EFI_SUCCESS; + } + + // + // Check capsule file name capsule + // + if (IsCapsuleNameCapsule(CapsuleHeader)) { + return EFI_SUCCESS; + } + + if (IsFmpCapsule(CapsuleHeader)) { + // + // Fake capsule header is valid case in QueryCapsuleCpapbilities(). + // + if (CapsuleHeader->HeaderSize == CapsuleHeader->CapsuleImageSize) { + return EFI_SUCCESS; + } + // + // Check layout of FMP capsule + // + return ValidateFmpCapsule(CapsuleHeader, NULL); + } + DEBUG((DEBUG_ERROR, "Unknown Capsule Guid - %g\n", &CapsuleHeader->CapsuleGuid)); + return EFI_UNSUPPORTED; +} + +/** + The firmware implements to process the capsule image. + + Caution: This function may receive untrusted input. + + @param[in] CapsuleHeader Points to a capsule header. + @param[in] CapFileName Capsule file name. + @param[out] ResetRequired Indicates whether reset is required or not. + + @retval EFI_SUCESS Process Capsule Image successfully. + @retval EFI_UNSUPPORTED Capsule image is not supported by the firmware. + @retval EFI_VOLUME_CORRUPTED FV volume in the capsule is corrupted. + @retval EFI_OUT_OF_RESOURCES Not enough memory. +**/ +EFI_STATUS +EFIAPI +ProcessThisCapsuleImage ( + IN EFI_CAPSULE_HEADER *CapsuleHeader, + IN CHAR16 *CapFileName, OPTIONAL + OUT BOOLEAN *ResetRequired OPTIONAL + ) +{ + EFI_STATUS Status; + + if (SupportCapsuleImage (CapsuleHeader) != EFI_SUCCESS) { + RecordCapsuleStatusVariable(CapsuleHeader, EFI_UNSUPPORTED); + return EFI_UNSUPPORTED; + } + + // + // Display image in firmware update display capsule + // + if (CompareGuid (&gWindowsUxCapsuleGuid, &CapsuleHeader->CapsuleGuid)) { + DEBUG((DEBUG_INFO, "ProcessCapsuleImage for WindowsUxCapsule ...\n")); + Status = DisplayCapsuleImage(CapsuleHeader); + RecordCapsuleStatusVariable(CapsuleHeader, Status); + return Status; + } + + // + // Check FMP capsule layout + // + if (IsFmpCapsule (CapsuleHeader)) { + DEBUG((DEBUG_INFO, "ProcessCapsuleImage for FmpCapsule ...\n")); + DEBUG((DEBUG_INFO, "ValidateFmpCapsule ...\n")); + Status = ValidateFmpCapsule(CapsuleHeader, NULL); + DEBUG((DEBUG_INFO, "ValidateFmpCapsule - %r\n", Status)); + if (EFI_ERROR(Status)) { + RecordCapsuleStatusVariable(CapsuleHeader, Status); + return Status; + } + + // + // Process EFI FMP Capsule + // + DEBUG((DEBUG_INFO, "ProcessFmpCapsuleImage ...\n")); + Status = ProcessFmpCapsuleImage(CapsuleHeader, CapFileName, ResetRequired); + DEBUG((DEBUG_INFO, "ProcessFmpCapsuleImage - %r\n", Status)); + + return Status; + } + + return EFI_UNSUPPORTED; +} + +/** + The firmware implements to process the capsule image. + + Caution: This function may receive untrusted input. + + @param[in] CapsuleHeader Points to a capsule header. + + @retval EFI_SUCESS Process Capsule Image successfully. + @retval EFI_UNSUPPORTED Capsule image is not supported by the firmware. + @retval EFI_VOLUME_CORRUPTED FV volume in the capsule is corrupted. + @retval EFI_OUT_OF_RESOURCES Not enough memory. +**/ +EFI_STATUS +EFIAPI +ProcessCapsuleImage ( + IN EFI_CAPSULE_HEADER *CapsuleHeader + ) +{ + return ProcessThisCapsuleImage (CapsuleHeader, NULL, NULL); +} + +/** + Callback function executed when the EndOfDxe event group is signaled. + + @param[in] Event Event whose notification function is being invoked. + @param[in] Context The pointer to the notification function's context, which + is implementation-dependent. +**/ +VOID +EFIAPI +DxeCapsuleLibEndOfDxe ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + mDxeCapsuleLibEndOfDxe = TRUE; +} + +/** + The constructor function. + + @param[in] ImageHandle The firmware allocated handle for the EFI image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The constructor successfully . +**/ +EFI_STATUS +EFIAPI +DxeCapsuleLibConstructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + Status = gBS->CreateEventEx ( + EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + DxeCapsuleLibEndOfDxe, + NULL, + &gEfiEndOfDxeEventGroupGuid, + &mDxeCapsuleLibEndOfDxeEvent + ); + ASSERT_EFI_ERROR (Status); + + InitCapsuleVariable(); + + return EFI_SUCCESS; +} + +/** + The destructor function closes the End of DXE event. + + @param ImageHandle The firmware allocated handle for the EFI image. + @param SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The destructor completed successfully. +**/ +EFI_STATUS +EFIAPI +DxeCapsuleLibDestructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + // + // Close the End of DXE event. + // + Status = gBS->CloseEvent (mDxeCapsuleLibEndOfDxeEvent); + ASSERT_EFI_ERROR (Status); + + return EFI_SUCCESS; +} |