/** @file
Provides FMP capsule dependency check services when updating the firmware
image of a FMP device.
Copyright (c) Microsoft Corporation.
Copyright (c) 2020, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/**
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;
}