diff --git a/SignedCapsulePkg/Universal/RecoveryModuleLoadPei/ParseConfigProfile.c b/SignedCapsulePkg/Universal/RecoveryModuleLoadPei/ParseConfigProfile.c new file mode 100644 index 0000000000..fef1daf081 --- /dev/null +++ b/SignedCapsulePkg/Universal/RecoveryModuleLoadPei/ParseConfigProfile.c @@ -0,0 +1,163 @@ +/** @file + Parse the INI configuration file and pass the information to the recovery driver + so that the driver can perform recovery accordingly. + + 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 "RecoveryModuleLoadPei.h" +#include +#include + +#define MAX_LINE_LENGTH 512 + +/** + 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 + ) +{ + EFI_STATUS Status; + CHAR8 *SectionName; + CHAR8 Entry[MAX_LINE_LENGTH]; + UINTN Num; + UINTN Index; + EFI_GUID FileGuid; + VOID *Context; + + // + // First process the data buffer and get all sections and entries + // + Context = OpenIniFile(DataBuffer, BufferSize); + if (Context == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Now get NumOfUpdate + // + Status = GetDecimalUintnFromDataFile( + Context, + "Head", + "NumOfRecovery", + &Num + ); + if (EFI_ERROR(Status) || (Num == 0)) { + DEBUG((DEBUG_ERROR, "NumOfRecovery not found\n")); + CloseIniFile(Context); + return EFI_NOT_FOUND; + } + + ConfigHeader->NumOfRecovery = Num; + *RecoveryArray = AllocateZeroPool ((sizeof (RECOVERY_CONFIG_DATA) * Num)); + if (*RecoveryArray == NULL) { + CloseIniFile(Context); + return EFI_OUT_OF_RESOURCES; + } + + for (Index = 0 ; Index < ConfigHeader->NumOfRecovery; Index++) { + // + // Get the section name of each update + // + AsciiStrCpyS (Entry, MAX_LINE_LENGTH, "Recovery"); + AsciiValueToString(Entry + AsciiStrLen(Entry), 0, Index, 0); + Status = GetStringFromDataFile( + Context, + "Head", + Entry, + &SectionName + ); + if (EFI_ERROR(Status) || (SectionName == NULL)) { + DEBUG((DEBUG_ERROR, "[%d] %a not found\n", Index, Entry)); + CloseIniFile(Context); + return EFI_NOT_FOUND; + } + + // + // The section name of this update has been found. + // Now looks for all the config data of this update + // + + // + // FileBuid + // + Status = GetGuidFromDataFile( + Context, + SectionName, + "FileGuid", + &FileGuid + ); + if (EFI_ERROR(Status)) { + CloseIniFile(Context); + DEBUG((DEBUG_ERROR, "[%d] FileGuid not found\n", Index)); + return EFI_NOT_FOUND; + } + + CopyGuid(&((*RecoveryArray)[Index].FileGuid), &FileGuid); + + // + // Length + // + Status = GetHexUintnFromDataFile( + Context, + SectionName, + "Length", + &Num + ); + if (EFI_ERROR(Status)) { + CloseIniFile(Context); + DEBUG((DEBUG_ERROR, "[%d] Length not found\n", Index)); + return EFI_NOT_FOUND; + } + (*RecoveryArray)[Index].Length = Num; + + // + // ImageOffset + // + Status = GetHexUintnFromDataFile( + Context, + SectionName, + "ImageOffset", + &Num + ); + if (EFI_ERROR(Status)) { + CloseIniFile(Context); + DEBUG((DEBUG_ERROR, "[%d] ImageOffset not found\n", Index)); + return EFI_NOT_FOUND; + } + (*RecoveryArray)[Index].ImageOffset = Num; + } + + // + // Now all configuration data got. Free those temporary buffers + // + CloseIniFile(Context); + + return EFI_SUCCESS; +} + diff --git a/SignedCapsulePkg/Universal/RecoveryModuleLoadPei/RecoveryModuleLoadPei.c b/SignedCapsulePkg/Universal/RecoveryModuleLoadPei/RecoveryModuleLoadPei.c new file mode 100644 index 0000000000..58a15e4953 --- /dev/null +++ b/SignedCapsulePkg/Universal/RecoveryModuleLoadPei/RecoveryModuleLoadPei.c @@ -0,0 +1,806 @@ +/** @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, 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. + +**/ + +// +// The package level header files this module uses +// +#include +#include +// +// The protocols, PPI and GUID defintions 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 Capusule 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_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 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; + } + 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; + } + + // + // 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_SUCESS 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( + NULL, + (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_SUCESS 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_SUCESS 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; + + 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_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, + 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 + // + Image = (UINT8 *)ImageHeader + OFFSET_OF(EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER, UpdateHardwareInstance); + } + + 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; + } + + // + // Press 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, + FeaturePcdGet(PcdFrameworkCompatibilitySupport) ? CapsuleInstance - 1 : 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, + FeaturePcdGet(PcdFrameworkCompatibilitySupport) ? CapsuleInstance - 1 : 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; +} diff --git a/SignedCapsulePkg/Universal/RecoveryModuleLoadPei/RecoveryModuleLoadPei.h b/SignedCapsulePkg/Universal/RecoveryModuleLoadPei/RecoveryModuleLoadPei.h new file mode 100644 index 0000000000..ce3a35f0f9 --- /dev/null +++ b/SignedCapsulePkg/Universal/RecoveryModuleLoadPei/RecoveryModuleLoadPei.h @@ -0,0 +1,44 @@ +/** @file + Recovery module header file. + + 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. + +**/ + +#ifndef _RECOVERY_MODULE_LOAD_PEI_H_ +#define _RECOVERY_MODULE_LOAD_PEI_H_ + +#include + +#include +#include +#include +#include +#include +#include +#include + +// +// Update data +// + +typedef struct { + UINTN NumOfRecovery; +} CONFIG_HEADER; + +typedef struct { + UINTN Index; + EFI_GUID FileGuid; + UINTN Length; + UINTN ImageOffset; +} RECOVERY_CONFIG_DATA; + +#endif + diff --git a/SignedCapsulePkg/Universal/RecoveryModuleLoadPei/RecoveryModuleLoadPei.inf b/SignedCapsulePkg/Universal/RecoveryModuleLoadPei/RecoveryModuleLoadPei.inf new file mode 100644 index 0000000000..563be18821 --- /dev/null +++ b/SignedCapsulePkg/Universal/RecoveryModuleLoadPei/RecoveryModuleLoadPei.inf @@ -0,0 +1,71 @@ +## @file +# Recovery module. +# +# Load Recovery capsule and install FV. +# +# 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 = RecoveryModuleLoadPei + MODULE_UNI_FILE = RecoveryModuleLoadPei.uni + FILE_GUID = 4278A574-4769-4D60-B090-DD4916691590 + MODULE_TYPE = PEIM + VERSION_STRING = 1.0 + ENTRY_POINT = InitializeRecoveryModule + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# + +[Sources] + RecoveryModuleLoadPei.c + ParseConfigProfile.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + SignedCapsulePkg/SignedCapsulePkg.dec + +[LibraryClasses] + PeimEntryPoint + DebugLib + HobLib + BaseMemoryLib + MemoryAllocationLib + EdkiiSystemCapsuleLib + IniParsingLib + PrintLib + +[Ppis] + gEfiPeiRecoveryModulePpiGuid ## PRODUCES + gEfiPeiDeviceRecoveryModulePpiGuid ## CONSUMES + +[Guids] + gEfiFmpCapsuleGuid ## SOMETIMES_CONSUMES ## GUID + +[Pcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdSystemFmpCapsuleImageTypeIdGuid ## CONSUMES + gEfiSignedCapsulePkgTokenSpaceGuid.PcdEdkiiSystemFirmwareFileGuid ## CONSUMES + +[FeaturePcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdFrameworkCompatibilitySupport ## CONSUMES + +[depex] + gEfiPeiBootInRecoveryModePpiGuid + +[UserExtensions.TianoCore."ExtraFiles"] + RecoveryModuleLoadPeiExtra.uni + diff --git a/SignedCapsulePkg/Universal/RecoveryModuleLoadPei/RecoveryModuleLoadPei.uni b/SignedCapsulePkg/Universal/RecoveryModuleLoadPei/RecoveryModuleLoadPei.uni new file mode 100644 index 0000000000..44e6cdc99f --- /dev/null +++ b/SignedCapsulePkg/Universal/RecoveryModuleLoadPei/RecoveryModuleLoadPei.uni @@ -0,0 +1,21 @@ +// /** @file +// Recovery module. +// +// Load Recovery capsule and install FV. +// +// 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 "Recovery module." + +#string STR_MODULE_DESCRIPTION #language en-US "Load Recovery capsule and install FV." diff --git a/SignedCapsulePkg/Universal/RecoveryModuleLoadPei/RecoveryModuleLoadPeiExtra.uni b/SignedCapsulePkg/Universal/RecoveryModuleLoadPei/RecoveryModuleLoadPeiExtra.uni new file mode 100644 index 0000000000..56057c6b40 --- /dev/null +++ b/SignedCapsulePkg/Universal/RecoveryModuleLoadPei/RecoveryModuleLoadPeiExtra.uni @@ -0,0 +1,20 @@ +// /** @file +// RecoveryModuleLoadPei Localized Strings and Content +// +// 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_PROPERTIES_MODULE_NAME +#language en-US +"RecoveryModuleLoad PEI Driver" + +