summaryrefslogtreecommitdiffstats
path: root/FmpDevicePkg/FmpDxe/Dependency.c
diff options
context:
space:
mode:
Diffstat (limited to 'FmpDevicePkg/FmpDxe/Dependency.c')
-rw-r--r--FmpDevicePkg/FmpDxe/Dependency.c679
1 files changed, 679 insertions, 0 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;
+}