]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.c
MdeModulePkg/Variable: Initialize local variable "RtPtrTrack"
[mirror_edk2.git] / MdeModulePkg / Universal / Variable / RuntimeDxe / VariableSmmRuntimeDxe.c
index e209d54755ef1bec8f1b47ffa2b20f0533b1915d..2cf0ed32ae55bea2094e7bb578a60810c242dbc1 100644 (file)
 \r
   InitCommunicateBuffer() is really function to check the variable data size.\r
 \r
-Copyright (c) 2010 - 2017, Intel Corporation. All rights reserved.<BR>\r
-This program and the accompanying materials\r
-are licensed and made available under the terms and conditions of the BSD License\r
-which accompanies this distribution.  The full text of the license may be found at\r
-http://opensource.org/licenses/bsd-license.php\r
-\r
-THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
-WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+Copyright (c) 2010 - 2019, Intel Corporation. All rights reserved.<BR>\r
+SPDX-License-Identifier: BSD-2-Clause-Patent\r
 \r
 **/\r
 #include <PiDxe.h>\r
@@ -44,32 +38,32 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
 #include <Guid/EventGroup.h>\r
 #include <Guid/SmmVariableCommon.h>\r
 \r
+#include "PrivilegePolymorphic.h"\r
+#include "VariableParsing.h"\r
+\r
 EFI_HANDLE                       mHandle                    = NULL;\r
 EFI_SMM_VARIABLE_PROTOCOL       *mSmmVariable               = NULL;\r
 EFI_EVENT                        mVirtualAddressChangeEvent = NULL;\r
 EFI_SMM_COMMUNICATION_PROTOCOL  *mSmmCommunication          = NULL;\r
 UINT8                           *mVariableBuffer            = NULL;\r
 UINT8                           *mVariableBufferPhysical    = NULL;\r
+VARIABLE_INFO_ENTRY             *mVariableInfo              = NULL;\r
+VARIABLE_STORE_HEADER           *mVariableRuntimeHobCacheBuffer           = NULL;\r
+VARIABLE_STORE_HEADER           *mVariableRuntimeNvCacheBuffer            = NULL;\r
+VARIABLE_STORE_HEADER           *mVariableRuntimeVolatileCacheBuffer      = NULL;\r
 UINTN                            mVariableBufferSize;\r
+UINTN                            mVariableRuntimeHobCacheBufferSize;\r
+UINTN                            mVariableRuntimeNvCacheBufferSize;\r
+UINTN                            mVariableRuntimeVolatileCacheBufferSize;\r
 UINTN                            mVariableBufferPayloadSize;\r
+BOOLEAN                          mVariableRuntimeCachePendingUpdate;\r
+BOOLEAN                          mVariableRuntimeCacheReadLock;\r
+BOOLEAN                          mVariableAuthFormat;\r
+BOOLEAN                          mHobFlushComplete;\r
 EFI_LOCK                         mVariableServicesLock;\r
 EDKII_VARIABLE_LOCK_PROTOCOL     mVariableLock;\r
 EDKII_VAR_CHECK_PROTOCOL         mVarCheck;\r
 \r
-/**\r
-  SecureBoot Hook for SetVariable.\r
-\r
-  @param[in] VariableName                 Name of Variable to be found.\r
-  @param[in] VendorGuid                   Variable vendor GUID.\r
-\r
-**/\r
-VOID\r
-EFIAPI\r
-SecureBootHook (\r
-  IN CHAR16                                 *VariableName,\r
-  IN EFI_GUID                               *VendorGuid\r
-  );\r
-\r
 /**\r
   Some Secure Boot Policy Variable may update following other variable changes(SecureBoot follows PK change, etc).\r
   Record their initial State when variable write service is ready.\r
@@ -125,6 +119,72 @@ ReleaseLockOnlyAtBootTime (
   }\r
 }\r
 \r
+/**\r
+  Return TRUE if ExitBootServices () has been called.\r
+\r
+  @retval TRUE If ExitBootServices () has been called. FALSE if ExitBootServices () has not been called.\r
+**/\r
+BOOLEAN\r
+AtRuntime (\r
+  VOID\r
+  )\r
+{\r
+  return EfiAtRuntime ();\r
+}\r
+\r
+/**\r
+  Initialize the variable cache buffer as an empty variable store.\r
+\r
+  @param[out]     VariableCacheBuffer     A pointer to pointer of a cache variable store.\r
+  @param[in,out]  TotalVariableCacheSize  On input, the minimum size needed for the UEFI variable store cache\r
+                                          buffer that is allocated. On output, the actual size of the buffer allocated.\r
+                                          If TotalVariableCacheSize is zero, a buffer will not be allocated and the\r
+                                          function will return with EFI_SUCCESS.\r
+\r
+  @retval EFI_SUCCESS             The variable cache was allocated and initialized successfully.\r
+  @retval EFI_INVALID_PARAMETER   A given pointer is NULL or an invalid variable store size was specified.\r
+  @retval EFI_OUT_OF_RESOURCES    Insufficient resources are available to allocate the variable store cache buffer.\r
+\r
+**/\r
+EFI_STATUS\r
+InitVariableCache (\r
+  OUT    VARIABLE_STORE_HEADER   **VariableCacheBuffer,\r
+  IN OUT UINTN                   *TotalVariableCacheSize\r
+  )\r
+{\r
+  VARIABLE_STORE_HEADER   *VariableCacheStorePtr;\r
+\r
+  if (TotalVariableCacheSize == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+  if (*TotalVariableCacheSize == 0) {\r
+    return EFI_SUCCESS;\r
+  }\r
+  if (VariableCacheBuffer == NULL || *TotalVariableCacheSize < sizeof (VARIABLE_STORE_HEADER)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+  *TotalVariableCacheSize = ALIGN_VALUE (*TotalVariableCacheSize, sizeof (UINT32));\r
+\r
+  //\r
+  // Allocate NV Storage Cache and initialize it to all 1's (like an erased FV)\r
+  //\r
+  *VariableCacheBuffer =  (VARIABLE_STORE_HEADER *) AllocateRuntimePages (\r
+                            EFI_SIZE_TO_PAGES (*TotalVariableCacheSize)\r
+                            );\r
+  if (*VariableCacheBuffer == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+  VariableCacheStorePtr = *VariableCacheBuffer;\r
+  SetMem32 ((VOID *) VariableCacheStorePtr, *TotalVariableCacheSize, (UINT32) 0xFFFFFFFF);\r
+\r
+  ZeroMem ((VOID *) VariableCacheStorePtr, sizeof (VARIABLE_STORE_HEADER));\r
+  VariableCacheStorePtr->Size    = (UINT32) *TotalVariableCacheSize;\r
+  VariableCacheStorePtr->Format  = VARIABLE_STORE_FORMATTED;\r
+  VariableCacheStorePtr->State   = VARIABLE_STORE_HEALTHY;\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
 /**\r
   Initialize the communicate buffer using DataSize and Function.\r
 \r
@@ -443,7 +503,56 @@ Done:
 }\r
 \r
 /**\r
-  This code finds variable in storage blocks (Volatile or Non-Volatile).\r
+  Signals SMM to synchronize any pending variable updates with the runtime cache(s).\r
+\r
+**/\r
+VOID\r
+SyncRuntimeCache (\r
+  VOID\r
+  )\r
+{\r
+  //\r
+  // Init the communicate buffer. The buffer data size is:\r
+  // SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE.\r
+  //\r
+  InitCommunicateBuffer (NULL, 0, SMM_VARIABLE_FUNCTION_SYNC_RUNTIME_CACHE);\r
+\r
+  //\r
+  // Send data to SMM.\r
+  //\r
+  SendCommunicateBuffer (0);\r
+}\r
+\r
+/**\r
+  Check whether a SMI must be triggered to retrieve pending cache updates.\r
+\r
+  If the variable HOB was finished being flushed since the last check for a runtime cache update, this function\r
+  will prevent the HOB cache from being used for future runtime cache hits.\r
+\r
+**/\r
+VOID\r
+CheckForRuntimeCacheSync (\r
+  VOID\r
+  )\r
+{\r
+  if (mVariableRuntimeCachePendingUpdate) {\r
+    SyncRuntimeCache ();\r
+  }\r
+  ASSERT (!mVariableRuntimeCachePendingUpdate);\r
+\r
+  //\r
+  // The HOB variable data may have finished being flushed in the runtime cache sync update\r
+  //\r
+  if (mHobFlushComplete && mVariableRuntimeHobCacheBuffer != NULL) {\r
+    if (!EfiAtRuntime ()) {\r
+      FreePages (mVariableRuntimeHobCacheBuffer, EFI_SIZE_TO_PAGES (mVariableRuntimeHobCacheBufferSize));\r
+    }\r
+    mVariableRuntimeHobCacheBuffer = NULL;\r
+  }\r
+}\r
+\r
+/**\r
+  Finds the given variable in a runtime cache variable store.\r
 \r
   Caution: This function may receive untrusted input.\r
   The data size is external input, so this function will validate it carefully to avoid buffer overflow.\r
@@ -455,20 +564,133 @@ Done:
                                      data, this value contains the required size.\r
   @param[out]     Data               Data pointer.\r
 \r
+  @retval EFI_SUCCESS                Found the specified variable.\r
   @retval EFI_INVALID_PARAMETER      Invalid parameter.\r
-  @retval EFI_SUCCESS                Find the specified variable.\r
-  @retval EFI_NOT_FOUND              Not found.\r
-  @retval EFI_BUFFER_TO_SMALL        DataSize is too small for the result.\r
+  @retval EFI_NOT_FOUND              The specified variable could not be found.\r
 \r
 **/\r
 EFI_STATUS\r
-EFIAPI\r
-RuntimeServiceGetVariable (\r
+FindVariableInRuntimeCache (\r
   IN      CHAR16                            *VariableName,\r
   IN      EFI_GUID                          *VendorGuid,\r
   OUT     UINT32                            *Attributes OPTIONAL,\r
   IN OUT  UINTN                             *DataSize,\r
-  OUT     VOID                              *Data\r
+  OUT     VOID                              *Data OPTIONAL\r
+  )\r
+{\r
+  EFI_STATUS              Status;\r
+  UINTN                   TempDataSize;\r
+  VARIABLE_POINTER_TRACK  RtPtrTrack;\r
+  VARIABLE_STORE_TYPE     StoreType;\r
+  VARIABLE_STORE_HEADER   *VariableStoreList[VariableStoreTypeMax];\r
+\r
+  Status = EFI_NOT_FOUND;\r
+\r
+  if (VariableName == NULL || VendorGuid == NULL || DataSize == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  ZeroMem (&RtPtrTrack, sizeof (RtPtrTrack));\r
+\r
+  //\r
+  // The UEFI specification restricts Runtime Services callers from invoking the same or certain other Runtime Service\r
+  // functions prior to completion and return from a previous Runtime Service call. These restrictions prevent\r
+  // a GetVariable () or GetNextVariable () call from being issued until a prior call has returned. The runtime\r
+  // cache read lock should always be free when entering this function.\r
+  //\r
+  ASSERT (!mVariableRuntimeCacheReadLock);\r
+\r
+  mVariableRuntimeCacheReadLock = TRUE;\r
+  CheckForRuntimeCacheSync ();\r
+\r
+  if (!mVariableRuntimeCachePendingUpdate) {\r
+    //\r
+    // 0: Volatile, 1: HOB, 2: Non-Volatile.\r
+    // The index and attributes mapping must be kept in this order as FindVariable\r
+    // makes use of this mapping to implement search algorithm.\r
+    //\r
+    VariableStoreList[VariableStoreTypeVolatile] = mVariableRuntimeVolatileCacheBuffer;\r
+    VariableStoreList[VariableStoreTypeHob]      = mVariableRuntimeHobCacheBuffer;\r
+    VariableStoreList[VariableStoreTypeNv]       = mVariableRuntimeNvCacheBuffer;\r
+\r
+    for (StoreType = (VARIABLE_STORE_TYPE) 0; StoreType < VariableStoreTypeMax; StoreType++) {\r
+      if (VariableStoreList[StoreType] == NULL) {\r
+        continue;\r
+      }\r
+\r
+      RtPtrTrack.StartPtr = GetStartPointer (VariableStoreList[StoreType]);\r
+      RtPtrTrack.EndPtr   = GetEndPointer   (VariableStoreList[StoreType]);\r
+      RtPtrTrack.Volatile = (BOOLEAN) (StoreType == VariableStoreTypeVolatile);\r
+\r
+      Status = FindVariableEx (VariableName, VendorGuid, FALSE, &RtPtrTrack, mVariableAuthFormat);\r
+      if (!EFI_ERROR (Status)) {\r
+        break;\r
+      }\r
+    }\r
+\r
+    if (!EFI_ERROR (Status)) {\r
+      //\r
+      // Get data size\r
+      //\r
+      TempDataSize = DataSizeOfVariable (RtPtrTrack.CurrPtr, mVariableAuthFormat);\r
+      ASSERT (TempDataSize != 0);\r
+\r
+      if (*DataSize >= TempDataSize) {\r
+        if (Data == NULL) {\r
+          Status = EFI_INVALID_PARAMETER;\r
+          goto Done;\r
+        }\r
+\r
+        CopyMem (Data, GetVariableDataPtr (RtPtrTrack.CurrPtr, mVariableAuthFormat), TempDataSize);\r
+        if (Attributes != NULL) {\r
+          *Attributes = RtPtrTrack.CurrPtr->Attributes;\r
+        }\r
+\r
+        *DataSize = TempDataSize;\r
+\r
+        UpdateVariableInfo (VariableName, VendorGuid, RtPtrTrack.Volatile, TRUE, FALSE, FALSE, TRUE, &mVariableInfo);\r
+\r
+        Status = EFI_SUCCESS;\r
+        goto Done;\r
+      } else {\r
+        *DataSize = TempDataSize;\r
+        Status = EFI_BUFFER_TOO_SMALL;\r
+        goto Done;\r
+      }\r
+    }\r
+  }\r
+\r
+Done:\r
+  mVariableRuntimeCacheReadLock = FALSE;\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Finds the given variable in a variable store in SMM.\r
+\r
+  Caution: This function may receive untrusted input.\r
+  The data size is external input, so this function will validate it carefully to avoid buffer overflow.\r
+\r
+  @param[in]      VariableName       Name of Variable to be found.\r
+  @param[in]      VendorGuid         Variable vendor GUID.\r
+  @param[out]     Attributes         Attribute value of the variable found.\r
+  @param[in, out] DataSize           Size of Data found. If size is less than the\r
+                                     data, this value contains the required size.\r
+  @param[out]     Data               Data pointer.\r
+\r
+  @retval EFI_SUCCESS                Found the specified variable.\r
+  @retval EFI_INVALID_PARAMETER      Invalid parameter.\r
+  @retval EFI_NOT_FOUND              The specified variable could not be found.\r
+\r
+**/\r
+EFI_STATUS\r
+FindVariableInSmm (\r
+  IN      CHAR16                            *VariableName,\r
+  IN      EFI_GUID                          *VendorGuid,\r
+  OUT     UINT32                            *Attributes OPTIONAL,\r
+  IN OUT  UINTN                             *DataSize,\r
+  OUT     VOID                              *Data OPTIONAL\r
   )\r
 {\r
   EFI_STATUS                                Status;\r
@@ -492,8 +714,6 @@ RuntimeServiceGetVariable (
     return EFI_INVALID_PARAMETER;\r
   }\r
 \r
-  AcquireLockOnlyAtBootTime(&mVariableServicesLock);\r
-\r
   //\r
   // Init the communicate buffer. The buffer data size is:\r
   // SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + PayloadSize.\r
@@ -506,7 +726,7 @@ RuntimeServiceGetVariable (
   }\r
   PayloadSize = OFFSET_OF (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name) + VariableNameSize + TempDataSize;\r
 \r
-  Status = InitCommunicateBuffer ((VOID **)&SmmVariableHeader, PayloadSize, SMM_VARIABLE_FUNCTION_GET_VARIABLE);\r
+  Status = InitCommunicateBuffer ((VOID **) &SmmVariableHeader, PayloadSize, SMM_VARIABLE_FUNCTION_GET_VARIABLE);\r
   if (EFI_ERROR (Status)) {\r
     goto Done;\r
   }\r
@@ -552,13 +772,134 @@ RuntimeServiceGetVariable (
   }\r
 \r
 Done:\r
+  return Status;\r
+}\r
+\r
+/**\r
+  This code finds variable in storage blocks (Volatile or Non-Volatile).\r
+\r
+  Caution: This function may receive untrusted input.\r
+  The data size is external input, so this function will validate it carefully to avoid buffer overflow.\r
+\r
+  @param[in]      VariableName       Name of Variable to be found.\r
+  @param[in]      VendorGuid         Variable vendor GUID.\r
+  @param[out]     Attributes         Attribute value of the variable found.\r
+  @param[in, out] DataSize           Size of Data found. If size is less than the\r
+                                     data, this value contains the required size.\r
+  @param[out]     Data               Data pointer.\r
+\r
+  @retval EFI_INVALID_PARAMETER      Invalid parameter.\r
+  @retval EFI_SUCCESS                Find the specified variable.\r
+  @retval EFI_NOT_FOUND              Not found.\r
+  @retval EFI_BUFFER_TO_SMALL        DataSize is too small for the result.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+RuntimeServiceGetVariable (\r
+  IN      CHAR16                            *VariableName,\r
+  IN      EFI_GUID                          *VendorGuid,\r
+  OUT     UINT32                            *Attributes OPTIONAL,\r
+  IN OUT  UINTN                             *DataSize,\r
+  OUT     VOID                              *Data\r
+  )\r
+{\r
+  EFI_STATUS                                Status;\r
+\r
+  if (VariableName == NULL || VendorGuid == NULL || DataSize == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+  if (VariableName[0] == 0) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  AcquireLockOnlyAtBootTime (&mVariableServicesLock);\r
+  if (FeaturePcdGet (PcdEnableVariableRuntimeCache)) {\r
+    Status = FindVariableInRuntimeCache (VariableName, VendorGuid, Attributes, DataSize, Data);\r
+  } else {\r
+    Status = FindVariableInSmm (VariableName, VendorGuid, Attributes, DataSize, Data);\r
+  }\r
   ReleaseLockOnlyAtBootTime (&mVariableServicesLock);\r
+\r
   return Status;\r
 }\r
 \r
+/**\r
+  Finds the next available variable in a runtime cache variable store.\r
+\r
+  @param[in, out] VariableNameSize   Size of the variable name.\r
+  @param[in, out] VariableName       Pointer to variable name.\r
+  @param[in, out] VendorGuid         Variable Vendor Guid.\r
+\r
+  @retval EFI_INVALID_PARAMETER      Invalid parameter.\r
+  @retval EFI_SUCCESS                Find the specified variable.\r
+  @retval EFI_NOT_FOUND              Not found.\r
+  @retval EFI_BUFFER_TO_SMALL        DataSize is too small for the result.\r
+\r
+**/\r
+EFI_STATUS\r
+GetNextVariableNameInRuntimeCache (\r
+  IN OUT  UINTN                             *VariableNameSize,\r
+  IN OUT  CHAR16                            *VariableName,\r
+  IN OUT  EFI_GUID                          *VendorGuid\r
+  )\r
+{\r
+  EFI_STATUS              Status;\r
+  UINTN                   VarNameSize;\r
+  VARIABLE_HEADER         *VariablePtr;\r
+  VARIABLE_STORE_HEADER   *VariableStoreHeader[VariableStoreTypeMax];\r
+\r
+  Status = EFI_NOT_FOUND;\r
+\r
+  //\r
+  // The UEFI specification restricts Runtime Services callers from invoking the same or certain other Runtime Service\r
+  // functions prior to completion and return from a previous Runtime Service call. These restrictions prevent\r
+  // a GetVariable () or GetNextVariable () call from being issued until a prior call has returned. The runtime\r
+  // cache read lock should always be free when entering this function.\r
+  //\r
+  ASSERT (!mVariableRuntimeCacheReadLock);\r
+\r
+  CheckForRuntimeCacheSync ();\r
+\r
+  mVariableRuntimeCacheReadLock = TRUE;\r
+  if (!mVariableRuntimeCachePendingUpdate) {\r
+    //\r
+    // 0: Volatile, 1: HOB, 2: Non-Volatile.\r
+    // The index and attributes mapping must be kept in this order as FindVariable\r
+    // makes use of this mapping to implement search algorithm.\r
+    //\r
+    VariableStoreHeader[VariableStoreTypeVolatile] = mVariableRuntimeVolatileCacheBuffer;\r
+    VariableStoreHeader[VariableStoreTypeHob]      = mVariableRuntimeHobCacheBuffer;\r
+    VariableStoreHeader[VariableStoreTypeNv]       = mVariableRuntimeNvCacheBuffer;\r
+\r
+    Status =  VariableServiceGetNextVariableInternal (\r
+                VariableName,\r
+                VendorGuid,\r
+                VariableStoreHeader,\r
+                &VariablePtr,\r
+                mVariableAuthFormat\r
+                );\r
+    if (!EFI_ERROR (Status)) {\r
+      VarNameSize = NameSizeOfVariable (VariablePtr, mVariableAuthFormat);\r
+      ASSERT (VarNameSize != 0);\r
+      if (VarNameSize <= *VariableNameSize) {\r
+        CopyMem (VariableName, GetVariableNamePtr (VariablePtr, mVariableAuthFormat), VarNameSize);\r
+        CopyMem (VendorGuid, GetVendorGuidPtr (VariablePtr, mVariableAuthFormat), sizeof (EFI_GUID));\r
+        Status = EFI_SUCCESS;\r
+      } else {\r
+        Status = EFI_BUFFER_TOO_SMALL;\r
+      }\r
+\r
+      *VariableNameSize = VarNameSize;\r
+    }\r
+  }\r
+  mVariableRuntimeCacheReadLock = FALSE;\r
+\r
+  return Status;\r
+}\r
 \r
 /**\r
-  This code Finds the Next available variable.\r
+  Finds the next available variable in a SMM variable store.\r
 \r
   @param[in, out] VariableNameSize   Size of the variable name.\r
   @param[in, out] VariableName       Pointer to variable name.\r
@@ -571,8 +912,7 @@ Done:
 \r
 **/\r
 EFI_STATUS\r
-EFIAPI\r
-RuntimeServiceGetNextVariableName (\r
+GetNextVariableNameInSmm (\r
   IN OUT  UINTN                             *VariableNameSize,\r
   IN OUT  CHAR16                            *VariableName,\r
   IN OUT  EFI_GUID                          *VendorGuid\r
@@ -584,10 +924,6 @@ RuntimeServiceGetNextVariableName (
   UINTN                                           OutVariableNameSize;\r
   UINTN                                           InVariableNameSize;\r
 \r
-  if (VariableNameSize == NULL || VariableName == NULL || VendorGuid == NULL) {\r
-    return EFI_INVALID_PARAMETER;\r
-  }\r
-\r
   OutVariableNameSize   = *VariableNameSize;\r
   InVariableNameSize    = StrSize (VariableName);\r
   SmmGetNextVariableName = NULL;\r
@@ -599,8 +935,6 @@ RuntimeServiceGetNextVariableName (
     return EFI_INVALID_PARAMETER;\r
   }\r
 \r
-  AcquireLockOnlyAtBootTime(&mVariableServicesLock);\r
-\r
   //\r
   // Init the communicate buffer. The buffer data size is:\r
   // SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + PayloadSize.\r
@@ -659,7 +993,59 @@ RuntimeServiceGetNextVariableName (
   CopyMem (VariableName, SmmGetNextVariableName->Name, SmmGetNextVariableName->NameSize);\r
 \r
 Done:\r
+  return Status;\r
+}\r
+\r
+/**\r
+  This code Finds the Next available variable.\r
+\r
+  @param[in, out] VariableNameSize   Size of the variable name.\r
+  @param[in, out] VariableName       Pointer to variable name.\r
+  @param[in, out] VendorGuid         Variable Vendor Guid.\r
+\r
+  @retval EFI_INVALID_PARAMETER      Invalid parameter.\r
+  @retval EFI_SUCCESS                Find the specified variable.\r
+  @retval EFI_NOT_FOUND              Not found.\r
+  @retval EFI_BUFFER_TO_SMALL        DataSize is too small for the result.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+RuntimeServiceGetNextVariableName (\r
+  IN OUT  UINTN                             *VariableNameSize,\r
+  IN OUT  CHAR16                            *VariableName,\r
+  IN OUT  EFI_GUID                          *VendorGuid\r
+  )\r
+{\r
+  EFI_STATUS              Status;\r
+  UINTN                   MaxLen;\r
+\r
+  Status = EFI_NOT_FOUND;\r
+\r
+  if (VariableNameSize == NULL || VariableName == NULL || VendorGuid == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // Calculate the possible maximum length of name string, including the Null terminator.\r
+  //\r
+  MaxLen = *VariableNameSize / sizeof (CHAR16);\r
+  if ((MaxLen == 0) || (StrnLenS (VariableName, MaxLen) == MaxLen)) {\r
+    //\r
+    // Null-terminator is not found in the first VariableNameSize bytes of the input VariableName buffer,\r
+    // follow spec to return EFI_INVALID_PARAMETER.\r
+    //\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  AcquireLockOnlyAtBootTime (&mVariableServicesLock);\r
+  if (FeaturePcdGet (PcdEnableVariableRuntimeCache)) {\r
+    Status = GetNextVariableNameInRuntimeCache (VariableNameSize, VariableName, VendorGuid);\r
+  } else {\r
+    Status = GetNextVariableNameInSmm (VariableNameSize, VariableName, VendorGuid);\r
+  }\r
   ReleaseLockOnlyAtBootTime (&mVariableServicesLock);\r
+\r
   return Status;\r
 }\r
 \r
@@ -888,6 +1274,17 @@ OnReadyToBoot (
   //\r
   SendCommunicateBuffer (0);\r
 \r
+  //\r
+  // Install the system configuration table for variable info data captured\r
+  //\r
+  if (FeaturePcdGet (PcdEnableVariableRuntimeCache) && FeaturePcdGet (PcdVariableCollectStatistics)) {\r
+    if (mVariableAuthFormat) {\r
+      gBS->InstallConfigurationTable (&gEfiAuthenticatedVariableGuid, mVariableInfo);\r
+    } else {\r
+      gBS->InstallConfigurationTable (&gEfiVariableGuid, mVariableInfo);\r
+    }\r
+  }\r
+\r
   gBS->CloseEvent (Event);\r
 }\r
 \r
@@ -911,6 +1308,9 @@ VariableAddressChangeEvent (
 {\r
   EfiConvertPointer (0x0, (VOID **) &mVariableBuffer);\r
   EfiConvertPointer (0x0, (VOID **) &mSmmCommunication);\r
+  EfiConvertPointer (EFI_OPTIONAL_PTR, (VOID **) &mVariableRuntimeHobCacheBuffer);\r
+  EfiConvertPointer (EFI_OPTIONAL_PTR, (VOID **) &mVariableRuntimeNvCacheBuffer);\r
+  EfiConvertPointer (EFI_OPTIONAL_PTR, (VOID **) &mVariableRuntimeVolatileCacheBuffer);\r
 }\r
 \r
 /**\r
@@ -987,6 +1387,159 @@ Done:
   return Status;\r
 }\r
 \r
+/**\r
+  This code gets information needed from SMM for runtime cache initialization.\r
+\r
+  @param[out] TotalHobStorageSize         Output pointer for the total HOB storage size in bytes.\r
+  @param[out] TotalNvStorageSize          Output pointer for the total non-volatile storage size in bytes.\r
+  @param[out] TotalVolatileStorageSize    Output pointer for the total volatile storage size in bytes.\r
+  @param[out] AuthenticatedVariableUsage  Output pointer that indicates if authenticated variables are to be used.\r
+\r
+  @retval EFI_SUCCESS                     Retrieved the size successfully.\r
+  @retval EFI_INVALID_PARAMETER           TotalNvStorageSize parameter is NULL.\r
+  @retval EFI_OUT_OF_RESOURCES            The memory resources needed for a CommBuffer are not available.\r
+  @retval Others                          Could not retrieve the size successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+GetRuntimeCacheInfo (\r
+  OUT UINTN                         *TotalHobStorageSize,\r
+  OUT UINTN                         *TotalNvStorageSize,\r
+  OUT UINTN                         *TotalVolatileStorageSize,\r
+  OUT BOOLEAN                       *AuthenticatedVariableUsage\r
+  )\r
+{\r
+  EFI_STATUS                                          Status;\r
+  SMM_VARIABLE_COMMUNICATE_GET_RUNTIME_CACHE_INFO     *SmmGetRuntimeCacheInfo;\r
+  EFI_SMM_COMMUNICATE_HEADER                          *SmmCommunicateHeader;\r
+  SMM_VARIABLE_COMMUNICATE_HEADER                     *SmmVariableFunctionHeader;\r
+  UINTN                                               CommSize;\r
+  UINT8                                               *CommBuffer;\r
+\r
+  SmmGetRuntimeCacheInfo = NULL;\r
+  CommBuffer = mVariableBuffer;\r
+\r
+  if (TotalHobStorageSize == NULL || TotalNvStorageSize == NULL || TotalVolatileStorageSize == NULL || AuthenticatedVariableUsage == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (CommBuffer == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  AcquireLockOnlyAtBootTime (&mVariableServicesLock);\r
+\r
+  CommSize = SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + sizeof (SMM_VARIABLE_COMMUNICATE_GET_RUNTIME_CACHE_INFO);\r
+  ZeroMem (CommBuffer, CommSize);\r
+\r
+  SmmCommunicateHeader = (EFI_SMM_COMMUNICATE_HEADER *) CommBuffer;\r
+  CopyGuid (&SmmCommunicateHeader->HeaderGuid, &gEfiSmmVariableProtocolGuid);\r
+  SmmCommunicateHeader->MessageLength = SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + sizeof (SMM_VARIABLE_COMMUNICATE_GET_RUNTIME_CACHE_INFO);\r
+\r
+  SmmVariableFunctionHeader = (SMM_VARIABLE_COMMUNICATE_HEADER *) SmmCommunicateHeader->Data;\r
+  SmmVariableFunctionHeader->Function = SMM_VARIABLE_FUNCTION_GET_RUNTIME_CACHE_INFO;\r
+  SmmGetRuntimeCacheInfo = (SMM_VARIABLE_COMMUNICATE_GET_RUNTIME_CACHE_INFO *) SmmVariableFunctionHeader->Data;\r
+\r
+  //\r
+  // Send data to SMM.\r
+  //\r
+  Status = mSmmCommunication->Communicate (mSmmCommunication, CommBuffer, &CommSize);\r
+  ASSERT_EFI_ERROR (Status);\r
+  if (CommSize <= SMM_VARIABLE_COMMUNICATE_HEADER_SIZE) {\r
+    Status = EFI_BAD_BUFFER_SIZE;\r
+    goto Done;\r
+  }\r
+\r
+  Status = SmmVariableFunctionHeader->ReturnStatus;\r
+  if (EFI_ERROR (Status)) {\r
+    goto Done;\r
+  }\r
+\r
+  //\r
+  // Get data from SMM.\r
+  //\r
+  *TotalHobStorageSize = SmmGetRuntimeCacheInfo->TotalHobStorageSize;\r
+  *TotalNvStorageSize = SmmGetRuntimeCacheInfo->TotalNvStorageSize;\r
+  *TotalVolatileStorageSize = SmmGetRuntimeCacheInfo->TotalVolatileStorageSize;\r
+  *AuthenticatedVariableUsage = SmmGetRuntimeCacheInfo->AuthenticatedVariableUsage;\r
+\r
+Done:\r
+  ReleaseLockOnlyAtBootTime (&mVariableServicesLock);\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Sends the runtime variable cache context information to SMM.\r
+\r
+  @retval EFI_SUCCESS               Retrieved the size successfully.\r
+  @retval EFI_INVALID_PARAMETER     TotalNvStorageSize parameter is NULL.\r
+  @retval EFI_OUT_OF_RESOURCES      The memory resources needed for a CommBuffer are not available.\r
+  @retval Others                    Could not retrieve the size successfully.;\r
+\r
+**/\r
+EFI_STATUS\r
+SendRuntimeVariableCacheContextToSmm (\r
+  VOID\r
+  )\r
+{\r
+  EFI_STATUS                                                Status;\r
+  SMM_VARIABLE_COMMUNICATE_RUNTIME_VARIABLE_CACHE_CONTEXT   *SmmRuntimeVarCacheContext;\r
+  EFI_SMM_COMMUNICATE_HEADER                                *SmmCommunicateHeader;\r
+  SMM_VARIABLE_COMMUNICATE_HEADER                           *SmmVariableFunctionHeader;\r
+  UINTN                                                     CommSize;\r
+  UINT8                                                     *CommBuffer;\r
+\r
+  SmmRuntimeVarCacheContext = NULL;\r
+  CommBuffer = mVariableBuffer;\r
+\r
+  if (CommBuffer == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  AcquireLockOnlyAtBootTime (&mVariableServicesLock);\r
+\r
+  //\r
+  // Init the communicate buffer. The buffer data size is:\r
+  // SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + sizeof (SMM_VARIABLE_COMMUNICATE_RUNTIME_VARIABLE_CACHE_CONTEXT);\r
+  //\r
+  CommSize = SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + sizeof (SMM_VARIABLE_COMMUNICATE_RUNTIME_VARIABLE_CACHE_CONTEXT);\r
+  ZeroMem (CommBuffer, CommSize);\r
+\r
+  SmmCommunicateHeader = (EFI_SMM_COMMUNICATE_HEADER *) CommBuffer;\r
+  CopyGuid (&SmmCommunicateHeader->HeaderGuid, &gEfiSmmVariableProtocolGuid);\r
+  SmmCommunicateHeader->MessageLength = SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + sizeof (SMM_VARIABLE_COMMUNICATE_RUNTIME_VARIABLE_CACHE_CONTEXT);\r
+\r
+  SmmVariableFunctionHeader = (SMM_VARIABLE_COMMUNICATE_HEADER *) SmmCommunicateHeader->Data;\r
+  SmmVariableFunctionHeader->Function = SMM_VARIABLE_FUNCTION_INIT_RUNTIME_VARIABLE_CACHE_CONTEXT;\r
+  SmmRuntimeVarCacheContext = (SMM_VARIABLE_COMMUNICATE_RUNTIME_VARIABLE_CACHE_CONTEXT *) SmmVariableFunctionHeader->Data;\r
+\r
+  SmmRuntimeVarCacheContext->RuntimeHobCache = mVariableRuntimeHobCacheBuffer;\r
+  SmmRuntimeVarCacheContext->RuntimeVolatileCache = mVariableRuntimeVolatileCacheBuffer;\r
+  SmmRuntimeVarCacheContext->RuntimeNvCache = mVariableRuntimeNvCacheBuffer;\r
+  SmmRuntimeVarCacheContext->PendingUpdate = &mVariableRuntimeCachePendingUpdate;\r
+  SmmRuntimeVarCacheContext->ReadLock = &mVariableRuntimeCacheReadLock;\r
+  SmmRuntimeVarCacheContext->HobFlushComplete = &mHobFlushComplete;\r
+\r
+  //\r
+  // Send data to SMM.\r
+  //\r
+  Status = mSmmCommunication->Communicate (mSmmCommunication, CommBuffer, &CommSize);\r
+  ASSERT_EFI_ERROR (Status);\r
+  if (CommSize <= SMM_VARIABLE_COMMUNICATE_HEADER_SIZE) {\r
+    Status = EFI_BAD_BUFFER_SIZE;\r
+    goto Done;\r
+  }\r
+\r
+  Status = SmmVariableFunctionHeader->ReturnStatus;\r
+  if (EFI_ERROR (Status)) {\r
+    goto Done;\r
+  }\r
+\r
+Done:\r
+  ReleaseLockOnlyAtBootTime (&mVariableServicesLock);\r
+  return Status;\r
+}\r
+\r
 /**\r
   Initialize variable service and install Variable Architectural protocol.\r
 \r
@@ -1003,7 +1556,7 @@ SmmVariableReady (
 {\r
   EFI_STATUS                                Status;\r
 \r
-  Status = gBS->LocateProtocol (&gEfiSmmVariableProtocolGuid, NULL, (VOID **)&mSmmVariable);\r
+  Status = gBS->LocateProtocol (&gEfiSmmVariableProtocolGuid, NULL, (VOID **) &mSmmVariable);\r
   if (EFI_ERROR (Status)) {\r
     return;\r
   }\r
@@ -1025,6 +1578,42 @@ SmmVariableReady (
   //\r
   mVariableBufferPhysical = mVariableBuffer;\r
 \r
+  if (FeaturePcdGet (PcdEnableVariableRuntimeCache)) {\r
+    DEBUG ((DEBUG_INFO, "Variable driver runtime cache is enabled.\n"));\r
+    //\r
+    // Allocate runtime variable cache memory buffers.\r
+    //\r
+    Status =  GetRuntimeCacheInfo (\r
+                &mVariableRuntimeHobCacheBufferSize,\r
+                &mVariableRuntimeNvCacheBufferSize,\r
+                &mVariableRuntimeVolatileCacheBufferSize,\r
+                &mVariableAuthFormat\r
+                );\r
+    if (!EFI_ERROR (Status)) {\r
+      Status = InitVariableCache (&mVariableRuntimeHobCacheBuffer, &mVariableRuntimeHobCacheBufferSize);\r
+      if (!EFI_ERROR (Status)) {\r
+        Status = InitVariableCache (&mVariableRuntimeNvCacheBuffer, &mVariableRuntimeNvCacheBufferSize);\r
+        if (!EFI_ERROR (Status)) {\r
+          Status = InitVariableCache (&mVariableRuntimeVolatileCacheBuffer, &mVariableRuntimeVolatileCacheBufferSize);\r
+          if (!EFI_ERROR (Status)) {\r
+            Status = SendRuntimeVariableCacheContextToSmm ();\r
+            if (!EFI_ERROR (Status)) {\r
+              SyncRuntimeCache ();\r
+            }\r
+          }\r
+        }\r
+      }\r
+      if (EFI_ERROR (Status)) {\r
+        mVariableRuntimeHobCacheBuffer = NULL;\r
+        mVariableRuntimeNvCacheBuffer = NULL;\r
+        mVariableRuntimeVolatileCacheBuffer = NULL;\r
+      }\r
+    }\r
+    ASSERT_EFI_ERROR (Status);\r
+  } else {\r
+    DEBUG ((DEBUG_INFO, "Variable driver runtime cache is disabled.\n"));\r
+  }\r
+\r
   gRT->GetVariable         = RuntimeServiceGetVariable;\r
   gRT->GetNextVariableName = RuntimeServiceGetNextVariableName;\r
   gRT->SetVariable         = RuntimeServiceSetVariable;\r