diff --git a/FmpDevicePkg/FmpDxe/Dependency.c b/FmpDevicePkg/FmpDxe/Dependency.c new file mode 100644 index 0000000000..b63a36b989 --- /dev/null +++ b/FmpDevicePkg/FmpDxe/Dependency.c @@ -0,0 +1,679 @@ +/** @file + Supports Capsule Dependency Expression. + + Copyright (c) 2020, Intel Corporation. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ +#include "FmpDxe.h" +#include "Dependency.h" + +// +// Define the initial size of the dependency expression evaluation stack +// +#define DEPEX_STACK_SIZE_INCREMENT 0x1000 + +// +// Type of stack element +// +typedef enum { + BooleanType, + VersionType +} ELEMENT_TYPE; + +// +// Value of stack element +// +typedef union { + BOOLEAN Boolean; + UINT32 Version; +} ELEMENT_VALUE; + +// +// Stack element used to evaluate dependency expressions +// +typedef struct { + ELEMENT_VALUE Value; + ELEMENT_TYPE Type; +} DEPEX_ELEMENT; + +// +// Global variable used to support dependency evaluation +// +UINTN mNumberOfFmpInstance = 0; +EFI_FIRMWARE_IMAGE_DESCRIPTOR **mFmpImageInfoBuf = NULL; + +// +// Indicates the status of dependency check, default value is DEPENDENCIES_SATISFIED. +// +UINT8 mDependenciesCheckStatus = DEPENDENCIES_SATISFIED; + +// +// Global stack used to evaluate dependency expressions +// +DEPEX_ELEMENT *mDepexEvaluationStack = NULL; +DEPEX_ELEMENT *mDepexEvaluationStackEnd = NULL; +DEPEX_ELEMENT *mDepexEvaluationStackPointer = NULL; + +/** + Grow size of the Depex stack + + @retval EFI_SUCCESS Stack successfully growed. + @retval EFI_OUT_OF_RESOURCES There is not enough system memory to grow the stack. + +**/ +EFI_STATUS +GrowDepexStack ( + VOID + ) +{ + DEPEX_ELEMENT *NewStack; + UINTN Size; + + Size = DEPEX_STACK_SIZE_INCREMENT; + if (mDepexEvaluationStack != NULL) { + Size = Size + (mDepexEvaluationStackEnd - mDepexEvaluationStack); + } + + NewStack = AllocatePool (Size * sizeof (DEPEX_ELEMENT)); + if (NewStack == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + if (mDepexEvaluationStack != NULL) { + // + // Copy to Old Stack to the New Stack + // + CopyMem ( + NewStack, + mDepexEvaluationStack, + (mDepexEvaluationStackEnd - mDepexEvaluationStack) * sizeof (DEPEX_ELEMENT) + ); + + // + // Free The Old Stack + // + FreePool (mDepexEvaluationStack); + } + + // + // Make the Stack pointer point to the old data in the new stack + // + mDepexEvaluationStackPointer = NewStack + (mDepexEvaluationStackPointer - mDepexEvaluationStack); + mDepexEvaluationStack = NewStack; + mDepexEvaluationStackEnd = NewStack + Size; + + return EFI_SUCCESS; +} + +/** + Push an element onto the Stack. + + @param[in] Value Value to push. + @param[in] Type Element Type + + @retval EFI_SUCCESS The value was pushed onto the stack. + @retval EFI_OUT_OF_RESOURCES There is not enough system memory to grow the stack. + @retval EFI_INVALID_PARAMETER Wrong stack element type. + +**/ +EFI_STATUS +Push ( + IN UINT32 Value, + IN UINTN Type + ) +{ + EFI_STATUS Status; + DEPEX_ELEMENT Element; + + // + // Check Type + // + if (Type != BooleanType && Type != VersionType) { + return EFI_INVALID_PARAMETER; + } + + // + // Check for a stack overflow condition + // + if (mDepexEvaluationStackPointer == mDepexEvaluationStackEnd) { + // + // Grow the stack + // + Status = GrowDepexStack (); + if (EFI_ERROR (Status)) { + return Status; + } + } + + Element.Value.Version = Value; + Element.Type = Type; + + // + // Push the item onto the stack + // + *mDepexEvaluationStackPointer = Element; + mDepexEvaluationStackPointer++; + + return EFI_SUCCESS; +} + + +/** + Pop an element from the stack. + + @param[in] Value Element to pop. + @param[in] Type Type of element. + + @retval EFI_SUCCESS The value was popped onto the stack. + @retval EFI_ACCESS_DENIED The pop operation underflowed the stack. + @retval EFI_INVALID_PARAMETER Type is mismatched. + +**/ +EFI_STATUS +Pop ( + OUT DEPEX_ELEMENT *Element, + IN ELEMENT_TYPE Type + ) +{ + // + // Check for a stack underflow condition + // + if (mDepexEvaluationStackPointer == mDepexEvaluationStack) { + return EFI_ACCESS_DENIED; + } + + // + // Pop the item off the stack + // + mDepexEvaluationStackPointer--; + *Element = *mDepexEvaluationStackPointer; + if ((*Element).Type != Type) { + return EFI_INVALID_PARAMETER; + } + return EFI_SUCCESS; +} + +/** + Evaluate the dependencies. + + @param[in] Dependencies Dependency expressions. + @param[in] DependenciesSize Size of Dependency expressions. + + @retval TRUE Dependency expressions evaluate to TRUE. + @retval FALSE Dependency expressions evaluate to FALSE. + +**/ +BOOLEAN +EvaluateDependencies ( + IN CONST EFI_FIRMWARE_IMAGE_DEP * Dependencies, + IN CONST UINTN DependenciesSize + ) +{ + EFI_STATUS Status; + UINT8 *Iterator; + UINT8 Index; + DEPEX_ELEMENT Element1; + DEPEX_ELEMENT Element2; + GUID ImageTypeId; + UINT32 Version; + + if (Dependencies == NULL || DependenciesSize == 0) { + return FALSE; + } + + // + // Clean out memory leaks in Depex Boolean stack. Leaks are only caused by + // incorrectly formed DEPEX expressions + // + mDepexEvaluationStackPointer = mDepexEvaluationStack; + + Iterator = (UINT8 *) Dependencies->Dependencies; + while (Iterator < (UINT8 *) Dependencies->Dependencies + DependenciesSize) { + switch (*Iterator) + { + case EFI_FMP_DEP_PUSH_GUID: + if (Iterator + sizeof (EFI_GUID) >= (UINT8 *) Dependencies->Dependencies + DependenciesSize) { + Status = EFI_INVALID_PARAMETER; + goto Error; + } + + CopyGuid (&ImageTypeId, (EFI_GUID *) (Iterator + 1)); + Iterator = Iterator + sizeof (EFI_GUID); + + for (Index = 0; Index < mNumberOfFmpInstance; Index ++){ + if (mFmpImageInfoBuf[Index] == NULL) { + continue; + } + if(CompareGuid (&mFmpImageInfoBuf[Index]->ImageTypeId, &ImageTypeId)){ + Status = Push (mFmpImageInfoBuf[Index]->Version, VersionType); + if (EFI_ERROR (Status)) { + goto Error; + } + break; + } + } + if (Index == mNumberOfFmpInstance) { + Status = EFI_NOT_FOUND; + goto Error; + } + break; + case EFI_FMP_DEP_PUSH_VERSION: + if (Iterator + sizeof (UINT32) >= (UINT8 *) Dependencies->Dependencies + DependenciesSize ) { + Status = EFI_INVALID_PARAMETER; + goto Error; + } + + Version = *(UINT32 *) (Iterator + 1); + Status = Push (Version, VersionType); + if (EFI_ERROR (Status)) { + goto Error; + } + Iterator = Iterator + sizeof (UINT32); + break; + case EFI_FMP_DEP_VERSION_STR: + Iterator += AsciiStrnLenS ((CHAR8 *) Iterator, DependenciesSize - (Iterator - Dependencies->Dependencies)); + break; + case EFI_FMP_DEP_AND: + Status = Pop (&Element1, BooleanType); + if (EFI_ERROR (Status)) { + goto Error; + } + Status = Pop (&Element2, BooleanType); + if (EFI_ERROR (Status)) { + goto Error; + } + Status = Push (Element1.Value.Boolean & Element2.Value.Boolean, BooleanType); + if (EFI_ERROR (Status)) { + goto Error; + } + break; + case EFI_FMP_DEP_OR: + Status = Pop (&Element1, BooleanType); + if (EFI_ERROR (Status)) { + goto Error; + } + Status = Pop(&Element2, BooleanType); + if (EFI_ERROR (Status)) { + goto Error; + } + Status = Push (Element1.Value.Boolean | Element2.Value.Boolean, BooleanType); + if (EFI_ERROR (Status)) { + goto Error; + } + break; + case EFI_FMP_DEP_NOT: + Status = Pop (&Element1, BooleanType); + if (EFI_ERROR (Status)) { + goto Error; + } + Status = Push (!(Element1.Value.Boolean), BooleanType); + if (EFI_ERROR (Status)) { + goto Error; + } + break; + case EFI_FMP_DEP_TRUE: + Status = Push (TRUE, BooleanType); + if (EFI_ERROR (Status)) { + goto Error; + } + break; + case EFI_FMP_DEP_FALSE: + Status = Push (FALSE, BooleanType); + if (EFI_ERROR (Status)) { + goto Error; + } + break; + case EFI_FMP_DEP_EQ: + Status = Pop (&Element1, VersionType); + if (EFI_ERROR (Status)) { + goto Error; + } + Status = Pop (&Element2, VersionType); + if (EFI_ERROR (Status)) { + goto Error; + } + Status = (Element1.Value.Version == Element2.Value.Version) ? Push (TRUE, BooleanType) : Push (FALSE, BooleanType); + if (EFI_ERROR (Status)) { + goto Error; + } + break; + case EFI_FMP_DEP_GT: + Status = Pop (&Element1, VersionType); + if (EFI_ERROR (Status)) { + goto Error; + } + Status = Pop (&Element2, VersionType); + if (EFI_ERROR (Status)) { + goto Error; + } + Status = (Element1.Value.Version > Element2.Value.Version) ? Push (TRUE, BooleanType) : Push (FALSE, BooleanType); + if (EFI_ERROR (Status)) { + goto Error; + } + break; + case EFI_FMP_DEP_GTE: + Status = Pop (&Element1, VersionType); + if (EFI_ERROR (Status)) { + goto Error; + } + Status = Pop (&Element2, VersionType); + if (EFI_ERROR (Status)) { + goto Error; + } + Status = (Element1.Value.Version >= Element2.Value.Version) ? Push (TRUE, BooleanType) : Push (FALSE, BooleanType); + if (EFI_ERROR (Status)) { + goto Error; + } + break; + case EFI_FMP_DEP_LT: + Status = Pop (&Element1, VersionType); + if (EFI_ERROR (Status)) { + goto Error; + } + Status = Pop (&Element2, VersionType); + if (EFI_ERROR (Status)) { + goto Error; + } + Status = (Element1.Value.Version < Element2.Value.Version) ? Push (TRUE, BooleanType) : Push (FALSE, BooleanType); + if (EFI_ERROR (Status)) { + goto Error; + } + break; + case EFI_FMP_DEP_LTE: + Status = Pop (&Element1, VersionType); + if (EFI_ERROR (Status)) { + goto Error; + } + Status = Pop (&Element2, VersionType); + if (EFI_ERROR (Status)) { + goto Error; + } + Status = (Element1.Value.Version <= Element2.Value.Version) ? Push (TRUE, BooleanType) : Push (FALSE, BooleanType); + if (EFI_ERROR (Status)) { + goto Error; + } + break; + case EFI_FMP_DEP_END: + Status = Pop (&Element1, BooleanType); + if (EFI_ERROR (Status)) { + goto Error; + } + return Element1.Value.Boolean; + default: + Status = EFI_INVALID_PARAMETER; + goto Error; + } + Iterator++; + } + +Error: + + DEBUG ((DEBUG_ERROR, "FmpDxe(%s): EvaluateDependencies() - RESULT = FALSE (Status = %r)\n", mImageIdName, Status)); + return FALSE; +} + +/** + Validate the dependency expression and output its size. + + @param[in] ImageDepex Pointer to the EFI_FIRMWARE_IMAGE_DEP. + @param[in] MaxDepexSize Max size of the dependency. + @param[out] DepexSize Size of dependency. + + @retval TRUE The capsule is valid. + @retval FALSE The capsule is invalid. + +**/ +BOOLEAN +ValidateImageDepex ( + IN EFI_FIRMWARE_IMAGE_DEP *ImageDepex, + IN CONST UINTN MaxDepexSize, + OUT UINT32 *DepexSize + ) +{ + UINT8 *Depex; + + *DepexSize = 0; + Depex = ImageDepex->Dependencies; + while (Depex < ImageDepex->Dependencies + MaxDepexSize) { + switch (*Depex) + { + case EFI_FMP_DEP_PUSH_GUID: + Depex += sizeof (EFI_GUID) + 1; + break; + case EFI_FMP_DEP_PUSH_VERSION: + Depex += sizeof (UINT32) + 1; + break; + case EFI_FMP_DEP_VERSION_STR: + Depex += AsciiStrnLenS ((CHAR8 *) Depex, ImageDepex->Dependencies + MaxDepexSize - Depex) + 1; + break; + case EFI_FMP_DEP_AND: + case EFI_FMP_DEP_OR: + case EFI_FMP_DEP_NOT: + case EFI_FMP_DEP_TRUE: + case EFI_FMP_DEP_FALSE: + case EFI_FMP_DEP_EQ: + case EFI_FMP_DEP_GT: + case EFI_FMP_DEP_GTE: + case EFI_FMP_DEP_LT: + case EFI_FMP_DEP_LTE: + Depex += 1; + break; + case EFI_FMP_DEP_END: + Depex += 1; + *DepexSize = (UINT32)(Depex - ImageDepex->Dependencies); + return TRUE; + default: + return FALSE; + } + } + + return FALSE; +} + + +/** + Get the size of dependencies. Assume the dependencies is validated before + calling this function. + + @param[in] Dependencies Pointer to the EFI_FIRMWARE_IMAGE_DEP. + + @retval The size of dependencies. + +**/ +UINTN +GetDepexSize ( + IN CONST EFI_FIRMWARE_IMAGE_DEP *Dependencies + ) +{ + UINTN Index; + + if (Dependencies == NULL) { + return 0; + } + + Index = 0; + while (Dependencies->Dependencies[Index] != EFI_FMP_DEP_END) { + Index ++; + } + + return Index + 1; +} + +/** + Check dependency for firmware update. + + @param[in] ImageTypeId Image Type Id. + @param[in] Version New version. + @param[in] Dependencies The dependencies. + @param[in] DependenciesSize Size of the dependencies + @param[out] IsSatisfied Indicate the dependencies is satisfied or not. + + @retval EFI_SUCCESS Dependency Evaluation is successful. + @retval Others Dependency Evaluation fails with unexpected error. + +**/ +EFI_STATUS +EvaluateImageDependencies ( + IN CONST EFI_GUID ImageTypeId, + IN CONST UINT32 Version, + IN CONST EFI_FIRMWARE_IMAGE_DEP *Dependencies, + IN CONST UINT32 DependenciesSize, + OUT BOOLEAN *IsSatisfied + ) +{ + EFI_STATUS Status; + EFI_HANDLE *HandleBuffer; + UINTN Index; + EFI_FIRMWARE_MANAGEMENT_PROTOCOL *Fmp; + UINTN ImageInfoSize; + UINT32 FmpImageInfoDescriptorVer; + UINT8 FmpImageInfoCount; + UINTN DescriptorSize; + UINT32 PackageVersion; + CHAR16 *PackageVersionName; + UINTN DepexSize; + + *IsSatisfied = TRUE; + PackageVersionName = NULL; + + // + // Get ImageDescriptors of all FMP instances, and archive them for depex evaluation. + // + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiFirmwareManagementProtocolGuid, + NULL, + &mNumberOfFmpInstance, + &HandleBuffer + ); + if (EFI_ERROR (Status)) { + return EFI_ABORTED; + } + + mFmpImageInfoBuf = AllocatePool (sizeof(EFI_FIRMWARE_IMAGE_DESCRIPTOR *) * mNumberOfFmpInstance); + if (mFmpImageInfoBuf == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + for (Index = 0; Index < mNumberOfFmpInstance; 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; + } + + mFmpImageInfoBuf[Index] = AllocateZeroPool (ImageInfoSize); + if (mFmpImageInfoBuf[Index] == NULL) { + continue; + } + + Status = Fmp->GetImageInfo ( + Fmp, + &ImageInfoSize, // ImageInfoSize + mFmpImageInfoBuf[Index], // ImageInfo + &FmpImageInfoDescriptorVer, // DescriptorVersion + &FmpImageInfoCount, // DescriptorCount + &DescriptorSize, // DescriptorSize + &PackageVersion, // PackageVersion + &PackageVersionName // PackageVersionName + ); + if (EFI_ERROR(Status)) { + FreePool (mFmpImageInfoBuf[Index]); + mFmpImageInfoBuf[Index] = NULL; + continue; + } + + if (PackageVersionName != NULL) { + FreePool (PackageVersionName); + PackageVersionName = NULL; + } + } + + // + // Step 1 - Evaluate firmware image's depex, against the version of other Fmp instances. + // + if (Dependencies != NULL) { + *IsSatisfied = EvaluateDependencies (Dependencies, DependenciesSize); + } + + if (!*IsSatisfied) { + goto cleanup; + } + + // + // Step 2 - Evaluate the depex of all other Fmp instances, against the new version in + // the firmware image. + // + + // + // Update the new version to mFmpImageInfoBuf. + // + for (Index = 0; Index < mNumberOfFmpInstance; Index ++) { + if (mFmpImageInfoBuf[Index] != NULL) { + if (CompareGuid (&ImageTypeId, &mFmpImageInfoBuf[Index]->ImageTypeId)) { + mFmpImageInfoBuf[Index]->Version = Version; + break; + } + } + } + + // + // Evaluate the Dependencies one by one. + // + for (Index = 0; Index < mNumberOfFmpInstance; Index ++) { + if (mFmpImageInfoBuf[Index] != NULL) { + // + // Skip the Fmp instance to be "SetImage". + // + if (CompareGuid (&ImageTypeId, &mFmpImageInfoBuf[Index]->ImageTypeId)) { + continue; + } + if ((mFmpImageInfoBuf[Index]->AttributesSupported & IMAGE_ATTRIBUTE_DEPENDENCY) && + mFmpImageInfoBuf[Index]->Dependencies != NULL) { + // + // Get the size of depex. + // Assume that the dependencies in EFI_FIRMWARE_IMAGE_DESCRIPTOR is validated when PopulateDescriptor(). + // + DepexSize = GetDepexSize (mFmpImageInfoBuf[Index]->Dependencies); + if (DepexSize > 0) { + *IsSatisfied = EvaluateDependencies (mFmpImageInfoBuf[Index]->Dependencies, DepexSize); + if (!*IsSatisfied) { + break; + } + } + } + } + } + +cleanup: + if (mFmpImageInfoBuf != NULL) { + for (Index = 0; Index < mNumberOfFmpInstance; Index ++) { + if (mFmpImageInfoBuf[Index] != NULL) { + FreePool (mFmpImageInfoBuf[Index]); + } + } + FreePool (mFmpImageInfoBuf); + } + + return EFI_SUCCESS; +} diff --git a/FmpDevicePkg/FmpDxe/Dependency.h b/FmpDevicePkg/FmpDxe/Dependency.h new file mode 100644 index 0000000000..a2aaaceeae --- /dev/null +++ b/FmpDevicePkg/FmpDxe/Dependency.h @@ -0,0 +1,63 @@ +/** @file + Fmp Capsule Dependency support functions for Firmware Management Protocol based + firmware updates. + + Copyright (c) 2020, Intel Corporation. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __DEPENDENCY_H__ +#define __DEPENDENCY_H__ + +#include +#include + +#define DEPENDENCIES_SATISFIED 0 +#define DEPENDENCIES_UNSATISFIED 1 +#define DEPENDENCIES_INVALID 2 + +extern UINT8 mDependenciesCheckStatus; + +/** + Validate the dependency expression and output its size. + + @param[in] ImageDepex Pointer to the EFI_FIRMWARE_IMAGE_DEP. + @param[in] MaxDepexSize Max size of the dependency. + @param[out] DepexSize Size of dependency. + + @retval TRUE The capsule is valid. + @retval FALSE The capsule is invalid. + +**/ +BOOLEAN +ValidateImageDepex ( + IN EFI_FIRMWARE_IMAGE_DEP *ImageDepex, + IN CONST UINTN MaxDepexSize, + OUT UINT32 *DepexSize + ); + +/** + Check dependency for firmware update. + + @param[in] ImageTypeId Image Type Id. + @param[in] Version New version. + @param[in] Dependencies The dependencies. + @param[in] DepexSize Size of the dependencies + @param[out] IsSatisfied Indicate the dependencies is satisfied or not. + + @retval EFI_SUCCESS Dependency Evaluation is successful. + @retval Others Dependency Evaluation fails with unexpected error. + +**/ +EFI_STATUS +EvaluateImageDependencies ( + IN CONST EFI_GUID ImageTypeId, + IN CONST UINT32 Version, + IN CONST EFI_FIRMWARE_IMAGE_DEP *Dependencies, + IN CONST UINT32 DependenciesSize, + OUT BOOLEAN *IsSatisfied + ); + +#endif diff --git a/FmpDevicePkg/FmpDxe/FmpDxe.c b/FmpDevicePkg/FmpDxe/FmpDxe.c index fe465af11e..aa92331966 100644 --- a/FmpDevicePkg/FmpDxe/FmpDxe.c +++ b/FmpDevicePkg/FmpDxe/FmpDxe.c @@ -4,7 +4,7 @@ information provided through PCDs and libraries. Copyright (c) 2016, Microsoft Corporation. All rights reserved.
- Copyright (c) 2018 - 2019, Intel Corporation. All rights reserved.
+ Copyright (c) 2018 - 2020, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent @@ -12,6 +12,7 @@ #include "FmpDxe.h" #include "VariableSupport.h" +#include "Dependency.h" /// /// FILE_GUID from FmpDxe.inf. When FmpDxe.inf is used in a platform, the @@ -275,6 +276,13 @@ PopulateDescriptor ( ) { EFI_STATUS Status; + VOID *Image; + UINTN ImageSize; + BOOLEAN IsDepexValid; + UINT32 DepexSize; + + Image = NULL; + ImageSize = 0; if (Private->DescriptorPopulated) { return; @@ -378,7 +386,47 @@ PopulateDescriptor ( Private->Descriptor.LastAttemptVersion = GetLastAttemptVersionFromVariable (Private); Private->Descriptor.LastAttemptStatus = GetLastAttemptStatusFromVariable (Private); + // + // Get the dependency from the FmpDeviceLib and populate it to the descriptor. + // + Private->Descriptor.Dependencies = NULL; + + // + // Check the attribute IMAGE_ATTRIBUTE_DEPENDENCY + // + if (Private->Descriptor.AttributesSupported & IMAGE_ATTRIBUTE_DEPENDENCY) { + // + // The parameter "Image" of FmpDeviceGetImage() is extended to contain the dependency. + // Get the dependency from the Image. + // + ImageSize = Private->Descriptor.Size; + Image = AllocatePool (ImageSize); + if (Image != NULL) { + Status = FmpDeviceGetImage (Image, &ImageSize); + if (Status == EFI_BUFFER_TOO_SMALL) { + FreePool (Image); + Image = AllocatePool (ImageSize); + if (Image != NULL) { + Status = FmpDeviceGetImage (Image, &ImageSize); + } + } + } + if (!EFI_ERROR (Status) && Image != NULL) { + IsDepexValid = ValidateImageDepex ((EFI_FIRMWARE_IMAGE_DEP *) Image, ImageSize, &DepexSize); + if (IsDepexValid == TRUE) { + Private->Descriptor.Dependencies = AllocatePool (DepexSize); + if (Private->Descriptor.Dependencies != NULL) { + CopyMem (Private->Descriptor.Dependencies->Dependencies, Image, DepexSize); + } + } + } + } + Private->DescriptorPopulated = TRUE; + + if (Image != NULL) { + FreePool (Image); + } } /** @@ -540,12 +588,17 @@ GetTheImage ( EFI_STATUS Status; FIRMWARE_MANAGEMENT_PRIVATE_DATA *Private; UINTN Size; + UINT8 *ImageBuffer; + UINTN ImageBufferSize; + UINT32 DepexSize; if (!FeaturePcdGet (PcdFmpDeviceStorageAccessEnable)) { return EFI_UNSUPPORTED; } - Status = EFI_SUCCESS; + Status = EFI_SUCCESS; + ImageBuffer = NULL; + DepexSize = 0; // // Retrieve the private context structure @@ -575,8 +628,45 @@ GetTheImage ( if (EFI_ERROR (Status)) { Size = 0; } - if (*ImageSize < Size) { - *ImageSize = Size; + + // + // The parameter "Image" of FmpDeviceGetImage() is extended to contain the dependency. + // Get the Fmp Payload from the Image. + // + ImageBufferSize = Size; + ImageBuffer = AllocatePool (ImageBufferSize); + if (ImageBuffer == NULL) { + DEBUG ((DEBUG_ERROR, "FmpDxe(%s): GetImage() - AllocatePool fails.\n", mImageIdName)); + Status = EFI_NOT_FOUND; + goto cleanup; + } + Status = FmpDeviceGetImage (ImageBuffer, &ImageBufferSize); + if (Status == EFI_BUFFER_TOO_SMALL) { + FreePool (ImageBuffer); + ImageBuffer = AllocatePool (ImageBufferSize); + if (ImageBuffer == NULL) { + DEBUG ((DEBUG_ERROR, "FmpDxe(%s): GetImage() - AllocatePool fails.\n", mImageIdName)); + Status = EFI_NOT_FOUND; + goto cleanup; + } + Status = FmpDeviceGetImage (ImageBuffer, &ImageBufferSize); + } + if (EFI_ERROR (Status)) { + goto cleanup; + } + + // + // Check the attribute IMAGE_ATTRIBUTE_DEPENDENCY + // + if (Private->Descriptor.AttributesSetting & IMAGE_ATTRIBUTE_DEPENDENCY) { + // + // Validate the dependency to get its size. + // + ValidateImageDepex ((EFI_FIRMWARE_IMAGE_DEP *) ImageBuffer, ImageBufferSize, &DepexSize); + } + + if (*ImageSize < ImageBufferSize - DepexSize) { + *ImageSize = ImageBufferSize - DepexSize; DEBUG ((DEBUG_VERBOSE, "FmpDxe(%s): GetImage() - ImageSize is to small.\n", mImageIdName)); Status = EFI_BUFFER_TOO_SMALL; goto cleanup; @@ -588,8 +678,17 @@ GetTheImage ( goto cleanup; } - Status = FmpDeviceGetImage (Image, ImageSize); + // + // Image is after the dependency expression. + // + *ImageSize = ImageBufferSize - DepexSize; + CopyMem (Image, ImageBuffer + DepexSize, *ImageSize); + Status = EFI_SUCCESS; + cleanup: + if (ImageBuffer != NULL) { + FreePool (ImageBuffer); + } return Status; } @@ -710,6 +809,10 @@ CheckTheImage ( UINTN PublicKeyDataLength; UINT8 *PublicKeyDataXdr; UINT8 *PublicKeyDataXdrEnd; + EFI_FIRMWARE_IMAGE_DEP *Dependencies; + UINT32 DependenciesSize; + BOOLEAN IsDepexValid; + BOOLEAN IsDepexSatisfied; Status = EFI_SUCCESS; RawSize = 0; @@ -718,6 +821,8 @@ CheckTheImage ( Version = 0; FmpHeaderSize = 0; AllHeaderSize = 0; + Dependencies = NULL; + DependenciesSize = 0; if (!FeaturePcdGet (PcdFmpDeviceStorageAccessEnable)) { return EFI_UNSUPPORTED; @@ -842,10 +947,33 @@ CheckTheImage ( } Status = GetFmpPayloadHeaderVersion (FmpPayloadHeader, FmpPayloadSize, &Version); if (EFI_ERROR (Status)) { - DEBUG ((DEBUG_ERROR, "FmpDxe(%s): CheckTheImage() - GetFmpPayloadHeaderVersion failed %r.\n", mImageIdName, Status)); - *ImageUpdatable = IMAGE_UPDATABLE_INVALID; - Status = EFI_SUCCESS; - goto cleanup; + // + // Check if there is dependency expression + // + IsDepexValid = ValidateImageDepex ((EFI_FIRMWARE_IMAGE_DEP*) FmpPayloadHeader, FmpPayloadSize, &DependenciesSize); + if (IsDepexValid && (DependenciesSize < FmpPayloadSize)) { + // + // Fmp payload is after dependency expression + // + Dependencies = (EFI_FIRMWARE_IMAGE_DEP*) FmpPayloadHeader; + FmpPayloadHeader = (UINT8 *) Dependencies + DependenciesSize; + FmpPayloadSize = FmpPayloadSize - DependenciesSize; + Status = GetFmpPayloadHeaderVersion (FmpPayloadHeader, FmpPayloadSize, &Version); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "FmpDxe(%s): CheckTheImage() - GetFmpPayloadHeaderVersion failed %r.\n", mImageIdName, Status)); + *ImageUpdatable = IMAGE_UPDATABLE_INVALID; + Status = EFI_SUCCESS; + goto cleanup; + } + } else { + DEBUG ((DEBUG_ERROR, "FmpDxe(%s): CheckTheImage() - Dependency is invalid.\n", mImageIdName)); + mDependenciesCheckStatus = DEPENDENCIES_INVALID; + *ImageUpdatable = IMAGE_UPDATABLE_INVALID; + Status = EFI_SUCCESS; + goto cleanup; + } + } else { + DEBUG ((DEBUG_WARN, "FmpDxe(%s): CheckTheImage() - No dependency associated in image.\n", mImageIdName)); } // @@ -862,6 +990,22 @@ CheckTheImage ( goto cleanup; } + // + // Evaluate dependency expression + // + Status = EvaluateImageDependencies (Private->Descriptor.ImageTypeId, Version, Dependencies, DependenciesSize, &IsDepexSatisfied); + if (!IsDepexSatisfied || EFI_ERROR (Status)) { + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "FmpDxe(%s): CheckTheImage() - Dependency check failed %r.\n", mImageIdName, Status)); + } else { + DEBUG ((DEBUG_ERROR, "FmpDxe(%s): CheckTheImage() - Dependency is not satisfied.\n", mImageIdName)); + } + mDependenciesCheckStatus = DEPENDENCIES_UNSATISFIED; + *ImageUpdatable = IMAGE_UPDATABLE_INVALID; + Status = EFI_SUCCESS; + goto cleanup; + } + // // Get the FmpHeaderSize so we can determine the real payload size // @@ -877,7 +1021,7 @@ CheckTheImage ( // Call FmpDevice Lib Check Image on the // Raw payload. So all headers need stripped off // - AllHeaderSize = GetAllHeaderSize ( (EFI_FIRMWARE_IMAGE_AUTHENTICATION *)Image, FmpHeaderSize ); + AllHeaderSize = GetAllHeaderSize ( (EFI_FIRMWARE_IMAGE_AUTHENTICATION *)Image, FmpHeaderSize + DependenciesSize); if (AllHeaderSize == 0) { DEBUG ((DEBUG_ERROR, "FmpDxe(%s): CheckTheImage() - GetAllHeaderSize failed.\n", mImageIdName)); Status = EFI_ABORTED; @@ -967,6 +1111,11 @@ SetTheImage ( UINT32 LastAttemptStatus; UINT32 Version; UINT32 LowestSupportedVersion; + EFI_FIRMWARE_IMAGE_DEP *Dependencies; + UINT32 DependenciesSize; + BOOLEAN IsDepexValid; + UINT8 *ImageBuffer; + UINTN ImageBufferSize; Status = EFI_SUCCESS; Updateable = 0; @@ -975,8 +1124,12 @@ SetTheImage ( FmpHeader = NULL; FmpPayloadSize = 0; AllHeaderSize = 0; - IncomingFwVersion = 0; + IncomingFwVersion = 0; LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_UNSUCCESSFUL; + Dependencies = NULL; + DependenciesSize = 0; + ImageBuffer = NULL; + ImageBufferSize = 0; if (!FeaturePcdGet (PcdFmpDeviceStorageAccessEnable)) { return EFI_UNSUPPORTED; @@ -1008,6 +1161,11 @@ SetTheImage ( goto cleanup; } + // + // Set check status to satisfied before CheckTheImage() + // + mDependenciesCheckStatus = DEPENDENCIES_SATISFIED; + // // Call check image to verify the image // @@ -1031,6 +1189,21 @@ SetTheImage ( goto cleanup; } Status = GetFmpPayloadHeaderVersion (FmpHeader, FmpPayloadSize, &IncomingFwVersion); + if (EFI_ERROR (Status)) { + // + // Check if there is dependency expression + // + IsDepexValid = ValidateImageDepex ((EFI_FIRMWARE_IMAGE_DEP*) FmpHeader, FmpPayloadSize, &DependenciesSize); + if (IsDepexValid && (DependenciesSize < FmpPayloadSize)) { + // + // Fmp payload is after dependency expression + // + Dependencies = (EFI_FIRMWARE_IMAGE_DEP*) FmpHeader; + FmpHeader = (UINT8 *) FmpHeader + DependenciesSize; + FmpPayloadSize = FmpPayloadSize - DependenciesSize; + Status = GetFmpPayloadHeaderVersion (FmpHeader, FmpPayloadSize, &IncomingFwVersion); + } + } if (!EFI_ERROR (Status)) { // // Set to actual value @@ -1045,6 +1218,11 @@ SetTheImage ( "FmpDxe(%s): SetTheImage() - Check The Image returned that the Image was not valid for update. Updatable value = 0x%X.\n", mImageIdName, Updateable) ); + if (mDependenciesCheckStatus == DEPENDENCIES_UNSATISFIED) { + LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_UNSATISFIED_DEPENDENCIES; + } else if (mDependenciesCheckStatus == DEPENDENCIES_INVALID) { + LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT; + } Status = EFI_ABORTED; goto cleanup; } @@ -1138,13 +1316,41 @@ SetTheImage ( goto cleanup; } - AllHeaderSize = GetAllHeaderSize ( (EFI_FIRMWARE_IMAGE_AUTHENTICATION *)Image, FmpHeaderSize ); + AllHeaderSize = GetAllHeaderSize ((EFI_FIRMWARE_IMAGE_AUTHENTICATION *)Image, FmpHeaderSize + DependenciesSize); if (AllHeaderSize == 0) { DEBUG ((DEBUG_ERROR, "FmpDxe(%s): SetTheImage() - GetAllHeaderSize failed.\n", mImageIdName)); Status = EFI_ABORTED; goto cleanup; } + // + // Check the attribute IMAGE_ATTRIBUTE_DEPENDENCY + // + if (Private->Descriptor.AttributesSetting & IMAGE_ATTRIBUTE_DEPENDENCY) { + // + // To support saving dependency, extend param "Image" of FmpDeviceSetImage() to + // contain the dependency inside. FmpDeviceSetImage() is responsible for saving + // the dependency which can be used for future dependency check. + // + ImageBufferSize = DependenciesSize + ImageSize - AllHeaderSize; + ImageBuffer = AllocatePool (ImageBufferSize); + if (ImageBuffer == NULL) { + DEBUG ((DEBUG_ERROR, "FmpDxe(%s): SetTheImage() - AllocatePool failed.\n", mImageIdName)); + Status = EFI_ABORTED; + goto cleanup; + } + CopyMem (ImageBuffer, Dependencies->Dependencies, DependenciesSize); + CopyMem (ImageBuffer + DependenciesSize, (UINT8 *)Image + AllHeaderSize, ImageBufferSize - DependenciesSize); + } else { + ImageBufferSize = ImageSize - AllHeaderSize; + ImageBuffer = AllocateCopyPool(ImageBufferSize, (UINT8 *)Image + AllHeaderSize); + if (ImageBuffer == NULL) { + DEBUG ((DEBUG_ERROR, "FmpDxe(%s): SetTheImage() - AllocatePool failed.\n", mImageIdName)); + Status = EFI_ABORTED; + goto cleanup; + } + } + // // Indicate that control is handed off to FmpDeviceLib // @@ -1154,8 +1360,8 @@ SetTheImage ( //Copy the requested image to the firmware using the FmpDeviceLib // Status = FmpDeviceSetImage ( - (((UINT8 *)Image) + AllHeaderSize), - ImageSize - AllHeaderSize, + ImageBuffer, + ImageBufferSize, VendorCode, FmpDxeProgress, IncomingFwVersion, @@ -1192,6 +1398,10 @@ SetTheImage ( LastAttemptStatus = LAST_ATTEMPT_STATUS_SUCCESS; cleanup: + if (ImageBuffer != NULL) { + FreePool (ImageBuffer); + } + mProgressFunc = NULL; SetLastAttemptStatusInVariable (Private, LastAttemptStatus); diff --git a/FmpDevicePkg/FmpDxe/FmpDxe.inf b/FmpDevicePkg/FmpDxe/FmpDxe.inf index bec73aa8fb..97b6518fa1 100644 --- a/FmpDevicePkg/FmpDxe/FmpDxe.inf +++ b/FmpDevicePkg/FmpDxe/FmpDxe.inf @@ -4,7 +4,7 @@ # information provided through PCDs and libraries. # # Copyright (c) 2016, Microsoft Corporation. All rights reserved.
-# Copyright (c) 2018 - 2019, Intel Corporation. All rights reserved.
+# Copyright (c) 2018 - 2020, Intel Corporation. All rights reserved.
# # SPDX-License-Identifier: BSD-2-Clause-Patent ## @@ -28,6 +28,8 @@ [Sources] FmpDxe.c FmpDxe.h + Dependency.c + Dependency.h DetectTestKey.c VariableSupport.h VariableSupport.c diff --git a/FmpDevicePkg/FmpDxe/FmpDxeLib.inf b/FmpDevicePkg/FmpDxe/FmpDxeLib.inf index edc0cd66c1..de005b6892 100644 --- a/FmpDevicePkg/FmpDxe/FmpDxeLib.inf +++ b/FmpDevicePkg/FmpDxe/FmpDxeLib.inf @@ -4,7 +4,7 @@ # information provided through PCDs and libraries. # # Copyright (c) 2016, Microsoft Corporation. All rights reserved.
-# Copyright (c) 2018 - 2019, Intel Corporation. All rights reserved.
+# Copyright (c) 2018 - 2020, Intel Corporation. All rights reserved.
# # SPDX-License-Identifier: BSD-2-Clause-Patent ## @@ -29,6 +29,8 @@ [Sources] FmpDxe.c FmpDxe.h + Dependency.c + Dependency.h DetectTestKey.c VariableSupport.h VariableSupport.c