[edk2-devel] [Patch v2 17/28] SecurityPkg: Add Protected Variable Services

Judah Vang posted 28 patches 1 month, 3 weeks ago
[edk2-devel] [Patch v2 17/28] SecurityPkg: Add Protected Variable Services
Posted by Judah Vang 1 month, 3 weeks ago
REF: https://bugzilla.tianocore.org/show_bug.cgi?id=2594

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: 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        |  611 ++++++
 SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableCommon.c          | 2095 ++++++++++++++++++++
 SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableDxe.c             |  163 ++
 SecurityPkg/Library/ProtectedVariableLib/ProtectedVariablePei.c             | 1331 +++++++++++++
 SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableSmm.c             |  209 ++
 SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableSmmDxeCommon.c    |  975 +++++++++
 SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableSmmRuntime.c      |  233 +++
 11 files changed, 5878 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.inf b/SecurityPkg/Library/ProtectedVariableLib/SmmRuntimeProtectedVariableLib.inf
new file mode 100644
index 000000000000..011ccdce2db8
--- /dev/null
+++ b/SecurityPkg/Library/ProtectedVariableLib/SmmRuntimeProtectedVariableLib.inf
@@ -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..fd38b5bd2019
--- /dev/null
+++ b/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableInternal.h
@@ -0,0 +1,611 @@
+/** @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
+  );
+
+/**
+
+  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.
+
+**/
+EFI_STATUS
+EFIAPI
+ProtectedVariableLibGetNextInternal (
+  IN  OUT PROTECTED_VARIABLE_INFO  *VarInfo
+  );
+
+/**
+
+  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..ed1a8e1ec545
--- /dev/null
+++ b/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableCommon.c
@@ -0,0 +1,2095 @@
+/** @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.
+  //
+  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;
+  }
+
+  //
+  // 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.
+  //
+  Buffer     = VarInfo->PlainData;
+  BufferSize = VarInfo->PlainDataSize;
+
+  Status = ProtectedVariableLibGetDataInternal (Global, VarInfo);
+  if (EFI_ERROR (Status)) {
+    //
+    // Return with caller provided buffer with zero DataSize
+    //
+    VarInfo->PlainData     = Buffer;
+    VarInfo->PlainDataSize = 0;
+    return Status;
+  }
+
+  if ((Buffer == NULL) || ((BufferSize) < VarInfo->PlainDataSize)) {
+    //
+    // Return with caller provided buffer with true PlainDataSize
+    //
+    VarInfo->PlainData = Buffer;
+    return EFI_BUFFER_TOO_SMALL;
+  }
+
+  //
+  // Copy Plain data to ouput data buffer
+  //
+  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;
+
+  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);
+  if (!EFI_ERROR (Status)) {
+    if ((*DataSize) < VarInfo.PlainDataSize) {
+      *DataSize = VarInfo.PlainDataSize;
+      return EFI_BUFFER_TOO_SMALL;
+    }
+
+    *DataSize = VarInfo.PlainDataSize;
+    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.
+
+  @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;
+    }
+  }
+
+  //
+  // Decrypt the data, if necessary.
+  //
+  Status = ProtectedVariableLibGetDataInternal (Global, &VarInfo);
+  if (!EFI_ERROR (Status)) {
+    if (*DataSize < VarInfo.PlainDataSize) {
+      *DataSize = VarInfo.PlainDataSize;
+      return EFI_BUFFER_TOO_SMALL;
+    }
+
+    *DataSize = VarInfo.PlainDataSize;
+    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..91d71a1e8525
--- /dev/null
+++ b/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariablePei.c
@@ -0,0 +1,1331 @@
+/** @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);
+
+  VarSize = Global->VariableNumber * MAX_VARIABLE_SIZE;
+  Buffer  = AllocateGlobalBuffer (VarSize, TRUE);
+
+  //
+  // It's safe to store the pointer in Global->VariableCache, which has been
+  // cleared by above calling of GetProtectedVariableGlobal().
+  //
+  Global->VariableCache     = GET_ADRS (Buffer);
+  Global->VariableCacheSize = EFI_SIZE_TO_PAGES (VarSize);
+
+  //
+  // 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..92ba7cb12763
--- /dev/null
+++ b/SecurityPkg/Library/ProtectedVariableLib/ProtectedVariableSmmDxeCommon.c
@@ -0,0 +1,975 @@
+/** @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 {
+    //
+    // RPMC1 is going to be one step ahead of RPMC2 here, in order to
+    // refresh the MetaDataHmacVar in each boot before writing any other
+    // variables.
+    //
+    Status = IncrementMonotonicCounter (RPMC_COUNTER_1);
+    ASSERT_EFI_ERROR (Status);
+
+    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



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#89438): https://edk2.groups.io/g/devel/message/89438
Mute This Topic: https://groups.io/mt/90781908/1787277
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [importer@patchew.org]
-=-=-=-=-=-=-=-=-=-=-=-