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: Jian J Wang <jian.j.wang@intel.com>,
	Liming Gao <gaoliming@byosoft.com.cn>,
	Hao A Wu <hao.a.wu@intel.com>,
	Nishant C Mistry <nishant.c.mistry@intel.com>
Subject: [PATCH v3 09/28] MdeModulePkg: Add support for Protected Variables
Date: Wed,  8 Jun 2022 23:03:03 -0700	[thread overview]
Message-ID: <20220609060322.3491-10-judah.vang@intel.com> (raw)
In-Reply-To: <20220609060322.3491-1-judah.vang@intel.com>

REF: https://bugzilla.tianocore.org/show_bug.cgi?id=2594

V3: Fix 'NextVariableStore' parameter for CopyMem.  It was causing
an exception. Need to correctly cast 'NextVariableStore' so all
platforms build.  Add code to initialize 'ContextIn' structure in
SmmVariableReay() to fix issue with NULL function pointer.

V1: Add support for Protected Variables.
Add new API to retrieve Variable Infomation and data.
Add new API to update variable in non-volatile storage or
cached copy.

Cc: Jian J Wang <jian.j.wang@intel.com>
Cc: Liming Gao <gaoliming@byosoft.com.cn>
Cc: Hao A Wu <hao.a.wu@intel.com>
Cc: Nishant C Mistry <nishant.c.mistry@intel.com>
Signed-off-by: Jian J Wang <jian.j.wang@intel.com>
Signed-off-by: Nishant C Mistry <nishant.c.mistry@intel.com>
Signed-off-by: Judah Vang <judah.vang@intel.com>
---
 MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.inf    |    3 +-
 MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.inf           |    3 +-
 MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.inf |    4 +-
 MdeModulePkg/Universal/Variable/RuntimeDxe/VariableStandaloneMm.inf  |    3 +-
 MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.h                |  127 +-
 MdeModulePkg/Universal/Variable/RuntimeDxe/VariableParsing.h         |   91 +-
 MdeModulePkg/Universal/Variable/RuntimeDxe/Reclaim.c                 |  349 +++-
 MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.c                | 2142 +++++++++++---------
 MdeModulePkg/Universal/Variable/RuntimeDxe/VariableDxe.c             |   26 +-
 MdeModulePkg/Universal/Variable/RuntimeDxe/VariableExLib.c           |  167 +-
 MdeModulePkg/Universal/Variable/RuntimeDxe/VariableNonVolatile.c     |  194 +-
 MdeModulePkg/Universal/Variable/RuntimeDxe/VariableParsing.c         |  320 ++-
 MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeCache.c    |    2 +-
 MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.c             |   39 +-
 MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.c   |   67 +-
 15 files changed, 2484 insertions(+), 1053 deletions(-)

diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.inf b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.inf
index 3858adf6739d..b7a3d3942ba7 100644
--- a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.inf
+++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.inf
@@ -9,7 +9,7 @@
 #  This external input must be validated carefully to avoid security issues such as
 #  buffer overflow or integer overflow.
 #
-# Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.<BR>
+# Copyright (c) 2006 - 2022, Intel Corporation. All rights reserved.<BR>
 # Copyright (c) Microsoft Corporation.
 # SPDX-License-Identifier: BSD-2-Clause-Patent
 #
@@ -75,6 +75,7 @@ [LibraryClasses]
   VariablePolicyLib
   VariablePolicyHelperLib
   SafeIntLib
+  ProtectedVariableLib
 
 [Protocols]
   gEfiFirmwareVolumeBlockProtocolGuid           ## CONSUMES
diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.inf b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.inf
index 8c552b87e080..4ba467a73255 100644
--- a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.inf
+++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.inf
@@ -18,7 +18,7 @@
 #  may not be modified without authorization. If platform fails to protect these resources,
 #  the authentication service provided in this driver will be broken, and the behavior is undefined.
 #
-# Copyright (c) 2010 - 2019, Intel Corporation. All rights reserved.<BR>
+# Copyright (c) 2010 - 2022, Intel Corporation. All rights reserved.<BR>
 # Copyright (c) Microsoft Corporation.
 # SPDX-License-Identifier: BSD-2-Clause-Patent
 #
@@ -84,6 +84,7 @@ [LibraryClasses]
   VariablePolicyLib
   VariablePolicyHelperLib
   SafeIntLib
+  ProtectedVariableLib
 
 [Protocols]
   gEfiSmmFirmwareVolumeBlockProtocolGuid        ## CONSUMES
diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.inf b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.inf
index a0d8b2267e92..5d2fc78ea917 100644
--- a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.inf
+++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.inf
@@ -13,7 +13,7 @@
 #  may not be modified without authorization. If platform fails to protect these resources,
 #  the authentication service provided in this driver will be broken, and the behavior is undefined.
 #
-# Copyright (c) 2010 - 2019, Intel Corporation. All rights reserved.<BR>
+# Copyright (c) 2010 - 2022, Intel Corporation. All rights reserved.<BR>
 # Copyright (c) Microsoft Corporation.<BR>
 # SPDX-License-Identifier: BSD-2-Clause-Patent
 #
@@ -61,6 +61,8 @@ [LibraryClasses]
   SafeIntLib
   PcdLib
   MmUnblockMemoryLib
+  ProtectedVariableLib
+  IoLib
 
 [Protocols]
   gEfiVariableWriteArchProtocolGuid             ## PRODUCES
diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableStandaloneMm.inf b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableStandaloneMm.inf
index f09bed40cf51..956a872cda09 100644
--- a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableStandaloneMm.inf
+++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableStandaloneMm.inf
@@ -18,7 +18,7 @@
 #  may not be modified without authorization. If platform fails to protect these resources,
 #  the authentication service provided in this driver will be broken, and the behavior is undefined.
 #
-# Copyright (c) 2010 - 2019, Intel Corporation. All rights reserved.<BR>
+# Copyright (c) 2010 - 2022, Intel Corporation. All rights reserved.<BR>
 # Copyright (c) 2018, Linaro, Ltd. All rights reserved.<BR>
 # Copyright (c) Microsoft Corporation.
 # SPDX-License-Identifier: BSD-2-Clause-Patent
@@ -80,6 +80,7 @@ [LibraryClasses]
   VariableFlashInfoLib
   VariablePolicyLib
   VariablePolicyHelperLib
+  ProtectedVariableLib
 
 [Protocols]
   gEfiSmmFirmwareVolumeBlockProtocolGuid        ## CONSUMES
diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.h b/MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.h
index a668abb82b15..a2065f4bc6e0 100644
--- a/MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.h
+++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.h
@@ -2,7 +2,7 @@
   The internal header file includes the common header files, defines
   internal structure and functions used by Variable modules.
 
-Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.<BR>
+Copyright (c) 2006 - 2022, Intel Corporation. All rights reserved.<BR>
 SPDX-License-Identifier: BSD-2-Clause-Patent
 
 **/
@@ -33,6 +33,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
 #include <Library/VarCheckLib.h>
 #include <Library/VariableFlashInfoLib.h>
 #include <Library/SafeIntLib.h>
+#include <Library/ProtectedVariableLib.h>
 #include <Guid/GlobalVariable.h>
 #include <Guid/EventGroup.h>
 #include <Guid/VariableFormat.h>
@@ -820,4 +821,128 @@ VariableExLibAtRuntime (
   VOID
   );
 
+/**
+  Is user variable?
+
+  @param[in] Variable   Pointer to variable header.
+
+  @retval TRUE          User variable.
+  @retval FALSE         System variable.
+
+**/
+BOOLEAN
+IsUserVariable (
+  IN VARIABLE_HEADER  *Variable
+  );
+
+/**
+
+  Variable store garbage collection and reclaim operation.
+
+  @param[in]      VariableBase            Base address of variable store.
+  @param[out]     LastVariableOffset      Offset of last variable.
+  @param[in]      IsVolatile              The variable store is volatile or not;
+                                          if it is non-volatile, need FTW.
+  @param[in, out] UpdatingPtrTrack        Pointer to updating variable pointer track structure.
+  @param[in]      NewVariable             Pointer to new variable.
+  @param[in]      NewVariableSize         New variable size.
+
+  @return EFI_SUCCESS                  Reclaim operation has finished successfully.
+  @return EFI_OUT_OF_RESOURCES         No enough memory resources or variable space.
+  @return Others                       Unexpect error happened during reclaim operation.
+
+**/
+EFI_STATUS
+Reclaim (
+  IN     EFI_PHYSICAL_ADDRESS    VariableBase,
+  OUT    UINTN                   *LastVariableOffset,
+  IN     BOOLEAN                 IsVolatile,
+  IN OUT VARIABLE_POINTER_TRACK  *UpdatingPtrTrack,
+  IN     VARIABLE_HEADER         *NewVariable,
+  IN     UINTN                   NewVariableSize
+  );
+
+/**
+
+  This function writes data to the FWH at the correct LBA even if the LBAs
+  are fragmented.
+
+  @param Global                  Pointer to VARIABLE_GLOBAL structure.
+  @param Volatile                Point out the Variable is Volatile or Non-Volatile.
+  @param SetByIndex              TRUE if target pointer is given as index.
+                                 FALSE if target pointer is absolute.
+  @param Fvb                     Pointer to the writable FVB protocol.
+  @param DataPtrIndex            Pointer to the Data from the end of VARIABLE_STORE_HEADER
+                                 structure.
+  @param DataSize                Size of data to be written.
+  @param Buffer                  Pointer to the buffer from which data is written.
+
+  @retval EFI_INVALID_PARAMETER  Parameters not valid.
+  @retval EFI_UNSUPPORTED        Fvb is a NULL for Non-Volatile variable update.
+  @retval EFI_OUT_OF_RESOURCES   The remaining size is not enough.
+  @retval EFI_SUCCESS            Variable store successfully updated.
+
+**/
+EFI_STATUS
+UpdateVariableStore (
+  IN VARIABLE_GLOBAL                     *Global,
+  IN BOOLEAN                             Volatile,
+  IN BOOLEAN                             SetByIndex,
+  IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *Fvb,
+  IN UINTN                               DataPtrIndex,
+  IN UINT32                              DataSize,
+  IN UINT8                               *Buffer
+  );
+
+/**
+  Update partial data of a variable on NV storage and/or cached copy.
+
+  @param[in]  VariableInfo  Pointer to a variable with detailed information.
+  @param[in]  Offset        Offset to write from.
+  @param[in]  Size          Size of data Buffer to update.
+  @param[in]  Buffer        Pointer to data buffer to update.
+
+  @retval EFI_SUCCESS             The variable data was updated successfully.
+  @retval EFI_UNSUPPORTED         If this function is called directly in runtime.
+  @retval EFI_INVALID_PARAMETER   If VariableInfo, Buffer or Size are not valid.
+  @retval Others                  Failed to update NV storage or variable cache.
+
+**/
+EFI_STATUS
+EFIAPI
+VariableExLibUpdateNvVariable (
+  IN  PROTECTED_VARIABLE_INFO  *VariableInfo,
+  IN  UINTN                    Offset,
+  IN  UINT32                   Size,
+  IN  UINT8                    *Buffer
+  );
+
+/**
+  Finds the given variable in a variable store in SMM.
+
+  Caution: This function may receive untrusted input.
+  The data size is external input, so this function will validate it carefully to avoid buffer overflow.
+
+  @param[in]      VariableName       Name of Variable to be found.
+  @param[in]      VendorGuid         Variable vendor GUID.
+  @param[out]     Attributes         Attribute value of the variable found.
+  @param[in, out] DataSize           Size of Data found. If size is less than the
+                                     data, this value contains the required size.
+  @param[out]     Data               Data pointer.
+
+  @retval EFI_SUCCESS                Found the specified variable.
+  @retval EFI_INVALID_PARAMETER      Invalid parameter.
+  @retval EFI_NOT_FOUND              The specified variable could not be found.
+
+**/
+EFI_STATUS
+EFIAPI
+FindVariableInSmm (
+  IN      CHAR16    *VariableName,
+  IN      EFI_GUID  *VendorGuid,
+  OUT     UINT32    *Attributes OPTIONAL,
+  IN OUT  UINTN     *DataSize,
+  OUT     VOID      *Data OPTIONAL
+  );
+
 #endif
diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableParsing.h b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableParsing.h
index 951e8a089e34..3a4e8019aaf9 100644
--- a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableParsing.h
+++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableParsing.h
@@ -2,7 +2,7 @@
   Functions in this module are associated with variable parsing operations and
   are intended to be usable across variable driver source files.
 
-Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
+Copyright (c) 2019 - 2022, Intel Corporation. All rights reserved.<BR>
 SPDX-License-Identifier: BSD-2-Clause-Patent
 
 **/
@@ -19,6 +19,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
 
   @param[in] Variable           Pointer to the Variable Header.
   @param[in] VariableStoreEnd   Pointer to the Variable Store End.
+  @param[in] AuthFormat         Auth-variable indicator.
 
   @retval TRUE              Variable header is valid.
   @retval FALSE             Variable header is not valid.
@@ -27,7 +28,8 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
 BOOLEAN
 IsValidVariableHeader (
   IN  VARIABLE_HEADER  *Variable,
-  IN  VARIABLE_HEADER  *VariableStoreEnd
+  IN  VARIABLE_HEADER  *VariableStoreEnd,
+  IN  BOOLEAN          AuthFormat
   );
 
 /**
@@ -192,6 +194,28 @@ GetVariableDataOffset (
   IN  BOOLEAN          AuthFormat
   );
 
+/**
+  Get variable data payload.
+
+  @param[in]      Variable     Pointer to the Variable Header.
+  @param[out]     Data         Pointer to buffer used to store the variable data.
+  @param[in]      DataSize     Size of buffer passed by Data.
+  @param[out]     DataSize     Size of data copied into Data buffer.
+  @param[in]      AuthFlag     Auth-variable indicator.
+
+  @return EFI_SUCCESS             Data was fetched.
+  @return EFI_INVALID_PARAMETER   DataSize is NULL.
+  @return EFI_BUFFER_TOO_SMALL    DataSize is smaller than size of variable data.
+
+**/
+EFI_STATUS
+GetVariableData (
+  IN      VARIABLE_HEADER  *Variable,
+  IN  OUT VOID             *Data,
+  IN  OUT UINT32           *DataSize,
+  IN      BOOLEAN          AuthFlag
+  );
+
 /**
 
   This code gets the pointer to the next variable header.
@@ -344,4 +368,67 @@ UpdateVariableInfo (
   IN OUT VARIABLE_INFO_ENTRY  **VariableInfo
   );
 
+/**
+
+  Retrieve details of the variable next to given variable within VariableStore.
+
+  If VarInfo->Address is NULL, the first one in VariableStore is returned.
+
+  VariableStart and/or VariableEnd can be given optionally for the situation
+  in which the valid storage space is smaller than the VariableStore->Size.
+  This usually happens when PEI variable services make a compact variable
+  cache to save memory, which cannot make use VariableStore->Size to determine
+  the correct variable storage range.
+
+  @param VariableStore            Pointer to a variable storage. It's optional.
+  @param VariableStart            Start point of valid range in VariableStore.
+  @param VariableEnd              End point of valid range in VariableStore.
+  @param VariableInfo             Pointer to variable information.
+
+  @retval EFI_INVALID_PARAMETER  VariableInfo or VariableStore is NULL.
+  @retval EFI_NOT_FOUND          If the end of VariableStore is reached.
+  @retval EFI_SUCCESS            The next variable is retrieved successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+GetNextVariableInfo (
+  IN OUT  PROTECTED_VARIABLE_INFO  *VarInfo
+  );
+
+/**
+
+  Retrieve details about a variable and return them in VariableInfo->Header.
+
+  If VariableInfo->Address is given, this function will calculate its offset
+  relative to given variable storage via VariableStore; Otherwise, it will try
+  other internal variable storages or cached copies. It's assumed that, for all
+  copies of NV variable storage, all variables are stored in the same relative
+  position. If VariableInfo->Address is found in the range of any storage copies,
+  its offset relative to that storage should be the same in other copies.
+
+  If VariableInfo->Offset is given (non-zero) but not VariableInfo->Address,
+  this function will return the variable memory address inside VariableStore,
+  if given, via VariableInfo->Address; Otherwise, the address of other storage
+  copies will be returned, if any.
+
+  For a new variable whose offset has not been determined, a value of -1 as
+  VariableInfo->Offset should be passed to skip the offset calculation.
+
+  @param VariableStore            Pointer to a variable storage. It's optional.
+  @param VariableInfo             Pointer to variable information.
+
+  @retval EFI_INVALID_PARAMETER  VariableInfo is NULL or both VariableInfo->Address
+                                 and VariableInfo->Offset are NULL (0).
+  @retval EFI_NOT_FOUND          If given Address or Offset is out of range of
+                                 any given or internal storage copies.
+  @retval EFI_SUCCESS            Variable details are retrieved successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+GetVariableInfo (
+  IN OUT  PROTECTED_VARIABLE_INFO  *VarInfo
+  );
+
 #endif
diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/Reclaim.c b/MdeModulePkg/Universal/Variable/RuntimeDxe/Reclaim.c
index fe64d0a2b3dd..a5b7f8a1fbe2 100644
--- a/MdeModulePkg/Universal/Variable/RuntimeDxe/Reclaim.c
+++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/Reclaim.c
@@ -2,12 +2,15 @@
   Handles non-volatile variable store garbage collection, using FTW
   (Fault Tolerant Write) protocol.
 
-Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.<BR>
+Copyright (c) 2006 - 2022, Intel Corporation. All rights reserved.<BR>
 SPDX-License-Identifier: BSD-2-Clause-Patent
 
 **/
 
 #include "Variable.h"
+#include "VariableNonVolatile.h"
+#include "VariableParsing.h"
+#include "VariableRuntimeCache.h"
 
 /**
   Gets LBA of block and offset by given address.
@@ -155,3 +158,347 @@ FtwVariableSpace (
 
   return Status;
 }
+
+/**
+
+  Variable store garbage collection and reclaim operation.
+
+  @param[in]      VariableBase            Base address of variable store.
+  @param[out]     LastVariableOffset      Offset of last variable.
+  @param[in]      IsVolatile              The variable store is volatile or not;
+                                          if it is non-volatile, need FTW.
+  @param[in, out] UpdatingPtrTrack        Pointer to updating variable pointer track structure.
+  @param[in]      NewVariable             Pointer to new variable.
+  @param[in]      NewVariableSize         New variable size.
+
+  @return EFI_SUCCESS                  Reclaim operation has finished successfully.
+  @return EFI_OUT_OF_RESOURCES         No enough memory resources or variable space.
+  @return Others                       Unexpect error happened during reclaim operation.
+
+**/
+EFI_STATUS
+Reclaim (
+  IN     EFI_PHYSICAL_ADDRESS    VariableBase,
+  OUT    UINTN                   *LastVariableOffset,
+  IN     BOOLEAN                 IsVolatile,
+  IN OUT VARIABLE_POINTER_TRACK  *UpdatingPtrTrack,
+  IN     VARIABLE_HEADER         *NewVariable,
+  IN     UINTN                   NewVariableSize
+  )
+{
+  VARIABLE_HEADER        *Variable;
+  VARIABLE_HEADER        *AddedVariable;
+  VARIABLE_HEADER        *NextVariable;
+  VARIABLE_HEADER        *NextAddedVariable;
+  VARIABLE_STORE_HEADER  *VariableStoreHeader;
+  UINT8                  *ValidBuffer;
+  UINTN                  MaximumBufferSize;
+  UINTN                  VariableSize;
+  UINTN                  NameSize;
+  UINT8                  *CurrPtr;
+  VOID                   *Point0;
+  VOID                   *Point1;
+  BOOLEAN                FoundAdded;
+  EFI_STATUS             Status;
+  EFI_STATUS             DoneStatus;
+  UINTN                  CommonVariableTotalSize;
+  UINTN                  CommonUserVariableTotalSize;
+  UINTN                  HwErrVariableTotalSize;
+  VARIABLE_HEADER        *UpdatingVariable;
+  VARIABLE_HEADER        *UpdatingInDeletedTransition;
+  BOOLEAN                AuthFormat;
+
+  AuthFormat                  = mVariableModuleGlobal->VariableGlobal.AuthFormat;
+  UpdatingVariable            = NULL;
+  UpdatingInDeletedTransition = NULL;
+  if (UpdatingPtrTrack != NULL) {
+    UpdatingVariable            = UpdatingPtrTrack->CurrPtr;
+    UpdatingInDeletedTransition = UpdatingPtrTrack->InDeletedTransitionPtr;
+  }
+
+  VariableStoreHeader = (VARIABLE_STORE_HEADER *)((UINTN)VariableBase);
+
+  CommonVariableTotalSize     = 0;
+  CommonUserVariableTotalSize = 0;
+  HwErrVariableTotalSize      = 0;
+
+  if (IsVolatile || mVariableModuleGlobal->VariableGlobal.EmuNvMode) {
+    //
+    // Start Pointers for the variable.
+    //
+    Variable          = GetStartPointer (VariableStoreHeader);
+    MaximumBufferSize = sizeof (VARIABLE_STORE_HEADER);
+
+    while (IsValidVariableHeader (Variable, GetEndPointer (VariableStoreHeader), AuthFormat)) {
+      NextVariable = GetNextVariablePtr (Variable, AuthFormat);
+      if (((Variable->State == VAR_ADDED) || (Variable->State == (VAR_IN_DELETED_TRANSITION & VAR_ADDED))) &&
+          (Variable != UpdatingVariable) &&
+          (Variable != UpdatingInDeletedTransition)
+          )
+      {
+        VariableSize       = (UINTN)NextVariable - (UINTN)Variable;
+        MaximumBufferSize += VariableSize;
+      }
+
+      Variable = NextVariable;
+    }
+
+    if (NewVariable != NULL) {
+      //
+      // Add the new variable size.
+      //
+      MaximumBufferSize += NewVariableSize;
+    }
+
+    //
+    // Reserve the 1 Bytes with Oxff to identify the
+    // end of the variable buffer.
+    //
+    MaximumBufferSize += 1;
+    ValidBuffer        = AllocatePool (MaximumBufferSize);
+    if (ValidBuffer == NULL) {
+      return EFI_OUT_OF_RESOURCES;
+    }
+  } else {
+    //
+    // For NV variable reclaim, don't allocate pool here and just use mNvVariableCache
+    // as the buffer to reduce SMRAM consumption for SMM variable driver.
+    //
+    MaximumBufferSize = mNvVariableCache->Size;
+    ValidBuffer       = (UINT8 *)mNvVariableCache;
+  }
+
+  SetMem (ValidBuffer, MaximumBufferSize, 0xff);
+
+  //
+  // Copy variable store header.
+  //
+  CopyMem (ValidBuffer, VariableStoreHeader, sizeof (VARIABLE_STORE_HEADER));
+  CurrPtr = (UINT8 *)GetStartPointer ((VARIABLE_STORE_HEADER *)ValidBuffer);
+
+  //
+  // Reinstall all ADDED variables as long as they are not identical to Updating Variable.
+  //
+  Variable = GetStartPointer (VariableStoreHeader);
+  while (IsValidVariableHeader (Variable, GetEndPointer (VariableStoreHeader), AuthFormat)) {
+    NextVariable = GetNextVariablePtr (Variable, AuthFormat);
+    if ((Variable != UpdatingVariable) && (Variable->State == VAR_ADDED)) {
+      VariableSize = (UINTN)NextVariable - (UINTN)Variable;
+      CopyMem (CurrPtr, (UINT8 *)Variable, VariableSize);
+      if (!IsVolatile) {
+        (VOID)ProtectedVariableLibRefresh (
+                (VARIABLE_HEADER *)CurrPtr,
+                VariableSize,
+                (UINTN)CurrPtr - (UINTN)ValidBuffer,
+                FALSE
+                );
+
+        if ((Variable->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD)
+            == EFI_VARIABLE_HARDWARE_ERROR_RECORD)
+        {
+          HwErrVariableTotalSize += VariableSize;
+        } else {
+          CommonVariableTotalSize += VariableSize;
+          if (IsUserVariable (Variable)) {
+            CommonUserVariableTotalSize += VariableSize;
+          }
+        }
+      }
+
+      CurrPtr += VariableSize;
+    }
+
+    Variable = NextVariable;
+  }
+
+  //
+  // Reinstall all in delete transition variables.
+  //
+  Variable = GetStartPointer (VariableStoreHeader);
+  while (IsValidVariableHeader (Variable, GetEndPointer (VariableStoreHeader), AuthFormat)) {
+    NextVariable = GetNextVariablePtr (Variable, AuthFormat);
+    if ((Variable != UpdatingVariable) && (Variable != UpdatingInDeletedTransition) && (Variable->State == (VAR_IN_DELETED_TRANSITION & VAR_ADDED)) &&
+        (ProtectedVariableLibIsHmac (GetVariableNamePtr (Variable, AuthFormat)) == FALSE))
+    {
+      FoundAdded    = FALSE;
+      AddedVariable = GetStartPointer ((VARIABLE_STORE_HEADER *)ValidBuffer);
+      while (IsValidVariableHeader (AddedVariable, GetEndPointer ((VARIABLE_STORE_HEADER *)ValidBuffer), AuthFormat)) {
+        NextAddedVariable = GetNextVariablePtr (AddedVariable, AuthFormat);
+        NameSize          = NameSizeOfVariable (AddedVariable, AuthFormat);
+        if (CompareGuid (
+              GetVendorGuidPtr (AddedVariable, AuthFormat),
+              GetVendorGuidPtr (Variable, AuthFormat)
+              ) && (NameSize == NameSizeOfVariable (Variable, AuthFormat)))
+        {
+          Point0 = (VOID *)GetVariableNamePtr (AddedVariable, AuthFormat);
+          Point1 = (VOID *)GetVariableNamePtr (Variable, AuthFormat);
+          if (CompareMem (Point0, Point1, NameSize) == 0) {
+            FoundAdded = TRUE;
+            break;
+          }
+        }
+
+        AddedVariable = NextAddedVariable;
+      }
+
+      if (!FoundAdded) {
+        //
+        // Promote VAR_IN_DELETED_TRANSITION to VAR_ADDED.
+        //
+        VariableSize = (UINTN)NextVariable - (UINTN)Variable;
+        CopyMem (CurrPtr, (UINT8 *)Variable, VariableSize);
+        ((VARIABLE_HEADER *)CurrPtr)->State = VAR_ADDED;
+        if (!IsVolatile) {
+          (VOID)ProtectedVariableLibRefresh (
+                  (VARIABLE_HEADER *)CurrPtr,
+                  VariableSize,
+                  (UINTN)CurrPtr - (UINTN)ValidBuffer,
+                  FALSE
+                  );
+
+          if ((Variable->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD)
+              == EFI_VARIABLE_HARDWARE_ERROR_RECORD)
+          {
+            HwErrVariableTotalSize += VariableSize;
+          } else {
+            CommonVariableTotalSize += VariableSize;
+            if (IsUserVariable (Variable)) {
+              CommonUserVariableTotalSize += VariableSize;
+            }
+          }
+        }
+
+        CurrPtr += VariableSize;
+      }
+    }
+
+    Variable = NextVariable;
+  }
+
+  //
+  // Install the new variable if it is not NULL.
+  //
+  if (NewVariable != NULL) {
+    if (((UINTN)CurrPtr - (UINTN)ValidBuffer) + NewVariableSize > VariableStoreHeader->Size) {
+      //
+      // No enough space to store the new variable.
+      //
+      Status = EFI_OUT_OF_RESOURCES;
+      goto Done;
+    }
+
+    if (!IsVolatile) {
+      if ((NewVariable->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == EFI_VARIABLE_HARDWARE_ERROR_RECORD) {
+        HwErrVariableTotalSize += NewVariableSize;
+      } else if ((NewVariable->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) != EFI_VARIABLE_HARDWARE_ERROR_RECORD) {
+        CommonVariableTotalSize += NewVariableSize;
+        if (IsUserVariable (NewVariable)) {
+          CommonUserVariableTotalSize += NewVariableSize;
+        }
+      }
+
+      if ((HwErrVariableTotalSize > PcdGet32 (PcdHwErrStorageSize)) ||
+          (CommonVariableTotalSize > mVariableModuleGlobal->CommonVariableSpace) ||
+          (CommonUserVariableTotalSize > mVariableModuleGlobal->CommonMaxUserVariableSpace))
+      {
+        //
+        // No enough space to store the new variable by NV or NV+HR attribute.
+        //
+        Status = EFI_OUT_OF_RESOURCES;
+        goto Done;
+      }
+    }
+
+    CopyMem (CurrPtr, (UINT8 *)NewVariable, NewVariableSize);
+    ((VARIABLE_HEADER *)CurrPtr)->State = VAR_ADDED;
+    if (UpdatingVariable != NULL) {
+      UpdatingPtrTrack->CurrPtr                = (VARIABLE_HEADER *)((UINTN)UpdatingPtrTrack->StartPtr + ((UINTN)CurrPtr - (UINTN)GetStartPointer ((VARIABLE_STORE_HEADER *)ValidBuffer)));
+      UpdatingPtrTrack->InDeletedTransitionPtr = NULL;
+    }
+
+    CurrPtr += NewVariableSize;
+  }
+
+  if (IsVolatile || mVariableModuleGlobal->VariableGlobal.EmuNvMode) {
+    //
+    // If volatile/emulated non-volatile variable store, just copy valid buffer.
+    //
+    SetMem ((UINT8 *)(UINTN)VariableBase, VariableStoreHeader->Size, 0xff);
+    CopyMem ((UINT8 *)(UINTN)VariableBase, ValidBuffer, (UINTN)CurrPtr - (UINTN)ValidBuffer);
+    *LastVariableOffset = (UINTN)CurrPtr - (UINTN)ValidBuffer;
+    if (!IsVolatile) {
+      //
+      // Emulated non-volatile variable mode.
+      //
+      mVariableModuleGlobal->HwErrVariableTotalSize      = HwErrVariableTotalSize;
+      mVariableModuleGlobal->CommonVariableTotalSize     = CommonVariableTotalSize;
+      mVariableModuleGlobal->CommonUserVariableTotalSize = CommonUserVariableTotalSize;
+    }
+
+    Status = EFI_SUCCESS;
+  } else {
+    //
+    // If non-volatile variable store, perform FTW here.
+    //
+    Status = FtwVariableSpace (
+               VariableBase,
+               (VARIABLE_STORE_HEADER *)ValidBuffer
+               );
+    if (!EFI_ERROR (Status)) {
+      *LastVariableOffset                                = (UINTN)CurrPtr - (UINTN)ValidBuffer;
+      mVariableModuleGlobal->HwErrVariableTotalSize      = HwErrVariableTotalSize;
+      mVariableModuleGlobal->CommonVariableTotalSize     = CommonVariableTotalSize;
+      mVariableModuleGlobal->CommonUserVariableTotalSize = CommonUserVariableTotalSize;
+    } else {
+      mVariableModuleGlobal->HwErrVariableTotalSize      = 0;
+      mVariableModuleGlobal->CommonVariableTotalSize     = 0;
+      mVariableModuleGlobal->CommonUserVariableTotalSize = 0;
+      Variable                                           = GetStartPointer ((VARIABLE_STORE_HEADER *)(UINTN)VariableBase);
+      while (IsValidVariableHeader (Variable, GetEndPointer ((VARIABLE_STORE_HEADER *)(UINTN)VariableBase), AuthFormat)) {
+        NextVariable = GetNextVariablePtr (Variable, AuthFormat);
+        VariableSize = (UINTN)NextVariable - (UINTN)Variable;
+        if ((Variable->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == EFI_VARIABLE_HARDWARE_ERROR_RECORD) {
+          mVariableModuleGlobal->HwErrVariableTotalSize += VariableSize;
+        } else if ((Variable->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) != EFI_VARIABLE_HARDWARE_ERROR_RECORD) {
+          mVariableModuleGlobal->CommonVariableTotalSize += VariableSize;
+          if (IsUserVariable (Variable)) {
+            mVariableModuleGlobal->CommonUserVariableTotalSize += VariableSize;
+          }
+        }
+
+        Variable = NextVariable;
+      }
+
+      *LastVariableOffset = (UINTN)Variable - (UINTN)VariableBase;
+    }
+  }
+
+Done:
+  DoneStatus = EFI_SUCCESS;
+  if (IsVolatile || mVariableModuleGlobal->VariableGlobal.EmuNvMode) {
+    DoneStatus = SynchronizeRuntimeVariableCache (
+                   &mVariableModuleGlobal->VariableGlobal.VariableRuntimeCacheContext.VariableRuntimeVolatileCache,
+                   0,
+                   VariableStoreHeader->Size
+                   );
+    ASSERT_EFI_ERROR (DoneStatus);
+    FreePool (ValidBuffer);
+  } else {
+    //
+    // For NV variable reclaim, we use mNvVariableCache as the buffer, so copy the data back.
+    //
+    CopyMem (mNvVariableCache, (UINT8 *)(UINTN)VariableBase, VariableStoreHeader->Size);
+    DoneStatus = SynchronizeRuntimeVariableCache (
+                   &mVariableModuleGlobal->VariableGlobal.VariableRuntimeCacheContext.VariableRuntimeNvCache,
+                   0,
+                   VariableStoreHeader->Size
+                   );
+    ASSERT_EFI_ERROR (DoneStatus);
+  }
+
+  if (!EFI_ERROR (Status) && EFI_ERROR (DoneStatus)) {
+    Status = DoneStatus;
+  }
+
+  return Status;
+}
diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.c b/MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.c
index 6c1a3440ac8c..19b432b772d7 100644
--- a/MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.c
+++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.c
@@ -16,7 +16,7 @@
   VariableServiceSetVariable() should also check authenticate data to avoid buffer overflow,
   integer overflow. It should also check attribute to avoid authentication bypass.
 
-Copyright (c) 2006 - 2020, Intel Corporation. All rights reserved.<BR>
+Copyright (c) 2006 - 2022, Intel Corporation. All rights reserved.<BR>
 (C) Copyright 2015-2018 Hewlett Packard Enterprise Development LP<BR>
 Copyright (c) Microsoft Corporation.<BR>
 Copyright (c) 2022, ARM Limited. All rights reserved.<BR>
@@ -30,7 +30,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
 #include "VariableParsing.h"
 #include "VariableRuntimeCache.h"
 
-VARIABLE_MODULE_GLOBAL  *mVariableModuleGlobal;
+VARIABLE_MODULE_GLOBAL  *mVariableModuleGlobal = NULL;
 
 ///
 /// Define a memory cache that improves the search performance for a variable.
@@ -458,7 +458,7 @@ CalculateCommonUserVariableTotalSize (
   //
   if (mEndOfDxe && (mVariableModuleGlobal->CommonMaxUserVariableSpace != mVariableModuleGlobal->CommonVariableSpace)) {
     Variable = GetStartPointer (mNvVariableCache);
-    while (IsValidVariableHeader (Variable, GetEndPointer (mNvVariableCache))) {
+    while (IsValidVariableHeader (Variable, GetEndPointer (mNvVariableCache), mVariableModuleGlobal->VariableGlobal.AuthFormat)) {
       NextVariable = GetNextVariablePtr (Variable, mVariableModuleGlobal->VariableGlobal.AuthFormat);
       VariableSize = (UINTN)NextVariable - (UINTN)Variable;
       if ((Variable->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) != EFI_VARIABLE_HARDWARE_ERROR_RECORD) {
@@ -497,330 +497,6 @@ InitializeVariableQuota (
   CalculateCommonUserVariableTotalSize ();
 }
 
-/**
-
-  Variable store garbage collection and reclaim operation.
-
-  @param[in]      VariableBase            Base address of variable store.
-  @param[out]     LastVariableOffset      Offset of last variable.
-  @param[in]      IsVolatile              The variable store is volatile or not;
-                                          if it is non-volatile, need FTW.
-  @param[in, out] UpdatingPtrTrack        Pointer to updating variable pointer track structure.
-  @param[in]      NewVariable             Pointer to new variable.
-  @param[in]      NewVariableSize         New variable size.
-
-  @return EFI_SUCCESS                  Reclaim operation has finished successfully.
-  @return EFI_OUT_OF_RESOURCES         No enough memory resources or variable space.
-  @return Others                       Unexpect error happened during reclaim operation.
-
-**/
-EFI_STATUS
-Reclaim (
-  IN     EFI_PHYSICAL_ADDRESS    VariableBase,
-  OUT    UINTN                   *LastVariableOffset,
-  IN     BOOLEAN                 IsVolatile,
-  IN OUT VARIABLE_POINTER_TRACK  *UpdatingPtrTrack,
-  IN     VARIABLE_HEADER         *NewVariable,
-  IN     UINTN                   NewVariableSize
-  )
-{
-  VARIABLE_HEADER        *Variable;
-  VARIABLE_HEADER        *AddedVariable;
-  VARIABLE_HEADER        *NextVariable;
-  VARIABLE_HEADER        *NextAddedVariable;
-  VARIABLE_STORE_HEADER  *VariableStoreHeader;
-  UINT8                  *ValidBuffer;
-  UINTN                  MaximumBufferSize;
-  UINTN                  VariableSize;
-  UINTN                  NameSize;
-  UINT8                  *CurrPtr;
-  VOID                   *Point0;
-  VOID                   *Point1;
-  BOOLEAN                FoundAdded;
-  EFI_STATUS             Status;
-  EFI_STATUS             DoneStatus;
-  UINTN                  CommonVariableTotalSize;
-  UINTN                  CommonUserVariableTotalSize;
-  UINTN                  HwErrVariableTotalSize;
-  VARIABLE_HEADER        *UpdatingVariable;
-  VARIABLE_HEADER        *UpdatingInDeletedTransition;
-  BOOLEAN                AuthFormat;
-
-  AuthFormat                  = mVariableModuleGlobal->VariableGlobal.AuthFormat;
-  UpdatingVariable            = NULL;
-  UpdatingInDeletedTransition = NULL;
-  if (UpdatingPtrTrack != NULL) {
-    UpdatingVariable            = UpdatingPtrTrack->CurrPtr;
-    UpdatingInDeletedTransition = UpdatingPtrTrack->InDeletedTransitionPtr;
-  }
-
-  VariableStoreHeader = (VARIABLE_STORE_HEADER *)((UINTN)VariableBase);
-
-  CommonVariableTotalSize     = 0;
-  CommonUserVariableTotalSize = 0;
-  HwErrVariableTotalSize      = 0;
-
-  if (IsVolatile || mVariableModuleGlobal->VariableGlobal.EmuNvMode) {
-    //
-    // Start Pointers for the variable.
-    //
-    Variable          = GetStartPointer (VariableStoreHeader);
-    MaximumBufferSize = sizeof (VARIABLE_STORE_HEADER);
-
-    while (IsValidVariableHeader (Variable, GetEndPointer (VariableStoreHeader))) {
-      NextVariable = GetNextVariablePtr (Variable, AuthFormat);
-      if (((Variable->State == VAR_ADDED) || (Variable->State == (VAR_IN_DELETED_TRANSITION & VAR_ADDED))) &&
-          (Variable != UpdatingVariable) &&
-          (Variable != UpdatingInDeletedTransition)
-          )
-      {
-        VariableSize       = (UINTN)NextVariable - (UINTN)Variable;
-        MaximumBufferSize += VariableSize;
-      }
-
-      Variable = NextVariable;
-    }
-
-    if (NewVariable != NULL) {
-      //
-      // Add the new variable size.
-      //
-      MaximumBufferSize += NewVariableSize;
-    }
-
-    //
-    // Reserve the 1 Bytes with Oxff to identify the
-    // end of the variable buffer.
-    //
-    MaximumBufferSize += 1;
-    ValidBuffer        = AllocatePool (MaximumBufferSize);
-    if (ValidBuffer == NULL) {
-      return EFI_OUT_OF_RESOURCES;
-    }
-  } else {
-    //
-    // For NV variable reclaim, don't allocate pool here and just use mNvVariableCache
-    // as the buffer to reduce SMRAM consumption for SMM variable driver.
-    //
-    MaximumBufferSize = mNvVariableCache->Size;
-    ValidBuffer       = (UINT8 *)mNvVariableCache;
-  }
-
-  SetMem (ValidBuffer, MaximumBufferSize, 0xff);
-
-  //
-  // Copy variable store header.
-  //
-  CopyMem (ValidBuffer, VariableStoreHeader, sizeof (VARIABLE_STORE_HEADER));
-  CurrPtr = (UINT8 *)GetStartPointer ((VARIABLE_STORE_HEADER *)ValidBuffer);
-
-  //
-  // Reinstall all ADDED variables as long as they are not identical to Updating Variable.
-  //
-  Variable = GetStartPointer (VariableStoreHeader);
-  while (IsValidVariableHeader (Variable, GetEndPointer (VariableStoreHeader))) {
-    NextVariable = GetNextVariablePtr (Variable, AuthFormat);
-    if ((Variable != UpdatingVariable) && (Variable->State == VAR_ADDED)) {
-      VariableSize = (UINTN)NextVariable - (UINTN)Variable;
-      CopyMem (CurrPtr, (UINT8 *)Variable, VariableSize);
-      CurrPtr += VariableSize;
-      if ((!IsVolatile) && ((Variable->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == EFI_VARIABLE_HARDWARE_ERROR_RECORD)) {
-        HwErrVariableTotalSize += VariableSize;
-      } else if ((!IsVolatile) && ((Variable->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) != EFI_VARIABLE_HARDWARE_ERROR_RECORD)) {
-        CommonVariableTotalSize += VariableSize;
-        if (IsUserVariable (Variable)) {
-          CommonUserVariableTotalSize += VariableSize;
-        }
-      }
-    }
-
-    Variable = NextVariable;
-  }
-
-  //
-  // Reinstall all in delete transition variables.
-  //
-  Variable = GetStartPointer (VariableStoreHeader);
-  while (IsValidVariableHeader (Variable, GetEndPointer (VariableStoreHeader))) {
-    NextVariable = GetNextVariablePtr (Variable, AuthFormat);
-    if ((Variable != UpdatingVariable) && (Variable != UpdatingInDeletedTransition) && (Variable->State == (VAR_IN_DELETED_TRANSITION & VAR_ADDED))) {
-      //
-      // Buffer has cached all ADDED variable.
-      // Per IN_DELETED variable, we have to guarantee that
-      // no ADDED one in previous buffer.
-      //
-
-      FoundAdded    = FALSE;
-      AddedVariable = GetStartPointer ((VARIABLE_STORE_HEADER *)ValidBuffer);
-      while (IsValidVariableHeader (AddedVariable, GetEndPointer ((VARIABLE_STORE_HEADER *)ValidBuffer))) {
-        NextAddedVariable = GetNextVariablePtr (AddedVariable, AuthFormat);
-        NameSize          = NameSizeOfVariable (AddedVariable, AuthFormat);
-        if (CompareGuid (
-              GetVendorGuidPtr (AddedVariable, AuthFormat),
-              GetVendorGuidPtr (Variable, AuthFormat)
-              ) && (NameSize == NameSizeOfVariable (Variable, AuthFormat)))
-        {
-          Point0 = (VOID *)GetVariableNamePtr (AddedVariable, AuthFormat);
-          Point1 = (VOID *)GetVariableNamePtr (Variable, AuthFormat);
-          if (CompareMem (Point0, Point1, NameSize) == 0) {
-            FoundAdded = TRUE;
-            break;
-          }
-        }
-
-        AddedVariable = NextAddedVariable;
-      }
-
-      if (!FoundAdded) {
-        //
-        // Promote VAR_IN_DELETED_TRANSITION to VAR_ADDED.
-        //
-        VariableSize = (UINTN)NextVariable - (UINTN)Variable;
-        CopyMem (CurrPtr, (UINT8 *)Variable, VariableSize);
-        ((VARIABLE_HEADER *)CurrPtr)->State = VAR_ADDED;
-        CurrPtr                            += VariableSize;
-        if ((!IsVolatile) && ((Variable->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == EFI_VARIABLE_HARDWARE_ERROR_RECORD)) {
-          HwErrVariableTotalSize += VariableSize;
-        } else if ((!IsVolatile) && ((Variable->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) != EFI_VARIABLE_HARDWARE_ERROR_RECORD)) {
-          CommonVariableTotalSize += VariableSize;
-          if (IsUserVariable (Variable)) {
-            CommonUserVariableTotalSize += VariableSize;
-          }
-        }
-      }
-    }
-
-    Variable = NextVariable;
-  }
-
-  //
-  // Install the new variable if it is not NULL.
-  //
-  if (NewVariable != NULL) {
-    if (((UINTN)CurrPtr - (UINTN)ValidBuffer) + NewVariableSize > VariableStoreHeader->Size) {
-      //
-      // No enough space to store the new variable.
-      //
-      Status = EFI_OUT_OF_RESOURCES;
-      goto Done;
-    }
-
-    if (!IsVolatile) {
-      if ((NewVariable->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == EFI_VARIABLE_HARDWARE_ERROR_RECORD) {
-        HwErrVariableTotalSize += NewVariableSize;
-      } else if ((NewVariable->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) != EFI_VARIABLE_HARDWARE_ERROR_RECORD) {
-        CommonVariableTotalSize += NewVariableSize;
-        if (IsUserVariable (NewVariable)) {
-          CommonUserVariableTotalSize += NewVariableSize;
-        }
-      }
-
-      if ((HwErrVariableTotalSize > PcdGet32 (PcdHwErrStorageSize)) ||
-          (CommonVariableTotalSize > mVariableModuleGlobal->CommonVariableSpace) ||
-          (CommonUserVariableTotalSize > mVariableModuleGlobal->CommonMaxUserVariableSpace))
-      {
-        //
-        // No enough space to store the new variable by NV or NV+HR attribute.
-        //
-        Status = EFI_OUT_OF_RESOURCES;
-        goto Done;
-      }
-    }
-
-    CopyMem (CurrPtr, (UINT8 *)NewVariable, NewVariableSize);
-    ((VARIABLE_HEADER *)CurrPtr)->State = VAR_ADDED;
-    if (UpdatingVariable != NULL) {
-      UpdatingPtrTrack->CurrPtr                = (VARIABLE_HEADER *)((UINTN)UpdatingPtrTrack->StartPtr + ((UINTN)CurrPtr - (UINTN)GetStartPointer ((VARIABLE_STORE_HEADER *)ValidBuffer)));
-      UpdatingPtrTrack->InDeletedTransitionPtr = NULL;
-    }
-
-    CurrPtr += NewVariableSize;
-  }
-
-  if (IsVolatile || mVariableModuleGlobal->VariableGlobal.EmuNvMode) {
-    //
-    // If volatile/emulated non-volatile variable store, just copy valid buffer.
-    //
-    SetMem ((UINT8 *)(UINTN)VariableBase, VariableStoreHeader->Size, 0xff);
-    CopyMem ((UINT8 *)(UINTN)VariableBase, ValidBuffer, (UINTN)CurrPtr - (UINTN)ValidBuffer);
-    *LastVariableOffset = (UINTN)CurrPtr - (UINTN)ValidBuffer;
-    if (!IsVolatile) {
-      //
-      // Emulated non-volatile variable mode.
-      //
-      mVariableModuleGlobal->HwErrVariableTotalSize      = HwErrVariableTotalSize;
-      mVariableModuleGlobal->CommonVariableTotalSize     = CommonVariableTotalSize;
-      mVariableModuleGlobal->CommonUserVariableTotalSize = CommonUserVariableTotalSize;
-    }
-
-    Status = EFI_SUCCESS;
-  } else {
-    //
-    // If non-volatile variable store, perform FTW here.
-    //
-    Status = FtwVariableSpace (
-               VariableBase,
-               (VARIABLE_STORE_HEADER *)ValidBuffer
-               );
-    if (!EFI_ERROR (Status)) {
-      *LastVariableOffset                                = (UINTN)CurrPtr - (UINTN)ValidBuffer;
-      mVariableModuleGlobal->HwErrVariableTotalSize      = HwErrVariableTotalSize;
-      mVariableModuleGlobal->CommonVariableTotalSize     = CommonVariableTotalSize;
-      mVariableModuleGlobal->CommonUserVariableTotalSize = CommonUserVariableTotalSize;
-    } else {
-      mVariableModuleGlobal->HwErrVariableTotalSize      = 0;
-      mVariableModuleGlobal->CommonVariableTotalSize     = 0;
-      mVariableModuleGlobal->CommonUserVariableTotalSize = 0;
-      Variable                                           = GetStartPointer ((VARIABLE_STORE_HEADER *)(UINTN)VariableBase);
-      while (IsValidVariableHeader (Variable, GetEndPointer ((VARIABLE_STORE_HEADER *)(UINTN)VariableBase))) {
-        NextVariable = GetNextVariablePtr (Variable, AuthFormat);
-        VariableSize = (UINTN)NextVariable - (UINTN)Variable;
-        if ((Variable->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == EFI_VARIABLE_HARDWARE_ERROR_RECORD) {
-          mVariableModuleGlobal->HwErrVariableTotalSize += VariableSize;
-        } else if ((Variable->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) != EFI_VARIABLE_HARDWARE_ERROR_RECORD) {
-          mVariableModuleGlobal->CommonVariableTotalSize += VariableSize;
-          if (IsUserVariable (Variable)) {
-            mVariableModuleGlobal->CommonUserVariableTotalSize += VariableSize;
-          }
-        }
-
-        Variable = NextVariable;
-      }
-
-      *LastVariableOffset = (UINTN)Variable - (UINTN)VariableBase;
-    }
-  }
-
-Done:
-  DoneStatus = EFI_SUCCESS;
-  if (IsVolatile || mVariableModuleGlobal->VariableGlobal.EmuNvMode) {
-    DoneStatus = SynchronizeRuntimeVariableCache (
-                   &mVariableModuleGlobal->VariableGlobal.VariableRuntimeCacheContext.VariableRuntimeVolatileCache,
-                   0,
-                   VariableStoreHeader->Size
-                   );
-    ASSERT_EFI_ERROR (DoneStatus);
-    FreePool (ValidBuffer);
-  } else {
-    //
-    // For NV variable reclaim, we use mNvVariableCache as the buffer, so copy the data back.
-    //
-    CopyMem (mNvVariableCache, (UINT8 *)(UINTN)VariableBase, VariableStoreHeader->Size);
-    DoneStatus = SynchronizeRuntimeVariableCache (
-                   &mVariableModuleGlobal->VariableGlobal.VariableRuntimeCacheContext.VariableRuntimeNvCache,
-                   0,
-                   VariableStoreHeader->Size
-                   );
-    ASSERT_EFI_ERROR (DoneStatus);
-  }
-
-  if (!EFI_ERROR (Status) && EFI_ERROR (DoneStatus)) {
-    Status = DoneStatus;
-  }
-
-  return Status;
-}
-
 /**
   Finds variable in storage blocks of volatile and non-volatile storage areas.
 
@@ -1657,9 +1333,665 @@ AutoUpdateLangVariable (
 }
 
 /**
-  Update the variable region with Variable information. If EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS is set,
-  index of associated public key is needed.
+  Check if there's enough free space in storage to write the new variable.
 
+  @param[in] NewVariable        Pointer to buffer of new variable.
+  @param[in] VariableSize       Size of new variable.
+  @param[in] VariableName       Name of variable.
+  @param[in] VendorGuid         Guid of variable.
+  @param[in] Attributes         Attributes of the variable.
+  @param[in] VolatileFlag       Volatile/non-volatile variable indicator.
+
+  @retval EFI_SUCCESS           Enough free space on variable storage.
+  @retval EFI_BUFFER_TOO_SMALL  There's not enough continuous free space.
+  @retval EFI_OUT_OF_RESOURCES  There's not enough free space in total.
+**/
+EFI_STATUS
+CheckVariableStoreSpace (
+  IN  VARIABLE_HEADER  *NewVariable,
+  IN  UINTN            VariableSize,
+  IN  CHAR16           *VariableName,
+  IN  EFI_GUID         *VendorGuid,
+  IN  UINT32           Attributes,
+  IN  BOOLEAN          VolatileFlag
+  )
+{
+  BOOLEAN                IsCommonVariable;
+  BOOLEAN                IsCommonUserVariable;
+  UINTN                  CommonVariableTotalSize;
+  UINTN                  CommonUserVariableTotalSize;
+  UINTN                  HwErrVariableTotalSize;
+  VARIABLE_STORE_HEADER  *VarStore;
+
+  if ((NewVariable == NULL) || (VariableSize == 0)) {
+    return EFI_SUCCESS;
+  }
+
+  if (VolatileFlag) {
+    VarStore = (VARIABLE_STORE_HEADER *)(UINTN)
+               mVariableModuleGlobal->VariableGlobal.VolatileVariableBase;
+    if ((UINT32)(VariableSize + mVariableModuleGlobal->VolatileLastVariableOffset)
+        > VarStore->Size)
+    {
+      return EFI_BUFFER_TOO_SMALL;
+    }
+  } else {
+    if ((Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == 0) {
+      IsCommonVariable     = TRUE;
+      IsCommonUserVariable = IsUserVariable (NewVariable);
+    } else {
+      IsCommonVariable     = FALSE;
+      IsCommonUserVariable = FALSE;
+    }
+
+    CommonVariableTotalSize     = mVariableModuleGlobal->CommonVariableTotalSize + VariableSize;
+    CommonUserVariableTotalSize = mVariableModuleGlobal->CommonUserVariableTotalSize + VariableSize;
+    HwErrVariableTotalSize      = mVariableModuleGlobal->HwErrVariableTotalSize + VariableSize;
+
+    if (  (((Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) != 0) &&
+           (HwErrVariableTotalSize > PcdGet32 (PcdHwErrStorageSize)))
+       || (IsCommonVariable && (CommonVariableTotalSize > mVariableModuleGlobal->CommonVariableSpace))
+       || (IsCommonVariable &&
+           AtRuntime () &&
+           (CommonVariableTotalSize > mVariableModuleGlobal->CommonRuntimeVariableSpace))
+       || (IsCommonUserVariable &&
+           (CommonUserVariableTotalSize > mVariableModuleGlobal->CommonMaxUserVariableSpace)))
+    {
+      if (AtRuntime ()) {
+        if (IsCommonUserVariable &&
+            ((VariableSize + mVariableModuleGlobal->CommonUserVariableTotalSize)
+             > mVariableModuleGlobal->CommonMaxUserVariableSpace))
+        {
+          RecordVarErrorFlag (
+            VAR_ERROR_FLAG_USER_ERROR,
+            VariableName,
+            VendorGuid,
+            Attributes,
+            VariableSize
+            );
+        }
+
+        if (IsCommonVariable &&
+            ((VariableSize + mVariableModuleGlobal->CommonVariableTotalSize)
+             > mVariableModuleGlobal->CommonRuntimeVariableSpace))
+        {
+          RecordVarErrorFlag (
+            VAR_ERROR_FLAG_SYSTEM_ERROR,
+            VariableName,
+            VendorGuid,
+            Attributes,
+            VariableSize
+            );
+        }
+
+        return EFI_OUT_OF_RESOURCES;
+      }
+
+      return EFI_BUFFER_TOO_SMALL;
+    }
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Fill specific data of auth-variable in buffer.
+
+  @param[in,out]  NewVariable        Pointer to buffer of new variable.
+  @param[in]      OldVariable        Pointer to buffer of old copy of the variable.
+  @param[in]      Attributes         Attributes of the variable.
+  @param[in]      KeyIndex           Index of associated public key.
+  @param[in]      MonotonicCount     Value of associated monotonic count.
+  @param[in]      TimeStamp          Value of associated TimeStamp.
+
+**/
+VOID
+SetVariableAuthData (
+  IN  OUT AUTHENTICATED_VARIABLE_HEADER  *NewVariable,
+  IN      AUTHENTICATED_VARIABLE_HEADER  *OldVariable,
+  IN      UINT32                         Attributes,
+  IN      UINT32                         KeyIndex,
+  IN      UINT64                         MonotonicCount,
+  IN      EFI_TIME                       *TimeStamp
+  )
+{
+  NewVariable->PubKeyIndex    = KeyIndex;
+  NewVariable->MonotonicCount = MonotonicCount;
+
+  if ((TimeStamp != NULL) &&
+      ((Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) != 0))
+  {
+    //
+    // In the case when the EFI_VARIABLE_APPEND_WRITE attribute is set, only
+    // when the new TimeStamp value is later than the current timestamp associated
+    // with the variable, we need associate the new timestamp with the updated value.
+    //
+    if (((Attributes & EFI_VARIABLE_APPEND_WRITE) != 0) &&
+        (OldVariable != NULL) &&
+        !VariableCompareTimeStampInternal (&OldVariable->TimeStamp, TimeStamp))
+    {
+      TimeStamp = &OldVariable->TimeStamp;
+    }
+
+    CopyMem (&NewVariable->TimeStamp, TimeStamp, sizeof (EFI_TIME));
+  } else {
+    ZeroMem (&NewVariable->TimeStamp, sizeof (EFI_TIME));
+  }
+}
+
+/**
+  Fill the variable data buffer according to variable format on storage.
+
+  @param[in,out]  NewVariable        Pointer to buffer of new variable.
+  @param[in]      OldVariable        Pointer to buffer of old copy of the variable.
+  @param[in]      VariableName       Name of variable.
+  @param[in]      VendorGuid         Guid of variable.
+  @param[in]      Data               Variable data.
+  @param[in]      DataSize           Size of data. 0 means delete.
+  @param[in]      Attributes         Attributes of the variable.
+  @param[in]      KeyIndex           Index of associated public key.
+  @param[in]      MonotonicCount     Value of associated monotonic count.
+  @param[in]      TimeStamp          Value of associated TimeStamp.
+
+  @retval Size of the new variable.
+
+**/
+UINTN
+SetVariableData (
+  IN  OUT VARIABLE_HEADER  *NewVariable,
+  IN      VARIABLE_HEADER  *OldVariable,
+  IN      CHAR16           *VariableName,
+  IN      EFI_GUID         *VendorGuid,
+  IN      VOID             *Data,
+  IN      UINTN            DataSize,
+  IN      UINT32           Attributes,
+  IN      UINT32           KeyIndex,
+  IN      UINT64           MonotonicCount,
+  IN      EFI_TIME         *TimeStamp
+  )
+{
+  EFI_STATUS  Status;
+  BOOLEAN     AuthFormat;
+  UINT8       *DataPtr;
+  UINTN       NameSize;
+  UINTN       OldDataSize;
+
+  AuthFormat = mVariableModuleGlobal->VariableGlobal.AuthFormat;
+
+  if (AuthFormat) {
+    SetVariableAuthData (
+      (AUTHENTICATED_VARIABLE_HEADER *)NewVariable,
+      (AUTHENTICATED_VARIABLE_HEADER *)OldVariable,
+      Attributes,
+      KeyIndex,
+      MonotonicCount,
+      TimeStamp
+      );
+  }
+
+  NewVariable->StartId    = VARIABLE_DATA;
+  NewVariable->State      = VAR_ADDED;
+  NewVariable->Reserved   = 0;
+  NewVariable->Attributes = Attributes & (~EFI_VARIABLE_APPEND_WRITE);
+
+  CopyMem (
+    GetVendorGuidPtr (NewVariable, AuthFormat),
+    VendorGuid,
+    sizeof (EFI_GUID)
+    );
+
+  NameSize = StrSize (VariableName);
+  SetNameSizeOfVariable (NewVariable, NameSize, AuthFormat);
+  CopyMem (
+    (UINT8 *)GetVariableNamePtr (NewVariable, AuthFormat),
+    VariableName,
+    NameSize
+    );
+
+  //
+  // Set data size first otherwise we can't get correct data pointer in the
+  // buffer of new variable.
+  //
+  SetDataSizeOfVariable (NewVariable, DataSize, AuthFormat);
+  DataPtr = GetVariableDataPtr (NewVariable, AuthFormat);
+  if (((Attributes & EFI_VARIABLE_APPEND_WRITE) != 0) &&
+      (OldVariable != NULL) &&
+      ((OldVariable->State == VAR_ADDED) ||
+       (OldVariable->State == (VAR_ADDED & VAR_IN_DELETED_TRANSITION))))
+  {
+    //
+    // Get old data, which might be encrypted.
+    //
+    OldDataSize = mVariableModuleGlobal->ScratchBufferSize
+                  - ((UINTN)DataPtr - (UINTN)NewVariable);
+    Status = ProtectedVariableLibGetByBuffer (
+               OldVariable,
+               DataPtr,
+               (UINT32 *)&OldDataSize,
+               AuthFormat
+               );
+    if (Status == EFI_UNSUPPORTED) {
+      OldDataSize = DataSizeOfVariable (OldVariable, AuthFormat);
+      CopyMem (DataPtr, GetVariableDataPtr (OldVariable, AuthFormat), OldDataSize);
+    } else if (EFI_ERROR (Status)) {
+      ASSERT_EFI_ERROR (Status);
+      return 0;
+    }
+
+    DataPtr += OldDataSize;
+    //
+    // Update data size.
+    //
+    SetDataSizeOfVariable (NewVariable, DataSize + OldDataSize, AuthFormat);
+  }
+
+  CopyMem (DataPtr, Data, DataSize);
+
+  //
+  // The actual size of the variable stored in storage should include padding.
+  //
+  return ((UINTN)GetNextVariablePtr (NewVariable, AuthFormat) - (UINTN)NewVariable);
+}
+
+/**
+  Update state of given variable as well as its cached copy.
+
+  @param[in,out]  Variable        Pointer to the buffer of the variable.
+  @param[in,out]  CacheVariable   Cache copy of the variable.
+  @param[in]      NewState        New state value.
+  @param[in]      Volatile        Volatile/non-volatile variable indicator.
+
+  @retval EFI_SUCCESS     Variable state was updated successfully.
+  @retval Others          Failed to update the variable state.
+
+**/
+EFI_STATUS
+UpdateVariableState (
+  IN  OUT VARIABLE_HEADER  *Variable,
+  IN  OUT VARIABLE_HEADER  *CacheVariable,
+  IN      UINT8            NewState,
+  IN      BOOLEAN          Volatile
+  )
+{
+  EFI_STATUS  Status;
+
+  Status = UpdateVariableStore (
+             &mVariableModuleGlobal->VariableGlobal,
+             Volatile,
+             FALSE,
+             mVariableModuleGlobal->FvbInstance,
+             (UINTN)&Variable->State,
+             sizeof (NewState),
+             &NewState
+             );
+  if (!EFI_ERROR (Status) && (CacheVariable != NULL)) {
+    CacheVariable->State = NewState;
+  }
+
+  return Status;
+}
+
+/**
+  Flush variable data to variable storage.
+
+  @param[in]      VarStoreBase    Base address of variable storage.
+  @param[in,out]  Offset          Offset to write the variable from.
+                                  Offset from where next variable can be written.
+  @param[in,out]  NewVariable     Pointer to the buffer of new variable.
+  @param[in]      VariableSize    Size of new variable.
+  @param[in]      Volatile        Volatile/non-volatile variable indicator.
+  @param[in]      AuthFormat      Auth-variable indicator.
+
+  @retval EFI_SUCCESS     Variable(s) were written successfully.
+  @retval Others          Failed to write the variable data.
+
+**/
+EFI_STATUS
+WriteVariable (
+  IN  EFI_PHYSICAL_ADDRESS  VarStoreBase,
+  IN  OUT UINTN             *Offset,
+  IN  OUT VARIABLE_HEADER   **NewVariable,
+  IN      UINT32            VariableSize,
+  IN      BOOLEAN           Volatile,
+  IN      BOOLEAN           AuthFormat
+  )
+{
+  EFI_STATUS  Status;
+
+  struct {
+    UINTN     Offset;
+    UINT8     *Buffer;
+    UINT32    Size;
+    UINT8     State;
+  }                   WriteSteps[4];
+  UINTN            Index;
+  UINTN            Steps;
+  VARIABLE_HEADER  *Variable;
+
+  Variable = *NewVariable;
+  if (Volatile) {
+    //
+    // For non-volatile variable, one step only :
+    //
+    WriteSteps[0].Offset = *Offset;
+    WriteSteps[0].Buffer = (UINT8 *)Variable;
+    WriteSteps[0].Size   = VariableSize;
+
+    Steps = 1;
+  } else {
+    //
+    // Four steps for non-volatile variable:
+    //
+    // 1. Write variable header
+    // 2. Set variable state to header valid
+    // 3. Write variable name and data
+    // 4. Set variable state to valid
+    //
+    Variable->State      = 0xff;
+    WriteSteps[0].Offset = *Offset;
+    WriteSteps[0].Buffer = (UINT8 *)Variable;
+    WriteSteps[0].Size   = (UINT32)GetVariableHeaderSize (AuthFormat);
+
+    WriteSteps[1].State  = VAR_HEADER_VALID_ONLY;
+    WriteSteps[1].Offset = *Offset + OFFSET_OF (VARIABLE_HEADER, State);
+    WriteSteps[1].Buffer = &WriteSteps[1].State;
+    WriteSteps[1].Size   = sizeof (Variable->State);
+
+    WriteSteps[2].Offset = *Offset + GetVariableHeaderSize (AuthFormat);
+    WriteSteps[2].Buffer = (UINT8 *)Variable + GetVariableHeaderSize (AuthFormat);
+    WriteSteps[2].Size   = VariableSize - (UINT32)GetVariableHeaderSize (AuthFormat);
+
+    WriteSteps[3].State  = VAR_ADDED;
+    WriteSteps[3].Offset = *Offset + OFFSET_OF (VARIABLE_HEADER, State);
+    WriteSteps[3].Buffer = &WriteSteps[3].State;
+    WriteSteps[3].Size   = sizeof (Variable->State);
+
+    Steps = ARRAY_SIZE (WriteSteps);
+  }
+
+  for (Index = 0; Index < Steps; ++Index) {
+    Status = UpdateVariableStore (
+               &mVariableModuleGlobal->VariableGlobal,
+               Volatile,
+               TRUE,
+               mVariableModuleGlobal->FvbInstance,
+               WriteSteps[Index].Offset,
+               WriteSteps[Index].Size,
+               WriteSteps[Index].Buffer
+               );
+    if (EFI_ERROR (Status)) {
+      ASSERT_EFI_ERROR (Status);
+      return Status;
+    }
+  }
+
+  Variable->State = VAR_ADDED;
+  if (!Volatile) {
+    CopyMem ((UINT8 *)mNvVariableCache + *Offset, Variable, VariableSize);
+  }
+
+  *NewVariable = (VARIABLE_HEADER *)((UINTN)VarStoreBase + *Offset);
+  *Offset     += HEADER_ALIGN (VariableSize);
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Rebase the given variable pointer(s) to the equivalent one in given variable
+  storage via VarStore.
+
+  @param[in]      InVarTrackPtr     Pointer to current variable in cache.
+  @param[out]     OutVarTrackPtr    Pointer to rebased variable against VarStore.
+  @param[in]      VarStore          Start of variable storage to rebase against.
+  @param[in]      VariableName      Name of variable.
+  @param[in]      VendorGuid        Guid of variable.
+  @param[in]      ByOffset          If TRUE, don't do variable search in VarStore.
+
+  @retval EFI_SUCCESS           Variable(s) were deleted successfully.
+  @retval EFI_INVALID_PARAMETER Invalid parameters passed.
+  @retval EFI_NOT_FOUND         Given variable (VariableName & VendorGuid) was
+                                not found in VarStore, if ByOffset is FALSE.
+
+**/
+EFI_STATUS
+RebaseVariablePtr (
+  IN      VARIABLE_POINTER_TRACK  *InVarTrackPtr,
+  OUT VARIABLE_POINTER_TRACK      *OutVarTrackPtr,
+  IN      VARIABLE_STORE_HEADER   *VarStore,
+  IN      CHAR16                  *VariableName,
+  IN      EFI_GUID                *VendorGuid,
+  IN      BOOLEAN                 ByOffset
+  )
+{
+  EFI_STATUS       Status;
+  BOOLEAN          AuthFormat;
+  VARIABLE_HEADER  *NewStart;
+
+  if ((InVarTrackPtr == NULL) || (OutVarTrackPtr == NULL) || (VarStore == NULL)) {
+    ASSERT (InVarTrackPtr != NULL);
+    ASSERT (OutVarTrackPtr != NULL);
+    ASSERT (VarStore != NULL);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  AuthFormat = mVariableModuleGlobal->VariableGlobal.AuthFormat;
+
+  if (  (InVarTrackPtr->CurrPtr == NULL)
+     || (InVarTrackPtr->StartPtr == GetStartPointer (VarStore)))
+  {
+    CopyMem (OutVarTrackPtr, InVarTrackPtr, sizeof (VARIABLE_POINTER_TRACK));
+    return EFI_SUCCESS;
+  }
+
+  NewStart = GetStartPointer (VarStore);
+  if (ByOffset) {
+    OutVarTrackPtr->CurrPtr = (VARIABLE_HEADER *)
+                              ((UINTN)NewStart + ((UINTN)InVarTrackPtr->CurrPtr -
+                                                  (UINTN)InVarTrackPtr->StartPtr));
+
+    if (InVarTrackPtr->InDeletedTransitionPtr != NULL) {
+      OutVarTrackPtr->InDeletedTransitionPtr =
+        (VARIABLE_HEADER *)((UINTN)NewStart +
+                            ((UINTN)InVarTrackPtr->InDeletedTransitionPtr -
+                             (UINTN)InVarTrackPtr->StartPtr));
+    } else {
+      OutVarTrackPtr->InDeletedTransitionPtr = NULL;
+    }
+
+    OutVarTrackPtr->StartPtr = NewStart;
+    OutVarTrackPtr->EndPtr   = GetEndPointer (VarStore);
+  } else {
+    OutVarTrackPtr->StartPtr = NewStart;
+    OutVarTrackPtr->EndPtr   = GetEndPointer (VarStore);
+
+    Status = FindVariableEx (VariableName, VendorGuid, FALSE, OutVarTrackPtr, AuthFormat);
+    if ((OutVarTrackPtr->CurrPtr == NULL) || EFI_ERROR (Status)) {
+      return EFI_NOT_FOUND;
+    }
+  }
+
+  if (  (VarStore == mNvVariableCache)
+     || ((UINTN)VarStore == (UINTN)mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase))
+  {
+    OutVarTrackPtr->Volatile = FALSE;
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Check if the given variable is from HOB.
+
+  @param[in] CacheVariable      Pointer to current variable in cache.
+
+  @retval TRUE    The variable is from HOB.
+  @retval FALSE   The variable is NOT from HOB.
+
+**/
+BOOLEAN
+IsHobVariable (
+  IN VARIABLE_POINTER_TRACK  *CacheVariable
+  )
+{
+  VARIABLE_STORE_HEADER  *HobVarStore;
+
+  HobVarStore = (VARIABLE_STORE_HEADER *)(UINTN)
+                mVariableModuleGlobal->VariableGlobal.HobVariableBase;
+  return (CacheVariable->CurrPtr != NULL &&
+          HobVarStore != NULL &&
+          CacheVariable->StartPtr == GetStartPointer (HobVarStore));
+}
+
+/**
+  Get temporary buffer for a new variable data.
+
+  @retval Pointer to the buffer address.
+
+**/
+VARIABLE_HEADER *
+GetNewVariableBuffer (
+  VOID
+  )
+{
+  VARIABLE_HEADER        *NewVariable;
+  VARIABLE_STORE_HEADER  *VarStore;
+
+  //
+  // Tricky part: Use scratch data area at the end of volatile variable store
+  // as a temporary storage.
+  //
+  VarStore = (VARIABLE_STORE_HEADER *)(UINTN)
+             mVariableModuleGlobal->VariableGlobal.VolatileVariableBase;
+  NewVariable = GetEndPointer (VarStore);
+
+  SetMem (NewVariable, mVariableModuleGlobal->ScratchBufferSize, 0xff);
+
+  return NewVariable;
+}
+
+/**
+  Delete old copies of variable completely.
+
+  @param[in]      VariableName       Name of variable.
+  @param[in]      VendorGuid         Guid of variable.
+  @param[in]      Variable           Pointer to current variable on storage.
+  @param[in,out]  CacheVariable      Pointer to current variable in cache.
+  @param[in]      VolatileFlag       Auth-variable indicator.
+
+  @retval EFI_SUCCESS           Variable(s) were deleted successfully.
+  @retval Others                Failed to update variable state.
+
+**/
+EFI_STATUS
+DeleteVariable (
+  IN      CHAR16                  *VariableName,
+  IN      EFI_GUID                *VendorGuid,
+  IN      VARIABLE_POINTER_TRACK  *Variable,
+  IN  OUT VARIABLE_POINTER_TRACK  *CacheVariable,
+  IN      BOOLEAN                 VolatileFlag
+  )
+{
+  EFI_STATUS  Status;
+
+  if (Variable->InDeletedTransitionPtr != NULL) {
+    ASSERT (CacheVariable->InDeletedTransitionPtr != NULL);
+    //
+    // Both ADDED and IN_DELETED_TRANSITION variable are present,
+    // set IN_DELETED_TRANSITION one to DELETED state first.
+    //
+    Status = UpdateVariableState (
+               Variable->InDeletedTransitionPtr,
+               CacheVariable->InDeletedTransitionPtr,
+               CacheVariable->InDeletedTransitionPtr->State & VAR_DELETED,
+               VolatileFlag
+               );
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+  }
+
+  ASSERT (CacheVariable->CurrPtr != NULL);
+  Status = UpdateVariableState (
+             Variable->CurrPtr,
+             CacheVariable->CurrPtr,
+             CacheVariable->CurrPtr->State & VAR_DELETED,
+             VolatileFlag
+             );
+
+  if (!EFI_ERROR (Status)) {
+    UpdateVariableInfo (
+      VariableName,
+      VendorGuid,
+      Variable->Volatile,
+      FALSE,
+      FALSE,
+      TRUE,
+      FALSE,
+      &gVariableInfo
+      );
+    if (!Variable->Volatile) {
+      FlushHobVariableToFlash (VariableName, VendorGuid);
+    }
+  }
+
+  return Status;
+}
+
+/**
+  Check if it's the right time to update a variable.
+
+  @param[in] Attributes         Attributes of a variable.
+
+  @retval TRUE    It's ready for variable update.
+  @retval FALSE   It's NOT ready for variable update.
+
+**/
+BOOLEAN
+ReadyForUpdate (
+  IN UINT32  Attributes
+  )
+{
+  if ((mVariableModuleGlobal->FvbInstance == NULL) &&
+      !mVariableModuleGlobal->VariableGlobal.EmuNvMode)
+  {
+    //
+    // The FVB protocol is not ready, so the EFI_VARIABLE_WRITE_ARCH_PROTOCOL
+    // is not installed.
+    //
+    if ((Attributes & EFI_VARIABLE_NON_VOLATILE) != 0) {
+      //
+      // Trying to update NV variable prior to the installation of
+      // EFI_VARIABLE_WRITE_ARCH_PROTOCOL
+      //
+      DEBUG ((
+        DEBUG_ERROR,
+        "Update NV variable before EFI_VARIABLE_WRITE_ARCH_PROTOCOL ready - %r\n",
+        EFI_NOT_AVAILABLE_YET
+        ));
+      return FALSE;
+    } else if ((Attributes & VARIABLE_ATTRIBUTE_AT_AW) != 0) {
+      //
+      // Trying to update volatile authenticated variable prior to the
+      // installation of EFI_VARIABLE_WRITE_ARCH_PROTOCOL. The authenticated
+      // variable perhaps is not initialized, just return here.
+      //
+      DEBUG ((
+        DEBUG_ERROR,
+        "Update AUTH variable before EFI_VARIABLE_WRITE_ARCH_PROTOCOL ready - %r\n",
+        EFI_NOT_AVAILABLE_YET
+        ));
+      return FALSE;
+    }
+  }
+
+  return TRUE;
+}
+
+/**
+  Check parameters associated with the variable to update.
+
+  @param[in] Variable           Pointer to current variable on storage.
+  @param[in] CacheVariable      Pointer to current variable in cache.
   @param[in] VariableName       Name of variable.
   @param[in] VendorGuid         Guid of variable.
   @param[in] Data               Variable data.
@@ -1667,9 +1999,130 @@ AutoUpdateLangVariable (
   @param[in] Attributes         Attributes of the variable.
   @param[in] KeyIndex           Index of associated public key.
   @param[in] MonotonicCount     Value of associated monotonic count.
-  @param[in, out] CacheVariable The variable information which is used to keep track of variable usage.
   @param[in] TimeStamp          Value of associated TimeStamp.
 
+  @retval EFI_SUCCESS           The variable is ok to be updated.
+  @retval EFI_ALREADY_STARTED   No need to update the variable.
+  @retval EFI_WRITE_PROTECTED   The variable cannot be updated.
+  @retval EFI_INVALID_PARAMETER The variable attributes are not valid.
+  @retval EFI_NOT_FOUND         Trying to delete non-existing variable.
+
+**/
+EFI_STATUS
+ValidateVariableParameters (
+  IN VARIABLE_POINTER_TRACK  *Variable,
+  IN VARIABLE_POINTER_TRACK  *CacheVariable,
+  IN CHAR16                  *VariableName,
+  IN EFI_GUID                *VendorGuid,
+  IN VOID                    *Data,
+  IN UINTN                   DataSize,
+  IN UINT32                  Attributes,
+  IN UINT32                  KeyIndex,
+  IN UINT64                  MonotonicCount,
+  IN EFI_TIME                *TimeStamp
+  )
+{
+  BOOLEAN  AuthFlag;
+
+  AuthFlag = mVariableModuleGlobal->VariableGlobal.AuthFormat;
+
+  if ((DataSize == 0) && ((Attributes & EFI_VARIABLE_APPEND_WRITE) != 0)) {
+    return EFI_ALREADY_STARTED;
+  }
+
+  if (Variable->CurrPtr != NULL) {
+    //
+    // Update/Delete existing variable.
+    //
+    if (AtRuntime ()) {
+      //
+      // If AtRuntime and the variable is Volatile and Runtime Access,
+      // the volatile is ReadOnly, and SetVariable should be aborted and
+      // return EFI_WRITE_PROTECTED.
+      //
+      if (Variable->Volatile) {
+        return EFI_WRITE_PROTECTED;
+      }
+
+      //
+      // Only variable that have NV attributes can be updated/deleted in Runtime.
+      //
+      if ((CacheVariable->CurrPtr->Attributes & EFI_VARIABLE_NON_VOLATILE) == 0) {
+        return EFI_INVALID_PARAMETER;
+      }
+
+      //
+      // Only variable that have RT attributes can be updated/deleted in Runtime.
+      //
+      if ((CacheVariable->CurrPtr->Attributes & EFI_VARIABLE_RUNTIME_ACCESS) == 0) {
+        return EFI_INVALID_PARAMETER;
+      }
+    }
+
+    //
+    // Variable content unchanged and no need to update timestamp, just return.
+    //
+    if (  ((Attributes & EFI_VARIABLE_APPEND_WRITE) == 0)
+       && (TimeStamp == NULL)
+       && (DataSizeOfVariable (CacheVariable->CurrPtr, AuthFlag) == DataSize)
+       && (CompareMem (Data, GetVariableDataPtr (CacheVariable->CurrPtr, AuthFlag), DataSize) == 0))
+    {
+      UpdateVariableInfo (
+        VariableName,
+        VendorGuid,
+        Variable->Volatile,
+        FALSE,
+        TRUE,
+        FALSE,
+        FALSE,
+        &gVariableInfo
+        );
+      return EFI_ALREADY_STARTED;
+    }
+  } else {
+    //
+    // Create a new variable.
+    //
+
+    //
+    // Make sure we are trying to create a new variable. You cannot delete a new
+    // variable.
+    //
+    if ((DataSize == 0) ||
+        ((Attributes & (EFI_VARIABLE_RUNTIME_ACCESS|EFI_VARIABLE_BOOTSERVICE_ACCESS)) == 0))
+    {
+      return EFI_NOT_FOUND;
+    }
+
+    //
+    // Only variable have NV|RT attribute can be created in Runtime.
+    //
+    if (  AtRuntime ()
+       && (  ((Attributes & EFI_VARIABLE_RUNTIME_ACCESS) == 0)
+          || ((Attributes & EFI_VARIABLE_NON_VOLATILE) == 0)))
+    {
+      return EFI_INVALID_PARAMETER;
+    }
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Update the variable region with Variable information. If EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS is set,
+  index of associated public key is needed.
+
+  @param[in]      VariableName       Name of variable.
+  @param[in]      VendorGuid         Guid of variable.
+  @param[in]      Data               Variable data.
+  @param[in]      DataSize           Size of data. 0 means delete.
+  @param[in]      Attributes         Attributes of the variable.
+  @param[in]      KeyIndex           Index of associated public key.
+  @param[in]      MonotonicCount     Value of associated monotonic count.
+  @param[in,out]  CacheVariable      The variable information which is used
+                                     to keep track of variable usage.
+  @param[in]      TimeStamp          Value of associated TimeStamp.
+
   @retval EFI_SUCCESS           The update operation is success.
   @retval EFI_OUT_OF_RESOURCES  Variable region is full, can not write other data into this region.
 
@@ -1687,710 +2140,386 @@ UpdateVariable (
   IN      EFI_TIME                *TimeStamp      OPTIONAL
   )
 {
-  EFI_STATUS                          Status;
-  VARIABLE_HEADER                     *NextVariable;
-  UINTN                               ScratchSize;
-  UINTN                               MaxDataSize;
-  UINTN                               VarNameOffset;
-  UINTN                               VarDataOffset;
-  UINTN                               VarNameSize;
-  UINTN                               VarSize;
-  BOOLEAN                             Volatile;
-  EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *Fvb;
-  UINT8                               State;
-  VARIABLE_POINTER_TRACK              *Variable;
-  VARIABLE_POINTER_TRACK              NvVariable;
-  VARIABLE_STORE_HEADER               *VariableStoreHeader;
-  VARIABLE_RUNTIME_CACHE              *VolatileCacheInstance;
-  UINT8                               *BufferForMerge;
-  UINTN                               MergedBufSize;
-  BOOLEAN                             DataReady;
-  UINTN                               DataOffset;
-  BOOLEAN                             IsCommonVariable;
-  BOOLEAN                             IsCommonUserVariable;
-  AUTHENTICATED_VARIABLE_HEADER       *AuthVariable;
-  BOOLEAN                             AuthFormat;
+  EFI_STATUS              Status;
+  VARIABLE_GLOBAL         *VarGlobal;
+  VARIABLE_HEADER         *NewVariable;
+  VARIABLE_HEADER         *NextVariable;
+  VARIABLE_HEADER         *UpdatingVariable;
+  UINTN                   VarSize;
+  UINTN                   UpdateSize;
+  UINTN                   Offset;
+  VARIABLE_POINTER_TRACK  *Variable;
+  VARIABLE_POINTER_TRACK  NvVariable;
+  VARIABLE_STORE_HEADER   *VariableStoreHeader;
+  VARIABLE_RUNTIME_CACHE  *VolatileCacheInstance;
+  BOOLEAN                 IsCommonVariable;
+  BOOLEAN                 IsCommonUserVariable;
+  BOOLEAN                 DeleteFlag;
+  BOOLEAN                 VolatileFlag;
+  BOOLEAN                 HobVarOnlyFlag;
+  EFI_PHYSICAL_ADDRESS    VarStoreBase;
+  UINTN                   *LastVariableOffset;
 
-  if ((mVariableModuleGlobal->FvbInstance == NULL) && !mVariableModuleGlobal->VariableGlobal.EmuNvMode) {
-    //
-    // The FVB protocol is not ready, so the EFI_VARIABLE_WRITE_ARCH_PROTOCOL is not installed.
-    //
-    if ((Attributes & EFI_VARIABLE_NON_VOLATILE) != 0) {
-      //
-      // Trying to update NV variable prior to the installation of EFI_VARIABLE_WRITE_ARCH_PROTOCOL
-      //
-      DEBUG ((DEBUG_ERROR, "Update NV variable before EFI_VARIABLE_WRITE_ARCH_PROTOCOL ready - %r\n", EFI_NOT_AVAILABLE_YET));
-      return EFI_NOT_AVAILABLE_YET;
-    } else if ((Attributes & VARIABLE_ATTRIBUTE_AT_AW) != 0) {
-      //
-      // Trying to update volatile authenticated variable prior to the installation of EFI_VARIABLE_WRITE_ARCH_PROTOCOL
-      // The authenticated variable perhaps is not initialized, just return here.
-      //
-      DEBUG ((DEBUG_ERROR, "Update AUTH variable before EFI_VARIABLE_WRITE_ARCH_PROTOCOL ready - %r\n", EFI_NOT_AVAILABLE_YET));
-      return EFI_NOT_AVAILABLE_YET;
-    }
+  if (!ReadyForUpdate (Attributes)) {
+    return EFI_NOT_AVAILABLE_YET;
   }
 
-  AuthFormat = mVariableModuleGlobal->VariableGlobal.AuthFormat;
+  VarGlobal = &mVariableModuleGlobal->VariableGlobal;
+
+  if (  (((Attributes & EFI_VARIABLE_APPEND_WRITE) == 0) && (DataSize == 0))
+     || (Attributes == 0)
+     || (AtRuntime () && ((Attributes & (EFI_VARIABLE_RUNTIME_ACCESS
+                                         |EFI_VARIABLE_BOOTSERVICE_ACCESS)) == 0)))
+  {
+    DeleteFlag = TRUE;
+  } else {
+    DeleteFlag = FALSE;
+  }
+
+  if (  ((Attributes & EFI_VARIABLE_NON_VOLATILE) != 0)
+     || ((CacheVariable->CurrPtr != NULL) &&
+         ((CacheVariable->CurrPtr->Attributes & EFI_VARIABLE_NON_VOLATILE) != 0)))
+  {
+    VolatileFlag = FALSE;
+  } else {
+    VolatileFlag = TRUE;
+  }
 
   //
   // Check if CacheVariable points to the variable in variable HOB.
   // If yes, let CacheVariable points to the variable in NV variable cache.
   //
-  if ((CacheVariable->CurrPtr != NULL) &&
-      (mVariableModuleGlobal->VariableGlobal.HobVariableBase != 0) &&
-      (CacheVariable->StartPtr == GetStartPointer ((VARIABLE_STORE_HEADER *)(UINTN)mVariableModuleGlobal->VariableGlobal.HobVariableBase))
-      )
-  {
-    CacheVariable->StartPtr = GetStartPointer (mNvVariableCache);
-    CacheVariable->EndPtr   = GetEndPointer (mNvVariableCache);
-    CacheVariable->Volatile = FALSE;
-    Status                  = FindVariableEx (VariableName, VendorGuid, FALSE, CacheVariable, AuthFormat);
+  HobVarOnlyFlag = FALSE;
+  if (IsHobVariable (CacheVariable)) {
+    Status = RebaseVariablePtr (
+               CacheVariable,
+               CacheVariable,
+               mNvVariableCache,
+               VariableName,
+               VendorGuid,
+               FALSE
+               );
     if ((CacheVariable->CurrPtr == NULL) || EFI_ERROR (Status)) {
       //
       // There is no matched variable in NV variable cache.
       //
-      if ((((Attributes & EFI_VARIABLE_APPEND_WRITE) == 0) && (DataSize == 0)) || (Attributes == 0)) {
+      if (DeleteFlag) {
         //
-        // It is to delete variable,
-        // go to delete this variable in variable HOB and
-        // try to flush other variables from HOB to flash.
+        // Leave the deletion to FlushHobVariableToFlash() before return.
         //
-        UpdateVariableInfo (VariableName, VendorGuid, FALSE, FALSE, FALSE, TRUE, FALSE, &gVariableInfo);
-        FlushHobVariableToFlash (VariableName, VendorGuid);
-        return EFI_SUCCESS;
+        HobVarOnlyFlag = TRUE;
+        Status         = EFI_SUCCESS;
+        goto Done;
       }
     }
   }
 
+  //
+  // Determine the physical position of variable store to update, due to cache
+  // mechanims of variable service.
+  //
   if ((CacheVariable->CurrPtr == NULL) || CacheVariable->Volatile) {
+    //
+    // - Add new variable (volatile or non-volatile); Or
+    // - Update/delete volatile variable in place.
+    //
     Variable = CacheVariable;
   } else {
     //
-    // Update/Delete existing NV variable.
-    // CacheVariable points to the variable in the memory copy of Flash area
-    // Now let Variable points to the same variable in Flash area.
+    // - Update/Delete existing NV variable.
+    //    CacheVariable points to the variable in the memory copy of Flash area.
+    //    Now let Variable points to the same variable in Flash area.
     //
-    VariableStoreHeader = (VARIABLE_STORE_HEADER *)((UINTN)mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase);
-    Variable            = &NvVariable;
-    Variable->StartPtr  = GetStartPointer (VariableStoreHeader);
-    Variable->EndPtr    = (VARIABLE_HEADER *)((UINTN)Variable->StartPtr + ((UINTN)CacheVariable->EndPtr - (UINTN)CacheVariable->StartPtr));
-
-    Variable->CurrPtr = (VARIABLE_HEADER *)((UINTN)Variable->StartPtr + ((UINTN)CacheVariable->CurrPtr - (UINTN)CacheVariable->StartPtr));
-    if (CacheVariable->InDeletedTransitionPtr != NULL) {
-      Variable->InDeletedTransitionPtr = (VARIABLE_HEADER *)((UINTN)Variable->StartPtr + ((UINTN)CacheVariable->InDeletedTransitionPtr - (UINTN)CacheVariable->StartPtr));
-    } else {
-      Variable->InDeletedTransitionPtr = NULL;
-    }
-
-    Variable->Volatile = FALSE;
+    VariableStoreHeader = (VARIABLE_STORE_HEADER *)(UINTN)
+                          VarGlobal->NonVolatileVariableBase;
+    Variable = &NvVariable;
+    Status   = RebaseVariablePtr (
+                 CacheVariable,
+                 Variable,
+                 VariableStoreHeader,
+                 VariableName,
+                 VendorGuid,
+                 TRUE
+                 );
+    ASSERT_EFI_ERROR (Status);
   }
 
-  Fvb = mVariableModuleGlobal->FvbInstance;
-
   //
-  // Tricky part: Use scratch data area at the end of volatile variable store
-  // as a temporary storage.
+  // Validate variable parameters.
   //
-  NextVariable = GetEndPointer ((VARIABLE_STORE_HEADER *)((UINTN)mVariableModuleGlobal->VariableGlobal.VolatileVariableBase));
-  ScratchSize  = mVariableModuleGlobal->ScratchBufferSize;
-  SetMem (NextVariable, ScratchSize, 0xff);
-  DataReady = FALSE;
-
-  if (Variable->CurrPtr != NULL) {
-    //
-    // Update/Delete existing variable.
-    //
-    if (AtRuntime ()) {
-      //
-      // If AtRuntime and the variable is Volatile and Runtime Access,
-      // the volatile is ReadOnly, and SetVariable should be aborted and
-      // return EFI_WRITE_PROTECTED.
-      //
-      if (Variable->Volatile) {
-        Status = EFI_WRITE_PROTECTED;
-        goto Done;
-      }
-
-      //
-      // Only variable that have NV attributes can be updated/deleted in Runtime.
-      //
-      if ((CacheVariable->CurrPtr->Attributes & EFI_VARIABLE_NON_VOLATILE) == 0) {
-        Status = EFI_INVALID_PARAMETER;
-        goto Done;
-      }
-
-      //
-      // Only variable that have RT attributes can be updated/deleted in Runtime.
-      //
-      if ((CacheVariable->CurrPtr->Attributes & EFI_VARIABLE_RUNTIME_ACCESS) == 0) {
-        Status = EFI_INVALID_PARAMETER;
-        goto Done;
-      }
-    }
-
-    //
-    // Setting a data variable with no access, or zero DataSize attributes
-    // causes it to be deleted.
-    // When the EFI_VARIABLE_APPEND_WRITE attribute is set, DataSize of zero will
-    // not delete the variable.
-    //
-    if ((((Attributes & EFI_VARIABLE_APPEND_WRITE) == 0) && (DataSize == 0)) || ((Attributes & (EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS)) == 0)) {
-      if (Variable->InDeletedTransitionPtr != NULL) {
-        //
-        // Both ADDED and IN_DELETED_TRANSITION variable are present,
-        // set IN_DELETED_TRANSITION one to DELETED state first.
-        //
-        ASSERT (CacheVariable->InDeletedTransitionPtr != NULL);
-        State  = CacheVariable->InDeletedTransitionPtr->State;
-        State &= VAR_DELETED;
-        Status = UpdateVariableStore (
-                   &mVariableModuleGlobal->VariableGlobal,
-                   Variable->Volatile,
-                   FALSE,
-                   Fvb,
-                   (UINTN)&Variable->InDeletedTransitionPtr->State,
-                   sizeof (UINT8),
-                   &State
-                   );
-        if (!EFI_ERROR (Status)) {
-          if (!Variable->Volatile) {
-            CacheVariable->InDeletedTransitionPtr->State = State;
-          }
-        } else {
-          goto Done;
-        }
-      }
-
-      State  = CacheVariable->CurrPtr->State;
-      State &= VAR_DELETED;
-
-      Status = UpdateVariableStore (
-                 &mVariableModuleGlobal->VariableGlobal,
-                 Variable->Volatile,
-                 FALSE,
-                 Fvb,
-                 (UINTN)&Variable->CurrPtr->State,
-                 sizeof (UINT8),
-                 &State
-                 );
-      if (!EFI_ERROR (Status)) {
-        UpdateVariableInfo (VariableName, VendorGuid, Variable->Volatile, FALSE, FALSE, TRUE, FALSE, &gVariableInfo);
-        if (!Variable->Volatile) {
-          CacheVariable->CurrPtr->State = State;
-          FlushHobVariableToFlash (VariableName, VendorGuid);
-        }
-      }
+  Status = ValidateVariableParameters (
+             Variable,
+             CacheVariable,
+             VariableName,
+             VendorGuid,
+             Data,
+             DataSize,
+             Attributes,
+             KeyIndex,
+             MonotonicCount,
+             TimeStamp
+             );
+  if (EFI_ERROR (Status)) {
+    goto Done;
+  }
 
-      goto Done;
-    }
+  //
+  // Add or update a variable. Allocate a buffer to hold it temporarily.
+  //
+  NewVariable = GetNewVariableBuffer ();
 
+  //
+  // Fill-up variable data first, if necessary.
+  //
+  IsCommonVariable     = FALSE;
+  IsCommonUserVariable = FALSE;
+  if (DeleteFlag) {
     //
-    // If the variable is marked valid, and the same data has been passed in,
-    // then return to the caller immediately.
+    // No need to fill up variable buffer when deleting a variable. But the
+    // buffer is still needed if variable protection is employed.
     //
-    if ((DataSizeOfVariable (CacheVariable->CurrPtr, AuthFormat) == DataSize) &&
-        (CompareMem (Data, GetVariableDataPtr (CacheVariable->CurrPtr, AuthFormat), DataSize) == 0) &&
-        ((Attributes & EFI_VARIABLE_APPEND_WRITE) == 0) &&
-        (TimeStamp == NULL))
-    {
-      //
-      // Variable content unchanged and no need to update timestamp, just return.
-      //
-      UpdateVariableInfo (VariableName, VendorGuid, Variable->Volatile, FALSE, TRUE, FALSE, FALSE, &gVariableInfo);
-      Status = EFI_SUCCESS;
-      goto Done;
-    } else if ((CacheVariable->CurrPtr->State == VAR_ADDED) ||
-               (CacheVariable->CurrPtr->State == (VAR_ADDED & VAR_IN_DELETED_TRANSITION)))
-    {
-      //
-      // EFI_VARIABLE_APPEND_WRITE attribute only effects for existing variable.
-      //
-      if ((Attributes & EFI_VARIABLE_APPEND_WRITE) != 0) {
-        //
-        // NOTE: From 0 to DataOffset of NextVariable is reserved for Variable Header and Name.
-        // From DataOffset of NextVariable is to save the existing variable data.
-        //
-        DataOffset     = GetVariableDataOffset (CacheVariable->CurrPtr, AuthFormat);
-        BufferForMerge = (UINT8 *)((UINTN)NextVariable + DataOffset);
-        CopyMem (
-          BufferForMerge,
-          (UINT8 *)((UINTN)CacheVariable->CurrPtr + DataOffset),
-          DataSizeOfVariable (CacheVariable->CurrPtr, AuthFormat)
-          );
-
-        //
-        // Set Max Auth/Non-Volatile/Volatile Variable Data Size as default MaxDataSize.
-        //
-        if ((Attributes & VARIABLE_ATTRIBUTE_AT_AW) != 0) {
-          MaxDataSize = mVariableModuleGlobal->MaxAuthVariableSize - DataOffset;
-        } else if ((Attributes & EFI_VARIABLE_NON_VOLATILE) != 0) {
-          MaxDataSize = mVariableModuleGlobal->MaxVariableSize - DataOffset;
-        } else {
-          MaxDataSize = mVariableModuleGlobal->MaxVolatileVariableSize - DataOffset;
-        }
-
-        //
-        // Append the new data to the end of existing data.
-        // Max Harware error record variable data size is different from common/auth variable.
-        //
-        if ((Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == EFI_VARIABLE_HARDWARE_ERROR_RECORD) {
-          MaxDataSize = PcdGet32 (PcdMaxHardwareErrorVariableSize) - DataOffset;
-        }
-
-        if (DataSizeOfVariable (CacheVariable->CurrPtr, AuthFormat) + DataSize > MaxDataSize) {
-          //
-          // Existing data size + new data size exceed maximum variable size limitation.
-          //
-          Status = EFI_INVALID_PARAMETER;
-          goto Done;
-        }
-
-        CopyMem (
-          (UINT8 *)(
-                    (UINTN)BufferForMerge + DataSizeOfVariable (CacheVariable->CurrPtr, AuthFormat)
-                    ),
-          Data,
-          DataSize
-          );
-        MergedBufSize = DataSizeOfVariable (CacheVariable->CurrPtr, AuthFormat) +
-                        DataSize;
-
-        //
-        // BufferForMerge(from DataOffset of NextVariable) has included the merged existing and new data.
-        //
-        Data      = BufferForMerge;
-        DataSize  = MergedBufSize;
-        DataReady = TRUE;
-      }
-
-      //
-      // Mark the old variable as in delete transition.
-      //
-      State  = CacheVariable->CurrPtr->State;
-      State &= VAR_IN_DELETED_TRANSITION;
-
-      Status = UpdateVariableStore (
-                 &mVariableModuleGlobal->VariableGlobal,
-                 Variable->Volatile,
-                 FALSE,
-                 Fvb,
-                 (UINTN)&Variable->CurrPtr->State,
-                 sizeof (UINT8),
-                 &State
-                 );
-      if (EFI_ERROR (Status)) {
-        goto Done;
-      }
-
-      if (!Variable->Volatile) {
-        CacheVariable->CurrPtr->State = State;
-      }
-    }
+    VarSize = 0;
   } else {
-    //
-    // Not found existing variable. Create a new variable.
-    //
+    VarSize = SetVariableData (
+                NewVariable,
+                CacheVariable->CurrPtr,
+                VariableName,
+                VendorGuid,
+                Data,
+                DataSize,
+                Attributes,
+                KeyIndex,
+                MonotonicCount,
+                TimeStamp
+                );
 
-    if ((DataSize == 0) && ((Attributes & EFI_VARIABLE_APPEND_WRITE) != 0)) {
-      Status = EFI_SUCCESS;
-      goto Done;
-    }
-
-    //
-    // Make sure we are trying to create a new variable.
-    // Setting a data variable with zero DataSize or no access attributes means to delete it.
-    //
-    if ((DataSize == 0) || ((Attributes & (EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS)) == 0)) {
-      Status = EFI_NOT_FOUND;
-      goto Done;
-    }
-
-    //
-    // Only variable have NV|RT attribute can be created in Runtime.
-    //
-    if (AtRuntime () &&
-        (((Attributes & EFI_VARIABLE_RUNTIME_ACCESS) == 0) || ((Attributes & EFI_VARIABLE_NON_VOLATILE) == 0)))
-    {
-      Status = EFI_INVALID_PARAMETER;
-      goto Done;
-    }
-  }
-
-  //
-  // Function part - create a new variable and copy the data.
-  // Both update a variable and create a variable will come here.
-  //
-  NextVariable->StartId = VARIABLE_DATA;
-  //
-  // NextVariable->State = VAR_ADDED;
-  //
-  NextVariable->Reserved = 0;
-  if (mVariableModuleGlobal->VariableGlobal.AuthFormat) {
-    AuthVariable                 = (AUTHENTICATED_VARIABLE_HEADER *)NextVariable;
-    AuthVariable->PubKeyIndex    = KeyIndex;
-    AuthVariable->MonotonicCount = MonotonicCount;
-    ZeroMem (&AuthVariable->TimeStamp, sizeof (EFI_TIME));
-
-    if (((Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) != 0) &&
-        (TimeStamp != NULL))
-    {
-      if ((Attributes & EFI_VARIABLE_APPEND_WRITE) == 0) {
-        CopyMem (&AuthVariable->TimeStamp, TimeStamp, sizeof (EFI_TIME));
-      } else {
-        //
-        // In the case when the EFI_VARIABLE_APPEND_WRITE attribute is set, only
-        // when the new TimeStamp value is later than the current timestamp associated
-        // with the variable, we need associate the new timestamp with the updated value.
-        //
-        if (Variable->CurrPtr != NULL) {
-          if (VariableCompareTimeStampInternal (&(((AUTHENTICATED_VARIABLE_HEADER *)CacheVariable->CurrPtr)->TimeStamp), TimeStamp)) {
-            CopyMem (&AuthVariable->TimeStamp, TimeStamp, sizeof (EFI_TIME));
-          } else {
-            CopyMem (&AuthVariable->TimeStamp, &(((AUTHENTICATED_VARIABLE_HEADER *)CacheVariable->CurrPtr)->TimeStamp), sizeof (EFI_TIME));
-          }
-        }
-      }
-    }
-  }
-
-  //
-  // The EFI_VARIABLE_APPEND_WRITE attribute will never be set in the returned
-  // Attributes bitmask parameter of a GetVariable() call.
-  //
-  NextVariable->Attributes = Attributes & (~EFI_VARIABLE_APPEND_WRITE);
-
-  VarNameOffset = GetVariableHeaderSize (AuthFormat);
-  VarNameSize   = StrSize (VariableName);
-  CopyMem (
-    (UINT8 *)((UINTN)NextVariable + VarNameOffset),
-    VariableName,
-    VarNameSize
-    );
-  VarDataOffset = VarNameOffset + VarNameSize + GET_PAD_SIZE (VarNameSize);
-
-  //
-  // If DataReady is TRUE, it means the variable data has been saved into
-  // NextVariable during EFI_VARIABLE_APPEND_WRITE operation preparation.
-  //
-  if (!DataReady) {
-    CopyMem (
-      (UINT8 *)((UINTN)NextVariable + VarDataOffset),
-      Data,
-      DataSize
-      );
-  }
-
-  CopyMem (
-    GetVendorGuidPtr (NextVariable, AuthFormat),
-    VendorGuid,
-    sizeof (EFI_GUID)
-    );
-  //
-  // There will be pad bytes after Data, the NextVariable->NameSize and
-  // NextVariable->DataSize should not include pad size so that variable
-  // service can get actual size in GetVariable.
-  //
-  SetNameSizeOfVariable (NextVariable, VarNameSize, AuthFormat);
-  SetDataSizeOfVariable (NextVariable, DataSize, AuthFormat);
-
-  //
-  // The actual size of the variable that stores in storage should
-  // include pad size.
-  //
-  VarSize = VarDataOffset + DataSize + GET_PAD_SIZE (DataSize);
-  if ((Attributes & EFI_VARIABLE_NON_VOLATILE) != 0) {
-    //
-    // Create a nonvolatile variable.
-    //
-    Volatile = FALSE;
-
-    IsCommonVariable     = FALSE;
-    IsCommonUserVariable = FALSE;
     if ((Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == 0) {
       IsCommonVariable     = TRUE;
-      IsCommonUserVariable = IsUserVariable (NextVariable);
+      IsCommonUserVariable = IsUserVariable (NewVariable);
     }
+  }
 
-    if (  (  ((Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) != 0)
-          && ((VarSize + mVariableModuleGlobal->HwErrVariableTotalSize) > PcdGet32 (PcdHwErrStorageSize)))
-       || (IsCommonVariable && ((VarSize + mVariableModuleGlobal->CommonVariableTotalSize) > mVariableModuleGlobal->CommonVariableSpace))
-       || (IsCommonVariable && AtRuntime () && ((VarSize + mVariableModuleGlobal->CommonVariableTotalSize) > mVariableModuleGlobal->CommonRuntimeVariableSpace))
-       || (IsCommonUserVariable && ((VarSize + mVariableModuleGlobal->CommonUserVariableTotalSize) > mVariableModuleGlobal->CommonMaxUserVariableSpace)))
-    {
-      if (AtRuntime ()) {
-        if (IsCommonUserVariable && ((VarSize + mVariableModuleGlobal->CommonUserVariableTotalSize) > mVariableModuleGlobal->CommonMaxUserVariableSpace)) {
-          RecordVarErrorFlag (VAR_ERROR_FLAG_USER_ERROR, VariableName, VendorGuid, Attributes, VarSize);
-        }
-
-        if (IsCommonVariable && ((VarSize + mVariableModuleGlobal->CommonVariableTotalSize) > mVariableModuleGlobal->CommonRuntimeVariableSpace)) {
-          RecordVarErrorFlag (VAR_ERROR_FLAG_SYSTEM_ERROR, VariableName, VendorGuid, Attributes, VarSize);
-        }
-
-        Status = EFI_OUT_OF_RESOURCES;
-        goto Done;
-      }
-
-      //
-      // Perform garbage collection & reclaim operation, and integrate the new variable at the same time.
-      //
-      Status = Reclaim (
-                 mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase,
-                 &mVariableModuleGlobal->NonVolatileLastVariableOffset,
-                 FALSE,
-                 Variable,
-                 NextVariable,
-                 HEADER_ALIGN (VarSize)
-                 );
-      if (!EFI_ERROR (Status)) {
-        //
-        // The new variable has been integrated successfully during reclaiming.
-        //
-        if (Variable->CurrPtr != NULL) {
-          CacheVariable->CurrPtr                = (VARIABLE_HEADER *)((UINTN)CacheVariable->StartPtr + ((UINTN)Variable->CurrPtr - (UINTN)Variable->StartPtr));
-          CacheVariable->InDeletedTransitionPtr = NULL;
-        }
-
-        UpdateVariableInfo (VariableName, VendorGuid, FALSE, FALSE, TRUE, FALSE, FALSE, &gVariableInfo);
-        FlushHobVariableToFlash (VariableName, VendorGuid);
-      } else {
-        if (IsCommonUserVariable && ((VarSize + mVariableModuleGlobal->CommonUserVariableTotalSize) > mVariableModuleGlobal->CommonMaxUserVariableSpace)) {
-          RecordVarErrorFlag (VAR_ERROR_FLAG_USER_ERROR, VariableName, VendorGuid, Attributes, VarSize);
-        }
+  //
+  // We might need to do protection for non-volatile variable before flushing
+  // the data to storage. A null version (meaning no protection) of following
+  // APIs should simply return EFI_SUCCESS or EFI_UNSUPPORTED without any
+  // changes to original data.
+  //
+  if (!VolatileFlag) {
+    Status = ProtectedVariableLibUpdate (
+               Variable->CurrPtr,
+               Variable->InDeletedTransitionPtr,
+               NewVariable,
+               &VarSize
+               );
+    if (EFI_ERROR (Status) && (Status != EFI_UNSUPPORTED)) {
+      return Status;
+    }
 
-        if (IsCommonVariable && ((VarSize + mVariableModuleGlobal->CommonVariableTotalSize) > mVariableModuleGlobal->CommonVariableSpace)) {
-          RecordVarErrorFlag (VAR_ERROR_FLAG_SYSTEM_ERROR, VariableName, VendorGuid, Attributes, VarSize);
-        }
-      }
+    Status = EFI_SUCCESS;
+  }
 
+  //
+  // Mark the old variable as in delete transition first. There's no such need
+  // for deleting a variable, even if variable protection is employed.
+  //
+  if (  !DeleteFlag
+     && (CacheVariable->CurrPtr != NULL)
+     && (  (CacheVariable->CurrPtr->State == VAR_ADDED)
+        || (CacheVariable->CurrPtr->State == (VAR_ADDED & VAR_IN_DELETED_TRANSITION))))
+  {
+    Status = UpdateVariableState (
+               Variable->CurrPtr,
+               CacheVariable->CurrPtr,
+               CacheVariable->CurrPtr->State & VAR_IN_DELETED_TRANSITION,
+               Variable->Volatile
+               );
+    if (EFI_ERROR (Status)) {
       goto Done;
     }
-
-    if (!mVariableModuleGlobal->VariableGlobal.EmuNvMode) {
-      //
-      // Four steps
-      // 1. Write variable header
-      // 2. Set variable state to header valid
-      // 3. Write variable data
-      // 4. Set variable state to valid
-      //
-      //
-      // Step 1:
-      //
-      Status = UpdateVariableStore (
-                 &mVariableModuleGlobal->VariableGlobal,
-                 FALSE,
-                 TRUE,
-                 Fvb,
-                 mVariableModuleGlobal->NonVolatileLastVariableOffset,
-                 (UINT32)GetVariableHeaderSize (AuthFormat),
-                 (UINT8 *)NextVariable
-                 );
-
-      if (EFI_ERROR (Status)) {
-        goto Done;
-      }
-
-      //
-      // Step 2:
-      //
-      NextVariable->State = VAR_HEADER_VALID_ONLY;
-      Status              = UpdateVariableStore (
-                              &mVariableModuleGlobal->VariableGlobal,
-                              FALSE,
-                              TRUE,
-                              Fvb,
-                              mVariableModuleGlobal->NonVolatileLastVariableOffset + OFFSET_OF (VARIABLE_HEADER, State),
-                              sizeof (UINT8),
-                              &NextVariable->State
-                              );
-
-      if (EFI_ERROR (Status)) {
-        goto Done;
-      }
-
-      //
-      // Step 3:
-      //
-      Status = UpdateVariableStore (
-                 &mVariableModuleGlobal->VariableGlobal,
-                 FALSE,
-                 TRUE,
-                 Fvb,
-                 mVariableModuleGlobal->NonVolatileLastVariableOffset + GetVariableHeaderSize (AuthFormat),
-                 (UINT32)(VarSize - GetVariableHeaderSize (AuthFormat)),
-                 (UINT8 *)NextVariable + GetVariableHeaderSize (AuthFormat)
+  }
+
+  //
+  // Have enough space to store the variable?
+  //
+  Status = CheckVariableStoreSpace (
+             NewVariable,
+             VarSize,
+             VariableName,
+             VendorGuid,
+             Attributes,
+             VolatileFlag
+             );
+  if (Status == EFI_OUT_OF_RESOURCES) {
+    //
+    // Not a chance.
+    //
+    goto Done;
+  }
+
+  //
+  // Maybe not...
+  //
+  VarStoreBase = (VolatileFlag) ? VarGlobal->VolatileVariableBase
+                                : VarGlobal->NonVolatileVariableBase;
+  LastVariableOffset = (VolatileFlag)
+                        ? &mVariableModuleGlobal->VolatileLastVariableOffset
+                        : &mVariableModuleGlobal->NonVolatileLastVariableOffset;
+  if (!EFI_ERROR (Status)) {
+    //
+    // There's enough free space at the tail of variable storage.
+    //
+
+    //
+    // If non-volatile variable is protected, a separate variable (MetaDataHmacVar)
+    // is always updated along with current updating variable. The buffer pointed
+    // by NewVariable must have two variables. They should be written at this
+    // time orderly.
+    //
+    NextVariable     = NewVariable;
+    UpdatingVariable = NULL;
+    UpdateSize       = 0;
+    while (  !EFI_ERROR (Status)
+          && ((UINTN)NextVariable - (UINTN)NewVariable) < VarSize)
+    {
+      UpdatingVariable = NextVariable;
+      NextVariable     = GetNextVariablePtr (UpdatingVariable, VarGlobal->AuthFormat);
+      UpdateSize       = (UINTN)NextVariable - (UINTN)UpdatingVariable;
+
+      Status = WriteVariable (
+                 VarStoreBase,
+                 LastVariableOffset,
+                 &UpdatingVariable,
+                 (UINT32)UpdateSize,
+                 VolatileFlag,
+                 VarGlobal->AuthFormat
                  );
-
-      if (EFI_ERROR (Status)) {
-        goto Done;
-      }
-
-      //
-      // Step 4:
-      //
-      NextVariable->State = VAR_ADDED;
-      Status              = UpdateVariableStore (
-                              &mVariableModuleGlobal->VariableGlobal,
-                              FALSE,
-                              TRUE,
-                              Fvb,
-                              mVariableModuleGlobal->NonVolatileLastVariableOffset + OFFSET_OF (VARIABLE_HEADER, State),
-                              sizeof (UINT8),
-                              &NextVariable->State
-                              );
-
-      if (EFI_ERROR (Status)) {
-        goto Done;
-      }
-
-      //
-      // Update the memory copy of Flash region.
-      //
-      CopyMem ((UINT8 *)mNvVariableCache + mVariableModuleGlobal->NonVolatileLastVariableOffset, (UINT8 *)NextVariable, VarSize);
-    } else {
-      //
-      // Emulated non-volatile variable mode.
-      //
-      NextVariable->State = VAR_ADDED;
-      Status              = UpdateVariableStore (
-                              &mVariableModuleGlobal->VariableGlobal,
-                              FALSE,
-                              TRUE,
-                              Fvb,
-                              mVariableModuleGlobal->NonVolatileLastVariableOffset,
-                              (UINT32)VarSize,
-                              (UINT8 *)NextVariable
-                              );
-
-      if (EFI_ERROR (Status)) {
-        goto Done;
-      }
     }
 
-    mVariableModuleGlobal->NonVolatileLastVariableOffset += HEADER_ALIGN (VarSize);
-
+    //
+    // UpdatingVariable must point to the last written variable. Restore it to
+    // the first one so that we can calculate the offset in variable storage.
+    //
+    UpdatingVariable = (VARIABLE_HEADER *)((UINTN)UpdatingVariable + UpdateSize
+                                           - VarSize);
     if ((Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) != 0) {
-      mVariableModuleGlobal->HwErrVariableTotalSize += HEADER_ALIGN (VarSize);
+      mVariableModuleGlobal->HwErrVariableTotalSize += VarSize;
     } else {
-      mVariableModuleGlobal->CommonVariableTotalSize += HEADER_ALIGN (VarSize);
+      mVariableModuleGlobal->CommonVariableTotalSize += VarSize;
       if (IsCommonUserVariable) {
-        mVariableModuleGlobal->CommonUserVariableTotalSize += HEADER_ALIGN (VarSize);
+        mVariableModuleGlobal->CommonUserVariableTotalSize += VarSize;
       }
     }
-  } else {
-    //
-    // Create a volatile variable.
-    //
-    Volatile = TRUE;
 
-    if ((UINT32)(VarSize + mVariableModuleGlobal->VolatileLastVariableOffset) >
-        ((VARIABLE_STORE_HEADER *)((UINTN)(mVariableModuleGlobal->VariableGlobal.VolatileVariableBase)))->Size)
-    {
-      //
-      // Perform garbage collection & reclaim operation, and integrate the new variable at the same time.
-      //
-      Status = Reclaim (
-                 mVariableModuleGlobal->VariableGlobal.VolatileVariableBase,
-                 &mVariableModuleGlobal->VolatileLastVariableOffset,
-                 TRUE,
+    //
+    // Mark the old variable(s) as deleted.
+    //
+    if (!EFI_ERROR (Status) && (Variable->CurrPtr != NULL)) {
+      Status = DeleteVariable (
+                 VariableName,
+                 VendorGuid,
                  Variable,
-                 NextVariable,
-                 HEADER_ALIGN (VarSize)
+                 CacheVariable,
+                 VolatileFlag
                  );
-      if (!EFI_ERROR (Status)) {
-        //
-        // The new variable has been integrated successfully during reclaiming.
-        //
-        if (Variable->CurrPtr != NULL) {
-          CacheVariable->CurrPtr                = (VARIABLE_HEADER *)((UINTN)CacheVariable->StartPtr + ((UINTN)Variable->CurrPtr - (UINTN)Variable->StartPtr));
-          CacheVariable->InDeletedTransitionPtr = NULL;
-        }
-
-        UpdateVariableInfo (VariableName, VendorGuid, TRUE, FALSE, TRUE, FALSE, FALSE, &gVariableInfo);
-      }
-
-      goto Done;
     }
-
-    NextVariable->State = VAR_ADDED;
-    Status              = UpdateVariableStore (
-                            &mVariableModuleGlobal->VariableGlobal,
-                            TRUE,
-                            TRUE,
-                            Fvb,
-                            mVariableModuleGlobal->VolatileLastVariableOffset,
-                            (UINT32)VarSize,
-                            (UINT8 *)NextVariable
-                            );
-
-    if (EFI_ERROR (Status)) {
-      goto Done;
-    }
-
-    mVariableModuleGlobal->VolatileLastVariableOffset += HEADER_ALIGN (VarSize);
-  }
-
-  //
-  // Mark the old variable as deleted.
-  //
-  if (!EFI_ERROR (Status) && (Variable->CurrPtr != NULL)) {
-    if (Variable->InDeletedTransitionPtr != NULL) {
-      //
-      // Both ADDED and IN_DELETED_TRANSITION old variable are present,
-      // set IN_DELETED_TRANSITION one to DELETED state first.
-      //
-      ASSERT (CacheVariable->InDeletedTransitionPtr != NULL);
-      State  = CacheVariable->InDeletedTransitionPtr->State;
-      State &= VAR_DELETED;
-      Status = UpdateVariableStore (
-                 &mVariableModuleGlobal->VariableGlobal,
-                 Variable->Volatile,
-                 FALSE,
-                 Fvb,
-                 (UINTN)&Variable->InDeletedTransitionPtr->State,
-                 sizeof (UINT8),
-                 &State
-                 );
-      if (!EFI_ERROR (Status)) {
-        if (!Variable->Volatile) {
-          CacheVariable->InDeletedTransitionPtr->State = State;
-        }
-      } else {
-        goto Done;
-      }
-    }
-
-    State  = CacheVariable->CurrPtr->State;
-    State &= VAR_DELETED;
-
-    Status = UpdateVariableStore (
-               &mVariableModuleGlobal->VariableGlobal,
-               Variable->Volatile,
-               FALSE,
-               Fvb,
-               (UINTN)&Variable->CurrPtr->State,
-               sizeof (UINT8),
-               &State
+  } else {
+    //
+    // There's not enough space at the tail of variable storage but there's
+    // enough free space holes in the whole storage. Perform garbage collection
+    // & reclaim operation, and integrate the new variable at the same time.
+    //
+    Status = Reclaim (
+               VarStoreBase,
+               LastVariableOffset,
+               VolatileFlag,
+               Variable,
+               NewVariable,
+               VarSize
                );
-    if (!EFI_ERROR (Status) && !Variable->Volatile) {
-      CacheVariable->CurrPtr->State = State;
+
+    if (Variable->CurrPtr != NULL) {
+      UpdatingVariable = Variable->CurrPtr;
+    } else {
+      UpdatingVariable = (VARIABLE_HEADER *)(((UINTN)VarStoreBase + *LastVariableOffset) - VarSize);
     }
-  }
 
-  if (!EFI_ERROR (Status)) {
-    UpdateVariableInfo (VariableName, VendorGuid, Volatile, FALSE, TRUE, FALSE, FALSE, &gVariableInfo);
-    if (!Volatile) {
-      FlushHobVariableToFlash (VariableName, VendorGuid);
+    if (EFI_ERROR (Status) &&
+        ((Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == 0))
+    {
+      //
+      // Out of space.
+      //
+      IsCommonUserVariable = IsUserVariable (NewVariable);
+      IsCommonVariable     = TRUE;
+
+      if (IsCommonUserVariable &&
+          ((VarSize + mVariableModuleGlobal->CommonUserVariableTotalSize)
+           > mVariableModuleGlobal->CommonMaxUserVariableSpace))
+      {
+        RecordVarErrorFlag (
+          VAR_ERROR_FLAG_USER_ERROR,
+          VariableName,
+          VendorGuid,
+          Attributes,
+          VarSize
+          );
+      }
+
+      if (IsCommonVariable &&
+          ((VarSize + mVariableModuleGlobal->CommonVariableTotalSize)
+           > mVariableModuleGlobal->CommonVariableSpace))
+      {
+        RecordVarErrorFlag (
+          VAR_ERROR_FLAG_SYSTEM_ERROR,
+          VariableName,
+          VendorGuid,
+          Attributes,
+          VarSize
+          );
+      }
     }
   }
 
 Done:
   if (!EFI_ERROR (Status)) {
-    if (((Variable->CurrPtr != NULL) && !Variable->Volatile) || ((Attributes & EFI_VARIABLE_NON_VOLATILE) != 0)) {
-      VolatileCacheInstance = &(mVariableModuleGlobal->VariableGlobal.VariableRuntimeCacheContext.VariableRuntimeNvCache);
+    if (!VolatileFlag) {
+      Offset = (UpdatingVariable != NULL) ? (UINTN)UpdatingVariable - (UINTN)VarStoreBase
+                                          : 0;
+      Status = ProtectedVariableLibWriteFinal (
+                 NewVariable,
+                 VarSize,
+                 Offset
+                 );
+      if (EFI_ERROR (Status) && (Status != EFI_UNSUPPORTED)) {
+        return Status;
+      }
+
+      Status = EFI_SUCCESS;
+    }
+
+    UpdateVariableInfo (
+      VariableName,
+      VendorGuid,
+      VolatileFlag,
+      FALSE,
+      TRUE,
+      FALSE,
+      FALSE,
+      &gVariableInfo
+      );
+    //
+    // HOB copy of the same variable is no longer needed, no matter it has
+    // been deleted, updated or added from/to real variable storage.
+    //
+    if (HobVarOnlyFlag || !VolatileFlag) {
+      FlushHobVariableToFlash (VariableName, VendorGuid);
+    }
+
+    if (!VolatileFlag) {
+      VolatileCacheInstance = &(VarGlobal->VariableRuntimeCacheContext.VariableRuntimeNvCache);
     } else {
-      VolatileCacheInstance = &(mVariableModuleGlobal->VariableGlobal.VariableRuntimeCacheContext.VariableRuntimeVolatileCache);
+      VolatileCacheInstance = &(VarGlobal->VariableRuntimeCacheContext.VariableRuntimeVolatileCache);
     }
 
     if (VolatileCacheInstance->Store != NULL) {
@@ -2401,6 +2530,11 @@ Done:
                   );
       ASSERT_EFI_ERROR (Status);
     }
+  } else if (Status == EFI_ALREADY_STARTED) {
+    //
+    // Meaning nothing needs to be done. Just return success.
+    //
+    Status = EFI_SUCCESS;
   }
 
   return Status;
@@ -2440,7 +2574,6 @@ VariableServiceGetVariable (
 {
   EFI_STATUS              Status;
   VARIABLE_POINTER_TRACK  Variable;
-  UINTN                   VarDataSize;
 
   if ((VariableName == NULL) || (VendorGuid == NULL) || (DataSize == NULL)) {
     return EFI_INVALID_PARAMETER;
@@ -2458,28 +2591,26 @@ VariableServiceGetVariable (
   }
 
   //
-  // Get data size
+  // Get data and its size
   //
-  VarDataSize = DataSizeOfVariable (Variable.CurrPtr, mVariableModuleGlobal->VariableGlobal.AuthFormat);
-  ASSERT (VarDataSize != 0);
+  if (!Variable.Volatile) {
+    //
+    // Currently only non-volatile variable needs protection.
+    //
+    Status = ProtectedVariableLibGetByBuffer (
+               Variable.CurrPtr,
+               Data,
+               (UINT32 *)DataSize,
+               mVariableModuleGlobal->VariableGlobal.AuthFormat
+               );
+  }
 
-  if (*DataSize >= VarDataSize) {
-    if (Data == NULL) {
-      Status = EFI_INVALID_PARAMETER;
-      goto Done;
-    }
+  if (Variable.Volatile || (Status == EFI_UNSUPPORTED)) {
+    Status = GetVariableData (Variable.CurrPtr, Data, (UINT32 *)DataSize, mVariableModuleGlobal->VariableGlobal.AuthFormat);
+  }
 
-    CopyMem (Data, GetVariableDataPtr (Variable.CurrPtr, mVariableModuleGlobal->VariableGlobal.AuthFormat), VarDataSize);
-
-    *DataSize = VarDataSize;
+  if (!EFI_ERROR (Status)) {
     UpdateVariableInfo (VariableName, VendorGuid, Variable.Volatile, TRUE, FALSE, FALSE, FALSE, &gVariableInfo);
-
-    Status = EFI_SUCCESS;
-    goto Done;
-  } else {
-    *DataSize = VarDataSize;
-    Status    = EFI_BUFFER_TOO_SMALL;
-    goto Done;
   }
 
 Done:
@@ -2860,7 +2991,7 @@ VariableServiceSetVariable (
     // Parse non-volatile variable data and get last variable offset.
     //
     NextVariable = GetStartPointer ((VARIABLE_STORE_HEADER *)(UINTN)Point);
-    while (IsValidVariableHeader (NextVariable, GetEndPointer ((VARIABLE_STORE_HEADER *)(UINTN)Point))) {
+    while (IsValidVariableHeader (NextVariable, GetEndPointer ((VARIABLE_STORE_HEADER *)(UINTN)Point), AuthFormat)) {
       NextVariable = GetNextVariablePtr (NextVariable, AuthFormat);
     }
 
@@ -3022,7 +3153,12 @@ VariableServiceQueryVariableInfoInternal (
   //
   // Now walk through the related variable store.
   //
-  while (IsValidVariableHeader (Variable, GetEndPointer (VariableStoreHeader))) {
+  while (IsValidVariableHeader (
+           Variable,
+           GetEndPointer (VariableStoreHeader),
+           mVariableModuleGlobal->VariableGlobal.AuthFormat
+           ))
+  {
     NextVariable = GetNextVariablePtr (Variable, mVariableModuleGlobal->VariableGlobal.AuthFormat);
     VariableSize = (UINT64)(UINTN)NextVariable - (UINT64)(UINTN)Variable;
 
@@ -3315,7 +3451,7 @@ FlushHobVariableToFlash (
     //
     mVariableModuleGlobal->VariableGlobal.HobVariableBase = 0;
     for ( Variable = GetStartPointer (VariableStoreHeader)
-          ; IsValidVariableHeader (Variable, GetEndPointer (VariableStoreHeader))
+          ; IsValidVariableHeader (Variable, GetEndPointer (VariableStoreHeader), AuthFormat)
           ; Variable = GetNextVariablePtr (Variable, AuthFormat)
           )
     {
@@ -3525,11 +3661,11 @@ ConvertNormalVarStorageToAuthVarStorage (
   VARIABLE_HEADER                *StartPtr;
   UINT8                          *NextPtr;
   VARIABLE_HEADER                *EndPtr;
-  UINTN                          AuthVarStroageSize;
+  UINTN                          AuthVarStorageSize;
   AUTHENTICATED_VARIABLE_HEADER  *AuthStartPtr;
   VARIABLE_STORE_HEADER          *AuthVarStorage;
 
-  AuthVarStroageSize = sizeof (VARIABLE_STORE_HEADER);
+  AuthVarStorageSize = sizeof (VARIABLE_STORE_HEADER);
   //
   // Set AuthFormat as FALSE for normal variable storage
   //
@@ -3542,10 +3678,10 @@ ConvertNormalVarStorageToAuthVarStorage (
   EndPtr   = GetEndPointer (NormalVarStorage);
   while (StartPtr < EndPtr) {
     if (StartPtr->State == VAR_ADDED) {
-      AuthVarStroageSize  = HEADER_ALIGN (AuthVarStroageSize);
-      AuthVarStroageSize += sizeof (AUTHENTICATED_VARIABLE_HEADER);
-      AuthVarStroageSize += StartPtr->NameSize + GET_PAD_SIZE (StartPtr->NameSize);
-      AuthVarStroageSize += StartPtr->DataSize + GET_PAD_SIZE (StartPtr->DataSize);
+      AuthVarStorageSize  = HEADER_ALIGN (AuthVarStorageSize);
+      AuthVarStorageSize += sizeof (AUTHENTICATED_VARIABLE_HEADER);
+      AuthVarStorageSize += StartPtr->NameSize + GET_PAD_SIZE (StartPtr->NameSize);
+      AuthVarStorageSize += StartPtr->DataSize + GET_PAD_SIZE (StartPtr->DataSize);
     }
 
     StartPtr = GetNextVariablePtr (StartPtr, mVariableModuleGlobal->VariableGlobal.AuthFormat);
@@ -3554,7 +3690,7 @@ ConvertNormalVarStorageToAuthVarStorage (
   //
   // Allocate Runtime memory for Auth Variable Storage
   //
-  AuthVarStorage = AllocateRuntimeZeroPool (AuthVarStroageSize);
+  AuthVarStorage = AllocateRuntimeZeroPool (AuthVarStorageSize);
   ASSERT (AuthVarStorage != NULL);
   if (AuthVarStorage == NULL) {
     return NULL;
@@ -3608,7 +3744,7 @@ ConvertNormalVarStorageToAuthVarStorage (
   AuthVarStorage->State  = NormalVarStorage->State;
   AuthVarStorage->Size   = (UINT32)((UINTN)AuthStartPtr - (UINTN)AuthVarStorage);
   CopyGuid (&AuthVarStorage->Signature, &gEfiAuthenticatedVariableGuid);
-  ASSERT (AuthVarStorage->Size <= AuthVarStroageSize);
+  ASSERT (AuthVarStorage->Size <= AuthVarStorageSize);
 
   //
   // Restore AuthFormat
@@ -3774,7 +3910,7 @@ VariableCommonInitialize (
   //
   // Allocate memory for volatile variable store, note that there is a scratch space to store scratch data.
   //
-  ScratchSize                              = GetMaxVariableSize ();
+  ScratchSize                              = GetMaxVariableSize () * 2;
   mVariableModuleGlobal->ScratchBufferSize = ScratchSize;
   VolatileVariableStore                    = AllocateRuntimePool (PcdGet32 (PcdVariableStoreSize) + ScratchSize);
   if (VolatileVariableStore == NULL) {
diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableDxe.c b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableDxe.c
index d5c409c914d1..4595bf8c9d02 100644
--- a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableDxe.c
+++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableDxe.c
@@ -3,7 +3,7 @@
   and volatile storage space and install variable architecture protocol.
 
 Copyright (C) 2013, Red Hat, Inc.
-Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.<BR>
+Copyright (c) 2006 - 2022, Intel Corporation. All rights reserved.<BR>
 (C) Copyright 2015 Hewlett Packard Enterprise Development LP<BR>
 Copyright (c) Microsoft Corporation.
 SPDX-License-Identifier: BSD-2-Clause-Patent
@@ -14,6 +14,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
 
 #include <Protocol/VariablePolicy.h>
 #include <Library/VariablePolicyLib.h>
+#include "VariableParsing.h"
 
 EFI_STATUS
 EFIAPI
@@ -542,6 +543,29 @@ VariableServiceInitialize (
   EFI_EVENT   ReadyToBootEvent;
   EFI_EVENT   EndOfDxeEvent;
 
+  PROTECTED_VARIABLE_CONTEXT_IN  ContextIn;
+
+  //
+  // Initialze protected variable service, if enabled.
+  //
+  ContextIn.StructSize    = sizeof (ContextIn);
+  ContextIn.StructVersion = PROTECTED_VARIABLE_CONTEXT_IN_STRUCT_VERSION;
+
+  ContextIn.FindVariableSmm     = NULL;
+  ContextIn.GetVariableInfo     = GetVariableInfo;
+  ContextIn.GetNextVariableInfo = GetNextVariableInfo;
+  ContextIn.UpdateVariableStore = VariableExLibUpdateNvVariable;
+  ContextIn.UpdateVariable      = VariableExLibUpdateVariable;
+
+  ContextIn.MaxVariableSize     = (UINT32)GetMaxVariableSize ();
+  ContextIn.VariableServiceUser = FromSmmModule;
+
+  Status = ProtectedVariableLibInitialize (&ContextIn);
+  if (EFI_ERROR (Status) && (Status != EFI_UNSUPPORTED)) {
+    ASSERT_EFI_ERROR (Status);
+    return Status;
+  }
+
   Status = VariableCommonInitialize ();
   ASSERT_EFI_ERROR (Status);
 
diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableExLib.c b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableExLib.c
index 62cde0335512..5904bcbff78a 100644
--- a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableExLib.c
+++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableExLib.c
@@ -1,13 +1,14 @@
 /** @file
   Provides variable driver extended services.
 
-Copyright (c) 2015 - 2019, Intel Corporation. All rights reserved.<BR>
+Copyright (c) 2015 - 2022, Intel Corporation. All rights reserved.<BR>
 SPDX-License-Identifier: BSD-2-Clause-Patent
 
 **/
 
 #include "Variable.h"
 #include "VariableParsing.h"
+#include "VariableRuntimeCache.h"
 
 /**
   Finds variable in storage blocks of volatile and non-volatile storage areas.
@@ -38,6 +39,7 @@ VariableExLibFindVariable (
   EFI_STATUS                     Status;
   VARIABLE_POINTER_TRACK         Variable;
   AUTHENTICATED_VARIABLE_HEADER  *AuthVariable;
+  PROTECTED_VARIABLE_INFO        VarInfo;
 
   Status = FindVariable (
              VariableName,
@@ -56,9 +58,12 @@ VariableExLibFindVariable (
     return Status;
   }
 
-  AuthVariableInfo->DataSize   = DataSizeOfVariable (Variable.CurrPtr, mVariableModuleGlobal->VariableGlobal.AuthFormat);
-  AuthVariableInfo->Data       = GetVariableDataPtr (Variable.CurrPtr, mVariableModuleGlobal->VariableGlobal.AuthFormat);
-  AuthVariableInfo->Attributes = Variable.CurrPtr->Attributes;
+  AuthVariableInfo->NameSize     = NameSizeOfVariable (Variable.CurrPtr, mVariableModuleGlobal->VariableGlobal.AuthFormat);
+  AuthVariableInfo->VariableName = GetVariableNamePtr (Variable.CurrPtr, mVariableModuleGlobal->VariableGlobal.AuthFormat);
+  AuthVariableInfo->VendorGuid   = GetVendorGuidPtr (Variable.CurrPtr, mVariableModuleGlobal->VariableGlobal.AuthFormat);
+  AuthVariableInfo->DataSize     = DataSizeOfVariable (Variable.CurrPtr, mVariableModuleGlobal->VariableGlobal.AuthFormat);
+  AuthVariableInfo->Data         = GetVariableDataPtr (Variable.CurrPtr, mVariableModuleGlobal->VariableGlobal.AuthFormat);
+  AuthVariableInfo->Attributes   = Variable.CurrPtr->Attributes;
   if (mVariableModuleGlobal->VariableGlobal.AuthFormat) {
     AuthVariable                     = (AUTHENTICATED_VARIABLE_HEADER *)Variable.CurrPtr;
     AuthVariableInfo->PubKeyIndex    = AuthVariable->PubKeyIndex;
@@ -66,6 +71,24 @@ VariableExLibFindVariable (
     AuthVariableInfo->TimeStamp      = &AuthVariable->TimeStamp;
   }
 
+  CopyMem (&VarInfo.Header, AuthVariableInfo, sizeof (VarInfo.Header));
+
+  VarInfo.Buffer        = Variable.CurrPtr;
+  VarInfo.PlainData     = NULL;
+  VarInfo.PlainDataSize = 0;
+  VarInfo.Flags.Auth    = mVariableModuleGlobal->VariableGlobal.AuthFormat;
+
+  //
+  // In case the variable is encrypted.
+  //
+  Status = ProtectedVariableLibGetByInfo (&VarInfo);
+  if (!EFI_ERROR (Status)) {
+    if (VarInfo.PlainData != NULL) {
+      AuthVariableInfo->Data     = VarInfo.PlainData;
+      AuthVariableInfo->DataSize = VarInfo.PlainDataSize;
+    }
+  }
+
   return EFI_SUCCESS;
 }
 
@@ -99,6 +122,7 @@ VariableExLibFindNextVariable (
   VARIABLE_HEADER                *VariablePtr;
   AUTHENTICATED_VARIABLE_HEADER  *AuthVariablePtr;
   VARIABLE_STORE_HEADER          *VariableStoreHeader[VariableStoreTypeMax];
+  PROTECTED_VARIABLE_INFO        VarInfo;
 
   VariableStoreHeader[VariableStoreTypeVolatile] = (VARIABLE_STORE_HEADER *)(UINTN)mVariableModuleGlobal->VariableGlobal.VolatileVariableBase;
   VariableStoreHeader[VariableStoreTypeHob]      = (VARIABLE_STORE_HEADER *)(UINTN)mVariableModuleGlobal->VariableGlobal.HobVariableBase;
@@ -123,6 +147,7 @@ VariableExLibFindNextVariable (
     return Status;
   }
 
+  AuthVariableInfo->NameSize     = NameSizeOfVariable (VariablePtr, mVariableModuleGlobal->VariableGlobal.AuthFormat);
   AuthVariableInfo->VariableName = GetVariableNamePtr (VariablePtr, mVariableModuleGlobal->VariableGlobal.AuthFormat);
   AuthVariableInfo->VendorGuid   = GetVendorGuidPtr (VariablePtr, mVariableModuleGlobal->VariableGlobal.AuthFormat);
   AuthVariableInfo->DataSize     = DataSizeOfVariable (VariablePtr, mVariableModuleGlobal->VariableGlobal.AuthFormat);
@@ -135,6 +160,20 @@ VariableExLibFindNextVariable (
     AuthVariableInfo->TimeStamp      = &AuthVariablePtr->TimeStamp;
   }
 
+  CopyMem (&VarInfo.Header, AuthVariableInfo, sizeof (VarInfo.Header));
+
+  VarInfo.Buffer        = VariablePtr;
+  VarInfo.PlainData     = NULL;
+  VarInfo.PlainDataSize = 0;
+
+  Status = ProtectedVariableLibGetByInfo (&VarInfo);
+  if (!EFI_ERROR (Status)) {
+    if (VarInfo.PlainData != NULL) {
+      AuthVariableInfo->Data     = VarInfo.PlainData;
+      AuthVariableInfo->DataSize = VarInfo.PlainDataSize;
+    }
+  }
+
   return EFI_SUCCESS;
 }
 
@@ -256,3 +295,123 @@ VariableExLibAtRuntime (
 {
   return AtRuntime ();
 }
+
+/**
+  Update partial data of a variable on NV storage and/or cached copy.
+
+  @param[in]  VariableInfo  Pointer to a variable with detailed information.
+  @param[in]  Offset        Offset to write from.
+  @param[in]  Size          Size of data Buffer to update.
+  @param[in]  Buffer        Pointer to data buffer to update.
+
+  @retval EFI_SUCCESS             The variable data was updated successfully.
+  @retval EFI_UNSUPPORTED         If this function is called directly in runtime.
+  @retval EFI_INVALID_PARAMETER   If VariableInfo, Buffer or Size are not valid.
+  @retval Others                  Failed to update NV storage or variable cache.
+
+**/
+EFI_STATUS
+EFIAPI
+VariableExLibUpdateNvVariable (
+  IN  PROTECTED_VARIABLE_INFO  *VariableInfo,
+  IN  UINTN                    Offset,
+  IN  UINT32                   Size,
+  IN  UINT8                    *Buffer
+  )
+{
+  EFI_STATUS              Status;
+  VARIABLE_GLOBAL         *Global;
+  VARIABLE_RUNTIME_CACHE  *CacheInstance;
+  VARIABLE_HEADER         *VariableCache;
+
+  if ((mVariableModuleGlobal == NULL) || (mNvVariableCache == NULL)) {
+    return EFI_UNSUPPORTED;
+  }
+
+  //
+  // Flush the cache to store.
+  //
+  if (Size == (UINT32)-1) {
+    Status = FtwVariableSpace (
+               mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase,
+               mNvVariableCache
+               );
+    if (  !EFI_ERROR (Status)
+       && (mVariableModuleGlobal->VariableGlobal.HobVariableBase != 0))
+    {
+      FlushHobVariableToFlash (NULL, NULL);
+      if (mVariableModuleGlobal->VariableGlobal.HobVariableBase != 0) {
+        FreePool ((VOID *)(UINTN)mVariableModuleGlobal->VariableGlobal.HobVariableBase);
+        mVariableModuleGlobal->VariableGlobal.HobVariableBase = 0;
+      }
+    }
+
+    return Status;
+  }
+
+  if (  (VariableInfo == NULL)
+     || (VariableInfo->StoreIndex == VAR_INDEX_INVALID)
+     || (Buffer == NULL)
+     || (Size == 0))
+  {
+    ASSERT (VariableInfo != NULL);
+    ASSERT (VariableInfo->StoreIndex != VAR_INDEX_INVALID);
+    ASSERT (Buffer != NULL);
+    ASSERT (Size != 0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Global = &mVariableModuleGlobal->VariableGlobal;
+
+  VariableCache = (VARIABLE_HEADER *)((UINTN)mNvVariableCache + (UINTN)VariableInfo->StoreIndex);
+
+  ASSERT (
+    StrCmp (
+      VariableInfo->Header.VariableName,
+      GetVariableNamePtr (VariableCache, Global->AuthFormat)
+      ) == 0
+    );
+  ASSERT (
+    CompareGuid (
+      VariableInfo->Header.VendorGuid,
+      GetVendorGuidPtr (VariableCache, Global->AuthFormat)
+      )
+    );
+
+  //
+  // Forcibly update part data of flash copy of the variable ...
+  //
+  Status =  UpdateVariableStore (
+              Global,
+              FALSE,
+              FALSE,
+              mVariableModuleGlobal->FvbInstance,
+              (UINTN)(Global->NonVolatileVariableBase + VariableInfo->StoreIndex + Offset),
+              Size,
+              Buffer
+              );
+  if (EFI_ERROR (Status)) {
+    ASSERT_EFI_ERROR (Status);
+    return Status;
+  }
+
+  //
+  // ... as well as the local cached copy.
+  //
+  CopyMem ((VOID *)((UINTN)VariableCache + Offset), Buffer, Size);
+
+  //
+  // Sync remote cached copy.
+  //
+  CacheInstance = &Global->VariableRuntimeCacheContext.VariableRuntimeNvCache;
+  if (CacheInstance->Store != NULL) {
+    Status =  SynchronizeRuntimeVariableCache (
+                CacheInstance,
+                (UINTN)VariableInfo->StoreIndex + Offset,
+                Size
+                );
+    ASSERT_EFI_ERROR (Status);
+  }
+
+  return EFI_SUCCESS;
+}
diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableNonVolatile.c b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableNonVolatile.c
index 9e2d8fe0fe0c..32dd9901b260 100644
--- a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableNonVolatile.c
+++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableNonVolatile.c
@@ -1,7 +1,7 @@
 /** @file
   Common variable non-volatile store routines.
 
-Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
+Copyright (c) 2019 - 2022, Intel Corporation. All rights reserved.<BR>
 SPDX-License-Identifier: BSD-2-Clause-Patent
 
 **/
@@ -120,6 +120,181 @@ InitEmuNonVolatileVariableStore (
   return EFI_SUCCESS;
 }
 
+/**
+
+  Create a dummy variable used to fill the gap in NV variable storage caused by
+  the invalid variables found in HMAC verification phase.
+
+  @param[out] Variable    Variable buffer.
+  @param[in]  Name        Variable Name.
+  @param[in]  Guid        Vendor GUID of the variable.
+  @param[in]  Size        Whole size of the variable requested.
+  @param[in]  AuthFlag    Variable format flag.
+
+**/
+STATIC
+VOID
+CreateDummyVariable (
+  OUT VARIABLE_HEADER  *Variable,
+  IN  CHAR16           *Name,
+  IN  EFI_GUID         *Guid,
+  IN  UINT32           Size,
+  IN  BOOLEAN          AuthFlag
+  )
+{
+  AUTHENTICATED_VARIABLE_HEADER  *AuthVariable;
+
+  ASSERT (Variable != NULL);
+
+  if (Name == NULL) {
+    Name = L"Dummy";
+  }
+
+  if (AuthFlag) {
+    AuthVariable = (AUTHENTICATED_VARIABLE_HEADER *)Variable;
+
+    AuthVariable->StartId    = VARIABLE_DATA;
+    AuthVariable->State      = VAR_ADDED & VAR_DELETED;
+    AuthVariable->Attributes = EFI_VARIABLE_NON_VOLATILE;
+    AuthVariable->NameSize   = (UINT32)StrSize (Name);
+    AuthVariable->DataSize   = Size - sizeof (AUTHENTICATED_VARIABLE_HEADER)
+                               - AuthVariable->NameSize;
+    if (Guid != NULL) {
+      CopyMem ((VOID *)&AuthVariable->VendorGuid, (VOID *)Guid, sizeof (EFI_GUID));
+    }
+
+    CopyMem (GetVariableNamePtr (Variable, AuthFlag), (VOID *)Name, AuthVariable->NameSize);
+  } else {
+    Variable->StartId    = VARIABLE_DATA;
+    Variable->State      = VAR_ADDED & VAR_DELETED;
+    Variable->Attributes = EFI_VARIABLE_NON_VOLATILE;
+    Variable->NameSize   = (UINT32)StrSize (Name);
+    Variable->DataSize   = Size - sizeof (VARIABLE_HEADER) - Variable->NameSize;
+    if (Guid != NULL) {
+      CopyMem ((VOID *)&Variable->VendorGuid, (VOID *)Guid, sizeof (EFI_GUID));
+    }
+
+    CopyMem (GetVariableNamePtr (Variable, AuthFlag), (VOID *)Name, Variable->NameSize);
+  }
+}
+
+/**
+
+  Init protected variable store.
+
+  @param[in, out]  VariableStore  Pointer to real protected variable store base.
+
+**/
+EFI_STATUS
+InitProtectedVariableStore (
+  IN  OUT VARIABLE_STORE_HEADER  *VariableStore
+  )
+{
+  EFI_STATUS               Status;
+  PROTECTED_VARIABLE_INFO  VarInfo;
+  UINTN                    Size;
+  UINTN                    Index;
+  BOOLEAN                  AuthFlag;
+  EFI_PHYSICAL_ADDRESS     NextVariableStore;
+  EFI_PHYSICAL_ADDRESS     *VarList;
+  UINTN                    NumVars;
+  UINTN                    CurrVar;
+
+  SetMem (
+    (UINT8 *)VariableStore + sizeof (VARIABLE_STORE_HEADER),
+    VariableStore->Size - sizeof (VARIABLE_STORE_HEADER),
+    0xFF
+    );
+  Index = sizeof (VARIABLE_STORE_HEADER);
+
+  VarList = NULL;
+  NumVars = 0;
+  ProtectedVariableLibGetSortedList (&VarList, &NumVars);
+
+  //
+  // Search variable in the order of StoreIndex
+  //
+  ZeroMem (&VarInfo, sizeof (VarInfo));
+
+  for (CurrVar = 0; CurrVar < NumVars; CurrVar++) {
+    VarInfo.Buffer     = NULL;
+    VarInfo.StoreIndex = VarList[CurrVar];
+    if (VarInfo.StoreIndex == VAR_INDEX_INVALID) {
+      break;
+    }
+
+    Status = ProtectedVariableLibFind (&VarInfo);
+    if (EFI_ERROR (Status)) {
+      break;
+    }
+
+    ASSERT (VarInfo.Buffer != NULL);
+
+    AuthFlag = VarInfo.Flags.Auth;
+    if (VarInfo.StoreIndex == VAR_INDEX_INVALID) {
+      continue;
+    } else {
+      ASSERT (VarInfo.StoreIndex == HEADER_ALIGN (VarInfo.StoreIndex));
+      ASSERT (VarInfo.StoreIndex < (VariableStore->Size - sizeof (VARIABLE_STORE_HEADER)));
+      ASSERT ((VariableStore->Size - VarInfo.StoreIndex) > GetVariableHeaderSize (AuthFlag));
+    }
+
+    //
+    // Fill gap caused by invalid variable.
+    //
+    if (VarInfo.StoreIndex > Index) {
+      Size = (UINTN)VarInfo.StoreIndex - Index;
+      CreateDummyVariable (
+        (VARIABLE_HEADER *)((UINT8 *)VariableStore + Index),
+        NULL,
+        NULL,
+        (UINT32)Size,
+        AuthFlag
+        );
+      Index += Size;
+    }
+
+    Size = (UINTN)GetNextVariablePtr (VarInfo.Buffer, AuthFlag)
+           - (UINTN)VarInfo.Buffer;
+    CopyMem ((UINT8 *)VariableStore + VarInfo.StoreIndex, VarInfo.Buffer, Size);
+
+    Index += Size;
+    Index  = HEADER_ALIGN (Index);
+
+    NextVariableStore = (EFI_PHYSICAL_ADDRESS)((UINTN)VariableStore + VarInfo.StoreIndex + Size);
+  }
+
+  //
+  // Search variable in the order of StoreIndex
+  //
+  ZeroMem (&VarInfo, sizeof (VarInfo));
+  for ( ; CurrVar < NumVars; CurrVar++) {
+    VarInfo.Buffer     = NULL;
+    VarInfo.StoreIndex = VarList[CurrVar];
+    Status             = ProtectedVariableLibFind (&VarInfo);
+    if (EFI_ERROR (Status)) {
+      break;
+    }
+
+    ASSERT (VarInfo.Buffer != NULL);
+
+    AuthFlag = VarInfo.Flags.Auth;
+    if (VarInfo.StoreIndex == VAR_INDEX_INVALID) {
+      Size = (UINTN)GetNextVariablePtr (VarInfo.Buffer, AuthFlag)
+             - (UINTN)VarInfo.Buffer;
+      CopyMem ((VOID *)(UINTN)NextVariableStore, VarInfo.Buffer, Size);
+      Status            = ProtectedVariableLibRefresh (VarInfo.Buffer, 0, NextVariableStore - (UINTN)VariableStore, FALSE);
+      NextVariableStore = NextVariableStore + Size;
+    }
+  }
+
+  if (Status == EFI_UNSUPPORTED) {
+    return Status;
+  }
+
+  return EFI_SUCCESS;
+}
+
 /**
   Init real non-volatile variable store.
 
@@ -236,6 +411,16 @@ InitRealNonVolatileVariableStore (
     return EFI_VOLUME_CORRUPTED;
   }
 
+  //
+  // Overwrite the store with verified copy of protected variables, if enabled.
+  //
+  Status = InitProtectedVariableStore (VariableStore);
+  if ((Status != EFI_SUCCESS) && (Status != EFI_UNSUPPORTED)) {
+    FreePool (NvStorageData);
+    DEBUG ((DEBUG_ERROR, "Variable integrity might have been compromised\n"));
+    return Status;
+  }
+
   mNvFvHeaderCache = FvHeader;
 
   *VariableStoreBase = (EFI_PHYSICAL_ADDRESS)(UINTN)VariableStore;
@@ -329,7 +514,12 @@ InitNonVolatileVariableStore (
   // Parse non-volatile variable data and get last variable offset.
   //
   Variable = GetStartPointer (mNvVariableCache);
-  while (IsValidVariableHeader (Variable, GetEndPointer (mNvVariableCache))) {
+  while (IsValidVariableHeader (
+           Variable,
+           GetEndPointer (mNvVariableCache),
+           mVariableModuleGlobal->VariableGlobal.AuthFormat
+           ))
+  {
     NextVariable = GetNextVariablePtr (Variable, mVariableModuleGlobal->VariableGlobal.AuthFormat);
     VariableSize = (UINTN)NextVariable - (UINTN)Variable;
     if ((Variable->Attributes & (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_HARDWARE_ERROR_RECORD)) == (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_HARDWARE_ERROR_RECORD)) {
diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableParsing.c b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableParsing.c
index 39060ed405b8..000e4b546888 100644
--- a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableParsing.c
+++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableParsing.c
@@ -2,11 +2,12 @@
   Functions in this module are associated with variable parsing operations and
   are intended to be usable across variable driver source files.
 
-Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
+Copyright (c) 2019 - 2022, Intel Corporation. All rights reserved.<BR>
 SPDX-License-Identifier: BSD-2-Clause-Patent
 
 **/
 
+#include "Variable.h"
 #include "VariableParsing.h"
 
 /**
@@ -15,6 +16,8 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
 
   @param[in] Variable           Pointer to the Variable Header.
   @param[in] VariableStoreEnd   Pointer to the Variable Store End.
+  @param[in] AuthFormat         TRUE indicates authenticated variables are used.
+                                FALSE indicates authenticated variables are not used.
 
   @retval TRUE              Variable header is valid.
   @retval FALSE             Variable header is not valid.
@@ -23,10 +26,14 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
 BOOLEAN
 IsValidVariableHeader (
   IN  VARIABLE_HEADER  *Variable,
-  IN  VARIABLE_HEADER  *VariableStoreEnd
+  IN  VARIABLE_HEADER  *VariableStoreEnd,
+  IN  BOOLEAN          AuthFormat
   )
 {
-  if ((Variable == NULL) || (Variable >= VariableStoreEnd) || (Variable->StartId != VARIABLE_DATA)) {
+  if (  (Variable == NULL)
+     || (((UINTN)Variable + GetVariableHeaderSize (AuthFormat)) >= (UINTN)VariableStoreEnd)
+     || (Variable->StartId != VARIABLE_DATA))
+  {
     //
     // Variable is NULL or has reached the end of variable store,
     // or the StartId is not correct.
@@ -341,6 +348,52 @@ GetVariableDataOffset (
   return Value;
 }
 
+/**
+  Get variable data payload.
+
+  @param[in]      Variable     Pointer to the Variable Header.
+  @param[in,out]  Data         Pointer to buffer used to store the variable data.
+  @param[in,out]  DataSize     Size of buffer passed by Data.
+                               Size of data copied into Data buffer.
+  @param[in]      AuthFlag     Auth-variable indicator.
+
+  @return EFI_SUCCESS             Data was fetched.
+  @return EFI_INVALID_PARAMETER   DataSize is NULL.
+  @return EFI_BUFFER_TOO_SMALL    DataSize is smaller than size of variable data.
+
+**/
+EFI_STATUS
+GetVariableData (
+  IN      VARIABLE_HEADER  *Variable,
+  IN  OUT VOID             *Data,
+  IN  OUT UINT32           *DataSize,
+  IN      BOOLEAN          AuthFlag
+  )
+{
+  UINT32  Size;
+
+  if (DataSize == NULL) {
+    ASSERT (DataSize != NULL);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Size = (UINT32)DataSizeOfVariable (Variable, AuthFlag);
+  if (*DataSize < Size) {
+    *DataSize = Size;
+    return EFI_BUFFER_TOO_SMALL;
+  }
+
+  if (Data == NULL) {
+    ASSERT (Data != NULL);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  CopyMem (Data, GetVariableDataPtr (Variable, AuthFlag), Size);
+  *DataSize = Size;
+
+  return EFI_SUCCESS;
+}
+
 /**
 
   This code gets the pointer to the next variable header.
@@ -479,7 +532,7 @@ FindVariableEx (
   InDeletedVariable = NULL;
 
   for ( PtrTrack->CurrPtr = PtrTrack->StartPtr
-        ; IsValidVariableHeader (PtrTrack->CurrPtr, PtrTrack->EndPtr)
+        ; IsValidVariableHeader (PtrTrack->CurrPtr, PtrTrack->EndPtr, AuthFormat)
         ; PtrTrack->CurrPtr = GetNextVariablePtr (PtrTrack->CurrPtr, AuthFormat)
         )
   {
@@ -608,7 +661,7 @@ VariableServiceGetNextVariableInternal (
     //
     // Switch to the next variable store if needed
     //
-    while (!IsValidVariableHeader (Variable.CurrPtr, Variable.EndPtr)) {
+    while (!IsValidVariableHeader (Variable.CurrPtr, Variable.EndPtr, AuthFormat)) {
       //
       // Find current storage index
       //
@@ -804,3 +857,260 @@ UpdateVariableInfo (
     }
   }
 }
+
+/**
+
+  Retrieve details about a variable and return them in VariableInfo->Header.
+
+  If VariableInfo->Address is given, this function will calculate its offset
+  relative to given variable storage via VariableStore; Otherwise, it will try
+  other internal variable storages or cached copies. It's assumed that, for all
+  copies of NV variable storage, all variables are stored in the same relative
+  position. If VariableInfo->Address is found in the range of any storage copies,
+  its offset relative to that storage should be the same in other copies.
+
+  If VariableInfo->Offset is given (non-zero) but not VariableInfo->Address,
+  this function will return the variable memory address inside VariableStore,
+  if given, via VariableInfo->Address; Otherwise, the address of other storage
+  copies will be returned, if any.
+
+  For a new variable whose offset has not been determined, a value of -1 as
+  VariableInfo->Offset should be passed to skip the offset calculation.
+
+  @param[in,out] VariableInfo             Pointer to variable information.
+
+  @retval EFI_INVALID_PARAMETER  VariableInfo is NULL or both VariableInfo->Address
+                                 and VariableInfo->Offset are NULL (0).
+  @retval EFI_NOT_FOUND          If given Address or Offset is out of range of
+                                 any given or internal storage copies.
+  @retval EFI_SUCCESS            Variable details are retrieved successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+GetVariableInfo (
+  IN OUT  PROTECTED_VARIABLE_INFO  *VariableInfo
+  )
+{
+  VARIABLE_STORE_HEADER          *Stores[2];
+  UINTN                          Index;
+  VARIABLE_HEADER                *VariablePtr;
+  VARIABLE_HEADER                *VariableBuffer;
+  AUTHENTICATED_VARIABLE_HEADER  *AuthVariablePtr;
+  BOOLEAN                        AuthFlag;
+  UINTN                          NameSize;
+  UINTN                          DataSize;
+  UINTN                          VariableSize;
+
+  if ((VariableInfo == NULL) || (  (VariableInfo->Buffer == NULL)
+                                && (VariableInfo->StoreIndex == VAR_INDEX_INVALID)))
+  {
+    ASSERT (VariableInfo != NULL);
+    ASSERT (VariableInfo->Buffer != NULL || VariableInfo->StoreIndex != VAR_INDEX_INVALID);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Stores[0] = mNvVariableCache;
+  Stores[1] = (mVariableModuleGlobal != NULL)
+              ? (VARIABLE_STORE_HEADER *)(UINTN)mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase
+              : NULL;
+
+  VariableBuffer = VariableInfo->Buffer;
+  VariablePtr    = NULL;
+  if (VariableInfo->StoreIndex != VAR_INDEX_INVALID) {
+    for (Index = 0; Index < ARRAY_SIZE (Stores); ++Index) {
+      if (Stores[Index] == NULL) {
+        continue;
+      }
+
+      if ((UINTN)VariableInfo->StoreIndex
+          < ((UINTN)GetEndPointer (Stores[Index]) - (UINTN)Stores[Index]))
+      {
+        VariablePtr          = (VARIABLE_HEADER *)((UINTN)Stores[Index] + (UINTN)VariableInfo->StoreIndex);
+        VariableInfo->Buffer = VariablePtr;
+        break;
+      }
+    }
+  } else {
+    VariablePtr = VariableInfo->Buffer;
+  }
+
+  if (VariablePtr == NULL) {
+    return EFI_NOT_FOUND;
+  }
+
+  AuthFlag = VariableInfo->Flags.Auth;
+  ASSERT (AuthFlag == TRUE || AuthFlag == FALSE);
+
+  //
+  // Make a copy of the whole variable if a buffer is passed in.
+  //
+  if ((VariableBuffer != NULL) && (VariableBuffer != VariablePtr)) {
+    VariableSize = (UINTN)GetNextVariablePtr (VariablePtr, AuthFlag)
+                   - (UINTN)VariablePtr;
+    CopyMem (VariableBuffer, VariablePtr, VariableSize);
+  }
+
+  //
+  // AuthVariable header
+  //
+  if (AuthFlag) {
+    AuthVariablePtr = (AUTHENTICATED_VARIABLE_HEADER *)VariablePtr;
+
+    VariableInfo->Header.State          = AuthVariablePtr->State;
+    VariableInfo->Header.Attributes     = AuthVariablePtr->Attributes;
+    VariableInfo->Header.PubKeyIndex    = AuthVariablePtr->PubKeyIndex;
+    VariableInfo->Header.MonotonicCount = ReadUnaligned64 (
+                                            &(AuthVariablePtr->MonotonicCount)
+                                            );
+    if (VariableInfo->Header.TimeStamp != NULL) {
+      CopyMem (
+        VariableInfo->Header.TimeStamp,
+        &AuthVariablePtr->TimeStamp,
+        sizeof (EFI_TIME)
+        );
+    } else if (VariableBuffer != NULL) {
+      AuthVariablePtr                = (AUTHENTICATED_VARIABLE_HEADER *)VariableBuffer;
+      VariableInfo->Header.TimeStamp = &AuthVariablePtr->TimeStamp;
+    }
+  } else {
+    VariableInfo->Header.State          = VariablePtr->State;
+    VariableInfo->Header.Attributes     = VariablePtr->Attributes;
+    VariableInfo->Header.PubKeyIndex    = 0;
+    VariableInfo->Header.MonotonicCount = 0;
+    VariableInfo->Header.TimeStamp      = NULL;
+  }
+
+  //
+  // VendorGuid
+  //
+  if (VariableInfo->Header.VendorGuid != NULL) {
+    CopyGuid (
+      VariableInfo->Header.VendorGuid,
+      GetVendorGuidPtr (VariablePtr, AuthFlag)
+      );
+  } else {
+    VariableInfo->Header.VendorGuid = GetVendorGuidPtr (VariablePtr, AuthFlag);
+  }
+
+  //
+  // VariableName
+  //
+  NameSize = NameSizeOfVariable (VariablePtr, AuthFlag);
+  if (  (VariableInfo->Header.VariableName != NULL)
+     && (VariableInfo->Header.NameSize >= NameSize))
+  {
+    CopyMem (
+      VariableInfo->Header.VariableName,
+      GetVariableNamePtr (VariablePtr, AuthFlag),
+      NameSize
+      );
+  } else if (VariableInfo->Header.VariableName != NULL) {
+    return EFI_BUFFER_TOO_SMALL;
+  } else {
+    VariableInfo->Header.VariableName = GetVariableNamePtr (VariablePtr, AuthFlag);
+  }
+
+  //
+  // Data
+  //
+  DataSize = DataSizeOfVariable (VariablePtr, AuthFlag);
+  if (  (VariableInfo->Header.Data != NULL)
+     && (VariableInfo->Header.DataSize >= DataSize))
+  {
+    CopyMem (
+      VariableInfo->Header.Data,
+      GetVariableDataPtr (VariablePtr, AuthFlag),
+      NameSize
+      );
+  } else if (VariableInfo->Header.Data != NULL) {
+    return EFI_BUFFER_TOO_SMALL;
+  } else {
+    VariableInfo->Header.Data = GetVariableDataPtr (VariablePtr, AuthFlag);
+  }
+
+  //
+  // Update size information about name & data.
+  //
+  VariableInfo->Header.NameSize = NameSize;
+  VariableInfo->Header.DataSize = DataSize;
+
+  return EFI_SUCCESS;
+}
+
+/**
+
+  Retrieve details of the variable next to given variable within VariableStore.
+
+  If VarInfo->Address is NULL, the first one in VariableStore is returned.
+
+  VariableStart and/or VariableEnd can be given optionally for the situation
+  in which the valid storage space is smaller than the VariableStore->Size.
+  This usually happens when PEI variable services make a compact variable
+  cache to save memory, which cannot make use VariableStore->Size to determine
+  the correct variable storage range.
+
+  @param[in,out] VariableInfo             Pointer to variable information.
+
+  @retval EFI_INVALID_PARAMETER  VariableInfo or VariableStore is NULL.
+  @retval EFI_NOT_FOUND          If the end of VariableStore is reached.
+  @retval EFI_SUCCESS            The next variable is retrieved successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+GetNextVariableInfo (
+  IN OUT  PROTECTED_VARIABLE_INFO  *VariableInfo
+  )
+{
+  VARIABLE_STORE_HEADER  *VarStore;
+  VARIABLE_HEADER        *VariablePtr;
+  VARIABLE_HEADER        *VariableStart;
+  VARIABLE_HEADER        *VariableEnd;
+  BOOLEAN                AuthFlag;
+
+  if (VariableInfo == NULL) {
+    ASSERT (VariableInfo != NULL);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (mNvVariableCache != NULL) {
+    VarStore = mNvVariableCache;
+  } else if (mVariableModuleGlobal != NULL) {
+    VarStore = (VARIABLE_STORE_HEADER *)(UINTN)
+               mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase;
+  } else {
+    return EFI_NOT_FOUND;
+  }
+
+  VariableStart = GetStartPointer (VarStore);
+  VariableEnd   = GetEndPointer (VarStore);
+
+  if ((VariableInfo->Flags.Auth != TRUE) && (VariableInfo->Flags.Auth != FALSE)) {
+    VariableInfo->Flags.Auth = CompareGuid (
+                                 &VarStore->Signature,
+                                 &gEfiAuthenticatedVariableGuid
+                                 );
+  }
+
+  AuthFlag = VariableInfo->Flags.Auth;
+
+  if (VariableInfo->StoreIndex == VAR_INDEX_INVALID) {
+    VariablePtr = VariableStart;
+  } else {
+    VariablePtr = (VARIABLE_HEADER *)
+                  ((UINTN)VarStore + (UINTN)VariableInfo->StoreIndex);
+    if (VariablePtr >= VariableEnd) {
+      return EFI_NOT_FOUND;
+    }
+
+    VariablePtr = GetNextVariablePtr (VariablePtr, AuthFlag);
+  }
+
+  if (!IsValidVariableHeader (VariablePtr, VariableEnd, AuthFlag)) {
+    return EFI_NOT_FOUND;
+  }
+
+  VariableInfo->StoreIndex = (UINTN)VariablePtr - (UINTN)VarStore;
+  return GetVariableInfo (VariableInfo);
+}
diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeCache.c b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeCache.c
index 9bb30bc1e804..dc319feee727 100644
--- a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeCache.c
+++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeCache.c
@@ -7,7 +7,7 @@
   This external input must be validated carefully to avoid security issue like
   buffer overflow, integer overflow.
 
-Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
+Copyright (c) 2019 - 2022, Intel Corporation. All rights reserved.<BR>
 SPDX-License-Identifier: BSD-2-Clause-Patent
 
 **/
diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.c b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.c
index 5253c328dcd9..f7bac0227577 100644
--- a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.c
+++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.c
@@ -14,7 +14,7 @@
   VariableServiceSetVariable(), VariableServiceQueryVariableInfo(), ReclaimForOS(),
   SmmVariableGetStatistics() should also do validation based on its own knowledge.
 
-Copyright (c) 2010 - 2019, Intel Corporation. All rights reserved.<BR>
+Copyright (c) 2010 - 2022, Intel Corporation. All rights reserved.<BR>
 Copyright (c) 2018, Linaro, Ltd. All rights reserved.<BR>
 SPDX-License-Identifier: BSD-2-Clause-Patent
 
@@ -1045,6 +1045,13 @@ VariableWriteServiceInitializeSmm (
 {
   EFI_STATUS  Status;
 
+  Status = ProtectedVariableLibWriteInit ();
+  if (EFI_ERROR (Status) && (Status != EFI_UNSUPPORTED)) {
+    DEBUG ((DEBUG_ERROR, "Variable protection service: write-init failed. Status = %r\n", Status));
+    ASSERT_EFI_ERROR (Status);
+    return;
+  }
+
   Status = VariableWriteServiceInitialize ();
   if (EFI_ERROR (Status)) {
     DEBUG ((DEBUG_ERROR, "Variable write service initialization failed. Status = %r\n", Status));
@@ -1152,10 +1159,32 @@ MmVariableServiceInitialize (
   VOID
   )
 {
-  EFI_STATUS  Status;
-  EFI_HANDLE  VariableHandle;
-  VOID        *SmmFtwRegistration;
-  VOID        *SmmEndOfDxeRegistration;
+  EFI_STATUS                     Status;
+  EFI_HANDLE                     VariableHandle;
+  VOID                           *SmmFtwRegistration;
+  VOID                           *SmmEndOfDxeRegistration;
+  PROTECTED_VARIABLE_CONTEXT_IN  ContextIn;
+
+  //
+  // Initialize protected variable service, if enabled.
+  //
+  ContextIn.StructSize    = sizeof (ContextIn);
+  ContextIn.StructVersion = PROTECTED_VARIABLE_CONTEXT_IN_STRUCT_VERSION;
+
+  ContextIn.FindVariableSmm     = NULL;
+  ContextIn.GetVariableInfo     = GetVariableInfo;
+  ContextIn.GetNextVariableInfo = GetNextVariableInfo;
+  ContextIn.UpdateVariableStore = VariableExLibUpdateNvVariable;
+  ContextIn.UpdateVariable      = VariableExLibUpdateVariable;
+
+  ContextIn.MaxVariableSize     = (UINT32)GetMaxVariableSize ();
+  ContextIn.VariableServiceUser = FromSmmModule;
+
+  Status = ProtectedVariableLibInitialize (&ContextIn);
+  if (EFI_ERROR (Status) && (Status != EFI_UNSUPPORTED)) {
+    ASSERT_EFI_ERROR (Status);
+    return Status;
+  }
 
   //
   // Variable initialize.
diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.c b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.c
index 4aaeb5ba8806..5d4b4f9b3da7 100644
--- a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.c
+++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.c
@@ -13,7 +13,7 @@
 
   InitCommunicateBuffer() is really function to check the variable data size.
 
-Copyright (c) 2010 - 2019, Intel Corporation. All rights reserved.<BR>
+Copyright (c) 2010 - 2022, Intel Corporation. All rights reserved.<BR>
 Copyright (c) Microsoft Corporation.<BR>
 SPDX-License-Identifier: BSD-2-Clause-Patent
 
@@ -36,11 +36,13 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
 #include <Library/UefiLib.h>
 #include <Library/BaseLib.h>
 #include <Library/MmUnblockMemoryLib.h>
+#include <Library/IoLib.h>
 
 #include <Guid/EventGroup.h>
 #include <Guid/SmmVariableCommon.h>
 
 #include "PrivilegePolymorphic.h"
+#include "Variable.h"
 #include "VariableParsing.h"
 
 EFI_HANDLE                      mHandle                              = NULL;
@@ -53,6 +55,8 @@ VARIABLE_INFO_ENTRY             *mVariableInfo                       = NULL;
 VARIABLE_STORE_HEADER           *mVariableRuntimeHobCacheBuffer      = NULL;
 VARIABLE_STORE_HEADER           *mVariableRuntimeNvCacheBuffer       = NULL;
 VARIABLE_STORE_HEADER           *mVariableRuntimeVolatileCacheBuffer = NULL;
+VARIABLE_STORE_HEADER           *mNvVariableCache                    = NULL;
+VARIABLE_MODULE_GLOBAL          *mVariableModuleGlobal               = NULL;
 UINTN                           mVariableBufferSize;
 UINTN                           mVariableRuntimeHobCacheBufferSize;
 UINTN                           mVariableRuntimeNvCacheBufferSize;
@@ -616,7 +620,6 @@ FindVariableInRuntimeCache (
   )
 {
   EFI_STATUS              Status;
-  UINTN                   TempDataSize;
   VARIABLE_POINTER_TRACK  RtPtrTrack;
   VARIABLE_STORE_TYPE     StoreType;
   VARIABLE_STORE_HEADER   *VariableStoreList[VariableStoreTypeMax];
@@ -669,31 +672,23 @@ FindVariableInRuntimeCache (
       //
       // Get data size
       //
-      TempDataSize = DataSizeOfVariable (RtPtrTrack.CurrPtr, mVariableAuthFormat);
-      ASSERT (TempDataSize != 0);
-
-      if (*DataSize >= TempDataSize) {
-        if (Data == NULL) {
-          Status = EFI_INVALID_PARAMETER;
-          goto Done;
-        }
-
-        CopyMem (Data, GetVariableDataPtr (RtPtrTrack.CurrPtr, mVariableAuthFormat), TempDataSize);
-        *DataSize = TempDataSize;
-
-        UpdateVariableInfo (VariableName, VendorGuid, RtPtrTrack.Volatile, TRUE, FALSE, FALSE, TRUE, &mVariableInfo);
-
-        Status = EFI_SUCCESS;
-        goto Done;
-      } else {
-        *DataSize = TempDataSize;
-        Status    = EFI_BUFFER_TOO_SMALL;
-        goto Done;
+      if (!RtPtrTrack.Volatile) {
+        //
+        // Currently only non-volatile variable needs protection.
+        //
+        Status = ProtectedVariableLibGetByBuffer (RtPtrTrack.CurrPtr, Data, (UINT32 *)DataSize, mVariableAuthFormat);
+      }
+
+      if (RtPtrTrack.Volatile || (Status == EFI_UNSUPPORTED)) {
+        Status = GetVariableData (RtPtrTrack.CurrPtr, Data, (UINT32 *)DataSize, mVariableAuthFormat);
+      }
+
+      if (!EFI_ERROR (Status)) {
+        UpdateVariableInfo (VariableName, VendorGuid, RtPtrTrack.Volatile, TRUE, FALSE, FALSE, FALSE, &mVariableInfo);
       }
     }
   }
 
-Done:
   if ((Status == EFI_SUCCESS) || (Status == EFI_BUFFER_TOO_SMALL)) {
     if ((Attributes != NULL) && (RtPtrTrack.CurrPtr != NULL)) {
       *Attributes = RtPtrTrack.CurrPtr->Attributes;
@@ -724,6 +719,7 @@ Done:
 
 **/
 EFI_STATUS
+EFIAPI
 FindVariableInSmm (
   IN      CHAR16    *VariableName,
   IN      EFI_GUID  *VendorGuid,
@@ -1633,7 +1629,8 @@ SmmVariableReady (
   IN  VOID       *Context
   )
 {
-  EFI_STATUS  Status;
+  EFI_STATUS                     Status;
+  PROTECTED_VARIABLE_CONTEXT_IN  ContextIn;
 
   Status = gBS->LocateProtocol (&gEfiSmmVariableProtocolGuid, NULL, (VOID **)&mSmmVariable);
   if (EFI_ERROR (Status)) {
@@ -1731,6 +1728,28 @@ SmmVariableReady (
                                                      );
   ASSERT_EFI_ERROR (Status);
 
+  ContextIn.StructSize    = sizeof (ContextIn);
+  ContextIn.StructVersion = PROTECTED_VARIABLE_CONTEXT_IN_STRUCT_VERSION;
+
+  ContextIn.FindVariableSmm     = FindVariableInSmm;
+  ContextIn.GetVariableInfo     = GetVariableInfo;
+  ContextIn.GetNextVariableInfo = GetNextVariableInfo;
+  ContextIn.VariableServiceUser = FromRuntimeModule;
+  ContextIn.MaxVariableSize     = 0;
+  ContextIn.UpdateVariableStore = NULL;
+  ContextIn.UpdateVariable      = NULL;
+
+  Status = ProtectedVariableLibInitialize (&ContextIn);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((
+      DEBUG_INFO,
+      "%a: %d ProtectedVariableLibInitialize() return status: %r\n",
+      __FUNCTION__,
+      __LINE__,
+      Status
+      ));
+  }
+
   gBS->CloseEvent (Event);
 }
 
-- 
2.35.1.windows.2


  parent reply	other threads:[~2022-06-09  6:03 UTC|newest]

Thread overview: 39+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-06-09  6:02 [PATCH v3 00/28] UEFI variable protection Judah Vang
2022-06-09  6:02 ` [PATCH v3 01/28] MdeModulePkg: Add new GUID for Variable Store Info Judah Vang
2022-06-13  6:05   ` [edk2-devel] " Wu, Hao A
2022-06-09  6:02 ` [PATCH v3 02/28] SecurityPkg: Add new GUIDs for Judah Vang
2022-06-09  6:02 ` [PATCH v3 03/28] MdeModulePkg: Update AUTH_VARIABLE_INFO struct Judah Vang
2022-06-09  6:02 ` [PATCH v3 04/28] MdeModulePkg: Add reference to new Ppi Guid Judah Vang
2022-06-09  6:02 ` [PATCH v3 05/28] MdeModulePkg: Add new ProtectedVariable GUIDs Judah Vang
2022-06-13  6:07   ` [edk2-devel] " Wu, Hao A
2022-06-09  6:03 ` [PATCH v3 06/28] MdeModulePkg: Add new include files Judah Vang
2022-06-13  6:07   ` [edk2-devel] " Wu, Hao A
2022-06-09  6:03 ` [PATCH v3 07/28] MdeModulePkg: Add Null ProtectedVariable Library Judah Vang
2022-06-09  6:03 ` [PATCH v3 08/28] MdeModulePkg: Add new Variable functionality Judah Vang
2022-06-13  6:08   ` Wu, Hao A
2022-06-09  6:03 ` Judah Vang [this message]
2022-06-13  6:08   ` [PATCH v3 09/28] MdeModulePkg: Add support for Protected Variables Wu, Hao A
2022-06-09  6:03 ` [PATCH v3 10/28] SecurityPkg: Add new KeyService types and defines Judah Vang
2022-06-09  6:03 ` [PATCH v3 11/28] SecurityPkg: Update RPMC APIs with index Judah Vang
2022-06-09  6:03 ` [PATCH v3 12/28] SecurityPkg: Add new variable types and functions Judah Vang
2022-06-09  6:03 ` [PATCH v3 13/28] SecurityPkg: Fix GetVariableKey API Judah Vang
2022-06-09  6:03 ` [PATCH v3 14/28] SecurityPkg: Add null encryption variable libs Judah Vang
2022-06-09  6:03 ` [PATCH v3 15/28] SecurityPkg: Add VariableKey library function Judah Vang
2022-06-09  6:03 ` [PATCH v3 16/28] SecurityPkg: Add EncryptionVariable lib with AES Judah Vang
2022-06-09  6:03 ` [PATCH v3 17/28] SecurityPkg: Add Protected Variable Services Judah Vang
2022-06-09  8:30   ` Min Xu
2022-06-16 19:23     ` [edk2-devel] " Judah Vang
2022-08-07 17:34   ` Wang, Jian J
2022-06-09  6:03 ` [PATCH v3 18/28] MdeModulePkg: Reference Null ProtectedVariableLib Judah Vang
2022-06-13  6:08   ` [edk2-devel] " Wu, Hao A
2022-06-09  6:03 ` [PATCH v3 19/28] SecurityPkg: Add references to new *.inf files Judah Vang
2022-06-09  6:03 ` [PATCH v3 20/28] ArmVirtPkg: Add reference to ProtectedVariableNull Judah Vang
2022-06-09  6:03 ` [PATCH v3 21/28] UefiPayloadPkg: Add ProtectedVariable reference Judah Vang
2022-06-09  6:03 ` [PATCH v3 22/28] EmulatorPkg: " Judah Vang
2022-07-13  5:21   ` [edk2-devel] " Ni, Ray
2022-06-09  6:03 ` [PATCH v3 23/28] OvmfPkg: " Judah Vang
2022-06-09  6:03 ` [PATCH v3 24/28] OvmfPkg: Add ProtectedVariableLib reference Judah Vang
2022-06-09  6:03 ` [PATCH v3 25/28] " Judah Vang
2022-06-09  6:03 ` [PATCH v3 26/28] " Judah Vang
2022-06-09  6:03 ` [PATCH v3 27/28] OvmfPkg: Add ProtectedVariable reference Judah Vang
2022-06-09  6:03 ` [PATCH v3 28/28] CryptoPkg: Enable cypto HMAC KDF and AES 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=20220609060322.3491-10-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