]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.c
Add Profiling support for Variable store and added a specialized caching algorithm
[mirror_edk2.git] / MdeModulePkg / Universal / Variable / RuntimeDxe / Variable.c
index 784a1987a8c3991fcf2702571e170b73a6a4ad45..8627c7dd9da6f32351cf8f750271b351a9fe45ad 100644 (file)
@@ -21,19 +21,24 @@ Revision History
 \r
 \r
 #include "Variable.h"\r
-#include <Common/FlashMap.h>\r
+#include <Guid/FlashMapHob.h>\r
+#include <Guid/VariableInfo.h>\r
+#include <Guid/GlobalVariable.h>\r
+\r
+\r
+\r
 \r
 //\r
 // Don't use module globals after the SetVirtualAddress map is signaled\r
 //\r
 ESAL_VARIABLE_GLOBAL  *mVariableModuleGlobal;\r
 \r
+\r
 //\r
 // This is a temperary function which will be removed\r
 // when EfiAcquireLock in UefiLib can handle the\r
 // the call in UEFI Runtimer driver in RT phase.\r
 //\r
-STATIC\r
 VOID\r
 AcquireLockOnlyAtBootTime (\r
   IN EFI_LOCK  *Lock\r
@@ -49,7 +54,6 @@ AcquireLockOnlyAtBootTime (
 // when EfiAcquireLock in UefiLib can handle the\r
 // the call in UEFI Runtimer driver in RT phase.\r
 //\r
-STATIC\r
 VOID\r
 ReleaseLockOnlyAtBootTime (\r
   IN EFI_LOCK  *Lock\r
@@ -60,6 +64,76 @@ ReleaseLockOnlyAtBootTime (
   }\r
 }\r
 \r
+\r
+GLOBAL_REMOVE_IF_UNREFERENCED VARIABLE_INFO_ENTRY *gVariableInfo = NULL;\r
+\r
+\r
+VOID\r
+UpdateVariableInfo (\r
+  IN  CHAR16                  *VariableName,\r
+  IN  EFI_GUID                *VendorGuid,\r
+  IN  BOOLEAN                 Volatile,\r
+  IN  BOOLEAN                 Read,\r
+  IN  BOOLEAN                 Write,\r
+  IN  BOOLEAN                 Delete,\r
+  IN  BOOLEAN                 Cache\r
+  )\r
+{\r
+  VARIABLE_INFO_ENTRY   *Entry;\r
+\r
+  if (FeaturePcdGet (PcdVariableCollectStatistics)) {\r
+\r
+    if (EfiAtRuntime ()) {\r
+      // Don't collect statistics at runtime\r
+      return;\r
+    }\r
+\r
+    if (gVariableInfo == NULL) {\r
+      gVariableInfo = AllocateZeroPool (sizeof (VARIABLE_INFO_ENTRY));\r
+      CopyGuid (&gVariableInfo->VendorGuid, VendorGuid);\r
+      gVariableInfo->Name = AllocatePool (StrLen (VariableName));\r
+      StrCpy (gVariableInfo->Name, VariableName);\r
+      gVariableInfo->Volatile = Volatile;\r
+\r
+      gBS->InstallConfigurationTable (&gEfiVariableInfoGuid, gVariableInfo);\r
+    }\r
+\r
+    \r
+    for (Entry = gVariableInfo; Entry != NULL; Entry = Entry->Next) {\r
+      if (CompareGuid (VendorGuid, &Entry->VendorGuid)) {\r
+        if (StrCmp (VariableName, Entry->Name) == 0) {\r
+          if (Read) {\r
+            Entry->ReadCount++;\r
+          }\r
+          if (Write) {\r
+            Entry->WriteCount++;\r
+          }\r
+          if (Delete) {\r
+            Entry->DeleteCount++;\r
+          }\r
+          if (Cache) {\r
+            Entry->CacheCount++;\r
+          }\r
+\r
+          return;\r
+        }\r
+      }\r
+\r
+      if (Entry->Next == NULL) {\r
+        Entry->Next = AllocateZeroPool (sizeof (VARIABLE_INFO_ENTRY));\r
+\r
+        CopyGuid (&Entry->Next->VendorGuid, VendorGuid);\r
+        Entry->Next->Name = AllocatePool (StrLen (VariableName));\r
+        StrCpy (Entry->Next->Name, VariableName);\r
+        Entry->Next->Volatile = Volatile;\r
+      }\r
+\r
+    }\r
+  }\r
+}\r
+\r
+\r
+\r
 STATIC\r
 BOOLEAN\r
 EFIAPI\r
@@ -112,19 +186,20 @@ Routine Description:
 \r
 Arguments:\r
 \r
-  Global            Pointer to VARAIBLE_GLOBAL structure\r
-  Volatile          If the Variable is Volatile or Non-Volatile\r
-  SetByIndex        TRUE: Target pointer is given as index\r
-                    FALSE: Target pointer is absolute\r
-  Instance          Instance of FV Block services\r
-  DataPtrIndex      Pointer to the Data from the end of VARIABLE_STORE_HEADER\r
-                    structure\r
-  DataSize          Size of data to be written.\r
-  Buffer            Pointer to the buffer from which data is written\r
+  Global            Pointer to VARAIBLE_GLOBAL structure\r
+  Volatile          If the Variable is Volatile or Non-Volatile\r
+  SetByIndex        TRUE: Target pointer is given as index\r
+                      FALSE: Target pointer is absolute\r
+  Instance          Instance of FV Block services\r
+  DataPtrIndex      Pointer to the Data from the end of VARIABLE_STORE_HEADER\r
+                      structure\r
+  DataSize          Size of data to be written.\r
+  Buffer            Pointer to the buffer from which data is written\r
 \r
 Returns:\r
 \r
-  EFI STATUS\r
+  EFI_INVALID_PARAMETER   - Parameters not valid\r
+  EFI_SUCCESS             - Variable store successfully updated\r
 \r
 --*/\r
 {\r
@@ -175,14 +250,14 @@ Returns:
     if ((DataPtr + DataSize) >= ((UINTN) ((UINT8 *) VolatileBase + VolatileBase->Size))) {\r
       return EFI_INVALID_PARAMETER;\r
     }\r
-  }\r
-  //\r
-  // If Volatile Variable just do a simple mem copy.\r
-  //\r
-  if (Volatile) {\r
-    CopyMem ((UINT8 *) ((UINTN) DataPtr), Buffer, DataSize);\r
+    \r
+    //\r
+    // If Volatile Variable just do a simple mem copy.\r
+    //    \r
+    CopyMem ((UINT8 *)(UINTN)DataPtr, Buffer, DataSize);\r
     return EFI_SUCCESS;\r
   }\r
+  \r
   //\r
   // If we are here we are dealing with Non-Volatile Variables\r
   //\r
@@ -211,9 +286,7 @@ Returns:
                     &CurrWriteSize,\r
                     CurrBuffer\r
                     );\r
-          if (EFI_ERROR (Status)) {\r
             return Status;\r
-          }\r
         } else {\r
           Size = (UINT32) (LinearOffset + PtrBlockMapEntry->Length - CurrWritePtr);\r
           Status = EfiFvbWriteBlock (\r
@@ -313,7 +386,6 @@ Returns:
 \r
 STATIC\r
 VARIABLE_HEADER *\r
-EFIAPI\r
 GetNextVariablePtr (\r
   IN  VARIABLE_HEADER   *Variable\r
   )\r
@@ -487,9 +559,111 @@ Returns:
   return Status;\r
 }\r
 \r
-STATIC\r
+typedef struct {\r
+  EFI_GUID    *Guid;\r
+  CHAR16      *Name;\r
+  UINT32      Attributes;\r
+  UINTN       DataSize;\r
+  VOID        *Data;\r
+} VARIABLE_CACHE_ENTRY;\r
+\r
+//\r
+// The current Hii implementation accesses this variable a larg # of times on every boot.\r
+// Other common variables are only accessed a single time. This is why this cache algorithm\r
+// only targets a single variable. Probably to get an performance improvement out of\r
+// a Cache you would need a cache that improves the search performance for a variable.\r
+//\r
+VARIABLE_CACHE_ENTRY mVariableCache[] = {\r
+  {\r
+    &gEfiGlobalVariableGuid,\r
+    L"Lang",\r
+    0x00000000,\r
+    0x00,\r
+    NULL\r
+  }\r
+};\r
+\r
+VOID\r
+UpdateVariableCache (\r
+  IN      CHAR16            *VariableName,\r
+  IN      EFI_GUID          *VendorGuid,\r
+  OUT     UINT32            Attributes,\r
+  IN OUT  UINTN             DataSize,\r
+  OUT     VOID              *Data\r
+  )\r
+{\r
+  VARIABLE_CACHE_ENTRY      *Entry;\r
+  UINTN                     Index;\r
+\r
+  if (EfiAtRuntime ()) {\r
+    // Don't use the cache at runtime\r
+    return;\r
+  }\r
+\r
+  for (Index = 0, Entry = mVariableCache; Index < sizeof (mVariableCache)/sizeof (VARIABLE_CACHE_ENTRY); Index++, Entry++) {\r
+    if (CompareGuid (VendorGuid, Entry->Guid)) {\r
+      if (StrCmp (VariableName, Entry->Name) == 0) { \r
+        Entry->Attributes = Attributes;\r
+        if (DataSize == 0) {\r
+          // Delete Case\r
+          if (Entry->DataSize != 0) {\r
+            FreePool (Entry->Data);\r
+          }\r
+          Entry->DataSize = DataSize;\r
+        } else if (DataSize == Entry->DataSize) {\r
+          CopyMem (Entry->Data, Data, DataSize);\r
+        } else {\r
+          Entry->Data = AllocatePool (DataSize);\r
+          Entry->DataSize = DataSize;\r
+          CopyMem (Entry->Data, Data, DataSize);\r
+        }\r
+      }\r
+    }\r
+  }\r
+}\r
+\r
+\r
+EFI_STATUS\r
+FindVariableInCache (\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
+  VARIABLE_CACHE_ENTRY      *Entry;\r
+  UINTN                     Index;\r
+\r
+  if (EfiAtRuntime ()) {\r
+    // Don't use the cache at runtime\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  for (Index = 0, Entry = mVariableCache; Index < sizeof (mVariableCache)/sizeof (VARIABLE_CACHE_ENTRY); Index++, Entry++) {\r
+    if (CompareGuid (VendorGuid, Entry->Guid)) {\r
+      if (StrCmp (VariableName, Entry->Name) == 0) {\r
+        if (Entry->DataSize == 0) {\r
+          return EFI_NOT_FOUND;\r
+        } else if (Entry->DataSize != *DataSize) {\r
+          *DataSize = Entry->DataSize;\r
+          return EFI_BUFFER_TOO_SMALL;\r
+        } else {\r
+          CopyMem (Data, Entry->Data, Entry->DataSize);\r
+          if (Attributes != NULL) {\r
+            *Attributes = Entry->Attributes;\r
+          }\r
+          return EFI_SUCCESS;\r
+        }\r
+      }\r
+    }\r
+  }\r
+  \r
+  return EFI_NOT_FOUND;\r
+}\r
+\r
+\r
 EFI_STATUS\r
-EFIAPI\r
 FindVariable (\r
   IN  CHAR16                  *VariableName,\r
   IN  EFI_GUID                *VendorGuid,\r
@@ -529,10 +703,10 @@ Returns:
   AcquireLockOnlyAtBootTime(&Global->VariableServicesLock);\r
 \r
   //\r
-  // 0: Non-Volatile, 1: Volatile\r
+  // 0: Volatile, 1: Non-Volatile\r
   //\r
-  VariableStoreHeader[0]  = (VARIABLE_STORE_HEADER *) ((UINTN) Global->NonVolatileVariableBase);\r
-  VariableStoreHeader[1]  = (VARIABLE_STORE_HEADER *) ((UINTN) Global->VolatileVariableBase);\r
+  VariableStoreHeader[0]  = (VARIABLE_STORE_HEADER *) ((UINTN) Global->VolatileVariableBase);\r
+  VariableStoreHeader[1]  = (VARIABLE_STORE_HEADER *) ((UINTN) Global->NonVolatileVariableBase);\r
 \r
   //\r
   // Start Pointers for the variable.\r
@@ -545,7 +719,7 @@ Returns:
     return EFI_INVALID_PARAMETER;\r
   }\r
   //\r
-  // Find the variable by walk through non-volatile and volatile variable store\r
+  // Find the variable by walk through volatile and then non-volatile variable store\r
   //\r
   for (Index = 0; Index < 2; Index++) {\r
     PtrTrack->StartPtr  = (VARIABLE_HEADER *) (VariableStoreHeader[Index] + 1);\r
@@ -553,16 +727,16 @@ Returns:
 \r
     while (IsValidVariableHeader (Variable[Index]) && (Variable[Index] <= GetEndPointer (VariableStoreHeader[Index]))) {\r
       if (Variable[Index]->State == VAR_ADDED) {\r
-        if (!(EfiAtRuntime () && !(Variable[Index]->Attributes & EFI_VARIABLE_RUNTIME_ACCESS))) {\r
+        if (!EfiAtRuntime () || (Variable[Index]->Attributes & EFI_VARIABLE_RUNTIME_ACCESS)) {\r
           if (VariableName[0] == 0) {\r
             PtrTrack->CurrPtr   = Variable[Index];\r
-            PtrTrack->Volatile  = (BOOLEAN) Index;\r
+            PtrTrack->Volatile  = (BOOLEAN)(Index == 0);\r
             return EFI_SUCCESS;\r
           } else {\r
             if (CompareGuid (VendorGuid, &Variable[Index]->VendorGuid)) {\r
               if (!CompareMem (VariableName, GET_VARIABLE_NAME_PTR (Variable[Index]), Variable[Index]->NameSize)) {\r
                 PtrTrack->CurrPtr   = Variable[Index];\r
-                PtrTrack->Volatile  = (BOOLEAN) Index;\r
+                PtrTrack->Volatile  = (BOOLEAN)(Index == 0);\r
                 return EFI_SUCCESS;\r
               }\r
             }\r
@@ -572,26 +746,21 @@ Returns:
 \r
       Variable[Index] = GetNextVariablePtr (Variable[Index]);\r
     }\r
-    //\r
-    // While (...)\r
-    //\r
   }\r
-  //\r
-  // for (...)\r
-  //\r
   PtrTrack->CurrPtr = NULL;\r
   return EFI_NOT_FOUND;\r
 }\r
 \r
+\r
 EFI_STATUS\r
 EFIAPI\r
 GetVariable (\r
   IN      CHAR16            *VariableName,\r
-  IN      EFI_GUID          * VendorGuid,\r
+  IN      EFI_GUID          *VendorGuid,\r
   OUT     UINT32            *Attributes OPTIONAL,\r
   IN OUT  UINTN             *DataSize,\r
   OUT     VOID              *Data,\r
-  IN      VARIABLE_GLOBAL   * Global,\r
+  IN      VARIABLE_GLOBAL   *Global,\r
   IN      UINT32            Instance\r
   )\r
 /*++\r
@@ -602,18 +771,22 @@ Routine Description:
 \r
 Arguments:\r
 \r
-  VariableName                    Name of Variable to be found\r
-  VendorGuid                      Variable vendor GUID\r
-  Attributes OPTIONAL             Attribute value of the variable found\r
-  DataSize                        Size of Data found. If size is less than the\r
-                                  data, this value contains the required size.\r
-  Data                            Data pointer\r
-  Global                          Pointer to VARIABLE_GLOBAL structure\r
-  Instance                        Instance of the Firmware Volume.\r
+  VariableName                Name of Variable to be found\r
+  VendorGuid                  Variable vendor GUID\r
+  Attributes OPTIONAL         Attribute value of the variable found\r
+  DataSize                    Size of Data found. If size is less than the\r
+                              data, this value contains the required size.\r
+  Data                        Data pointer\r
+  Global                      Pointer to VARIABLE_GLOBAL structure\r
+  Instance                    Instance of the Firmware Volume.\r
 \r
 Returns:\r
 \r
-  EFI STATUS\r
+  EFI_INVALID_PARAMETER       - Invalid parameter\r
+  EFI_SUCCESS                 - Find the specified variable\r
+  EFI_NOT_FOUND               - Not found\r
+  EFI_BUFFER_TO_SMALL         - DataSize is too small for the result\r
+\r
 \r
 --*/\r
 {\r
@@ -624,14 +797,22 @@ Returns:
   if (VariableName == NULL || VendorGuid == NULL || DataSize == NULL) {\r
     return EFI_INVALID_PARAMETER;\r
   }\r
+\r
   //\r
   // Find existing variable\r
   //\r
+  Status = FindVariableInCache (VariableName, VendorGuid, Attributes, DataSize, Data);\r
+  if ((Status == EFI_BUFFER_TOO_SMALL) || (Status == EFI_SUCCESS)){\r
+    // Hit in the Cache\r
+    UpdateVariableInfo (VariableName, VendorGuid, FALSE, TRUE, FALSE, FALSE, TRUE);\r
+    return Status;\r
+  }\r
+  \r
   Status = FindVariable (VariableName, VendorGuid, &Variable, Global);\r
-\r
   if (Variable.CurrPtr == NULL || EFI_ERROR (Status)) {\r
     goto Done;\r
   }\r
+\r
   //\r
   // Get data size\r
   //\r
@@ -648,6 +829,9 @@ Returns:
     }\r
 \r
     *DataSize = VarDataSize;\r
+    UpdateVariableInfo (VariableName, VendorGuid, Variable.Volatile, TRUE, FALSE, FALSE, FALSE);\r
+    UpdateVariableCache (VariableName, VendorGuid, Variable.CurrPtr->Attributes, VarDataSize, Data);\r
\r
     Status = EFI_SUCCESS;\r
     goto Done;\r
   } else {\r
@@ -800,11 +984,12 @@ Arguments:
 \r
 Returns:\r
 \r
-  EFI STATUS\r
   EFI_INVALID_PARAMETER           - Invalid parameter\r
   EFI_SUCCESS                     - Set successfully\r
   EFI_OUT_OF_RESOURCES            - Resource not enough to set variable\r
   EFI_NOT_FOUND                   - Not found\r
+  EFI_DEVICE_ERROR                - Variable can not be saved due to hardware failure\r
+  EFI_WRITE_PROTECTED             - Variable is read-only\r
 \r
 --*/\r
 {\r
@@ -820,312 +1005,358 @@ Returns:
 \r
   Reclaimed = FALSE;\r
 \r
+  //\r
+  // Check input parameters\r
+  //\r
   if (VariableName == NULL || VariableName[0] == 0 || VendorGuid == NULL) {\r
     return EFI_INVALID_PARAMETER;\r
+  }  \r
+  //\r
+  //  Make sure if runtime bit is set, boot service bit is set also\r
+  //\r
+  if ((Attributes & (EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS)) == EFI_VARIABLE_RUNTIME_ACCESS) {\r
+    return EFI_INVALID_PARAMETER;\r
   }\r
-\r
+  //\r
+  //  The size of the VariableName, including the Unicode Null in bytes plus\r
+  //  the DataSize is limited to maximum size of MAX_HARDWARE_ERROR_VARIABLE_SIZE (32K)\r
+  //  bytes for HwErrRec, and MAX_VARIABLE_SIZE (1024) bytes for the others.\r
+  //\r
+  if ((Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == EFI_VARIABLE_HARDWARE_ERROR_RECORD) {\r
+    if ((DataSize > MAX_HARDWARE_ERROR_VARIABLE_SIZE) ||                                                       \r
+        (sizeof (VARIABLE_HEADER) + StrSize (VariableName) + DataSize > MAX_HARDWARE_ERROR_VARIABLE_SIZE)) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }    \r
+  } else {\r
+  //\r
+  //  The size of the VariableName, including the Unicode Null in bytes plus\r
+  //  the DataSize is limited to maximum size of MAX_VARIABLE_SIZE (1024) bytes.\r
+  //\r
+    if ((DataSize > MAX_VARIABLE_SIZE) ||\r
+        (sizeof (VARIABLE_HEADER) + StrSize (VariableName) + DataSize > MAX_VARIABLE_SIZE)) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }  \r
+  }  \r
+  //\r
+  // Check whether the input variable is already existed\r
+  //\r
+  \r
   Status = FindVariable (VariableName, VendorGuid, &Variable, Global);\r
 \r
-  if (Status == EFI_INVALID_PARAMETER) {\r
-    goto Done;\r
-  } else if (!EFI_ERROR (Status) && Variable.Volatile && EfiAtRuntime()) {\r
+  if (Status == EFI_SUCCESS && Variable.CurrPtr != NULL) {\r
     //\r
-    // If EfiAtRuntime and the variable is Volatile and Runtime Access,\r
-    // the volatile is ReadOnly, and SetVariable should be aborted and\r
-    // return EFI_WRITE_PROTECTED.\r
+    // Update/Delete existing variable\r
     //\r
-    Status = EFI_WRITE_PROTECTED;\r
-    goto Done;\r
-  } else if (sizeof (VARIABLE_HEADER) + StrSize (VariableName) + DataSize > MAX_VARIABLE_SIZE) {\r
+    \r
+    if (EfiAtRuntime ()) {        \r
+      //\r
+      // If EfiAtRuntime and the variable is Volatile and Runtime Access,  \r
+      // the volatile is ReadOnly, and SetVariable should be aborted and \r
+      // return EFI_WRITE_PROTECTED.\r
+      //\r
+      if (Variable.Volatile) {\r
+        Status = EFI_WRITE_PROTECTED;\r
+        goto Done;\r
+      }\r
+      //\r
+      // Only variable have NV attribute can be updated/deleted in Runtime\r
+      //\r
+      if (!(Variable.CurrPtr->Attributes & EFI_VARIABLE_NON_VOLATILE)) {\r
+        Status = EFI_INVALID_PARAMETER;\r
+        goto Done;      \r
+      }\r
+    }\r
     //\r
-    //  The size of the VariableName, including the Unicode Null in bytes plus\r
-    //  the DataSize is limited to maximum size of MAX_VARIABLE_SIZE (1024) bytes.\r
+    // Setting a data variable with no access, or zero DataSize attributes\r
+    // specified causes it to be deleted.\r
     //\r
-    Status = EFI_INVALID_PARAMETER;\r
-    goto Done;\r
-  } else if (Attributes == EFI_VARIABLE_NON_VOLATILE) {\r
+    if (DataSize == 0 || (Attributes & (EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS)) == 0) {    \r
+      State = Variable.CurrPtr->State;\r
+      State &= VAR_DELETED;\r
+\r
+      Status = UpdateVariableStore (\r
+                 Global,\r
+                 Variable.Volatile,\r
+                 FALSE,\r
+                 Instance,\r
+                 (UINTN) &Variable.CurrPtr->State,\r
+                 sizeof (UINT8),\r
+                 &State\r
+                 ); \r
+      if (!EFI_ERROR (Status)) {\r
+        UpdateVariableInfo (VariableName, VendorGuid, Variable.Volatile, FALSE, FALSE, TRUE, FALSE);\r
+        UpdateVariableCache (VariableName, VendorGuid, Attributes, DataSize, Data);\r
+      }\r
+      goto Done;     \r
+    }\r
     //\r
-    //  Make sure not only EFI_VARIABLE_NON_VOLATILE is set\r
+    // If the variable is marked valid and the same data has been passed in\r
+    // then return to the caller immediately.\r
     //\r
-    Status = EFI_INVALID_PARAMETER;\r
-    goto Done;\r
-  } else if ((Attributes & (EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS)) ==\r
-                EFI_VARIABLE_RUNTIME_ACCESS) {\r
+    if (Variable.CurrPtr->DataSize == DataSize &&\r
+        (CompareMem (Data, GetVariableDataPtr (Variable.CurrPtr), DataSize) == 0)) {\r
+      \r
+      UpdateVariableInfo (VariableName, VendorGuid, Variable.Volatile, FALSE, TRUE, FALSE, FALSE);\r
+      Status = EFI_SUCCESS;\r
+      goto Done;\r
+    } else if ((Variable.CurrPtr->State == VAR_ADDED) ||\r
+               (Variable.CurrPtr->State == (VAR_ADDED & VAR_IN_DELETED_TRANSITION))) {\r
+      //\r
+      // Mark the old variable as in delete transition\r
+      //\r
+      State = Variable.CurrPtr->State;\r
+      State &= VAR_IN_DELETED_TRANSITION;\r
+\r
+      Status = UpdateVariableStore (\r
+                 Global,\r
+                 Variable.Volatile,\r
+                 FALSE,\r
+                 Instance,\r
+                 (UINTN) &Variable.CurrPtr->State,\r
+                 sizeof (UINT8),\r
+                 &State\r
+                 );      \r
+      if (EFI_ERROR (Status)) {\r
+        goto Done;  \r
+      } \r
+    }    \r
+  } else if (Status == EFI_NOT_FOUND) {\r
     //\r
-    //  Make sure if runtime bit is set, boot service bit is set also\r
+    // Create a new variable\r
+    //  \r
+    \r
     //\r
-    Status = EFI_INVALID_PARAMETER;\r
-    goto Done;\r
-  } else if (EfiAtRuntime () && Attributes && !(Attributes & EFI_VARIABLE_RUNTIME_ACCESS)) {\r
+    // Make sure we are trying to create a new variable.\r
+    // Setting a data variable with no access, or zero DataSize attributes means to delete it.    \r
     //\r
-    // Runtime but Attribute is not Runtime\r
+    if (DataSize == 0 || (Attributes & (EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS)) == 0) {\r
+      Status = EFI_NOT_FOUND;\r
+      goto Done;\r
+    }\r
+        \r
     //\r
-    Status = EFI_INVALID_PARAMETER;\r
-    goto Done;\r
-  } else if (EfiAtRuntime () && Attributes && !(Attributes & EFI_VARIABLE_NON_VOLATILE)) {\r
+    // Only variable have NV|RT attribute can be created in Runtime\r
     //\r
-    // Cannot set volatile variable in Runtime\r
+    if (EfiAtRuntime () &&\r
+        (!(Attributes & EFI_VARIABLE_RUNTIME_ACCESS) || !(Attributes & EFI_VARIABLE_NON_VOLATILE))) {\r
+      Status = EFI_INVALID_PARAMETER;\r
+      goto Done;\r
+    }         \r
+  } else {\r
     //\r
-    Status = EFI_INVALID_PARAMETER;\r
+    // Status should be EFI_INVALID_PARAMETER here according to return status of FindVariable().\r
+    //\r
+    ASSERT (Status == EFI_INVALID_PARAMETER);\r
     goto Done;\r
-  } else if (DataSize == 0 || (Attributes & (EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS)) == 0) {\r
+  }\r
+\r
+  //\r
+  // Function part - create a new variable and copy the data.\r
+  // Both update a variable and create a variable will come here.\r
+  //\r
+  // Tricky part: Use scratch data area at the end of volatile variable store\r
+  // as a temporary storage.\r
+  //\r
+  NextVariable = GetEndPointer ((VARIABLE_STORE_HEADER *) ((UINTN) Global->VolatileVariableBase));\r
+\r
+  SetMem (NextVariable, SCRATCH_SIZE, 0xff);\r
+\r
+  NextVariable->StartId     = VARIABLE_DATA;\r
+  NextVariable->Attributes  = Attributes;\r
+  //\r
+  // NextVariable->State = VAR_ADDED;\r
+  //\r
+  NextVariable->Reserved  = 0;\r
+  VarNameOffset           = sizeof (VARIABLE_HEADER);\r
+  VarNameSize             = StrSize (VariableName);\r
+  CopyMem (\r
+    (UINT8 *) ((UINTN) NextVariable + VarNameOffset),\r
+    VariableName,\r
+    VarNameSize\r
+    );\r
+  VarDataOffset = VarNameOffset + VarNameSize + GET_PAD_SIZE (VarNameSize);\r
+  CopyMem (\r
+    (UINT8 *) ((UINTN) NextVariable + VarDataOffset),\r
+    Data,\r
+    DataSize\r
+    );\r
+  CopyMem (&NextVariable->VendorGuid, VendorGuid, sizeof (EFI_GUID));\r
+  //\r
+  // There will be pad bytes after Data, the NextVariable->NameSize and\r
+  // NextVariable->DataSize should not include pad size so that variable\r
+  // service can get actual size in GetVariable\r
+  //\r
+  NextVariable->NameSize  = (UINT32)VarNameSize;\r
+  NextVariable->DataSize  = (UINT32)DataSize;\r
+\r
+  //\r
+  // The actual size of the variable that stores in storage should\r
+  // include pad size.\r
+  //\r
+  VarSize = VarDataOffset + DataSize + GET_PAD_SIZE (DataSize);\r
+  if (Attributes & EFI_VARIABLE_NON_VOLATILE) {\r
     //\r
-    // Setting a data variable with no access, or zero DataSize attributes\r
-    // specified causes it to be deleted.\r
+    // Create a nonvolatile variable\r
     //\r
-    if (!EFI_ERROR (Status)) {\r
-      State = Variable.CurrPtr->State;\r
-      State &= VAR_DELETED;\r
-\r
-      Status = UpdateVariableStore (\r
-                Global,\r
-                Variable.Volatile,\r
-                FALSE,\r
-                Instance,\r
-                (UINTN) &Variable.CurrPtr->State,\r
-                sizeof (UINT8),\r
-                &State\r
-                );\r
+    Variable.Volatile = FALSE;\r
+    \r
+    if ((UINT32) (VarSize +*NonVolatileOffset) >\r
+          ((VARIABLE_STORE_HEADER *) ((UINTN) (Global->NonVolatileVariableBase)))->Size\r
+          ) {\r
+      if (EfiAtRuntime ()) {\r
+        Status = EFI_OUT_OF_RESOURCES;\r
+        goto Done;\r
+      }\r
+      //\r
+      // Perform garbage collection & reclaim operation\r
+      //\r
+      Status = Reclaim (Global->NonVolatileVariableBase, NonVolatileOffset, FALSE);\r
       if (EFI_ERROR (Status)) {\r
         goto Done;\r
       }\r
-\r
-      Status = EFI_SUCCESS;\r
-      goto Done;\r
-    }\r
-\r
-    Status = EFI_NOT_FOUND;\r
-    goto Done;\r
-  } else {\r
-    if (!EFI_ERROR (Status)) {\r
       //\r
-      // If the variable is marked valid and the same data has been passed in\r
-      // then return to the caller immediately.\r
+      // If still no enough space, return out of resources\r
       //\r
-      if (Variable.CurrPtr->DataSize == DataSize &&\r
-          !CompareMem (Data, GetVariableDataPtr (Variable.CurrPtr), DataSize)\r
+      if ((UINT32) (VarSize +*NonVolatileOffset) >\r
+            ((VARIABLE_STORE_HEADER *) ((UINTN) (Global->NonVolatileVariableBase)))->Size\r
             ) {\r
-        Status = EFI_SUCCESS;\r
+        Status = EFI_OUT_OF_RESOURCES;\r
         goto Done;\r
-      } else if (Variable.CurrPtr->State == VAR_ADDED) {\r
-        //\r
-        // Mark the old variable as in delete transition\r
-        //\r
-        State = Variable.CurrPtr->State;\r
-        State &= VAR_IN_DELETED_TRANSITION;\r
-\r
-        Status = UpdateVariableStore (\r
-                  Global,\r
-                  Variable.Volatile,\r
-                  FALSE,\r
-                  Instance,\r
-                  (UINTN) &Variable.CurrPtr->State,\r
-                  sizeof (UINT8),\r
-                  &State\r
-                  );\r
-        if (EFI_ERROR (Status)) {\r
-          goto Done;\r
-        }\r
       }\r
+      \r
+      Reclaimed = TRUE;\r
     }\r
     //\r
-    // Create a new variable and copy the data.\r
+    // Three steps\r
+    // 1. Write variable header\r
+    // 2. Write variable data\r
+    // 3. Set variable state to valid\r
     //\r
-    // Tricky part: Use scratch data area at the end of volatile variable store\r
-    // as a temporary storage.\r
     //\r
-    NextVariable = GetEndPointer ((VARIABLE_STORE_HEADER *) ((UINTN) Global->VolatileVariableBase));\r
-\r
-    SetMem (NextVariable, SCRATCH_SIZE, 0xff);\r
-\r
-    NextVariable->StartId     = VARIABLE_DATA;\r
-    NextVariable->Attributes  = Attributes;\r
+    // Step 1:\r
     //\r
-    // NextVariable->State = VAR_ADDED;\r
-    //\r
-    NextVariable->Reserved  = 0;\r
-    VarNameOffset           = sizeof (VARIABLE_HEADER);\r
-    VarNameSize             = StrSize (VariableName);\r
-    CopyMem (\r
-      (UINT8 *) ((UINTN) NextVariable + VarNameOffset),\r
-      VariableName,\r
-      VarNameSize\r
-      );\r
-    VarDataOffset = VarNameOffset + VarNameSize + GET_PAD_SIZE (VarNameSize);\r
-    CopyMem (\r
-      (UINT8 *) ((UINTN) NextVariable + VarDataOffset),\r
-      Data,\r
-      DataSize\r
-      );\r
-    CopyMem (&NextVariable->VendorGuid, VendorGuid, sizeof (EFI_GUID));\r
+    Status = UpdateVariableStore (\r
+               Global,\r
+               FALSE,\r
+               TRUE,\r
+               Instance,\r
+               *NonVolatileOffset,\r
+               sizeof (VARIABLE_HEADER),\r
+               (UINT8 *) NextVariable\r
+               );\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      goto Done;\r
+    }\r
     //\r
-    // There will be pad bytes after Data, the NextVariable->NameSize and\r
-    // NextVariable->DataSize should not include pad size so that variable\r
-    // service can get actual size in GetVariable\r
+    // Step 2:\r
     //\r
-    NextVariable->NameSize  = (UINT32)VarNameSize;\r
-    NextVariable->DataSize  = (UINT32)DataSize;\r
+    Status = UpdateVariableStore (\r
+               Global,\r
+               FALSE,\r
+               TRUE,\r
+               Instance,\r
+               *NonVolatileOffset + sizeof (VARIABLE_HEADER),\r
+               (UINT32) VarSize - sizeof (VARIABLE_HEADER),\r
+               (UINT8 *) NextVariable + sizeof (VARIABLE_HEADER)\r
+               );\r
 \r
+    if (EFI_ERROR (Status)) {\r
+      goto Done;\r
+    }\r
     //\r
-    // The actual size of the variable that stores in storage should\r
-    // include pad size.\r
+    // Step 3:\r
     //\r
-    VarSize = VarDataOffset + DataSize + GET_PAD_SIZE (DataSize);\r
-    if (Attributes & EFI_VARIABLE_NON_VOLATILE) {\r
-      if ((UINT32) (VarSize +*NonVolatileOffset) >\r
-            ((VARIABLE_STORE_HEADER *) ((UINTN) (Global->NonVolatileVariableBase)))->Size\r
-            ) {\r
-        if (EfiAtRuntime ()) {\r
-          Status = EFI_OUT_OF_RESOURCES;\r
-          goto Done;\r
-        }\r
-        //\r
-        // Perform garbage collection & reclaim operation\r
-        //\r
-        Status = Reclaim (Global->NonVolatileVariableBase, NonVolatileOffset, FALSE);\r
-        if (EFI_ERROR (Status)) {\r
-          goto Done;\r
-        }\r
-        //\r
-        // If still no enough space, return out of resources\r
-        //\r
-        if ((UINT32) (VarSize +*NonVolatileOffset) >\r
-              ((VARIABLE_STORE_HEADER *) ((UINTN) (Global->NonVolatileVariableBase)))->Size\r
-              ) {\r
-          Status = EFI_OUT_OF_RESOURCES;\r
-          goto Done;\r
-        }\r
+    NextVariable->State = VAR_ADDED;\r
+    Status = UpdateVariableStore (\r
+               Global,\r
+               FALSE,\r
+               TRUE,\r
+               Instance,\r
+               *NonVolatileOffset,\r
+               sizeof (VARIABLE_HEADER),\r
+               (UINT8 *) NextVariable\r
+               );\r
 \r
-        Reclaimed = TRUE;\r
-      }\r
-      //\r
-      // Three steps\r
-      // 1. Write variable header\r
-      // 2. Write variable data\r
-      // 3. Set variable state to valid\r
-      //\r
-      //\r
-      // Step 1:\r
-      //\r
-      Status = UpdateVariableStore (\r
-                Global,\r
-                FALSE,\r
-                TRUE,\r
-                Instance,\r
-                *NonVolatileOffset,\r
-                sizeof (VARIABLE_HEADER),\r
-                (UINT8 *) NextVariable\r
-                );\r
+    if (EFI_ERROR (Status)) {\r
+      goto Done;\r
+    }\r
 \r
-      if (EFI_ERROR (Status)) {\r
-        goto Done;\r
-      }\r
+    *NonVolatileOffset = *NonVolatileOffset + VarSize;\r
+\r
+  } else {\r
+    //\r
+    // Create a volatile variable\r
+    //      \r
+    Variable.Volatile = TRUE;\r
+\r
+    if ((UINT32) (VarSize +*VolatileOffset) >\r
+        ((VARIABLE_STORE_HEADER *) ((UINTN) (Global->VolatileVariableBase)))->Size) {\r
       //\r
-      // Step 2:\r
+      // Perform garbage collection & reclaim operation\r
       //\r
-      Status = UpdateVariableStore (\r
-                Global,\r
-                FALSE,\r
-                TRUE,\r
-                Instance,\r
-                *NonVolatileOffset + sizeof (VARIABLE_HEADER),\r
-                (UINT32) VarSize - sizeof (VARIABLE_HEADER),\r
-                (UINT8 *) NextVariable + sizeof (VARIABLE_HEADER)\r
-                );\r
-\r
+      Status = Reclaim (Global->VolatileVariableBase, VolatileOffset, TRUE);\r
       if (EFI_ERROR (Status)) {\r
         goto Done;\r
       }\r
       //\r
-      // Step 3:\r
+      // If still no enough space, return out of resources\r
       //\r
-      NextVariable->State = VAR_ADDED;\r
-      Status = UpdateVariableStore (\r
-                Global,\r
-                FALSE,\r
-                TRUE,\r
-                Instance,\r
-                *NonVolatileOffset,\r
-                sizeof (VARIABLE_HEADER),\r
-                (UINT8 *) NextVariable\r
-                );\r
-\r
-      if (EFI_ERROR (Status)) {\r
-        goto Done;\r
-      }\r
-\r
-      *NonVolatileOffset = *NonVolatileOffset + VarSize;\r
-\r
-    } else {\r
-      if (EfiAtRuntime ()) {\r
-        Status = EFI_INVALID_PARAMETER;\r
-        goto Done;\r
-      }\r
-\r
       if ((UINT32) (VarSize +*VolatileOffset) >\r
             ((VARIABLE_STORE_HEADER *) ((UINTN) (Global->VolatileVariableBase)))->Size\r
             ) {\r
-        //\r
-        // Perform garbage collection & reclaim operation\r
-        //\r
-        Status = Reclaim (Global->VolatileVariableBase, VolatileOffset, TRUE);\r
-        if (EFI_ERROR (Status)) {\r
-          goto Done;\r
-        }\r
-        //\r
-        // If still no enough space, return out of resources\r
-        //\r
-        if ((UINT32) (VarSize +*VolatileOffset) >\r
-              ((VARIABLE_STORE_HEADER *) ((UINTN) (Global->VolatileVariableBase)))->Size\r
-              ) {\r
-          Status = EFI_OUT_OF_RESOURCES;\r
-          goto Done;\r
-        }\r
-\r
-        Reclaimed = TRUE;\r
-      }\r
-\r
-      NextVariable->State = VAR_ADDED;\r
-      Status = UpdateVariableStore (\r
-                Global,\r
-                TRUE,\r
-                TRUE,\r
-                Instance,\r
-                *VolatileOffset,\r
-                (UINT32) VarSize,\r
-                (UINT8 *) NextVariable\r
-                );\r
-\r
-      if (EFI_ERROR (Status)) {\r
+        Status = EFI_OUT_OF_RESOURCES;\r
         goto Done;\r
       }\r
-\r
-      *VolatileOffset = *VolatileOffset + VarSize;\r
+      \r
+      Reclaimed = TRUE;\r
     }\r
-    //\r
-    // Mark the old variable as deleted\r
-    //\r
-    if (!Reclaimed && !EFI_ERROR (Status) && Variable.CurrPtr != NULL) {\r
-      State = Variable.CurrPtr->State;\r
-      State &= VAR_DELETED;\r
 \r
-      Status = UpdateVariableStore (\r
-                Global,\r
-                Variable.Volatile,\r
-                FALSE,\r
-                Instance,\r
-                (UINTN) &Variable.CurrPtr->State,\r
-                sizeof (UINT8),\r
-                &State\r
-                );\r
+    NextVariable->State = VAR_ADDED;\r
+    Status = UpdateVariableStore (\r
+               Global,\r
+               TRUE,\r
+               TRUE,\r
+               Instance,\r
+               *VolatileOffset,\r
+               (UINT32) VarSize,\r
+               (UINT8 *) NextVariable\r
+               );\r
 \r
-      if (EFI_ERROR (Status)) {\r
-        goto Done;\r
-      }\r
+    if (EFI_ERROR (Status)) {\r
+      goto Done;\r
+    }\r
+\r
+    *VolatileOffset = *VolatileOffset + VarSize;\r
+  }\r
+  //\r
+  // Mark the old variable as deleted\r
+  //\r
+  if (!Reclaimed && !EFI_ERROR (Status) && Variable.CurrPtr != NULL) {\r
+    State = Variable.CurrPtr->State;\r
+    State &= VAR_DELETED;\r
+\r
+    Status = UpdateVariableStore (\r
+               Global,\r
+               Variable.Volatile,\r
+               FALSE,\r
+               Instance,\r
+               (UINTN) &Variable.CurrPtr->State,\r
+               sizeof (UINT8),\r
+               &State\r
+               );\r
+    \r
+    if (!EFI_ERROR (Status)) {\r
+      UpdateVariableInfo (VariableName, VendorGuid, Variable.Volatile, FALSE, TRUE, FALSE, FALSE);\r
+      UpdateVariableCache (VariableName, VendorGuid, Attributes, DataSize, Data);\r
     }\r
+    goto Done;      \r
   }\r
 \r
   Status = EFI_SUCCESS;\r
+  UpdateVariableInfo (VariableName, VendorGuid, Variable.Volatile, FALSE, TRUE, FALSE, FALSE);\r
+  UpdateVariableCache (VariableName, VendorGuid, Attributes, DataSize, Data);\r
+\r
 Done:\r
   ReleaseLockOnlyAtBootTime (&Global->VariableServicesLock);\r
   return Status;\r
@@ -1154,8 +1385,8 @@ Arguments:
   MaximumVariableStorageSize      Pointer to the maximum size of the storage space available\r
                                   for the EFI variables associated with the attributes specified.\r
   RemainingVariableStorageSize    Pointer to the remaining size of the storage space available\r
-                                  for the EFI variables associated with the attributes specified.\r
-  MaximumVariableSize             Pointer to the maximum size of the individual EFI variables\r
+                                  for EFI variables associated with the attributes specified.\r
+  MaximumVariableSize             Pointer to the maximum size of an individual EFI variables\r
                                   associated with the attributes specified.\r
   Global                          Pointer to VARIABLE_GLOBAL structure.\r
   Instance                        Instance of the Firmware Volume.\r
@@ -1174,15 +1405,15 @@ Returns:
   UINT64                 VariableSize;\r
   VARIABLE_STORE_HEADER  *VariableStoreHeader;\r
 \r
-  if(MaximumVariableStorageSize == NULL || RemainingVariableStorageSize == NULL || MaximumVariableSize == NULL) {\r
+  if(MaximumVariableStorageSize == NULL || RemainingVariableStorageSize == NULL || MaximumVariableSize == NULL || Attributes == 0) {\r
     return EFI_INVALID_PARAMETER;\r
   }\r
-\r
-  if((Attributes & (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS)) == 0) {\r
+  \r
+  if((Attributes & (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_HARDWARE_ERROR_RECORD)) == 0) {\r
     //\r
     // Make sure the Attributes combination is supported by the platform.\r
     //\r
-    return EFI_UNSUPPORTED;\r
+    return EFI_UNSUPPORTED;  \r
   } else if ((Attributes & (EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS)) == EFI_VARIABLE_RUNTIME_ACCESS) {\r
     //\r
     // Make sure if runtime bit is set, boot service bit is set also.\r
@@ -1217,9 +1448,16 @@ Returns:
   *RemainingVariableStorageSize = VariableStoreHeader->Size - sizeof (VARIABLE_STORE_HEADER);\r
 \r
   //\r
-  // Let *MaximumVariableSize be MAX_VARIABLE_SIZE.\r
+  // Let *MaximumVariableSize be MAX_VARIABLE_SIZE with the exception of the variable header size.\r
+  //\r
+  *MaximumVariableSize = MAX_VARIABLE_SIZE - sizeof (VARIABLE_HEADER);\r
+\r
   //\r
-  *MaximumVariableSize = MAX_VARIABLE_SIZE;\r
+  // Harware error record variable needs larger size.\r
+  //\r
+  if ((Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == EFI_VARIABLE_HARDWARE_ERROR_RECORD) {\r
+    *MaximumVariableSize = MAX_HARDWARE_ERROR_VARIABLE_SIZE - sizeof (VARIABLE_HEADER);\r
+  }\r
 \r
   //\r
   // Point to the starting address of the variables.\r
@@ -1257,6 +1495,12 @@ Returns:
     Variable = NextVariable;\r
   }\r
 \r
+  if (*RemainingVariableStorageSize < sizeof (VARIABLE_HEADER)) {\r
+    *MaximumVariableSize = 0;\r
+  } else if ((*RemainingVariableStorageSize - sizeof (VARIABLE_HEADER)) < *MaximumVariableSize) {\r
+    *MaximumVariableSize = *RemainingVariableStorageSize - sizeof (VARIABLE_HEADER);\r
+  }\r
+\r
   ReleaseLockOnlyAtBootTime (&Global->VariableServicesLock);\r
   return EFI_SUCCESS;\r
 }\r
@@ -1295,9 +1539,7 @@ Returns:
   VARIABLE_HEADER                 *NextVariable;\r
   UINT32                          Instance;\r
   EFI_PHYSICAL_ADDRESS            FvVolHdr;\r
-\r
   UINT64                          TempVariableStoreHeader;\r
-\r
   EFI_GCD_MEMORY_SPACE_DESCRIPTOR GcdDescriptor;\r
   EFI_FLASH_SUBAREA_ENTRY         VariableStoreEntry;\r
   UINT64                          BaseAddress;\r