public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
From: "Judah Vang" <judah.vang@intel.com>
To: devel@edk2.groups.io
Cc: Judah Vang <judah.vang@intel.com>,
	Jian J Wang <jian.j.wang@intel.com>,
	Jiewen Yao <jiewen.yao@intel.com>,
	Nishant C Mistry <nishant.c.mistry@intel.com>
Subject: [PATCH v1 17/28] SecurityPkg: Add Protected Variable Services
Date: Fri, 25 Mar 2022 16:28:14 -0700	[thread overview]
Message-ID: <20220325232825.2167-13-judah.vang@intel.com> (raw)
In-Reply-To: <20220325232825.2167-1-judah.vang@intel.com>

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.

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: 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.26.2.windows.1


  parent reply	other threads:[~2022-03-25 23:28 UTC|newest]

Thread overview: 27+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-03-25 23:28 [PATCH v1 05/28] MdeModulePkg: Add new ProtectedVariable GUIDs Judah Vang
2022-03-25 23:28 ` [PATCH v1 06/28] MdeModulePkg: Add new include files Judah Vang
2022-03-25 23:28 ` [PATCH v1 07/28] MdeModulePkg: Add Null ProtectedVariable Library Judah Vang
2022-03-25 23:28 ` [PATCH v1 08/28] MdeModulePkg: Add new Variable functionality Judah Vang
2022-03-25 23:28 ` [PATCH v1 09/28] MdeModulePkg: Add support for Protected Variables Judah Vang
2022-03-25 23:28 ` [PATCH v1 10/28] SecurityPkg: Add new KeyService types and defines Judah Vang
2022-03-25 23:28 ` [PATCH v1 11/28] SecurityPkg: Update RPMC APIs with index Judah Vang
2022-03-25 23:28 ` [PATCH v1 12/28] SecurityPkg: Add new variable types and functions Judah Vang
2022-03-25 23:28 ` [PATCH v1 13/28] SecurityPkg: Fix GetVariableKey API Judah Vang
2022-03-25 23:28 ` [PATCH v1 14/28] SecurityPkg: Add null encryption variable libs Judah Vang
2022-03-25 23:28 ` [PATCH v1 15/28] SecurityPkg: Add VariableKey library function Judah Vang
2022-03-25 23:28 ` [PATCH v1 16/28] SecurityPkg: Add EncryptionVariable lib with AES Judah Vang
2022-03-25 23:28 ` Judah Vang [this message]
2022-03-25 23:28 ` [PATCH v1 18/28] MdeModulePkg: Reference Null ProtectedVariableLib Judah Vang
2022-03-25 23:28 ` [PATCH v1 19/28] SecurityPkg: Add references to new *.inf files Judah Vang
2022-03-25 23:28 ` [PATCH v1 20/28] ArmVirtPkg: Add reference to ProtectedVariableNull Judah Vang
2022-03-25 23:28 ` [PATCH v1 21/28] UefiPayloadPkg: Add ProtectedVariable reference Judah Vang
2022-03-28  1:31   ` Ni, Ray
2022-03-25 23:28 ` [PATCH v1 22/28] EmulatorPkg: " Judah Vang
2022-03-28  1:30   ` Ni, Ray
2022-03-25 23:28 ` [PATCH v1 23/28] OvmfPkg: " Judah Vang
2022-03-25 23:28 ` [PATCH v1 24/28] OvmfPkg: Add ProtectedVariableLib reference Judah Vang
2022-03-25 23:28 ` [PATCH v1 25/28] " Judah Vang
2022-03-25 23:28 ` [PATCH v1 26/28] " Judah Vang
2022-03-25 23:28 ` [PATCH v1 27/28] OvmfPkg: Add ProtectedVariable reference Judah Vang
2022-03-28  7:42   ` Boeuf, Sebastien
2022-03-25 23:28 ` [PATCH v1 28/28] CryptoPkg: Enable cypto HMAC KDF library Judah Vang

Reply instructions:

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

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

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

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

  git send-email \
    --in-reply-to=20220325232825.2167-13-judah.vang@intel.com \
    --to=devel@edk2.groups.io \
    /path/to/YOUR_REPLY

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

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