From: Jiewen Yao Date: Wed, 21 Sep 2016 01:49:12 +0000 (+0800) Subject: MdeModulePkg/DxeCapsuleLibFmp: Add DxeCapsuleLibFmp instance. X-Git-Tag: edk2-stable201903~5293 X-Git-Url: https://git.proxmox.com/?p=mirror_edk2.git;a=commitdiff_plain;h=d2a160304e9358eece635b563dc6a74ec250f1f0;hp=bf468a6ca060d8c5214a34175a309a11d1a65aba MdeModulePkg/DxeCapsuleLibFmp: Add DxeCapsuleLibFmp instance. This instance handles Microsoft UX capsule, UEFI defined FMP capsule. This instance should not assume any capsule image format. Cc: Feng Tian Cc: Star Zeng Cc: Michael D Kinney Cc: Liming Gao Cc: Chao Zhang Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Jiewen Yao Reviewed-by: Liming Gao Reviewed-by: Michael Kinney Tested-by: Michael Kinney --- diff --git a/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleLib.c b/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleLib.c new file mode 100644 index 0000000000..b3d31b8a4d --- /dev/null +++ b/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleLib.c @@ -0,0 +1,1364 @@ +/** @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(), DisplayCapsuleImage(), ConvertBmpToGopBlt() will + receive untrusted input and do basic validation. + + Copyright (c) 2016, 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 + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +BOOLEAN mAreAllImagesProcessed; + +EFI_SYSTEM_RESOURCE_TABLE *mEsrtTable = NULL; +BOOLEAN mIsVirtualAddrConverted = FALSE; +BOOLEAN mDxeCapsuleLibEndOfDxe = FALSE; + +/** + Initialize capsule related variables. +**/ +VOID +InitCapsuleVariable ( + VOID + ); + +/** + Check if this FMP capsule is processed. + + @param[in] CapsuleHeader The capsule image header + @param[in] PayloadIndex FMP payload index + @param[in] ImageHeader FMP image header + + @retval TRUE This FMP capsule is processed. + @retval FALSE This FMP capsule is not processed. +**/ +BOOLEAN +IsFmpCapsuleProcessed ( + IN EFI_CAPSULE_HEADER *CapsuleHeader, + IN UINTN PayloadIndex, + IN EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *ImageHeader + ); + +/** + 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 + + @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 + ); + +/** + 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 Input capsule is a correct FMP capsule. +**/ +EFI_STATUS +EFIAPI +Update_Image_Progress ( + IN UINTN Completion + ) +{ + return EFI_SUCCESS; +} + +/** + 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, 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]; + + if (FmpImageSize < OFFSET_OF(EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER, UpdateHardwareInstance)) { + DEBUG((DEBUG_ERROR, "FmpImageSize(0x%lx) < EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER\n", FmpImageSize)); + return EFI_INVALID_PARAMETER; + } + 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 < EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER_INIT_VERSION) { + FmpImageHeaderSize = OFFSET_OF(EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER, UpdateHardwareInstance); + } + + // 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; +} + +/** + Convert a *.BMP graphics image to a GOP blt buffer. If a NULL Blt buffer + is passed in a GopBlt buffer will be allocated by this routine. If a GopBlt + buffer is passed in it will be used if it is big enough. + + Caution: This function may receive untrusted input. + + @param[in] BmpImage Pointer to BMP file + @param[in] BmpImageSize Number of bytes in BmpImage + @param[in, out] GopBlt Buffer containing GOP version of BmpImage. + @param[in, out] GopBltSize Size of GopBlt in bytes. + @param[out] PixelHeight Height of GopBlt/BmpImage in pixels + @param[out] PixelWidth Width of GopBlt/BmpImage in pixels + + @retval EFI_SUCCESS GopBlt and GopBltSize are returned. + @retval EFI_UNSUPPORTED BmpImage is not a valid *.BMP image + @retval EFI_BUFFER_TOO_SMALL The passed in GopBlt buffer is not big enough. + GopBltSize will contain the required size. + @retval EFI_OUT_OF_RESOURCES No enough buffer to allocate. + +**/ +STATIC +EFI_STATUS +ConvertBmpToGopBlt ( + IN VOID *BmpImage, + IN UINTN BmpImageSize, + IN OUT VOID **GopBlt, + IN OUT UINTN *GopBltSize, + OUT UINTN *PixelHeight, + OUT UINTN *PixelWidth + ) +{ + UINT8 *Image; + UINT8 *ImageHeader; + BMP_IMAGE_HEADER *BmpHeader; + BMP_COLOR_MAP *BmpColorMap; + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer; + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *Blt; + UINT64 BltBufferSize; + UINTN Index; + UINTN Height; + UINTN Width; + UINTN ImageIndex; + UINT32 DataSizePerLine; + BOOLEAN IsAllocated; + UINT32 ColorMapNum; + + if (sizeof (BMP_IMAGE_HEADER) > BmpImageSize) { + return EFI_INVALID_PARAMETER; + } + + BmpHeader = (BMP_IMAGE_HEADER *) BmpImage; + + if (BmpHeader->CharB != 'B' || BmpHeader->CharM != 'M') { + return EFI_UNSUPPORTED; + } + + // + // Doesn't support compress. + // + if (BmpHeader->CompressionType != 0) { + return EFI_UNSUPPORTED; + } + + // + // Only support BITMAPINFOHEADER format. + // BITMAPFILEHEADER + BITMAPINFOHEADER = BMP_IMAGE_HEADER + // + if (BmpHeader->HeaderSize != sizeof (BMP_IMAGE_HEADER) - OFFSET_OF(BMP_IMAGE_HEADER, HeaderSize)) { + return EFI_UNSUPPORTED; + } + + // + // The data size in each line must be 4 byte alignment. + // + DataSizePerLine = ((BmpHeader->PixelWidth * BmpHeader->BitPerPixel + 31) >> 3) & (~0x3); + BltBufferSize = MultU64x32 (DataSizePerLine, BmpHeader->PixelHeight); + if (BltBufferSize > (UINT32) ~0) { + return EFI_INVALID_PARAMETER; + } + + if ((BmpHeader->Size != BmpImageSize) || + (BmpHeader->Size < BmpHeader->ImageOffset) || + (BmpHeader->Size - BmpHeader->ImageOffset != BmpHeader->PixelHeight * DataSizePerLine)) { + return EFI_INVALID_PARAMETER; + } + + // + // Calculate Color Map offset in the image. + // + Image = BmpImage; + BmpColorMap = (BMP_COLOR_MAP *) (Image + sizeof (BMP_IMAGE_HEADER)); + if (BmpHeader->ImageOffset < sizeof (BMP_IMAGE_HEADER)) { + return EFI_INVALID_PARAMETER; + } + + if (BmpHeader->ImageOffset > sizeof (BMP_IMAGE_HEADER)) { + switch (BmpHeader->BitPerPixel) { + case 1: + ColorMapNum = 2; + break; + case 4: + ColorMapNum = 16; + break; + case 8: + ColorMapNum = 256; + break; + default: + ColorMapNum = 0; + break; + } + // + // BMP file may has padding data between the bmp header section and the bmp data section. + // + if (BmpHeader->ImageOffset - sizeof (BMP_IMAGE_HEADER) < sizeof (BMP_COLOR_MAP) * ColorMapNum) { + return EFI_INVALID_PARAMETER; + } + } + + // + // Calculate graphics image data address in the image + // + Image = ((UINT8 *) BmpImage) + BmpHeader->ImageOffset; + ImageHeader = Image; + + // + // Calculate the BltBuffer needed size. + // + BltBufferSize = MultU64x32 ((UINT64) BmpHeader->PixelWidth, BmpHeader->PixelHeight); + // + // Ensure the BltBufferSize * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL) doesn't overflow + // + if (BltBufferSize > DivU64x32 ((UINTN) ~0, sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL))) { + return EFI_UNSUPPORTED; + } + BltBufferSize = MultU64x32 (BltBufferSize, sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL)); + + IsAllocated = FALSE; + if (*GopBlt == NULL) { + // + // GopBlt is not allocated by caller. + // + *GopBltSize = (UINTN) BltBufferSize; + *GopBlt = AllocatePool (*GopBltSize); + IsAllocated = TRUE; + if (*GopBlt == NULL) { + return EFI_OUT_OF_RESOURCES; + } + } else { + // + // GopBlt has been allocated by caller. + // + if (*GopBltSize < (UINTN) BltBufferSize) { + *GopBltSize = (UINTN) BltBufferSize; + return EFI_BUFFER_TOO_SMALL; + } + } + + *PixelWidth = BmpHeader->PixelWidth; + *PixelHeight = BmpHeader->PixelHeight; + + // + // Convert image from BMP to Blt buffer format + // + BltBuffer = *GopBlt; + for (Height = 0; Height < BmpHeader->PixelHeight; Height++) { + Blt = &BltBuffer[(BmpHeader->PixelHeight - Height - 1) * BmpHeader->PixelWidth]; + for (Width = 0; Width < BmpHeader->PixelWidth; Width++, Image++, Blt++) { + switch (BmpHeader->BitPerPixel) { + case 1: + // + // Convert 1-bit (2 colors) BMP to 24-bit color + // + for (Index = 0; Index < 8 && Width < BmpHeader->PixelWidth; Index++) { + Blt->Red = BmpColorMap[((*Image) >> (7 - Index)) & 0x1].Red; + Blt->Green = BmpColorMap[((*Image) >> (7 - Index)) & 0x1].Green; + Blt->Blue = BmpColorMap[((*Image) >> (7 - Index)) & 0x1].Blue; + Blt++; + Width++; + } + + Blt--; + Width--; + break; + + case 4: + // + // Convert 4-bit (16 colors) BMP Palette to 24-bit color + // + Index = (*Image) >> 4; + Blt->Red = BmpColorMap[Index].Red; + Blt->Green = BmpColorMap[Index].Green; + Blt->Blue = BmpColorMap[Index].Blue; + if (Width < (BmpHeader->PixelWidth - 1)) { + Blt++; + Width++; + Index = (*Image) & 0x0f; + Blt->Red = BmpColorMap[Index].Red; + Blt->Green = BmpColorMap[Index].Green; + Blt->Blue = BmpColorMap[Index].Blue; + } + break; + + case 8: + // + // Convert 8-bit (256 colors) BMP Palette to 24-bit color + // + Blt->Red = BmpColorMap[*Image].Red; + Blt->Green = BmpColorMap[*Image].Green; + Blt->Blue = BmpColorMap[*Image].Blue; + break; + + case 24: + // + // It is 24-bit BMP. + // + Blt->Blue = *Image++; + Blt->Green = *Image++; + Blt->Red = *Image; + break; + + case 32: + // + // it is 32-bit BMP. Skip pixel's highest byte + // + Blt->Blue = *Image++; + Blt->Green = *Image++; + Blt->Red = *Image++; + break; + + default: + // + // Other bit format BMP is not supported. + // + if (IsAllocated) { + FreePool (*GopBlt); + *GopBlt = NULL; + } + return EFI_UNSUPPORTED; + }; + + } + + ImageIndex = (UINTN) (Image - ImageHeader); + if ((ImageIndex % 4) != 0) { + // + // Bmp Image starts each row on a 32-bit boundary! + // + Image = Image + (4 - (ImageIndex % 4)); + } + } + + 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; + + ImagePayload = (DISPLAY_DISPLAY_PAYLOAD *)(CapsuleHeader + 1); + PayloadSize = (UINTN)(CapsuleHeader->CapsuleImageSize - sizeof(EFI_CAPSULE_HEADER)); + + 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 = ConvertBmpToGopBlt ( + ImagePayload + 1, + PayloadSize - sizeof(DISPLAY_DISPLAY_PAYLOAD), + (VOID **)&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 < (UINTN)(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 >= EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER_INIT_VERSION) { + DEBUG((DEBUG_VERBOSE, " UpdateHardwareInstance - 0x%lx\n", ImageHeader->UpdateHardwareInstance)); + } + } +} + +/** + 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] AreAllImagesProcessed If all the FMP images in the capsule are processed. + + @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 +ProcessFmpCapsuleImage ( + IN EFI_CAPSULE_HEADER *CapsuleHeader, + OUT BOOLEAN *AreAllImagesProcessed + ) +{ + EFI_STATUS Status; + EFI_STATUS StatusEsrt; + EFI_STATUS StatusRet; + EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER *FmpCapsuleHeader; + EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *ImageHeader; + UINT8 *Image; + EFI_HANDLE ImageHandle; + UINT64 *ItemOffsetList; + UINT32 ItemNum; + UINTN Index; + UINTN ExitDataSize; + EFI_HANDLE *HandleBuffer; + EFI_FIRMWARE_MANAGEMENT_PROTOCOL *Fmp; + UINTN NumberOfHandles; + UINTN DescriptorSize; + UINT8 FmpImageInfoCount; + UINT32 FmpImageInfoDescriptorVer; + UINTN ImageInfoSize; + UINT32 PackageVersion; + CHAR16 *PackageVersionName; + CHAR16 *AbortReason; + EFI_FIRMWARE_IMAGE_DESCRIPTOR *FmpImageInfoBuf; + EFI_FIRMWARE_IMAGE_DESCRIPTOR *TempFmpImageInfo; + UINTN DriverLen; + UINTN Index1; + UINTN Index2; + MEMMAP_DEVICE_PATH MemMapNode; + EFI_DEVICE_PATH_PROTOCOL *DriverDevicePath; + ESRT_MANAGEMENT_PROTOCOL *EsrtProtocol; + EFI_SYSTEM_RESOURCE_ENTRY EsrtEntry; + VOID *VendorCode; + + if (!IsFmpCapsuleGuid(&CapsuleHeader->CapsuleGuid)) { + return ProcessFmpCapsuleImage ((EFI_CAPSULE_HEADER *)((UINTN)CapsuleHeader + CapsuleHeader->HeaderSize), AreAllImagesProcessed); + } + + ASSERT(AreAllImagesProcessed != NULL); + + Status = EFI_SUCCESS; + StatusRet = EFI_NOT_FOUND; + HandleBuffer = NULL; + ExitDataSize = 0; + DriverDevicePath = NULL; + EsrtProtocol = NULL; + *AreAllImagesProcessed = 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) { + *AreAllImagesProcessed = TRUE; + return EFI_SUCCESS; + } + + // + // Update corresponding ESRT entry LastAttemp Status + // + Status = gBS->LocateProtocol(&gEsrtManagementProtocolGuid, NULL, (VOID **)&EsrtProtocol); + if (EFI_ERROR (Status)) { + EsrtProtocol = NULL; + } + + // + // 1. Try to load & start all the drivers within capsule + // + 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)CapsuleHeader; + MemMapNode.EndingAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)((UINT8 *)CapsuleHeader + CapsuleHeader->CapsuleImageSize - 1); + + DriverDevicePath = AppendDevicePathNode (NULL, &MemMapNode.Header); + if (DriverDevicePath == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + 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]; + } + + DEBUG((DEBUG_INFO, "FmpCapsule: LoadImage ...\n")); + Status = gBS->LoadImage( + FALSE, + gImageHandle, + DriverDevicePath, + (UINT8 *)FmpCapsuleHeader + ItemOffsetList[Index], + DriverLen, + &ImageHandle + ); + DEBUG((DEBUG_INFO, "FmpCapsule: LoadImage - %r\n", Status)); + if (EFI_ERROR(Status)) { + StatusRet = Status; + goto EXIT; + } + + 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)); + StatusRet = Status; + goto EXIT; + } + } + + // + // 2. Route payload to right FMP instance + // + DEBUG((DEBUG_INFO, "FmpCapsule: route payload to right FMP instance ...\n")); + + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiFirmwareManagementProtocolGuid, + NULL, + &NumberOfHandles, + &HandleBuffer + ); + + if (!EFI_ERROR(Status)) { + for(Index1 = 0; Index1 < NumberOfHandles; Index1++) { + Status = gBS->HandleProtocol( + HandleBuffer[Index1], + &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 = NULL; + FmpImageInfoBuf = AllocateZeroPool (ImageInfoSize); + if (FmpImageInfoBuf == NULL) { + StatusRet = EFI_OUT_OF_RESOURCES; + goto EXIT; + } + + PackageVersionName = NULL; + Status = Fmp->GetImageInfo ( + Fmp, + &ImageInfoSize, // ImageInfoSize + FmpImageInfoBuf, // ImageInfo + &FmpImageInfoDescriptorVer, // DescriptorVersion + &FmpImageInfoCount, // DescriptorCount + &DescriptorSize, // DescriptorSize + &PackageVersion, // PackageVersion + &PackageVersionName // PackageVersionName + ); + + // + // If FMP GetInformation interface failed, skip this resource + // + 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); + } + + TempFmpImageInfo = FmpImageInfoBuf; + for (Index2 = 0; Index2 < FmpImageInfoCount; Index2++) { + // + // 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]); + + if (IsFmpCapsuleProcessed(CapsuleHeader, Index - FmpCapsuleHeader->EmbeddedDriverCount, ImageHeader)) { + DEBUG((DEBUG_INFO, "FMP Capsule already processed (%g):", CapsuleHeader)); + DEBUG((DEBUG_INFO, "ImageTypeId - %g, ", ImageHeader->UpdateImageTypeId)); + DEBUG((DEBUG_INFO, "PayloadIndex - 0x%x, ImageIndex - 0x%x\n", Index - FmpCapsuleHeader->EmbeddedDriverCount, ImageHeader->UpdateImageIndex)); + continue; + } + + if (CompareGuid(&ImageHeader->UpdateImageTypeId, &TempFmpImageInfo->ImageTypeId) && + ImageHeader->UpdateImageIndex == TempFmpImageInfo->ImageIndex) { + AbortReason = NULL; + if (ImageHeader->Version >= EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER_INIT_VERSION) { + if(ImageHeader->UpdateHardwareInstance != 0){ + // + // FMP Version is >=2 & UpdateHardwareInstance Skip 2 case + // 1. FMP Image info Version < 3 + // 2. HardwareInstance doesn't match + // + if (FmpImageInfoDescriptorVer < EFI_FIRMWARE_IMAGE_DESCRIPTOR_VERSION || + ImageHeader->UpdateHardwareInstance != TempFmpImageInfo->HardwareInstance) { + continue; + } + } + Image = (UINT8 *)(ImageHeader + 1); + } else { + // + // If the EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER is version 1, only match ImageTypeId. + // Header should exclude UpdateHardwareInstance field + // + Image = (UINT8 *)ImageHeader + OFFSET_OF(EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER, UpdateHardwareInstance); + } + + if (ImageHeader->UpdateVendorCodeSize == 0) { + VendorCode = NULL; + } else { + VendorCode = Image + ImageHeader->UpdateImageSize; + } + DEBUG((DEBUG_INFO, "Fmp->SetImage ...\n")); + Status = Fmp->SetImage( + Fmp, + ImageHeader->UpdateImageIndex, // ImageIndex + Image, // Image + ImageHeader->UpdateImageSize, // ImageSize + VendorCode, // VendorCode + Update_Image_Progress, // Progress + &AbortReason // AbortReason + ); + DEBUG((DEBUG_INFO, "Fmp->SetImage - %r\n", Status)); + if (AbortReason != NULL) { + DEBUG ((DEBUG_ERROR, "%s\n", AbortReason)); + FreePool(AbortReason); + } + RecordFmpCapsuleStatusVariable( + CapsuleHeader, // CapsuleGuid + Status, // CapsuleStatus + Index - FmpCapsuleHeader->EmbeddedDriverCount, // PayloadIndex + ImageHeader // ImageHeader + ); + if (StatusRet != EFI_SUCCESS) { + StatusRet = Status; + } + // + // Update EsrtEntry For V1, V2 FMP instance. V3 FMP ESRT cache will be synced up through EsrtSyncFmp interface + // + if (FmpImageInfoDescriptorVer < EFI_FIRMWARE_IMAGE_DESCRIPTOR_VERSION && EsrtProtocol != NULL) { + StatusEsrt = EsrtProtocol->GetEsrtEntry(&TempFmpImageInfo->ImageTypeId, &EsrtEntry); + if (!EFI_ERROR(StatusEsrt)){ + if (!EFI_ERROR(Status)) { + EsrtEntry.LastAttemptStatus = LAST_ATTEMPT_STATUS_SUCCESS; + } else { + EsrtEntry.LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_UNSUCCESSFUL; + } + EsrtEntry.LastAttemptVersion = 0; + EsrtProtocol->UpdateEsrtEntry(&EsrtEntry); + } + } + } + } + // + // Use DescriptorSize to move ImageInfo Pointer to stay compatible with different ImageInfo version + // + TempFmpImageInfo = (EFI_FIRMWARE_IMAGE_DESCRIPTOR *)((UINT8 *)TempFmpImageInfo + DescriptorSize); + } + FreePool(FmpImageInfoBuf); + } + } + + // + // final check for AreAllImagesProcessed + // + *AreAllImagesProcessed = TRUE; + for (Index = FmpCapsuleHeader->EmbeddedDriverCount; Index < ItemNum; Index++) { + ImageHeader = (EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *)((UINT8 *)FmpCapsuleHeader + ItemOffsetList[Index]); + + if (!IsFmpCapsuleProcessed(CapsuleHeader, Index - FmpCapsuleHeader->EmbeddedDriverCount, ImageHeader)) { + *AreAllImagesProcessed = FALSE; + break; + } + } + +EXIT: + + if (HandleBuffer != NULL) { + FreePool(HandleBuffer); + } + + if (DriverDevicePath != NULL) { + FreePool(DriverDevicePath); + } + + return StatusRet; +} + +/** + 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_TABLE *Esrt; + 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; + + // + // 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 ESRT configuration table + // + if (!EsrtGuidFound) { + Status = EfiGetSystemConfigurationTable(&gEfiSystemResourceTableGuid, (VOID **)&Esrt); + if (!EFI_ERROR(Status)) { + ASSERT (Esrt != NULL); + EsrtEntry = (VOID *)(Esrt + 1); + for (Index = 0; Index < Esrt->FwResourceCount; Index++, EsrtEntry++) { + if (CompareGuid(&EsrtEntry->FwClass, &CapsuleHeader->CapsuleGuid)) { + EsrtGuidFound = TRUE; + break; + } + } + } + } + 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; + } + + if (IsFmpCapsule(CapsuleHeader)) { + // + // 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. + + @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 + ) +{ + EFI_STATUS Status; + BOOLEAN AreAllImagesProcessed; + + if (SupportCapsuleImage (CapsuleHeader) != EFI_SUCCESS) { + 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)) { + return Status; + } + + // + // Press EFI FMP Capsule + // + DEBUG((DEBUG_INFO, "ProcessFmpCapsuleImage ...\n")); + Status = ProcessFmpCapsuleImage(CapsuleHeader, &AreAllImagesProcessed); + DEBUG((DEBUG_INFO, "ProcessFmpCapsuleImage - %r\n", Status)); + + if (!AreAllImagesProcessed) { + mAreAllImagesProcessed = FALSE; + } + + return Status; + } + + return EFI_UNSUPPORTED; +} + +/** + 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_EVENT EndOfDxeEvent; + EFI_STATUS Status; + + Status = gBS->CreateEventEx ( + EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + DxeCapsuleLibEndOfDxe, + NULL, + &gEfiEndOfDxeEventGroupGuid, + &EndOfDxeEvent + ); + ASSERT_EFI_ERROR (Status); + + InitCapsuleVariable(); + + return EFI_SUCCESS; +} diff --git a/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleLib.inf b/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleLib.inf new file mode 100644 index 0000000000..5e437dc418 --- /dev/null +++ b/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleLib.inf @@ -0,0 +1,80 @@ +## @file +# Capsule library instance for DXE_DRIVER. +# +# Capsule library instance for DXE_DRIVER module types. +# +# Copyright (c) 2016, 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 +# http://opensource.org/licenses/bsd-license.php +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = DxeCapsuleLib + MODULE_UNI_FILE = DxeCapsuleLib.uni + FILE_GUID = 534E35DE-8EB3-47b3-A4E0-72A571E50733 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + LIBRARY_CLASS = CapsuleLib|DXE_DRIVER UEFI_APPLICATION + CONSTRUCTOR = DxeCapsuleLibConstructor + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# + +[Sources] + DxeCapsuleLib.c + DxeCapsuleProcessLib.c + DxeCapsuleReportLib.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + BaseLib + BaseMemoryLib + DebugLib + MemoryAllocationLib + DxeServicesTableLib + UefiBootServicesTableLib + DevicePathLib + ReportStatusCodeLib + PrintLib + HobLib + +[Pcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdCapsuleMax ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdSystemRebootAfterCapsuleProcessFlag ## CONSUMES + + gEfiMdeModulePkgTokenSpaceGuid.PcdStatusCodeSubClassCapsule ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdCapsuleStatusCodeProcessCapsulesBegin ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdCapsuleStatusCodeProcessCapsulesEnd ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdCapsuleStatusCodeUpdatingFirmware ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdCapsuleStatusCodeUpdateFirmwareSuccess ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdCapsuleStatusCodeUpdateFirmwareFailed ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdCapsuleStatusCodeResettingSystem ## CONSUMES + +[Protocols] + gEsrtManagementProtocolGuid ## CONSUMES + gEfiFirmwareManagementProtocolGuid ## SOMETIMES_CONSUMES + gEdkiiVariableLockProtocolGuid ## SOMETIMES_CONSUMES + +[Guids] + gEfiFmpCapsuleGuid ## SOMETIMES_CONSUMES ## GUID + gWindowsUxCapsuleGuid ## SOMETIMES_CONSUMES ## GUID + gEfiSystemResourceTableGuid ## SOMETIMES_CONSUMES ## GUID + gEfiCapsuleReportGuid ## CONSUMES ## Variable + gEfiCapsuleVendorGuid ## CONSUMES ## Variable + gEfiEndOfDxeEventGroupGuid ## CONSUMES ## Event + +[Depex] + gEfiVariableWriteArchProtocolGuid diff --git a/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleLib.uni b/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleLib.uni new file mode 100644 index 0000000000..05a80d007e --- /dev/null +++ b/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleLib.uni @@ -0,0 +1,22 @@ +// /** @file +// Capsule library instance for DXE_DRIVER. +// +// Capsule library instance for DXE_DRIVER module types. +// +// Copyright (c) 2016, 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 +// http://opensource.org/licenses/bsd-license.php +// +// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "Capsule Support Library" + +#string STR_MODULE_DESCRIPTION #language en-US "Capsule library instance for DXE_DRIVER module types." + diff --git a/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleProcessLib.c b/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleProcessLib.c new file mode 100644 index 0000000000..62257a43cc --- /dev/null +++ b/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleProcessLib.c @@ -0,0 +1,475 @@ +/** @file + DXE capsule process. + + 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. + + ProcessCapsules(), ProcessTheseCapsules() will receive untrusted + input and do basic validation. + + Copyright (c) 2016, 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 + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/** + 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 + ); + +/** + 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 + ); + +/** + Validate if it is valid capsule header + + 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 + ); + +extern BOOLEAN mDxeCapsuleLibEndOfDxe; +extern BOOLEAN mAreAllImagesProcessed; +BOOLEAN mNeedReset; + +/** + + This routine is called to process capsules. + + Caution: This function may receive untrusted input. + + Each individual capsule result is recorded in capsule record variable. + + @param[in] NeedBlockDriver TRUE: Need skip the FMP capsules with non zero EmbeddedDriverCount. + FALSE: No need to skip any FMP capsules. + + @retval EFI_SUCCESS There is no error when processing capsules. + @retval EFI_OUT_OF_RESOURCES No enough resource to process capsules. + +**/ +EFI_STATUS +ProcessTheseCapsules ( + IN BOOLEAN NeedBlockDriver + ) +{ + EFI_STATUS Status; + EFI_PEI_HOB_POINTERS HobPointer; + EFI_CAPSULE_HEADER *CapsuleHeader; + UINT32 Size; + UINT32 CapsuleNumber; + UINT32 CapsuleTotalNumber; + EFI_CAPSULE_TABLE *CapsuleTable; + UINT32 Index; + UINT32 CacheIndex; + UINT32 CacheNumber; + VOID **CapsulePtr; + VOID **CapsulePtrCache; + EFI_GUID *CapsuleGuidCache; + EFI_STATUS *CapsuleStatusArray; + BOOLEAN DisplayCapsuleExist; + ESRT_MANAGEMENT_PROTOCOL *EsrtManagement; + UINT16 EmbeddedDriverCount; + + REPORT_STATUS_CODE(EFI_PROGRESS_CODE, (EFI_SOFTWARE | PcdGet32(PcdStatusCodeSubClassCapsule) | PcdGet32(PcdCapsuleStatusCodeProcessCapsulesBegin))); + + CapsuleNumber = 0; + CapsuleTotalNumber = 0; + CacheIndex = 0; + CacheNumber = 0; + CapsulePtr = NULL; + CapsulePtrCache = NULL; + CapsuleGuidCache = NULL; + DisplayCapsuleExist = FALSE; + EsrtManagement = NULL; + + Status = EFI_SUCCESS; + // + // Find all capsule images from hob + // + HobPointer.Raw = GetHobList (); + while ((HobPointer.Raw = GetNextHob (EFI_HOB_TYPE_UEFI_CAPSULE, HobPointer.Raw)) != NULL) { + if (!IsValidCapsuleHeader((VOID *)(UINTN)HobPointer.Capsule->BaseAddress, HobPointer.Capsule->Length)) { + HobPointer.Header->HobType = EFI_HOB_TYPE_UNUSED; // Mark this hob as invalid + } else { + CapsuleTotalNumber++; + } + HobPointer.Raw = GET_NEXT_HOB (HobPointer); + } + + if (CapsuleTotalNumber == 0) { + // + // We didn't find a hob, so had no errors. + // + DEBUG ((DEBUG_ERROR, "We can not find capsule data in capsule update boot mode.\n")); + Status = EFI_SUCCESS; + goto Done; + } + + // + // Init temp Capsule Data table. + // + CapsulePtr = (VOID **) AllocateZeroPool (sizeof (VOID *) * CapsuleTotalNumber); + ASSERT (CapsulePtr != NULL); + if (CapsulePtr == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + CapsulePtrCache = (VOID **) AllocateZeroPool (sizeof (VOID *) * CapsuleTotalNumber); + ASSERT (CapsulePtrCache != NULL); + if (CapsulePtrCache == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + CapsuleGuidCache = (EFI_GUID *) AllocateZeroPool (sizeof (EFI_GUID) * CapsuleTotalNumber); + ASSERT (CapsuleGuidCache != NULL); + if (CapsuleGuidCache == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + CapsuleStatusArray = (EFI_STATUS *) AllocateZeroPool (sizeof (EFI_STATUS) * CapsuleTotalNumber); + ASSERT (CapsuleStatusArray != NULL); + if (CapsuleStatusArray == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + + // + // Find all capsule images from hob + // + HobPointer.Raw = GetHobList (); + while ((HobPointer.Raw = GetNextHob (EFI_HOB_TYPE_UEFI_CAPSULE, HobPointer.Raw)) != NULL) { + CapsulePtr [CapsuleNumber++] = (VOID *) (UINTN) HobPointer.Capsule->BaseAddress; + HobPointer.Raw = GET_NEXT_HOB (HobPointer); + } + + // + // Check the capsule flags,if contains CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE, install + // capsuleTable to configure table with EFI_CAPSULE_GUID + // + + // + // Capsules who have CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE always are used for operating + // System to have information persist across a system reset. EFI System Table must + // point to an array of capsules that contains the same CapsuleGuid value. And agents + // searching for this type capsule will look in EFI System Table and search for the + // capsule's Guid and associated pointer to retrieve the data. Two steps below describes + // how to sorting the capsules by the unique guid and install the array to EFI System Table. + // Firstly, Loop for all coalesced capsules, record unique CapsuleGuids and cache them in an + // array for later sorting capsules by CapsuleGuid. + // + for (Index = 0; Index < CapsuleTotalNumber; Index++) { + CapsuleStatusArray [Index] = EFI_UNSUPPORTED; + CapsuleHeader = (EFI_CAPSULE_HEADER*) CapsulePtr [Index]; + if ((CapsuleHeader->Flags & CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE) != 0) { + // + // For each capsule, we compare it with known CapsuleGuid in the CacheArray. + // If already has the Guid, skip it. Whereas, record it in the CacheArray as + // an additional one. + // + CacheIndex = 0; + while (CacheIndex < CacheNumber) { + if (CompareGuid(&CapsuleGuidCache[CacheIndex],&CapsuleHeader->CapsuleGuid)) { + break; + } + CacheIndex++; + } + if (CacheIndex == CacheNumber) { + CopyMem(&CapsuleGuidCache[CacheNumber++],&CapsuleHeader->CapsuleGuid,sizeof(EFI_GUID)); + } + } + } + + // + // Secondly, for each unique CapsuleGuid in CacheArray, gather all coalesced capsules + // whose guid is the same as it, and malloc memory for an array which preceding + // with UINT32. The array fills with entry point of capsules that have the same + // CapsuleGuid, and UINT32 represents the size of the array of capsules. Then install + // this array into EFI System Table, so that agents searching for this type capsule + // will look in EFI System Table and search for the capsule's Guid and associated + // pointer to retrieve the data. + // + CacheIndex = 0; + while (CacheIndex < CacheNumber) { + CapsuleNumber = 0; + for (Index = 0; Index < CapsuleTotalNumber; Index++) { + CapsuleHeader = (EFI_CAPSULE_HEADER*) CapsulePtr [Index]; + if ((CapsuleHeader->Flags & CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE) != 0) { + if (CompareGuid (&CapsuleGuidCache[CacheIndex], &CapsuleHeader->CapsuleGuid)) { + // + // Cache Caspuleheader to the array, this array is uniqued with certain CapsuleGuid. + // + CapsulePtrCache[CapsuleNumber++] = (VOID*)CapsuleHeader; + // + // When a Capsule is listed in CapsulePtrCache, it will be reported in ConfigurationTable + // So, report the CapsuleStatus as "processed successfully". + // + CapsuleStatusArray [Index] = EFI_SUCCESS; + } + } + } + if (CapsuleNumber != 0) { + Size = sizeof(EFI_CAPSULE_TABLE) + (CapsuleNumber - 1) * sizeof(VOID*); + CapsuleTable = AllocateRuntimePool (Size); + ASSERT (CapsuleTable != NULL); + if (CapsuleTable == NULL) { + return EFI_OUT_OF_RESOURCES; + } + CapsuleTable->CapsuleArrayNumber = CapsuleNumber; + CopyMem(&CapsuleTable->CapsulePtr[0], CapsulePtrCache, CapsuleNumber * sizeof(VOID*)); + Status = gBS->InstallConfigurationTable (&CapsuleGuidCache[CacheIndex], (VOID*)CapsuleTable); + ASSERT_EFI_ERROR (Status); + } + CacheIndex++; + } + + REPORT_STATUS_CODE(EFI_PROGRESS_CODE, (EFI_SOFTWARE | PcdGet32(PcdStatusCodeSubClassCapsule) | PcdGet32(PcdCapsuleStatusCodeUpdatingFirmware))); + + // + // If Windows UX capsule exist, process it first + // + for (Index = 0; Index < CapsuleTotalNumber; Index++) { + CapsuleHeader = (EFI_CAPSULE_HEADER*) CapsulePtr [Index]; + if (CompareGuid(&CapsuleHeader->CapsuleGuid ,&gWindowsUxCapsuleGuid)) { + DEBUG ((DEBUG_INFO, "ProcessCapsuleImage (Ux) - 0x%x\n", CapsuleHeader)); + DisplayCapsuleExist = TRUE; + DEBUG ((DEBUG_INFO, "Display logo capsule is found.\n")); + Status = ProcessCapsuleImage (CapsuleHeader); + DEBUG((DEBUG_INFO, "ProcessCapsuleImage (Ux) - %r\n", Status)); + CapsuleStatusArray [Index] = Status; + break; + } + } + + if (!DisplayCapsuleExist) { + // + // Display Capsule not found. Display the default string. + // + Print (L"Updating the firmware ......\r\n"); + } + + // + // All capsules left are recognized by platform. + // + for (Index = 0; Index < CapsuleTotalNumber; Index++) { + CapsuleHeader = (EFI_CAPSULE_HEADER*) CapsulePtr [Index]; + if (!CompareGuid(&CapsuleHeader->CapsuleGuid ,&gWindowsUxCapsuleGuid)) { + // + // Call capsule library to process capsule image. + // + EmbeddedDriverCount = 0; + if (IsFmpCapsule(CapsuleHeader)) { + Status = ValidateFmpCapsule(CapsuleHeader, &EmbeddedDriverCount); + if (EFI_ERROR(Status)) { + DEBUG((DEBUG_ERROR, "ValidateFmpCapsule failed. Ignore!\n")); + continue; + } + } + + if ((!NeedBlockDriver) || (EmbeddedDriverCount == 0)) { + DEBUG((DEBUG_INFO, "ProcessCapsuleImage - 0x%x\n", CapsuleHeader)); + Status = ProcessCapsuleImage (CapsuleHeader); + CapsuleStatusArray [Index] = Status; + DEBUG((DEBUG_INFO, "ProcessCapsuleImage - %r\n", Status)); + + if (EFI_ERROR(Status)) { + REPORT_STATUS_CODE(EFI_ERROR_CODE, (EFI_SOFTWARE | PcdGet32(PcdStatusCodeSubClassCapsule) | PcdGet32(PcdCapsuleStatusCodeUpdateFirmwareFailed))); + DEBUG ((DEBUG_ERROR, "Capsule process failed. reset the system!\n")); + Print (L"Firmware update failed...\r\n"); + } else { + REPORT_STATUS_CODE(EFI_PROGRESS_CODE, (EFI_SOFTWARE | PcdGet32(PcdStatusCodeSubClassCapsule) | PcdGet32(PcdCapsuleStatusCodeUpdateFirmwareSuccess))); + } + + if ((CapsuleHeader->Flags & PcdGet16(PcdSystemRebootAfterCapsuleProcessFlag)) != 0 || + IsFmpCapsule(CapsuleHeader)) { + mNeedReset = TRUE; + } + } + } + } + + Status = gBS->LocateProtocol(&gEsrtManagementProtocolGuid, NULL, (VOID **)&EsrtManagement); + // + // Always sync ESRT Cache from FMP Instance + // + if (!EFI_ERROR(Status)) { + EsrtManagement->SyncEsrtFmp(); + } + Status = EFI_SUCCESS; + +Done: + // + // Free the allocated temp memory space. + // + if (CapsuleGuidCache != NULL) { + FreePool(CapsuleGuidCache); + } + if (CapsulePtrCache != NULL) { + FreePool(CapsulePtrCache); + } + if (CapsulePtr != NULL) { + FreePool(CapsulePtr); + } + + REPORT_STATUS_CODE(EFI_PROGRESS_CODE, (EFI_SOFTWARE | PcdGet32(PcdStatusCodeSubClassCapsule) | PcdGet32(PcdCapsuleStatusCodeProcessCapsulesEnd))); + + return Status; +} + +/** + Do reset system. +**/ +VOID +DoResetSystem ( + VOID + ) +{ + UINTN Index; + + REPORT_STATUS_CODE(EFI_PROGRESS_CODE, (EFI_SOFTWARE | PcdGet32(PcdStatusCodeSubClassCapsule) | PcdGet32(PcdCapsuleStatusCodeResettingSystem))); + + Print(L"Capsule Request Cold Reboot.\n"); + DEBUG((DEBUG_INFO, "Capsule Request Cold Reboot.")); + + for (Index = 5; Index > 0; Index--) { + Print(L"\rResetting system in %d seconds ...", Index); + DEBUG((DEBUG_INFO, "\rResetting system in %d seconds ...", Index)); + gBS->Stall(1000000); + } + + gRT->ResetSystem(EfiResetCold, EFI_SUCCESS, 0, NULL); + + CpuDeadLoop(); +} + +/** + + This routine is called to process capsules. + + Caution: This function may receive untrusted input. + + The capsules reported in EFI_HOB_UEFI_CAPSULE are processed. + If there is no EFI_HOB_UEFI_CAPSULE, this routine does nothing. + + This routine should be called twice in BDS. + 1) The first call must be before EndOfDxe. The system capsules is processed. + If device capsule FMP protocols are exposted at this time and device FMP + capsule has zero EmbeddedDriverCount, the device capsules are processed. + Each individual capsule result is recorded in capsule record variable. + System may reset in this function, if reset is required by capsule and + all capsules are processed. + If not all capsules are processed, reset will be defered to second call. + + 2) The second call must be after EndOfDxe and after ConnectAll, so that all + device capsule FMP protocols are exposed. + The system capsules are skipped. If the device capsules are NOT processed + in first call, they are processed here. + Each individual capsule result is recorded in capsule record variable. + System may reset in this function, if reset is required by capsule + processed in first call and second call. + + @retval EFI_SUCCESS There is no error when processing capsules. + @retval EFI_OUT_OF_RESOURCES No enough resource to process capsules. + +**/ +EFI_STATUS +EFIAPI +ProcessCapsules ( + VOID + ) +{ + EFI_STATUS Status; + + if (!mDxeCapsuleLibEndOfDxe) { + // + // Initialize mAreAllImagesProcessed to be TRUE. + // + // It will be updated to FALSE in ProcessTheseCapsules()->ProcessCapsuleImage(), + // if there is any FMP image in any FMP capsule not processed. + // + mAreAllImagesProcessed = TRUE; + + Status = ProcessTheseCapsules(TRUE); + // + // Reboot System if and only if all capsule processed. + // If not, defer reset to 2nd process. + // + if (mNeedReset && mAreAllImagesProcessed) { + DoResetSystem(); + } + } else { + Status = ProcessTheseCapsules(FALSE); + // + // Reboot System if required after all capsule processed + // + if (mNeedReset) { + DoResetSystem(); + } + } + return Status; +} diff --git a/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleProcessLibNull.c b/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleProcessLibNull.c new file mode 100644 index 0000000000..07e9e46eae --- /dev/null +++ b/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleProcessLibNull.c @@ -0,0 +1,57 @@ +/** @file + DXE capsule process. + Dummy function for runtime module, because CapsuleDxeRuntime + does not need call ProcessCapsules(). + + Copyright (c) 2016, 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 + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include +#include + +/** + + This routine is called to process capsules. + + Caution: This function may receive untrusted input. + + The capsules reported in EFI_HOB_UEFI_CAPSULE are processed. + If there is no EFI_HOB_UEFI_CAPSULE, this routine does nothing. + + This routine should be called twice in BDS. + 1) The first call must be before EndOfDxe. The system capsules is processed. + If device capsule FMP protocols are exposted at this time and device FMP + capsule has zero EmbeddedDriverCount, the device capsules are processed. + Each individual capsule result is recorded in capsule record variable. + System may reset in this function, if reset is required by capsule and + all capsules are processed. + If not all capsules are processed, reset will be defered to second call. + + 2) The second call must be after EndOfDxe and after ConnectAll, so that all + device capsule FMP protocols are exposed. + The system capsules are skipped. If the device capsules are NOT processed + in first call, they are processed here. + Each individual capsule result is recorded in capsule record variable. + System may reset in this function, if reset is required by capsule + processed in first call and second call. + + @retval EFI_SUCCESS There is no error when processing capsules. + @retval EFI_OUT_OF_RESOURCES No enough resource to process capsules. + +**/ +EFI_STATUS +EFIAPI +ProcessCapsules ( + VOID + ) +{ + return EFI_UNSUPPORTED; +} diff --git a/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleReportLib.c b/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleReportLib.c new file mode 100644 index 0000000000..a0ed2d0b7e --- /dev/null +++ b/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleReportLib.c @@ -0,0 +1,489 @@ +/** @file + DXE capsule report related function. + + Copyright (c) 2016, 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 + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +typedef struct { + EFI_CAPSULE_RESULT_VARIABLE_HEADER CapsuleResultHeader; + EFI_CAPSULE_RESULT_VARIABLE_FMP CapsuleResultFmp; +} CAPSULE_RESULT_VARIABLE_CACHE; + +#define CAPSULE_RESULT_VARIABLE_CACHE_COUNT 0x10 + +CAPSULE_RESULT_VARIABLE_CACHE *mCapsuleResultVariableCache; +UINTN mCapsuleResultVariableCacheMaxCount; +UINTN mCapsuleResultVariableCacheCount; + +/** + Get current capsule last variable index. + + @return Current capsule last variable index. + @retval -1 No current capsule last variable. +**/ +INTN +GetCurrentCapsuleLastIndex ( + VOID + ) +{ + UINTN Size; + CHAR16 CapsuleLastStr[sizeof("Capsule####")]; + EFI_STATUS Status; + UINT16 CurrentIndex; + + Size = sizeof(L"Capsule####") - sizeof(CHAR16); // no zero terminator + Status = gRT->GetVariable( + L"CapsuleLast", + &gEfiCapsuleReportGuid, + NULL, + &Size, + CapsuleLastStr + ); + if (EFI_ERROR(Status)) { + return -1; + } + CurrentIndex = (UINT16)StrHexToUintn(&CapsuleLastStr[sizeof("Capsule") - 1]); + return CurrentIndex; +} + +/** + Check if this FMP capsule is processed. + + @param[in] CapsuleHeader The capsule image header + @param[in] PayloadIndex FMP payload index + @param[in] ImageHeader FMP image header + + @retval TRUE This FMP capsule is processed. + @retval FALSE This FMP capsule is not processed. +**/ +BOOLEAN +IsFmpCapsuleProcessed ( + IN EFI_CAPSULE_HEADER *CapsuleHeader, + IN UINTN PayloadIndex, + IN EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *ImageHeader + ) +{ + UINTN Index; + EFI_CAPSULE_RESULT_VARIABLE_HEADER *CapsuleResult; + EFI_CAPSULE_RESULT_VARIABLE_FMP *CapsuleResultFmp; + + for (Index = 0; Index < mCapsuleResultVariableCacheCount; Index++) { + // + // Check + // + CapsuleResult = &mCapsuleResultVariableCache[Index].CapsuleResultHeader; + if (CapsuleResult->VariableTotalSize >= sizeof(EFI_CAPSULE_RESULT_VARIABLE_HEADER)) { + if (CompareGuid(&CapsuleResult->CapsuleGuid, &gEfiFmpCapsuleGuid)) { + if (CapsuleResult->VariableTotalSize >= sizeof(EFI_CAPSULE_RESULT_VARIABLE_HEADER) + sizeof(EFI_CAPSULE_RESULT_VARIABLE_FMP)) { + CapsuleResultFmp = (EFI_CAPSULE_RESULT_VARIABLE_FMP *)(CapsuleResult + 1); + if (CompareGuid(&CapsuleResultFmp->UpdateImageTypeId, &ImageHeader->UpdateImageTypeId) && + (CapsuleResultFmp->UpdateImageIndex == ImageHeader->UpdateImageIndex) && + (CapsuleResultFmp->PayloadIndex == PayloadIndex) ) { + return TRUE; + } + } + } + } + } + + return FALSE; +} + +/** + Write a new capsule status variable cache. + + @param[in] CapsuleResult The capsule status variable + @param[in] CapsuleResultSize The size of the capsule stauts variable in bytes + + @retval EFI_SUCCESS The capsule status variable is cached. + @retval EFI_OUT_OF_RESOURCES No resource to cache the capsule status variable. +**/ +EFI_STATUS +WriteNewCapsuleResultVariableCache ( + IN VOID *CapsuleResult, + IN UINTN CapsuleResultSize + ) +{ + if (CapsuleResultSize > sizeof(CAPSULE_RESULT_VARIABLE_CACHE)) { + CapsuleResultSize = sizeof(CAPSULE_RESULT_VARIABLE_CACHE); + } + + if (mCapsuleResultVariableCacheCount == mCapsuleResultVariableCacheMaxCount) { + mCapsuleResultVariableCache = ReallocatePool( + mCapsuleResultVariableCacheMaxCount * sizeof(CAPSULE_RESULT_VARIABLE_CACHE), + (mCapsuleResultVariableCacheMaxCount + CAPSULE_RESULT_VARIABLE_CACHE_COUNT) * sizeof(CAPSULE_RESULT_VARIABLE_CACHE), + mCapsuleResultVariableCache + ); + if (mCapsuleResultVariableCache == NULL) { + return EFI_OUT_OF_RESOURCES; + } + mCapsuleResultVariableCacheMaxCount += CAPSULE_RESULT_VARIABLE_CACHE_COUNT; + } + + ASSERT(mCapsuleResultVariableCacheCount < mCapsuleResultVariableCacheMaxCount); + ASSERT(mCapsuleResultVariableCache != NULL); + CopyMem( + &mCapsuleResultVariableCache[mCapsuleResultVariableCacheCount], + CapsuleResult, + CapsuleResultSize + ); + mCapsuleResultVariableCacheCount++; + + return EFI_SUCCESS; +} + +/** + Get a new capsule status variable index. + + @return A new capsule status variable index. + @retval -1 No new capsule status variable index. +**/ +INTN +GetNewCapsuleResultIndex ( + VOID + ) +{ + INTN CurrentIndex; + + CurrentIndex = GetCurrentCapsuleLastIndex(); + if (CurrentIndex >= PcdGet16(PcdCapsuleMax)) { + return -1; + } + + return CurrentIndex + 1; +} + +/** + Write a new capsule status variable. + + @param[in] CapsuleResult The capsule status variable + @param[in] CapsuleResultSize The size of the capsule stauts variable in bytes + + @retval EFI_SUCCESS The capsule status variable is recorded. + @retval EFI_OUT_OF_RESOURCES No resource to record the capsule status variable. +**/ +EFI_STATUS +WriteNewCapsuleResultVariable ( + IN VOID *CapsuleResult, + IN UINTN CapsuleResultSize + ) +{ + INTN CapsuleResultIndex; + CHAR16 CapsuleResultStr[sizeof("Capsule####")]; + UINTN Size; + EFI_STATUS Status; + + CapsuleResultIndex = GetNewCapsuleResultIndex(); + DEBUG((DEBUG_INFO, "New CapsuleResultIndex - 0x%x\n", CapsuleResultIndex)); + if (CapsuleResultIndex == -1) { + return EFI_OUT_OF_RESOURCES; + } + UnicodeSPrint( + CapsuleResultStr, + sizeof(CapsuleResultStr), + L"Capsule%04x", + CapsuleResultIndex + ); + + Status = gRT->SetVariable( + CapsuleResultStr, + &gEfiCapsuleReportGuid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, + CapsuleResultSize, + CapsuleResult + ); + if (!EFI_ERROR(Status)) { + Size = sizeof(L"Capsule####") - sizeof(CHAR16); // no zero terminator + DEBUG((DEBUG_INFO, "Set CapsuleLast - %s\n", CapsuleResultStr)); + Status = gRT->SetVariable( + L"CapsuleLast", + &gEfiCapsuleReportGuid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, + Size, + CapsuleResultStr + ); + } + + return Status; +} + +/** + Record capsule status variable and to local cache. + + @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 + ) +{ + EFI_CAPSULE_RESULT_VARIABLE_HEADER CapsuleResultVariable; + EFI_STATUS Status; + + CapsuleResultVariable.VariableTotalSize = sizeof(CapsuleResultVariable); + CopyGuid (&CapsuleResultVariable.CapsuleGuid, &CapsuleHeader->CapsuleGuid); + ZeroMem(&CapsuleResultVariable.CapsuleProcessed, sizeof(CapsuleResultVariable.CapsuleProcessed)); + gRT->GetTime(&CapsuleResultVariable.CapsuleProcessed, NULL); + CapsuleResultVariable.CapsuleStatus = CapsuleStatus; + + // + // Save Local Cache + // + Status = WriteNewCapsuleResultVariableCache(&CapsuleResultVariable, sizeof(CapsuleResultVariable)); + + if ((CapsuleHeader->Flags & CAPSULE_FLAGS_PERSIST_ACROSS_RESET) != 0) { + Status = WriteNewCapsuleResultVariable(&CapsuleResultVariable, sizeof(CapsuleResultVariable)); + } + return Status; +} + +/** + Record FMP capsule status variable and to local cache. + + @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 + + @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 + ) +{ + UINT8 CapsuleResultVariable[sizeof(EFI_CAPSULE_RESULT_VARIABLE_HEADER) + sizeof(EFI_CAPSULE_RESULT_VARIABLE_FMP)]; + EFI_CAPSULE_RESULT_VARIABLE_HEADER *CapsuleResultVariableHeader; + EFI_CAPSULE_RESULT_VARIABLE_FMP *CapsuleResultVariableFmp; + EFI_STATUS Status; + + CapsuleResultVariableHeader = (VOID *)&CapsuleResultVariable[0]; + CapsuleResultVariableHeader->VariableTotalSize = sizeof(CapsuleResultVariable); + CopyGuid(&CapsuleResultVariableHeader->CapsuleGuid, &CapsuleHeader->CapsuleGuid); + ZeroMem(&CapsuleResultVariableHeader->CapsuleProcessed, sizeof(CapsuleResultVariableHeader->CapsuleProcessed)); + gRT->GetTime(&CapsuleResultVariableHeader->CapsuleProcessed, NULL); + CapsuleResultVariableHeader->CapsuleStatus = CapsuleStatus; + + CapsuleResultVariableFmp = (VOID *)&CapsuleResultVariable[sizeof(EFI_CAPSULE_RESULT_VARIABLE_HEADER)]; + CapsuleResultVariableFmp->Version = 0x1; + CapsuleResultVariableFmp->PayloadIndex = (UINT8)PayloadIndex; + CapsuleResultVariableFmp->UpdateImageIndex = ImageHeader->UpdateImageIndex; + CopyGuid (&CapsuleResultVariableFmp->UpdateImageTypeId, &ImageHeader->UpdateImageTypeId); + + // + // Save Local Cache + // + Status = WriteNewCapsuleResultVariableCache(&CapsuleResultVariable, sizeof(CapsuleResultVariable)); + + if ((CapsuleHeader->Flags & CAPSULE_FLAGS_PERSIST_ACROSS_RESET) != 0) { + Status = WriteNewCapsuleResultVariable(&CapsuleResultVariable, sizeof(CapsuleResultVariable)); + } + return Status; +} + +/** + Initialize CapsuleMax variables. +**/ +VOID +InitCapsuleMaxVariable ( + VOID + ) +{ + EFI_STATUS Status; + UINTN Size; + CHAR16 CapsuleMaxStr[sizeof("Capsule####")]; + EDKII_VARIABLE_LOCK_PROTOCOL *VariableLock; + + UnicodeSPrint( + CapsuleMaxStr, + sizeof(CapsuleMaxStr), + L"Capsule%04x", + PcdGet16(PcdCapsuleMax) + ); + + Size = sizeof(L"Capsule####") - sizeof(CHAR16); // no zero terminator + Status = gRT->SetVariable( + L"CapsuleMax", + &gEfiCapsuleReportGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, + Size, + CapsuleMaxStr + ); + if (!EFI_ERROR(Status)) { + // Lock it per UEFI spec. + Status = gBS->LocateProtocol(&gEdkiiVariableLockProtocolGuid, NULL, (VOID **)&VariableLock); + if (!EFI_ERROR(Status)) { + Status = VariableLock->RequestToLock(VariableLock, L"CapsuleMax", &gEfiCapsuleReportGuid); + ASSERT_EFI_ERROR(Status); + } + } +} + +/** + Initialize CapsuleLast variables. +**/ +VOID +InitCapsuleLastVariable ( + VOID + ) +{ + EFI_STATUS Status; + EFI_BOOT_MODE BootMode; + EDKII_VARIABLE_LOCK_PROTOCOL *VariableLock; + VOID *CapsuleResult; + UINTN Size; + CHAR16 CapsuleLastStr[sizeof("Capsule####")]; + + BootMode = GetBootModeHob(); + if (BootMode == BOOT_ON_FLASH_UPDATE) { + Status = gRT->SetVariable( + L"CapsuleLast", + &gEfiCapsuleReportGuid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, + 0, + NULL + ); + // Do not lock it because it will be updated later. + } else { + // + // Check if OS/APP cleared L"Capsule####" + // + ZeroMem(CapsuleLastStr, sizeof(CapsuleLastStr)); + Size = sizeof(L"Capsule####") - sizeof(CHAR16); // no zero terminator + Status = gRT->GetVariable( + L"CapsuleLast", + &gEfiCapsuleReportGuid, + NULL, + &Size, + CapsuleLastStr + ); + if (!EFI_ERROR(Status)) { + // + // L"CapsuleLast" is got, check if data is there. + // + Status = GetVariable2 ( + CapsuleLastStr, + &gEfiCapsuleReportGuid, + (VOID **) &CapsuleResult, + NULL + ); + if (EFI_ERROR(Status)) { + // + // If no data, delete L"CapsuleLast" + // + Status = gRT->SetVariable( + L"CapsuleLast", + &gEfiCapsuleReportGuid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, + 0, + NULL + ); + } + } + + // Lock it in normal boot path per UEFI spec. + Status = gBS->LocateProtocol(&gEdkiiVariableLockProtocolGuid, NULL, (VOID **)&VariableLock); + if (!EFI_ERROR(Status)) { + Status = VariableLock->RequestToLock(VariableLock, L"CapsuleLast", &gEfiCapsuleReportGuid); + ASSERT_EFI_ERROR(Status); + } + } +} + +/** + Initialize capsule update variables. +**/ +VOID +InitCapsuleUpdateVariable ( + VOID + ) +{ + EFI_STATUS Status; + UINTN Index; + CHAR16 CapsuleVarName[30]; + CHAR16 *TempVarName; + + // + // Clear all the capsule variables CapsuleUpdateData, CapsuleUpdateData1, CapsuleUpdateData2... + // as early as possible which will avoid the next time boot after the capsule update + // will still into the capsule loop + // + StrCpyS (CapsuleVarName, sizeof(CapsuleVarName)/sizeof(CapsuleVarName[0]), EFI_CAPSULE_VARIABLE_NAME); + TempVarName = CapsuleVarName + StrLen (CapsuleVarName); + Index = 0; + while (TRUE) { + if (Index > 0) { + UnicodeValueToString (TempVarName, 0, Index, 0); + } + Status = gRT->SetVariable ( + CapsuleVarName, + &gEfiCapsuleVendorGuid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS, + 0, + (VOID *)NULL + ); + if (EFI_ERROR (Status)) { + // + // There is no capsule variables, quit + // + break; + } + Index++; + } +} + +/** + Initialize capsule related variables. +**/ +VOID +InitCapsuleVariable ( + VOID + ) +{ + InitCapsuleUpdateVariable(); + InitCapsuleMaxVariable(); + InitCapsuleLastVariable(); + // + // No need to clear L"Capsule####", because OS/APP should refer L"CapsuleLast" + // to check status and delete them. + // +} diff --git a/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleReportLibNull.c b/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleReportLibNull.c new file mode 100644 index 0000000000..bf550e51ee --- /dev/null +++ b/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleReportLibNull.c @@ -0,0 +1,91 @@ +/** @file + DXE capsule report related function. + Dummy function for runtime module, because CapsuleDxeRuntime + does not need record capsule status variable. + + Copyright (c) 2016, 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 + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include +#include +#include +#include + +/** + Check if this FMP capsule is processed. + + @param[in] CapsuleHeader The capsule image header + @param[in] PayloadIndex FMP payload index + @param[in] ImageHeader FMP image header + + @retval TRUE This FMP capsule is processed. + @retval FALSE This FMP capsule is not processed. +**/ +BOOLEAN +IsFmpCapsuleProcessed ( + IN EFI_CAPSULE_HEADER *CapsuleHeader, + IN UINTN PayloadIndex, + IN EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *ImageHeader + ) +{ + return FALSE; +} + +/** + Record capsule status variable and to local cache. + + @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 + ) +{ + return EFI_UNSUPPORTED; +} + +/** + Record FMP capsule status variable and to local cache. + + @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 + + @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 + ) +{ + return EFI_UNSUPPORTED; +} + +/** + Initialize capsule related variables. +**/ +VOID +InitCapsuleVariable ( + VOID + ) +{ + return; +} diff --git a/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleRuntime.c b/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleRuntime.c new file mode 100644 index 0000000000..880143905a --- /dev/null +++ b/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleRuntime.c @@ -0,0 +1,112 @@ +/** @file + Capsule library runtime support. + + Copyright (c) 2016, 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 + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +extern EFI_SYSTEM_RESOURCE_TABLE *mEsrtTable; +extern BOOLEAN mIsVirtualAddrConverted; + +/** + Convert EsrtTable physical address to virtual address. + + @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 +DxeCapsuleLibVirtualAddressChangeEvent ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + UINTN Index; + EFI_CONFIGURATION_TABLE *ConfigEntry; + + // + // Get Esrt table first + // + ConfigEntry = gST->ConfigurationTable; + for (Index = 0; Index < gST->NumberOfTableEntries; Index++) { + if (CompareGuid(&gEfiSystemResourceTableGuid, &ConfigEntry->VendorGuid)) { + break; + } + ConfigEntry++; + } + + // + // If no Esrt table installed in Configure Table + // + if (Index < gST->NumberOfTableEntries) { + // + // Search Esrt to check given capsule is qualified + // + mEsrtTable = (EFI_SYSTEM_RESOURCE_TABLE *) ConfigEntry->VendorTable; + + // + // Update protocol pointer to Esrt Table. + // + gRT->ConvertPointer (0x00, (VOID**) &(mEsrtTable)); + } + + mIsVirtualAddrConverted = TRUE; + +} + +/** + The constructor function hook VirtualAddressChange event to use ESRT table as capsule routing table. + + @param ImageHandle The firmware allocated handle for the EFI image. + @param SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The constructor successfully . +**/ +EFI_STATUS +EFIAPI +DxeRuntimeCapsuleLibConstructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + EFI_EVENT Event; + + // + // Make sure we can handle virtual address changes. + // + Event = NULL; + Status = gBS->CreateEventEx ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + DxeCapsuleLibVirtualAddressChangeEvent, + NULL, + &gEfiEventVirtualAddressChangeGuid, + &Event + ); + ASSERT_EFI_ERROR (Status); + + return EFI_SUCCESS; +} diff --git a/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeRuntimeCapsuleLib.inf b/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeRuntimeCapsuleLib.inf new file mode 100644 index 0000000000..88a3c61a28 --- /dev/null +++ b/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeRuntimeCapsuleLib.inf @@ -0,0 +1,83 @@ +## @file +# Capsule library instance for DXE_RUNTIME_DRIVER. +# +# Capsule library instance for DXE_RUNTIME_DRIVER module types. +# +# Copyright (c) 2016, 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 +# http://opensource.org/licenses/bsd-license.php +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = DxeRuntimeCapsuleLib + MODULE_UNI_FILE = DxeRuntimeCapsuleLib.uni + FILE_GUID = 19BE1E4B-1A9A-44c1-8F12-32DD0470516A + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + LIBRARY_CLASS = CapsuleLib|DXE_RUNTIME_DRIVER + CONSTRUCTOR = DxeCapsuleLibConstructor + CONSTRUCTOR = DxeRuntimeCapsuleLibConstructor + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# + +[Sources] + DxeCapsuleLib.c + DxeCapsuleProcessLibNull.c + DxeCapsuleReportLibNull.c + DxeCapsuleRuntime.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + BaseLib + BaseMemoryLib + DebugLib + MemoryAllocationLib + DxeServicesTableLib + UefiBootServicesTableLib + DevicePathLib + ReportStatusCodeLib + PrintLib + HobLib + +[Pcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdCapsuleMax ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdSystemRebootAfterCapsuleProcessFlag ## CONSUMES + + gEfiMdeModulePkgTokenSpaceGuid.PcdStatusCodeSubClassCapsule ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdCapsuleStatusCodeProcessCapsulesBegin ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdCapsuleStatusCodeProcessCapsulesEnd ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdCapsuleStatusCodeUpdatingFirmware ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdCapsuleStatusCodeUpdateFirmwareSuccess ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdCapsuleStatusCodeUpdateFirmwareFailed ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdCapsuleStatusCodeResettingSystem ## CONSUMES + +[Protocols] + gEsrtManagementProtocolGuid ## CONSUMES + gEfiFirmwareManagementProtocolGuid ## SOMETIMES_CONSUMES + gEdkiiVariableLockProtocolGuid ## SOMETIMES_CONSUMES + +[Guids] + gEfiFmpCapsuleGuid ## SOMETIMES_CONSUMES ## GUID + gWindowsUxCapsuleGuid ## SOMETIMES_CONSUMES ## GUID + gEfiSystemResourceTableGuid ## SOMETIMES_CONSUMES ## GUID + gEfiCapsuleReportGuid ## CONSUMES ## Variable + gEfiCapsuleVendorGuid ## CONSUMES ## Variable + gEfiEndOfDxeEventGroupGuid ## CONSUMES ## Event + gEfiEventVirtualAddressChangeGuid ## CONSUMES ## Event + +[Depex] + gEfiVariableWriteArchProtocolGuid diff --git a/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeRuntimeCapsuleLib.uni b/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeRuntimeCapsuleLib.uni new file mode 100644 index 0000000000..cd89b138c7 --- /dev/null +++ b/MdeModulePkg/Library/DxeCapsuleLibFmp/DxeRuntimeCapsuleLib.uni @@ -0,0 +1,22 @@ +// /** @file +// Capsule library instance for DXE_RUNTIME_DRIVER. +// +// Capsule library instance for DXE_RUNTIME_DRIVER module types. +// +// Copyright (c) 2016, 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 +// http://opensource.org/licenses/bsd-license.php +// +// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "Capsule Support Library" + +#string STR_MODULE_DESCRIPTION #language en-US "Capsule library instance for DXE_RUNTIME_DRIVER module types." +