--- /dev/null
+/** @file\r
+ Recovery module.\r
+\r
+ Caution: This module requires additional review when modified.\r
+ This module will have external input - capsule image.\r
+ This external input must be validated carefully to avoid security issue like\r
+ buffer overflow, integer overflow.\r
+\r
+ ProcessRecoveryCapsule(), ProcessFmpCapsuleImage(), ProcessRecoveryImage(),\r
+ ValidateFmpCapsule() will receive untrusted input and do basic validation.\r
+\r
+Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>\r
+This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution. The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+//\r
+// The package level header files this module uses\r
+//\r
+#include <Uefi.h>\r
+#include <PiPei.h>\r
+//\r
+// The protocols, PPI and GUID defintions for this module\r
+//\r
+#include <Ppi/MasterBootMode.h>\r
+#include <Ppi/BootInRecoveryMode.h>\r
+#include <Ppi/RecoveryModule.h>\r
+#include <Ppi/DeviceRecoveryModule.h>\r
+#include <Ppi/FirmwareVolumeInfo.h>\r
+#include <Guid/FirmwareFileSystem2.h>\r
+#include <Guid/FmpCapsule.h>\r
+#include <Guid/EdkiiSystemFmpCapsule.h>\r
+\r
+//\r
+// The Library classes this module consumes\r
+//\r
+#include <Library/DebugLib.h>\r
+#include <Library/PeimEntryPoint.h>\r
+#include <Library/PeiServicesLib.h>\r
+#include <Library/HobLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/PcdLib.h>\r
+\r
+#include "RecoveryModuleLoadPei.h"\r
+\r
+/**\r
+ Loads a DXE capsule from some media into memory and updates the HOB table\r
+ with the DXE firmware volume information.\r
+\r
+ @param[in] PeiServices General-purpose services that are available to every PEIM.\r
+ @param[in] This Indicates the EFI_PEI_RECOVERY_MODULE_PPI instance.\r
+\r
+ @retval EFI_SUCCESS The capsule was loaded correctly.\r
+ @retval EFI_DEVICE_ERROR A device error occurred.\r
+ @retval EFI_NOT_FOUND A recovery DXE capsule cannot be found.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+LoadRecoveryCapsule (\r
+ IN EFI_PEI_SERVICES **PeiServices,\r
+ IN EFI_PEI_RECOVERY_MODULE_PPI *This\r
+ );\r
+\r
+EFI_PEI_RECOVERY_MODULE_PPI mRecoveryPpi = {\r
+ LoadRecoveryCapsule\r
+};\r
+\r
+EFI_PEI_PPI_DESCRIPTOR mRecoveryPpiList = {\r
+ (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),\r
+ &gEfiPeiRecoveryModulePpiGuid,\r
+ &mRecoveryPpi\r
+};\r
+\r
+/**\r
+ Parse Config data file to get the updated data array.\r
+\r
+ @param[in] DataBuffer Config raw file buffer.\r
+ @param[in] BufferSize Size of raw buffer.\r
+ @param[in, out] ConfigHeader Pointer to the config header.\r
+ @param[in, out] RecoveryArray Pointer to the config of recovery data.\r
+\r
+ @retval EFI_NOT_FOUND No config data is found.\r
+ @retval EFI_OUT_OF_RESOURCES No enough memory is allocated.\r
+ @retval EFI_SUCCESS Parse the config file successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+ParseRecoveryDataFile (\r
+ IN UINT8 *DataBuffer,\r
+ IN UINTN BufferSize,\r
+ IN OUT CONFIG_HEADER *ConfigHeader,\r
+ IN OUT RECOVERY_CONFIG_DATA **RecoveryArray\r
+ );\r
+\r
+/**\r
+ Return if this FMP is a system FMP or a device FMP, based upon FmpImageInfo.\r
+\r
+ @param[in] FmpImageHeader A pointer to EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER\r
+\r
+ @return TRUE It is a system FMP.\r
+ @return FALSE It is a device FMP.\r
+**/\r
+BOOLEAN\r
+IsSystemFmpImage (\r
+ IN EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *FmpImageHeader\r
+ )\r
+{\r
+ GUID *Guid;\r
+ UINTN Count;\r
+ UINTN Index;\r
+\r
+ Guid = PcdGetPtr(PcdSystemFmpCapsuleImageTypeIdGuid);\r
+ Count = PcdGetSize(PcdSystemFmpCapsuleImageTypeIdGuid) / sizeof(GUID);\r
+\r
+ for (Index = 0; Index < Count; Index++, Guid++) {\r
+ if (CompareGuid(&FmpImageHeader->UpdateImageTypeId, Guid)) {\r
+ return TRUE;\r
+ }\r
+ }\r
+\r
+ return FALSE;\r
+}\r
+\r
+/**\r
+ Return if this CapsuleGuid is a FMP capsule GUID or not.\r
+\r
+ @param[in] CapsuleGuid A pointer to EFI_GUID\r
+\r
+ @return TRUE It is a FMP capsule GUID.\r
+ @return FALSE It is not a FMP capsule GUID.\r
+**/\r
+BOOLEAN\r
+IsFmpCapsuleGuid (\r
+ IN EFI_GUID *CapsuleGuid\r
+ )\r
+{\r
+ if (CompareGuid(&gEfiFmpCapsuleGuid, CapsuleGuid)) {\r
+ return TRUE;\r
+ }\r
+\r
+ return FALSE;\r
+}\r
+\r
+/**\r
+ This function assumes the input Capusule image already passes basic check in\r
+ ValidateFmpCapsule().\r
+\r
+ Criteria of system FMP capsule is:\r
+ 1) FmpCapsuleHeader->EmbeddedDriverCount is 0.\r
+ 2) FmpCapsuleHeader->PayloadItemCount is not 0.\r
+ 3) All ImageHeader->UpdateImageTypeId matches PcdSystemFmpCapsuleImageTypeIdGuid.\r
+\r
+ @param[in] CapsuleHeader Points to a capsule header.\r
+\r
+ @retval TRUE Input capsule is a correct system FMP capsule.\r
+ @retval FALSE Input capsule is not a correct system FMP capsule.\r
+**/\r
+BOOLEAN\r
+IsSystemFmpCapsuleImage (\r
+ IN EFI_CAPSULE_HEADER *CapsuleHeader\r
+ )\r
+{\r
+ EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER *FmpCapsuleHeader;\r
+ EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *ImageHeader;\r
+ UINT64 *ItemOffsetList;\r
+ UINT32 ItemNum;\r
+ UINTN Index;\r
+\r
+ FmpCapsuleHeader = (EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER *) ((UINT8 *) CapsuleHeader + CapsuleHeader->HeaderSize);\r
+\r
+ if (FmpCapsuleHeader->EmbeddedDriverCount != 0) {\r
+ return FALSE;\r
+ }\r
+\r
+ if (FmpCapsuleHeader->PayloadItemCount == 0) {\r
+ return FALSE;\r
+ }\r
+\r
+ ItemNum = FmpCapsuleHeader->EmbeddedDriverCount + FmpCapsuleHeader->PayloadItemCount;\r
+\r
+ ItemOffsetList = (UINT64 *)(FmpCapsuleHeader + 1);\r
+\r
+ for (Index = 0; Index < ItemNum; Index++) {\r
+ ImageHeader = (EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *)((UINT8 *)FmpCapsuleHeader + ItemOffsetList[Index]);\r
+ if (!IsSystemFmpImage(ImageHeader)) {\r
+ return FALSE;\r
+ }\r
+ }\r
+\r
+ return TRUE;\r
+}\r
+\r
+/**\r
+ Validate if it is valid capsule header\r
+\r
+ This function assumes the caller provided correct CapsuleHeader pointer\r
+ and CapsuleSize.\r
+\r
+ This function validates the fields in EFI_CAPSULE_HEADER.\r
+\r
+ @param[in] CapsuleHeader Points to a capsule header.\r
+ @param[in] CapsuleSize Size of the whole capsule image.\r
+\r
+**/\r
+BOOLEAN\r
+IsValidCapsuleHeader (\r
+ IN EFI_CAPSULE_HEADER *CapsuleHeader,\r
+ IN UINT64 CapsuleSize\r
+ )\r
+{\r
+ if (CapsuleHeader->CapsuleImageSize != CapsuleSize) {\r
+ return FALSE;\r
+ }\r
+ if (CapsuleHeader->HeaderSize >= CapsuleHeader->CapsuleImageSize) {\r
+ return FALSE;\r
+ }\r
+ return TRUE;\r
+}\r
+\r
+/**\r
+ Validate Fmp capsules layout.\r
+\r
+ Caution: This function may receive untrusted input.\r
+\r
+ This function assumes the caller validated the capsule by using\r
+ IsValidCapsuleHeader(), so that all fields in EFI_CAPSULE_HEADER are correct.\r
+ The capsule buffer size is CapsuleHeader->CapsuleImageSize.\r
+\r
+ This function validates the fields in EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER\r
+ and EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER.\r
+\r
+ @param[in] CapsuleHeader Points to a capsule header.\r
+ @param[out] IsSystemFmp If it is a system FMP.\r
+ @param[out] EmbeddedDriverCount The EmbeddedDriverCount in the FMP capsule.\r
+\r
+ @retval EFI_SUCESS Input capsule is a correct FMP capsule.\r
+ @retval EFI_INVALID_PARAMETER Input capsule is not a correct FMP capsule.\r
+**/\r
+EFI_STATUS\r
+ValidateFmpCapsule (\r
+ IN EFI_CAPSULE_HEADER *CapsuleHeader,\r
+ OUT BOOLEAN *IsSystemFmp, OPTIONAL\r
+ OUT UINT16 *EmbeddedDriverCount OPTIONAL\r
+ )\r
+{\r
+ EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER *FmpCapsuleHeader;\r
+ UINT8 *EndOfCapsule;\r
+ EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *ImageHeader;\r
+ UINT8 *EndOfPayload;\r
+ UINT64 *ItemOffsetList;\r
+ UINT32 ItemNum;\r
+ UINTN Index;\r
+ UINTN FmpCapsuleSize;\r
+ UINTN FmpCapsuleHeaderSize;\r
+ UINT64 FmpImageSize;\r
+ UINTN FmpImageHeaderSize;\r
+\r
+ if (CapsuleHeader->HeaderSize >= CapsuleHeader->CapsuleImageSize) {\r
+ DEBUG((DEBUG_ERROR, "HeaderSize(0x%x) >= CapsuleImageSize(0x%x)\n", CapsuleHeader->HeaderSize, CapsuleHeader->CapsuleImageSize));\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ FmpCapsuleHeader = (EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER *) ((UINT8 *) CapsuleHeader + CapsuleHeader->HeaderSize);\r
+ EndOfCapsule = (UINT8 *) CapsuleHeader + CapsuleHeader->CapsuleImageSize;\r
+ FmpCapsuleSize = (UINTN)EndOfCapsule - (UINTN)FmpCapsuleHeader;\r
+\r
+ if (FmpCapsuleSize < sizeof(EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER)) {\r
+ DEBUG((DEBUG_ERROR, "FmpCapsuleSize(0x%x) < EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER\n", FmpCapsuleSize));\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ // Check EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER\r
+ if (FmpCapsuleHeader->Version != EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER_INIT_VERSION) {\r
+ DEBUG((DEBUG_ERROR, "FmpCapsuleHeader->Version(0x%x) != EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER_INIT_VERSION\n", FmpCapsuleHeader->Version));\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ ItemOffsetList = (UINT64 *)(FmpCapsuleHeader + 1);\r
+\r
+ // No overflow\r
+ ItemNum = FmpCapsuleHeader->EmbeddedDriverCount + FmpCapsuleHeader->PayloadItemCount;\r
+\r
+ if ((FmpCapsuleSize - sizeof(EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER))/sizeof(UINT64) < ItemNum) {\r
+ DEBUG((DEBUG_ERROR, "ItemNum(0x%x) too big\n", ItemNum));\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ FmpCapsuleHeaderSize = sizeof(EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER) + sizeof(UINT64)*ItemNum;\r
+\r
+ // Check ItemOffsetList\r
+ for (Index = 0; Index < ItemNum; Index++) {\r
+ if (ItemOffsetList[Index] >= FmpCapsuleSize) {\r
+ DEBUG((DEBUG_ERROR, "ItemOffsetList[%d](0x%lx) >= FmpCapsuleSize(0x%x)\n", Index, ItemOffsetList[Index], FmpCapsuleSize));\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ if (ItemOffsetList[Index] < FmpCapsuleHeaderSize) {\r
+ DEBUG((DEBUG_ERROR, "ItemOffsetList[%d](0x%lx) < FmpCapsuleHeaderSize(0x%x)\n", Index, ItemOffsetList[Index], FmpCapsuleHeaderSize));\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ //\r
+ // All the address in ItemOffsetList must be stored in ascending order\r
+ //\r
+ if (Index > 0) {\r
+ if (ItemOffsetList[Index] <= ItemOffsetList[Index - 1]) {\r
+ DEBUG((DEBUG_ERROR, "ItemOffsetList[%d](0x%lx) < ItemOffsetList[%d](0x%x)\n", Index, ItemOffsetList[Index], Index, ItemOffsetList[Index - 1]));\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ }\r
+ }\r
+\r
+ // Check EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER\r
+ for (Index = FmpCapsuleHeader->EmbeddedDriverCount; Index < ItemNum; Index++) {\r
+ ImageHeader = (EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *)((UINT8 *)FmpCapsuleHeader + ItemOffsetList[Index]);\r
+ if (Index == ItemNum - 1) {\r
+ EndOfPayload = (UINT8 *)((UINTN)EndOfCapsule - (UINTN)FmpCapsuleHeader);\r
+ } else {\r
+ EndOfPayload = (UINT8 *)(UINTN)ItemOffsetList[Index+1];\r
+ }\r
+ FmpImageSize = (UINTN)EndOfPayload - ItemOffsetList[Index];\r
+\r
+ if (FmpImageSize < OFFSET_OF(EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER, UpdateHardwareInstance)) {\r
+ DEBUG((DEBUG_ERROR, "FmpImageSize(0x%lx) < EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER\n", FmpImageSize));\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ FmpImageHeaderSize = sizeof(EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER);\r
+ if ((ImageHeader->Version > EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER_INIT_VERSION) ||\r
+ (ImageHeader->Version < 1)) {\r
+ DEBUG((DEBUG_ERROR, "ImageHeader->Version(0x%x) Unknown\n", ImageHeader->Version));\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ if (ImageHeader->Version < EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER_INIT_VERSION) {\r
+ FmpImageHeaderSize = OFFSET_OF(EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER, UpdateHardwareInstance);\r
+ }\r
+\r
+ // No overflow\r
+ if (FmpImageSize != (UINT64)FmpImageHeaderSize + (UINT64)ImageHeader->UpdateImageSize + (UINT64)ImageHeader->UpdateVendorCodeSize) {\r
+ DEBUG((DEBUG_ERROR, "FmpImageSize(0x%lx) mismatch, UpdateImageSize(0x%x) UpdateVendorCodeSize(0x%x)\n", FmpImageSize, ImageHeader->UpdateImageSize, ImageHeader->UpdateVendorCodeSize));\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ }\r
+\r
+ if (ItemNum == 0) {\r
+ //\r
+ // No driver & payload element in FMP\r
+ //\r
+ EndOfPayload = (UINT8 *)(FmpCapsuleHeader + 1);\r
+ if (EndOfPayload != EndOfCapsule) {\r
+ DEBUG((DEBUG_ERROR, "EndOfPayload(0x%x) mismatch, EndOfCapsule(0x%x)\n", EndOfPayload, EndOfCapsule));\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ //\r
+ // Check in system FMP capsule\r
+ //\r
+ if (IsSystemFmp != NULL) {\r
+ *IsSystemFmp = IsSystemFmpCapsuleImage(CapsuleHeader);\r
+ }\r
+\r
+ if (EmbeddedDriverCount != NULL) {\r
+ *EmbeddedDriverCount = FmpCapsuleHeader->EmbeddedDriverCount;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Recovery module entrypoint\r
+\r
+ @param[in] FileHandle Handle of the file being invoked.\r
+ @param[in] PeiServices Describes the list of possible PEI Services.\r
+\r
+ @return EFI_SUCCESS Recovery module is initialized.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+InitializeRecoveryModule (\r
+ IN EFI_PEI_FILE_HANDLE FileHandle,\r
+ IN CONST EFI_PEI_SERVICES **PeiServices\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINTN BootMode;\r
+\r
+ BootMode = GetBootModeHob();\r
+ ASSERT(BootMode == BOOT_IN_RECOVERY_MODE);\r
+\r
+ Status = (**PeiServices).InstallPpi (PeiServices, &mRecoveryPpiList);\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Create hob and install FvInfo PPI for recovery capsule.\r
+\r
+ @param[in] FvImage Points to the DXE FV image.\r
+ @param[in] FvImageSize The length of the DXE FV image in bytes.\r
+\r
+ @retval EFI_SUCESS Create hob and install FvInfo PPI successfully.\r
+ @retval EFI_VOLUME_CORRUPTED The input data is not an FV.\r
+ @retval EFI_OUT_OF_RESOURCES No enough resource to process the input data.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+CreateHobForRecoveryCapsule (\r
+ IN VOID *FvImage,\r
+ IN UINTN FvImageSize\r
+ )\r
+{\r
+ EFI_FIRMWARE_VOLUME_HEADER *FvHeader;\r
+ UINT32 FvAlignment;\r
+ UINT64 FvLength;\r
+ VOID *NewFvBuffer;\r
+\r
+ //\r
+ // FvImage should be at its required alignment.\r
+ //\r
+ FvHeader = (EFI_FIRMWARE_VOLUME_HEADER *) FvImage;\r
+ //\r
+ // Validate FV Header, if not as expected, return\r
+ //\r
+ if (ReadUnaligned32 (&FvHeader->Signature) != EFI_FVH_SIGNATURE) {\r
+ DEBUG((DEBUG_ERROR, "CreateHobForRecoveryCapsule (Fv Signature Error)\n"));\r
+ return EFI_VOLUME_CORRUPTED;\r
+ }\r
+ //\r
+ // If EFI_FVB2_WEAK_ALIGNMENT is set in the volume header then the first byte of the volume\r
+ // can be aligned on any power-of-two boundary. A weakly aligned volume can not be moved from\r
+ // its initial linked location and maintain its alignment.\r
+ //\r
+ if ((ReadUnaligned32 (&FvHeader->Attributes) & EFI_FVB2_WEAK_ALIGNMENT) != EFI_FVB2_WEAK_ALIGNMENT) {\r
+ //\r
+ // Get FvHeader alignment\r
+ //\r
+ FvAlignment = 1 << ((ReadUnaligned32 (&FvHeader->Attributes) & EFI_FVB2_ALIGNMENT) >> 16);\r
+ //\r
+ // FvAlignment must be greater than or equal to 8 bytes of the minimum FFS alignment value.\r
+ //\r
+ if (FvAlignment < 8) {\r
+ FvAlignment = 8;\r
+ }\r
+ //\r
+ // Allocate the aligned buffer for the FvImage.\r
+ //\r
+ if ((UINTN) FvHeader % FvAlignment != 0) {\r
+ DEBUG((DEBUG_INFO, "CreateHobForRecoveryCapsule (FvHeader 0x%lx is not aligned)\n", (UINT64)(UINTN)FvHeader));\r
+ FvLength = ReadUnaligned64 (&FvHeader->FvLength);\r
+ NewFvBuffer = AllocateAlignedPages (EFI_SIZE_TO_PAGES ((UINTN) FvLength), FvAlignment);\r
+ if (NewFvBuffer == NULL) {\r
+ DEBUG((DEBUG_ERROR, "CreateHobForRecoveryCapsule (Not enough resource to allocate 0x%lx bytes)\n", FvLength));\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ CopyMem (NewFvBuffer, FvHeader, (UINTN) FvLength);\r
+ FvHeader = (EFI_FIRMWARE_VOLUME_HEADER*) NewFvBuffer;\r
+ }\r
+ }\r
+\r
+ BuildFvHob((UINT64)(UINTN)FvHeader, FvHeader->FvLength);\r
+ DEBUG((DEBUG_INFO, "BuildFvHob (FV in recovery) - 0x%lx - 0x%lx\n", (UINT64)(UINTN)FvHeader, FvHeader->FvLength));\r
+\r
+ PeiServicesInstallFvInfoPpi(\r
+ NULL,\r
+ (VOID *)FvHeader,\r
+ (UINT32)FvHeader->FvLength,\r
+ NULL,\r
+ NULL\r
+ );\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Create recovery context based upon System Firmware image and config file.\r
+\r
+ @param[in] SystemFirmwareImage Points to the System Firmware image.\r
+ @param[in] SystemFirmwareImageSize The length of the System Firmware image in bytes.\r
+ @param[in] ConfigImage Points to the config file image.\r
+ @param[in] ConfigImageSize The length of the config file image in bytes.\r
+\r
+ @retval EFI_SUCESS Process Recovery Image successfully.\r
+**/\r
+EFI_STATUS\r
+RecoverImage (\r
+ IN VOID *SystemFirmwareImage,\r
+ IN UINTN SystemFirmwareImageSize,\r
+ IN VOID *ConfigImage,\r
+ IN UINTN ConfigImageSize\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ RECOVERY_CONFIG_DATA *ConfigData;\r
+ RECOVERY_CONFIG_DATA *RecoveryConfigData;\r
+ CONFIG_HEADER ConfigHeader;\r
+ UINTN Index;\r
+\r
+ if (ConfigImage == NULL) {\r
+ DEBUG((DEBUG_INFO, "RecoverImage (NoConfig)\n"));\r
+ Status = CreateHobForRecoveryCapsule(\r
+ SystemFirmwareImage,\r
+ SystemFirmwareImageSize\r
+ );\r
+ return Status;\r
+ }\r
+\r
+ ConfigData = NULL;\r
+ ZeroMem (&ConfigHeader, sizeof(ConfigHeader));\r
+ Status = ParseRecoveryDataFile (\r
+ ConfigImage,\r
+ ConfigImageSize,\r
+ &ConfigHeader,\r
+ &ConfigData\r
+ );\r
+ DEBUG((DEBUG_INFO, "ParseRecoveryDataFile - %r\n", Status));\r
+ if (EFI_ERROR(Status)) {\r
+ return Status;\r
+ }\r
+ DEBUG((DEBUG_INFO, "ConfigHeader.NumOfRecovery - 0x%x\n", ConfigHeader.NumOfRecovery));\r
+ DEBUG((DEBUG_INFO, "PcdEdkiiSystemFirmwareFileGuid - %g\n", PcdGetPtr(PcdEdkiiSystemFirmwareFileGuid)));\r
+\r
+ Index = 0;\r
+ RecoveryConfigData = ConfigData;\r
+ while (Index < ConfigHeader.NumOfRecovery) {\r
+ if (CompareGuid(&RecoveryConfigData->FileGuid, PcdGetPtr(PcdEdkiiSystemFirmwareFileGuid))) {\r
+ DEBUG((DEBUG_INFO, "FileGuid - %g (processing)\n", &RecoveryConfigData->FileGuid));\r
+ Status = CreateHobForRecoveryCapsule (\r
+ (UINT8 *)SystemFirmwareImage + RecoveryConfigData->ImageOffset,\r
+ RecoveryConfigData->Length\r
+ );\r
+ //\r
+ // Shall updates be serialized so that if a recovery FV is not successfully completed,\r
+ // the remaining updates won't be performed.\r
+ //\r
+ if (EFI_ERROR (Status)) {\r
+ break;\r
+ }\r
+ } else {\r
+ DEBUG((DEBUG_INFO, "FileGuid - %g (ignored)\n", &RecoveryConfigData->FileGuid));\r
+ }\r
+\r
+ Index++;\r
+ RecoveryConfigData++;\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Process recovery image.\r
+\r
+ Caution: This function may receive untrusted input.\r
+\r
+ @param[in] Image Points to the recovery image.\r
+ @param[in] Length The length of the recovery image in bytes.\r
+\r
+ @retval EFI_SUCESS Process Recovery Image successfully.\r
+ @retval EFI_SECURITY_VIOLATION Recovery image is not processed due to security violation.\r
+**/\r
+EFI_STATUS\r
+ProcessRecoveryImage (\r
+ IN VOID *Image,\r
+ IN UINTN Length\r
+ )\r
+{\r
+ UINT32 LastAttemptVersion;\r
+ UINT32 LastAttemptStatus;\r
+ EFI_STATUS Status;\r
+ VOID *SystemFirmwareImage;\r
+ UINTN SystemFirmwareImageSize;\r
+ VOID *ConfigImage;\r
+ UINTN ConfigImageSize;\r
+ VOID *AuthenticatedImage;\r
+ UINTN AuthenticatedImageSize;\r
+\r
+ Status = CapsuleAuthenticateSystemFirmware(Image, Length, TRUE, &LastAttemptVersion, &LastAttemptStatus, &AuthenticatedImage, &AuthenticatedImageSize);\r
+ if (EFI_ERROR(Status)) {\r
+ DEBUG((DEBUG_INFO, "CapsuleAuthenticateSystemFirmware - %r\n", Status));\r
+ return Status;\r
+ }\r
+\r
+ ExtractSystemFirmwareImage(AuthenticatedImage, AuthenticatedImageSize, &SystemFirmwareImage, &SystemFirmwareImageSize);\r
+ ExtractConfigImage(AuthenticatedImage, AuthenticatedImageSize, &ConfigImage, &ConfigImageSize);\r
+\r
+ Status = RecoverImage(SystemFirmwareImage, SystemFirmwareImageSize, ConfigImage, ConfigImageSize);\r
+ if (EFI_ERROR(Status)) {\r
+ DEBUG((DEBUG_INFO, "RecoverImage - %r\n", Status));\r
+ return Status;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Process Firmware management protocol data capsule.\r
+\r
+ Caution: This function may receive untrusted input.\r
+\r
+ This function assumes the caller validated the capsule by using\r
+ ValidateFmpCapsule(), so that all fields in EFI_CAPSULE_HEADER,\r
+ EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER and\r
+ EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER are correct.\r
+\r
+ @param[in] CapsuleHeader Points to a capsule header.\r
+ @param[in] IsSystemFmp If this capsule is a system FMP capsule.\r
+\r
+ @retval EFI_SUCESS Process Capsule Image successfully.\r
+ @retval EFI_UNSUPPORTED Capsule image is not supported by the firmware.\r
+ @retval EFI_VOLUME_CORRUPTED FV volume in the capsule is corrupted.\r
+ @retval EFI_OUT_OF_RESOURCES Not enough memory.\r
+**/\r
+EFI_STATUS\r
+ProcessFmpCapsuleImage (\r
+ IN EFI_CAPSULE_HEADER *CapsuleHeader,\r
+ IN BOOLEAN IsSystemFmp\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER *FmpCapsuleHeader;\r
+ EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *ImageHeader;\r
+ UINT8 *Image;\r
+ UINT64 *ItemOffsetList;\r
+ UINTN ItemIndex;\r
+\r
+ if (!IsSystemFmp) {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ FmpCapsuleHeader = (EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER *)((UINT8 *)CapsuleHeader + CapsuleHeader->HeaderSize);\r
+ ItemOffsetList = (UINT64 *)(FmpCapsuleHeader + 1);\r
+\r
+ for (ItemIndex = 0; ItemIndex < FmpCapsuleHeader->PayloadItemCount; ItemIndex++) {\r
+ ImageHeader = (EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *)((UINT8 *)FmpCapsuleHeader + ItemOffsetList[ItemIndex]);\r
+ if (ImageHeader->Version >= EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER_INIT_VERSION) {\r
+ Image = (UINT8 *)(ImageHeader + 1);\r
+ } else {\r
+ //\r
+ // If the EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER is version 1, only match ImageTypeId.\r
+ // Header should exclude UpdateHardwareInstance field\r
+ //\r
+ Image = (UINT8 *)ImageHeader + OFFSET_OF(EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER, UpdateHardwareInstance);\r
+ }\r
+\r
+ Status = ProcessRecoveryImage (Image, ImageHeader->UpdateImageSize);\r
+ if (EFI_ERROR(Status)) {\r
+ return Status;\r
+ }\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Process recovery capsule image.\r
+\r
+ Caution: This function may receive untrusted input.\r
+\r
+ @param[in] CapsuleBuffer The capsule image buffer.\r
+ @param[in] CapsuleSize The size of the capsule image in bytes.\r
+\r
+ @retval EFI_SUCCESS The recovery capsule is processed.\r
+ @retval EFI_SECURITY_VIOLATION The recovery capsule is not process because of security violation.\r
+ @retval EFI_NOT_FOUND The recovery capsule is not process because of unrecognization.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+ProcessRecoveryCapsule (\r
+ IN VOID *CapsuleBuffer,\r
+ IN UINTN CapsuleSize\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ BOOLEAN IsSystemFmp;\r
+ EFI_CAPSULE_HEADER *CapsuleHeader;\r
+\r
+ CapsuleHeader = CapsuleBuffer;\r
+ if (!IsValidCapsuleHeader (CapsuleHeader, CapsuleSize)) {\r
+ DEBUG((DEBUG_ERROR, "CapsuleImageSize incorrect\n"));\r
+ return EFI_SECURITY_VIOLATION;\r
+ }\r
+\r
+ //\r
+ // Check FMP capsule layout\r
+ //\r
+ if (IsFmpCapsuleGuid(&CapsuleHeader->CapsuleGuid)) {\r
+ DEBUG((DEBUG_INFO, "CreateHobForRecoveryCapsule\n"));\r
+\r
+ DEBUG((DEBUG_INFO, "ProcessCapsuleImage for FmpCapsule ...\n"));\r
+ DEBUG((DEBUG_INFO, "ValidateFmpCapsule ...\n"));\r
+ Status = ValidateFmpCapsule(CapsuleHeader, &IsSystemFmp, NULL);\r
+ DEBUG((DEBUG_INFO, "ValidateFmpCapsule - %r\n", Status));\r
+ if (EFI_ERROR(Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Press EFI FMP Capsule\r
+ //\r
+ DEBUG((DEBUG_INFO, "ProcessFmpCapsuleImage ...\n"));\r
+ Status = ProcessFmpCapsuleImage(CapsuleHeader, IsSystemFmp);\r
+ DEBUG((DEBUG_INFO, "ProcessFmpCapsuleImage - %r\n", Status));\r
+\r
+ DEBUG((DEBUG_INFO, "CreateHobForRecoveryCapsule Done\n"));\r
+ return Status;\r
+ }\r
+\r
+ return EFI_UNSUPPORTED;\r
+}\r
+\r
+/**\r
+ Loads a DXE capsule from some media into memory and updates the HOB table\r
+ with the DXE firmware volume information.\r
+\r
+ @param[in] PeiServices General-purpose services that are available to every PEIM.\r
+ @param[in] This Indicates the EFI_PEI_RECOVERY_MODULE_PPI instance.\r
+\r
+ @retval EFI_SUCCESS The capsule was loaded correctly.\r
+ @retval EFI_DEVICE_ERROR A device error occurred.\r
+ @retval EFI_NOT_FOUND A recovery DXE capsule cannot be found.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+LoadRecoveryCapsule (\r
+ IN EFI_PEI_SERVICES **PeiServices,\r
+ IN EFI_PEI_RECOVERY_MODULE_PPI *This\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_PEI_DEVICE_RECOVERY_MODULE_PPI *DeviceRecoveryPpi;\r
+ UINTN NumberRecoveryCapsules;\r
+ UINTN Instance;\r
+ UINTN CapsuleInstance;\r
+ UINTN CapsuleSize;\r
+ EFI_GUID CapsuleType;\r
+ VOID *CapsuleBuffer;\r
+\r
+ DEBUG((DEBUG_INFO | DEBUG_LOAD, "Recovery Entry\n"));\r
+\r
+ for (Instance = 0; ; Instance++) {\r
+ Status = PeiServicesLocatePpi (\r
+ &gEfiPeiDeviceRecoveryModulePpiGuid,\r
+ Instance,\r
+ NULL,\r
+ (VOID **)&DeviceRecoveryPpi\r
+ );\r
+ DEBUG ((DEBUG_ERROR, "LoadRecoveryCapsule - LocateRecoveryPpi (%d) - %r\n", Instance, Status));\r
+ if (EFI_ERROR (Status)) {\r
+ break;\r
+ }\r
+ NumberRecoveryCapsules = 0;\r
+ Status = DeviceRecoveryPpi->GetNumberRecoveryCapsules (\r
+ (EFI_PEI_SERVICES **)PeiServices,\r
+ DeviceRecoveryPpi,\r
+ &NumberRecoveryCapsules\r
+ );\r
+ DEBUG ((DEBUG_ERROR, "LoadRecoveryCapsule - GetNumberRecoveryCapsules (%d) - %r\n", NumberRecoveryCapsules, Status));\r
+ if (EFI_ERROR (Status)) {\r
+ continue;\r
+ }\r
+ for (CapsuleInstance = 1; CapsuleInstance <= NumberRecoveryCapsules; CapsuleInstance++) {\r
+ CapsuleSize = 0;\r
+ Status = DeviceRecoveryPpi->GetRecoveryCapsuleInfo (\r
+ (EFI_PEI_SERVICES **)PeiServices,\r
+ DeviceRecoveryPpi,\r
+ FeaturePcdGet(PcdFrameworkCompatibilitySupport) ? CapsuleInstance - 1 : CapsuleInstance,\r
+ &CapsuleSize,\r
+ &CapsuleType\r
+ );\r
+ DEBUG ((DEBUG_ERROR, "LoadRecoveryCapsule - GetRecoveryCapsuleInfo (%d - %x) - %r\n", CapsuleInstance, CapsuleSize, Status));\r
+ if (EFI_ERROR (Status)) {\r
+ break;\r
+ }\r
+\r
+ CapsuleBuffer = AllocatePages (EFI_SIZE_TO_PAGES(CapsuleSize));\r
+ if (CapsuleBuffer == NULL) {\r
+ DEBUG ((DEBUG_ERROR, "LoadRecoveryCapsule - AllocatePool fail\n"));\r
+ continue;\r
+ }\r
+ Status = DeviceRecoveryPpi->LoadRecoveryCapsule (\r
+ (EFI_PEI_SERVICES **)PeiServices,\r
+ DeviceRecoveryPpi,\r
+ FeaturePcdGet(PcdFrameworkCompatibilitySupport) ? CapsuleInstance - 1 : CapsuleInstance,\r
+ CapsuleBuffer\r
+ );\r
+ DEBUG ((DEBUG_ERROR, "LoadRecoveryCapsule - LoadRecoveryCapsule (%d) - %r\n", CapsuleInstance, Status));\r
+ if (EFI_ERROR (Status)) {\r
+ FreePages (CapsuleBuffer, EFI_SIZE_TO_PAGES(CapsuleSize));\r
+ break;\r
+ }\r
+ //\r
+ // good, load capsule buffer\r
+ //\r
+ Status = ProcessRecoveryCapsule (CapsuleBuffer, CapsuleSize);\r
+ return Status;\r
+ }\r
+ }\r
+\r
+ return EFI_NOT_FOUND;\r
+}\r