/** @file | |
Provides FMP capsule dependency check services when updating the firmware | |
image of a FMP device. | |
Copyright (c) Microsoft Corporation.<BR> | |
Copyright (c) 2020, Intel Corporation. All rights reserved.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include <PiDxe.h> | |
#include <Library/BaseLib.h> | |
#include <Library/BaseMemoryLib.h> | |
#include <Library/DebugLib.h> | |
#include <Library/FmpDependencyLib.h> | |
#include <Library/FmpDependencyCheckLib.h> | |
#include <Library/MemoryAllocationLib.h> | |
#include <Library/UefiLib.h> | |
#include <Library/UefiBootServicesTableLib.h> | |
#include <Guid/SystemResourceTable.h> | |
#include <LastAttemptStatus.h> | |
#include <FmpLastAttemptStatus.h> | |
/** | |
Check dependency for firmware update. | |
@param[in] ImageTypeId Image Type Id. | |
@param[in] Version New version. | |
@param[in] Dependencies Fmp dependency. | |
@param[in] DependenciesSize Size, in bytes, of the Fmp dependency. | |
@param[out] LastAttemptStatus An optional pointer to a UINT32 that holds the | |
last attempt status to report back to the caller. | |
This function will set the value to LAST_ATTEMPT_STATUS_SUCCESS | |
if an error code is not set. | |
@retval TRUE Dependencies are satisfied. | |
@retval FALSE Dependencies are unsatisfied or dependency check fails. | |
**/ | |
BOOLEAN | |
EFIAPI | |
CheckFmpDependency ( | |
IN EFI_GUID ImageTypeId, | |
IN UINT32 Version, | |
IN EFI_FIRMWARE_IMAGE_DEP *Dependencies, OPTIONAL | |
IN UINT32 DependenciesSize, | |
OUT UINT32 *LastAttemptStatus OPTIONAL | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_HANDLE *HandleBuffer; | |
UINTN Index; | |
EFI_FIRMWARE_MANAGEMENT_PROTOCOL *Fmp; | |
UINTN ImageInfoSize; | |
UINT32 LocalLastAttemptStatus; | |
UINT32 *DescriptorVer; | |
UINT8 FmpImageInfoCount; | |
UINTN *DescriptorSize; | |
UINT32 PackageVersion; | |
CHAR16 *PackageVersionName; | |
UINTN NumberOfFmpInstance; | |
EFI_FIRMWARE_IMAGE_DESCRIPTOR **FmpImageInfoBuf; | |
FMP_DEPEX_CHECK_VERSION_DATA *FmpVersions; | |
UINTN FmpVersionsCount; | |
BOOLEAN IsSatisfied; | |
LocalLastAttemptStatus = LAST_ATTEMPT_STATUS_SUCCESS; | |
FmpImageInfoBuf = NULL; | |
DescriptorVer = NULL; | |
DescriptorSize = NULL; | |
NumberOfFmpInstance = 0; | |
FmpVersions = NULL; | |
FmpVersionsCount = 0; | |
IsSatisfied = TRUE; | |
PackageVersionName = NULL; | |
// | |
// Get ImageDescriptors of all FMP instances, and archive them for dependency evaluation. | |
// | |
Status = gBS->LocateHandleBuffer ( | |
ByProtocol, | |
&gEfiFirmwareManagementProtocolGuid, | |
NULL, | |
&NumberOfFmpInstance, | |
&HandleBuffer | |
); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_ERROR, "CheckFmpDependency: Get Firmware Management Protocol failed. (%r)", Status)); | |
IsSatisfied = FALSE; | |
LocalLastAttemptStatus = LAST_ATTEMPT_STATUS_DEPENDENCY_CHECK_LIB_ERROR_FMP_PROTOCOL_NOT_FOUND; | |
goto cleanup; | |
} | |
FmpImageInfoBuf = AllocateZeroPool (sizeof(EFI_FIRMWARE_IMAGE_DESCRIPTOR *) * NumberOfFmpInstance); | |
if (FmpImageInfoBuf == NULL) { | |
IsSatisfied = FALSE; | |
LocalLastAttemptStatus = LAST_ATTEMPT_STATUS_DEPENDENCY_CHECK_LIB_ERROR_MEM_ALLOC_FMP_INFO_BUFFER_FAILED; | |
goto cleanup; | |
} | |
DescriptorVer = AllocateZeroPool (sizeof(UINT32) * NumberOfFmpInstance); | |
if (DescriptorVer == NULL ) { | |
IsSatisfied = FALSE; | |
LocalLastAttemptStatus = LAST_ATTEMPT_STATUS_DEPENDENCY_CHECK_LIB_ERROR_MEM_ALLOC_DESC_VER_BUFFER_FAILED; | |
goto cleanup; | |
} | |
DescriptorSize = AllocateZeroPool (sizeof(UINTN) * NumberOfFmpInstance); | |
if (DescriptorSize == NULL ) { | |
IsSatisfied = FALSE; | |
LocalLastAttemptStatus = LAST_ATTEMPT_STATUS_DEPENDENCY_CHECK_LIB_ERROR_MEM_ALLOC_DESC_SIZE_BUFFER_FAILED; | |
goto cleanup; | |
} | |
FmpVersions = AllocateZeroPool (sizeof(FMP_DEPEX_CHECK_VERSION_DATA) * NumberOfFmpInstance); | |
if (FmpVersions == NULL) { | |
IsSatisfied = FALSE; | |
LocalLastAttemptStatus = LAST_ATTEMPT_STATUS_DEPENDENCY_CHECK_LIB_ERROR_MEM_ALLOC_FMP_VER_BUFFER_FAILED; | |
goto cleanup; | |
} | |
for (Index = 0; Index < NumberOfFmpInstance; Index ++) { | |
Status = gBS->HandleProtocol ( | |
HandleBuffer[Index], | |
&gEfiFirmwareManagementProtocolGuid, | |
(VOID **) &Fmp | |
); | |
if (EFI_ERROR(Status)) { | |
continue; | |
} | |
ImageInfoSize = 0; | |
Status = Fmp->GetImageInfo ( | |
Fmp, | |
&ImageInfoSize, | |
NULL, | |
NULL, | |
NULL, | |
NULL, | |
NULL, | |
NULL | |
); | |
if (Status != EFI_BUFFER_TOO_SMALL) { | |
continue; | |
} | |
FmpImageInfoBuf[Index] = AllocateZeroPool (ImageInfoSize); | |
if (FmpImageInfoBuf[Index] == NULL) { | |
continue; | |
} | |
Status = Fmp->GetImageInfo ( | |
Fmp, | |
&ImageInfoSize, // ImageInfoSize | |
FmpImageInfoBuf[Index], // ImageInfo | |
&DescriptorVer[Index], // DescriptorVersion | |
&FmpImageInfoCount, // DescriptorCount | |
&DescriptorSize[Index], // DescriptorSize | |
&PackageVersion, // PackageVersion | |
&PackageVersionName // PackageVersionName | |
); | |
if (EFI_ERROR(Status)) { | |
FreePool (FmpImageInfoBuf[Index]); | |
FmpImageInfoBuf[Index] = NULL; | |
continue; | |
} | |
if (PackageVersionName != NULL) { | |
FreePool (PackageVersionName); | |
PackageVersionName = NULL; | |
} | |
CopyGuid (&FmpVersions[FmpVersionsCount].ImageTypeId, &FmpImageInfoBuf[Index]->ImageTypeId); | |
FmpVersions[FmpVersionsCount].Version = FmpImageInfoBuf[Index]->Version; | |
FmpVersionsCount ++; | |
} | |
// | |
// Evaluate firmware image's depex, against the version of other Fmp instances. | |
// | |
if (Dependencies != NULL) { | |
IsSatisfied = EvaluateDependency (Dependencies, DependenciesSize, FmpVersions, FmpVersionsCount, &LocalLastAttemptStatus); | |
} | |
if (!IsSatisfied) { | |
DEBUG ((DEBUG_ERROR, "CheckFmpDependency: %g\'s dependency is not satisfied!\n", ImageTypeId)); | |
goto cleanup; | |
} | |
cleanup: | |
if (FmpImageInfoBuf != NULL) { | |
for (Index = 0; Index < NumberOfFmpInstance; Index ++) { | |
if (FmpImageInfoBuf[Index] != NULL) { | |
FreePool (FmpImageInfoBuf[Index]); | |
} | |
} | |
FreePool (FmpImageInfoBuf); | |
} | |
if (DescriptorVer != NULL) { | |
FreePool (DescriptorVer); | |
} | |
if (DescriptorSize != NULL) { | |
FreePool (DescriptorSize); | |
} | |
if (FmpVersions != NULL) { | |
FreePool (FmpVersions); | |
} | |
if (LastAttemptStatus != NULL) { | |
*LastAttemptStatus = LocalLastAttemptStatus; | |
} | |
return IsSatisfied; | |
} |