summaryrefslogtreecommitdiffstats
path: root/FmpDevicePkg/FmpDxe
diff options
context:
space:
mode:
Diffstat (limited to 'FmpDevicePkg/FmpDxe')
-rw-r--r--FmpDevicePkg/FmpDxe/Dependency.c679
-rw-r--r--FmpDevicePkg/FmpDxe/Dependency.h63
-rw-r--r--FmpDevicePkg/FmpDxe/FmpDxe.c238
-rw-r--r--FmpDevicePkg/FmpDxe/FmpDxe.inf4
-rw-r--r--FmpDevicePkg/FmpDxe/FmpDxeLib.inf4
5 files changed, 972 insertions, 16 deletions
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.<BR>
+
+ 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.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef __DEPENDENCY_H__
+#define __DEPENDENCY_H__
+
+#include <Library/UefiLib.h>
+#include <Protocol/FirmwareManagement.h>
+
+#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.<BR>
- Copyright (c) 2018 - 2019, Intel Corporation. All rights reserved.<BR>
+ Copyright (c) 2018 - 2020, Intel Corporation. All rights reserved.<BR>
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));
}
//
@@ -863,6 +991,22 @@ CheckTheImage (
}
//
+ // 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
//
Status = GetFmpPayloadHeaderSize (FmpPayloadHeader, FmpPayloadSize, &FmpHeaderSize);
@@ -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;
@@ -1009,6 +1162,11 @@ SetTheImage (
}
//
+ // Set check status to satisfied before CheckTheImage()
+ //
+ mDependenciesCheckStatus = DEPENDENCIES_SATISFIED;
+
+ //
// Call check image to verify the image
//
Status = CheckTheImage (This, ImageIndex, Image, ImageSize, &Updateable);
@@ -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,7 +1316,7 @@ 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;
@@ -1146,6 +1324,34 @@ SetTheImage (
}
//
+ // 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
//
Progress (5);
@@ -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.<BR>
-# Copyright (c) 2018 - 2019, Intel Corporation. All rights reserved.<BR>
+# Copyright (c) 2018 - 2020, Intel Corporation. All rights reserved.<BR>
#
# 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.<BR>
-# Copyright (c) 2018 - 2019, Intel Corporation. All rights reserved.<BR>
+# Copyright (c) 2018 - 2020, Intel Corporation. All rights reserved.<BR>
#
# 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