| /** @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.<BR> | |
| SPDX-License-Identifier: BSD-2-Clause-Patent | |
| **/ | |
| // | |
| // The package level header files this module uses | |
| // | |
| #include <Uefi.h> | |
| #include <PiPei.h> | |
| // | |
| // The protocols, PPI and GUID definitions for this module | |
| // | |
| #include <Ppi/MasterBootMode.h> | |
| #include <Ppi/BootInRecoveryMode.h> | |
| #include <Ppi/RecoveryModule.h> | |
| #include <Ppi/DeviceRecoveryModule.h> | |
| #include <Ppi/FirmwareVolumeInfo.h> | |
| #include <Guid/FirmwareFileSystem2.h> | |
| #include <Guid/FmpCapsule.h> | |
| #include <Guid/EdkiiSystemFmpCapsule.h> | |
| // | |
| // The Library classes this module consumes | |
| // | |
| #include <Library/DebugLib.h> | |
| #include <Library/PeimEntryPoint.h> | |
| #include <Library/PeiServicesLib.h> | |
| #include <Library/HobLib.h> | |
| #include <Library/BaseMemoryLib.h> | |
| #include <Library/MemoryAllocationLib.h> | |
| #include <Library/PcdLib.h> | |
| #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; | |
| } |