FmpDevicePkg: Add Last Attempt Status support to dependency libs

The FMP dependency libraries are leveraged during firmware update
to check for dependencies required to update the image.

This change adds granular Last Attempt Status code support to these
services so failures can be more easily observed during the firmware
update process via Last Attempt Status codes.

Cc: Liming Gao <gaoliming@byosoft.com.cn>
Cc: Michael D Kinney <michael.d.kinney@intel.com>
Cc: Guomin Jiang <guomin.jiang@intel.com>
Cc: Wei6 Xu <wei6.xu@intel.com>
Signed-off-by: Michael Kubacki <michael.kubacki@microsoft.com>
Acked-by: Liming Gao <gaoliming@byosoft.com.cn>
Reviewed-by: Wei6 Xu <wei6.xu@intel.com>
Reviewed-by: Michael D Kinney <michael.d.kinney@intel.com>
diff --git a/FmpDevicePkg/FmpDxe/FmpDxe.c b/FmpDevicePkg/FmpDxe/FmpDxe.c
index b7fd9c9..de7f1fe 100644
--- a/FmpDevicePkg/FmpDxe/FmpDxe.c
+++ b/FmpDevicePkg/FmpDxe/FmpDxe.c
@@ -730,6 +730,15 @@
                                  LAST_ATTEMPT_STATUS_DRIVER_MIN_ERROR_CODE_VALUE

                                  to LAST_ATTEMPT_STATUS_DRIVER_MAX_ERROR_CODE_VALUE.

 

+                                 This function might also return error codes that occur within libraries

+                                 linked against this module that return last attempt error codes such as:

+

+                                 LAST_ATTEMPT_STATUS_FMP_DEPENDENCY_LIB_MIN_ERROR_CODE_VALUE to

+                                 LAST_ATTEMPT_STATUS_FMP_DEPENDENCY_LIB_MAX_ERROR_CODE_VALUE

+

+                                 LAST_ATTEMPT_STATUS_FMP_DEPENDENCY_CHECK_LIB_MIN_ERROR_CODE_VALUE to

+                                 LAST_ATTEMPT_STATUS_FMP_DEPENDENCY_CHECK_LIB_MAX_ERROR_CODE_VALUE

+

   @retval EFI_SUCCESS            The image was successfully checked.

   @retval EFI_ABORTED            The operation is aborted.

   @retval EFI_INVALID_PARAMETER  The Image was NULL.

@@ -925,7 +934,16 @@
   //

   // Get the dependency from Image.

   //

-  Dependencies = GetImageDependency ((EFI_FIRMWARE_IMAGE_AUTHENTICATION *)Image, ImageSize, &DependenciesSize);

+  Dependencies =  GetImageDependency (

+                    (EFI_FIRMWARE_IMAGE_AUTHENTICATION *) Image,

+                    ImageSize,

+                    &DependenciesSize,

+                    LastAttemptStatus

+                    );

+  if (*LastAttemptStatus != LAST_ATTEMPT_STATUS_SUCCESS) {

+    Status = EFI_ABORTED;

+    goto cleanup;

+  }

 

   //

   // Check the FmpPayloadHeader

@@ -964,11 +982,18 @@
   //

   // Evaluate dependency expression

   //

-  Private->DependenciesSatisfied = CheckFmpDependency (Private->Descriptor.ImageTypeId, Version, Dependencies, DependenciesSize);

+  Private->DependenciesSatisfied =  CheckFmpDependency (

+                                      Private->Descriptor.ImageTypeId,

+                                      Version,

+                                      Dependencies,

+                                      DependenciesSize,

+                                      &LocalLastAttemptStatus

+                                      );

   if (!Private->DependenciesSatisfied) {

     DEBUG ((DEBUG_ERROR, "FmpDxe(%s): CheckTheImage() - Dependency check failed.\n", mImageIdName));

     *ImageUpdatable = IMAGE_UPDATABLE_INVALID;

     Status = EFI_SUCCESS;

+    *LastAttemptStatus = LocalLastAttemptStatus;

     goto cleanup;

   }

 

@@ -1181,7 +1206,7 @@
   //

   // Get the dependency from Image.

   //

-  Dependencies = GetImageDependency ((EFI_FIRMWARE_IMAGE_AUTHENTICATION *)Image, ImageSize, &DependenciesSize);

+  Dependencies = GetImageDependency ((EFI_FIRMWARE_IMAGE_AUTHENTICATION *)Image, ImageSize, &DependenciesSize, &LastAttemptStatus);

 

   //

   // No functional error in CheckTheImage.  Attempt to get the Version to

diff --git a/FmpDevicePkg/Include/Library/FmpDependencyCheckLib.h b/FmpDevicePkg/Include/Library/FmpDependencyCheckLib.h
index ec380c4..785bda9 100644
--- a/FmpDevicePkg/Include/Library/FmpDependencyCheckLib.h
+++ b/FmpDevicePkg/Include/Library/FmpDependencyCheckLib.h
@@ -2,6 +2,7 @@
   Fmp Capsule Dependency check functions for Firmware Management Protocol based

   firmware updates.

 

+  Copyright (c) Microsoft Corporation.<BR>

   Copyright (c) 2020, Intel Corporation. All rights reserved.<BR>

 

   SPDX-License-Identifier: BSD-2-Clause-Patent

@@ -21,6 +22,10 @@
   @param[in]  Version            New version.

   @param[in]  Dependencies       Fmp dependency.

   @param[in]  DependenciesSize   Size, in bytes, of the Fmp dependency.

+  @param[out] LastAttemptStatus  An optional pointer to a UINT32 that holds the

+                                 last attempt status to report back to the caller.

+                                 This function will set the value to LAST_ATTEMPT_STATUS_SUCCESS

+                                 if an error code is not set.

 

   @retval  TRUE    Dependencies are satisfied.

   @retval  FALSE   Dependencies are unsatisfied or dependency check fails.

@@ -32,7 +37,8 @@
   IN  EFI_GUID                ImageTypeId,

   IN  UINT32                  Version,

   IN  EFI_FIRMWARE_IMAGE_DEP  *Dependencies,    OPTIONAL

-  IN  UINT32                  DependenciesSize

+  IN  UINT32                  DependenciesSize,

+  OUT UINT32                  *LastAttemptStatus OPTIONAL

   );

 

 #endif

diff --git a/FmpDevicePkg/Include/Library/FmpDependencyLib.h b/FmpDevicePkg/Include/Library/FmpDependencyLib.h
index c732903..f460f0f 100644
--- a/FmpDevicePkg/Include/Library/FmpDependencyLib.h
+++ b/FmpDevicePkg/Include/Library/FmpDependencyLib.h
@@ -26,9 +26,13 @@
 /**

   Validate the dependency expression and output its size.

 

-  @param[in]   Dependencies   Pointer to the EFI_FIRMWARE_IMAGE_DEP.

-  @param[in]   MaxDepexSize   Max size of the dependency.

-  @param[out]  DepexSize      Size of dependency.

+  @param[in]   Dependencies       Pointer to the EFI_FIRMWARE_IMAGE_DEP.

+  @param[in]   MaxDepexSize       Max size of the dependency.

+  @param[out]  DepexSize          Size of dependency.

+  @param[out]  LastAttemptStatus  An optional pointer to a UINT32 that holds the

+                                  last attempt status to report back to the caller.

+                                  If a last attempt status error code is not returned,

+                                  this function will not modify the LastAttemptStatus value.

 

   @retval TRUE    The dependency expression is valid.

   @retval FALSE   The dependency expression is invalid.

@@ -39,16 +43,20 @@
 ValidateDependency (

   IN  EFI_FIRMWARE_IMAGE_DEP  *Dependencies,

   IN  UINTN                   MaxDepexSize,

-  OUT UINT32                  *DepexSize

+  OUT UINT32                  *DepexSize,

+  OUT UINT32                  *LastAttemptStatus OPTIONAL

   );

 

 /**

   Get dependency from firmware image.

 

-  @param[in]  Image       Points to the firmware image.

-  @param[in]  ImageSize   Size, in bytes, of the firmware image.

-  @param[out] DepexSize   Size, in bytes, of the dependency.

-

+  @param[in]  Image               Points to the firmware image.

+  @param[in]  ImageSize           Size, in bytes, of the firmware image.

+  @param[out] DepexSize           Size, in bytes, of the dependency.

+  @param[out] LastAttemptStatus   An optional pointer to a UINT32 that holds the

+                                  last attempt status to report back to the caller.

+                                  If a last attempt status error code is not returned,

+                                  this function will not modify the LastAttemptStatus value.

   @retval  The pointer to dependency.

   @retval  Null

 

@@ -56,9 +64,10 @@
 EFI_FIRMWARE_IMAGE_DEP*

 EFIAPI

 GetImageDependency (

-  IN  EFI_FIRMWARE_IMAGE_AUTHENTICATION  *Image,

-  IN  UINTN                              ImageSize,

-  OUT UINT32                             *DepexSize

+  IN  EFI_FIRMWARE_IMAGE_AUTHENTICATION *Image,

+  IN  UINTN                             ImageSize,

+  OUT UINT32                            *DepexSize,

+  OUT UINT32                            *LastAttemptStatus  OPTIONAL

   );

 

 /**

@@ -73,6 +82,10 @@
                                   parameter is optional and can be set to NULL.

   @param[in]   FmpVersionsCount   Element count of the array. When FmpVersions

                                   is NULL, FmpVersionsCount must be 0.

+  @param[out]  LastAttemptStatus  An optional pointer to a UINT32 that holds the

+                                  last attempt status to report back to the caller.

+                                  This function will set the value to LAST_ATTEMPT_STATUS_SUCCESS

+                                  if an error code is not set.

 

   @retval TRUE    Dependency expressions evaluate to TRUE.

   @retval FALSE   Dependency expressions evaluate to FALSE.

@@ -81,10 +94,11 @@
 BOOLEAN

 EFIAPI

 EvaluateDependency (

-  IN EFI_FIRMWARE_IMAGE_DEP        *Dependencies,

-  IN UINTN                         DependenciesSize,

-  IN FMP_DEPEX_CHECK_VERSION_DATA  *FmpVersions      OPTIONAL,

-  IN UINTN                         FmpVersionsCount

+  IN  EFI_FIRMWARE_IMAGE_DEP        *Dependencies,

+  IN  UINTN                         DependenciesSize,

+  IN  FMP_DEPEX_CHECK_VERSION_DATA  *FmpVersions,      OPTIONAL

+  IN  UINTN                         FmpVersionsCount,

+  OUT UINT32                        *LastAttemptStatus OPTIONAL

   );

 

 #endif

diff --git a/FmpDevicePkg/Library/FmpDependencyCheckLib/FmpDependencyCheckLib.c b/FmpDevicePkg/Library/FmpDependencyCheckLib/FmpDependencyCheckLib.c
index 02ed600..cca83db 100644
--- a/FmpDevicePkg/Library/FmpDependencyCheckLib/FmpDependencyCheckLib.c
+++ b/FmpDevicePkg/Library/FmpDependencyCheckLib/FmpDependencyCheckLib.c
@@ -17,6 +17,9 @@
 #include <Library/MemoryAllocationLib.h>

 #include <Library/UefiLib.h>

 #include <Library/UefiBootServicesTableLib.h>

+#include <Guid/SystemResourceTable.h>

+#include <LastAttemptStatus.h>

+#include <FmpLastAttemptStatus.h>

 

 /**

   Check dependency for firmware update.

@@ -25,6 +28,10 @@
   @param[in]  Version            New version.

   @param[in]  Dependencies       Fmp dependency.

   @param[in]  DependenciesSize   Size, in bytes, of the Fmp dependency.

+  @param[out] LastAttemptStatus  An optional pointer to a UINT32 that holds the

+                                 last attempt status to report back to the caller.

+                                 This function will set the value to LAST_ATTEMPT_STATUS_SUCCESS

+                                 if an error code is not set.

 

   @retval  TRUE    Dependencies are satisfied.

   @retval  FALSE   Dependencies are unsatisfied or dependency check fails.

@@ -36,7 +43,8 @@
   IN  EFI_GUID                ImageTypeId,

   IN  UINT32                  Version,

   IN  EFI_FIRMWARE_IMAGE_DEP  *Dependencies,    OPTIONAL

-  IN  UINT32                  DependenciesSize

+  IN  UINT32                  DependenciesSize,

+  OUT UINT32                  *LastAttemptStatus OPTIONAL

   )

 {

   EFI_STATUS                        Status;

@@ -44,6 +52,7 @@
   UINTN                             Index;

   EFI_FIRMWARE_MANAGEMENT_PROTOCOL  *Fmp;

   UINTN                             ImageInfoSize;

+  UINT32                            LocalLastAttemptStatus;

   UINT32                            *DescriptorVer;

   UINT8                             FmpImageInfoCount;

   UINTN                             *DescriptorSize;

@@ -55,14 +64,15 @@
   UINTN                             FmpVersionsCount;

   BOOLEAN                           IsSatisfied;

 

-  FmpImageInfoBuf     = NULL;

-  DescriptorVer       = NULL;

-  DescriptorSize      = NULL;

-  NumberOfFmpInstance = 0;

-  FmpVersions         = NULL;

-  FmpVersionsCount    = 0;

-  IsSatisfied         = TRUE;

-  PackageVersionName  = NULL;

+  LocalLastAttemptStatus  = LAST_ATTEMPT_STATUS_SUCCESS;

+  FmpImageInfoBuf         = NULL;

+  DescriptorVer           = NULL;

+  DescriptorSize          = NULL;

+  NumberOfFmpInstance     = 0;

+  FmpVersions             = NULL;

+  FmpVersionsCount        = 0;

+  IsSatisfied             = TRUE;

+  PackageVersionName      = NULL;

 

   //

   // Get ImageDescriptors of all FMP instances, and archive them for dependency evaluation.

@@ -77,30 +87,35 @@
   if (EFI_ERROR (Status)) {

     DEBUG ((DEBUG_ERROR, "CheckFmpDependency: Get Firmware Management Protocol failed. (%r)", Status));

     IsSatisfied = FALSE;

+    LocalLastAttemptStatus = LAST_ATTEMPT_STATUS_DEPENDENCY_CHECK_LIB_ERROR_FMP_PROTOCOL_NOT_FOUND;

     goto cleanup;

   }

 

   FmpImageInfoBuf = AllocateZeroPool (sizeof(EFI_FIRMWARE_IMAGE_DESCRIPTOR *) * NumberOfFmpInstance);

   if (FmpImageInfoBuf == NULL) {

     IsSatisfied = FALSE;

+    LocalLastAttemptStatus = LAST_ATTEMPT_STATUS_DEPENDENCY_CHECK_LIB_ERROR_MEM_ALLOC_FMP_INFO_BUFFER_FAILED;

     goto cleanup;

   }

 

   DescriptorVer = AllocateZeroPool (sizeof(UINT32) * NumberOfFmpInstance);

   if (DescriptorVer == NULL ) {

     IsSatisfied = FALSE;

+    LocalLastAttemptStatus = LAST_ATTEMPT_STATUS_DEPENDENCY_CHECK_LIB_ERROR_MEM_ALLOC_DESC_VER_BUFFER_FAILED;

     goto cleanup;

   }

 

   DescriptorSize = AllocateZeroPool (sizeof(UINTN) * NumberOfFmpInstance);

   if (DescriptorSize == NULL ) {

     IsSatisfied = FALSE;

+    LocalLastAttemptStatus = LAST_ATTEMPT_STATUS_DEPENDENCY_CHECK_LIB_ERROR_MEM_ALLOC_DESC_SIZE_BUFFER_FAILED;

     goto cleanup;

   }

 

   FmpVersions = AllocateZeroPool (sizeof(FMP_DEPEX_CHECK_VERSION_DATA) * NumberOfFmpInstance);

   if (FmpVersions == NULL) {

     IsSatisfied = FALSE;

+    LocalLastAttemptStatus = LAST_ATTEMPT_STATUS_DEPENDENCY_CHECK_LIB_ERROR_MEM_ALLOC_FMP_VER_BUFFER_FAILED;

     goto cleanup;

   }

 

@@ -164,7 +179,7 @@
   // Evaluate firmware image's depex, against the version of other Fmp instances.

   //

   if (Dependencies != NULL) {

-    IsSatisfied = EvaluateDependency (Dependencies, DependenciesSize, FmpVersions, FmpVersionsCount);

+    IsSatisfied = EvaluateDependency (Dependencies, DependenciesSize, FmpVersions, FmpVersionsCount, &LocalLastAttemptStatus);

   }

 

   if (!IsSatisfied) {

@@ -194,5 +209,9 @@
     FreePool (FmpVersions);

   }

 

+  if (LastAttemptStatus != NULL) {

+    *LastAttemptStatus = LocalLastAttemptStatus;

+  }

+

   return IsSatisfied;

 }

diff --git a/FmpDevicePkg/Library/FmpDependencyCheckLibNull/FmpDependencyCheckLibNull.c b/FmpDevicePkg/Library/FmpDependencyCheckLibNull/FmpDependencyCheckLibNull.c
index 55e9af2..7424795 100644
--- a/FmpDevicePkg/Library/FmpDependencyCheckLibNull/FmpDependencyCheckLibNull.c
+++ b/FmpDevicePkg/Library/FmpDependencyCheckLibNull/FmpDependencyCheckLibNull.c
@@ -2,11 +2,13 @@
   Null instance of FmpDependencyCheckLib.

 

   Copyright (c) 2020, Intel Corporation. All rights reserved.<BR>

+  Copyright (c) Microsoft Corporation.<BR>

 

   SPDX-License-Identifier: BSD-2-Clause-Patent

 

 **/

 #include <PiDxe.h>

+#include <Guid/SystemResourceTable.h>

 #include <Library/FmpDependencyCheckLib.h>

 

 /**

@@ -16,7 +18,10 @@
   @param[in]  Version            New version.

   @param[in]  Dependencies       Fmp dependency.

   @param[in]  DependenciesSize   Size, in bytes, of the Fmp dependency.

-

+  @param[out] LastAttemptStatus  An optional pointer to a UINT32 that holds the

+                                 last attempt status to report back to the caller.

+                                 This function will set the value to LAST_ATTEMPT_STATUS_SUCCESS

+                                 if an error code is not set.

   @retval  TRUE    Dependencies are satisfied.

   @retval  FALSE   Dependencies are unsatisfied or dependency check fails.

 

@@ -27,8 +32,13 @@
   IN  EFI_GUID                ImageTypeId,

   IN  UINT32                  Version,

   IN  EFI_FIRMWARE_IMAGE_DEP  *Dependencies,    OPTIONAL

-  IN  UINT32                  DependenciesSize

+  IN  UINT32                  DependenciesSize,

+  OUT UINT32                  *LastAttemptStatus OPTIONAL

   )

 {

+  if (LastAttemptStatus != NULL) {

+    *LastAttemptStatus = LAST_ATTEMPT_STATUS_SUCCESS;

+  }

+

   return TRUE;

 }

diff --git a/FmpDevicePkg/Library/FmpDependencyLib/FmpDependencyLib.c b/FmpDevicePkg/Library/FmpDependencyLib/FmpDependencyLib.c
index 5ef25d2..b222ace 100644
--- a/FmpDevicePkg/Library/FmpDependencyLib/FmpDependencyLib.c
+++ b/FmpDevicePkg/Library/FmpDependencyLib/FmpDependencyLib.c
@@ -13,6 +13,9 @@
 #include <Library/DebugLib.h>

 #include <Library/FmpDependencyLib.h>

 #include <Library/MemoryAllocationLib.h>

+#include <Guid/SystemResourceTable.h>

+#include <LastAttemptStatus.h>

+#include <FmpLastAttemptStatus.h>

 

 //

 // Define the initial size of the dependency expression evaluation stack

@@ -203,6 +206,10 @@
                                   parameter is optional and can be set to NULL.

   @param[in]   FmpVersionsCount   Element count of the array. When FmpVersions

                                   is NULL, FmpVersionsCount must be 0.

+  @param[out]  LastAttemptStatus  An optional pointer to a UINT32 that holds the

+                                  last attempt status to report back to the caller.

+                                  This function will set the value to LAST_ATTEMPT_STATUS_SUCCESS

+                                  if an error code is not set.

 

   @retval TRUE    Dependency expressions evaluate to TRUE.

   @retval FALSE   Dependency expressions evaluate to FALSE.

@@ -211,10 +218,11 @@
 BOOLEAN

 EFIAPI

 EvaluateDependency (

-  IN EFI_FIRMWARE_IMAGE_DEP        *Dependencies,

-  IN UINTN                         DependenciesSize,

-  IN FMP_DEPEX_CHECK_VERSION_DATA  *FmpVersions      OPTIONAL,

-  IN UINTN                         FmpVersionsCount

+  IN  EFI_FIRMWARE_IMAGE_DEP        *Dependencies,

+  IN  UINTN                         DependenciesSize,

+  IN  FMP_DEPEX_CHECK_VERSION_DATA  *FmpVersions,      OPTIONAL

+  IN  UINTN                         FmpVersionsCount,

+  OUT UINT32                        *LastAttemptStatus OPTIONAL

   )

 {

   EFI_STATUS                        Status;

@@ -224,6 +232,9 @@
   DEPEX_ELEMENT                     Element2;

   GUID                              ImageTypeId;

   UINT32                            Version;

+  UINT32                            LocalLastAttemptStatus;

+

+  LocalLastAttemptStatus = LAST_ATTEMPT_STATUS_SUCCESS;

 

   //

   // Check if parameter is valid.

@@ -249,6 +260,7 @@
     case EFI_FMP_DEP_PUSH_GUID:

       if (Iterator + sizeof (EFI_GUID) >= (UINT8 *) Dependencies->Dependencies + DependenciesSize) {

         DEBUG ((DEBUG_ERROR, "EvaluateDependency: GUID extends beyond end of dependency expression!\n"));

+        LocalLastAttemptStatus = LAST_ATTEMPT_STATUS_DEPENDENCY_LIB_ERROR_GUID_BEYOND_DEPEX;

         goto Error;

       }

 

@@ -259,6 +271,7 @@
         if(CompareGuid (&FmpVersions[Index].ImageTypeId, &ImageTypeId)){

           Status = Push (FmpVersions[Index].Version, VersionType);

           if (EFI_ERROR (Status)) {

+            LocalLastAttemptStatus = LAST_ATTEMPT_STATUS_DEPENDENCY_LIB_ERROR_PUSH_FAILURE;

             goto Error;

           }

           break;

@@ -266,18 +279,21 @@
       }

       if (Index == FmpVersionsCount) {

         DEBUG ((DEBUG_ERROR, "EvaluateDependency: %g is not found!\n", &ImageTypeId));

+        LocalLastAttemptStatus = LAST_ATTEMPT_STATUS_DEPENDENCY_LIB_ERROR_FMP_NOT_FOUND;

         goto Error;

       }

       break;

     case EFI_FMP_DEP_PUSH_VERSION:

       if (Iterator + sizeof (UINT32) >= (UINT8 *) Dependencies->Dependencies + DependenciesSize ) {

         DEBUG ((DEBUG_ERROR, "EvaluateDependency: VERSION extends beyond end of dependency expression!\n"));

+        LocalLastAttemptStatus = LAST_ATTEMPT_STATUS_DEPENDENCY_LIB_ERROR_VERSION_BEYOND_DEPEX;

         goto Error;

       }

 

       Version = *(UINT32 *) (Iterator + 1);

       Status = Push (Version, VersionType);

       if (EFI_ERROR (Status)) {

+        LocalLastAttemptStatus = LAST_ATTEMPT_STATUS_DEPENDENCY_LIB_ERROR_PUSH_FAILURE;

         goto Error;

       }

       Iterator = Iterator + sizeof (UINT32);

@@ -286,154 +302,191 @@
       Iterator += AsciiStrnLenS ((CHAR8 *) Iterator, DependenciesSize - (Iterator - Dependencies->Dependencies));

       if (Iterator == (UINT8 *) Dependencies->Dependencies + DependenciesSize) {

         DEBUG ((DEBUG_ERROR, "EvaluateDependency: STRING extends beyond end of dependency expression!\n"));

+        LocalLastAttemptStatus = LAST_ATTEMPT_STATUS_DEPENDENCY_LIB_ERROR_VERSION_STR_BEYOND_DEPEX;

         goto Error;

       }

       break;

     case EFI_FMP_DEP_AND:

       Status = Pop (&Element1, BooleanType);

       if (EFI_ERROR (Status)) {

+        LocalLastAttemptStatus = LAST_ATTEMPT_STATUS_DEPENDENCY_LIB_ERROR_POP_FAILURE;

         goto Error;

       }

       Status = Pop (&Element2, BooleanType);

       if (EFI_ERROR (Status)) {

+        LocalLastAttemptStatus = LAST_ATTEMPT_STATUS_DEPENDENCY_LIB_ERROR_POP_FAILURE;

         goto Error;

       }

       Status = Push (Element1.Value.Boolean & Element2.Value.Boolean, BooleanType);

       if (EFI_ERROR (Status)) {

+        LocalLastAttemptStatus = LAST_ATTEMPT_STATUS_DEPENDENCY_LIB_ERROR_PUSH_FAILURE;

         goto Error;

       }

       break;

     case EFI_FMP_DEP_OR:

       Status = Pop (&Element1, BooleanType);

       if (EFI_ERROR (Status)) {

+        LocalLastAttemptStatus = LAST_ATTEMPT_STATUS_DEPENDENCY_LIB_ERROR_POP_FAILURE;

         goto Error;

       }

       Status = Pop(&Element2, BooleanType);

       if (EFI_ERROR (Status)) {

+        LocalLastAttemptStatus = LAST_ATTEMPT_STATUS_DEPENDENCY_LIB_ERROR_POP_FAILURE;

         goto Error;

       }

       Status = Push (Element1.Value.Boolean | Element2.Value.Boolean, BooleanType);

       if (EFI_ERROR (Status)) {

+        LocalLastAttemptStatus = LAST_ATTEMPT_STATUS_DEPENDENCY_LIB_ERROR_PUSH_FAILURE;

         goto Error;

       }

       break;

     case EFI_FMP_DEP_NOT:

       Status = Pop (&Element1, BooleanType);

       if (EFI_ERROR (Status)) {

+        LocalLastAttemptStatus = LAST_ATTEMPT_STATUS_DEPENDENCY_LIB_ERROR_POP_FAILURE;

         goto Error;

       }

       Status = Push (!(Element1.Value.Boolean), BooleanType);

       if (EFI_ERROR (Status)) {

+        LocalLastAttemptStatus = LAST_ATTEMPT_STATUS_DEPENDENCY_LIB_ERROR_PUSH_FAILURE;

         goto Error;

       }

       break;

     case EFI_FMP_DEP_TRUE:

       Status = Push (TRUE, BooleanType);

       if (EFI_ERROR (Status)) {

+        LocalLastAttemptStatus = LAST_ATTEMPT_STATUS_DEPENDENCY_LIB_ERROR_PUSH_FAILURE;

         goto Error;

       }

       break;

     case EFI_FMP_DEP_FALSE:

       Status = Push (FALSE, BooleanType);

       if (EFI_ERROR (Status)) {

+        LocalLastAttemptStatus = LAST_ATTEMPT_STATUS_DEPENDENCY_LIB_ERROR_PUSH_FAILURE;

         goto Error;

       }

       break;

     case EFI_FMP_DEP_EQ:

       Status = Pop (&Element1, VersionType);

       if (EFI_ERROR (Status)) {

+        LocalLastAttemptStatus = LAST_ATTEMPT_STATUS_DEPENDENCY_LIB_ERROR_POP_FAILURE;

         goto Error;

       }

       Status = Pop (&Element2, VersionType);

       if (EFI_ERROR (Status)) {

+        LocalLastAttemptStatus = LAST_ATTEMPT_STATUS_DEPENDENCY_LIB_ERROR_POP_FAILURE;

         goto Error;

       }

       Status = (Element1.Value.Version == Element2.Value.Version) ? Push (TRUE, BooleanType) : Push (FALSE, BooleanType);

       if (EFI_ERROR (Status)) {

+        LocalLastAttemptStatus = LAST_ATTEMPT_STATUS_DEPENDENCY_LIB_ERROR_PUSH_FAILURE;

         goto Error;

       }

       break;

     case EFI_FMP_DEP_GT:

       Status = Pop (&Element1, VersionType);

       if (EFI_ERROR (Status)) {

+        LocalLastAttemptStatus = LAST_ATTEMPT_STATUS_DEPENDENCY_LIB_ERROR_POP_FAILURE;

         goto Error;

       }

       Status = Pop (&Element2, VersionType);

       if (EFI_ERROR (Status)) {

+        LocalLastAttemptStatus = LAST_ATTEMPT_STATUS_DEPENDENCY_LIB_ERROR_POP_FAILURE;

         goto Error;

       }

       Status = (Element1.Value.Version >  Element2.Value.Version) ? Push (TRUE, BooleanType) : Push (FALSE, BooleanType);

       if (EFI_ERROR (Status)) {

+        LocalLastAttemptStatus = LAST_ATTEMPT_STATUS_DEPENDENCY_LIB_ERROR_PUSH_FAILURE;

         goto Error;

       }

       break;

     case EFI_FMP_DEP_GTE:

       Status = Pop (&Element1, VersionType);

       if (EFI_ERROR (Status)) {

+        LocalLastAttemptStatus = LAST_ATTEMPT_STATUS_DEPENDENCY_LIB_ERROR_POP_FAILURE;

         goto Error;

       }

       Status = Pop (&Element2, VersionType);

       if (EFI_ERROR (Status)) {

+        LocalLastAttemptStatus = LAST_ATTEMPT_STATUS_DEPENDENCY_LIB_ERROR_POP_FAILURE;

         goto Error;

       }

       Status = (Element1.Value.Version >= Element2.Value.Version) ? Push (TRUE, BooleanType) : Push (FALSE, BooleanType);

       if (EFI_ERROR (Status)) {

+        LocalLastAttemptStatus = LAST_ATTEMPT_STATUS_DEPENDENCY_LIB_ERROR_PUSH_FAILURE;

         goto Error;

       }

       break;

     case EFI_FMP_DEP_LT:

       Status = Pop (&Element1, VersionType);

       if (EFI_ERROR (Status)) {

+        LocalLastAttemptStatus = LAST_ATTEMPT_STATUS_DEPENDENCY_LIB_ERROR_POP_FAILURE;

         goto Error;

       }

       Status = Pop (&Element2, VersionType);

       if (EFI_ERROR (Status)) {

+        LocalLastAttemptStatus= LAST_ATTEMPT_STATUS_DEPENDENCY_LIB_ERROR_POP_FAILURE;

         goto Error;

       }

       Status = (Element1.Value.Version <  Element2.Value.Version) ? Push (TRUE, BooleanType) : Push (FALSE, BooleanType);

       if (EFI_ERROR (Status)) {

+        LocalLastAttemptStatus = LAST_ATTEMPT_STATUS_DEPENDENCY_LIB_ERROR_PUSH_FAILURE;

         goto Error;

       }

       break;

     case EFI_FMP_DEP_LTE:

       Status = Pop (&Element1, VersionType);

       if (EFI_ERROR (Status)) {

+        LocalLastAttemptStatus = LAST_ATTEMPT_STATUS_DEPENDENCY_LIB_ERROR_POP_FAILURE;

         goto Error;

       }

       Status = Pop (&Element2, VersionType);

       if (EFI_ERROR (Status)) {

+        LocalLastAttemptStatus = LAST_ATTEMPT_STATUS_DEPENDENCY_LIB_ERROR_POP_FAILURE;

         goto Error;

       }

       Status = (Element1.Value.Version <= Element2.Value.Version) ? Push (TRUE, BooleanType) : Push (FALSE, BooleanType);

       if (EFI_ERROR (Status)) {

+        LocalLastAttemptStatus = LAST_ATTEMPT_STATUS_DEPENDENCY_LIB_ERROR_PUSH_FAILURE;

         goto Error;

       }

       break;

     case EFI_FMP_DEP_END:

       Status = Pop (&Element1, BooleanType);

       if (EFI_ERROR (Status)) {

+        LocalLastAttemptStatus = LAST_ATTEMPT_STATUS_DEPENDENCY_LIB_ERROR_POP_FAILURE;

         goto Error;

       }

       return Element1.Value.Boolean;

     default:

       DEBUG ((DEBUG_ERROR, "EvaluateDependency: Unknown Opcode - %02x!\n", *Iterator));

+      LocalLastAttemptStatus = LAST_ATTEMPT_STATUS_DEPENDENCY_LIB_ERROR_UNKNOWN_OPCODE;

       goto Error;

     }

     Iterator++;

   }

 

   DEBUG ((DEBUG_ERROR, "EvaluateDependency: No EFI_FMP_DEP_END Opcode in expression!\n"));

+  LocalLastAttemptStatus = LAST_ATTEMPT_STATUS_DEPENDENCY_LIB_ERROR_NO_END_OPCODE;

 

 Error:

+  if (LastAttemptStatus != NULL) {

+    *LastAttemptStatus = LocalLastAttemptStatus;

+  }

+

   return FALSE;

 }

 

 /**

   Validate the dependency expression and output its size.

 

-  @param[in]   Dependencies   Pointer to the EFI_FIRMWARE_IMAGE_DEP.

-  @param[in]   MaxDepexSize   Max size of the dependency.

-  @param[out]  DepexSize      Size of dependency.

+  @param[in]   Dependencies       Pointer to the EFI_FIRMWARE_IMAGE_DEP.

+  @param[in]   MaxDepexSize       Max size of the dependency.

+  @param[out]  DepexSize          Size of dependency.

+  @param[out]  LastAttemptStatus  An optional pointer to a UINT32 that holds the

+                                  last attempt status to report back to the caller.

+                                  If a last attempt status error code is not returned,

+                                  this function will not modify the LastAttemptStatus value.

 

   @retval TRUE    The dependency expression is valid.

   @retval FALSE   The dependency expression is invalid.

@@ -444,7 +497,8 @@
 ValidateDependency (

   IN  EFI_FIRMWARE_IMAGE_DEP  *Dependencies,

   IN  UINTN                   MaxDepexSize,

-  OUT UINT32                  *DepexSize

+  OUT UINT32                  *DepexSize,

+  OUT UINT32                  *LastAttemptStatus OPTIONAL

   )

 {

   UINT8  *Depex;

@@ -493,16 +547,23 @@
     }

   }

 

+  if (LastAttemptStatus != NULL) {

+    *LastAttemptStatus = LAST_ATTEMPT_STATUS_DEPENDENCY_LIB_ERROR_NO_END_OPCODE;

+  }

+

   return FALSE;

 }

 

 /**

   Get dependency from firmware image.

 

-  @param[in]  Image       Points to the firmware image.

-  @param[in]  ImageSize   Size, in bytes, of the firmware image.

-  @param[out] DepexSize   Size, in bytes, of the dependency.

-

+  @param[in]  Image               Points to the firmware image.

+  @param[in]  ImageSize           Size, in bytes, of the firmware image.

+  @param[out] DepexSize           Size, in bytes, of the dependency.

+  @param[out] LastAttemptStatus   An optional pointer to a UINT32 that holds the

+                                  last attempt status to report back to the caller.

+                                  If a last attempt status error code is not returned,

+                                  this function will not modify the LastAttemptStatus value.

   @retval  The pointer to dependency.

   @retval  Null

 

@@ -512,7 +573,8 @@
 GetImageDependency (

   IN  EFI_FIRMWARE_IMAGE_AUTHENTICATION *Image,

   IN  UINTN                             ImageSize,

-  OUT UINT32                            *DepexSize

+  OUT UINT32                            *DepexSize,

+  OUT UINT32                            *LastAttemptStatus  OPTIONAL

   )

 {

   EFI_FIRMWARE_IMAGE_DEP *Depex;

@@ -530,6 +592,9 @@
     //

     // Pointer overflow. Invalid image.

     //

+    if (LastAttemptStatus != NULL) {

+      *LastAttemptStatus = LAST_ATTEMPT_STATUS_DEPENDENCY_LIB_ERROR_GET_DEPEX_FAILURE;

+    }

     return NULL;

   }

 

@@ -539,7 +604,7 @@
   //

   // Validate the dependency and get the size of dependency

   //

-  if (ValidateDependency (Depex, MaxDepexSize, DepexSize)) {

+  if (ValidateDependency (Depex, MaxDepexSize, DepexSize, LastAttemptStatus)) {

     return Depex;

   }

 

diff --git a/FmpDevicePkg/Test/UnitTest/Library/FmpDependencyLib/EvaluateDependencyUnitTest.c b/FmpDevicePkg/Test/UnitTest/Library/FmpDependencyLib/EvaluateDependencyUnitTest.c
index f8ccdd9..01ac19a 100644
--- a/FmpDevicePkg/Test/UnitTest/Library/FmpDependencyLib/EvaluateDependencyUnitTest.c
+++ b/FmpDevicePkg/Test/UnitTest/Library/FmpDependencyLib/EvaluateDependencyUnitTest.c
@@ -2,6 +2,7 @@
   Unit tests of EvaluateDependency API in FmpDependencyLib.

 

   Copyright (c) 2020, Intel Corporation. All rights reserved.<BR>

+  Copyright (c) Microsoft Corporation.<BR>

   SPDX-License-Identifier: BSD-2-Clause-Patent

 

 **/

@@ -162,6 +163,7 @@
 {

   BASIC_TEST_CONTEXT  *TestContext;

   BOOLEAN             EvaluationResult;

+  UINT32              LastAttemptStatus;

 

   TestContext = (BASIC_TEST_CONTEXT *)Context;

 

@@ -169,8 +171,9 @@
                        (EFI_FIRMWARE_IMAGE_DEP *)TestContext->Dependencies,

                        TestContext->DependenciesSize,

                        mFmpVersions,

-                       sizeof(mFmpVersions)/sizeof(FMP_DEPEX_CHECK_VERSION_DATA)

-                     );

+                       sizeof(mFmpVersions)/sizeof(FMP_DEPEX_CHECK_VERSION_DATA),

+                       &LastAttemptStatus

+                       );

 

   UT_ASSERT_EQUAL (EvaluationResult, TestContext->ExpectedResult);