From af1a266670d040d2f4083ff309d732d648afba2a Mon Sep 17 00:00:00 2001 From: Angelos Mouzakitis Date: Tue, 10 Oct 2023 14:33:42 +0000 Subject: Add submodule dependency files Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec --- .../RecoveryModuleLoadPei/RecoveryModuleLoadPei.c | 814 +++++++++++++++++++++ 1 file changed, 814 insertions(+) create mode 100644 roms/edk2/SignedCapsulePkg/Universal/RecoveryModuleLoadPei/RecoveryModuleLoadPei.c (limited to 'roms/edk2/SignedCapsulePkg/Universal/RecoveryModuleLoadPei/RecoveryModuleLoadPei.c') diff --git a/roms/edk2/SignedCapsulePkg/Universal/RecoveryModuleLoadPei/RecoveryModuleLoadPei.c b/roms/edk2/SignedCapsulePkg/Universal/RecoveryModuleLoadPei/RecoveryModuleLoadPei.c new file mode 100644 index 000000000..af8e5cb84 --- /dev/null +++ b/roms/edk2/SignedCapsulePkg/Universal/RecoveryModuleLoadPei/RecoveryModuleLoadPei.c @@ -0,0 +1,814 @@ +/** @file + Recovery module. + + 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. + + ProcessRecoveryCapsule(), ProcessFmpCapsuleImage(), ProcessRecoveryImage(), + ValidateFmpCapsule() will receive untrusted input and do basic validation. + +Copyright (c) 2016 - 2019, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +// +// The package level header files this module uses +// +#include +#include +// +// The protocols, PPI and GUID definitions for this module +// +#include +#include +#include +#include +#include +#include +#include +#include + +// +// The Library classes this module consumes +// +#include +#include +#include +#include +#include +#include +#include + +#include "RecoveryModuleLoadPei.h" + +/** + Loads a DXE capsule from some media into memory and updates the HOB table + with the DXE firmware volume information. + + @param[in] PeiServices General-purpose services that are available to every PEIM. + @param[in] This Indicates the EFI_PEI_RECOVERY_MODULE_PPI instance. + + @retval EFI_SUCCESS The capsule was loaded correctly. + @retval EFI_DEVICE_ERROR A device error occurred. + @retval EFI_NOT_FOUND A recovery DXE capsule cannot be found. + +**/ +EFI_STATUS +EFIAPI +LoadRecoveryCapsule ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_RECOVERY_MODULE_PPI *This + ); + +EFI_PEI_RECOVERY_MODULE_PPI mRecoveryPpi = { + LoadRecoveryCapsule +}; + +EFI_PEI_PPI_DESCRIPTOR mRecoveryPpiList = { + (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), + &gEfiPeiRecoveryModulePpiGuid, + &mRecoveryPpi +}; + +/** + Parse Config data file to get the updated data array. + + @param[in] DataBuffer Config raw file buffer. + @param[in] BufferSize Size of raw buffer. + @param[in, out] ConfigHeader Pointer to the config header. + @param[in, out] RecoveryArray Pointer to the config of recovery data. + + @retval EFI_NOT_FOUND No config data is found. + @retval EFI_OUT_OF_RESOURCES No enough memory is allocated. + @retval EFI_SUCCESS Parse the config file successfully. + +**/ +EFI_STATUS +ParseRecoveryDataFile ( + IN UINT8 *DataBuffer, + IN UINTN BufferSize, + IN OUT CONFIG_HEADER *ConfigHeader, + IN OUT RECOVERY_CONFIG_DATA **RecoveryArray + ); + +/** + Return if this FMP is a system FMP or a device FMP, based upon FmpImageInfo. + + @param[in] FmpImageHeader A pointer to EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER + + @return TRUE It is a system FMP. + @return FALSE It is a device FMP. +**/ +BOOLEAN +IsSystemFmpImage ( + IN EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *FmpImageHeader + ) +{ + GUID *Guid; + UINTN Count; + UINTN Index; + + Guid = PcdGetPtr(PcdSystemFmpCapsuleImageTypeIdGuid); + Count = PcdGetSize(PcdSystemFmpCapsuleImageTypeIdGuid) / sizeof(GUID); + + for (Index = 0; Index < Count; Index++, Guid++) { + if (CompareGuid(&FmpImageHeader->UpdateImageTypeId, Guid)) { + return TRUE; + } + } + + return FALSE; +} + +/** + Return if this CapsuleGuid is a FMP capsule GUID or not. + + @param[in] CapsuleGuid A pointer to EFI_GUID + + @return TRUE It is a FMP capsule GUID. + @return FALSE It is not a FMP capsule GUID. +**/ +BOOLEAN +IsFmpCapsuleGuid ( + IN EFI_GUID *CapsuleGuid + ) +{ + if (CompareGuid(&gEfiFmpCapsuleGuid, CapsuleGuid)) { + return TRUE; + } + + return FALSE; +} + +/** + This function assumes the input Capsule image already passes basic check in + ValidateFmpCapsule(). + + Criteria of system FMP capsule is: + 1) FmpCapsuleHeader->EmbeddedDriverCount is 0. + 2) FmpCapsuleHeader->PayloadItemCount is not 0. + 3) All ImageHeader->UpdateImageTypeId matches PcdSystemFmpCapsuleImageTypeIdGuid. + + @param[in] CapsuleHeader Points to a capsule header. + + @retval TRUE Input capsule is a correct system FMP capsule. + @retval FALSE Input capsule is not a correct system FMP capsule. +**/ +BOOLEAN +IsSystemFmpCapsuleImage ( + IN EFI_CAPSULE_HEADER *CapsuleHeader + ) +{ + EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER *FmpCapsuleHeader; + EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *ImageHeader; + UINT64 *ItemOffsetList; + UINT32 ItemNum; + UINTN Index; + + FmpCapsuleHeader = (EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER *) ((UINT8 *) CapsuleHeader + CapsuleHeader->HeaderSize); + + if (FmpCapsuleHeader->EmbeddedDriverCount != 0) { + return FALSE; + } + + if (FmpCapsuleHeader->PayloadItemCount == 0) { + return FALSE; + } + + ItemNum = FmpCapsuleHeader->EmbeddedDriverCount + FmpCapsuleHeader->PayloadItemCount; + + ItemOffsetList = (UINT64 *)(FmpCapsuleHeader + 1); + + for (Index = 0; Index < ItemNum; Index++) { + ImageHeader = (EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *)((UINT8 *)FmpCapsuleHeader + ItemOffsetList[Index]); + if (!IsSystemFmpImage(ImageHeader)) { + return FALSE; + } + } + + return TRUE; +} + +/** + 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 + ) +{ + 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. + + @param[in] CapsuleHeader Points to a capsule header. + @param[out] IsSystemFmp If it is a system FMP. + @param[out] EmbeddedDriverCount The EmbeddedDriverCount in the FMP capsule. + + @retval EFI_SUCCESS 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 BOOLEAN *IsSystemFmp, OPTIONAL + 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 (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; + } + /// + /// Current Init ImageHeader version is 3. UpdateHardwareInstance field was added in version 2 + /// and ImageCapsuleSupport field was added in version 3 + /// + 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); + } + + // 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; + } + + // + // Check in system FMP capsule + // + if (IsSystemFmp != NULL) { + *IsSystemFmp = IsSystemFmpCapsuleImage(CapsuleHeader); + } + + if (EmbeddedDriverCount != NULL) { + *EmbeddedDriverCount = FmpCapsuleHeader->EmbeddedDriverCount; + } + + return EFI_SUCCESS; +} + +/** + Recovery module entrypoint + + @param[in] FileHandle Handle of the file being invoked. + @param[in] PeiServices Describes the list of possible PEI Services. + + @return EFI_SUCCESS Recovery module is initialized. +**/ +EFI_STATUS +EFIAPI +InitializeRecoveryModule ( + IN EFI_PEI_FILE_HANDLE FileHandle, + IN CONST EFI_PEI_SERVICES **PeiServices + ) +{ + EFI_STATUS Status; + UINTN BootMode; + + BootMode = GetBootModeHob(); + ASSERT(BootMode == BOOT_IN_RECOVERY_MODE); + + Status = (**PeiServices).InstallPpi (PeiServices, &mRecoveryPpiList); + ASSERT_EFI_ERROR (Status); + + return Status; +} + +/** + Create hob and install FvInfo PPI for recovery capsule. + + @param[in] FvImage Points to the DXE FV image. + @param[in] FvImageSize The length of the DXE FV image in bytes. + + @retval EFI_SUCCESS Create hob and install FvInfo PPI successfully. + @retval EFI_VOLUME_CORRUPTED The input data is not an FV. + @retval EFI_OUT_OF_RESOURCES No enough resource to process the input data. +**/ +EFI_STATUS +EFIAPI +CreateHobForRecoveryCapsule ( + IN VOID *FvImage, + IN UINTN FvImageSize + ) +{ + EFI_FIRMWARE_VOLUME_HEADER *FvHeader; + UINT32 FvAlignment; + UINT64 FvLength; + VOID *NewFvBuffer; + + // + // FvImage should be at its required alignment. + // + FvHeader = (EFI_FIRMWARE_VOLUME_HEADER *) FvImage; + // + // Validate FV Header, if not as expected, return + // + if (ReadUnaligned32 (&FvHeader->Signature) != EFI_FVH_SIGNATURE) { + DEBUG((DEBUG_ERROR, "CreateHobForRecoveryCapsule (Fv Signature Error)\n")); + return EFI_VOLUME_CORRUPTED; + } + // + // If EFI_FVB2_WEAK_ALIGNMENT is set in the volume header then the first byte of the volume + // can be aligned on any power-of-two boundary. A weakly aligned volume can not be moved from + // its initial linked location and maintain its alignment. + // + if ((ReadUnaligned32 (&FvHeader->Attributes) & EFI_FVB2_WEAK_ALIGNMENT) != EFI_FVB2_WEAK_ALIGNMENT) { + // + // Get FvHeader alignment + // + FvAlignment = 1 << ((ReadUnaligned32 (&FvHeader->Attributes) & EFI_FVB2_ALIGNMENT) >> 16); + // + // FvAlignment must be greater than or equal to 8 bytes of the minimum FFS alignment value. + // + if (FvAlignment < 8) { + FvAlignment = 8; + } + // + // Allocate the aligned buffer for the FvImage. + // + if ((UINTN) FvHeader % FvAlignment != 0) { + DEBUG((DEBUG_INFO, "CreateHobForRecoveryCapsule (FvHeader 0x%lx is not aligned)\n", (UINT64)(UINTN)FvHeader)); + FvLength = ReadUnaligned64 (&FvHeader->FvLength); + NewFvBuffer = AllocateAlignedPages (EFI_SIZE_TO_PAGES ((UINTN) FvLength), FvAlignment); + if (NewFvBuffer == NULL) { + DEBUG((DEBUG_ERROR, "CreateHobForRecoveryCapsule (Not enough resource to allocate 0x%lx bytes)\n", FvLength)); + return EFI_OUT_OF_RESOURCES; + } + CopyMem (NewFvBuffer, FvHeader, (UINTN) FvLength); + FvHeader = (EFI_FIRMWARE_VOLUME_HEADER*) NewFvBuffer; + } + } + + BuildFvHob((UINT64)(UINTN)FvHeader, FvHeader->FvLength); + DEBUG((DEBUG_INFO, "BuildFvHob (FV in recovery) - 0x%lx - 0x%lx\n", (UINT64)(UINTN)FvHeader, FvHeader->FvLength)); + + PeiServicesInstallFvInfoPpi( + &FvHeader->FileSystemGuid, + (VOID *)FvHeader, + (UINT32)FvHeader->FvLength, + NULL, + NULL + ); + + return EFI_SUCCESS; +} + +/** + Create recovery context based upon System Firmware image and config file. + + @param[in] SystemFirmwareImage Points to the System Firmware image. + @param[in] SystemFirmwareImageSize The length of the System Firmware image in bytes. + @param[in] ConfigImage Points to the config file image. + @param[in] ConfigImageSize The length of the config file image in bytes. + + @retval EFI_SUCCESS Process Recovery Image successfully. +**/ +EFI_STATUS +RecoverImage ( + IN VOID *SystemFirmwareImage, + IN UINTN SystemFirmwareImageSize, + IN VOID *ConfigImage, + IN UINTN ConfigImageSize + ) +{ + EFI_STATUS Status; + RECOVERY_CONFIG_DATA *ConfigData; + RECOVERY_CONFIG_DATA *RecoveryConfigData; + CONFIG_HEADER ConfigHeader; + UINTN Index; + + if (ConfigImage == NULL) { + DEBUG((DEBUG_INFO, "RecoverImage (NoConfig)\n")); + Status = CreateHobForRecoveryCapsule( + SystemFirmwareImage, + SystemFirmwareImageSize + ); + return Status; + } + + ConfigData = NULL; + ZeroMem (&ConfigHeader, sizeof(ConfigHeader)); + Status = ParseRecoveryDataFile ( + ConfigImage, + ConfigImageSize, + &ConfigHeader, + &ConfigData + ); + DEBUG((DEBUG_INFO, "ParseRecoveryDataFile - %r\n", Status)); + if (EFI_ERROR(Status)) { + return Status; + } + DEBUG((DEBUG_INFO, "ConfigHeader.NumOfRecovery - 0x%x\n", ConfigHeader.NumOfRecovery)); + DEBUG((DEBUG_INFO, "PcdEdkiiSystemFirmwareFileGuid - %g\n", PcdGetPtr(PcdEdkiiSystemFirmwareFileGuid))); + + Index = 0; + RecoveryConfigData = ConfigData; + while (Index < ConfigHeader.NumOfRecovery) { + if (CompareGuid(&RecoveryConfigData->FileGuid, PcdGetPtr(PcdEdkiiSystemFirmwareFileGuid))) { + DEBUG((DEBUG_INFO, "FileGuid - %g (processing)\n", &RecoveryConfigData->FileGuid)); + Status = CreateHobForRecoveryCapsule ( + (UINT8 *)SystemFirmwareImage + RecoveryConfigData->ImageOffset, + RecoveryConfigData->Length + ); + // + // Shall updates be serialized so that if a recovery FV is not successfully completed, + // the remaining updates won't be performed. + // + if (EFI_ERROR (Status)) { + break; + } + } else { + DEBUG((DEBUG_INFO, "FileGuid - %g (ignored)\n", &RecoveryConfigData->FileGuid)); + } + + Index++; + RecoveryConfigData++; + } + + return Status; +} + +/** + Process recovery image. + + Caution: This function may receive untrusted input. + + @param[in] Image Points to the recovery image. + @param[in] Length The length of the recovery image in bytes. + + @retval EFI_SUCCESS Process Recovery Image successfully. + @retval EFI_SECURITY_VIOLATION Recovery image is not processed due to security violation. +**/ +EFI_STATUS +ProcessRecoveryImage ( + IN VOID *Image, + IN UINTN Length + ) +{ + UINT32 LastAttemptVersion; + UINT32 LastAttemptStatus; + EFI_STATUS Status; + VOID *SystemFirmwareImage; + UINTN SystemFirmwareImageSize; + VOID *ConfigImage; + UINTN ConfigImageSize; + VOID *AuthenticatedImage; + UINTN AuthenticatedImageSize; + + AuthenticatedImage = NULL; + AuthenticatedImageSize = 0; + + Status = CapsuleAuthenticateSystemFirmware(Image, Length, TRUE, &LastAttemptVersion, &LastAttemptStatus, &AuthenticatedImage, &AuthenticatedImageSize); + if (EFI_ERROR(Status)) { + DEBUG((DEBUG_INFO, "CapsuleAuthenticateSystemFirmware - %r\n", Status)); + return Status; + } + + ExtractSystemFirmwareImage(AuthenticatedImage, AuthenticatedImageSize, &SystemFirmwareImage, &SystemFirmwareImageSize); + ExtractConfigImage(AuthenticatedImage, AuthenticatedImageSize, &ConfigImage, &ConfigImageSize); + + Status = RecoverImage(SystemFirmwareImage, SystemFirmwareImageSize, ConfigImage, ConfigImageSize); + if (EFI_ERROR(Status)) { + DEBUG((DEBUG_INFO, "RecoverImage - %r\n", Status)); + return Status; + } + + return EFI_SUCCESS; +} + +/** + Process Firmware management protocol data capsule. + + Caution: This function may receive untrusted input. + + 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. + + @param[in] CapsuleHeader Points to a capsule header. + @param[in] IsSystemFmp If this capsule is a system FMP capsule. + + @retval EFI_SUCCESS 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, + IN BOOLEAN IsSystemFmp + ) +{ + EFI_STATUS Status; + EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER *FmpCapsuleHeader; + EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *ImageHeader; + UINT8 *Image; + UINT64 *ItemOffsetList; + UINTN ItemIndex; + + if (!IsSystemFmp) { + return EFI_UNSUPPORTED; + } + + FmpCapsuleHeader = (EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER *)((UINT8 *)CapsuleHeader + CapsuleHeader->HeaderSize); + ItemOffsetList = (UINT64 *)(FmpCapsuleHeader + 1); + + for (ItemIndex = 0; ItemIndex < FmpCapsuleHeader->PayloadItemCount; ItemIndex++) { + ImageHeader = (EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *)((UINT8 *)FmpCapsuleHeader + ItemOffsetList[ItemIndex]); + 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, only match ImageTypeId. + // Header should exclude UpdateHardwareInstance field. + // If version is 2 Header should exclude ImageCapsuleSupport field. + // + 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); + } + } + + Status = ProcessRecoveryImage (Image, ImageHeader->UpdateImageSize); + if (EFI_ERROR(Status)) { + return Status; + } + } + + return EFI_SUCCESS; +} + +/** + Process recovery capsule image. + + Caution: This function may receive untrusted input. + + @param[in] CapsuleBuffer The capsule image buffer. + @param[in] CapsuleSize The size of the capsule image in bytes. + + @retval EFI_SUCCESS The recovery capsule is processed. + @retval EFI_SECURITY_VIOLATION The recovery capsule is not process because of security violation. + @retval EFI_NOT_FOUND The recovery capsule is not process because of unrecognization. +**/ +EFI_STATUS +EFIAPI +ProcessRecoveryCapsule ( + IN VOID *CapsuleBuffer, + IN UINTN CapsuleSize + ) +{ + EFI_STATUS Status; + BOOLEAN IsSystemFmp; + EFI_CAPSULE_HEADER *CapsuleHeader; + + CapsuleHeader = CapsuleBuffer; + if (!IsValidCapsuleHeader (CapsuleHeader, CapsuleSize)) { + DEBUG((DEBUG_ERROR, "CapsuleImageSize incorrect\n")); + return EFI_SECURITY_VIOLATION; + } + + // + // Check FMP capsule layout + // + if (IsFmpCapsuleGuid(&CapsuleHeader->CapsuleGuid)) { + DEBUG((DEBUG_INFO, "CreateHobForRecoveryCapsule\n")); + + DEBUG((DEBUG_INFO, "ProcessCapsuleImage for FmpCapsule ...\n")); + DEBUG((DEBUG_INFO, "ValidateFmpCapsule ...\n")); + Status = ValidateFmpCapsule(CapsuleHeader, &IsSystemFmp, NULL); + DEBUG((DEBUG_INFO, "ValidateFmpCapsule - %r\n", Status)); + if (EFI_ERROR(Status)) { + return Status; + } + + // + // Process EFI FMP Capsule + // + DEBUG((DEBUG_INFO, "ProcessFmpCapsuleImage ...\n")); + Status = ProcessFmpCapsuleImage(CapsuleHeader, IsSystemFmp); + DEBUG((DEBUG_INFO, "ProcessFmpCapsuleImage - %r\n", Status)); + + DEBUG((DEBUG_INFO, "CreateHobForRecoveryCapsule Done\n")); + return Status; + } + + return EFI_UNSUPPORTED; +} + +/** + Loads a DXE capsule from some media into memory and updates the HOB table + with the DXE firmware volume information. + + @param[in] PeiServices General-purpose services that are available to every PEIM. + @param[in] This Indicates the EFI_PEI_RECOVERY_MODULE_PPI instance. + + @retval EFI_SUCCESS The capsule was loaded correctly. + @retval EFI_DEVICE_ERROR A device error occurred. + @retval EFI_NOT_FOUND A recovery DXE capsule cannot be found. + +**/ +EFI_STATUS +EFIAPI +LoadRecoveryCapsule ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_RECOVERY_MODULE_PPI *This + ) +{ + EFI_STATUS Status; + EFI_PEI_DEVICE_RECOVERY_MODULE_PPI *DeviceRecoveryPpi; + UINTN NumberRecoveryCapsules; + UINTN Instance; + UINTN CapsuleInstance; + UINTN CapsuleSize; + EFI_GUID CapsuleType; + VOID *CapsuleBuffer; + + DEBUG((DEBUG_INFO | DEBUG_LOAD, "Recovery Entry\n")); + + for (Instance = 0; ; Instance++) { + Status = PeiServicesLocatePpi ( + &gEfiPeiDeviceRecoveryModulePpiGuid, + Instance, + NULL, + (VOID **)&DeviceRecoveryPpi + ); + DEBUG ((DEBUG_ERROR, "LoadRecoveryCapsule - LocateRecoveryPpi (%d) - %r\n", Instance, Status)); + if (EFI_ERROR (Status)) { + break; + } + NumberRecoveryCapsules = 0; + Status = DeviceRecoveryPpi->GetNumberRecoveryCapsules ( + (EFI_PEI_SERVICES **)PeiServices, + DeviceRecoveryPpi, + &NumberRecoveryCapsules + ); + DEBUG ((DEBUG_ERROR, "LoadRecoveryCapsule - GetNumberRecoveryCapsules (%d) - %r\n", NumberRecoveryCapsules, Status)); + if (EFI_ERROR (Status)) { + continue; + } + for (CapsuleInstance = 1; CapsuleInstance <= NumberRecoveryCapsules; CapsuleInstance++) { + CapsuleSize = 0; + Status = DeviceRecoveryPpi->GetRecoveryCapsuleInfo ( + (EFI_PEI_SERVICES **)PeiServices, + DeviceRecoveryPpi, + CapsuleInstance, + &CapsuleSize, + &CapsuleType + ); + DEBUG ((DEBUG_ERROR, "LoadRecoveryCapsule - GetRecoveryCapsuleInfo (%d - %x) - %r\n", CapsuleInstance, CapsuleSize, Status)); + if (EFI_ERROR (Status)) { + break; + } + + CapsuleBuffer = AllocatePages (EFI_SIZE_TO_PAGES(CapsuleSize)); + if (CapsuleBuffer == NULL) { + DEBUG ((DEBUG_ERROR, "LoadRecoveryCapsule - AllocatePool fail\n")); + continue; + } + Status = DeviceRecoveryPpi->LoadRecoveryCapsule ( + (EFI_PEI_SERVICES **)PeiServices, + DeviceRecoveryPpi, + CapsuleInstance, + CapsuleBuffer + ); + DEBUG ((DEBUG_ERROR, "LoadRecoveryCapsule - LoadRecoveryCapsule (%d) - %r\n", CapsuleInstance, Status)); + if (EFI_ERROR (Status)) { + FreePages (CapsuleBuffer, EFI_SIZE_TO_PAGES(CapsuleSize)); + break; + } + // + // good, load capsule buffer + // + Status = ProcessRecoveryCapsule (CapsuleBuffer, CapsuleSize); + return Status; + } + } + + return EFI_NOT_FOUND; +} -- cgit 1.2.3-korg