--- /dev/null
+/** @file\r
+ Supports Fmp Capsule Dependency Expression.\r
+\r
+ Copyright (c) 2020, Intel Corporation. All rights reserved.<BR>\r
+\r
+ SPDX-License-Identifier: BSD-2-Clause-Patent\r
+\r
+**/\r
+#include <PiDxe.h>\r
+#include <Library/BaseLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/FmpDependencyLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+\r
+//\r
+// Define the initial size of the dependency expression evaluation stack\r
+//\r
+#define DEPEX_STACK_SIZE_INCREMENT 0x1000\r
+\r
+//\r
+// Type of stack element\r
+//\r
+typedef enum {\r
+ BooleanType,\r
+ VersionType\r
+} ELEMENT_TYPE;\r
+\r
+//\r
+// Value of stack element\r
+//\r
+typedef union {\r
+ BOOLEAN Boolean;\r
+ UINT32 Version;\r
+} ELEMENT_VALUE;\r
+\r
+//\r
+// Stack element used to evaluate dependency expressions\r
+//\r
+typedef struct {\r
+ ELEMENT_VALUE Value;\r
+ ELEMENT_TYPE Type;\r
+} DEPEX_ELEMENT;\r
+\r
+//\r
+// Global stack used to evaluate dependency expressions\r
+//\r
+DEPEX_ELEMENT *mDepexEvaluationStack = NULL;\r
+DEPEX_ELEMENT *mDepexEvaluationStackEnd = NULL;\r
+DEPEX_ELEMENT *mDepexEvaluationStackPointer = NULL;\r
+\r
+/**\r
+ Grow size of the Depex stack\r
+\r
+ @retval EFI_SUCCESS Stack successfully growed.\r
+ @retval EFI_OUT_OF_RESOURCES There is not enough system memory to grow the stack.\r
+\r
+**/\r
+EFI_STATUS\r
+GrowDepexStack (\r
+ VOID\r
+ )\r
+{\r
+ DEPEX_ELEMENT *NewStack;\r
+ UINTN Size;\r
+\r
+ Size = DEPEX_STACK_SIZE_INCREMENT;\r
+ if (mDepexEvaluationStack != NULL) {\r
+ Size = Size + (mDepexEvaluationStackEnd - mDepexEvaluationStack);\r
+ }\r
+\r
+ NewStack = AllocatePool (Size * sizeof (DEPEX_ELEMENT));\r
+ if (NewStack == NULL) {\r
+ DEBUG ((DEBUG_ERROR, "GrowDepexStack: Cannot allocate memory for dependency evaluation stack!\n"));\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ if (mDepexEvaluationStack != NULL) {\r
+ //\r
+ // Copy to Old Stack to the New Stack\r
+ //\r
+ CopyMem (\r
+ NewStack,\r
+ mDepexEvaluationStack,\r
+ (mDepexEvaluationStackEnd - mDepexEvaluationStack) * sizeof (DEPEX_ELEMENT)\r
+ );\r
+\r
+ //\r
+ // Free The Old Stack\r
+ //\r
+ FreePool (mDepexEvaluationStack);\r
+ }\r
+\r
+ //\r
+ // Make the Stack pointer point to the old data in the new stack\r
+ //\r
+ mDepexEvaluationStackPointer = NewStack + (mDepexEvaluationStackPointer - mDepexEvaluationStack);\r
+ mDepexEvaluationStack = NewStack;\r
+ mDepexEvaluationStackEnd = NewStack + Size;\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Push an element onto the Stack.\r
+\r
+ @param[in] Value Value to push.\r
+ @param[in] Type Element Type\r
+\r
+ @retval EFI_SUCCESS The value was pushed onto the stack.\r
+ @retval EFI_OUT_OF_RESOURCES There is not enough system memory to grow the stack.\r
+ @retval EFI_INVALID_PARAMETER Wrong stack element type.\r
+\r
+**/\r
+EFI_STATUS\r
+Push (\r
+ IN UINT32 Value,\r
+ IN UINTN Type\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ DEPEX_ELEMENT Element;\r
+\r
+ //\r
+ // Check Type\r
+ //\r
+ if (Type != BooleanType && Type != VersionType) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // Check for a stack overflow condition\r
+ //\r
+ if (mDepexEvaluationStackPointer == mDepexEvaluationStackEnd) {\r
+ //\r
+ // Grow the stack\r
+ //\r
+ Status = GrowDepexStack ();\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ }\r
+\r
+ Element.Value.Version = Value;\r
+ Element.Type = Type;\r
+\r
+ //\r
+ // Push the item onto the stack\r
+ //\r
+ *mDepexEvaluationStackPointer = Element;\r
+ mDepexEvaluationStackPointer++;\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Pop an element from the stack.\r
+\r
+ @param[out] Element Element to pop.\r
+ @param[in] Type Type of element.\r
+\r
+ @retval EFI_SUCCESS The value was popped onto the stack.\r
+ @retval EFI_ACCESS_DENIED The pop operation underflowed the stack.\r
+ @retval EFI_INVALID_PARAMETER Type is mismatched.\r
+\r
+**/\r
+EFI_STATUS\r
+Pop (\r
+ OUT DEPEX_ELEMENT *Element,\r
+ IN ELEMENT_TYPE Type\r
+ )\r
+{\r
+ //\r
+ // Check for a stack underflow condition\r
+ //\r
+ if (mDepexEvaluationStackPointer == mDepexEvaluationStack) {\r
+ DEBUG ((DEBUG_ERROR, "EvaluateDependency: Stack underflow!\n"));\r
+ return EFI_ACCESS_DENIED;\r
+ }\r
+\r
+ //\r
+ // Pop the item off the stack\r
+ //\r
+ mDepexEvaluationStackPointer--;\r
+ *Element = *mDepexEvaluationStackPointer;\r
+ if ((*Element).Type != Type) {\r
+ DEBUG ((DEBUG_ERROR, "EvaluateDependency: Popped element type is mismatched!\n"));\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Evaluate the dependencies. The caller must search all the Fmp instances and\r
+ gather their versions into FmpVersions parameter. If there is PUSH_GUID opcode\r
+ in dependency expression with no FmpVersions provided, the dependency will\r
+ evaluate to FALSE.\r
+\r
+ @param[in] Dependencies Dependency expressions.\r
+ @param[in] DependenciesSize Size of Dependency expressions.\r
+ @param[in] FmpVersions Array of Fmp ImageTypeId and version. This\r
+ parameter is optional and can be set to NULL.\r
+ @param[in] FmpVersionsCount Element count of the array. When FmpVersions\r
+ is NULL, FmpVersionsCount must be 0.\r
+\r
+ @retval TRUE Dependency expressions evaluate to TRUE.\r
+ @retval FALSE Dependency expressions evaluate to FALSE.\r
+\r
+**/\r
+BOOLEAN\r
+EFIAPI\r
+EvaluateDependency (\r
+ IN EFI_FIRMWARE_IMAGE_DEP *Dependencies,\r
+ IN UINTN DependenciesSize,\r
+ IN FMP_DEPEX_CHECK_VERSION_DATA *FmpVersions OPTIONAL,\r
+ IN UINTN FmpVersionsCount\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINT8 *Iterator;\r
+ UINT8 Index;\r
+ DEPEX_ELEMENT Element1;\r
+ DEPEX_ELEMENT Element2;\r
+ GUID ImageTypeId;\r
+ UINT32 Version;\r
+\r
+ //\r
+ // Check if parameter is valid.\r
+ //\r
+ if (Dependencies == NULL || DependenciesSize == 0) {\r
+ return FALSE;\r
+ }\r
+\r
+ if (FmpVersions == NULL && FmpVersionsCount > 0) {\r
+ return FALSE;\r
+ }\r
+\r
+ //\r
+ // Clean out memory leaks in Depex Boolean stack. Leaks are only caused by\r
+ // incorrectly formed DEPEX expressions\r
+ //\r
+ mDepexEvaluationStackPointer = mDepexEvaluationStack;\r
+\r
+ Iterator = (UINT8 *) Dependencies->Dependencies;\r
+ while (Iterator < (UINT8 *) Dependencies->Dependencies + DependenciesSize) {\r
+ switch (*Iterator)\r
+ {\r
+ case EFI_FMP_DEP_PUSH_GUID:\r
+ if (Iterator + sizeof (EFI_GUID) >= (UINT8 *) Dependencies->Dependencies + DependenciesSize) {\r
+ DEBUG ((DEBUG_ERROR, "EvaluateDependency: GUID extends beyond end of dependency expression!\n"));\r
+ goto Error;\r
+ }\r
+\r
+ CopyGuid (&ImageTypeId, (EFI_GUID *) (Iterator + 1));\r
+ Iterator = Iterator + sizeof (EFI_GUID);\r
+\r
+ for (Index = 0; Index < FmpVersionsCount; Index ++) {\r
+ if(CompareGuid (&FmpVersions[Index].ImageTypeId, &ImageTypeId)){\r
+ Status = Push (FmpVersions[Index].Version, VersionType);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error;\r
+ }\r
+ break;\r
+ }\r
+ }\r
+ if (Index == FmpVersionsCount) {\r
+ DEBUG ((DEBUG_ERROR, "EvaluateDependency: %g is not found!\n", &ImageTypeId));\r
+ goto Error;\r
+ }\r
+ break;\r
+ case EFI_FMP_DEP_PUSH_VERSION:\r
+ if (Iterator + sizeof (UINT32) >= (UINT8 *) Dependencies->Dependencies + DependenciesSize ) {\r
+ DEBUG ((DEBUG_ERROR, "EvaluateDependency: VERSION extends beyond end of dependency expression!\n"));\r
+ goto Error;\r
+ }\r
+\r
+ Version = *(UINT32 *) (Iterator + 1);\r
+ Status = Push (Version, VersionType);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error;\r
+ }\r
+ Iterator = Iterator + sizeof (UINT32);\r
+ break;\r
+ case EFI_FMP_DEP_VERSION_STR:\r
+ Iterator += AsciiStrnLenS ((CHAR8 *) Iterator, DependenciesSize - (Iterator - Dependencies->Dependencies));\r
+ if (Iterator == (UINT8 *) Dependencies->Dependencies + DependenciesSize) {\r
+ DEBUG ((DEBUG_ERROR, "EvaluateDependency: STRING extends beyond end of dependency expression!\n"));\r
+ }\r
+ break;\r
+ case EFI_FMP_DEP_AND:\r
+ Status = Pop (&Element1, BooleanType);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error;\r
+ }\r
+ Status = Pop (&Element2, BooleanType);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error;\r
+ }\r
+ Status = Push (Element1.Value.Boolean & Element2.Value.Boolean, BooleanType);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error;\r
+ }\r
+ break;\r
+ case EFI_FMP_DEP_OR:\r
+ Status = Pop (&Element1, BooleanType);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error;\r
+ }\r
+ Status = Pop(&Element2, BooleanType);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error;\r
+ }\r
+ Status = Push (Element1.Value.Boolean | Element2.Value.Boolean, BooleanType);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error;\r
+ }\r
+ break;\r
+ case EFI_FMP_DEP_NOT:\r
+ Status = Pop (&Element1, BooleanType);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error;\r
+ }\r
+ Status = Push (!(Element1.Value.Boolean), BooleanType);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error;\r
+ }\r
+ break;\r
+ case EFI_FMP_DEP_TRUE:\r
+ Status = Push (TRUE, BooleanType);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error;\r
+ }\r
+ break;\r
+ case EFI_FMP_DEP_FALSE:\r
+ Status = Push (FALSE, BooleanType);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error;\r
+ }\r
+ break;\r
+ case EFI_FMP_DEP_EQ:\r
+ Status = Pop (&Element1, VersionType);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error;\r
+ }\r
+ Status = Pop (&Element2, VersionType);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error;\r
+ }\r
+ Status = (Element1.Value.Version == Element2.Value.Version) ? Push (TRUE, BooleanType) : Push (FALSE, BooleanType);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error;\r
+ }\r
+ break;\r
+ case EFI_FMP_DEP_GT:\r
+ Status = Pop (&Element1, VersionType);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error;\r
+ }\r
+ Status = Pop (&Element2, VersionType);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error;\r
+ }\r
+ Status = (Element1.Value.Version > Element2.Value.Version) ? Push (TRUE, BooleanType) : Push (FALSE, BooleanType);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error;\r
+ }\r
+ break;\r
+ case EFI_FMP_DEP_GTE:\r
+ Status = Pop (&Element1, VersionType);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error;\r
+ }\r
+ Status = Pop (&Element2, VersionType);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error;\r
+ }\r
+ Status = (Element1.Value.Version >= Element2.Value.Version) ? Push (TRUE, BooleanType) : Push (FALSE, BooleanType);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error;\r
+ }\r
+ break;\r
+ case EFI_FMP_DEP_LT:\r
+ Status = Pop (&Element1, VersionType);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error;\r
+ }\r
+ Status = Pop (&Element2, VersionType);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error;\r
+ }\r
+ Status = (Element1.Value.Version < Element2.Value.Version) ? Push (TRUE, BooleanType) : Push (FALSE, BooleanType);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error;\r
+ }\r
+ break;\r
+ case EFI_FMP_DEP_LTE:\r
+ Status = Pop (&Element1, VersionType);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error;\r
+ }\r
+ Status = Pop (&Element2, VersionType);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error;\r
+ }\r
+ Status = (Element1.Value.Version <= Element2.Value.Version) ? Push (TRUE, BooleanType) : Push (FALSE, BooleanType);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error;\r
+ }\r
+ break;\r
+ case EFI_FMP_DEP_END:\r
+ Status = Pop (&Element1, BooleanType);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error;\r
+ }\r
+ return Element1.Value.Boolean;\r
+ default:\r
+ DEBUG ((DEBUG_ERROR, "EvaluateDependency: Unknown Opcode - %02x!\n", *Iterator));\r
+ goto Error;\r
+ }\r
+ Iterator++;\r
+ }\r
+\r
+ DEBUG ((DEBUG_ERROR, "EvaluateDependency: No EFI_FMP_DEP_END Opcode in exression!\n"));\r
+\r
+Error:\r
+ return FALSE;\r
+}\r
+\r
+/**\r
+ Validate the dependency expression and output its size.\r
+\r
+ @param[in] Dependencies Pointer to the EFI_FIRMWARE_IMAGE_DEP.\r
+ @param[in] MaxDepexSize Max size of the dependency.\r
+ @param[out] DepexSize Size of dependency.\r
+\r
+ @retval TRUE The capsule is valid.\r
+ @retval FALSE The capsule is invalid.\r
+\r
+**/\r
+BOOLEAN\r
+EFIAPI\r
+ValidateDependency (\r
+ IN EFI_FIRMWARE_IMAGE_DEP *Dependencies,\r
+ IN UINTN MaxDepexSize,\r
+ OUT UINT32 *DepexSize\r
+ )\r
+{\r
+ UINT8 *Depex;\r
+\r
+ if (DepexSize != NULL) {\r
+ *DepexSize = 0;\r
+ }\r
+\r
+ if (Dependencies == NULL) {\r
+ return FALSE;\r
+ }\r
+\r
+ Depex = Dependencies->Dependencies;\r
+ while (Depex < Dependencies->Dependencies + MaxDepexSize) {\r
+ switch (*Depex)\r
+ {\r
+ case EFI_FMP_DEP_PUSH_GUID:\r
+ Depex += sizeof (EFI_GUID) + 1;\r
+ break;\r
+ case EFI_FMP_DEP_PUSH_VERSION:\r
+ Depex += sizeof (UINT32) + 1;\r
+ break;\r
+ case EFI_FMP_DEP_VERSION_STR:\r
+ Depex += AsciiStrnLenS ((CHAR8 *) Depex, Dependencies->Dependencies + MaxDepexSize - Depex) + 1;\r
+ break;\r
+ case EFI_FMP_DEP_AND:\r
+ case EFI_FMP_DEP_OR:\r
+ case EFI_FMP_DEP_NOT:\r
+ case EFI_FMP_DEP_TRUE:\r
+ case EFI_FMP_DEP_FALSE:\r
+ case EFI_FMP_DEP_EQ:\r
+ case EFI_FMP_DEP_GT:\r
+ case EFI_FMP_DEP_GTE:\r
+ case EFI_FMP_DEP_LT:\r
+ case EFI_FMP_DEP_LTE:\r
+ Depex += 1;\r
+ break;\r
+ case EFI_FMP_DEP_END:\r
+ Depex += 1;\r
+ if (DepexSize != NULL) {\r
+ *DepexSize = (UINT32)(Depex - Dependencies->Dependencies);\r
+ }\r
+ return TRUE;\r
+ default:\r
+ return FALSE;\r
+ }\r
+ }\r
+\r
+ return FALSE;\r
+}\r
+\r
+/**\r
+ Get dependency from firmware image.\r
+\r
+ @param[in] Image Points to the firmware image.\r
+ @param[in] ImageSize Size, in bytes, of the firmware image.\r
+ @param[out] DepexSize Size, in bytes, of the dependency.\r
+\r
+ @retval The pointer to dependency.\r
+ @retval Null\r
+\r
+**/\r
+EFI_FIRMWARE_IMAGE_DEP*\r
+EFIAPI\r
+GetImageDependency (\r
+ IN EFI_FIRMWARE_IMAGE_AUTHENTICATION *Image,\r
+ IN UINTN ImageSize,\r
+ OUT UINT32 *DepexSize\r
+ )\r
+{\r
+ EFI_FIRMWARE_IMAGE_DEP *Depex;\r
+ UINTN MaxDepexSize;\r
+\r
+ if (Image == NULL) {\r
+ return NULL;\r
+ }\r
+\r
+ //\r
+ // Check to make sure that operation can be safely performed.\r
+ //\r
+ if (((UINTN)Image + sizeof (Image->MonotonicCount) + Image->AuthInfo.Hdr.dwLength) < (UINTN)Image || \\r
+ ((UINTN)Image + sizeof (Image->MonotonicCount) + Image->AuthInfo.Hdr.dwLength) >= (UINTN)Image + ImageSize) {\r
+ //\r
+ // Pointer overflow. Invalid image.\r
+ //\r
+ return NULL;\r
+ }\r
+\r
+ Depex = (EFI_FIRMWARE_IMAGE_DEP*)((UINT8 *)Image + sizeof (Image->MonotonicCount) + Image->AuthInfo.Hdr.dwLength);\r
+ MaxDepexSize = ImageSize - (sizeof (Image->MonotonicCount) + Image->AuthInfo.Hdr.dwLength);\r
+\r
+ //\r
+ // Validate the dependency and get the size of dependency\r
+ //\r
+ if (ValidateDependency (Depex, MaxDepexSize, DepexSize)) {\r
+ return Depex;\r
+ }\r
+\r
+ return NULL;\r
+}\r
+\r