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 --- .../CapsuleOnDiskLoadPei/CapsuleOnDiskLoadPei.c | 437 +++++++++++++++++++++ 1 file changed, 437 insertions(+) create mode 100644 roms/edk2/MdeModulePkg/Universal/CapsuleOnDiskLoadPei/CapsuleOnDiskLoadPei.c (limited to 'roms/edk2/MdeModulePkg/Universal/CapsuleOnDiskLoadPei/CapsuleOnDiskLoadPei.c') diff --git a/roms/edk2/MdeModulePkg/Universal/CapsuleOnDiskLoadPei/CapsuleOnDiskLoadPei.c b/roms/edk2/MdeModulePkg/Universal/CapsuleOnDiskLoadPei/CapsuleOnDiskLoadPei.c new file mode 100644 index 000000000..e9b125008 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/CapsuleOnDiskLoadPei/CapsuleOnDiskLoadPei.c @@ -0,0 +1,437 @@ +/** @file + Recovery module. + + Caution: This module requires additional review when modified. + This module will have external input - Capsule-on-Disk Temp Relocation image. + This external input must be validated carefully to avoid security issue like + buffer overflow, integer overflow. + + RetrieveRelocatedCapsule() will receive untrusted input and do basic validation. + + Copyright (c) 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 defintions for this module +// +#include +#include +#include +#include +#include +#include + +#include +// +// The Library classes this module consumes +// +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + 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 +LoadCapsuleOnDisk ( + IN EFI_PEI_SERVICES **PeiServices, + IN EDKII_PEI_CAPSULE_ON_DISK_PPI *This + ); + +static EDKII_PEI_CAPSULE_ON_DISK_PPI mCapsuleOnDiskPpi = { + LoadCapsuleOnDisk +}; + +static EFI_PEI_PPI_DESCRIPTOR mCapsuleOnDiskPpiList = { + (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), + &gEdkiiPeiCapsuleOnDiskPpiGuid, + &mCapsuleOnDiskPpi +}; + +/** + Determine if capsule comes from memory by checking Capsule PPI. + + @param[in] PeiServices General purpose services available to every PEIM. + + @retval TRUE Capsule comes from memory. + @retval FALSE No capsule comes from memory. + +**/ +static +BOOLEAN +CheckCapsuleFromRam ( + IN CONST EFI_PEI_SERVICES **PeiServices + ) +{ + EFI_STATUS Status; + PEI_CAPSULE_PPI *Capsule; + + Status = PeiServicesLocatePpi ( + &gEfiPeiCapsulePpiGuid, + 0, + NULL, + (VOID **) &Capsule + ); + if (!EFI_ERROR(Status)) { + Status = Capsule->CheckCapsuleUpdate ((EFI_PEI_SERVICES **)PeiServices); + if (!EFI_ERROR(Status)) { + return TRUE; + } + } + + return FALSE; +} + +/** + Determine if it is a Capsule On Disk mode. + + @retval TRUE Capsule On Disk mode. + @retval FALSE Not capsule On Disk mode. + +**/ +static +BOOLEAN +IsCapsuleOnDiskMode ( + VOID + ) +{ + EFI_STATUS Status; + UINTN Size; + EFI_PEI_READ_ONLY_VARIABLE2_PPI *PPIVariableServices; + BOOLEAN CodRelocInfo; + + Status = PeiServicesLocatePpi ( + &gEfiPeiReadOnlyVariable2PpiGuid, + 0, + NULL, + (VOID **) &PPIVariableServices + ); + ASSERT_EFI_ERROR (Status); + + Size = sizeof (BOOLEAN); + Status = PPIVariableServices->GetVariable ( + PPIVariableServices, + COD_RELOCATION_INFO_VAR_NAME, + &gEfiCapsuleVendorGuid, + NULL, + &Size, + &CodRelocInfo + ); + + if (EFI_ERROR (Status) || Size != sizeof(BOOLEAN) || !CodRelocInfo) { + DEBUG (( DEBUG_ERROR, "Error Get CodRelocationInfo variable %r!\n", Status)); + return FALSE; + } + + return TRUE; +} + +/** + Gets capsule images from relocated capsule buffer. + Create Capsule hob for each Capsule. + + Caution: This function may receive untrusted input. + Capsule-on-Disk Temp Relocation image is external input, so this function + will validate Capsule-on-Disk Temp Relocation image to make sure the content + is read within the buffer. + + @param[in] RelocCapsuleBuf Buffer pointer to the relocated capsule. + @param[in] RelocCapsuleTotalSize Total size of the relocated capsule. + + @retval EFI_SUCCESS Succeed to get capsules and create hob. + @retval Others Fail to get capsules and create hob. + +**/ +static +EFI_STATUS +RetrieveRelocatedCapsule ( + IN UINT8 *RelocCapsuleBuf, + IN UINTN RelocCapsuleTotalSize + ) +{ + UINTN Index; + UINT8 *CapsuleDataBufEnd; + UINT8 *CapsulePtr; + UINT32 CapsuleSize; + UINT64 TotalImageSize; + UINTN CapsuleNum; + + // + // Temp file contains at least 2 capsule (including 1 capsule name capsule) & 1 UINT64 + // + if (RelocCapsuleTotalSize < sizeof(UINT64) + sizeof(EFI_CAPSULE_HEADER) * 2) { + return EFI_INVALID_PARAMETER; + } + + CopyMem(&TotalImageSize, RelocCapsuleBuf, sizeof(UINT64)); + + DEBUG ((DEBUG_INFO, "ProcessRelocatedCapsule CapsuleBuf %x TotalCapSize %lx\n", + RelocCapsuleBuf, TotalImageSize)); + + RelocCapsuleBuf += sizeof(UINT64); + + // + // TempCaspule file length check + // + if (MAX_ADDRESS - TotalImageSize <= sizeof(UINT64) || + (UINT64)RelocCapsuleTotalSize != TotalImageSize + sizeof(UINT64) || + (UINTN)(MAX_ADDRESS - (PHYSICAL_ADDRESS)(UINTN)RelocCapsuleBuf) <= TotalImageSize) { + return EFI_INVALID_PARAMETER; + } + + CapsuleDataBufEnd = RelocCapsuleBuf + TotalImageSize; + + // + // TempCapsule file integrity check over Capsule Header to ensure no data corruption in NV Var & Relocation storage + // + CapsulePtr = RelocCapsuleBuf; + CapsuleNum = 0; + + while (CapsulePtr < CapsuleDataBufEnd) { + if ((CapsuleDataBufEnd - CapsulePtr) < sizeof(EFI_CAPSULE_HEADER) || + ((EFI_CAPSULE_HEADER *)CapsulePtr)->CapsuleImageSize < sizeof(EFI_CAPSULE_HEADER) || + (UINTN)(MAX_ADDRESS - (PHYSICAL_ADDRESS)(UINTN)CapsulePtr) < ((EFI_CAPSULE_HEADER *)CapsulePtr)->CapsuleImageSize + ) { + break; + } + CapsulePtr += ((EFI_CAPSULE_HEADER *)CapsulePtr)->CapsuleImageSize; + CapsuleNum ++; + } + + if (CapsulePtr != CapsuleDataBufEnd) { + return EFI_INVALID_PARAMETER; + } + + // + // Capsule count must be less than PcdCapsuleMax, avoid building too many CvHobs to occupy all the free space in HobList. + // + if (CapsuleNum > PcdGet16 (PcdCapsuleMax)) { + return EFI_INVALID_PARAMETER; + } + + // + // Re-iterate the capsule buffer to create Capsule hob & Capsule Name Str Hob for each Capsule saved in relocated capsule file + // + CapsulePtr = RelocCapsuleBuf; + Index = 0; + while (CapsulePtr < CapsuleDataBufEnd) { + CapsuleSize = ((EFI_CAPSULE_HEADER *)CapsulePtr)->CapsuleImageSize; + BuildCvHob ((EFI_PHYSICAL_ADDRESS)(UINTN)CapsulePtr, CapsuleSize); + + DEBUG((DEBUG_INFO, "Capsule saved in address %x size %x\n", CapsulePtr, CapsuleSize)); + + CapsulePtr += CapsuleSize; + Index++; + } + + 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 +InitializeCapsuleOnDiskLoad ( + IN EFI_PEI_FILE_HANDLE FileHandle, + IN CONST EFI_PEI_SERVICES **PeiServices + ) +{ + EFI_STATUS Status; + UINTN BootMode; + UINTN FileNameSize; + + BootMode = GetBootModeHob(); + ASSERT(BootMode == BOOT_ON_FLASH_UPDATE); + + // + // If there are capsules provisioned in memory, quit. + // Only one capsule resource is accept, CapsuleOnRam's priority is higher than CapsuleOnDisk. + // + if (CheckCapsuleFromRam(PeiServices)) { + DEBUG((DEBUG_ERROR, "Capsule On Memory Detected! Quit.\n")); + return EFI_ABORTED; + } + + DEBUG_CODE ( + VOID *CapsuleOnDiskModePpi; + + if (!IsCapsuleOnDiskMode()){ + return EFI_NOT_FOUND; + } + + // + // Check Capsule On Disk Relocation flag. If exists, load capsule & create Capsule Hob + // + Status = PeiServicesLocatePpi ( + &gEdkiiPeiBootInCapsuleOnDiskModePpiGuid, + 0, + NULL, + (VOID **)&CapsuleOnDiskModePpi + ); + if (EFI_ERROR(Status)) { + DEBUG((DEBUG_ERROR, "Locate CapsuleOnDiskModePpi error %x\n", Status)); + return Status; + } + ); + + Status = PeiServicesInstallPpi (&mCapsuleOnDiskPpiList); + ASSERT_EFI_ERROR (Status); + + FileNameSize = PcdGetSize (PcdCoDRelocationFileName); + Status = PcdSetPtrS (PcdRecoveryFileName, &FileNameSize, (VOID *) PcdGetPtr(PcdCoDRelocationFileName)); + ASSERT_EFI_ERROR (Status); + + return Status; +} + +/** + 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 +LoadCapsuleOnDisk ( + IN EFI_PEI_SERVICES **PeiServices, + IN EDKII_PEI_CAPSULE_ON_DISK_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, "Load Capsule On Disk Entry\n")); + + for (Instance = 0; ; Instance++) { + Status = PeiServicesLocatePpi ( + &gEfiPeiDeviceRecoveryModulePpiGuid, + Instance, + NULL, + (VOID **)&DeviceRecoveryPpi + ); + DEBUG ((DEBUG_INFO, "LoadCapsuleOnDisk - LocateRecoveryPpi (%d) - %r\n", Instance, Status)); + if (EFI_ERROR (Status)) { + if (Instance == 0) { + REPORT_STATUS_CODE ( + EFI_ERROR_CODE | EFI_ERROR_MAJOR, + (EFI_SOFTWARE_PEI_MODULE | EFI_SW_PEI_EC_RECOVERY_PPI_NOT_FOUND) + ); + } + break; + } + NumberRecoveryCapsules = 0; + Status = DeviceRecoveryPpi->GetNumberRecoveryCapsules ( + (EFI_PEI_SERVICES **)PeiServices, + DeviceRecoveryPpi, + &NumberRecoveryCapsules + ); + DEBUG ((DEBUG_INFO, "LoadCapsuleOnDisk - 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_INFO, "LoadCapsuleOnDisk - GetRecoveryCapsuleInfo (%d - %x) - %r\n", CapsuleInstance, CapsuleSize, Status)); + if (EFI_ERROR (Status)) { + break; + } + + // + // Allocate the memory so that it gets preserved into DXE. + // Capsule is special because it may need to populate to system table + // + CapsuleBuffer = AllocateRuntimePages (EFI_SIZE_TO_PAGES (CapsuleSize)); + + if (CapsuleBuffer == NULL) { + DEBUG ((DEBUG_ERROR, "LoadCapsuleOnDisk - AllocateRuntimePages fail\n")); + continue; + } + + Status = DeviceRecoveryPpi->LoadRecoveryCapsule ( + (EFI_PEI_SERVICES **)PeiServices, + DeviceRecoveryPpi, + CapsuleInstance, + CapsuleBuffer + ); + DEBUG ((DEBUG_INFO, "LoadCapsuleOnDisk - LoadRecoveryCapsule (%d) - %r\n", CapsuleInstance, Status)); + if (EFI_ERROR (Status)) { + FreePages (CapsuleBuffer, EFI_SIZE_TO_PAGES(CapsuleSize)); + break; + } + + // + // Capsule Update Mode, Split relocated Capsule buffer into different capsule vehical hobs. + // + Status = RetrieveRelocatedCapsule(CapsuleBuffer, CapsuleSize); + + break; + } + + if (EFI_ERROR (Status)) { + REPORT_STATUS_CODE ( + EFI_ERROR_CODE | EFI_ERROR_MAJOR, + (EFI_SOFTWARE_PEI_MODULE | EFI_SW_PEI_EC_NO_RECOVERY_CAPSULE) + ); + } + + return Status; + } + + // + // Any attack against GPT, Relocation Info Variable or temp relocation file will result in no Capsule HOB and return EFI_NOT_FOUND. + // After flow to DXE phase. since no capsule hob is detected. Platform will clear Info flag and force restart. + // No volunerability will be exposed + // + + return EFI_NOT_FOUND; +} -- cgit