public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
From: "Wang, Jian J" <jian.j.wang@intel.com>
To: "Vang, Judah" <judah.vang@intel.com>,
	"devel@edk2.groups.io" <devel@edk2.groups.io>
Cc: "Yao, Jiewen" <jiewen.yao@intel.com>,
	"Xu, Min M" <min.m.xu@intel.com>,
	"Mistry, Nishant C" <nishant.c.mistry@intel.com>
Subject: Re: [PATCH v5 18/19] SecurityPkg: Add Protected Variable Services
Date: Tue, 22 Nov 2022 07:59:52 +0000	[thread overview]
Message-ID: <MW4PR11MB6763CFA4ED41217F261D42E9B60D9@MW4PR11MB6763.namprd11.prod.outlook.com> (raw)
In-Reply-To: <20221106073509.3071-19-judah.vang@intel.com>

Judah,

Several format issues. See inline comments starting with "[JianJW]".
With them addressed,

    Reviewed-by: Jian J Wang <jian.j.wang@intel.com>

Regards,
Jian

> -----Original Message-----
> From: Vang, Judah <judah.vang@intel.com>
> Sent: Sunday, November 06, 2022 3:35 PM
> To: devel@edk2.groups.io
> Cc: Wang, Jian J <jian.j.wang@intel.com>; Yao, Jiewen <jiewen.yao@intel.com>;
> Xu, Min M <min.m.xu@intel.com>; Mistry, Nishant C
> <nishant.c.mistry@intel.com>
> Subject: [PATCH v5 18/19] SecurityPkg: Add Protected Variable Services
> 
> REF: https://bugzilla.tianocore.org/show_bug.cgi?id=2594
> 
> V5: Applied code review comments. Remove unused API.
> V3: Change placement of buffer used for confidentiality crypto
> operation to fix an issue when enabling confidentiality. Remove
> un-needed increment of monotonic counter.
> 
> V1: Add Protected Variable Services across the different UEFI phases.
> Functions includes creating variable digest, performing integrity
> check, initializing protected variables, updating protected
> variables, and verifying the MetaDataHmacVar variable.
> This module prevents UEFI variable tampering.  It provides
> variable integrity and confidentiality.
> 
> Cc: Jian J Wang <jian.j.wang@intel.com>
> Cc: Jiewen Yao <jiewen.yao@intel.com>
> Cc: Min Xu <min.m.xu@intel.com>
> Cc: Nishant C Mistry <nishant.c.mistry@intel.com>
> Signed-off-by: Jian J Wang <jian.j.wang@intel.com>
> Signed-off-by: Nishant C Mistry <nishant.c.mistry@intel.com>
> Signed-off-by: Judah Vang <judah.vang@intel.com>
> ---
>  SecurityPkg/Library/ProtectedVariableLib/DxeProtectedVariableLib.inf        |   64
> +
>  SecurityPkg/Library/ProtectedVariableLib/PeiProtectedVariableLib.inf        |   68
> +
>  SecurityPkg/Library/ProtectedVariableLib/SmmProtectedVariableLib.inf        |
> 67 +
>  SecurityPkg/Library/ProtectedVariableLib/SmmRuntimeProtectedVariableLib.inf
> |   62 +
>  SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableInternal.h        |
> 589 ++++++
>  SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableCommon.c          |
> 2103 ++++++++++++++++++++
>  SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableDxe.c             |  163
> ++
>  SecurityPkg/Library/ProtectedVariableLib/ProtectedVariablePei.c             | 1327
> ++++++++++++
>  SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableSmm.c             |  209
> ++
>  SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableSmmDxeCommon.c
> |  967 +++++++++
>  SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableSmmRuntime.c      |
> 233 +++
>  11 files changed, 5852 insertions(+)
> 
> diff --git
> a/SecurityPkg/Library/ProtectedVariableLib/DxeProtectedVariableLib.inf
> b/SecurityPkg/Library/ProtectedVariableLib/DxeProtectedVariableLib.inf
> new file mode 100644
> index 000000000000..74a0285af7ef
> --- /dev/null
> +++ b/SecurityPkg/Library/ProtectedVariableLib/DxeProtectedVariableLib.inf
> @@ -0,0 +1,64 @@
> +## @file
> +#  Provides protected variable services for EmulatorPkg.
> +#
> +#  Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
> +#  SPDX-License-Identifier: BSD-2-Clause-Patent
> +#
> +##
> +
> +[Defines]
> +  INF_VERSION                    = 0x00010029
> +  BASE_NAME                      = DxeProtectedVariableLib
> +  MODULE_UNI_FILE                = ProtectedVariableLib.uni
> +  FILE_GUID                      = 6F424E10-0F75-4716-9F97-58C2E1C643AD
> +  MODULE_TYPE                    = DXE_RUNTIME_DRIVER
> +  VERSION_STRING                 = 0.1
> +  LIBRARY_CLASS                  = ProtectedVariableLib|DXE_RUNTIME_DRIVER
> +
> +#
> +# The following information is for reference only and not required by the build
> tools.
> +#
> +#  VALID_ARCHITECTURES           = IA32 X64
> +#
> +
> +[Sources]
> +  ProtectedVariableDxe.c
> +  ProtectedVariableCommon.c
> +  ProtectedVariableSmmDxeCommon.c
> +  ProtectedVariableInternal.h
> +
> +[Packages]
> +  MdePkg/MdePkg.dec
> +  MdeModulePkg/MdeModulePkg.dec
> +  SecurityPkg/SecurityPkg.dec
> +  CryptoPkg/CryptoPkg.dec
> +
> +[LibraryClasses]
> +  BaseLib
> +  BaseMemoryLib
> +  DebugLib
> +  MemoryAllocationLib
> +  HobLib
> +  BaseCryptLib
> +  EncryptionVariableLib
> +  RpmcLib
> +  HashApiLib
> +  SortLib
> +
> +[Protocols]
> +  gEfiVariableWriteArchProtocolGuid
> +
> +[Guids]
> +  gEdkiiMetaDataHmacVariableGuid
> +  gEdkiiProtectedVariableGlobalGuid
> +  gEdkiiVarErrorFlagGuid
> +  gEdkiiProtectedVariableContextGuid
> +
> +[Pcd]
> +  gEfiSecurityPkgTokenSpaceGuid.PcdPlatformVariableName
> +  gEfiSecurityPkgTokenSpaceGuid.PcdPlatformVariableGuid
> +  gEfiSecurityPkgTokenSpaceGuid.PcdProtectedVariableIntegrity
> +  gEfiSecurityPkgTokenSpaceGuid.PcdProtectedVariableConfidentiality
> +  gEfiMdeModulePkgTokenSpaceGuid.PcdMaxVariableSize
> +  gEfiMdeModulePkgTokenSpaceGuid.PcdMaxAuthVariableSize
> +  gEfiMdeModulePkgTokenSpaceGuid.PcdMaxHardwareErrorVariableSize
> diff --git a/SecurityPkg/Library/ProtectedVariableLib/PeiProtectedVariableLib.inf
> b/SecurityPkg/Library/ProtectedVariableLib/PeiProtectedVariableLib.inf
> new file mode 100644
> index 000000000000..44c959a94ca3
> --- /dev/null
> +++ b/SecurityPkg/Library/ProtectedVariableLib/PeiProtectedVariableLib.inf
> @@ -0,0 +1,68 @@
> +## @file
> +#  Provides protected variable services.
> +#
> +#  Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
> +#  SPDX-License-Identifier: BSD-2-Clause-Patent
> +#
> +##
> +
> +[Defines]
> +  INF_VERSION                    = 0x00010029
> +  BASE_NAME                      = PeiProtectedVariableLib
> +  MODULE_UNI_FILE                = ProtectedVariableLib.uni
> +  FILE_GUID                      = 76FBFBCE-ACBB-4084-A348-8FCC97AAEB9D
> +  MODULE_TYPE                    = PEIM
> +  VERSION_STRING                 = 0.1
> +  LIBRARY_CLASS                  = ProtectedVariableLib|PEIM
> +
> +#
> +# The following information is for reference only and not required by the build
> tools.
> +#
> +#  VALID_ARCHITECTURES           = IA32 X64 AARCH64
> +#
> +
> +[Sources]
> +  ProtectedVariablePei.c
> +  ProtectedVariableCommon.c
> +  ProtectedVariableInternal.h
> +
> +[Packages]
> +  MdePkg/MdePkg.dec
> +  MdeModulePkg/MdeModulePkg.dec
> +  SecurityPkg/SecurityPkg.dec
> +  CryptoPkg/CryptoPkg.dec
> +
> +[LibraryClasses]
> +  BaseLib
> +  BaseMemoryLib
> +  DebugLib
> +  MemoryAllocationLib
> +  HobLib
> +  BaseCryptLib
> +  RpmcLib
> +  VariableKeyLib
> +  EncryptionVariableLib
> +  ReportStatusCodeLib
> +  PeiServicesLib
> +  HashApiLib
> +  SortLib
> +
> +[Guids]
> +  gEdkiiMetaDataHmacVariableGuid
> +  gEdkiiProtectedVariableGlobalGuid
> +  gEdkiiVarErrorFlagGuid
> +  gEdkiiProtectedVariableContextGuid
> +
> +[Ppis]
> +  gEfiPeiMemoryDiscoveredPpiGuid
> +  gEfiPeiVariableStoreDiscoveredPpiGuid
> +
> +[Pcd]
> +  gEfiSecurityPkgTokenSpaceGuid.PcdStatusCodeVariableIntegrity
> +  gEfiSecurityPkgTokenSpaceGuid.PcdPlatformVariableName
> +  gEfiSecurityPkgTokenSpaceGuid.PcdPlatformVariableGuid
> +  gEfiSecurityPkgTokenSpaceGuid.PcdProtectedVariableIntegrity
> +  gEfiSecurityPkgTokenSpaceGuid.PcdProtectedVariableConfidentiality
> +  gEfiMdeModulePkgTokenSpaceGuid.PcdMaxVariableSize
> +  gEfiMdeModulePkgTokenSpaceGuid.PcdMaxAuthVariableSize
> +  gEfiMdeModulePkgTokenSpaceGuid.PcdMaxHardwareErrorVariableSize
> diff --git
> a/SecurityPkg/Library/ProtectedVariableLib/SmmProtectedVariableLib.inf
> b/SecurityPkg/Library/ProtectedVariableLib/SmmProtectedVariableLib.inf
> new file mode 100644
> index 000000000000..ecf0b1a43d30
> --- /dev/null
> +++ b/SecurityPkg/Library/ProtectedVariableLib/SmmProtectedVariableLib.inf
> @@ -0,0 +1,67 @@
> +## @file
> +#  Provides protected variable services.
> +#
> +#  Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
> +#  SPDX-License-Identifier: BSD-2-Clause-Patent
> +#
> +##
> +
> +[Defines]
> +  INF_VERSION                    = 0x00010029
> +  BASE_NAME                      = SmmProtectedVariableLib
> +  MODULE_UNI_FILE                = ProtectedVariableLib.uni
> +  FILE_GUID                      = 2BEE71E5-259B-4057-A2C1-2115DF43C76A
> +  MODULE_TYPE                    = DXE_SMM_DRIVER
> +  VERSION_STRING                 = 0.1
> +  LIBRARY_CLASS                  = ProtectedVariableLib|DXE_SMM_DRIVER
> MM_STANDALONE
> +
> +#
> +# The following information is for reference only and not required by the build
> tools.
> +#
> +#  VALID_ARCHITECTURES           = IA32 X64
> +#
> +
> +[Sources]
> +  ProtectedVariableSmm.c
> +  ProtectedVariableCommon.c
> +  ProtectedVariableSmmDxeCommon.c
> +  ProtectedVariableInternal.h
> +
> +[Packages]
> +  MdePkg/MdePkg.dec
> +  MdeModulePkg/MdeModulePkg.dec
> +  SecurityPkg/SecurityPkg.dec
> +  CryptoPkg/CryptoPkg.dec
> +
> +[LibraryClasses]
> +  BaseLib
> +  BaseMemoryLib
> +  DebugLib
> +  MemoryAllocationLib
> +  HobLib
> +  BaseCryptLib
> +  EncryptionVariableLib
> +  RpmcLib
> +  VariableKeyLib
> +  HashApiLib
> +  SortLib
> +
> +[Protocols]
> +  gEfiMmEndOfDxeProtocolGuid
> +
> +[Guids]
> +  gSmmVariableWriteGuid
> +  gEdkiiMetaDataHmacVariableGuid
> +  gEdkiiProtectedVariableGlobalGuid
> +  gEdkiiVarErrorFlagGuid
> +  gEdkiiProtectedVariableContextGuid
> +
> +[Pcd]
> +  gEfiSecurityPkgTokenSpaceGuid.PcdPlatformVariableName
> +  gEfiSecurityPkgTokenSpaceGuid.PcdPlatformVariableGuid
> +  gEfiSecurityPkgTokenSpaceGuid.PcdProtectedVariableIntegrity
> +  gEfiSecurityPkgTokenSpaceGuid.PcdProtectedVariableConfidentiality
> +  gEfiMdeModulePkgTokenSpaceGuid.PcdMaxVariableSize
> +  gEfiMdeModulePkgTokenSpaceGuid.PcdMaxAuthVariableSize
> +  gEfiMdeModulePkgTokenSpaceGuid.PcdMaxHardwareErrorVariableSize
> +
> diff --git
> a/SecurityPkg/Library/ProtectedVariableLib/SmmRuntimeProtectedVariableLib.i
> nf
> b/SecurityPkg/Library/ProtectedVariableLib/SmmRuntimeProtectedVariableLib.i
> nf
> new file mode 100644
> index 000000000000..011ccdce2db8
> --- /dev/null
> +++
> b/SecurityPkg/Library/ProtectedVariableLib/SmmRuntimeProtectedVariableLib.i
> nf
> @@ -0,0 +1,62 @@
> +## @file
> +#  Provides protected variable services.
> +#
> +#  Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
> +#  SPDX-License-Identifier: BSD-2-Clause-Patent
> +#
> +##
> +
> +[Defines]
> +  INF_VERSION                    = 0x00010029
> +  BASE_NAME                      = SmmRuntimeProtectedVariableLib
> +  MODULE_UNI_FILE                = ProtectedVariableLib.uni
> +  FILE_GUID                      = 99A623DE-1AD3-4AB3-909D-E3AADD7845EF
> +  MODULE_TYPE                    = DXE_RUNTIME_DRIVER
> +  VERSION_STRING                 = 0.1
> +  LIBRARY_CLASS                  = ProtectedVariableLib|DXE_DRIVER
> DXE_RUNTIME_DRIVER
> +
> +#
> +# The following information is for reference only and not required by the build
> tools.
> +#
> +#  VALID_ARCHITECTURES           = IA32 X64
> +#
> +
> +[Sources]
> +  ProtectedVariableSmmRuntime.c
> +  ProtectedVariableCommon.c
> +  ProtectedVariableInternal.h
> +
> +[Packages]
> +  MdePkg/MdePkg.dec
> +  MdeModulePkg/MdeModulePkg.dec
> +  SecurityPkg/SecurityPkg.dec
> +  CryptoPkg/CryptoPkg.dec
> +
> +[LibraryClasses]
> +  BaseLib
> +  BaseMemoryLib
> +  DebugLib
> +  MemoryAllocationLib
> +  HobLib
> +  BaseCryptLib
> +  EncryptionVariableLib
> +  RpmcLib
> +  HashApiLib
> +  SortLib
> +
> +[Guids]
> +  gEfiEventVirtualAddressChangeGuid
> +  gEdkiiMetaDataHmacVariableGuid
> +  gEdkiiProtectedVariableGlobalGuid
> +  gEdkiiVarErrorFlagGuid
> +  gEdkiiProtectedVariableContextGuid
> +
> +[Pcd]
> +  gEfiSecurityPkgTokenSpaceGuid.PcdPlatformVariableName
> +  gEfiSecurityPkgTokenSpaceGuid.PcdPlatformVariableGuid
> +  gEfiSecurityPkgTokenSpaceGuid.PcdProtectedVariableIntegrity
> +  gEfiSecurityPkgTokenSpaceGuid.PcdProtectedVariableConfidentiality
> +  gEfiMdeModulePkgTokenSpaceGuid.PcdMaxVariableSize
> +  gEfiMdeModulePkgTokenSpaceGuid.PcdMaxAuthVariableSize
> +  gEfiMdeModulePkgTokenSpaceGuid.PcdMaxHardwareErrorVariableSize
> +
> diff --git
> a/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableInternal.h
> b/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableInternal.h
> new file mode 100644
> index 000000000000..7948649b21bd
> --- /dev/null
> +++ b/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableInternal.h
> @@ -0,0 +1,589 @@
> +/** @file
> +  Definitions shared among different implementation of ProtectedVariableLib.
> +
> +Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
> +SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#ifndef PROTECTED_VARIABLE_INTERNAL_H_
> +#define PROTECTED_VARIABLE_INTERNAL_H_
> +
> +#include <Guid/VariableFormat.h>
> +#include <Guid/ProtectedVariable.h>
> +
> +#include <Library/DebugLib.h>
> +#include <Library/BaseMemoryLib.h>
> +#include <Library/BaseCryptLib.h>
> +#include <Library/RpmcLib.h>
> +#include <Library/VariableKeyLib.h>
> +#include <Library/EncryptionVariableLib.h>
> +#include <Library/ProtectedVariableLib.h>
> +#include <Library/HashApiLib.h>
> +
> +#define VARIABLE_KEY_SIZE  (256/8)
> +
> +#define METADATA_HMAC_SIZE           (256/8)
> +#define METADATA_HMAC_KEY_NAME       L"HMAC_KEY"
> +#define METADATA_HMAC_KEY_NAME_SIZE  0x10
> +
> +#define METADATA_HMAC_SEP       L":"
> +#define METADATA_HMAC_SEP_SIZE  2
> +
> +#define METADATA_HMAC_VARIABLE_NAME       L"MetaDataHmacVar"
> +#define METADATA_HMAC_VARIABLE_NAME_SIZE  sizeof
> (METADATA_HMAC_VARIABLE_NAME)
> +#define METADATA_HMAC_VARIABLE_GUID
> gEdkiiMetaDataHmacVariableGuid
> +#define METADATA_HMAC_VARIABLE_ATTR
> VARIABLE_ATTRIBUTE_NV_BS_RT
> +
> +#define DIGEST_CONTEXT_SIZE  (HashApiGetContextSize())
> +
> +#define MAX_VARIABLE_SIZE                                                       \
> +  MAX (MAX (PcdGet32 (PcdMaxVariableSize), PcdGet32
> (PcdMaxAuthVariableSize)),  \
> +       PcdGet32 (PcdMaxHardwareErrorVariableSize))
> +
> +#define IS_VARIABLE(Var, Name, Guid)          \
> +  (StrCmp ((Var)->VariableName, (Name)) == 0  \
> +   && CompareGuid ((CONST EFI_GUID *)(Var)->VendorGuid, (CONST EFI_GUID
> *)(Guid)))
> +
> +#define VARIABLE_SIZE(VarInfo)                                  \
> +  (((UINTN)(VarInfo)->Header.Data - (UINTN)(VarInfo)->Buffer)   \
> +   + (VarInfo)->Header.DataSize                                 \
> +   + GET_PAD_SIZE ((VarInfo)->Header.DataSize))
> +
> +#define VARIABLE_HEADER_SIZE(AuthFlag)                    \
> +  ((AuthFlag) ? sizeof (AUTHENTICATED_VARIABLE_HEADER)    \
> +              : sizeof (VARIABLE_HEADER))
> +
> +#define VARIABLE_NAME(Var, AuthFlag)                    \
> +  ((CHAR16 *)((UINTN)(Var) + VARIABLE_HEADER_SIZE(AuthFlag)))
> +
> +#define VARIABLE_START(VarStore)  \
> +   ((VARIABLE_HEADER *)HEADER_ALIGN ((VARIABLE_STORE_HEADER
> *)(VarStore) + 1))
> +
> +#define VARIABLE_END(VarStore)                         \
> +   ((VARIABLE_HEADER *)HEADER_ALIGN ((UINTN)(VarStore) \
> +     + ((VARIABLE_STORE_HEADER *)(VarStore))->Size))
> +
> +#define SET_VARIABLE_DATA_SIZE(VarInfo, Size)                                 \
> +  if ((VarInfo)->Flags.Auth) {                                                \
> +    ((AUTHENTICATED_VARIABLE_HEADER *)((VarInfo)->Buffer))->DataSize =
> Size;  \
> +    (VarInfo)->Header.DataSize = Size;                                        \
> +  } else {                                                                    \
> +    ((VARIABLE_HEADER *)((VarInfo)->Buffer))->DataSize = Size;                \
> +      (VarInfo)->Header.DataSize = Size;                                      \
> +  }
> +
> +#define IS_KNOWN_UNPROTECTED_VARIABLE(Global, VarInfo)  \
> +  (CheckKnownUnprotectedVariable ((Global), (VarInfo)) <
> UnprotectedVarIndexMax)
> +
> +#define GET_CNTX(Global)   ((PROTECTED_VARIABLE_CONTEXT_IN
> *)(UINTN)((Global)->ContextIn))
> +#define GET_BUFR(Address)  ((VOID *)(UINTN)(Address))
> +#define GET_ADRS(Buffer)   ((EFI_PHYSICAL_ADDRESS)(UINTN)(Buffer))
> +
> +typedef struct _VARIABLE_IDENTIFIER {
> +  CHAR16      *VariableName;
> +  EFI_GUID    *VendorGuid;
> +  UINT8       State;
> +} VARIABLE_IDENTIFIER;
> +
> +typedef enum {
> +  IndexHmacInDel = 0,     /// MetaDataHmacVar with state
> VAR_IN_DELETED_TRANSITION
> +  IndexHmacAdded,         /// MetaDataHmacVar with state VAR_ADDED
> +  IndexErrorFlag,         /// VarErrorFlag
> +  IndexPlatformVar,       /// Platform Variable
> +  UnprotectedVarIndexMax
> +} UNPROTECTED_VARIABLE_INDEX;
> +
> +#pragma pack(1)
> +
> +#define PROTECTED_VARIABLE_CONTEXT_OUT_STRUCT_VERSION  0x02
> +
> +typedef struct _PROTECTED_VARIABLE_FLAG {
> +  BOOLEAN    Auth;          // Authenticated variable format
> +  BOOLEAN    WriteInit;     // Write-init-done
> +  BOOLEAN    WriteReady;    // Ready-to-write
> +  BOOLEAN    RecoveryMode;  // Variable storage recovery or provisioning
> +  BOOLEAN    CacheReady;    // Indicates Cache is available
> +  BOOLEAN    Reserved;      // reserved
> +} PROTECTED_VARIABLE_FLAG;
> +
> +typedef struct _PROTECTED_VARIABLE_GLOBAL {
> +  UINT32                     StructVersion;
> +  UINT32                     StructSize;
> +
> +  ///
> +  /// Variable root key used to derive Encryption key and HMAC key.
> +  ///
> +  UINT8                      RootKey[VARIABLE_KEY_SIZE];
> +  ///
> +  /// HMAC key derived from RootKey.
> +  ///
> +  UINT8                      MetaDataHmacKey[VARIABLE_KEY_SIZE];
> +  ///
> +  /// Number of variables in linked list pointed by VariableDigests.
> +  ///
> +  UINT32                     VariableNumber;
> +  ///
> +  /// Size of memory reserved by VariableCache.
> +  ///
> +  UINT32                     VariableCacheSize;
> +  ///
> +  /// Memory reserved to temporarily hold data of one variable, for integrity
> +  /// validation purpose.
> +  ///
> +  EFI_PHYSICAL_ADDRESS       VariableCache;
> +  ///
> +  /// Pointer to linked list, in which each node holds the digest value of each
> +  /// variable.
> +  ///
> +  EFI_PHYSICAL_ADDRESS       VariableDigests;
> +  ///
> +  /// Memory reserved for Context used in hash API to avoid repeat alloc/free.
> +  ///
> +  EFI_PHYSICAL_ADDRESS       DigestContext;
> +  ///
> +  /// Pointer to one of node in linked list pointed by VariableDigests, which
> +  /// has been just accessed. This is mainly used to facilitate the two calls
> +  /// use case of GetVariable().
> +  ///
> +  EFI_PHYSICAL_ADDRESS       LastAccessedVariable;
> +  ///
> +  /// Cached copy of pointers to nodes of unprotected variables in the linked
> +  /// list pointed by VariableDigests.
> +  ///
> +  EFI_PHYSICAL_ADDRESS       Unprotected[UnprotectedVarIndexMax];
> +  ///
> +  /// Pointer to data structure holding helper functions passed by user of
> +  /// ProtectedVariableLib, most of which are used to complete operations on
> +  /// variable storage.
> +  ///
> +  EFI_PHYSICAL_ADDRESS       ContextIn;
> +
> +  ///
> +  /// Pointer to Global data structure. This is to hold pre-mem address value.
> +  /// Later to be used to identify pre-mem to post-mem transition.
> +  ///
> +  EFI_PHYSICAL_ADDRESS       GlobalSelf;
> +
> +  PROTECTED_VARIABLE_FLAG    Flags;
> +} PROTECTED_VARIABLE_GLOBAL;
> +
> +#pragma pack()
> +
> +/* Sort method function pointer taking two parameters */
> +typedef
> +INTN
> +(*SORT_METHOD) (
> +  IN VARIABLE_DIGEST  *Variable1,
> +  IN VARIABLE_DIGEST  *Variable2
> +  );
> +
> +/* Update variable digest data function pointer */
> +typedef
> +BOOLEAN
> +(*DIGEST_UPDATE) (
> +  IN OUT  VOID   *Context,
> +  IN      VOID   *Data,
> +  IN      UINTN  DataSize
> +  );
> +
> +/**
> +
> +  Print variable information
> +
> +  @param[in]   Data8      Pointer to data
> +  @param[out]  DataSize   Size of data
> +
> +**/
> +VOID
> +PrintVariableData (
> +  IN UINT8  *Data8,
> +  IN UINTN  DataSize
> +  );
> +
> +/**
> +
> +  Derive HMAC key from given variable root key.
> +
> +  @param[in]  RootKey       Pointer to root key to derive from.
> +  @param[in]  RootKeySize   Size of root key.
> +  @param[out] HmacKey       Pointer to generated HMAC key.
> +  @param[in]  HmacKeySize   Size of HMAC key.
> +
> +  @retval TRUE      The HMAC key is derived successfully.
> +  @retval FALSE     Failed to generate HMAC key from given root key.
> +
> +**/
> +BOOLEAN
> +EFIAPI
> +GenerateMetaDataHmacKey (
> +  IN   CONST UINT8  *RootKey,
> +  IN   UINTN        RootKeySize,
> +  OUT  UINT8        *HmacKey,
> +  IN   UINTN        HmacKeySize
> +  );
> +
> +/**
> +
> +  Digests the given variable data and updates HMAC context.
> +
> +  @param[in,out]  Context   Pointer to initialized HMAC context.
> +  @param[in]      VarInfo   Pointer to variable data.
> +
> +  @retval TRUE    HMAC context was updated successfully.
> +  @retval FALSE   Failed to update HMAC context.
> +
> +**/
> +BOOLEAN
> +UpdateVariableMetadataHmac (
> +  IN  VOID                     *Context,
> +  IN  PROTECTED_VARIABLE_INFO  *VarInfo
> +  );
> +
> +/**
> +
> +  Re-calculate HMAC based on new variable data and re-generate
> MetaDataHmacVar.
> +
> +  @param[in]      Global          Pointer to global configuration data.
> +  @param[in]      NewVarInfo      Pointer to buffer of new variable data.
> +  @param[in,out]  NewHmacVarInfo  Pointer to buffer of new
> MetaDataHmacVar.
> +
> +  @return EFI_SUCCESS           The HMAC value was updated successfully.
> +  @return EFI_ABORTED           Failed to calculate the HMAC value.
> +  @return EFI_OUT_OF_RESOURCES  Not enough resource to calculate HMC
> value.
> +  @return EFI_NOT_FOUND         The MetaDataHmacVar was not found in
> storage.
> +
> +**/
> +EFI_STATUS
> +RefreshVariableMetadataHmac (
> +  IN      PROTECTED_VARIABLE_GLOBAL  *Global,
> +  IN      PROTECTED_VARIABLE_INFO    *NewVarInfo,
> +  IN  OUT PROTECTED_VARIABLE_INFO    *NewHmacVarInfo
> +  );
> +
> +/**
> +
> +  Retrieve the context and global configuration data structure from HOB.
> +
> +  Once protected NV variable storage is cached and verified in PEI phase,
> +  all related information are stored in a HOB which can be used by PEI variable
> +  service itself and passed to SMM along with the boot flow, which can avoid
> +  many duplicate works, like generating HMAC key, verifying NV variable
> storage,
> +  etc.
> +
> +  The HOB can be identified by gEdkiiProtectedVariableGlobalGuid.
> +
> +  @param[out]   Global      Pointer to global configuration data from PEI phase.
> +
> +  @retval EFI_SUCCESS     The HOB was found, and Context and Global are
> retrieved.
> +  @retval EFI_NOT_FOUND   The HOB was not found.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +GetProtectedVariableGlobalFromHob (
> +  OUT PROTECTED_VARIABLE_GLOBAL  **Global OPTIONAL
> +  );
> +
> +/**
> +
> +  Get context and/or global data structure used to process protected variable.
> +
> +  @param[out]   Global      Pointer to global configuration data.
> +
> +  @retval EFI_SUCCESS         Get requested structure successfully.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +GetProtectedVariableGlobal (
> +  OUT PROTECTED_VARIABLE_GLOBAL  **Global OPTIONAL
> +  );
> +
> +/**
> +
> +  Get context data structure used to process protected variable.
> +
> +  @param[out]   ContextIn   Pointer to context provided by variable runtime
> services.
> +
> +  @retval EFI_SUCCESS         Get requested structure successfully.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +GetProtectedVariableContext (
> +  PROTECTED_VARIABLE_CONTEXT_IN  **ContextIn  OPTIONAL
> +  );
> +
> +/**
> +
> +  Check if a given variable is unprotected variable specified in advance
> +  and return its index ID.
> +
> +  @param[in] Global     Pointer to global configuration data.
> +  @param[in] VarInfo    Pointer to variable information data.
> +
> +  @retval IndexHmacInDel    Variable is MetaDataHmacVar in delete-transition
> state.
> +  @retval IndexHmacAdded    Variable is MetaDataHmacVar in valid state.
> +  @retval IndexErrorFlag    Variable is VarErrorLog.
> +  @retval Others            Variable is not any known unprotected variables.
> +
> +**/
> +UNPROTECTED_VARIABLE_INDEX
> +CheckKnownUnprotectedVariable (
> +  IN  PROTECTED_VARIABLE_GLOBAL  *Global,
> +  IN  PROTECTED_VARIABLE_INFO    *VarInfo
> +  );
> +
> +/**
> +
> +  Return the size of variable MetaDataHmacVar.
> +
> +  @param[in] AuthFlag         Auth-variable indicator.
> +
> +  @retval size of variable MetaDataHmacVar.
> +
> +**/
> +UINTN
> +GetMetaDataHmacVarSize (
> +  IN      BOOLEAN  AuthFlag
> +  );
> +
> +/**
> +
> +  Fix state of MetaDataHmacVar on NV variable storage, if there's failure at
> +  last boot during updating variable.
> +
> +  This must be done before the first writing of variable in current boot,
> +  including storage reclaim.
> +
> +  @retval EFI_UNSUPPORTED        Updating NV variable storage is not
> supported.
> +  @retval EFI_OUT_OF_RESOURCES   Not enough resource to complete the
> operation.
> +  @retval EFI_SUCCESS            Variable store was successfully updated.
> +
> +**/
> +EFI_STATUS
> +FixupHmacVariable (
> +  VOID
> +  );
> +
> +/**
> +
> +  Verify the variable digest.
> +
> +  @param[in]  Global      Pointer to global configuration data.
> +  @param[in]  VarInfo     Pointer to verified copy of protected variables.
> +  @param[in]  VarDig      Pointer to variable digest data.
> +
> +  @retval EFI_INVALID_PARAMETER  Invalid parameter was passed in.
> +  @retval EFI_OUT_OF_RESOURCES   Not enough resource to calculate hash.
> +  @retval EFI_ABORTED            An error was encountered.
> +  @retval EFI_COMPROMISED_DATA   The data was compromised.
> +  @retval EFI_SUCCESS            Variable digest was successfully verified.
> +
> +**/
> +EFI_STATUS
> +VerifyVariableDigest (
> +  IN  PROTECTED_VARIABLE_GLOBAL  *Global,
> +  IN  PROTECTED_VARIABLE_INFO    *VarInfo,
> +  IN  VARIABLE_DIGEST            *VarDig
> +  );
> +
> +/**
> +
> +  Get the variable digest.
> +
> +  @param[in]      Global        Pointer to global configuration data.
> +  @param[in]      VarInfo       Pointer to verified copy of protected variables.
> +  @param[in,out]  DigestValue   Pointer to variable digest value.
> +
> +  @retval EFI_INVALID_PARAMETER  Invalid parameter was passed in.
> +  @retval EFI_OUT_OF_RESOURCES   Not enough resource to calculate hash.
> +  @retval EFI_ABORTED            An error was encountered.
> +  @retval EFI_COMPROMISED_DATA   The data was compromised.
> +  @retval EFI_SUCCESS            Variable digest was successfully verified.
> +
> +**/
> +EFI_STATUS
> +GetVariableDigest (
> +  IN      PROTECTED_VARIABLE_GLOBAL  *Global,
> +  IN      PROTECTED_VARIABLE_INFO    *VarInfo,
> +  IN  OUT UINT8                      *DigestValue
> +  );
> +
> +/**
> +
> +  Compare variable name and Guid
> +
> +  @param[in]  Name1      Name of first variable.
> +  @param[in]  Name1Size  Size of first variable.
> +  @param[in]  Name2      Name of second variable.
> +  @param[in]  Name2Size  Size of second variable.
> +  @param[in]  Guid1      Guid for first variable.
> +  @param[in]  Guid2      Guid for second variable.
> +
> +  @retval 0         First name is identical to Second name.
> +  @return others    First name is not identical to Second name.
> +
> +**/
> +INTN
> +CompareVariableNameAndGuid (
> +  IN CONST CHAR16  *Name1,
> +  IN UINTN         Name1Size,
> +  IN CONST CHAR16  *Name2,
> +  IN UINTN         Name2Size,
> +  IN EFI_GUID      *Guid1,
> +  IN EFI_GUID      *Guid2
> +  );
> +
> +/**
> +
> +  Compare variable digest.
> +
> +  @param[in]  Variable1     Pointer to first variable digest.
> +  @param[in]  Variable2     Pointer to second variable digest.
> +
> +  @retval 0         Variables are identical.
> +  @return others    Variables are not identical.
> +
> +**/
> +INTN
> +CompareVariableDigestInfo (
> +  IN  VARIABLE_DIGEST  *Variable1,
> +  IN  VARIABLE_DIGEST  *Variable2
> +  );
> +
> +/**
> +
> +  Move a node backward in the order controlled by SortMethod.
> +
> +  @param[in]  Node          Pointer to node to be moved.
> +  @param[in]  SortMethod    Method used to compare node in list.
> +
> +**/
> +VOID
> +MoveNodeBackward (
> +  IN  OUT VARIABLE_DIGEST  *Node,
> +  IN  SORT_METHOD          SortMethod
> +  );
> +
> +/**
> +
> +  Remove variable digest node.
> +
> +  @param[in,out]  Global        Pointer to global configuration data.
> +  @param[in,out]  VarDig        Pointer to variable digest value.
> +  @param[in]      FreeResource  Flag to indicate whether to free resource.
> +
> +**/
> +VOID
> +RemoveVariableDigestNode (
> +  IN  OUT PROTECTED_VARIABLE_GLOBAL  *Global,
> +  IN  OUT VARIABLE_DIGEST            *VarDig,
> +  IN      BOOLEAN                    FreeResource
> +  );
> +
> +/**
> +
> +  Insert variable digest node.
> +
> +  @param[in,out]  Global        Pointer to global configuration data.
> +  @param[in]      VarDig        Pointer to variable digest value.
> +  @param[in]      SortMethod    Method for sorting.
> +
> +**/
> +VOID
> +InsertVariableDigestNode (
> +  IN  OUT PROTECTED_VARIABLE_GLOBAL  *Global,
> +  IN      VARIABLE_DIGEST            *VarDig,
> +  IN      SORT_METHOD                SortMethod
> +  );
> +
> +/**
> +
> +  Create variable digest node.
> +
> +  @param[in]  VariableName      Name of variable.
> +  @param[in]  VendorGuid        Guid of variable.
> +  @param[in]  NameSize          Size of variable name.
> +  @param[in]  DataSize          Size of variable data.
> +  @param[in]  AuthVar           Authenticated variable flag.
> +  @param[in]  Global            Pointer to global configuration data.
> +
> +  @retval Ptr   Pointer to variable digest
> +
> +**/
> +VARIABLE_DIGEST *
> +CreateVariableDigestNode (
> +  IN CHAR16                     *VariableName,
> +  IN EFI_GUID                   *VendorGuid,
> +  IN UINT16                     NameSize,
> +  IN UINT32                     DataSize,
> +  IN BOOLEAN                    AuthVar,
> +  IN PROTECTED_VARIABLE_GLOBAL  *Global
> +  );
> +
> +/**
> +
> +  Find the specified variable digest
> +
> +  @param[in]  Global        Pointer to global configuration data.
> +  @param[in]  VarInfo       Pointer to variable data.
> +  @param[in]  FindNext      Flag to continue looking for variable.
> +
> +**/
> +VARIABLE_DIGEST *
> +FindVariableInternal (
> +  IN PROTECTED_VARIABLE_GLOBAL  *Global,
> +  IN PROTECTED_VARIABLE_INFO    *VarInfo,
> +  IN BOOLEAN                    FindNext
> +  );
> +
> +/**
> +
> +  Synchronize the RPMC counters
> +
> +  @param[in]  Global      Pointer to global configuration data.
> +  @param[in]  VarInfo     Pointer to variable data.
> +  @param[in]  FindNext    Flag to continue looking for variable.
> +
> +  @retval EFI_SUCCESS     Successfully sync RPMC counters.
> +  @return others          Failed to sync RPMC counters.
> +
> +**/
> +EFI_STATUS
> +SyncRpmcCounter (
> +  VOID
> +  );
> +
> +/**
> +
> +  Perform for protected variable integrity check.
> +
> +  If this initialization failed upon any error, the whole variable services
> +  should not be used.  A system reset might be needed to re-construct NV
> +  variable storage to be the default state.
> +
> +  @param[in]  ContextIn   Pointer to variable service context needed by
> +                          protected variable.
> +
> +  @retval EFI_SUCCESS               Protected variable services are ready.
> +  @retval EFI_INVALID_PARAMETER     If ContextIn == NULL or something
> missing or
> +                                    mismatching in the content in ContextIn.
> +  @retval EFI_COMPROMISED_DATA      If failed to check integrity of protected
> variables.
> +  @retval EFI_OUT_OF_RESOURCES      Fail to allocate enough resource.
> +  @retval EFI_UNSUPPORTED           Unsupported to process protected variable.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +PerformVariableIntegrityCheck (
> +  IN  PROTECTED_VARIABLE_CONTEXT_IN  *ContextIn
> +  );
> +
> +extern EFI_TIME                       mDefaultTimeStamp;
> +extern VARIABLE_IDENTIFIER
> mUnprotectedVariables[UnprotectedVarIndexMax];
> +extern PROTECTED_VARIABLE_CONTEXT_IN  mVariableContextIn;
> +extern PROTECTED_VARIABLE_GLOBAL      mProtectedVariableGlobal;
> +
> +#endif
> diff --git
> a/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableCommon.c
> b/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableCommon.c
> new file mode 100644
> index 000000000000..456c871a4561
> --- /dev/null
> +++ b/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableCommon.c
> @@ -0,0 +1,2103 @@
> +/** @file
> +  The common protected variable operation routines.
> +
> +Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
> +SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#include <Base.h>
> +#include <Uefi.h>
> +#include <PiPei.h>
> +
> +#include <Guid/VariableFormat.h>
> +#include <Guid/VarErrorFlag.h>
> +
> +#include <Library/HobLib.h>
> +#include <Library/MemoryAllocationLib.h>
> +#include <Library/HashApiLib.h>
> +#include <Library/SortLib.h>
> +
> +#include "ProtectedVariableInternal.h"
> +
> +EFI_TIME             mDefaultTimeStamp       = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
> +VARIABLE_IDENTIFIER  mUnprotectedVariables[] = {
> +  {
> +    METADATA_HMAC_VARIABLE_NAME,
> +    &METADATA_HMAC_VARIABLE_GUID,
> +    VAR_ADDED & VAR_IN_DELETED_TRANSITION
> +  },
> +  {
> +    METADATA_HMAC_VARIABLE_NAME,
> +    &METADATA_HMAC_VARIABLE_GUID,
> +    VAR_ADDED
> +  },
> +  {
> +    VAR_ERROR_FLAG_NAME,
> +    &gEdkiiVarErrorFlagGuid,
> +    VAR_ADDED
> +  },
> +  {
> +    (CHAR16 *)PcdGetPtr (PcdPlatformVariableName),
> +    (EFI_GUID *)PcdGetPtr (PcdPlatformVariableGuid),
> +    VAR_ADDED
> +  }
> +};
> +
> +/**
> +  Print variable information.
> +
> +  @param[in]   Data8      Pointer to data.
> +  @param[in]   DataSize   Size of data.
> +
> +**/
> +VOID
> +PrintVariableData (
> +  IN UINT8  *Data8,
> +  IN UINTN  DataSize
> +  )
> +{
> +  UINTN  Index;
> +
> +  for (Index = 0; Index < DataSize; Index++) {
> +    if (Index % 0x10 == 0) {
> +      DEBUG ((DEBUG_INFO, "\n%08X:", Index));
> +    }
> +
> +    DEBUG ((DEBUG_INFO, " %02X", *Data8++));
> +  }
> +
> +  DEBUG ((DEBUG_INFO, "\n"));
> +}
> +
> +/**
> +
> +  Retrieve the context and global configuration data structure from HOB.
> +
> +  Once protected NV variable storage is cached and verified in PEI phase,
> +  all related information are stored in a HOB which can be used by PEI variable
> +  service itself and passed to SMM along with the boot flow, which can avoid
> +  many duplicate works, like generating HMAC key, verifying NV variable
> storage,
> +  etc.
> +
> +  The HOB can be identified by gEdkiiProtectedVariableGlobalGuid.
> +
> +  @param[out]   Global      Pointer to global configuration data from PEI phase.
> +
> +  @retval EFI_SUCCESS     The HOB was found, and Context and Global are
> retrieved.
> +  @retval EFI_NOT_FOUND   The HOB was not found.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +GetProtectedVariableGlobalFromHob (
> +  OUT PROTECTED_VARIABLE_GLOBAL  **Global
> +  )
> +{
> +  VOID                           *Data;
> +  EFI_PEI_HOB_POINTERS           Hob;
> +  EFI_HOB_MEMORY_ALLOCATION      *MemoryAllocationHob;
> +  PROTECTED_VARIABLE_CONTEXT_IN  *ContextIn;
> +  EFI_PHYSICAL_ADDRESS           OldStart;
> +  VARIABLE_DIGEST                *VarDig;
> +  EFI_HOB_GUID_TYPE              *GuidHob;
> +  UINTN                          Index;
> +
> +  Hob.Raw = GetFirstGuidHob (&gEdkiiProtectedVariableGlobalGuid);
> +  if (Hob.Raw != NULL) {
> +    Data = GET_GUID_HOB_DATA (Hob);
> +  } else {
> +    //
> +    // Search the global from allocated memory blob.
> +    //
> +    Data                = NULL;
> +    MemoryAllocationHob = NULL;
> +
> +    Hob.Raw = GetFirstHob (EFI_HOB_TYPE_MEMORY_ALLOCATION);
> +    while (Hob.Raw != NULL) {
> +      MemoryAllocationHob = (EFI_HOB_MEMORY_ALLOCATION *)Hob.Raw;
> +      if (CompareGuid (
> +            &MemoryAllocationHob->AllocDescriptor.Name,
> +            &gEdkiiProtectedVariableGlobalGuid
> +            ))
> +      {
> +        Data = (VOID *)(UINTN)
> +               MemoryAllocationHob->AllocDescriptor.MemoryBaseAddress;
> +        break;
> +      }
> +
> +      Hob.Raw = GET_NEXT_HOB (Hob);
> +      Hob.Raw = GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION,
> Hob.Raw);
> +    }
> +  }
> +
> +  if (Data == NULL) {
> +    return EFI_NOT_FOUND;
> +  }
> +
> +  if (Global != NULL) {
> +    GuidHob = GetFirstGuidHob (&gEdkiiProtectedVariableContextGuid);
> +    if (GuidHob != NULL) {
> +      ContextIn = (PROTECTED_VARIABLE_CONTEXT_IN *)GET_GUID_HOB_DATA
> (GuidHob);
> +    } else {
> +      ASSERT (GuidHob == NULL);
> +    }
> +
> +    *Global = (PROTECTED_VARIABLE_GLOBAL *)((UINT8 *)Data);
> +    //
> +    // Fix pointers in the HOB (due to physical memory readiness)
> +    //
> +    if ((*Global)->GlobalSelf != (EFI_PHYSICAL_ADDRESS)(UINTN)(*Global)) {
> +      OldStart             = (*Global)->GlobalSelf;
> +      (*Global)->ContextIn = GET_ADRS (ContextIn);
> +
> +      //
> +      // Mark Memory caching is available
> +      //
> +      (*Global)->Flags.CacheReady = TRUE;
> +
> +      //
> +      // Re-allocate new minimum cache
> +      //
> +      (*Global)->VariableCache = GET_ADRS (Data)
> +                                 + ((*Global)->VariableCache - OldStart);
> +
> +      (*Global)->DigestContext = GET_ADRS (((*Global) + 1));
> +      for (Index = 0; Index < UnprotectedVarIndexMax; Index++) {
> +        if ((*Global)->Unprotected[Index] != VAR_INDEX_INVALID) {
> +          (*Global)->Unprotected[Index] = GET_ADRS (Data)
> +                                          + ((*Global)->Unprotected[Index] - OldStart);
> +        }
> +      }
> +
> +      (*Global)->LastAccessedVariable = GET_ADRS (Data)
> +                                        + ((*Global)->LastAccessedVariable - OldStart);
> +
> +      //
> +      // Fix all linked-list pointers inside VARIABLE_SIGNATURE.
> +      //
> +      (*Global)->VariableDigests = GET_ADRS (Data)
> +                                   + ((*Global)->VariableDigests - OldStart);
> +      VarDig = VAR_DIG_PTR ((*Global)->VariableDigests);
> +      while (VarDig != NULL) {
> +        if (VarDig->Prev != 0) {
> +          VarDig->Prev = GET_ADRS (Data) + (VarDig->Prev - OldStart);
> +        }
> +
> +        if (VarDig->Next != 0) {
> +          VarDig->Next = GET_ADRS (Data) + (VarDig->Next - OldStart);
> +        }
> +
> +        VarDig = VAR_DIG_NEXT (VarDig);
> +      }
> +
> +      (*Global)->GlobalSelf = (EFI_PHYSICAL_ADDRESS)(UINTN)(*Global);
> +    }
> +  }
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +
> +  Derive HMAC key from given variable root key.
> +
> +  @param[in]  RootKey       Pointer to root key to derive from.
> +  @param[in]  RootKeySize   Size of root key.
> +  @param[out] HmacKey       Pointer to generated HMAC key.
> +  @param[in]  HmacKeySize   Size of HMAC key.
> +
> +  @retval TRUE      The HMAC key is derived successfully.
> +  @retval FALSE     Failed to generate HMAC key from given root key.
> +
> +**/
> +BOOLEAN
> +EFIAPI
> +GenerateMetaDataHmacKey (
> +  IN   CONST UINT8  *RootKey,
> +  IN   UINTN        RootKeySize,
> +  OUT  UINT8        *HmacKey,
> +  IN   UINTN        HmacKeySize
> +  )
> +{
> +  UINT8  Salt[AES_BLOCK_SIZE];
> +
> +  return HkdfSha256ExtractAndExpand (
> +           RootKey,
> +           RootKeySize,
> +           Salt,
> +           0,
> +           (UINT8 *)METADATA_HMAC_KEY_NAME,
> +           METADATA_HMAC_KEY_NAME_SIZE,
> +           HmacKey,
> +           HmacKeySize
> +           );
> +}
> +
> +/**
> +
> +  Return the size of variable MetaDataHmacVar.
> +
> +  @param[in] AuthFlag         Auth-variable indicator.
> +
> +  @retval size of variable MetaDataHmacVar.
> +
> +**/
> +UINTN
> +GetMetaDataHmacVarSize (
> +  IN      BOOLEAN  AuthFlag
> +  )
> +{
> +  UINTN  Size;
> +
> +  if (AuthFlag) {
> +    Size = sizeof (AUTHENTICATED_VARIABLE_HEADER);
> +  } else {
> +    Size = sizeof (VARIABLE_HEADER);
> +  }
> +
> +  Size += METADATA_HMAC_VARIABLE_NAME_SIZE;
> +  Size += GET_PAD_SIZE (Size);
> +  Size += METADATA_HMAC_SIZE;
> +  Size += GET_PAD_SIZE (Size);
> +
> +  return Size;
> +}
> +
> +/**
> +
> +  Digests the given variable data and updates HMAC context.
> +
> +  @param[in]      Context        Pointer to initialized HMAC context.
> +  @param[in]      VarInfo        Pointer to variable data.
> +  @param[in]      UpdateMethod   Function to run when updating variable digest.
> +
> +  @retval TRUE    HMAC context was updated successfully.
> +  @retval FALSE   Failed to update HMAC context.
> +
> +**/
> +STATIC
> +BOOLEAN
> +UpdateVariableDigestData (
> +  IN  VOID                     *Context,
> +  IN  PROTECTED_VARIABLE_INFO  *VarInfo,
> +  IN  DIGEST_UPDATE            UpdateMethod
> +  )
> +{
> +  VOID     *Buffer[12];
> +  UINT32   BufferSize[12];
> +  UINTN    Index;
> +  BOOLEAN  Status;
> +
> +  //
> +  // Empty variable is legal here (e.g. variable deletion case or write-init case).
> +  //
> +  if ((VarInfo == NULL) ||
> +      (VarInfo->CipherData == NULL) ||
> +      (VarInfo->CipherDataSize == 0))
> +  {
> +    return TRUE;
> +  }
> +
> +  //
> +  // HMAC (":" || VariableName)
> +  //
> +  Buffer[0]     = METADATA_HMAC_SEP;
> +  BufferSize[0] = METADATA_HMAC_SEP_SIZE;
> +
> +  Buffer[1]     = VarInfo->Header.VariableName;
> +  BufferSize[1] = (UINT32)VarInfo->Header.NameSize;
> +
> +  //
> +  // HMAC (":" || VendorGuid || Attributes || DataSize)
> +  //
> +  Buffer[2]     = METADATA_HMAC_SEP;
> +  BufferSize[2] = METADATA_HMAC_SEP_SIZE;
> +
> +  Buffer[3]     = VarInfo->Header.VendorGuid;
> +  BufferSize[3] = sizeof (EFI_GUID);
> +
> +  Buffer[4]     = &VarInfo->Header.Attributes;
> +  BufferSize[4] = sizeof (VarInfo->Header.Attributes);
> +
> +  Buffer[5]     = &VarInfo->CipherDataSize;
> +  BufferSize[5] = sizeof (VarInfo->CipherDataSize);
> +
> +  //
> +  // HMAC (":" || CipherData)
> +  //
> +  Buffer[6]     = METADATA_HMAC_SEP;
> +  BufferSize[6] = METADATA_HMAC_SEP_SIZE;
> +
> +  Buffer[7]     = VarInfo->CipherData;
> +  BufferSize[7] = VarInfo->CipherDataSize;
> +
> +  //
> +  // HMAC (":" || PubKeyIndex || AuthMonotonicCount || TimeStamp)
> +  //
> +  Buffer[8]     = METADATA_HMAC_SEP;
> +  BufferSize[8] = METADATA_HMAC_SEP_SIZE;
> +
> +  Buffer[9]     = &VarInfo->Header.PubKeyIndex;
> +  BufferSize[9] = sizeof (VarInfo->Header.PubKeyIndex);
> +
> +  Buffer[10]     = &VarInfo->Header.MonotonicCount;
> +  BufferSize[10] = sizeof (VarInfo->Header.MonotonicCount);
> +
> +  Buffer[11] = (VarInfo->Header.TimeStamp != NULL) ?
> +               VarInfo->Header.TimeStamp : &mDefaultTimeStamp;
> +  BufferSize[11] = sizeof (EFI_TIME);
> +
> +  for (Index = 0; Index < ARRAY_SIZE (Buffer); ++Index) {
> +    Status = UpdateMethod (Context, Buffer[Index], BufferSize[Index]);
> +    if (!Status) {
> +      ASSERT (FALSE);
> +      return FALSE;
> +    }
> +  }
> +
> +  return TRUE;
> +}
> +
> +/**
> +
> +  Digests the given variable data and updates HMAC context.
> +
> +  @param[in]      Context   Pointer to initialized HMAC context.
> +  @param[in]      VarInfo   Pointer to variable data.
> +
> +  @retval TRUE    HMAC context was updated successfully.
> +  @retval FALSE   Failed to update HMAC context.
> +
> +**/
> +BOOLEAN
> +UpdateVariableMetadataHmac (
> +  IN  VOID                     *Context,
> +  IN  PROTECTED_VARIABLE_INFO  *VarInfo
> +  )
> +{
> +  return UpdateVariableDigestData (Context, VarInfo,
> (DIGEST_UPDATE)HmacSha256Update);
> +}
> +
> +/**
> +
> +  Get the variable digest.
> +
> +  @param[in]      Global        Pointer to global configuration data.
> +  @param[in]      VarInfo       Pointer to verified copy of protected variables.
> +  @param[in,out]  DigestValue   Pointer to variable digest value.
> +
> +  @retval EFI_INVALID_PARAMETER  Invalid parameter was passed in.
> +  @retval EFI_OUT_OF_RESOURCES   Not enough resource to calculate hash.
> +  @retval EFI_ABORTED            An error was encountered.
> +  @retval EFI_COMPROMISED_DATA   The data was compromised.
> +  @retval EFI_SUCCESS            Variable digest was successfully verified.
> +
> +**/
> +EFI_STATUS
> +GetVariableDigest (
> +  IN      PROTECTED_VARIABLE_GLOBAL  *Global,
> +  IN      PROTECTED_VARIABLE_INFO    *VarInfo,
> +  IN  OUT UINT8                      *DigestValue
> +  )
> +{
> +  EFI_STATUS  Status;
> +  VOID        *Context;
> +
> +  if ((Global == NULL) || (VarInfo == NULL) || (DigestValue == NULL)) {
> +    ASSERT (Global != NULL);
> +    ASSERT (VarInfo != NULL);
> +    ASSERT (DigestValue != NULL);
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  Context = GET_BUFR (Global->DigestContext);
> +  if (!HashApiInit (Context)) {
> +    ASSERT (Context != NULL);
> +    return EFI_OUT_OF_RESOURCES;
> +  }
> +
> +  if (VarInfo->CipherData == NULL) {
> +    VarInfo->CipherData     = VarInfo->Header.Data;
> +    VarInfo->CipherDataSize = (UINT32)VarInfo->Header.DataSize;
> +  }
> +
> +  if (  !UpdateVariableDigestData (Context, VarInfo,
> (DIGEST_UPDATE)HashApiUpdate)
> +     || !HashApiFinal (Context, DigestValue))
> +  {
> +    ASSERT (FALSE);
> +    Status = EFI_ABORTED;
> +  } else {
> +    Status = EFI_SUCCESS;
> +  }
> +
> +  return Status;
> +}
> +
> +/**
> +
> +  Verify the variable digest.
> +
> +  @param[in]  Global      Pointer to global configuration data.
> +  @param[in]  VarInfo     Pointer to verified copy of protected variables.
> +  @param[in]  VarDig      Pointer to variable digest data.
> +
> +  @retval EFI_INVALID_PARAMETER  Invalid parameter was passed in.
> +  @retval EFI_OUT_OF_RESOURCES   Not enough resource to calculate hash.
> +  @retval EFI_ABORTED            An error was encountered.
> +  @retval EFI_COMPROMISED_DATA   The data was compromised.
> +  @retval EFI_SUCCESS            Variable digest was successfully verified.
> +
> +**/
> +EFI_STATUS
> +VerifyVariableDigest (
> +  IN  PROTECTED_VARIABLE_GLOBAL  *Global,
> +  IN  PROTECTED_VARIABLE_INFO    *VarInfo,
> +  IN  VARIABLE_DIGEST            *VarDig
> +  )
> +{
> +  EFI_STATUS  Status;
> +  UINT8       NewDigest[METADATA_HMAC_SIZE];
> +
> +  if (Global->Flags.RecoveryMode || !VarDig->Flags.Protected) {
> +    return EFI_SUCCESS;
> +  }
> +
> +  ASSERT (VarDig->DigestSize == sizeof (NewDigest));
> +
> +  Status = GetVariableDigest (Global, VarInfo, NewDigest);
> +  if (!EFI_ERROR (Status)) {
> +    if (CompareMem (VAR_DIG_VALUE (VarDig), NewDigest, VarDig-
> >DigestSize) != 0) {
> +      Status = EFI_COMPROMISED_DATA;
> +    }
> +  }
> +
> +  return Status;
> +}
> +
> +/**
> +  Initialize variable MetaDataHmacVar.
> +
> +  @param[in,out]  Variable      Pointer to buffer of MetaDataHmacVar.
> +  @param[in]      AuthFlag      Variable format flag.
> +
> +**/
> +VOID
> +InitMetadataHmacVariable (
> +  IN  OUT VARIABLE_HEADER  *Variable,
> +  IN      BOOLEAN          AuthFlag
> +  )
> +{
> +  UINT8                          *NamePtr;
> +  AUTHENTICATED_VARIABLE_HEADER  *AuthVariable;
> +
> +  Variable->StartId    = VARIABLE_DATA;
> +  Variable->State      = VAR_ADDED;
> +  Variable->Reserved   = 0;
> +  Variable->Attributes = VARIABLE_ATTRIBUTE_NV_BS_RT;
> +
> +  if (AuthFlag) {
> +    AuthVariable = (AUTHENTICATED_VARIABLE_HEADER *)Variable;
> +
> +    AuthVariable->NameSize       = METADATA_HMAC_VARIABLE_NAME_SIZE;
> +    AuthVariable->DataSize       = METADATA_HMAC_SIZE;
> +    AuthVariable->PubKeyIndex    = 0;
> +    AuthVariable->MonotonicCount = 0;
> +
> +    ZeroMem (&AuthVariable->TimeStamp, sizeof (EFI_TIME));
> +    CopyMem (&AuthVariable->VendorGuid,
> &METADATA_HMAC_VARIABLE_GUID, sizeof (EFI_GUID));
> +
> +    NamePtr = (UINT8 *)AuthVariable + sizeof
> (AUTHENTICATED_VARIABLE_HEADER);
> +  } else {
> +    Variable->NameSize = METADATA_HMAC_VARIABLE_NAME_SIZE;
> +    Variable->DataSize = METADATA_HMAC_SIZE;
> +
> +    CopyMem (&Variable->VendorGuid, &METADATA_HMAC_VARIABLE_GUID,
> sizeof (EFI_GUID));
> +
> +    NamePtr = (UINT8 *)Variable + sizeof (VARIABLE_HEADER);
> +  }
> +
> +  CopyMem (NamePtr, METADATA_HMAC_VARIABLE_NAME,
> METADATA_HMAC_VARIABLE_NAME_SIZE);
> +}
> +
> +/**
> +  Re-calculate HMAC based on new variable data and re-generate
> MetaDataHmacVar.
> +
> +  @param[in]      Global          Pointer to global configuration data.
> +  @param[in]      NewVarInfo      Pointer to buffer of new variable data.
> +  @param[in,out]  NewHmacVarInfo  Pointer to buffer of new
> MetaDataHmacVar.
> +
> +  @return EFI_SUCCESS           The HMAC value was updated successfully.
> +  @return EFI_ABORTED           Failed to calculate the HMAC value.
> +  @return EFI_OUT_OF_RESOURCES  Not enough resource to calculate HMC
> value.
> +  @return EFI_NOT_FOUND         The MetaDataHmacVar was not found in
> storage.
> +
> +**/
> +EFI_STATUS
> +RefreshVariableMetadataHmac (
> +  IN      PROTECTED_VARIABLE_GLOBAL  *Global,
> +  IN      PROTECTED_VARIABLE_INFO    *NewVarInfo,
> +  IN  OUT PROTECTED_VARIABLE_INFO    *NewHmacVarInfo
> +  )
> +{
> +  EFI_STATUS                     Status;
> +  VOID                           *Context;
> +  UINT32                         Counter;
> +  PROTECTED_VARIABLE_INFO        VarInfo;
> +  PROTECTED_VARIABLE_INFO        CurrHmacVarInfo;
> +  UINT8                          *HmacValue;
> +  PROTECTED_VARIABLE_CONTEXT_IN  *ContextIn;
> +  VARIABLE_DIGEST                *VarDig;
> +  VARIABLE_DIGEST                *HmacVarDig;
> +
> +  ZeroMem ((VOID *)&VarInfo, sizeof (VarInfo));
> +  ZeroMem ((VOID *)&CurrHmacVarInfo, sizeof (CurrHmacVarInfo));
> +
> +  Status = RequestMonotonicCounter (RPMC_COUNTER_2, &Counter);
> +  if (EFI_ERROR (Status)) {
> +    ASSERT_EFI_ERROR (Status);
> +    return Status;
> +  }
> +
> +  Counter  += 1;
> +  ContextIn = GET_CNTX (Global);
> +
> +  //
> +  // Delete current MetaDataHmacVariable first, if any.
> +  //
> +  if (Global->Unprotected[IndexHmacAdded] != VAR_INDEX_INVALID) {
> +    HmacVarDig = VAR_DIG_PTR (Global->Unprotected[IndexHmacAdded]);
> +
> +    CurrHmacVarInfo.Header.NameSize     = HmacVarDig->NameSize;
> +    CurrHmacVarInfo.Header.VariableName = VAR_DIG_NAME (HmacVarDig);
> +    CurrHmacVarInfo.Header.VendorGuid   = VAR_DIG_GUID (HmacVarDig);
> +
> +    CurrHmacVarInfo.Buffer     = VAR_HDR_PTR (HmacVarDig->CacheIndex);
> +    CurrHmacVarInfo.StoreIndex = HmacVarDig->StoreIndex;
> +    CurrHmacVarInfo.Flags.Auth = HmacVarDig->Flags.Auth;
> +    //
> +    // Force marking current MetaDataHmacVariable as
> VAR_IN_DELETED_TRANSITION.
> +    //
> +    CurrHmacVarInfo.Buffer->State &= VAR_IN_DELETED_TRANSITION;
> +    HmacVarDig->State             &= VAR_IN_DELETED_TRANSITION;
> +    Status                         = ContextIn->UpdateVariableStore (
> +                                                  &CurrHmacVarInfo,
> +                                                  OFFSET_OF (VARIABLE_HEADER, State),
> +                                                  sizeof (CurrHmacVarInfo.Buffer->State),
> +                                                  &CurrHmacVarInfo.Buffer->State
> +                                                  );
> +    if (EFI_ERROR (Status)) {
> +      ASSERT_EFI_ERROR (Status);
> +      return Status;
> +    }
> +  } else if (Global->Unprotected[IndexHmacInDel] != VAR_INDEX_INVALID) {
> +    HmacVarDig = VAR_DIG_PTR (Global->Unprotected[IndexHmacInDel]);
> +  } else {
> +    //
> +    // No MetaDataHmacVar. Allocate space to cache its value.
> +    //
> +    HmacVarDig = CreateVariableDigestNode (
> +                   METADATA_HMAC_VARIABLE_NAME,
> +                   &METADATA_HMAC_VARIABLE_GUID,
> +                   METADATA_HMAC_VARIABLE_NAME_SIZE,
> +                   METADATA_HMAC_SIZE,
> +                   Global->Flags.Auth,
> +                   Global
> +                   );
> +    if (HmacVarDig == NULL) {
> +      ASSERT (HmacVarDig != NULL);
> +      return EFI_OUT_OF_RESOURCES;
> +    }
> +
> +    HmacVarDig->Flags.Protected = FALSE;
> +  }
> +
> +  if (HmacVarDig->CacheIndex == VAR_INDEX_INVALID) {
> +    HmacVarDig->CacheIndex = (GET_ADRS (Global)) + (Global->StructSize -
> GetMetaDataHmacVarSize (Global->Flags.Auth));
> +  }
> +
> +  //
> +  // Construct new MetaDataHmacVar.
> +  //
> +  if (NewHmacVarInfo == NULL) {
> +    NewHmacVarInfo         = &VarInfo;
> +    NewHmacVarInfo->Buffer = GET_BUFR (HmacVarDig->CacheIndex);
> +  }
> +
> +  InitMetadataHmacVariable (NewHmacVarInfo->Buffer, Global->Flags.Auth);
> +
> +  NewHmacVarInfo->StoreIndex = VAR_INDEX_INVALID;     // Skip calculating
> offset
> +  NewHmacVarInfo->Flags.Auth = Global->Flags.Auth;
> +  Status                     = ContextIn->GetVariableInfo (NewHmacVarInfo);
> +  ASSERT_EFI_ERROR (Status);
> +  HmacValue = NewHmacVarInfo->Header.Data;
> +
> +  //
> +  // Re-calculate HMAC for all valid variables
> +  //
> +  Context = HmacSha256New ();
> +  if (Context == NULL) {
> +    ASSERT (Context != NULL);
> +    return EFI_OUT_OF_RESOURCES;
> +  }
> +
> +  Status = EFI_ABORTED;
> +  if (!HmacSha256SetKey (
> +         Context,
> +         Global->MetaDataHmacKey,
> +         sizeof (Global->MetaDataHmacKey)
> +         ))
> +  {
> +    ASSERT (FALSE);
> +    goto Done;
> +  }
> +
> +  //
> +  // HMAC (|| hash(Var1) || hash(Var2) || ... || hash(VarN))
> +  //
> +  VarDig = VAR_DIG_PTR (Global->VariableDigests);
> +  while (VarDig != NULL) {
> +    if (VarDig->Flags.Valid && VarDig->Flags.Protected) {
> +      HmacSha256Update (Context, VAR_DIG_VALUE (VarDig), VarDig-
> >DigestSize);
> +    }
> +
> +    VarDig = VAR_DIG_NEXT (VarDig);
> +  }
> +
> +  //
> +  // HMAC (RpmcMonotonicCounter)
> +  //
> +  if (!HmacSha256Update (Context, &Counter, sizeof (Counter))) {
> +    ASSERT (FALSE);
> +    goto Done;
> +  }
> +
> +  if (!HmacSha256Final (Context, HmacValue)) {
> +    ASSERT (FALSE);
> +    goto Done;
> +  }
> +
> +  //
> +  // Update HMAC value in cache.
> +  //
> +  CopyMem (VAR_DIG_VALUE (HmacVarDig), HmacValue, HmacVarDig-
> >DataSize);
> +  if ((HmacVarDig->Prev == 0) && (HmacVarDig->Next == 0)) {
> +    InsertVariableDigestNode (Global, HmacVarDig, NULL);
> +  }
> +
> +  //
> +  // Just one MetaDataHmacVar is needed for normal operation.
> +  //
> +  Global->Unprotected[IndexHmacAdded] = VAR_DIG_ADR (HmacVarDig);
> +  Global->Unprotected[IndexHmacInDel] = VAR_INDEX_INVALID;
> +
> +  Status = EFI_SUCCESS;
> +
> +Done:
> +  if (Context != NULL) {
> +    HmacSha256Free (Context);
> +  }
> +
> +  return Status;
> +}
> +
> +/**
> +
> +  Check if a given variable is unprotected variable specified in advance
> +  and return its index ID.
> +
> +  @param[in] Global     Pointer to global configuration data.
> +  @param[in] VarInfo    Pointer to variable information data.
> +
> +  @retval IndexHmacInDel    Variable is MetaDataHmacVar in delete-transition
> state.
> +  @retval IndexHmacAdded    Variable is MetaDataHmacVar in valid state.
> +  @retval IndexErrorFlag    Variable is VarErrorLog.
> +  @retval Others            Variable is not any known unprotected variables.
> +
> +**/
> +UNPROTECTED_VARIABLE_INDEX
> +CheckKnownUnprotectedVariable (
> +  IN  PROTECTED_VARIABLE_GLOBAL  *Global,
> +  IN  PROTECTED_VARIABLE_INFO    *VarInfo
> +  )
> +{
> +  UNPROTECTED_VARIABLE_INDEX  Index;
> +
> +  if ((VarInfo == NULL) || (  (VarInfo->StoreIndex == VAR_INDEX_INVALID)
> +                           && (  (VarInfo->Header.VariableName == NULL)
> +                              || (VarInfo->Header.VendorGuid == NULL))))
> +  {
> +    ASSERT (VarInfo != NULL);
> +    ASSERT (VarInfo->StoreIndex != VAR_INDEX_INVALID);
> +    ASSERT (VarInfo->Header.VariableName != NULL);
> +    ASSERT (VarInfo->Header.VendorGuid != NULL);
> +    return UnprotectedVarIndexMax;
> +  }
> +
> +  for (Index = 0; Index < UnprotectedVarIndexMax; ++Index) {
> +    if (  (Global->Unprotected[Index] != VAR_INDEX_INVALID)
> +       && (VarInfo->StoreIndex != VAR_INDEX_INVALID))
> +    {
> +      if (VarInfo->StoreIndex == VAR_DIG_PTR (Global->Unprotected[Index])-
> >StoreIndex) {
> +        break;
> +      }
> +    } else if (IS_VARIABLE (
> +                 &VarInfo->Header,
> +                 mUnprotectedVariables[Index].VariableName,
> +                 mUnprotectedVariables[Index].VendorGuid
> +                 ) && (VarInfo->Header.State == mUnprotectedVariables[Index].State))
> +    {
> +      break;
> +    }
> +  }
> +
> +  return Index;
> +}
> +
> +/**
> +
> +  Compare variable name and Guid
> +
> +  @param[in]  Name1      Name of first variable.
> +  @param[in]  Name1Size  Size of first variable.
> +  @param[in]  Name2      Name of second variable.
> +  @param[in]  Name2Size  Size of second variable.
> +  @param[in]  Guid1      Guid for first variable.
> +  @param[in]  Guid2      Guid for second variable.
> +
> +  @retval 0         First name is identical to Second name.
> +  @return others    First name is not identical to Second name.
> +
> +**/
> +INTN
> +CompareVariableNameAndGuid (
> +  IN CONST CHAR16  *Name1,
> +  IN UINTN         Name1Size,
> +  IN CONST CHAR16  *Name2,
> +  IN UINTN         Name2Size,
> +  IN EFI_GUID      *Guid1,
> +  IN EFI_GUID      *Guid2
> +  )
> +{
> +  INTN  Result;
> +
> +  Result = StrnCmp (
> +             Name1,
> +             Name2,
> +             MIN (Name1Size, Name2Size) / sizeof (CHAR16)
> +             );
> +  if (Result == 0) {
> +    if (Name1Size != Name2Size) {
> +      //
> +      // Longer name is 'bigger' than shorter one.
> +      //
> +      Result = (INTN)Name1Size - (INTN)Name2Size;
> +    } else {
> +      //
> +      // The variable name is the same. Compare the GUID.
> +      //
> +      Result = CompareMem ((VOID *)Guid1, (VOID *)Guid2, sizeof (EFI_GUID));
> +    }
> +  }
> +
> +  return Result;
> +}
> +
> +/**
> +
> +  Compare variable digest.
> +
> +  @param[in]  Variable1     Pointer to first variable digest.
> +  @param[in]  Variable2     Pointer to second variable digest.
> +
> +  @retval 0         Variables are identical.
> +  @return others    Variables are not identical.
> +
> +**/
> +INTN
> +CompareVariableDigestInfo (
> +  IN  VARIABLE_DIGEST  *Variable1,
> +  IN  VARIABLE_DIGEST  *Variable2
> +  )
> +{
> +  return CompareVariableNameAndGuid (
> +           VAR_DIG_NAME (Variable1),
> +           Variable1->NameSize,
> +           VAR_DIG_NAME (Variable2),
> +           Variable2->NameSize,
> +           &Variable1->VendorGuid,
> +           &Variable2->VendorGuid
> +           );
> +}
> +
> +/**
> +
> +  Move a node backward in the order controlled by SortMethod.
> +
> +  @param[in,out]  Node          Pointer to node to be moved.
> +  @param[in]      SortMethod    Method used to compare node in list.
> +
> +**/
> +VOID
> +MoveNodeBackward (
> +  IN  OUT VARIABLE_DIGEST  *Node,
> +  IN      SORT_METHOD      SortMethod
> +  )
> +{
> +  VARIABLE_DIGEST  *Curr;
> +  VARIABLE_DIGEST  *Prev;
> +  INTN             Result;
> +
> +  Curr = Node;
> +  while (Curr != NULL) {
> +    Prev = VAR_DIG_PREV (Curr);
> +    if (Prev == NULL) {
> +      Result = -1;
> +    } else {
> +      Result = SortMethod (Prev, Node);
> +    }
> +
> +    //
> +    // 'Result > 0' means the 'Prev' is 'bigger' than 'Node'. Continue to check
> +    // previous node til a node 'smaller' than 'Node' found.
> +    //
> +    if (Result > 0) {
> +      Curr = Prev;
> +      continue;
> +    }
> +
> +    if (Curr != Node) {
> +      //
> +      // Remove Node first
> +      //
> +      if (VAR_DIG_PREV (Node) != NULL) {
> +        VAR_DIG_PREV (Node)->Next = Node->Next;
> +      }
> +
> +      if (VAR_DIG_NEXT (Node) != NULL) {
> +        VAR_DIG_NEXT (Node)->Prev = Node->Prev;
> +      }
> +
> +      //
> +      // Insert Node before Curr.
> +      //
> +      Node->Prev = Curr->Prev;
> +      Node->Next = VAR_DIG_ADR (Curr);
> +
> +      if (Curr->Prev != 0) {
> +        VAR_DIG_PREV (Curr)->Next = VAR_DIG_ADR (Node);
> +      }
> +
> +      Curr->Prev = VAR_DIG_ADR (Node);
> +    }
> +
> +    //
> +    // If there're two identical variables in storage, one of them must be
> +    // "in-delete-transition" state. Mark it as "deleted" anyway.
> +    //
> +    if (Result == 0) {
> +      if (Curr->State == (VAR_ADDED & VAR_IN_DELETED_TRANSITION)) {
> +        Curr->State &= VAR_DELETED;
> +      }
> +
> +      if (Prev->State == (VAR_ADDED & VAR_IN_DELETED_TRANSITION)) {
> +        Prev->State &= VAR_DELETED;
> +      }
> +    }
> +
> +    break;
> +  }
> +}
> +
> +/**
> +
> +  Create variable digest node.
> +
> +  @param[in]  VariableName      Name of variable.
> +  @param[in]  VendorGuid        Guid of variable.
> +  @param[in]  NameSize          Size of variable name.
> +  @param[in]  DataSize          Size of variable data.
> +  @param[in]  AuthVar           Authenticated variable flag.
> +  @param[in]  Global            Pointer to global configuration data.
> +
> +  @retval Ptr   Pointer to variable digest
> +
> +**/
> +VARIABLE_DIGEST *
> +CreateVariableDigestNode (
> +  IN CHAR16                     *VariableName,
> +  IN EFI_GUID                   *VendorGuid,
> +  IN UINT16                     NameSize,
> +  IN UINT32                     DataSize,
> +  IN BOOLEAN                    AuthVar,
> +  IN PROTECTED_VARIABLE_GLOBAL  *Global
> +  )
> +{
> +  VARIABLE_DIGEST  *VarDig;
> +  VOID             *Buffer;
> +  UINTN            VarSize;
> +
> +  VarDig = (VARIABLE_DIGEST *)AllocateZeroPool (
> +                                sizeof (VARIABLE_DIGEST) + NameSize +
> METADATA_HMAC_SIZE
> +                                );
> +  if ((VarDig == NULL) || (Global == NULL)) {
> +    ASSERT (VarDig != NULL);
> +    ASSERT (Global != NULL);
> +    return NULL;
> +  }
> +
> +  VarDig->DataSize        = DataSize;
> +  VarDig->NameSize        = NameSize;
> +  VarDig->DigestSize      = METADATA_HMAC_SIZE;
> +  VarDig->State           = VAR_ADDED;
> +  VarDig->Attributes      = VARIABLE_ATTRIBUTE_NV_BS_RT;
> +  VarDig->Flags.Auth      = AuthVar;
> +  VarDig->Flags.Valid     = TRUE;
> +  VarDig->Flags.Freeable  = TRUE;
> +  VarDig->Flags.Protected = PcdGetBool (PcdProtectedVariableIntegrity);
> +  VarDig->Flags.Encrypted = PcdGetBool (PcdProtectedVariableConfidentiality);
> +  VarDig->StoreIndex      = VAR_INDEX_INVALID;
> +  VarDig->CacheIndex      = VAR_INDEX_INVALID;
> +
> +  if (Global->Flags.CacheReady == TRUE) {
> +    VarSize  =  VARIABLE_HEADER_SIZE (VarDig->Flags.Auth);
> +    VarSize += VarDig->NameSize + GET_PAD_SIZE (VarDig->NameSize);
> +    VarSize += VarDig->DataSize + GET_PAD_SIZE (VarDig->DataSize);
> +    VarSize  = HEADER_ALIGN (VarSize);
> +
> +    Buffer = AllocateZeroPool (VarSize);
> +    if (Buffer != NULL) {
> +      VarDig->CacheIndex = (EFI_PHYSICAL_ADDRESS)(UINTN)Buffer;
> +    }
> +  }
> +
> +  CopyMem (VAR_DIG_NAME (VarDig), VariableName, NameSize);
> +  CopyMem (VAR_DIG_GUID (VarDig), VendorGuid, sizeof (EFI_GUID));
> +
> +  return VarDig;
> +}
> +
> +/**
> +
> +  Remove variable digest node.
> +
> +  @param[in,out]  Global        Pointer to global configuration data.
> +  @param[in,out]  VarDig        Pointer to variable digest value.
> +  @param[in]      FreeResource  Flag to indicate whether to free resource.
> +
> +**/
> +VOID
> +RemoveVariableDigestNode (
> +  IN  OUT PROTECTED_VARIABLE_GLOBAL  *Global,
> +  IN  OUT VARIABLE_DIGEST            *VarDig,
> +  IN      BOOLEAN                    FreeResource
> +  )
> +{
> +  VARIABLE_DIGEST  *Prev;
> +  VARIABLE_DIGEST  *Next;
> +
> +  Prev = VAR_DIG_PREV (VarDig);
> +  Next = VAR_DIG_NEXT (VarDig);
> +
> +  if (Global->VariableDigests == VAR_DIG_ADR (VarDig)) {
> +    Global->VariableDigests = VAR_DIG_ADR (Next);
> +  }
> +
> +  if (Prev != NULL) {
> +    Prev->Next = VAR_DIG_ADR (Next);
> +  }
> +
> +  if (Next != NULL) {
> +    Next->Prev = VAR_DIG_ADR (Prev);
> +  }
> +
> +  VarDig->Prev        = 0;
> +  VarDig->Next        = 0;
> +  VarDig->Flags.Valid = FALSE;
> +
> +  if (FreeResource && VarDig->Flags.Freeable) {
> +    if ((VarDig->CacheIndex != 0) && (VarDig->CacheIndex !=
> VAR_INDEX_INVALID)) {
> +      VarDig->CacheIndex = VAR_INDEX_INVALID;
> +    }
> +  }
> +}
> +
> +/**
> +
> +  Insert variable digest node.
> +
> +  @param[in,out]  Global        Pointer to global configuration data.
> +  @param[in]      VarDig        Pointer to variable digest value.
> +  @param[in]      SortMethod    Method for sorting.
> +
> +**/
> +VOID
> +InsertVariableDigestNode (
> +  IN  OUT PROTECTED_VARIABLE_GLOBAL  *Global,
> +  IN      VARIABLE_DIGEST            *VarDig,
> +  IN      SORT_METHOD                SortMethod
> +  )
> +{
> +  VARIABLE_DIGEST  *Curr;
> +  VARIABLE_DIGEST  *Prev;
> +  BOOLEAN          DoReplace;
> +  INTN             Result;
> +
> +  if (SortMethod == NULL) {
> +    SortMethod = CompareVariableDigestInfo;
> +  }
> +
> +  DoReplace = FALSE;
> +  Curr      = VAR_DIG_PTR (Global->VariableDigests);
> +  if (Curr == NULL) {
> +    //
> +    // First one.
> +    //
> +    VarDig->Prev            = 0;
> +    VarDig->Next            = 0;
> +    Global->VariableDigests = VAR_DIG_ADR (VarDig);
> +    return;
> +  }
> +
> +  while (Curr != NULL && Curr != VarDig) {
> +    Result = SortMethod (VarDig, Curr);
> +
> +    if (Result <= 0) {
> +      ASSERT (VarDig->StoreIndex != Curr->StoreIndex);
> +
> +      //
> +      // The same variable already in list?
> +      //
> +      if (Result == 0) {
> +        //
> +        // Keep only the same new one, unless states are different. In such
> +        // situation, the one with no VAR_ADDED will be deleted.
> +        //
> +        if (VarDig->State >= Curr->State) {
> +          DoReplace         = TRUE;
> +          Curr->Flags.Valid = FALSE;    // to-be-deleted
> +        } else {
> +          DoReplace           = FALSE;
> +          VarDig->Flags.Valid = FALSE;  // to-be-deleted
> +        }
> +      }
> +
> +      //
> +      // Put VarDig before Curr
> +      //
> +      VarDig->Next = VAR_DIG_ADR (Curr);
> +      VarDig->Prev = Curr->Prev;
> +
> +      if (VAR_DIG_PREV (Curr) != NULL) {
> +        VAR_DIG_PREV (Curr)->Next = VAR_DIG_ADR (VarDig);
> +      }
> +
> +      Curr->Prev = VAR_DIG_ADR (VarDig);
> +
> +      if (DoReplace) {
> +        RemoveVariableDigestNode (Global, Curr, TRUE);
> +      }
> +
> +      break;
> +    }
> +
> +    Prev = Curr;
> +    Curr = VAR_DIG_NEXT (Curr);
> +    if (Curr == NULL) {
> +      Prev->Next = VAR_DIG_ADR (VarDig);
> +
> +      VarDig->Prev = VAR_DIG_ADR (Prev);
> +      VarDig->Next = 0;
> +    }
> +  }
> +
> +  //
> +  // Update the head node if necessary.
> +  //
> +  if (VAR_DIG_PTR (VarDig->Prev) == NULL) {
> +    Global->VariableDigests = VAR_DIG_ADR (VarDig);
> +  }
> +}
> +
> +/**
> +
> +  Find the specified variable digest
> +
> +  @param[in]  Global        Pointer to global configuration data.
> +  @param[in]  VarInfo       Pointer to variable data.
> +  @param[in]  FindNext      Flag to continue looking for variable.
> +
> +**/
> +VARIABLE_DIGEST *
> +FindVariableInternal (
> +  IN PROTECTED_VARIABLE_GLOBAL  *Global,
> +  IN PROTECTED_VARIABLE_INFO    *VarInfo,
> +  IN BOOLEAN                    FindNext
> +  )
> +{
> +  VARIABLE_DIGEST  *VarDig;
> +  VARIABLE_DIGEST  *Found;
> +  VARIABLE_DIGEST  *FirstStoreIndexVar;
> +  BOOLEAN          ByIndex;
> +  INTN             FwdOrBwd;
> +
> +  //
> +  // If VarInfo->StoreIndex is valid, use it to find the variable. Otherwise,
> +  // use the variable name and guid instead, if given. If no clue at all, return
> +  // the variable with lowest StoreIndex.
> +  //
> +  if (  (VarInfo->StoreIndex != VAR_INDEX_INVALID)
> +     || (VarInfo->Header.VariableName == NULL)
> +     || (VarInfo->Header.VendorGuid == NULL))
> +  {
> +    ByIndex = TRUE;
> +  } else {
> +    ByIndex = FALSE;
> +  }
> +
> +  Found              = NULL;
> +  VarDig             = VAR_DIG_PTR (Global->VariableDigests);
> +  FirstStoreIndexVar = VarDig;
> +  FwdOrBwd           = 1;
> +
> +  //
> +  // Discover variable with first/smallest store index
> +  //
> +  while (VarDig != NULL) {
> +    if (VarDig->StoreIndex < FirstStoreIndexVar->StoreIndex) {
> +      FirstStoreIndexVar = VAR_DIG_PTR (VarDig);
> +    }
> +
> +    VarDig = VAR_DIG_NEXT (VarDig);
> +  }
> +
> +  //
> +  // Input variable is NULL than return first variable
> +  // with smallest store index from the variable digest list.
> +  //
> +  if (((VarInfo->Header.VariableName == NULL) ||
> +       (VarInfo->Header.VendorGuid == NULL)) &&
> +      (ByIndex == FALSE))
> +  {
> +    return FirstStoreIndexVar;
> +  }
> +
> +  //
> +  // Start with first entry
> +  //
> +  VarDig = VAR_DIG_PTR (Global->VariableDigests);
> +  while (VarDig != NULL) {
> +    if (ByIndex) {
> +      if (FindNext) {
> +        if (VarDig->StoreIndex == VarInfo->StoreIndex) {
> +          Found = VarDig = VAR_DIG_NEXT (VarDig);
> +          break;
> +        }
> +      } else if (VarDig->StoreIndex == VarInfo->StoreIndex) {
> +        Found = VarDig;
> +        break;
> +      }
> +    } else {
> +      //
> +      // Match given variable name and vendor guid.
> +      //
> +      if (IS_VARIABLE (&VarInfo->Header, VAR_DIG_NAME (VarDig),
> VAR_DIG_GUID (VarDig))) {
> +        Found = (FindNext) ? VAR_DIG_NEXT (VarDig) : VarDig;
> +        break;
> +      }
> +    }
> +
> +    VarDig = (FwdOrBwd > 0) ? VAR_DIG_NEXT (VarDig) : VAR_DIG_PREV
> (VarDig);
> +    if (VarDig == NULL) {
> +    }
> +  }
> +
> +  return Found;
> +}
> +
> +/**
> +
> +  Synchronize the RPMC counters
> +
> +  @param[in]  Global      Pointer to global configuration data.
> +  @param[in]  VarInfo     Pointer to variable data.
> +  @param[in]  FindNext    Flag to continue looking for variable.
> +
> +  @retval EFI_SUCCESS     Successfully sync RPMC counters.
> +  @return others          Failed to sync RPMC counters.
> +
> +**/
> +EFI_STATUS
> +SyncRpmcCounter (
> +  VOID
> +  )
> +{
> +  UINT32      Counter1;
> +  UINT32      Counter2;
> +  EFI_STATUS  Status;
> +
> +  //
> +  // Sync RPMC1 & RPMC2.
> +  //
> +  Status = RequestMonotonicCounter (RPMC_COUNTER_1, &Counter1);
> +  if (EFI_ERROR (Status)) {
> +    ASSERT_EFI_ERROR (Status);
> +    return Status;
> +  }
> +
> +  Status = RequestMonotonicCounter (RPMC_COUNTER_2, &Counter2);
> +  if (EFI_ERROR (Status)) {
> +    ASSERT_EFI_ERROR (Status);
> +    return Status;
> +  }
> +
> +  while (Counter1 < Counter2) {
> +    Status = IncrementMonotonicCounter (RPMC_COUNTER_1);
> +    if (EFI_ERROR (Status)) {
> +      ASSERT_EFI_ERROR (Status);
> +      return Status;
> +    }
> +
> +    ++Counter1;
> +  }
> +
> +  while (Counter2 < Counter1) {
> +    Status = IncrementMonotonicCounter (RPMC_COUNTER_2);
> +    if (EFI_ERROR (Status)) {
> +      ASSERT_EFI_ERROR (Status);
> +      return Status;
> +    }
> +
> +    ++Counter2;
> +  }
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +
> +  An alternative version of ProtectedVariableLibGetData to get plain data from
> +  given variable, if encrypted.
> +
> +  @param[in]          Global        Pointer to global configuration data.
> +  @param[in,out]      VarInfo       Pointer to structure containing variable
> +                                    information. VarInfo->Header.Data must point
> +                                    to the original variable data.
> +
> +  @retval EFI_SUCCESS               Found the specified variable.
> +  @retval EFI_INVALID_PARAMETER     VarInfo is NULL or both VarInfo->Buffer
> and
> +                                    VarInfo->Offset are invalid.
> +  @retval EFI_NOT_FOUND             The specified variable could not be found.
> +
> +**/
> +STATIC
> +EFI_STATUS
> +ProtectedVariableLibGetDataInternal (
> +  IN      PROTECTED_VARIABLE_GLOBAL  *Global,
> +  IN  OUT PROTECTED_VARIABLE_INFO    *VarInfo
> +  )
> +{
> +  EFI_STATUS                     Status;
> +  PROTECTED_VARIABLE_CONTEXT_IN  *ContextIn;
> +  VOID                           *Buffer;
> +  UINTN                          BufferSize;
> +
> +  if ((Global == NULL) || (VarInfo == NULL)) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  ContextIn = GET_CNTX (Global);
> +
> +  //
> +  // Check if the data has been decrypted or not.
> +  //
> +  BufferSize              = VarInfo->PlainDataSize;
> +  VarInfo->CipherData     = NULL;
> +  VarInfo->CipherDataSize = 0;
> +  VarInfo->PlainData      = NULL;
> +  VarInfo->PlainDataSize  = 0;
> +  Status                  = GetCipherDataInfo (VarInfo);
> +
> +  if ((Status == EFI_UNSUPPORTED) || (Status == EFI_NOT_FOUND)) {
> +    VarInfo->Flags.DecryptInPlace = TRUE;
> +    VarInfo->PlainDataSize        = (UINT32)VarInfo->Header.DataSize;
> +    VarInfo->PlainData            = VarInfo->Header.Data;
> +    VarInfo->CipherDataType       = 0;
> +    VarInfo->CipherHeaderSize     = 0;
> +    Status                        = EFI_SUCCESS;
> +  } else if (EFI_ERROR (Status)) {
> +    ASSERT_EFI_ERROR (Status);
> +    return Status;
> +  }
> +
> +  //
> +  // Don't do decryption if the caller provided buffer is too small
> +  // Simply return the real Plain Data Size via VarInfo->PlainDataSize
> +  //
> +  if (BufferSize < VarInfo->PlainDataSize) {
> +    return EFI_BUFFER_TOO_SMALL;
> +  }
> +
> +  //
> +  // If the variable data is cipher data, decrypt it inplace if possible.
> +  //
> +  if ((VarInfo->PlainData == NULL) && (VarInfo->CipherData != NULL)) {
> +    VarInfo->Key     = Global->RootKey;
> +    VarInfo->KeySize = sizeof (Global->RootKey);
> +
> +    switch (ContextIn->VariableServiceUser) {
> +      case FromPeiModule:
> +        VarInfo->Flags.DecryptInPlace = FALSE;
> +        //
> +        // In PEI VariableCache holds Cipher header + Cipher data
> +        // Do not override Cipher header data during decrypt operation
> +        //
> +        VarInfo->PlainData = GET_BUFR (Global->VariableCache + VarInfo-
> >CipherHeaderSize);
> +
> +        Status = DecryptVariable (VarInfo);
> +        if (Status == EFI_UNSUPPORTED) {
> +          VarInfo->PlainData        = VarInfo->Header.Data;
> +          VarInfo->PlainDataSize    = (UINT32)VarInfo->Header.DataSize;
> +          VarInfo->CipherDataType   = 0;
> +          VarInfo->CipherHeaderSize = 0;
> +
> +          Status = EFI_SUCCESS;
> +        }
> +
> +        break;
> +
> +      case FromSmmModule:
> +        VarInfo->Flags.DecryptInPlace = FALSE;
> +        VarInfo->PlainData            = GET_BUFR (Global->VariableCache);
> +
> +        Status = DecryptVariable (VarInfo);
> +        if (Status == EFI_UNSUPPORTED) {
> +          VarInfo->PlainData        = VarInfo->Header.Data;
> +          VarInfo->PlainDataSize    = (UINT32)VarInfo->Header.DataSize;
> +          VarInfo->CipherDataType   = 0;
> +          VarInfo->CipherHeaderSize = 0;
> +
> +          Status = EFI_SUCCESS;
> +        }
> +
> +        break;
> +
> +      case FromBootServiceModule:
> +      case FromRuntimeModule:
> +        //
> +        // The SMM passes back only decrypted data. We re-use the original cipher
> +        // data buffer to keep the plain data along with the cipher header.
> +        //
> +        VarInfo->Flags.DecryptInPlace = TRUE;
> +        Buffer                        = (VOID *)((UINTN)VarInfo->CipherData + VarInfo-
> >CipherHeaderSize);
> +        BufferSize                    = VarInfo->PlainDataSize;
> +        Status                        = ContextIn->FindVariableSmm (
> +                                                     VarInfo->Header.VariableName,
> +                                                     VarInfo->Header.VendorGuid,
> +                                                     &VarInfo->Header.Attributes,
> +                                                     &BufferSize,
> +                                                     Buffer
> +                                                     );
> +        if (!EFI_ERROR (Status)) {
> +          //
> +          // Flag the payload as plain data to avoid re-decrypting.
> +          //
> +          VarInfo->CipherDataType = ENC_TYPE_NULL;
> +          VarInfo->PlainDataSize  = (UINT32)BufferSize;
> +          VarInfo->PlainData      = Buffer;
> +
> +          Status = SetCipherDataInfo (VarInfo);
> +          if (Status == EFI_UNSUPPORTED) {
> +            Status = EFI_SUCCESS;
> +          }
> +        }
> +
> +        break;
> +
> +      default:
> +        Status = EFI_UNSUPPORTED;
> +        break;
> +    }
> +
> +    VarInfo->CipherData     = NULL;
> +    VarInfo->CipherDataSize = 0;
> +  }
> +
> +  return Status;
> +}
> +
> +/**
> +
> +  An alternative version of ProtectedVariableLibGetData to get plain data, if
> +  encrypted, from given variable, for different use cases.
> +
> +  @param[in,out]      VarInfo     Pointer to structure containing variable
> information.
> +
> +  @retval EFI_SUCCESS               Found the specified variable.
> +  @retval EFI_INVALID_PARAMETER     VarInfo is NULL or both VarInfo->Buffer
> and
> +                                    VarInfo->Offset are invalid.
> +  @retval EFI_NOT_FOUND             The specified variable could not be found.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +ProtectedVariableLibGetByInfo (
> +  IN  OUT PROTECTED_VARIABLE_INFO  *VarInfo
> +  )
> +{
> +  EFI_STATUS                 Status;
> +  PROTECTED_VARIABLE_GLOBAL  *Global;
> +  VOID                       **Buffer;
> +  UINT32                     BufferSize;
> +
> +  Status = GetProtectedVariableGlobal (&Global);
> +  if (EFI_ERROR (Status)) {
> +    ASSERT_EFI_ERROR (Status);
> +    return Status;
> +  }
> +
> +  //
> +  // Save the output data buffer because below call
> +  // call will use this struct field internally.

[JianJW] "call" here is duplicated

> +  //
> +  Buffer     = VarInfo->PlainData;
> +  BufferSize = VarInfo->PlainDataSize;
> +
> +  Status = ProtectedVariableLibGetDataInternal (Global, VarInfo);
> +  if (EFI_ERROR (Status) || ((BufferSize) < VarInfo->PlainDataSize)) {
> +    //
> +    // Return with caller provided buffer with zero DataSize
> +    //
> +    VarInfo->PlainData = Buffer;
> +    return Status;
> +  }
> +
> +  //
> +  // Copy Plain data to ouput data buffer

[JianJW] typo: "ouput" -> "output", "Plain" -> "plain"

> +  //
> +  CopyMem (Buffer, VarInfo->PlainData, VarInfo->PlainDataSize);
> +  VarInfo->PlainData = Buffer;
> +
> +  return Status;
> +}
> +
> +/**
> +
> +  Retrieve plain data, if encrypted, of given variable.
> +
> +  If variable encryption is employed, this function will initiate a SMM request
> +  to get the plain data. Due to security consideration, the decryption can only
> +  be done in SMM environment.
> +
> +  @param[in]      Variable           Pointer to header of a Variable.
> +  @param[in,out]  Data               Pointer to plain data of the given variable.
> +  @param[in,out]  DataSize           Size of data returned or data buffer needed.
> +  @param[in]      AuthFlag           Auth-variable indicator.
> +
> +  @retval EFI_SUCCESS                Found the specified variable.
> +  @retval EFI_INVALID_PARAMETER      Invalid parameter.
> +  @retval EFI_NOT_FOUND              The specified variable could not be found.
> +  @retval EFI_BUFFER_TOO_SMALL       If *DataSize is smaller than needed.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +ProtectedVariableLibGetByBuffer (
> +  IN      VARIABLE_HEADER  *Variable,
> +  IN  OUT VOID             *Data,
> +  IN  OUT UINT32           *DataSize,
> +  IN      BOOLEAN          AuthFlag
> +  )
> +{
> +  EFI_STATUS                     Status;
> +  PROTECTED_VARIABLE_INFO        VarInfo;
> +  PROTECTED_VARIABLE_GLOBAL      *Global;
> +  AUTHENTICATED_VARIABLE_HEADER  *AuthVariable;
> +  VOID                           *Buffer;
> +
> +  if ((Variable == NULL) || (DataSize == NULL)) {
> +    ASSERT (Variable != NULL);
> +    ASSERT (DataSize != NULL);
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  Status = GetProtectedVariableGlobal (&Global);
> +  if (EFI_ERROR (Status)) {
> +    ASSERT_EFI_ERROR (Status);
> +    return Status;
> +  }
> +
> +  ZeroMem (&VarInfo, sizeof (VarInfo));
> +
> +  VarInfo.Buffer        = Variable;
> +  VarInfo.Flags.Auth    = AuthFlag;
> +  VarInfo.PlainDataSize = *DataSize;
> +
> +  if (VarInfo.Flags.Auth == TRUE) {
> +    AuthVariable = (AUTHENTICATED_VARIABLE_HEADER *)Variable;
> +
> +    VarInfo.Header.VariableName = (CHAR16 *)((UINTN)Variable + sizeof
> (AUTHENTICATED_VARIABLE_HEADER));
> +    VarInfo.Header.NameSize     = AuthVariable->NameSize;
> +    VarInfo.Header.VendorGuid   = &AuthVariable->VendorGuid;
> +    VarInfo.Header.Attributes   = AuthVariable->Attributes;
> +    VarInfo.Header.DataSize     = AuthVariable->DataSize;
> +  } else {
> +    VarInfo.Header.VariableName = (CHAR16 *)((UINTN)Variable + sizeof
> (VARIABLE_HEADER));
> +    VarInfo.Header.NameSize     = Variable->NameSize;
> +    VarInfo.Header.VendorGuid   = &Variable->VendorGuid;
> +    VarInfo.Header.Attributes   = Variable->Attributes;
> +    VarInfo.Header.DataSize     = Variable->DataSize;
> +  }
> +
> +  Buffer              = VARIABLE_NAME (VarInfo.Buffer, VarInfo.Flags.Auth);
> +  Buffer              = GET_BUFR (GET_ADRS (Buffer) + VarInfo.Header.NameSize);
> +  Buffer              = GET_BUFR (GET_ADRS (Buffer) + GET_PAD_SIZE
> (VarInfo.Header.NameSize));
> +  VarInfo.Header.Data = Buffer;
> +
> +  Status    = ProtectedVariableLibGetDataInternal (Global, &VarInfo);
> +  *DataSize = VarInfo.PlainDataSize;
> +  if (EFI_ERROR (Status)) {
> +    return Status;
> +  }
> +
> +  CopyMem (Data, VarInfo.PlainData, VarInfo.PlainDataSize);
> +
> +  return Status;
> +}
> +
> +/**
> +  This service retrieves a variable's value using its name and GUID.
> +
> +  Read the specified variable from the UEFI variable store. If the Data
> +  buffer is too small to hold the contents of the variable, the error
> +  EFI_BUFFER_TOO_SMALL is returned and DataSize is set to the required buffer
> +  size to obtain the data.
> +
> +  @param  VariableName          A pointer to a null-terminated string that is the
> variable's name.
> +  @param  VariableGuid          A pointer to an EFI_GUID that is the variable's
> GUID. The combination of
> +                                VariableGuid and VariableName must be unique.
> +  @param  Attributes            If non-NULL, on return, points to the variable's
> attributes.
> +  @param  DataSize              On entry, points to the size in bytes of the Data
> buffer.
> +                                On return, points to the size of the data returned in Data.
> +  @param  Data                  Points to the buffer which will hold the returned
> variable value.
> +                                May be NULL with a zero DataSize in order to determine the
> size of the buffer needed.

[JianJW] missing [in/out] for parameters

> +
> +  @retval EFI_SUCCESS           The variable was read successfully.
> +  @retval EFI_NOT_FOUND         The variable was be found.
> +  @retval EFI_BUFFER_TOO_SMALL  The DataSize is too small for the resulting
> data.
> +                                DataSize is updated with the size required for
> +                                the specified variable.
> +  @retval EFI_INVALID_PARAMETER VariableName, VariableGuid, DataSize or
> Data is NULL.
> +  @retval EFI_DEVICE_ERROR      The variable could not be retrieved because of
> a device error.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +ProtectedVariableLibGetByName (
> +  IN      CONST  CHAR16    *VariableName,
> +  IN      CONST  EFI_GUID  *VariableGuid,
> +  OUT UINT32               *Attributes,
> +  IN  OUT UINTN            *DataSize,
> +  OUT VOID                 *Data OPTIONAL
> +  )
> +{
> +  EFI_STATUS                     Status;
> +  PROTECTED_VARIABLE_CONTEXT_IN  *ContextIn;
> +  PROTECTED_VARIABLE_GLOBAL      *Global;
> +  VARIABLE_DIGEST                *VarDig;
> +  PROTECTED_VARIABLE_INFO        VarInfo;
> +  EFI_TIME                       TimeStamp;
> +  VOID                           *DataBuffer;
> +
> +  if ((VariableName == NULL) || (VariableGuid == NULL) || (DataSize == NULL)) {
> +    ASSERT (VariableName != NULL);
> +    ASSERT (VariableGuid != NULL);
> +    ASSERT (DataSize != NULL);
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  Status = GetProtectedVariableGlobal (&Global);
> +
> +  if (EFI_ERROR (Status)) {
> +    return EFI_UNSUPPORTED;
> +  }
> +
> +  ContextIn = GET_CNTX (Global);
> +
> +  ZeroMem (&VarInfo, sizeof (VarInfo));
> +  VarInfo.StoreIndex          = VAR_INDEX_INVALID;
> +  VarInfo.Header.VariableName = (CHAR16 *)VariableName;
> +  VarInfo.Header.NameSize     = StrSize (VariableName);
> +  VarInfo.Header.VendorGuid   = (EFI_GUID *)VariableGuid;
> +
> +  VarDig = FindVariableInternal (Global, &VarInfo, FALSE);
> +  if (VarDig == NULL) {
> +    return EFI_NOT_FOUND;
> +  }
> +
> +  if (Attributes != NULL) {
> +    *Attributes = VarDig->Attributes;
> +  }
> +
> +  if ((Data == NULL) || (*DataSize < VarDig->PlainDataSize)) {
> +    *DataSize = VarDig->PlainDataSize;
> +    return EFI_BUFFER_TOO_SMALL;
> +  }
> +
> +  VarInfo.Flags.Auth      = VarDig->Flags.Auth;
> +  VarInfo.Flags.Protected = VarDig->Flags.Protected;
> +
> +  //
> +  // Verify digest before copy the data back, if the variable is not in cache.
> +  //
> +  if (VarDig->CacheIndex != VAR_INDEX_INVALID) {
> +    VarInfo.Header.VariableName = NULL;
> +    VarInfo.Header.VendorGuid   = NULL;
> +    VarInfo.Buffer              = GET_BUFR (VarDig->CacheIndex);
> +
> +    Status = ContextIn->GetVariableInfo (&VarInfo);
> +    ASSERT_EFI_ERROR (Status);
> +  } else {
> +    //
> +    // A buffer for at least one variable data (<=PcdMax(Auth)VariableSize)
> +    // must be reserved in advance.
> +    //
> +    ASSERT (
> +      Global->VariableCache != 0
> +           && Global->VariableCacheSize >= VarDig->DataSize
> +      );
> +    DataBuffer = GET_BUFR (Global->VariableCache);
> +    //
> +    // Note name and GUID are already there.
> +    //
> +    VarInfo.StoreIndex = VarDig->StoreIndex;
> +
> +    VarInfo.Header.VariableName = NULL; // Prevent name from being retrieved
> again.
> +    VarInfo.Header.NameSize     = 0;
> +    VarInfo.Header.VendorGuid   = NULL; // Prevent guid from being retrieved
> again.
> +    VarInfo.Header.TimeStamp    = &TimeStamp;
> +    VarInfo.Header.Data         = DataBuffer;
> +    VarInfo.Header.DataSize     = VarDig->DataSize;
> +
> +    //
> +    // Get detailed information about the variable.
> +    //
> +    Status = ContextIn->GetVariableInfo (&VarInfo);
> +    ASSERT_EFI_ERROR (Status);
> +
> +    //
> +    // The variable must be validated its digest value to avoid TOCTOU, if it's
> +    // not been cached yet.
> +    //
> +    VarInfo.Header.VariableName = VAR_DIG_NAME (VarDig);
> +    VarInfo.Header.NameSize     = VarDig->NameSize;
> +    VarInfo.Header.VendorGuid   = &VarDig->VendorGuid;
> +    Status                      = VerifyVariableDigest (Global, &VarInfo, VarDig);
> +    if (EFI_ERROR (Status)) {
> +      return Status;
> +    }
> +  }
> +
> +  VarInfo.PlainDataSize = (UINT32)*DataSize;
> +
> +  //
> +  // Decrypt the data, if necessary.
> +  //
> +  Status    = ProtectedVariableLibGetDataInternal (Global, &VarInfo);
> +  *DataSize = VarInfo.PlainDataSize;
> +  if (EFI_ERROR (Status)) {
> +    DEBUG ((
> +      DEBUG_INFO,
> +      "%a: %d Exit(). VariableName = %s, VariableGuid = 0x%g, DataSize = 0x%X,
> Data Buffer = 0x%lX, Status = %r\n",
> +      __FUNCTION__,
> +      __LINE__,
> +      VariableName,
> +      VariableGuid,
> +      *DataSize,
> +      Data,
> +      Status
> +      ));
> +    return Status;
> +  }
> +
> +  CopyMem (Data, VarInfo.PlainData, VarInfo.PlainDataSize);
> +
> +  return Status;
> +}
> +
> +/**
> +
> +  This function is used to enumerate the variables managed by current
> +  ProtectedVariableLib.
> +
> +  If the VarInfo->StoreIndex is invalid (VAR_INDEX_INVALID), the first variable
> +  with the smallest StoreIndex will be returned. Otherwise, the variable with
> +  StoreIndex just after than the VarInfo->StoreIndex will be returned.
> +
> +  @param[in,out]      VarInfo     Pointer to structure containing variable
> information.
> +
> +  @retval EFI_SUCCESS               Found the specified variable.
> +  @retval EFI_INVALID_PARAMETER     VarInfo is NULL.
> +  @retval EFI_NOT_FOUND             The specified variable could not be found.
> +
> +**/
> +STATIC
> +EFI_STATUS
> +GetNextVariableInternal (
> +  IN  OUT PROTECTED_VARIABLE_INFO  *VarInfo
> +  )
> +{
> +  EFI_STATUS                 Status;
> +  PROTECTED_VARIABLE_GLOBAL  *Global;
> +  VARIABLE_DIGEST            *Found;
> +
> +  if (VarInfo == NULL) {
> +    ASSERT (VarInfo != NULL);
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  Status = GetProtectedVariableGlobal (&Global);
> +  if (EFI_ERROR (Status)) {
> +    ASSERT_EFI_ERROR (Status);
> +    return Status;
> +  }
> +
> +  Found = FindVariableInternal (Global, VarInfo, TRUE);
> +  if (Found == NULL) {
> +    return EFI_NOT_FOUND;
> +  }
> +
> +  //
> +  // Return all cached data.
> +  //
> +  VarInfo->Header.VariableName = VAR_DIG_NAME (Found);
> +  VarInfo->Header.VendorGuid   = VAR_DIG_GUID (Found);
> +  VarInfo->Header.NameSize     = Found->NameSize;
> +  VarInfo->Header.DataSize     = Found->DataSize;
> +  VarInfo->Header.Attributes   = Found->Attributes;
> +
> +  VarInfo->PlainDataSize = Found->PlainDataSize;
> +  VarInfo->StoreIndex    = Found->StoreIndex;
> +  if (Found->CacheIndex != VAR_INDEX_INVALID) {
> +    VarInfo->Buffer = GET_BUFR (Found->CacheIndex);
> +  }
> +
> +  VarInfo->Flags.Auth = Found->Flags.Auth;
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +
> +  Find the request variable.
> +
> +  @param[in, out]  VarInfo      Pointer to variable data.
> +
> +  @retval EFI_SUCCESS           The variable was read successfully.
> +  @retval EFI_NOT_FOUND         The variable could not be found.
> +  @retval EFI_INVALID_PARAMETER Variable info is NULL.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +ProtectedVariableLibFind (
> +  IN  OUT PROTECTED_VARIABLE_INFO  *VarInfo
> +  )
> +{
> +  EFI_STATUS                 Status;
> +  PROTECTED_VARIABLE_GLOBAL  *Global;
> +  VARIABLE_DIGEST            *Found;
> +
> +  if (VarInfo == NULL) {
> +    ASSERT (VarInfo != NULL);
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  Status = GetProtectedVariableGlobal (&Global);
> +  if (EFI_ERROR (Status)) {
> +    ASSERT_EFI_ERROR (Status);
> +    return Status;
> +  }
> +
> +  Found = FindVariableInternal (Global, VarInfo, FALSE);
> +  if (Found == NULL) {
> +    return EFI_NOT_FOUND;
> +  }
> +
> +  //
> +  // Return all cached data.
> +  //
> +  VarInfo->Header.VariableName = VAR_DIG_NAME (Found);
> +  VarInfo->Header.VendorGuid   = VAR_DIG_GUID (Found);
> +  VarInfo->Header.NameSize     = Found->NameSize;
> +  VarInfo->Header.DataSize     = Found->DataSize;
> +  VarInfo->Header.Attributes   = Found->Attributes;
> +
> +  VarInfo->PlainDataSize = Found->PlainDataSize;
> +  VarInfo->StoreIndex    = Found->StoreIndex;
> +  if (Found->CacheIndex != VAR_INDEX_INVALID) {
> +    VarInfo->Buffer = GET_BUFR (Found->CacheIndex);
> +  }
> +
> +  VarInfo->Flags.Auth = Found->Flags.Auth;
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +  Return the next variable name and GUID.
> +
> +  This function is called multiple times to retrieve the VariableName
> +  and VariableGuid of all variables currently available in the system.
> +  On each call, the previous results are passed into the interface,
> +  and, on return, the interface returns the data for the next
> +  interface. When the entire variable list has been returned,
> +  EFI_NOT_FOUND is returned.
> +
> +  @param  VariableNameSize  On entry, points to the size of the buffer pointed
> to by VariableName.
> +                            On return, the size of the variable name buffer.
> +  @param  VariableName      On entry, a pointer to a null-terminated string that
> is the variable's name.
> +                            On return, points to the next variable's null-terminated name
> string.
> +  @param  VariableGuid      On entry, a pointer to an EFI_GUID that is the
> variable's GUID.
> +                            On return, a pointer to the next variable's GUID.
> +
> +  @retval EFI_SUCCESS           The variable was read successfully.
> +  @retval EFI_NOT_FOUND         The variable could not be found.
> +  @retval EFI_BUFFER_TOO_SMALL  The VariableNameSize is too small for the
> resulting
> +                                data. VariableNameSize is updated with the size
> +                                required for the specified variable.
> +  @retval EFI_INVALID_PARAMETER VariableName, VariableGuid or
> +                                VariableNameSize is NULL.
> +  @retval EFI_DEVICE_ERROR      The variable could not be retrieved because of
> a device error.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +ProtectedVariableLibFindNext (
> +  IN OUT UINTN     *VariableNameSize,
> +  IN OUT CHAR16    *VariableName,
> +  IN OUT EFI_GUID  *VariableGuid
> +  )
> +{
> +  EFI_STATUS                 Status;
> +  PROTECTED_VARIABLE_INFO    VarInfo;
> +  PROTECTED_VARIABLE_GLOBAL  *Global;
> +  VARIABLE_DIGEST            *VarDig;
> +  UINTN                      Size;
> +
> +  if ((VariableNameSize == NULL) || (VariableName == NULL) || (VariableGuid
> == NULL)) {
> +    ASSERT (VariableNameSize != NULL);
> +    ASSERT (VariableName != NULL);
> +    ASSERT (VariableGuid != NULL);
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  Status = GetProtectedVariableGlobal (&Global);
> +  if (EFI_ERROR (Status)) {
> +    return Status;
> +  }
> +
> +  SetMem (&VarInfo, sizeof (VarInfo), 0);
> +  Size = StrSize (VariableName);
> +
> +  if (Size <= 2) {
> +    VarDig = VAR_DIG_PTR (Global->VariableDigests);
> +  } else {
> +    VarInfo.Header.VariableName = VariableName;
> +    VarInfo.Header.NameSize     = Size;
> +    VarInfo.Header.VendorGuid   = VariableGuid;
> +
> +    VarInfo.StoreIndex = VAR_INDEX_INVALID;
> +
> +    VarDig = FindVariableInternal (Global, &VarInfo, TRUE);
> +  }
> +
> +  if (VarDig == NULL) {
> +    return EFI_NOT_FOUND;
> +  }
> +
> +  if (VarDig->NameSize > *VariableNameSize) {
> +    return EFI_BUFFER_TOO_SMALL;
> +  }
> +
> +  CopyMem (VariableName, VAR_DIG_NAME (VarDig), VarDig->NameSize);
> +  CopyGuid (VariableGuid, &VarDig->VendorGuid);
> +  *VariableNameSize = VarInfo.Header.NameSize;
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +
> +  Return the next variable name and GUID.
> +
> +  @param[in, out]  VarInfo        Pointer to variable data.
> +
> +  @retval EFI_SUCCESS             The variable was read successfully.
> +  @retval EFI_INVALID_PARAMETER   VarInfo is NULL.
> +  @retval EFI_NOT_FOUND           The specified variable could not be found.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +ProtectedVariableLibFindNextEx (
> +  IN  OUT PROTECTED_VARIABLE_INFO  *VarInfo
> +  )
> +{
> +  return GetNextVariableInternal (VarInfo);
> +}
> +
> +/**
> +
> +  Return the max count of a variable.
> +
> +  @return   max count of a variable.
> +
> +**/
> +UINTN
> +ProtectedVariableLibGetMaxVariablesCount (
> +  VOID
> +  )
> +{
> +  PROTECTED_VARIABLE_GLOBAL  *Global;
> +  PROTECTED_VARIABLE_INFO    VarInfo;
> +  VARIABLE_DIGEST            *VarDig;
> +  EFI_STATUS                 Status;
> +  UINTN                      Count;
> +
> +  Status = GetProtectedVariableGlobal (&Global);
> +  if (EFI_ERROR (Status)) {
> +    ASSERT_EFI_ERROR (Status);
> +    return 0;
> +  }
> +
> +  Count = 0;
> +  ZeroMem (&VarInfo, sizeof (VarInfo));
> +
> +  //
> +  // Start with first entry
> +  //
> +  VarDig                      = VAR_DIG_PTR (Global->VariableDigests);
> +  VarInfo.Header.VariableName = VAR_DIG_NAME (VarDig);
> +  VarInfo.Header.VendorGuid   = VAR_DIG_GUID (VarDig);
> +  VarInfo.StoreIndex          = VarDig->StoreIndex;
> +
> +  do {
> +    VarInfo.Buffer = NULL;
> +    Status         = ProtectedVariableLibFindNextEx (&VarInfo);
> +    if (EFI_ERROR (Status)) {
> +      return Count;
> +    }
> +
> +    Count++;
> +  } while (TRUE);
> +}
> +
> +/**
> +  The function is called by PerformQuickSort to sort.
> +
> +  @param[in] Left            The pointer to first buffer.
> +  @param[in] Right           The pointer to second buffer.
> +
> +  @retval 0                  Buffer1 equal to Buffer2.
> +  @return < 0                Buffer1 is less than Buffer2.
> +  @return > 0                Buffer1 is greater than Buffer2.
> +
> +**/
> +INTN
> +EFIAPI
> +CompareStoreIndex (
> +  IN CONST VOID  *Left,
> +  IN CONST VOID  *Right
> +  )
> +{
> +  EFI_PHYSICAL_ADDRESS  StoreIndex1;
> +  EFI_PHYSICAL_ADDRESS  StoreIndex2;
> +
> +  StoreIndex1 = (*(EFI_PHYSICAL_ADDRESS *)Left);
> +  StoreIndex2 = (*(EFI_PHYSICAL_ADDRESS *)Right);
> +
> +  if (StoreIndex1 == StoreIndex2) {
> +    return (0);
> +  }
> +
> +  if (StoreIndex1 < StoreIndex2) {
> +    return (-1);
> +  }
> +
> +  return (1);
> +}
> +
> +/**
> +  Refresh variable information changed by variable service.
> +
> +  @param Buffer           Pointer to a pointer of buffer.
> +  @param NumElements      Pointer to number of elements in list.
> +
> +
> +  @return EFI_SUCCESS     Successfully retrieved sorted list.
> +  @return others          Unsuccessful.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +ProtectedVariableLibGetSortedList (
> +  IN  OUT  EFI_PHYSICAL_ADDRESS  **Buffer,
> +  IN  OUT  UINTN                 *NumElements
> +  )
> +{
> +  EFI_STATUS                 Status;
> +  UINTN                      Count;
> +  UINTN                      StoreIndexTableSize;
> +  EFI_PHYSICAL_ADDRESS       *StoreIndexTable;
> +  PROTECTED_VARIABLE_INFO    VarInfo;
> +  PROTECTED_VARIABLE_GLOBAL  *Global;
> +  VARIABLE_DIGEST            *VarDig;
> +
> +  Status = GetProtectedVariableGlobal (&Global);
> +  if (EFI_ERROR (Status)) {
> +    ASSERT_EFI_ERROR (Status);
> +    return Status;
> +  }
> +
> +  Count = 0;
> +  ZeroMem (&VarInfo, sizeof (VarInfo));
> +  StoreIndexTableSize = ProtectedVariableLibGetMaxVariablesCount ();
> +  StoreIndexTable     = AllocateZeroPool (sizeof (EFI_PHYSICAL_ADDRESS) *
> StoreIndexTableSize);
> +
> +  //
> +  // Start with first entry
> +  //
> +  VarDig                      = VAR_DIG_PTR (Global->VariableDigests);
> +  VarInfo.Header.VariableName = VAR_DIG_NAME (VarDig);
> +  VarInfo.Header.VendorGuid   = VAR_DIG_GUID (VarDig);
> +  VarInfo.StoreIndex          = VarDig->StoreIndex;
> +  StoreIndexTable[Count]      = VarInfo.StoreIndex;
> +  Count++;
> +
> +  //
> +  // Populate the un-sorted table
> +  //
> +  do {
> +    VarInfo.Buffer = NULL;
> +    Status         = ProtectedVariableLibFindNextEx (&VarInfo);
> +    if (EFI_ERROR (Status)) {
> +      break;
> +    }
> +
> +    StoreIndexTable[Count] = VarInfo.StoreIndex;
> +    Count++;
> +  } while (TRUE);
> +
> +  PerformQuickSort (
> +    StoreIndexTable,
> +    Count,
> +    sizeof (EFI_PHYSICAL_ADDRESS),
> +    (SORT_COMPARE)CompareStoreIndex
> +    );
> +
> +  *Buffer      = StoreIndexTable;
> +  *NumElements = Count;
> +
> +  return EFI_SUCCESS;
> +}
> diff --git a/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableDxe.c
> b/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableDxe.c
> new file mode 100644
> index 000000000000..94df21eacf25
> --- /dev/null
> +++ b/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableDxe.c
> @@ -0,0 +1,163 @@
> +/** @file
> +  Implemention of ProtectedVariableLib for SMM variable services.
> +
> +Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
> +SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#include <PiDxe.h>
> +#include <Uefi.h>
> +
> +#include "Library/UefiBootServicesTableLib.h"
> +#include "Library/MemoryAllocationLib.h"
> +
> +#include "ProtectedVariableInternal.h"
> +
> +PROTECTED_VARIABLE_CONTEXT_IN  mVariableContextIn = {
> +  PROTECTED_VARIABLE_CONTEXT_IN_STRUCT_VERSION,
> +  sizeof (PROTECTED_VARIABLE_CONTEXT_IN),
> +  0,
> +  FromSmmModule,
> +  NULL,
> +  NULL,
> +  NULL,
> +  NULL,
> +  NULL
> +};
> +
> +PROTECTED_VARIABLE_GLOBAL  mProtectedVariableGlobal = {
> +  PROTECTED_VARIABLE_CONTEXT_OUT_STRUCT_VERSION,
> +  sizeof (PROTECTED_VARIABLE_GLOBAL),
> +  { 0 },
> +  { 0 },
> +  0,
> +  0,
> +  0,
> +  0,
> +  0,
> +  0,
> +  { 0,                                          0,  0 },
> +  0,
> +  0,
> +  { 0,                                          0,  0, 0, 0, 0}
> +};
> +
> +/**
> +  Fix incorrect state of MetaDataHmacVariable before any variable update.
> +
> +  @param[in]   Event    The event that occurred
> +  @param[in]   Context  For EFI compatibility.  Not used.
> +
> +**/
> +VOID
> +EFIAPI
> +VariableWriteProtocolCallback (
> +  IN  EFI_EVENT  Event,
> +  IN  VOID       *Context
> +  )
> +{
> +  EFI_STATUS  Status;
> +
> +  //
> +  // Fix incorrect state of MetaDataHmacVariable before any variable update.
> +  // This has to be done here due to the fact that this operation needs to
> +  // update NV storage but the FVB and FTW protocol might not be ready during
> +  // ProtectedVariableLibInitialize().
> +  //
> +  Status = FixupHmacVariable ();
> +  ASSERT_EFI_ERROR (Status);
> +
> +  Status = ProtectedVariableLibWriteInit ();
> +  ASSERT_EFI_ERROR (Status);
> +
> +  gBS->CloseEvent (Event);
> +}
> +
> +/**
> +
> +  Initialization for protected variable services.
> +
> +  If this initialization failed upon any error, the whole variable services
> +  should not be used.  A system reset might be needed to re-construct NV
> +  variable storage to be the default state.
> +
> +  @param[in]  ContextIn   Pointer to variable service context needed by
> +                          protected variable.
> +
> +  @retval EFI_SUCCESS               Protected variable services are ready.
> +  @retval EFI_INVALID_PARAMETER     If ContextIn == NULL or something
> missing or
> +                                    mismatching in the content in ContextIn.
> +  @retval EFI_COMPROMISED_DATA      If failed to check integrity of protected
> variables.
> +  @retval EFI_OUT_OF_RESOURCES      Fail to allocate enough resource.
> +  @retval EFI_UNSUPPORTED           Unsupported to process protected variable.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +ProtectedVariableLibInitialize (
> +  IN  PROTECTED_VARIABLE_CONTEXT_IN  *ContextIn
> +  )
> +{
> +  EFI_STATUS                     Status;
> +  PROTECTED_VARIABLE_CONTEXT_IN  *ProtectedVarContext;
> +  PROTECTED_VARIABLE_GLOBAL      *OldGlobal;
> +  PROTECTED_VARIABLE_GLOBAL      *NewGlobal;
> +  VOID                           *VarWriteReg;
> +
> +  if (  (ContextIn == NULL)
> +     || (ContextIn->StructVersion !=
> PROTECTED_VARIABLE_CONTEXT_IN_STRUCT_VERSION)
> +     || (ContextIn->StructSize != sizeof (PROTECTED_VARIABLE_CONTEXT_IN))
> +     || (ContextIn->GetVariableInfo == NULL)
> +     || (ContextIn->GetNextVariableInfo == NULL)
> +     || (ContextIn->UpdateVariableStore == NULL)
> +     || (ContextIn->UpdateVariable == NULL))
> +  {
> +    ASSERT (ContextIn != NULL);
> +    ASSERT (ContextIn->StructVersion ==
> PROTECTED_VARIABLE_CONTEXT_IN_STRUCT_VERSION);
> +    ASSERT (ContextIn->StructSize == sizeof
> (PROTECTED_VARIABLE_CONTEXT_IN));
> +    ASSERT (ContextIn->GetVariableInfo != NULL);
> +    ASSERT (ContextIn->GetNextVariableInfo != NULL);
> +    ASSERT (ContextIn->UpdateVariableStore != NULL);
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  GetProtectedVariableGlobal (&NewGlobal);
> +  ProtectedVarContext = GET_CNTX (NewGlobal);
> +  CopyMem (ProtectedVarContext, ContextIn, sizeof (mVariableContextIn));
> +  ProtectedVarContext->VariableServiceUser = FromSmmModule;
> +
> +  //
> +  // Get root key and HMAC key from HOB created by PEI variable driver.
> +  //
> +  Status = GetProtectedVariableGlobalFromHob (&OldGlobal);
> +  ASSERT_EFI_ERROR (Status);
> +
> +  CopyMem ((VOID *)NewGlobal, (CONST VOID *)OldGlobal, sizeof
> (*OldGlobal));
> +
> +  //
> +  // The keys must not be available outside SMM.
> +  //
> +  if (ProtectedVarContext->VariableServiceUser == FromSmmModule) {
> +    ZeroMem (OldGlobal->RootKey, sizeof (OldGlobal->RootKey));
> +    ZeroMem (OldGlobal->MetaDataHmacKey, sizeof (OldGlobal-
> >MetaDataHmacKey));
> +  }
> +
> +  //
> +  // Register variable write protocol notify function used to fix any
> +  // inconsistency in MetaDataHmacVariable before the first variable write
> +  // operation.
> +  //
> +  NewGlobal->Flags.WriteInit  = FALSE;
> +  NewGlobal->Flags.WriteReady = FALSE;
> +
> +  EfiCreateProtocolNotifyEvent (
> +    &gEfiVariableWriteArchProtocolGuid,
> +    TPL_CALLBACK,
> +    VariableWriteProtocolCallback,
> +    NULL,
> +    &VarWriteReg
> +    );
> +
> +  return EFI_SUCCESS;
> +}
> diff --git a/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariablePei.c
> b/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariablePei.c
> new file mode 100644
> index 000000000000..8b5ccb83e32d
> --- /dev/null
> +++ b/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariablePei.c
> @@ -0,0 +1,1327 @@
> +/** @file
> +
> +Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
> +SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#include <Base.h>
> +#include <Uefi.h>
> +#include <PiPei.h>
> +
> +#include <Guid/VariableFormat.h>
> +#include <Ppi/MemoryDiscovered.h>
> +
> +#include <Library/HobLib.h>
> +#include <Library/PeiServicesLib.h>
> +#include <Library/MemoryAllocationLib.h>
> +#include <Library/ReportStatusCodeLib.h>
> +
> +#include "ProtectedVariableInternal.h"
> +
> +/**
> +  Function allocates a global buffer.
> +
> +  This function allocates a buffer with the specified size.
> +
> +  @param[in] Size           Size of buffer to allocate.
> +  @param[in] AllocatePage   Whether to allocate pages.
> +
> +  @retval Buffer             Pointer to the Buffer allocated.
> +  @retval NULL               if no Buffer was found.
> +
> +**/
> +VOID *
> +AllocateGlobalBuffer (
> +  IN UINT32   Size,
> +  IN BOOLEAN  AllocatePage
> +  )
> +{
> +  VOID                       *Buffer;
> +  EFI_HOB_MEMORY_ALLOCATION  *MemoryAllocationHob;
> +  EFI_PEI_HOB_POINTERS       Hob;
> +
> +  Buffer = NULL;
> +  if (!AllocatePage) {
> +    Buffer = BuildGuidHob (&gEdkiiProtectedVariableGlobalGuid, Size);
> +  }
> +
> +  if (Buffer == NULL) {
> +    //
> +    // Use the AllocatePages() to get over size limit of general GUID-ed HOB.
> +    //
> +    Buffer = AllocatePages (EFI_SIZE_TO_PAGES (Size));
> +    if (Buffer == NULL) {
> +      ASSERT_EFI_ERROR (EFI_OUT_OF_RESOURCES);
> +      return NULL;
> +    }
> +
> +    //
> +    // Mark the HOB holding the pages just allocated so that it can be
> +    // identified later.
> +    //
> +    MemoryAllocationHob = NULL;
> +    Hob.Raw             = GetFirstHob (EFI_HOB_TYPE_MEMORY_ALLOCATION);
> +    while (Hob.Raw != NULL) {
> +      MemoryAllocationHob = (EFI_HOB_MEMORY_ALLOCATION *)Hob.Raw;
> +      if ((UINTN)Buffer == (UINTN)MemoryAllocationHob-
> >AllocDescriptor.MemoryBaseAddress) {
> +        CopyGuid (
> +          &MemoryAllocationHob->AllocDescriptor.Name,
> +          &gEdkiiProtectedVariableGlobalGuid
> +          );
> +        break;
> +      }
> +
> +      Hob.Raw = GET_NEXT_HOB (Hob);
> +      Hob.Raw = GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION,
> Hob.Raw);
> +    }
> +  }
> +
> +  return Buffer;
> +}
> +
> +/**
> +  Callback use to re-verify all variables and cache them in memory.
> +
> +  @param[in] PeiServices          General purpose services available to every PEIM.
> +  @param[in] NotifyDescriptor     The notification structure this PEIM registered
> on install.
> +  @param[in] Ppi                  The memory discovered PPI.  Not used.
> +
> +  @retval EFI_SUCCESS             The function completed successfully.
> +  @retval others                  There's error in MP initialization.
> +**/
> +EFI_STATUS
> +EFIAPI
> +MemoryDiscoveredPpiNotifyCallback (
> +  IN EFI_PEI_SERVICES           **PeiServices,
> +  IN EFI_PEI_NOTIFY_DESCRIPTOR  *NotifyDescriptor,
> +  IN VOID                       *Ppi
> +  )
> +{
> +  EFI_STATUS                     Status;
> +  PROTECTED_VARIABLE_CONTEXT_IN  *ContextIn;
> +  PROTECTED_VARIABLE_GLOBAL      *Global;
> +  VARIABLE_DIGEST                *VarDig;
> +  PROTECTED_VARIABLE_INFO        VarInfo;
> +  VOID                           *Buffer;
> +  UINT32                         VarSize;
> +  INTN                           Result;
> +
> +  Status = GetProtectedVariableGlobal (&Global);
> +  if (EFI_ERROR (Status)) {
> +    ASSERT_EFI_ERROR (Status);
> +    return Status;
> +  }
> +
> +  ContextIn = GET_CNTX (Global);
> +
> +  //
> +  // Allocate last Var buffer for confidentiality crypto operation
> +  //
> +  VarSize = (Global->VariableNumber + 1) * MAX_VARIABLE_SIZE;
> +  Buffer  = AllocateGlobalBuffer (VarSize, TRUE);
> +
> +  //
> +  // Traverse all valid variables.
> +  //
> +  VarDig = VAR_DIG_PTR (Global->VariableDigests);
> +  while (VarDig != NULL) {
> +    if (VarDig->CacheIndex == VAR_INDEX_INVALID) {
> +      ASSERT (VarDig->StoreIndex != VAR_INDEX_INVALID);
> +
> +      VarSize  =  VARIABLE_HEADER_SIZE (Global->Flags.Auth);
> +      VarSize += VarDig->NameSize + GET_PAD_SIZE (VarDig->NameSize);
> +      VarSize += VarDig->DataSize + GET_PAD_SIZE (VarDig->DataSize);
> +      VarSize  = HEADER_ALIGN (VarSize);
> +
> +      //
> +      // Note the variable might be in unconsecutive space.
> +      //
> +      ZeroMem (&VarInfo, sizeof (VarInfo));
> +      VarInfo.StoreIndex = VarDig->StoreIndex;
> +      VarInfo.Buffer     = Buffer;
> +      VarInfo.Flags.Auth = VarDig->Flags.Auth;
> +
> +      Status = ContextIn->GetVariableInfo (&VarInfo);
> +      ASSERT_EFI_ERROR (Status);
> +      //
> +      // VerifyVariableDigest() refers to CipherData for raw data.
> +      //
> +      VarInfo.CipherData     = VarInfo.Header.Data;
> +      VarInfo.CipherDataSize = (UINT32)VarInfo.Header.DataSize;
> +
> +      //
> +      // Make sure that the cached copy is not compromised.
> +      //
> +      Status = VerifyVariableDigest (Global, &VarInfo, VarDig);
> +      if (EFI_ERROR (Status)) {
> +        REPORT_STATUS_CODE (
> +          EFI_ERROR_CODE | EFI_ERROR_UNRECOVERED,
> +          (PcdGet32 (PcdStatusCodeVariableIntegrity) | (Status & 0xFF))
> +          );
> +        ASSERT_EFI_ERROR (Status);
> +        CpuDeadLoop ();
> +      }
> +
> +      //
> +      // Simply use the cache address as CacheIndex of the variable.
> +      //
> +      VarDig->CacheIndex = GET_ADRS (Buffer);
> +      Buffer             = (UINT8 *)Buffer + MAX_VARIABLE_SIZE;
> +    } else {
> +      Result = StrnCmp (
> +                 VAR_DIG_NAME (VarDig),
> +                 METADATA_HMAC_VARIABLE_NAME,
> +                 METADATA_HMAC_VARIABLE_NAME_SIZE
> +                 );
> +      if (Result == 0) {
> +        CopyMem (
> +          Buffer,
> +          GET_BUFR (Global->GlobalSelf + (Global->StructSize -
> GetMetaDataHmacVarSize (Global->Flags.Auth))),
> +          GetMetaDataHmacVarSize (Global->Flags.Auth)
> +          );
> +
> +        //
> +        // Simply use the cache address as CacheIndex of the variable.
> +        //
> +        VarDig->CacheIndex = GET_ADRS (Buffer);
> +        Buffer             = (UINT8 *)Buffer + MAX_VARIABLE_SIZE;
> +      }
> +    }
> +
> +    VarDig = VAR_DIG_NEXT (VarDig);
> +  }
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +  Callback use to perform variable integrity check.
> +
> +  @param[in] PeiServices          General purpose services available to every PEIM.
> +  @param[in] NotifyDescriptor     The notification structure this PEIM registered
> on install.
> +  @param[in] Ppi                  The memory discovered PPI.  Not used.
> +
> +  @retval EFI_SUCCESS             The function completed successfully.
> +  @retval others                  There's error in MP initialization.
> +**/
> +EFI_STATUS
> +EFIAPI
> +VariableStoreDiscoveredPpiNotifyCallback (
> +  IN EFI_PEI_SERVICES           **PeiServices,
> +  IN EFI_PEI_NOTIFY_DESCRIPTOR  *NotifyDescriptor,
> +  IN VOID                       *Ppi
> +  )
> +{
> +  EFI_STATUS                     Status;
> +  EFI_HOB_GUID_TYPE              *GuidHob;
> +  PROTECTED_VARIABLE_CONTEXT_IN  *ContextIn;
> +
> +  GuidHob = GetFirstGuidHob (&gEdkiiProtectedVariableContextGuid);
> +  if (GuidHob != NULL) {
> +    ContextIn = (PROTECTED_VARIABLE_CONTEXT_IN *)GET_GUID_HOB_DATA
> (GuidHob);
> +  } else {
> +    ASSERT (GuidHob == NULL);
> +  }
> +
> +  Status = ContextIn->IsHobVariableStoreAvailable ();
> +
> +  if (Status == EFI_NOT_READY) {
> +    ASSERT_EFI_ERROR (Status);
> +    return Status;
> +  }
> +
> +  Status = PerformVariableIntegrityCheck (ContextIn);
> +
> +  return Status;
> +}
> +
> +EFI_PEI_NOTIFY_DESCRIPTOR  mPostMemNotifyList[] = {
> +  {
> +    (EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK |
> EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
> +    &gEfiPeiMemoryDiscoveredPpiGuid,
> +    MemoryDiscoveredPpiNotifyCallback
> +  }
> +};
> +
> +EFI_PEI_NOTIFY_DESCRIPTOR  mVariableStoreNotifyList[] = {
> +  {
> +    (EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK |
> EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
> +    &gEfiPeiVariableStoreDiscoveredPpiGuid,
> +    VariableStoreDiscoveredPpiNotifyCallback
> +  }
> +};
> +
> +/**
> +
> +  Get global data structure used to process protected variable.
> +
> +  @param[out]   Global      Pointer to global configuration data.
> +
> +  @retval EFI_SUCCESS         Get requested structure successfully.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +GetProtectedVariableGlobal (
> +  OUT PROTECTED_VARIABLE_GLOBAL  **Global OPTIONAL
> +  )
> +{
> +  return GetProtectedVariableGlobalFromHob (Global);
> +}
> +
> +/**
> +
> +  Get context data structure used to process protected variable.
> +
> +  @param[out]   ContextIn   Pointer to context provided by variable runtime
> services.
> +
> +  @retval EFI_SUCCESS         Get requested structure successfully.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +GetProtectedVariableContext (
> +  PROTECTED_VARIABLE_CONTEXT_IN  **ContextIn  OPTIONAL
> +  )
> +{
> +  EFI_HOB_GUID_TYPE  *GuidHob;
> +
> +  GuidHob = GetFirstGuidHob (&gEdkiiProtectedVariableContextGuid);
> +  if (GuidHob != NULL) {
> +    *ContextIn = (PROTECTED_VARIABLE_CONTEXT_IN *)GET_GUID_HOB_DATA
> (GuidHob);
> +    return EFI_SUCCESS;
> +  }
> +
> +  return EFI_NOT_FOUND;
> +}
> +
> +/**
> +  Verify the HMAC value stored in MetaDataHmacVar against all valid and
> +  protected variables in storage.
> +
> +  @param[in,out]  Global          Pointer to global configuration data.
> +
> +  @retval   EFI_SUCCESS           The HMAC value matches.
> +  @retval   EFI_ABORTED           Error in HMAC value calculation.
> +  @retval   EFI_VOLUME_CORRUPTED  Inconsistency found in NV variable
> storage.
> +  @retval   EFI_COMPROMISED_DATA  The HMAC value doesn't match.
> +
> +**/
> +EFI_STATUS
> +VerifyMetaDataHmac (
> +  IN OUT  PROTECTED_VARIABLE_GLOBAL  *Global
> +  )
> +{
> +  EFI_STATUS       Status;
> +  VARIABLE_DIGEST  *VariableDig;
> +  UINT32           Counter1;
> +  UINT32           Counter2;
> +  VOID             *Hmac1;
> +  VOID             *Hmac2;
> +  UINT8            HmacVal1[METADATA_HMAC_SIZE];
> +  UINT8            HmacVal2[METADATA_HMAC_SIZE];
> +
> +  Hmac1 = NULL;
> +  Hmac2 = HmacSha256New ();
> +  if (Hmac2 == NULL) {
> +    ASSERT (Hmac2 != NULL);
> +    return EFI_OUT_OF_RESOURCES;
> +  }
> +
> +  if (!HmacSha256SetKey (Hmac2, Global->MetaDataHmacKey, sizeof (Global-
> >MetaDataHmacKey))) {
> +    ASSERT (FALSE);
> +    Status = EFI_ABORTED;
> +    goto Done;
> +  }
> +
> +  //
> +  // Retrieve the RPMC counter value.
> +  //
> +  Status = RequestMonotonicCounter (RPMC_COUNTER_1, &Counter1);
> +  if (EFI_ERROR (Status)) {
> +    ASSERT_EFI_ERROR (Status);
> +    goto Done;
> +  }
> +
> +  Status = RequestMonotonicCounter (RPMC_COUNTER_2, &Counter2);
> +  if (EFI_ERROR (Status)) {
> +    ASSERT_EFI_ERROR (Status);
> +    goto Done;
> +  }
> +
> +  //
> +  // Counter1 must be either equal to Counter2 or just one step ahead of
> Counter2.
> +  //
> +  if ((Counter1 > Counter2) && ((Counter1 - Counter2) > 1)) {
> +    Status = EFI_COMPROMISED_DATA;
> +    goto Done;
> +  }
> +
> +  VariableDig = VAR_DIG_PTR (Global->VariableDigests);
> +  while (VariableDig != NULL) {
> +    //
> +    // Only take valid protected variables into account.
> +    //
> +    if (VariableDig->Flags.Protected && VariableDig->Flags.Valid) {
> +      if (!HmacSha256Update (
> +             Hmac2,
> +             VAR_DIG_VALUE (VariableDig),
> +             VariableDig->DigestSize
> +             ))
> +      {
> +        ASSERT (FALSE);
> +        Status = EFI_ABORTED;
> +        goto Done;
> +      }
> +    }
> +
> +    VariableDig = VAR_DIG_NEXT (VariableDig);
> +  }
> +
> +  //
> +  // If two MetaDataHmacVariable were found, check which one is valid. We
> might
> +  // need two HMAC values to check against: one for Counter1, one for
> Counter2.
> +  //
> +  if (  (Global->Unprotected[IndexHmacAdded] != VAR_INDEX_INVALID)
> +     && (Global->Unprotected[IndexHmacInDel] != VAR_INDEX_INVALID)
> +     && (Counter1 != Counter2))
> +  {
> +    //
> +    // Might need to check Counter1. There must be something wrong in last
> boot.
> +    //
> +    Hmac1 = HmacSha256New ();
> +    if ((Hmac1 == NULL) || !HmacSha256Duplicate (Hmac2, Hmac1)) {
> +      ASSERT (FALSE);
> +      Status = EFI_OUT_OF_RESOURCES;
> +      goto Done;
> +    }
> +
> +    if (  !HmacSha256Update (Hmac1, &Counter1, sizeof (Counter1))
> +       || !HmacSha256Final (Hmac1, HmacVal1))
> +    {
> +      ASSERT (FALSE);
> +      Status = EFI_ABORTED;
> +      goto Done;
> +    }
> +  }
> +
> +  //
> +  // Always check Counter2.
> +  //
> +  if (  !HmacSha256Update (Hmac2, &Counter2, sizeof (Counter2))
> +     || !HmacSha256Final (Hmac2, HmacVal2))
> +  {
> +    ASSERT (FALSE);
> +    Status = EFI_ABORTED;
> +    goto Done;
> +  }
> +
> +  //
> +  //  When writing (update or add) a variable, there must be following steps
> +  //  performed:
> +  //
> +  //    A - Increment Counter1
> +  //    B - Mark old MetaDataHmacVar as VAR_IN_DELETED_TRANSITION
> +  //    C - Calculate new HMAC value against Counter2+1,
> +  //        and force-add a new MetaDataHmacVar with state of VAR_ADDED
> +  //    D - Write the new protected variable
> +  //    E - Increment Counter2
> +  //    F - Mark old MetaDataHmacVar as VAR_DELETED
> +  //
> +  Status = EFI_COMPROMISED_DATA;
> +  if (  (Global->Unprotected[IndexHmacAdded] != VAR_INDEX_INVALID)
> +     && (Global->Unprotected[IndexHmacInDel] == VAR_INDEX_INVALID))
> +  {
> +    if (CompareMem (
> +          VAR_DIG_VALUE (VAR_DIG_PTR (Global-
> >Unprotected[IndexHmacAdded])),
> +          HmacVal2,
> +          METADATA_HMAC_SIZE
> +          ) == 0)
> +    {
> +      //
> +      //
> +      // + A - Increment Counter1
> +      //   B - Mark old MetaDataHmacVar as VAR_IN_DELETED_TRANSITION
> +      //   C - Calculate new HMAC value against Counter2+1,
> +      //       and force-add a new MetaDataHmacVar with state of VAR_ADDED
> +      //   D - Write the new protected variable
> +      //   E - Increment Counter2
> +      //   F - Mark old MetaDataHmacVar as VAR_DELETED
> +      //
> +      // or,
> +      //
> +      // + A - Increment Counter1
> +      // + B - Mark old MetaDataHmacVar as VAR_IN_DELETED_TRANSITION
> +      // + C - Calculate new HMAC value against Counter2+1,
> +      //       and force-add a new MetaDataHmacVar with state of VAR_ADDED
> +      // + D - Write the new protected variable
> +      // + E - Increment Counter2
> +      // + F - Mark old MetaDataHmacVar as VAR_DELETED
> +      //
> +      Status = EFI_SUCCESS;
> +
> +      VAR_DIG_PTR (Global->Unprotected[IndexHmacAdded])->Flags.Valid =
> TRUE;
> +    }
> +  } else if (  (Global->Unprotected[IndexHmacAdded] == VAR_INDEX_INVALID)
> +            && (Global->Unprotected[IndexHmacInDel] != VAR_INDEX_INVALID))
> +  {
> +    if (CompareMem (
> +          VAR_DIG_VALUE (VAR_DIG_PTR (Global-
> >Unprotected[IndexHmacInDel])),
> +          HmacVal2,
> +          METADATA_HMAC_SIZE
> +          ) == 0)
> +    {
> +      //
> +      // + A - Increment Counter1
> +      // + B - Mark old MetaDataHmacVar as VAR_IN_DELETED_TRANSITION
> +      //   C - Calculate new HMAC value against Counter2+1,
> +      //       and force-add a new MetaDataHmacVar with state of VAR_ADDED
> +      //   D - Write the new protected variable
> +      //   E - Increment Counter2
> +      //   F - Mark old MetaDataHmacVar as VAR_DELETED
> +      //
> +      Status = EFI_SUCCESS;
> +
> +      VAR_DIG_PTR (Global->Unprotected[IndexHmacInDel])->Flags.Valid = TRUE;
> +    }
> +  } else if (  (Global->Unprotected[IndexHmacAdded] != VAR_INDEX_INVALID)
> +            && (Global->Unprotected[IndexHmacInDel] != VAR_INDEX_INVALID))
> +  {
> +    if (Counter1 > Counter2) {
> +      if (CompareMem (
> +            VAR_DIG_VALUE (VAR_DIG_PTR (Global-
> >Unprotected[IndexHmacInDel])),
> +            HmacVal2,
> +            METADATA_HMAC_SIZE
> +            ) == 0)
> +      {
> +        //
> +        // + A - Increment Counter1
> +        // + B - Mark old MetaDataHmacVar as VAR_IN_DELETED_TRANSITION
> +        // + C - Calculate new HMAC value against Counter2+1,
> +        //       and force-add a new MetaDataHmacVar with state VAR_ADDED
> +        //   D - Write the new protected variable
> +        //   E - Increment Counter2
> +        //   F - Mark old MetaDataHmacVar as VAR_DELETED
> +        //
> +        Status = EFI_SUCCESS;
> +
> +        VAR_DIG_PTR (Global->Unprotected[IndexHmacAdded])->Flags.Valid =
> FALSE;
> +        VAR_DIG_PTR (Global->Unprotected[IndexHmacInDel])->Flags.Valid =
> TRUE;
> +      } else if (CompareMem (
> +                   VAR_DIG_VALUE (VAR_DIG_PTR (Global-
> >Unprotected[IndexHmacAdded])),
> +                   HmacVal1,
> +                   METADATA_HMAC_SIZE
> +                   ) == 0)
> +      {
> +        //
> +        // + A - Increment Counter1
> +        // + B - Mark old MetaDataHmacVar as VAR_IN_DELETED_TRANSITION
> +        // + C - Calculate new HMAC value against Counter2+1,
> +        //       and force-add a new MetaDataHmacVar with state of VAR_ADDED
> +        // + D - Write the new protected variable
> +        //   E - Increment Counter2
> +        //   F - Mark old MetaDataHmacVar as VAR_DELETED
> +        //
> +        Status = EFI_SUCCESS;
> +
> +        VAR_DIG_PTR (Global->Unprotected[IndexHmacAdded])->Flags.Valid =
> TRUE;
> +        VAR_DIG_PTR (Global->Unprotected[IndexHmacInDel])->Flags.Valid =
> FALSE;
> +      }
> +    } else {
> +      if (CompareMem (
> +            VAR_DIG_VALUE (VAR_DIG_PTR (Global-
> >Unprotected[IndexHmacAdded])),
> +            HmacVal2,
> +            METADATA_HMAC_SIZE
> +            ) == 0)
> +      {
> +        //
> +        // + A - Increment Counter1
> +        // + B - Mark old MetaDataHmacVar as VAR_IN_DELETED_TRANSITION
> +        // + C - Calculate new HMAC value against Counter2+1,
> +        //       and force-add a new MetaDataHmacVar with state of VAR_ADDED
> +        // + D - Write the new protected variable
> +        // + E - Increment Counter2
> +        //   F - Mark old MetaDataHmacVar as VAR_DELETED
> +        //
> +        Status = EFI_SUCCESS;
> +
> +        VAR_DIG_PTR (Global->Unprotected[IndexHmacAdded])->Flags.Valid =
> TRUE;
> +        VAR_DIG_PTR (Global->Unprotected[IndexHmacInDel])->Flags.Valid =
> FALSE;
> +      }
> +    }
> +  } else {
> +    //
> +    // There must be logic error or variable written to storage skipped
> +    // the protected variable service, if code reaches here.
> +    //
> +    ASSERT (FALSE);
> +  }
> +
> +Done:
> +  if (Hmac1 != NULL) {
> +    HmacSha256Free (Hmac1);
> +  }
> +
> +  if (Hmac2 != NULL) {
> +    HmacSha256Free (Hmac2);
> +  }
> +
> +  return Status;
> +}
> +
> +/**
> +  Collect variable digest information.
> +
> +  This information is collected to be used to for integrity check.
> +
> +  @param[in]       Global             Pointer to global configuration data.
> +  @param[in]       ContextIn          Pointer to variable service context needed by
> +                                      protected variable.
> +  @param[in, out]  DigestBuffer       Base address of digest of each variable.
> +  @param[out]      DigestBufferSize   Digest size of one variable if DigestBuffer
> is NULL.
> +                                      Size of DigestBuffer if DigestBuffer is NOT NULL.
> +  @param[out]      VariableNumber     Number of valid variables.
> +
> +  @retval   EFI_SUCCESS             Successfully retreived variable digest.
> +  @retval   EFI_INVALID_PARAMETER   One ore more parameters are invalid.
> +  @retval   EFI_OUT_OF_RESOURCES    Unable to allocate memory.
> +  @retval   EFI_BUFFER_TOO_SMALL    The DigestBufferSize pass in is too small.
> +
> +**/
> +EFI_STATUS
> +CollectVariableDigestInfo (
> +  IN      PROTECTED_VARIABLE_GLOBAL      *Global,
> +  IN      PROTECTED_VARIABLE_CONTEXT_IN  *ContextIn,
> +  IN  OUT VOID                           *DigestBuffer OPTIONAL,
> +  OUT UINT32                             *DigestBufferSize OPTIONAL,
> +  OUT UINT32                             *VariableNumber OPTIONAL
> +  )
> +{
> +  EFI_STATUS                  Status;
> +  PROTECTED_VARIABLE_INFO     VarInfo;
> +  UINT32                      VarNum;
> +  UINT32                      DigSize;
> +  VARIABLE_DIGEST             *VarDig;
> +  EFI_TIME                    TimeStamp;
> +  UNPROTECTED_VARIABLE_INDEX  VarIndex;
> +
> +  //
> +  // This function might be called before Global is initialized. In that case,
> +  // Global must be NULL but not ContextIn.
> +  //
> +  if ((Global == NULL) && (ContextIn == NULL)) {
> +    ASSERT (Global != NULL || ContextIn != NULL);
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  if ((Global == NULL) && (DigestBuffer != NULL)) {
> +    ASSERT (Global != NULL && DigestBuffer != NULL);
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  if (  (DigestBuffer != NULL)
> +     && ((DigestBufferSize == NULL) || (*DigestBufferSize == 0)))
> +  {
> +    ASSERT (
> +      DigestBuffer != NULL
> +           && DigestBufferSize != NULL && *DigestBufferSize > 0
> +      );
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  if ((Global != NULL) && (ContextIn == NULL)) {
> +    ContextIn = GET_CNTX (Global);
> +  }
> +
> +  DigSize = 0;
> +  VarNum  = 0;
> +  VarDig  = NULL;
> +
> +  ZeroMem (&VarInfo, sizeof (VarInfo));
> +  VarInfo.StoreIndex = VAR_INDEX_INVALID; // To get the first variable.
> +
> +  if ((Global != NULL) &&
> +      (Global->VariableCache != 0) &&
> +      (Global->VariableCacheSize > 0))
> +  {
> +    //
> +    // Use the variable cache to hold a copy of one variable.
> +    //
> +    VarInfo.Buffer = GET_BUFR (Global->VariableCache);
> +  } else {
> +    //
> +    // Allocate a buffer to hold a copy of one variable
> +    //
> +    VarInfo.Buffer = AllocatePages (EFI_SIZE_TO_PAGES (MAX_VARIABLE_SIZE));
> +    if (VarInfo.Buffer == NULL) {
> +      ASSERT (VarInfo.Buffer != NULL);
> +      return EFI_OUT_OF_RESOURCES;
> +    }
> +  }
> +
> +  if ((DigestBuffer != NULL) && (*DigestBufferSize > 0)) {
> +    VarDig = DigestBuffer;
> +  }
> +
> +  while (TRUE) {
> +    if (VarDig != NULL) {
> +      if (DigSize >= (*DigestBufferSize)) {
> +        //
> +        // Out of buffer.
> +        //
> +        break;
> +      }
> +
> +      VarInfo.Header.VendorGuid   = &VarDig->VendorGuid;
> +      VarInfo.Header.VariableName = VAR_DIG_NAME (VarDig);
> +      VarInfo.Header.NameSize     = (UINTN)DigestBuffer +
> (UINTN)*DigestBufferSize
> +                                    - (UINTN)VarInfo.Header.VariableName;
> +      VarInfo.Header.TimeStamp = &TimeStamp;
> +      VarInfo.Header.Data      = NULL;
> +    } else {
> +      ZeroMem ((VOID *)&VarInfo.Header, sizeof (VarInfo.Header));
> +    }
> +
> +    Status = ContextIn->GetNextVariableInfo (&VarInfo);
> +    if (EFI_ERROR (Status)) {
> +      break;
> +    }
> +
> +    //
> +    // Skip deleted variables.
> +    //
> +    if (  (VarInfo.Header.State != VAR_ADDED)
> +       && (VarInfo.Header.State != (VAR_ADDED &
> VAR_IN_DELETED_TRANSITION)))
> +    {
> +      continue;
> +    }
> +
> +    if (Global != NULL) {
> +      Global->Flags.Auth &= VarInfo.Flags.Auth;
> +    }
> +
> +    VarNum  += 1;
> +    DigSize += (UINT32)(sizeof (VARIABLE_DIGEST)
> +                        + VarInfo.Header.NameSize
> +                        + METADATA_HMAC_SIZE);
> +    if ((DigestBuffer != NULL) && (DigSize > *DigestBufferSize)) {
> +      ASSERT (DigSize <= *DigestBufferSize);
> +      return EFI_BUFFER_TOO_SMALL;
> +    }
> +
> +    if (VarDig != NULL) {
> +      VarDig->Prev       = 0;
> +      VarDig->Next       = 0;
> +      VarDig->State      = VarInfo.Header.State;
> +      VarDig->Attributes = VarInfo.Header.Attributes;
> +      VarDig->DataSize   = (UINT32)VarInfo.Header.DataSize;
> +      VarDig->NameSize   = (UINT16)VarInfo.Header.NameSize;
> +      VarDig->DigestSize = METADATA_HMAC_SIZE;
> +      VarDig->StoreIndex = VarInfo.StoreIndex;
> +
> +      if ((VarInfo.Buffer != NULL) && ((UINTN)VarInfo.Buffer != Global-
> >VariableCache)) {
> +        VarDig->CacheIndex = GET_ADRS (VarInfo.Buffer);
> +      } else {
> +        VarDig->CacheIndex = VAR_INDEX_INVALID;
> +      }
> +
> +      VarDig->Flags.Auth  = VarInfo.Flags.Auth;
> +      VarDig->Flags.Valid = TRUE;
> +
> +      VarIndex = CheckKnownUnprotectedVariable (Global, &VarInfo);
> +      if (VarIndex >= UnprotectedVarIndexMax) {
> +        //
> +        // Check information relating to encryption, if enabled.
> +        //
> +        VarDig->Flags.Encrypted = FALSE;
> +        if ((VarInfo.Header.Data != NULL) && (VarInfo.Header.DataSize > 0)) {
> +          VarInfo.CipherData     = NULL;
> +          VarInfo.CipherDataSize = 0;
> +          VarInfo.PlainData      = NULL;
> +          VarInfo.PlainDataSize  = 0;
> +          Status                 = GetCipherDataInfo (&VarInfo);
> +          if (!EFI_ERROR (Status)) {
> +            //
> +            // Discovered encrypted variable mark variable to be
> +            // encrypted on the next SetVariable() operation
> +            //
> +            VarDig->Flags.Encrypted = PcdGetBool
> (PcdProtectedVariableConfidentiality);
> +          } else {
> +            VarInfo.PlainData        = VarInfo.Header.Data;
> +            VarInfo.PlainDataSize    = (UINT32)VarInfo.Header.DataSize;
> +            VarInfo.CipherDataType   = 0;
> +            VarInfo.CipherHeaderSize = 0;
> +            if (Status == EFI_NOT_FOUND) {
> +              //
> +              // Found variable that is not encrypted mark variable to be
> +              // encrypted on the next SetVariable() operation
> +              //
> +              VarDig->Flags.Encrypted = PcdGetBool
> (PcdProtectedVariableConfidentiality);
> +            }
> +          }
> +        }
> +
> +        //
> +        // Variable is protected
> +        //
> +        VarDig->Flags.Protected = PcdGetBool (PcdProtectedVariableIntegrity);
> +        VarDig->PlainDataSize   = VarInfo.PlainDataSize;
> +
> +        //
> +        // Calculate digest only for protected variable.
> +        //
> +        Status = GetVariableDigest (Global, &VarInfo, VAR_DIG_VALUE (VarDig));
> +        if (EFI_ERROR (Status)) {
> +          return Status;
> +        }
> +
> +        //
> +        // Keep the VarDig in an ordered list.
> +        //
> +        InsertVariableDigestNode (Global, VarDig, CompareVariableDigestInfo);
> +      } else {
> +        VarDig->Flags.Protected = FALSE;
> +        VarDig->Flags.Encrypted = FALSE;
> +        VarDig->PlainDataSize   = VarDig->DataSize;
> +
> +        //
> +        // Make use of VARIABLE_DIGEST->DigestValue to cache HMAC value
> from
> +        // MetaDataHmacVar, which doesn't need a digest value (only protected
> +        // variables need it for integrity check).
> +        //
> +        if ((VarIndex == IndexHmacInDel) || (VarIndex == IndexHmacAdded)) {
> +          if (VarDig->State == VAR_ADDED) {
> +            VarIndex = IndexHmacAdded;
> +          } else {
> +            VarIndex = IndexHmacInDel;
> +          }
> +        }
> +
> +        Global->Unprotected[VarIndex] = VAR_DIG_ADR (VarDig);
> +
> +        if ((VarInfo.Header.Data != NULL) && (VarDig->DataSize <= VarDig-
> >DigestSize)) {
> +          CopyMem (VAR_DIG_VALUE (VarDig), VarInfo.Header.Data, VarDig-
> >DataSize);
> +        }
> +
> +        //
> +        // Don't add the VarDig for MetaDataHmacVar into the linked list now.
> +        // Do it after the HMAC has been validated.
> +        //
> +        if ((VarIndex != IndexHmacInDel) || (VarIndex != IndexHmacAdded)) {
> +          InsertVariableDigestNode (Global, VarDig, CompareVariableDigestInfo);
> +        }
> +      }
> +
> +      VarDig = (VARIABLE_DIGEST *)((UINTN)VarDig + VAR_DIG_END (VarDig));
> +    }
> +  }
> +
> +  if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
> +    return Status;
> +  }
> +
> +  if (DigestBufferSize != NULL) {
> +    *DigestBufferSize = DigSize;
> +  }
> +
> +  if (VariableNumber != NULL) {
> +    *VariableNumber = VarNum;
> +  }
> +
> +  if ((Global == NULL) && (VarInfo.Buffer != NULL)) {
> +    //
> +    // Free Buffer
> +    //
> +    FreePages (VarInfo.Buffer, EFI_SIZE_TO_PAGES (MAX_VARIABLE_SIZE));
> +  }
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +
> +  Perform for protected variable integrity check.
> +
> +  If this initialization failed upon any error, the whole variable services
> +  should not be used.  A system reset might be needed to re-construct NV
> +  variable storage to be the default state.
> +
> +  @param[in]  ContextIn   Pointer to variable service context needed by
> +                          protected variable.
> +
> +  @retval EFI_SUCCESS               Protected variable services are ready.
> +  @retval EFI_INVALID_PARAMETER     If ContextIn == NULL or something
> missing or
> +                                    mismatching in the content in ContextIn.
> +  @retval EFI_COMPROMISED_DATA      If failed to check integrity of protected
> variables.
> +  @retval EFI_OUT_OF_RESOURCES      Fail to allocate enough resource.
> +  @retval EFI_UNSUPPORTED           Unsupported to process protected variable.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +PerformVariableIntegrityCheck (
> +  IN  PROTECTED_VARIABLE_CONTEXT_IN  *ContextIn
> +  )
> +{
> +  EFI_STATUS                 Status;
> +  UINT32                     HobDataSize;
> +  UINT32                     VarNumber;
> +  VOID                       *Buffer;
> +  PROTECTED_VARIABLE_GLOBAL  *Global;
> +  VARIABLE_DIGEST            *DigBuffer;
> +  UINT32                     DigBufferSize;
> +  UINT32                     HmacMetaDataSize;
> +  UINTN                      Index;
> +  BOOLEAN                    PreviousKey;
> +  EFI_HOB_GUID_TYPE          *GuidHob;
> +
> +  if ((ContextIn == NULL) || (ContextIn->GetNextVariableInfo == NULL)) {
> +    ASSERT (ContextIn != NULL);
> +    ASSERT (ContextIn->GetNextVariableInfo != NULL);
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  ContextIn->StructSize = (ContextIn->StructSize == 0) ? sizeof (*ContextIn)
> +                                                       : ContextIn->StructSize;
> +
> +  //
> +  // Enumerate all variables first to collect info for resource allocation.
> +  //
> +  DigBufferSize = 0;
> +  Status        = CollectVariableDigestInfo (
> +                    NULL,
> +                    ContextIn,
> +                    NULL,
> +                    &DigBufferSize,
> +                    &VarNumber
> +                    );
> +  if (EFI_ERROR (Status)) {
> +    ASSERT_EFI_ERROR (Status);
> +    return EFI_VOLUME_CORRUPTED;
> +  }
> +
> +  //
> +  // Allocate buffer for Global. Memory layout:
> +  //
> +  //      Global
> +  //      Digest context
> +  //      Variable Digest List
> +  //      HmacMetaData
> +  //
> +  // To save precious NEM space of processor, variable cache will not be
> +  // allocated at this point until physical memory is ready for use.
> +  //
> +  HmacMetaDataSize = (UINT32)GetMetaDataHmacVarSize (TRUE);
> +  HobDataSize      = sizeof (PROTECTED_VARIABLE_GLOBAL)
> +                     + (UINT32)DIGEST_CONTEXT_SIZE
> +                     + DigBufferSize
> +                     + HmacMetaDataSize;
> +  Buffer = AllocateGlobalBuffer (HobDataSize, FALSE);
> +  if (Buffer == NULL) {
> +    ASSERT (Buffer != NULL);
> +    return EFI_OUT_OF_RESOURCES;
> +  }
> +
> +  Global                = (PROTECTED_VARIABLE_GLOBAL *)((UINTN)Buffer);
> +  Global->DigestContext = GET_ADRS (Global + 1);
> +
> +  if (DigBufferSize > 0) {
> +    DigBuffer = (VARIABLE_DIGEST *)(UINTN)(Global->DigestContext +
> DIGEST_CONTEXT_SIZE);
> +    ZeroMem (DigBuffer, DigBufferSize);
> +  } else {
> +    DigBuffer = NULL;
> +  }
> +
> +  //
> +  // Keep a copy of ContextIn in HOB for later uses.
> +  //
> +  Global->GlobalSelf = GET_ADRS (Global);
> +  Global->ContextIn  = GET_ADRS (ContextIn);
> +
> +  Global->StructVersion =
> PROTECTED_VARIABLE_CONTEXT_OUT_STRUCT_VERSION;
> +  Global->StructSize    = HobDataSize;
> +
> +  Global->VariableNumber  = VarNumber;
> +  Global->VariableDigests = 0;
> +
> +  Global->Flags.Auth       = TRUE;
> +  Global->Flags.WriteInit  = FALSE;
> +  Global->Flags.WriteReady = FALSE;
> +
> +  GuidHob = GetFirstGuidHob (&gEfiAuthenticatedVariableGuid);
> +  if (GuidHob == NULL) {
> +    GuidHob = GetFirstGuidHob (&gEfiVariableGuid);
> +    if (GuidHob != NULL) {
> +      Global->Flags.Auth = FALSE;
> +    }
> +  }
> +
> +  Global->Flags.RecoveryMode = (GuidHob != NULL);
> +
> +  //
> +  // Before physical memory is ready, we cannot cache all variables in the very
> +  // limited NEM space. But we still need to reserve buffer to hold data of
> +  // one variable as well as context for integrity check (HMAC calculation).
> +  //
> +  Global->VariableCacheSize = MAX_VARIABLE_SIZE;
> +  Buffer                    = AllocatePages (EFI_SIZE_TO_PAGES (Global-
> >VariableCacheSize));
> +  if (Buffer == NULL) {
> +    ASSERT (Buffer != NULL);
> +    return EFI_OUT_OF_RESOURCES;
> +  }
> +
> +  Global->VariableCache        = GET_ADRS (Buffer);
> +  Global->LastAccessedVariable = VAR_INDEX_INVALID;
> +
> +  for (Index = 0; Index < UnprotectedVarIndexMax; ++Index) {
> +    Global->Unprotected[Index] = VAR_INDEX_INVALID;
> +  }
> +
> +  //
> +  // Re-enumerate all NV variables and build digest list.
> +  //
> +  Status = CollectVariableDigestInfo (
> +             Global,
> +             ContextIn,
> +             DigBuffer,
> +             &DigBufferSize,
> +             &VarNumber
> +             );
> +  if (EFI_ERROR (Status)) {
> +    ASSERT_EFI_ERROR (Status);
> +    return Status;
> +  }
> +
> +  ASSERT (Global->VariableNumber == VarNumber);
> +
> +  //
> +  // Fix-up number of valid protected variables (i.e. exclude unprotected ones)
> +  //
> +  for (Index = 0; VarNumber != 0 && Index < UnprotectedVarIndexMax; ++Index)
> {
> +    if (Global->Unprotected[Index] != VAR_INDEX_INVALID) {
> +      --VarNumber;
> +    }
> +  }
> +
> +  //
> +  // Get root key and generate HMAC key.
> +  //
> +  PreviousKey = FALSE;
> +  Status      = GetVariableKey ((VOID *)Global->RootKey, sizeof (Global-
> >RootKey));
> +  if (EFI_ERROR (Status)) {
> +    ASSERT (FALSE);
> +    Status = EFI_COMPROMISED_DATA;
> +  }
> +
> +  //
> +  // Derive the MetaDataHmacKey from root key
> +  //
> +  if (!GenerateMetaDataHmacKey (
> +         Global->RootKey,
> +         sizeof (Global->RootKey),
> +         Global->MetaDataHmacKey,
> +         sizeof (Global->MetaDataHmacKey)
> +         ))
> +  {
> +    ASSERT (FALSE);
> +    Status = EFI_COMPROMISED_DATA;
> +  }
> +
> +  //
> +  // Check the integrity of all NV variables, if any.
> +  //
> +  if ((  (Global->Unprotected[IndexHmacAdded] != VAR_INDEX_INVALID)
> +      || (Global->Unprotected[IndexHmacInDel] != VAR_INDEX_INVALID)))
> +  {
> +    //
> +    // Validate the HMAC stored in variable MetaDataHmacVar.
> +    //
> +    Status = VerifyMetaDataHmac (Global);
> +    if (EFI_ERROR (Status)) {
> +      //
> +      // Try again with the previous root key if the latest key failed the HMAC
> validation.
> +      //
> +      Status = GetVariableKey ((VOID *)Global->RootKey, sizeof (Global-
> >RootKey));
> +      if (!EFI_ERROR (Status)) {
> +        //
> +        // Derive the MetaDataHmacKey from previous root key
> +        //
> +        if (GenerateMetaDataHmacKey (
> +              Global->RootKey,
> +              sizeof (Global->RootKey),
> +              Global->MetaDataHmacKey,
> +              sizeof (Global->MetaDataHmacKey)
> +              ) == TRUE)
> +        {
> +          //
> +          // Validate the HMAC stored in variable MetaDataHmacVar.
> +          //
> +          Status = VerifyMetaDataHmac (Global);
> +          if (!EFI_ERROR (Status)) {
> +            Status = EFI_COMPROMISED_DATA;
> +          }
> +        } else {
> +          Status = EFI_COMPROMISED_DATA;
> +        }
> +      }
> +    }
> +  } else if (Global->Flags.RecoveryMode) {
> +    //
> +    // Generate the first version of MetaDataHmacVar.
> +    //
> +    Status = SyncRpmcCounter ();
> +    if (!EFI_ERROR (Status)) {
> +      Status = RefreshVariableMetadataHmac (Global, NULL, NULL);
> +      if (!EFI_ERROR (Status)) {
> +        //
> +        // MetaDataHmacVar is always calculated against Counter2+1. Updating
> +        // RPMCs to match it.
> +        //
> +        (VOID)IncrementMonotonicCounter (RPMC_COUNTER_1);
> +        (VOID)IncrementMonotonicCounter (RPMC_COUNTER_2);
> +      }
> +    }
> +  } else if ((VarNumber > 0) && !Global->Flags.RecoveryMode) {
> +    //
> +    // There's no MetaDataHmacVar found for protected variables. Suppose
> +    // the variable storage is compromised.
> +    //
> +    Status = EFI_COMPROMISED_DATA;
> +  }
> +
> +  if (EFI_ERROR (Status)) {
> +    //
> +    // The integrity of variables have been compromised. The platform has to do
> +    // something to recover the variable store. But the boot should not go on
> +    // anyway this time.
> +    //
> +    DEBUG ((DEBUG_ERROR, "%a: %d Integrity check Status = %r\n",
> __FUNCTION__, __LINE__, Status));
> +    REPORT_STATUS_CODE (
> +      EFI_ERROR_CODE | EFI_ERROR_UNRECOVERED,
> +      (PcdGet32 (PcdStatusCodeVariableIntegrity) |
> EFI_SW_PEI_PC_RECOVERY_BEGIN)
> +      );
> + #if defined (EDKII_UNIT_TEST_FRAMEWORK_ENABLED) // Avoid test
> malfunctioning.
> +    return Status;
> + #else
> +    ASSERT_EFI_ERROR (Status);
> +    CpuDeadLoop ();
> + #endif
> +  }
> +
> +  //
> +  // Everything's OK.
> +  //
> +  REPORT_STATUS_CODE (
> +    EFI_PROGRESS_CODE,
> +    PcdGet32 (PcdStatusCodeVariableIntegrity)
> +    );
> +
> +  if (GET_BUFR (Global->VariableCacheSize) != NULL) {
> +    //
> +    // Free Buffer
> +    //
> +    FreePages (Buffer, EFI_SIZE_TO_PAGES (Global->VariableCacheSize));
> +  }
> +
> +  //
> +  // Keep the valid MetaDataHmacVar in the list.
> +  //
> +  for (Index = 0; Index < IndexPlatformVar; ++Index) {
> +    if (  (Global->Unprotected[Index] != VAR_INDEX_INVALID)
> +       && VAR_DIG_PTR (Global->Unprotected[Index])->Flags.Valid)
> +    {
> +      InsertVariableDigestNode (
> +        Global,
> +        VAR_DIG_PTR (Global->Unprotected[Index]),
> +        NULL
> +        );
> +    }
> +  }
> +
> +  //
> +  // Restore the key to the latest one.
> +  //
> +  if (PreviousKey) {
> +    Status = GetVariableKey ((VOID *)Global->RootKey, sizeof (Global->RootKey));
> +    ASSERT_EFI_ERROR (Status);
> +
> +    //
> +    // Derive the MetaDataHmacKey from root key
> +    //
> +    if (!GenerateMetaDataHmacKey (
> +           Global->RootKey,
> +           sizeof (Global->RootKey),
> +           Global->MetaDataHmacKey,
> +           sizeof (Global->MetaDataHmacKey)
> +           ))
> +    {
> +      ASSERT (FALSE);
> +    }
> +  }
> +
> +  //
> +  // Make sure that the RPMC counter is in-sync.
> +  //
> +  Status = SyncRpmcCounter ();
> +
> +  //
> +  // Setup a hook to migrate data in Global once physical memory is ready.
> +  //
> +  Status = PeiServicesNotifyPpi (mPostMemNotifyList);
> +
> +  return Status;
> +}
> +
> +/**
> +
> +  Initialization for protected variable services.
> +
> +  If the variable store is available than perform integrity check.
> +  Otherwise, defer integrity check until variable store is available.
> +
> +
> +  @param[in]  ContextIn   Pointer to variable service context needed by
> +                          protected variable.
> +
> +  @retval EFI_SUCCESS               Protected variable services are ready.
> +  @retval EFI_INVALID_PARAMETER     If ContextIn == NULL or something
> missing or
> +                                    mismatching in the content in ContextIn.
> +  @retval EFI_COMPROMISED_DATA      If failed to check integrity of protected
> variables.
> +  @retval EFI_OUT_OF_RESOURCES      Fail to allocate enough resource.
> +  @retval EFI_UNSUPPORTED           Unsupported to process protected variable.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +ProtectedVariableLibInitialize (
> +  IN  PROTECTED_VARIABLE_CONTEXT_IN  *ContextIn
> +  )
> +{
> +  EFI_STATUS               Status;
> +  VOID                     *ContextInHob;
> +  PROTECTED_VARIABLE_INFO  VarInfo;
> +
> +  if ((ContextIn == NULL) || (ContextIn->GetNextVariableInfo == NULL)) {
> +    ASSERT (ContextIn != NULL);
> +    ASSERT (ContextIn->GetNextVariableInfo != NULL);
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  //
> +  // Keep a copy of ContextIn in HOB for later uses.
> +  //
> +  ContextIn->StructSize = (ContextIn->StructSize == 0) ? sizeof (*ContextIn)
> +                                                       : ContextIn->StructSize;
> +  ContextInHob = BuildGuidHob (&gEdkiiProtectedVariableContextGuid,
> ContextIn->StructSize);
> +  CopyMem (ContextInHob, ContextIn, ContextIn->StructSize);
> +
> +  //
> +  // Discover if Variable Store Info Hob has been published by platform driver.
> +  // It contains information regards to HOB or NV Variable Store availability
> +  //
> +  ZeroMem ((VOID *)&VarInfo.Header, sizeof (VarInfo.Header));
> +  VarInfo.StoreIndex = VAR_INDEX_INVALID;
> +  VarInfo.Buffer     = AllocatePages (EFI_SIZE_TO_PAGES (MAX_VARIABLE_SIZE));
> +  if (VarInfo.Buffer == NULL) {
> +    ASSERT (VarInfo.Buffer != NULL);
> +    return EFI_OUT_OF_RESOURCES;
> +  }
> +
> +  FreePages (VarInfo.Buffer, EFI_SIZE_TO_PAGES ((MAX_VARIABLE_SIZE)));
> +
> +  Status = ContextIn->GetNextVariableInfo (&VarInfo);
> +  if (EFI_ERROR (Status)) {
> +    //
> +    // Register for platform driver callback when Variable Store is available.
> +    //
> +    DEBUG ((DEBUG_INFO, "Variable Store is not available. Register for a
> integrity check callback\n"));
> +    Status = PeiServicesNotifyPpi (mVariableStoreNotifyList);
> +    return Status;
> +  }
> +
> +  //
> +  // HOB Variable store is not available
> +  // Assume NV Variable store is available instead
> +  // Perform integrity check on NV Variable Store
> +  //
> +  DEBUG ((DEBUG_INFO, "NV Variable Store is available. Perform integrity
> check\n"));
> +  Status = PerformVariableIntegrityCheck (ContextInHob);
> +  return Status;
> +}
> +
> +/**
> +
> +  Prepare for variable update.
> +
> +  (Not suppported in PEI phase.)
> +
> +  @retval EFI_UNSUPPORTED         Updating variable is not supported.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +ProtectedVariableLibWriteInit (
> +  VOID
> +  )
> +{
> +  ASSERT (FALSE);
> +  return EFI_UNSUPPORTED;
> +}
> +
> +/**
> +
> +  Update a variable with protection provided by this library.
> +
> +  Not supported in PEI phase.
> +
> +  @param[in,out]  CurrVariable        Variable to be updated. It's NULL if
> +                                      adding a new variable.
> +  @param[in]      CurrVariableInDel   In-delete-transition copy of updating
> variable.
> +  @param[in,out]  NewVariable         Buffer of new variable data.
> +                                      Buffer of "MetaDataHmacVar" and new
> +                                      variable (encrypted).
> +  @param[in,out]  NewVariableSize     Size of NewVariable.
> +                                      Size of (encrypted) NewVariable and
> +                                      "MetaDataHmacVar".
> +
> +  @retval EFI_UNSUPPORTED         Not support updating variable in PEI phase.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +ProtectedVariableLibUpdate (
> +  IN  OUT VARIABLE_HEADER  *CurrVariable,
> +  IN      VARIABLE_HEADER  *CurrVariableInDel,
> +  IN  OUT VARIABLE_HEADER  *NewVariable,
> +  IN  OUT UINTN            *NewVariableSize
> +  )
> +{
> +  ASSERT (FALSE);
> +  return EFI_UNSUPPORTED;
> +}
> +
> +/**
> +
> +  Finalize a variable updating after it's written to NV variable storage
> +  successfully.
> +
> +  @param[in]      NewVariable       Buffer of new variables and
> MetaDataHmacVar.
> +  @param[in]      VariableSize      Size of buffer pointed by NewVariable.
> +  @param[in]      StoreIndex        StoreIndex to NV variable storage from where
> the new
> +                                    variable and MetaDataHmacVar have been written.
> +
> +  @retval EFI_UNSUPPORTED           Not support updating variable in PEI phase.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +ProtectedVariableLibWriteFinal (
> +  IN  VARIABLE_HEADER  *NewVariable,
> +  IN  UINTN            VariableSize,
> +  IN  UINT64           StoreIndex
> +  )
> +{
> +  ASSERT (FALSE);
> +  return EFI_UNSUPPORTED;
> +}
> diff --git a/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableSmm.c
> b/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableSmm.c
> new file mode 100644
> index 000000000000..8e964f4cd28d
> --- /dev/null
> +++ b/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableSmm.c
> @@ -0,0 +1,209 @@
> +/** @file
> +  Implemention of ProtectedVariableLib for SMM variable services.
> +
> +Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
> +SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#include <PiDxe.h>
> +#include <Uefi.h>
> +
> +#include "Guid/SmmVariableCommon.h"
> +
> +#include "Library/MmServicesTableLib.h"
> +#include "Library/MemoryAllocationLib.h"
> +
> +#include "ProtectedVariableInternal.h"
> +
> +PROTECTED_VARIABLE_CONTEXT_IN  mVariableContextIn = {
> +  PROTECTED_VARIABLE_CONTEXT_IN_STRUCT_VERSION,
> +  sizeof (PROTECTED_VARIABLE_CONTEXT_IN),
> +  0,
> +  FromSmmModule,
> +  NULL,
> +  NULL,
> +  NULL,
> +  NULL,
> +  NULL
> +};
> +
> +PROTECTED_VARIABLE_GLOBAL  mProtectedVariableGlobal = {
> +  PROTECTED_VARIABLE_CONTEXT_OUT_STRUCT_VERSION,
> +  sizeof (PROTECTED_VARIABLE_GLOBAL),
> +  { 0 },
> +  { 0 },
> +  0,
> +  0,
> +  0,
> +  0,
> +  0,
> +  0,
> +  { 0,                                          0,  0 },
> +  0,
> +  0,
> +  { 0,                                          0,  0, 0}
> +};
> +
> +/**
> +
> +  Callback function to call variable write.
> +
> +  @param[in]  Protocol    Not Used.
> +  @param[in]  Interface   Not Used.
> +  @param[in]  Handle      Not Used.
> +
> +  @retval EFI_SUCCESS     Protected variable write successful.
> +  @retval others          Protected variable write failed.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +VariableWriteProtocolCallback (
> +  IN CONST EFI_GUID  *Protocol,
> +  IN VOID            *Interface,
> +  IN EFI_HANDLE      Handle
> +  )
> +{
> +  EFI_STATUS  Status;
> +
> +  Status = ProtectedVariableLibWriteInit ();
> +  ASSERT_EFI_ERROR (Status);
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +
> +  Initialization for protected variable services.
> +
> +  If this initialization failed upon any error, the whole variable services
> +  should not be used.  A system reset might be needed to re-construct NV
> +  variable storage to be the default state.
> +
> +  @param[in]  ContextIn   Pointer to variable service context needed by
> +                          protected variable.
> +
> +  @retval EFI_SUCCESS               Protected variable services are ready.
> +  @retval EFI_INVALID_PARAMETER     If ContextIn == NULL or something
> missing or
> +                                    mismatching in the content in ContextIn.
> +  @retval EFI_COMPROMISED_DATA      If failed to check integrity of protected
> variables.
> +  @retval EFI_OUT_OF_RESOURCES      Fail to allocate enough resource.
> +  @retval EFI_UNSUPPORTED           Unsupported to process protected variable.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +ProtectedVariableLibInitialize (
> +  IN  PROTECTED_VARIABLE_CONTEXT_IN  *ContextIn
> +  )
> +{
> +  EFI_STATUS                     Status;
> +  PROTECTED_VARIABLE_CONTEXT_IN  *ProtectedVarContext;
> +  PROTECTED_VARIABLE_GLOBAL      *OldGlobal;
> +  PROTECTED_VARIABLE_GLOBAL      *NewGlobal;
> +  VARIABLE_DIGEST                *VarDig;
> +  VARIABLE_DIGEST                *NewVarDig;
> +  EFI_PHYSICAL_ADDRESS           NewCacheIndex;
> +  UINTN                          VarSize;
> +  UNPROTECTED_VARIABLE_INDEX     Index;
> +
> +  if (  (ContextIn == NULL)
> +     || (ContextIn->StructVersion !=
> PROTECTED_VARIABLE_CONTEXT_IN_STRUCT_VERSION)
> +     || (ContextIn->StructSize != sizeof (PROTECTED_VARIABLE_CONTEXT_IN))
> +     || (ContextIn->GetVariableInfo == NULL)
> +     || (ContextIn->GetNextVariableInfo == NULL)
> +     || (ContextIn->UpdateVariableStore == NULL)
> +     || (ContextIn->UpdateVariable == NULL))
> +  {
> +    ASSERT (ContextIn != NULL);
> +    ASSERT (ContextIn->StructVersion ==
> PROTECTED_VARIABLE_CONTEXT_IN_STRUCT_VERSION);
> +    ASSERT (ContextIn->StructSize == sizeof
> (PROTECTED_VARIABLE_CONTEXT_IN));
> +    ASSERT (ContextIn->GetVariableInfo != NULL);
> +    ASSERT (ContextIn->GetNextVariableInfo != NULL);
> +    ASSERT (ContextIn->UpdateVariableStore != NULL);
> +    ASSERT (ContextIn->UpdateVariable != NULL);
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  GetProtectedVariableGlobal (&NewGlobal);
> +  ProtectedVarContext = GET_CNTX (NewGlobal);
> +  CopyMem (ProtectedVarContext, ContextIn, sizeof (mVariableContextIn));
> +  ProtectedVarContext->VariableServiceUser = FromSmmModule;
> +
> +  //
> +  // Get root key and HMAC key from HOB created by PEI variable driver.
> +  //
> +  Status = GetProtectedVariableGlobalFromHob (&OldGlobal);
> +  ASSERT_EFI_ERROR (Status);
> +
> +  CopyMem ((VOID *)NewGlobal, (CONST VOID *)OldGlobal, sizeof
> (*OldGlobal));
> +
> +  //
> +  // The keys must not be available outside SMM.
> +  //
> +  if (ProtectedVarContext->VariableServiceUser == FromSmmModule) {
> +    ZeroMem (OldGlobal->RootKey, sizeof (OldGlobal->RootKey));
> +    ZeroMem (OldGlobal->MetaDataHmacKey, sizeof (OldGlobal-
> >MetaDataHmacKey));
> +  }
> +
> +  NewGlobal->Flags.WriteInit      = FALSE;
> +  NewGlobal->Flags.WriteReady     = FALSE;
> +  NewGlobal->LastAccessedVariable = 0;
> +  NewGlobal->VariableCache        = GET_ADRS (AllocateZeroPool
> (MAX_VARIABLE_SIZE));
> +  NewGlobal->DigestContext        = GET_ADRS (AllocateZeroPool
> (DIGEST_CONTEXT_SIZE));
> +  if (NewGlobal->DigestContext == 0) {
> +    return EFI_OUT_OF_RESOURCES;
> +  }
> +
> +  //
> +  // Copy over variable from HOB to SMM memory
> +  //
> +  NewGlobal->VariableDigests = 0;
> +  VarDig                     = VAR_DIG_PTR (OldGlobal->VariableDigests);
> +  while (VarDig != NULL) {
> +    //
> +    // Allocate new Var Digest in SMM memory
> +    //
> +    NewVarDig = (VARIABLE_DIGEST *)AllocateZeroPool (
> +                                     sizeof (VARIABLE_DIGEST) + VarDig->NameSize +
> METADATA_HMAC_SIZE
> +                                     );
> +    if (NewVarDig == NULL) {
> +      ASSERT (NewVarDig != NULL);
> +      return EFI_OUT_OF_RESOURCES;
> +    }
> +
> +    CopyMem (NewVarDig, VarDig, sizeof (VARIABLE_DIGEST));
> +    NewVarDig->Prev = 0;
> +    NewVarDig->Next = 0;
> +
> +    CopyMem (VAR_DIG_NAME (NewVarDig), VAR_DIG_NAME (VarDig), VarDig-
> >NameSize);
> +    CopyMem (VAR_DIG_VALUE (NewVarDig), VAR_DIG_VALUE (VarDig),
> VarDig->DigestSize);
> +
> +    VarSize  =  VARIABLE_HEADER_SIZE (NewGlobal->Flags.Auth);
> +    VarSize += VarDig->NameSize + GET_PAD_SIZE (VarDig->NameSize);
> +    VarSize += VarDig->DataSize + GET_PAD_SIZE (VarDig->DataSize);
> +    VarSize  = HEADER_ALIGN (VarSize);
> +
> +    NewCacheIndex = GET_ADRS (AllocateZeroPool (VarSize));
> +    if (GET_BUFR (NewCacheIndex) == NULL) {
> +      return EFI_OUT_OF_RESOURCES;
> +    }
> +
> +    CopyMem (GET_BUFR (NewCacheIndex), GET_BUFR (VarDig->CacheIndex),
> VarSize);
> +    NewVarDig->CacheIndex     = NewCacheIndex;
> +    NewVarDig->Flags.Freeable = TRUE;
> +
> +    for (Index = 0; Index < UnprotectedVarIndexMax; ++Index) {
> +      if (OldGlobal->Unprotected[Index] == VAR_DIG_ADR (VarDig)) {
> +        NewGlobal->Unprotected[Index] = VAR_DIG_ADR (NewVarDig);
> +      }
> +    }
> +
> +    InsertVariableDigestNode (NewGlobal, NewVarDig, NULL);
> +
> +    VarDig = VAR_DIG_NEXT (VarDig);
> +  }
> +
> +  return Status;
> +}
> diff --git
> a/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableSmmDxeCommon
> .c
> b/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableSmmDxeCommon
> .c
> new file mode 100644
> index 000000000000..8472fc8a33c7
> --- /dev/null
> +++
> b/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableSmmDxeCommon
> .c
> @@ -0,0 +1,967 @@
> +/** @file
> +  Implemention of ProtectedVariableLib for SMM variable services.
> +
> +Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
> +SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#include <PiDxe.h>
> +#include <Uefi.h>
> +
> +#include "Guid/SmmVariableCommon.h"
> +
> +#include "Library/MmServicesTableLib.h"
> +#include "Library/MemoryAllocationLib.h"
> +#include <Library/HobLib.h>
> +
> +#include "ProtectedVariableInternal.h"
> +
> +/**
> +
> +  Get context and/or global data structure used to process protected variable.
> +
> +  @param[out]   Global      Pointer to global configuration data.
> +
> +  @retval EFI_SUCCESS         Get requested structure successfully.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +GetProtectedVariableGlobal (
> +  OUT PROTECTED_VARIABLE_GLOBAL  **Global OPTIONAL
> +  )
> +{
> +  if (Global != NULL) {
> +    mProtectedVariableGlobal.ContextIn = GET_ADRS (&mVariableContextIn);
> +    *Global                            = &mProtectedVariableGlobal;
> +  }
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +  Encrypt given variable data and generate new HMAC value against it.
> +
> +  @param[in]      Global          Pointer to global configuration data.
> +  @param[in,out]  NewVarInfo      Pointer to buffer of new variable data.
> +  @param[in,out]  NewVarDig       Pointer to buffer of new variable digest.
> +
> +  @retval EFI_SUCCESS           No error occurred during the encryption and HMC
> calculation.
> +  @retval EFI_ABORTED           Failed to do HMC calculation.
> +  @return EFI_OUT_OF_RESOURCES  Not enough resource to calculate HMC
> value.
> +  @return EFI_NOT_FOUND         The MetaDataHmacVar was not found in
> storage.
> +
> +**/
> +STATIC
> +EFI_STATUS
> +UpdateVariableInternal (
> +  IN      PROTECTED_VARIABLE_GLOBAL  *Global,
> +  IN  OUT PROTECTED_VARIABLE_INFO    *NewVarInfo,
> +  IN  OUT VARIABLE_DIGEST            *NewVarDig
> +  )
> +{
> +  EFI_STATUS               Status;
> +  PROTECTED_VARIABLE_INFO  CachedVarInfo;
> +  VOID                     *Buffer;
> +  UINTN                    VarSize;
> +
> +  if ((NewVarInfo == NULL) || (NewVarDig == NULL)) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  //
> +  // If Add or update variable, encrypt new data first.
> +  //
> +  if (NewVarInfo->Buffer != NULL) {
> +    Status = EFI_UNSUPPORTED;
> +
> +    if (NewVarDig->Flags.Encrypted) {
> +      NewVarInfo->PlainData          = NULL;
> +      NewVarInfo->PlainDataSize      = 0;
> +      NewVarInfo->CipherData         = NULL;
> +      NewVarInfo->CipherDataSize     = 0;
> +      NewVarInfo->Key                = Global->RootKey;
> +      NewVarInfo->KeySize            = sizeof (Global->RootKey);
> +      NewVarInfo->Header.Attributes &= (~EFI_VARIABLE_APPEND_WRITE);
> +      Status                         = EncryptVariable (NewVarInfo);
> +      if (!EFI_ERROR (Status)) {
> +        //
> +        // Update new data size in variable header.
> +        //
> +        SET_VARIABLE_DATA_SIZE (NewVarInfo, NewVarInfo->CipherDataSize);
> +      } else if (Status != EFI_UNSUPPORTED) {
> +        ASSERT (FALSE);
> +        return Status;
> +      }
> +    }
> +
> +    if (Status == EFI_UNSUPPORTED) {
> +      NewVarInfo->CipherData     = NewVarInfo->Header.Data;
> +      NewVarInfo->CipherDataSize = (UINT32)NewVarInfo->Header.DataSize;
> +      NewVarInfo->PlainData      = NULL;
> +      NewVarInfo->PlainDataSize  = 0;
> +    }
> +  } else {
> +    NewVarInfo->CipherData     = NULL;
> +    NewVarInfo->CipherDataSize = 0;
> +    NewVarInfo->PlainData      = NULL;
> +    NewVarInfo->PlainDataSize  = 0;
> +  }
> +
> +  if (NewVarDig->CacheIndex != 0) {
> +    //
> +    // Update the cached copy.
> +    //
> +    ZeroMem ((VOID *)&CachedVarInfo, sizeof (CachedVarInfo));
> +    CachedVarInfo.Buffer     = GET_BUFR (NewVarDig->CacheIndex);
> +    CachedVarInfo.StoreIndex = VAR_INDEX_INVALID;
> +    CachedVarInfo.Flags.Auth = NewVarInfo->Flags.Auth;
> +
> +    Status = GET_CNTX (Global)->GetVariableInfo (&CachedVarInfo);
> +    ASSERT_EFI_ERROR (Status);
> +
> +    if ((CachedVarInfo.Header.DataSize != 0) && (NewVarInfo->CipherDataSize >
> CachedVarInfo.Header.DataSize)) {
> +      //
> +      // allocate new VarInfo buffer that is of greater CipherDataSize
> +      //
> +      VarSize  =  VARIABLE_HEADER_SIZE (NewVarDig->Flags.Auth);
> +      VarSize += NewVarInfo->Header.NameSize + GET_PAD_SIZE (NewVarInfo-
> >Header.NameSize);
> +      VarSize += NewVarInfo->CipherDataSize + GET_PAD_SIZE (NewVarInfo-
> >CipherDataSize);
> +      VarSize  = HEADER_ALIGN (VarSize);
> +      Buffer   = AllocateZeroPool (VarSize);
> +      if (Buffer != NULL) {
> +        VarSize  =  VARIABLE_HEADER_SIZE (NewVarDig->Flags.Auth);
> +        VarSize += CachedVarInfo.Header.NameSize + GET_PAD_SIZE
> (CachedVarInfo.Header.NameSize);
> +        VarSize += CachedVarInfo.Header.DataSize + GET_PAD_SIZE
> (CachedVarInfo.DataSize);
> +        VarSize  = HEADER_ALIGN (VarSize);
> +
> +        CopyMem (
> +          Buffer,
> +          CachedVarInfo.Buffer,
> +          VarSize
> +          );
> +
> +        FreePool (CachedVarInfo.Buffer);
> +
> +        //
> +        // Update the cached copy.
> +        //
> +        ZeroMem ((VOID *)&CachedVarInfo, sizeof (CachedVarInfo));
> +        CachedVarInfo.Buffer     = Buffer;
> +        CachedVarInfo.StoreIndex = VAR_INDEX_INVALID;
> +        CachedVarInfo.Flags.Auth = NewVarInfo->Flags.Auth;
> +        Status                   = GET_CNTX (Global)->GetVariableInfo (&CachedVarInfo);
> +        ASSERT_EFI_ERROR (Status);
> +        NewVarDig->CacheIndex = (EFI_PHYSICAL_ADDRESS)(UINTN)Buffer;
> +      }
> +    }
> +
> +    CopyMem (
> +      CachedVarInfo.Header.Data,
> +      NewVarInfo->CipherData,
> +      NewVarInfo->CipherDataSize
> +      );
> +    SET_VARIABLE_DATA_SIZE (&CachedVarInfo, NewVarInfo->CipherDataSize);
> +
> +    NewVarDig->State    = VAR_ADDED;
> +    NewVarDig->DataSize = NewVarInfo->CipherDataSize;
> +
> +    if (NewVarInfo->PlainDataSize > 0) {
> +      NewVarDig->PlainDataSize = NewVarInfo->PlainDataSize;
> +    } else {
> +      NewVarDig->PlainDataSize = NewVarDig->DataSize;
> +    }
> +
> +    //
> +    // (Re-)Calculate the hash of the variable.
> +    //
> +    if (NewVarDig->Flags.Protected) {
> +      GetVariableDigest (Global, NewVarInfo, VAR_DIG_VALUE (NewVarDig));
> +    }
> +  }
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +
> +  Fix state of MetaDataHmacVar on NV variable storage, if there's failure at
> +  last boot during updating variable.
> +
> +  This must be done before the first writing of variable in current boot,
> +  including storage reclaim.
> +
> +  @retval EFI_UNSUPPORTED        Updating NV variable storage is not
> supported.
> +  @retval EFI_OUT_OF_RESOURCES   Not enough resource to complete the
> operation.
> +  @retval EFI_SUCCESS            Variable store was successfully updated.
> +
> +**/
> +EFI_STATUS
> +FixupHmacVariable (
> +  VOID
> +  )
> +{
> +  EFI_STATUS                     Status;
> +  PROTECTED_VARIABLE_INFO        HmacVarInfo;
> +  PROTECTED_VARIABLE_GLOBAL      *Global;
> +  PROTECTED_VARIABLE_CONTEXT_IN  *ContextIn;
> +  VARIABLE_DIGEST                *VarDig;
> +  UINTN                          Index;
> +
> +  Status = GetProtectedVariableGlobal (&Global);
> +  ASSERT_EFI_ERROR (Status);
> +
> +  if (Global->Flags.WriteReady) {
> +    return EFI_SUCCESS;
> +  }
> +
> +  ContextIn = GET_CNTX (Global);
> +
> +  //
> +  // Delete invalid MetaDataHmacVar.
> +  //
> +  for (Index = 0; Index <= IndexHmacAdded; ++Index) {
> +    if (Global->Unprotected[Index] == VAR_INDEX_INVALID) {
> +      continue;
> +    }
> +
> +    VarDig = VAR_DIG_PTR (Global->Unprotected[Index]);
> +    if (VarDig->Flags.Valid) {
> +      continue;
> +    }
> +
> +    ZeroMem ((VOID *)&HmacVarInfo, sizeof (HmacVarInfo));
> +    HmacVarInfo.StoreIndex = VarDig->StoreIndex;
> +    HmacVarInfo.Flags.Auth = VarDig->Flags.Auth;
> +
> +    Status = ContextIn->GetVariableInfo (&HmacVarInfo);
> +    if (!EFI_ERROR (Status) && (HmacVarInfo.Buffer != NULL)) {
> +      HmacVarInfo.Buffer->State &= VAR_DELETED;
> +      Status                     = ContextIn->UpdateVariableStore (
> +                                                &HmacVarInfo,
> +                                                OFFSET_OF (VARIABLE_HEADER, State),
> +                                                sizeof (HmacVarInfo.Buffer->State),
> +                                                &HmacVarInfo.Buffer->State
> +                                                );
> +      if (EFI_ERROR (Status)) {
> +        ASSERT_EFI_ERROR (Status);
> +        return Status;
> +      }
> +    }
> +
> +    //
> +    // Release the resource and update related states.
> +    //
> +    VarDig->State &= VAR_DELETED;
> +    RemoveVariableDigestNode (Global, VarDig, FALSE);
> +    Global->Unprotected[Index] = VAR_INDEX_INVALID;
> +  }
> +
> +  //
> +  // There should be no MetaDataHmacVar if in variable storage recovery mode.
> +  //
> +  if (Global->Flags.RecoveryMode) {
> +    ASSERT (Global->Unprotected[IndexHmacAdded] == VAR_INDEX_INVALID);
> +    ASSERT (Global->Unprotected[IndexHmacInDel] == VAR_INDEX_INVALID);
> +  }
> +
> +  Global->Flags.WriteReady = TRUE;
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +
> +  Prepare for variable update.
> +
> +  This is needed only once during current boot to mitigate replay attack. Its
> +  major job is to advance RPMC (Replay Protected Monotonic Counter).
> +
> +  @retval EFI_SUCCESS             Variable is ready to update hereafter.
> +  @retval EFI_UNSUPPORTED         Updating variable is not supported.
> +  @retval EFI_DEVICE_ERROR        Error in advancing RPMC.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +ProtectedVariableLibWriteInit (
> +  VOID
> +  )
> +{
> +  EFI_STATUS                     Status;
> +  PROTECTED_VARIABLE_GLOBAL      *Global;
> +  PROTECTED_VARIABLE_CONTEXT_IN  *ContextIn;
> +  PROTECTED_VARIABLE_INFO        VarInfo;
> +
> +  (VOID)GetProtectedVariableGlobal (&Global);
> +  ContextIn = GET_CNTX (Global);
> +
> +  //
> +  // HmacVarInfo should be here
> +  //
> +  if (Global->Flags.RecoveryMode) {
> +    //
> +    // Flush default variables to variable storage if in variable recovery mode.
> +    //
> +    Status = ContextIn->UpdateVariableStore (NULL, 0, (UINT32)-1, NULL);
> +    if (EFI_ERROR (Status)) {
> +      ASSERT_EFI_ERROR (Status);
> +      return Status;
> +    }
> +  } else {
> +    ContextIn = GET_CNTX (Global);
> +
> +    //
> +    // Fix any wrong MetaDataHmacVar information before adding new one.
> +    //
> +    Status = FixupHmacVariable ();
> +    if (EFI_ERROR (Status)) {
> +      ASSERT_EFI_ERROR (Status);
> +      return Status;
> +    }
> +
> +    if (!Global->Flags.WriteReady) {
> +      return EFI_NOT_READY;
> +    }
> +
> +    //
> +    // Refresh MetaDataHmacVar with RPMC2 by 1 in each boot before any
> variable
> +    // update,  by deleting (attr == 0 && datasize == 0) the old one.
> +    //
> +    ZeroMem (&VarInfo, sizeof (PROTECTED_VARIABLE_INFO));  // Zero attr &
> datasize
> +
> +    VarInfo.Flags.Auth          = Global->Flags.Auth;
> +    VarInfo.Header.VariableName = METADATA_HMAC_VARIABLE_NAME;
> +    VarInfo.Header.NameSize     = METADATA_HMAC_VARIABLE_NAME_SIZE;
> +    VarInfo.Header.VendorGuid   = &METADATA_HMAC_VARIABLE_GUID;
> +
> +    //
> +    // Pretend to delete MetaDataHmacVar.
> +    //
> +    Status = ContextIn->UpdateVariable (&VarInfo.Header);
> +    if (EFI_ERROR (Status)) {
> +      ASSERT_EFI_ERROR (Status);
> +      return Status;
> +    }
> +  }
> +
> +  mProtectedVariableGlobal.Flags.WriteInit = TRUE;
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +
> +  Update a variable with protection provided by this library.
> +
> +  If variable encryption is employed, the new variable data will be encrypted
> +  before being written to NV variable storage.
> +
> +  A special variable, called "MetaDataHmacVar", will always be updated along
> +  with variable being updated to reflect the changes (HMAC value) of all
> +  protected valid variables. The only exceptions, currently, is variable
> +  variable "VarErrorLog".
> +
> +  The buffer passed by NewVariable must be double of maximum variable size,
> +  which allows to pass the "MetaDataHmacVar" back to caller along with
> encrypted
> +  new variable data, if any. This can make sure the new variable data and
> +  "MetaDataHmacVar" can be written at almost the same time to reduce the
> chance
> +  of compromising the integrity.
> +
> +  If *NewVariableSize is zero, it means to delete variable passed by CurrVariable
> +  and/or CurrVariableInDel. "MetaDataHmacVar" will be updated as well in such
> +  case because of less variables in storage. NewVariable should be always
> passed
> +  in to convey new "MetaDataHmacVar" back.
> +
> +  @param[in,out]  CurrVariable        Variable to be updated. It's NULL if
> +                                      adding a new variable.
> +  @param[in]      CurrVariableInDel   In-delete-transition copy of updating
> variable.
> +  @param[in,out]  NewVariable         Buffer of new variable data.
> +                                      Buffer of "MetaDataHmacVar" and new
> +                                      variable (encrypted).
> +  @param[in,out]  NewVariableSize     Size of NewVariable.
> +                                      Size of (encrypted) NewVariable and
> +                                      "MetaDataHmacVar".
> +
> +  @retval EFI_SUCCESS             The variable is updated with protection
> successfully.
> +  @retval EFI_INVALID_PARAMETER   NewVariable is NULL.
> +  @retval EFI_NOT_FOUND           Information missing to finish the operation.
> +  @retval EFI_ABORTED             Failed to encrypt variable or calculate HMAC.
> +  @retval EFI_NOT_READY           The RPMC device is not yet initialized.
> +  @retval EFI_DEVICE_ERROR        The RPMC device has error in updating.
> +  @retval EFI_ACCESS_DENIED       The given variable is not allowed to update.
> +                                  Currently this only happens on updating
> +                                  "MetaDataHmacVar" from code outside of this
> +                                  library.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +ProtectedVariableLibUpdate (
> +  IN  OUT VARIABLE_HEADER  *CurrVariable,
> +  IN      VARIABLE_HEADER  *CurrVariableInDel,
> +  IN  OUT VARIABLE_HEADER  *NewVariable,
> +  IN  OUT UINTN            *NewVariableSize
> +  )
> +{
> +  EFI_STATUS                     Status;
> +  PROTECTED_VARIABLE_CONTEXT_IN  *ContextIn;
> +  PROTECTED_VARIABLE_GLOBAL      *Global;
> +  PROTECTED_VARIABLE_INFO        VarInfo;
> +  VARIABLE_DIGEST                *VarDig;
> +  VARIABLE_DIGEST                *CurrVarDig;
> +  VARIABLE_DIGEST                *NewVarDig;
> +  PROTECTED_VARIABLE_INFO        NewVarInfo;
> +  PROTECTED_VARIABLE_INFO        NewHmacVarInfo;
> +  UINTN                          VarSize;
> +  UINT64                         UnprotectedVarIndex;
> +
> +  //
> +  // Advance RPMC
> +  //
> +  Status = IncrementMonotonicCounter (RPMC_COUNTER_1);
> +  ASSERT_EFI_ERROR (Status);
> +
> +  //
> +  // Buffer for new variable is always needed, even this function is called to
> +  // delete an existing one, because we need to pass the MetaDataHmacVar
> back
> +  // which will be updated upon each variable addition or deletion.
> +  //
> +  if ((NewVariable == NULL) || (NewVariableSize == NULL)) {
> +    ASSERT (NewVariable != NULL);
> +    ASSERT (NewVariableSize != NULL);
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  Status = GetProtectedVariableGlobal (&Global);
> +  ASSERT_EFI_ERROR (Status);
> +  ContextIn = GET_CNTX (Global);
> +
> +  if (!Global->Flags.WriteReady && !Global->Flags.WriteInit) {
> +    return EFI_NOT_READY;
> +  }
> +
> +  VarSize             = 0;
> +  CurrVarDig          = NULL;
> +  NewVarDig           = NULL;
> +  UnprotectedVarIndex = VAR_INDEX_INVALID;
> +
> +  //
> +  // Check existing copy of the same variable.
> +  //
> +  if (CurrVariable != NULL) {
> +    //
> +    // Find local cached copy, if possible.
> +    //
> +    ZeroMem (&VarInfo, sizeof (VarInfo));
> +    VarInfo.Buffer     = CurrVariable;
> +    VarInfo.StoreIndex = VAR_INDEX_INVALID;
> +    VarInfo.Flags.Auth = Global->Flags.Auth;
> +
> +    Status = ContextIn->GetVariableInfo (&VarInfo); // Retrieve the name/guid
> +    ASSERT_EFI_ERROR (Status);
> +
> +    UnprotectedVarIndex = CheckKnownUnprotectedVariable (Global, &VarInfo);
> +    if (UnprotectedVarIndex < UnprotectedVarIndexMax) {
> +      CurrVarDig = VAR_DIG_PTR (Global->Unprotected[UnprotectedVarIndex]);
> +    } else {
> +      CurrVarDig = FindVariableInternal (Global, &VarInfo, FALSE);
> +    }
> +
> +    ASSERT (CurrVarDig != NULL);
> +    CurrVarDig->State &= VAR_DELETED;
> +  }
> +
> +  //
> +  // The old copy of the variable might haven't been deleted completely.
> +  //
> +  if (CurrVariableInDel != NULL) {
> +    //
> +    // Find local cached copy, if possible.
> +    //
> +    ZeroMem (&VarInfo, sizeof (VarInfo));
> +    VarInfo.Buffer     = CurrVariableInDel;
> +    VarInfo.StoreIndex = VAR_INDEX_INVALID;
> +    VarInfo.Flags.Auth = Global->Flags.Auth;
> +
> +    Status = ContextIn->GetVariableInfo (&VarInfo); // Retrieve the name/guid
> +    ASSERT_EFI_ERROR (Status);
> +
> +    if (UnprotectedVarIndex == VAR_INDEX_INVALID) {
> +      UnprotectedVarIndex = CheckKnownUnprotectedVariable (Global,
> &VarInfo);
> +    }
> +
> +    if (UnprotectedVarIndex < UnprotectedVarIndexMax) {
> +      VarDig = VAR_DIG_PTR (Global->Unprotected[UnprotectedVarIndex]);
> +    } else {
> +      VarDig = FindVariableInternal (Global, &VarInfo, FALSE);
> +    }
> +
> +    if ((VarDig != NULL) && (VAR_DIG_ADR (VarDig) != VAR_INDEX_INVALID)) {
> +      VarDig->State &= VAR_DELETED;
> +
> +      //
> +      // Just need one node for the same variable. So remove the one
> +      // in-del-transition.
> +      //
> +      if ((CurrVarDig != NULL) && (VarDig != CurrVarDig)) {
> +        RemoveVariableDigestNode (Global, VarDig, TRUE);
> +      } else {
> +        CurrVarDig = VarDig;  // Reuse the one in-del-transition.
> +      }
> +    }
> +  }
> +
> +  //
> +  // New data of the variable or new variable to be added.
> +  //
> +  if (NewVariable != NULL) {
> +    //
> +    // Completely new variable?
> +    //
> +    if (UnprotectedVarIndex == VAR_INDEX_INVALID) {
> +      ZeroMem (&VarInfo, sizeof (VarInfo));
> +      VarInfo.Buffer     = NewVariable;
> +      VarInfo.StoreIndex = VAR_INDEX_INVALID;
> +      VarInfo.Flags.Auth = Global->Flags.Auth;
> +
> +      Status = ContextIn->GetVariableInfo (&VarInfo); // Retrieve the name/guid
> +      ASSERT_EFI_ERROR (Status);
> +
> +      UnprotectedVarIndex = CheckKnownUnprotectedVariable (Global,
> &VarInfo);
> +    }
> +  }
> +
> +  //
> +  // Reserve space for MetaDataHmacVar (before the new variable so
> +  // that it can be written first).
> +  //
> +  ZeroMem (&NewVarInfo, sizeof (NewVarInfo));
> +  ZeroMem (&NewHmacVarInfo, sizeof (NewHmacVarInfo));
> +
> +  //
> +  // Put the MetaDataHmacVar at the beginning of buffer.
> +  //
> +  NewHmacVarInfo.Buffer = NewVariable;
> +
> +  if (*NewVariableSize == 0) {
> +    //
> +    // Delete variable (but not MetaDataHmacVar)
> +    //
> +    if (  (UnprotectedVarIndex != IndexHmacAdded)
> +       && (UnprotectedVarIndex != IndexHmacInDel))
> +    {
> +      RemoveVariableDigestNode (Global, CurrVarDig, TRUE);
> +    }
> +
> +    NewVarInfo.Buffer = NULL;
> +  } else if (UnprotectedVarIndex >= IndexPlatformVar) {
> +    //
> +    // Add/update variable. Move new variable data to be after
> MetaDataHmacVar.
> +    //
> +    // TRICK: New MetaDataHmacVar will be put at the beginning of buffer
> +    //        for new variable so that they can be written into non-volatile
> +    //        variable storage in one call. This can avoid writing one variable
> +    //        (NewHmacVarInfo) in the middle of writing another variable
> +    //        (NewVarInfo), which will need two calls and introduce extra
> +    //        complexities (from temp variable buffer reservation to variable
> +    //        space reclaim, etc.) in current implementation of variable
> +    //        services. The caller must make sure there's enough space in
> +    //        variable buffer (i.e. at least 2 * MaxVariableSize).
> +    //
> +    NewVarInfo.Buffer = (VARIABLE_HEADER *)((UINTN)NewVariable
> +                                            + GetMetaDataHmacVarSize (Global->Flags.Auth));
> +    CopyMem ((VOID *)NewVarInfo.Buffer, (VOID *)NewVariable,
> *NewVariableSize);
> +
> +    NewVarInfo.StoreIndex = VAR_INDEX_INVALID;   // Skip offset calculation
> (it's new one)
> +    NewVarInfo.Flags.Auth = Global->Flags.Auth;
> +
> +    Status = ContextIn->GetVariableInfo (&NewVarInfo);
> +    ASSERT_EFI_ERROR (Status);
> +
> +    if (CurrVarDig != NULL) {
> +      //
> +      // Update existing variable. Re-use the node.
> +      //
> +      NewVarDig = CurrVarDig;
> +    } else {
> +      //
> +      // Add new variable.
> +      //
> +      NewVarDig = CreateVariableDigestNode (
> +                    NewVarInfo.Header.VariableName,
> +                    NewVarInfo.Header.VendorGuid,
> +                    (UINT16)NewVarInfo.Header.NameSize,
> +                    (UINT32)NewVarInfo.Header.DataSize,
> +                    NewVarInfo.Flags.Auth,
> +                    Global
> +                    );
> +      if (NewVarDig == NULL) {
> +        return EFI_OUT_OF_RESOURCES;
> +      }
> +
> +      NewVarDig->Attributes = NewVarInfo.Header.Attributes;
> +      if (UnprotectedVarIndex < UnprotectedVarIndexMax) {
> +        NewVarDig->Flags.Protected               = FALSE;
> +        NewVarDig->Flags.Encrypted               = FALSE;
> +        Global->Unprotected[UnprotectedVarIndex] = VAR_DIG_ADR (NewVarDig);
> +      }
> +
> +      //
> +      // copy new variable to CacheIndex
> +      //
> +      VarSize  =  VARIABLE_HEADER_SIZE (NewVarInfo.Flags.Auth);
> +      VarSize += NewVarInfo.Header.NameSize + GET_PAD_SIZE
> (NewVarInfo.Header.NameSize);
> +      VarSize += NewVarInfo.Header.DataSize + GET_PAD_SIZE
> (NewVarInfo.Header.DataSize);
> +      VarSize  = HEADER_ALIGN (VarSize);
> +      CopyMem (GET_BUFR (NewVarDig->CacheIndex), GET_BUFR
> (NewVarInfo.Buffer), VarSize);
> +      InsertVariableDigestNode (Global, NewVarDig, NULL);
> +    }
> +  }
> +
> +  if (  (UnprotectedVarIndex == IndexHmacAdded)
> +     || (UnprotectedVarIndex == IndexHmacInDel))
> +  {
> +    //
> +    // MetaDataHmacVar should be managed only by this library. It's not
> +    // supposed to be updated by external users of variable service. The only
> +    // exception is that deleting it (not really delete but refresh the HMAC
> +    // value against RPMC+1) is allowed before WriteInit, as a way to always
> +    // increment RPMC once in current boot before any variable updates.
> +    //
> +    if ((NewVarInfo.Buffer != NULL) || Global->Flags.WriteInit) {
> +      return EFI_ACCESS_DENIED;
> +    }
> +  } else {
> +    //
> +    // Do encryption, if enabled.
> +    //
> +    if ((NewVarDig != NULL) && (NewVarInfo.Buffer != NULL)) {
> +      Status = UpdateVariableInternal (Global, &NewVarInfo, NewVarDig);
> +      if (EFI_ERROR (Status)) {
> +        ASSERT_EFI_ERROR (Status);
> +        return Status;
> +      }
> +    }
> +  }
> +
> +  //
> +  // Refresh MetaDataHmacVar.
> +  //
> +  Status = RefreshVariableMetadataHmac (Global, NULL, &NewHmacVarInfo);
> +  if (EFI_ERROR (Status)) {
> +    ASSERT_EFI_ERROR (Status);
> +    return Status;
> +  }
> +
> +  //
> +  // Return size for both MetaDataHmacVar and added/updated one.
> +  //
> +  VarSize          = VARIABLE_SIZE (&NewHmacVarInfo);
> +  *NewVariableSize = HEADER_ALIGN (VarSize);
> +  if (NewVarInfo.Buffer != NULL) {
> +    VarSize = VARIABLE_SIZE (&NewVarInfo);
> +    VarSize = HEADER_ALIGN (VarSize);
> +
> +    if (VarSize > GET_CNTX (Global)->MaxVariableSize) {
> +      return EFI_BAD_BUFFER_SIZE;
> +    }
> +
> +    *NewVariableSize += VarSize;
> +  }
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +
> +  Finalize a variable updating after it's written to NV variable storage
> +  successfully.
> +
> +  This usually includes works like increasing RPMC, synchronizing local cache,
> +  updating new position of "MetaDataHmacVar", deleting old copy of
> "MetaDataHmacVar"
> +  completely, etc.
> +
> +  @param[in]      NewVariable       Buffer of new variables and
> MetaDataHmacVar.
> +  @param[in]      VariableSize      Size of buffer pointed by NewVariable.
> +  @param[in]      StoreIndex        StoreIndex to NV variable storage from where
> the new
> +                                    variable and MetaDataHmacVar have been written.
> +
> +  @retval EFI_SUCCESS         No problem in winding up the variable write
> operation.
> +  @retval Others              Failed to updating state of old copy of updated
> +                              variable, or failed to increase RPMC, etc.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +ProtectedVariableLibWriteFinal (
> +  IN  VARIABLE_HEADER  *NewVariable,
> +  IN  UINTN            VariableSize,
> +  IN  UINT64           StoreIndex
> +  )
> +{
> +  EFI_STATUS                     Status;
> +  PROTECTED_VARIABLE_INFO        VarInfo;
> +  PROTECTED_VARIABLE_CONTEXT_IN  *ContextIn;
> +  PROTECTED_VARIABLE_GLOBAL      *Global;
> +  UNPROTECTED_VARIABLE_INDEX     Index;
> +  VARIABLE_DIGEST                *VarDig;
> +  VOID                           *Buffer;
> +  UINTN                          VarSize;
> +  UINTN                          NewVarSize;
> +
> +  Status = GetProtectedVariableGlobal (&Global);
> +  ASSERT_EFI_ERROR (Status);
> +  ContextIn = GET_CNTX (Global);
> +
> +  ZeroMem ((VOID *)&VarInfo, sizeof (VarInfo));
> +  VarInfo.Buffer     = NewVariable;
> +  VarInfo.StoreIndex = VAR_INDEX_INVALID;
> +  VarInfo.Flags.Auth = Global->Flags.Auth;
> +
> +  Status = ContextIn->GetVariableInfo (&VarInfo);
> +  ASSERT_EFI_ERROR (Status);
> +
> +  Index = CheckKnownUnprotectedVariable (Global, &VarInfo);
> +  if (Index < UnprotectedVarIndexMax) {
> +    VarDig = VAR_DIG_PTR (Global->Unprotected[Index]);
> +  } else {
> +    VarDig = FindVariableInternal (Global, &VarInfo, FALSE);
> +  }
> +
> +  if (Index == IndexHmacAdded) {
> +    //
> +    // Advance the RPMC to let it match new MetaDataHmacVar.
> +    //
> +    Status = IncrementMonotonicCounter (RPMC_COUNTER_2);
> +    ASSERT_EFI_ERROR (Status);
> +
> +    if ((VarDig->StoreIndex != VAR_INDEX_INVALID) && (VarDig->State !=
> VAR_ADDED)) {
> +      ZeroMem ((VOID *)&VarInfo, sizeof (VarInfo));
> +      VarInfo.StoreIndex = VarDig->StoreIndex;  // Still point to old copy
> +      VarInfo.Flags.Auth = Global->Flags.Auth;
> +
> +      //
> +      // Delete variable completely.
> +      //
> +      Status = ContextIn->GetVariableInfo (&VarInfo);
> +      ASSERT_EFI_ERROR (Status);
> +
> +      if (  (VarInfo.Buffer->State == VAR_ADDED)
> +         || (VarInfo.Buffer->State == (VAR_ADDED &
> VAR_IN_DELETED_TRANSITION)))
> +      {
> +        VarInfo.Buffer->State &= VAR_DELETED;
> +        Status                 = ContextIn->UpdateVariableStore (
> +                                              &VarInfo,
> +                                              OFFSET_OF (VARIABLE_HEADER, State),
> +                                              sizeof (VarInfo.Buffer->State),
> +                                              &VarInfo.Buffer->State
> +                                              );
> +        if (EFI_ERROR (Status)) {
> +          ASSERT_EFI_ERROR (Status);
> +          return Status;
> +        }
> +      }
> +    }
> +  }
> +
> +  VarDig->StoreIndex = StoreIndex;
> +  VarDig->State      = VAR_ADDED;
> +
> +  ZeroMem ((VOID *)&VarInfo, sizeof (VarInfo));
> +  VarInfo.Buffer     = NULL;
> +  VarInfo.StoreIndex = VarDig->StoreIndex;
> +  VarInfo.Flags.Auth = Global->Flags.Auth;
> +  Status             = ContextIn->GetVariableInfo (&VarInfo);
> +
> +  //
> +  // Check if cache pool need re-allocation due to variable size increase
> +  //
> +  VarSize  =  VARIABLE_HEADER_SIZE (VarDig->Flags.Auth);
> +  VarSize += VarDig->NameSize + GET_PAD_SIZE (VarDig->NameSize);
> +  VarSize += VarDig->DataSize + GET_PAD_SIZE (VarDig->DataSize);
> +  VarSize  = HEADER_ALIGN (VarSize);
> +
> +  NewVarSize  =  VARIABLE_HEADER_SIZE (VarInfo.Flags.Auth);
> +  NewVarSize += VarInfo.Header.NameSize + GET_PAD_SIZE
> (VarInfo.Header.NameSize);
> +  NewVarSize += VarInfo.Header.DataSize + GET_PAD_SIZE
> (VarInfo.Header.DataSize);
> +  NewVarSize  = HEADER_ALIGN (NewVarSize);
> +
> +  if (VarSize < NewVarSize) {
> +    if (VarDig->Flags.Freeable == TRUE) {
> +      FreePool (GET_BUFR (VarDig->CacheIndex));
> +    }
> +
> +    Buffer = AllocatePool (NewVarSize);
> +    if (Buffer != NULL) {
> +      VarDig->CacheIndex = (EFI_PHYSICAL_ADDRESS)(UINTN)Buffer;
> +    } else {
> +      ASSERT (FALSE);
> +      return EFI_ABORTED;
> +    }
> +  }
> +
> +  //
> +  // Update cached copy.
> +  //
> +  CopyMem (GET_BUFR (VarDig->CacheIndex), NewVariable, NewVarSize);
> +
> +  //
> +  // Check if there is consecutive variable as part of the write or
> +  // is it just the MetaDataHmacVar variable
> +  //
> +  if (NewVarSize < VariableSize) {
> +    //
> +    // Advance to consecutive Variable
> +    //
> +    NewVariable = GET_BUFR (GET_ADRS (NewVariable) + NewVarSize);
> +
> +    //
> +    // Update the StoreIndex of consecutive Variable
> +    //
> +    ZeroMem ((VOID *)&VarInfo, sizeof (VarInfo));
> +    VarInfo.Buffer     = NULL;
> +    VarInfo.StoreIndex = VarDig->StoreIndex;
> +    VarInfo.Flags.Auth = Global->Flags.Auth;
> +    Status             = ContextIn->GetNextVariableInfo (&VarInfo);
> +    StoreIndex         = VarInfo.StoreIndex;
> +
> +    //
> +    // The new StoreIndex does not exist in the variable digest.
> +    // It is yet to be updated.
> +    // Therefore, find variable by Name & Guid instead.
> +    //
> +    VarInfo.StoreIndex = VAR_INDEX_INVALID;
> +    VarDig             = FindVariableInternal (Global, &VarInfo, FALSE);
> +
> +    //
> +    // Check if cache pool need re-allocation due to variable size increase
> +    //
> +    VarSize  =  VARIABLE_HEADER_SIZE (VarDig->Flags.Auth);
> +    VarSize += VarDig->NameSize + GET_PAD_SIZE (VarDig->NameSize);
> +    VarSize += VarDig->DataSize + GET_PAD_SIZE (VarDig->DataSize);
> +    VarSize  = HEADER_ALIGN (VarSize);
> +
> +    NewVarSize  =  VARIABLE_HEADER_SIZE (VarInfo.Flags.Auth);
> +    NewVarSize += VarInfo.Header.NameSize + GET_PAD_SIZE
> (VarInfo.Header.NameSize);
> +    NewVarSize += VarInfo.Header.DataSize + GET_PAD_SIZE
> (VarInfo.Header.DataSize);
> +    NewVarSize  = HEADER_ALIGN (NewVarSize);
> +
> +    if (VarSize < NewVarSize) {
> +      if (VarDig->Flags.Freeable == TRUE) {
> +        FreePool (GET_BUFR (VarDig->CacheIndex));
> +      }
> +
> +      Buffer = AllocatePool (NewVarSize);
> +      if (Buffer != NULL) {
> +        VarDig->CacheIndex = (EFI_PHYSICAL_ADDRESS)(UINTN)Buffer;
> +      } else {
> +        ASSERT (FALSE);
> +        return EFI_ABORTED;
> +      }
> +    }
> +
> +    //
> +    // Update cached copy.
> +    //
> +    CopyMem (GET_BUFR (VarDig->CacheIndex), NewVariable, NewVarSize);
> +    VarDig->StoreIndex = StoreIndex;
> +  }
> +
> +  return Status;
> +}
> +
> +/**
> +  Refresh variable information changed by variable service.
> +
> +  @param[in]  Variable         Pointer to buffer of the updated variable.
> +  @param[in]  VariableSize     Size of variable pointed by Variable.
> +  @param[in]  StoreIndex       New index of the variable in store.
> +  @param[in]  RefreshData      Flag to indicate if the variable has been updated.
> +
> +  @return EFI_SUCCESS     No error occurred in updating.
> +  @return EFI_NOT_FOUND   The given variable was not found in
> +                          ProtectedVariableLib.
> +**/
> +EFI_STATUS
> +EFIAPI
> +ProtectedVariableLibRefresh (
> +  IN  VARIABLE_HEADER  *Variable,
> +  IN  UINTN            VariableSize,
> +  IN  UINT64           StoreIndex,
> +  IN  BOOLEAN          RefreshData
> +  )
> +{
> +  EFI_STATUS                 Status;
> +  PROTECTED_VARIABLE_INFO    VarInfo;
> +  PROTECTED_VARIABLE_GLOBAL  *Global;
> +  VARIABLE_DIGEST            *VarDig;
> +
> +  (VOID)GetProtectedVariableGlobal (&Global);
> +
> +  ZeroMem ((VOID *)&VarInfo, sizeof (VarInfo));
> +  VarInfo.Buffer     = Variable;
> +  VarInfo.StoreIndex = VAR_INDEX_INVALID;
> +  VarInfo.Flags.Auth = Global->Flags.Auth;
> +
> +  Status = GET_CNTX (Global)->GetVariableInfo (&VarInfo);
> +  ASSERT_EFI_ERROR (Status);
> +
> +  VarDig = FindVariableInternal (Global, &VarInfo, FALSE);
> +  if (VarDig == NULL) {
> +    ASSERT (VarDig != NULL);
> +    return EFI_NOT_FOUND;
> +  }
> +
> +  if (StoreIndex != VAR_INDEX_INVALID) {
> +    VarDig->StoreIndex = StoreIndex;
> +  }
> +
> +  if (RefreshData) {
> +    if (VarDig->CacheIndex == VAR_INDEX_INVALID) {
> +      VarDig->CacheIndex = (EFI_PHYSICAL_ADDRESS)(UINTN)
> +                           AllocatePool (MAX_VARIABLE_SIZE);
> +    }
> +
> +    CopyMem (GET_BUFR (VarDig->CacheIndex), Variable, VariableSize);
> +  }
> +
> +  //
> +  // Information should stay the same other than following ones.
> +  //
> +  VarDig->State    = VarInfo.Header.State;
> +  VarDig->DataSize = (UINT32)VarInfo.Header.DataSize;
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +
> +  Determine if the variable is the HMAC variable
> +
> +  @param VariableName   Pointer to variable name.
> +
> +  @return TRUE      Variable is HMAC variable
> +  @return FALSE     Variable is not HMAC variable
> +
> +**/
> +BOOLEAN
> +ProtectedVariableLibIsHmac (
> +  IN CHAR16  *VariableName
> +  )
> +{
> +  INTN  Result;
> +
> +  Result = StrnCmp (
> +             METADATA_HMAC_VARIABLE_NAME,
> +             VariableName,
> +             METADATA_HMAC_VARIABLE_NAME_SIZE
> +             );
> +
> +  if (Result == 0) {
> +    return TRUE;
> +  }
> +
> +  return FALSE;
> +}
> diff --git
> a/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableSmmRuntime.c
> b/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableSmmRuntime.c
> new file mode 100644
> index 000000000000..4591d1cd59e5
> --- /dev/null
> +++
> b/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableSmmRuntime.c
> @@ -0,0 +1,233 @@
> +/** @file
> +  Implemention of ProtectedVariableLib for BootService/Runtime use cases.
> +
> +Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
> +SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#include <PiDxe.h>
> +#include <Uefi.h>
> +
> +#include "Library/MemoryAllocationLib.h"
> +#include "Library/UefiBootServicesTableLib.h"
> +#include "Library/UefiRuntimeLib.h"
> +#include "ProtectedVariableInternal.h"
> +
> +EFI_EVENT  mVaChangeEvent = NULL;
> +
> +PROTECTED_VARIABLE_CONTEXT_IN  mRtVariableContextIn = {
> +  PROTECTED_VARIABLE_CONTEXT_IN_STRUCT_VERSION,
> +  sizeof (PROTECTED_VARIABLE_CONTEXT_IN),
> +  0,
> +  FromRuntimeModule,
> +  NULL,
> +  NULL,
> +  NULL,
> +  NULL,
> +  NULL
> +};
> +
> +PROTECTED_VARIABLE_GLOBAL  mRtProtectedVariableGlobal = {
> +  PROTECTED_VARIABLE_CONTEXT_OUT_STRUCT_VERSION,
> +  sizeof (PROTECTED_VARIABLE_GLOBAL),
> +  { 0 },
> +  { 0 },
> +  0,
> +  0,
> +  0,
> +  0,
> +  0,
> +  0,
> +  { 0,                                          0,  0 },
> +  0,
> +  0,
> +  { 0,                                          0,  0, 0, 0, 0}
> +};
> +
> +/**
> +
> +  Get global data structure used to process protected variable.
> +
> +  @param[out]   Global      Pointer to global configuration data.
> +
> +  @retval EFI_SUCCESS         Get requested structure successfully.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +GetProtectedVariableGlobal (
> +  OUT PROTECTED_VARIABLE_GLOBAL  **Global OPTIONAL
> +  )
> +{
> +  if (Global != NULL) {
> +    mRtProtectedVariableGlobal.ContextIn = GET_ADRS
> (&mRtVariableContextIn);
> +    *Global                              = &mRtProtectedVariableGlobal;
> +  }
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +  Notification function of EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE.
> +
> +  This is a notification function registered on
> EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event.
> +  It convers pointer to new virtual address.
> +
> +  @param[in]  Event        Event whose notification function is being invoked.
> +  @param[in]  Context      Pointer to the notification function's context.
> +
> +**/
> +VOID
> +EFIAPI
> +VirtualAddressChangeEvent (
> +  IN EFI_EVENT  Event,
> +  IN VOID       *Context
> +  )
> +{
> +  if (mRtVariableContextIn.FindVariableSmm != NULL) {
> +    EfiConvertPointer (0x0, (VOID **)&mRtVariableContextIn.FindVariableSmm);
> +  }
> +
> +  if (mRtVariableContextIn.GetVariableInfo != NULL) {
> +    EfiConvertPointer (0x0, (VOID **)&mRtVariableContextIn.GetVariableInfo);
> +  }
> +
> +  if (mRtVariableContextIn.GetNextVariableInfo != NULL) {
> +    EfiConvertPointer (0x0, (VOID
> **)&mRtVariableContextIn.GetNextVariableInfo);
> +  }
> +
> +  if (mRtVariableContextIn.UpdateVariableStore != NULL) {
> +    EfiConvertPointer (0x0, (VOID
> **)&mRtVariableContextIn.UpdateVariableStore);
> +  }
> +
> +  EfiConvertPointer (0x0, (VOID **)&mRtVariableContextIn);
> +  if (mRtProtectedVariableGlobal.VariableCache != 0) {
> +    EfiConvertPointer (0x0, (VOID
> **)&mRtProtectedVariableGlobal.VariableCache);
> +  }
> +
> +  EfiConvertPointer (0x0, (VOID **)&mRtProtectedVariableGlobal);
> +}
> +
> +/**
> +
> +  Initialization for protected variable services.
> +
> +  If this initialization failed upon any error, the whole variable services
> +  should not be used.  A system reset might be needed to re-construct NV
> +  variable storage to be the default state.
> +
> +  @param[in]  ContextIn   Pointer to variable service context needed by
> +                          protected variable.
> +
> +  @retval EFI_SUCCESS               Protected variable services are ready.
> +  @retval EFI_INVALID_PARAMETER     If ContextIn == NULL or something
> missing or
> +                                    mismatching in the content in ContextIn.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +ProtectedVariableLibInitialize (
> +  IN  PROTECTED_VARIABLE_CONTEXT_IN  *ContextIn
> +  )
> +{
> +  if (  (ContextIn == NULL)
> +     || (ContextIn->StructVersion !=
> PROTECTED_VARIABLE_CONTEXT_IN_STRUCT_VERSION)
> +     || (ContextIn->FindVariableSmm == NULL)
> +     || (ContextIn->GetVariableInfo == NULL))
> +  {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  CopyMem (&mRtVariableContextIn, ContextIn, sizeof
> (mRtVariableContextIn));
> +
> +  //
> +  // Register the event to convert the pointer for runtime.
> +  //
> +  gBS->CreateEventEx (
> +         EVT_NOTIFY_SIGNAL,
> +         TPL_NOTIFY,
> +         VirtualAddressChangeEvent,
> +         NULL,
> +         &gEfiEventVirtualAddressChangeGuid,
> +         &mVaChangeEvent
> +         );
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +
> +  Prepare for variable update.
> +
> +  Not supported in DXE phase.
> +
> +  @retval EFI_UNSUPPORTED         Updating variable is not supported.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +ProtectedVariableLibWriteInit (
> +  VOID
> +  )
> +{
> +  ASSERT (FALSE);
> +  return EFI_UNSUPPORTED;
> +}
> +
> +/**
> +
> +  Update a variable with protection provided by this library.
> +
> +  Not supported in DXE phase.
> +
> +  @param[in,out]  CurrVariable        Variable to be updated. It's NULL if
> +                                      adding a new variable.
> +  @param[in]      CurrVariableInDel   In-delete-transition copy of updating
> variable.
> +  @param[in,out]  NewVariable         Buffer of new variable data or
> +                                      Buffer of "MetaDataHmacVar" and new variable
> (encrypted).
> +  @param[in,out]  NewVariableSize     Size of NewVariable or
> +                                      Size of (encrypted) NewVariable and "MetaDataHmacVar".
> +
> +  @retval EFI_UNSUPPORTED             Not support updating variable.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +ProtectedVariableLibUpdate (
> +  IN  OUT VARIABLE_HEADER  *CurrVariable,
> +  IN      VARIABLE_HEADER  *CurrVariableInDel,
> +  IN  OUT VARIABLE_HEADER  *NewVariable,
> +  IN  OUT UINTN            *NewVariableSize
> +  )
> +{
> +  ASSERT (FALSE);
> +  return EFI_UNSUPPORTED;
> +}
> +
> +/**
> +
> +  Finalize a variable updating after it's written to NV variable storage
> +  successfully.
> +
> +  (Not supported for BootService/Runtime use cases.)
> +
> +  @param[in]      NewVariable       Buffer of new variables and
> MetaDataHmacVar.
> +  @param[in]      VariableSize      Size of buffer pointed by NewVariable.
> +  @param[in]      StoreIndex        StoreIndex to NV variable storage from where
> the new
> +                                    variable and MetaDataHmacVar have been written.
> +
> +  @retval EFI_UNSUPPORTED           Not supported for BootService/Runtime use
> cases.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +ProtectedVariableLibWriteFinal (
> +  IN  VARIABLE_HEADER  *NewVariable,
> +  IN  UINTN            VariableSize,
> +  IN  UINT64           StoreIndex
> +  )
> +{
> +  ASSERT (FALSE);
> +  return EFI_UNSUPPORTED;
> +}
> --
> 2.35.1.windows.2


  reply	other threads:[~2022-11-22  8:00 UTC|newest]

Thread overview: 37+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-11-06  7:34 [PATCH v5 00/19] UEFI variable protection Judah Vang
2022-11-06  7:34 ` [PATCH v5 01/19] MdePkg: Add reference to new Ppi Guid Judah Vang
2022-11-06  7:34 ` [PATCH v5 02/19] MdeModulePkg: Update AUTH_VARIABLE_INFO struct Judah Vang
2022-11-06  7:34 ` [PATCH v5 03/19] MdeModulePkg: Add new ProtectedVariable GUIDs Judah Vang
2022-11-06  7:34 ` [PATCH v5 04/19] MdeModulePkg: Add new include files Judah Vang
2022-11-22  6:31   ` Wang, Jian J
2022-11-06  7:34 ` [PATCH v5 05/19] MdeModulePkg: Add new GUID for Variable Store Info Judah Vang
2022-11-06  7:34 ` [PATCH v5 06/19] MdeModulePkg: Add Null ProtectedVariable Library Judah Vang
2022-11-22  6:39   ` Wang, Jian J
2022-11-06  7:34 ` [PATCH v5 07/19] MdeModulePkg: Add new Variable functionality Judah Vang
2022-11-14  3:43   ` Wang, Jian J
     [not found]   ` <1727569A8ECB6F9D.19699@groups.io>
2022-11-14  4:27     ` [edk2-devel] " Wang, Jian J
2022-11-06  7:34 ` [PATCH v5 08/19] MdeModulePkg: Add support for Protected Variables Judah Vang
2022-11-14  7:14   ` Wang, Jian J
2022-11-14 17:19     ` Judah Vang
2022-11-15  8:49       ` [edk2-devel] " Sami Mujawar
2022-11-22  6:26         ` Wang, Jian J
     [not found]         ` <1729D430BF77E016.5511@groups.io>
2022-11-22  6:42           ` Wang, Jian J
2022-11-06  7:34 ` [PATCH v5 09/19] MdeModulePkg: Reference Null ProtectedVariableLib Judah Vang
2022-11-22  6:44   ` Wang, Jian J
2022-11-06  7:35 ` [PATCH v5 10/19] SecurityPkg: Add new GUIDs for Judah Vang
2022-11-06  7:35 ` [PATCH v5 11/19] SecurityPkg: Add new KeyService types and defines Judah Vang
2022-11-22  6:46   ` Wang, Jian J
2022-11-06  7:35 ` [PATCH v5 12/19] SecurityPkg: Add new variable types and functions Judah Vang
2022-11-06  7:35 ` [PATCH v5 13/19] SecurityPkg: Update RPMC APIs with index Judah Vang
2022-11-06  7:35 ` [PATCH v5 14/19] SecurityPkg: Fix GetVariableKey API Judah Vang
2022-11-06  7:35 ` [PATCH v5 15/19] SecurityPkg: Add null encryption variable libs Judah Vang
2022-11-22  6:55   ` Wang, Jian J
2022-11-06  7:35 ` [PATCH v5 16/19] SecurityPkg: Add VariableKey library function Judah Vang
2022-11-06  7:35 ` [PATCH v5 17/19] SecurityPkg: Add EncryptionVariable lib with AES Judah Vang
2022-11-22  7:15   ` Wang, Jian J
2022-11-06  7:35 ` [PATCH v5 18/19] SecurityPkg: Add Protected Variable Services Judah Vang
2022-11-22  7:59   ` Wang, Jian J [this message]
2022-11-06  7:35 ` [PATCH v5 19/19] SecurityPkg: Add references to new *.inf files Judah Vang
2022-11-22  8:05   ` Wang, Jian J
2022-12-09  8:03 ` [edk2-devel] [PATCH v5 00/19] UEFI variable protection Yao, Jiewen
     [not found] ` <172F11512E3044E7.1612@groups.io>
2022-12-09  9:41   ` Yao, Jiewen

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-list from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=MW4PR11MB6763CFA4ED41217F261D42E9B60D9@MW4PR11MB6763.namprd11.prod.outlook.com \
    --to=devel@edk2.groups.io \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox