]> git.proxmox.com Git - mirror_edk2.git/blobdiff - FmpDevicePkg/Library/FmpDependencyLib/FmpDependencyLib.c
FmpDevicePkg: Add FmpDependency library class and BASE instance
[mirror_edk2.git] / FmpDevicePkg / Library / FmpDependencyLib / FmpDependencyLib.c
diff --git a/FmpDevicePkg/Library/FmpDependencyLib/FmpDependencyLib.c b/FmpDevicePkg/Library/FmpDependencyLib/FmpDependencyLib.c
new file mode 100644 (file)
index 0000000..91dc0b9
--- /dev/null
@@ -0,0 +1,546 @@
+/** @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