]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.c
MdeModulePkg Variable: Add emulated variable NV mode support
[mirror_edk2.git] / MdeModulePkg / Universal / Variable / RuntimeDxe / Variable.c
index d3df21819abe2794397a1efc9a958b6847206180..e8e19508cd9a3037be0033afff141291fcbed632 100644 (file)
@@ -34,6 +34,7 @@ VARIABLE_MODULE_GLOBAL  *mVariableModuleGlobal;
 \r
 ///\r
 /// Define a memory cache that improves the search performance for a variable.\r
+/// For EmuNvMode == TRUE, it will be equal to NonVolatileVariableBase.\r
 ///\r
 VARIABLE_STORE_HEADER  *mNvVariableCache      = NULL;\r
 \r
@@ -273,7 +274,7 @@ UpdateVariableStore (
   //\r
   // Check if the Data is Volatile.\r
   //\r
-  if (!Volatile) {\r
+  if (!Volatile && !mVariableModuleGlobal->VariableGlobal.EmuNvMode) {\r
     if (Fvb == NULL) {\r
       return EFI_UNSUPPORTED;\r
     }\r
@@ -296,17 +297,30 @@ UpdateVariableStore (
     // Data Pointer should point to the actual Address where data is to be\r
     // written.\r
     //\r
-    VolatileBase = (VARIABLE_STORE_HEADER *) ((UINTN) mVariableModuleGlobal->VariableGlobal.VolatileVariableBase);\r
-    if (SetByIndex) {\r
-      DataPtr += mVariableModuleGlobal->VariableGlobal.VolatileVariableBase;\r
-    }\r
+    if (Volatile) {\r
+      VolatileBase = (VARIABLE_STORE_HEADER *) ((UINTN) mVariableModuleGlobal->VariableGlobal.VolatileVariableBase);\r
+      if (SetByIndex) {\r
+        DataPtr += mVariableModuleGlobal->VariableGlobal.VolatileVariableBase;\r
+      }\r
 \r
-    if ((DataPtr + DataSize) > ((UINTN) VolatileBase + VolatileBase->Size)) {\r
-      return EFI_OUT_OF_RESOURCES;\r
+      if ((DataPtr + DataSize) > ((UINTN) VolatileBase + VolatileBase->Size)) {\r
+        return EFI_OUT_OF_RESOURCES;\r
+      }\r
+    } else {\r
+      //\r
+      // Emulated non-volatile variable mode.\r
+      //\r
+      if (SetByIndex) {\r
+        DataPtr += (UINTN) mNvVariableCache;\r
+      }\r
+\r
+      if ((DataPtr + DataSize) > ((UINTN) mNvVariableCache + mNvVariableCache->Size)) {\r
+        return EFI_OUT_OF_RESOURCES;\r
+      }\r
     }\r
 \r
     //\r
-    // If Volatile Variable just do a simple mem copy.\r
+    // If Volatile/Emulated Non-volatile Variable just do a simple mem copy.\r
     //\r
     CopyMem ((UINT8 *)(UINTN)DataPtr, Buffer, DataSize);\r
     return EFI_SUCCESS;\r
@@ -987,7 +1001,7 @@ Reclaim (
   CommonUserVariableTotalSize = 0;\r
   HwErrVariableTotalSize  = 0;\r
 \r
-  if (IsVolatile) {\r
+  if (IsVolatile || mVariableModuleGlobal->VariableGlobal.EmuNvMode) {\r
     //\r
     // Start Pointers for the variable.\r
     //\r
@@ -1155,13 +1169,21 @@ Reclaim (
     CurrPtr += NewVariableSize;\r
   }\r
 \r
-  if (IsVolatile) {\r
+  if (IsVolatile || mVariableModuleGlobal->VariableGlobal.EmuNvMode) {\r
     //\r
-    // If volatile variable store, just copy valid buffer.\r
+    // If volatile/emulated non-volatile variable store, just copy valid buffer.\r
     //\r
     SetMem ((UINT8 *) (UINTN) VariableBase, VariableStoreHeader->Size, 0xff);\r
     CopyMem ((UINT8 *) (UINTN) VariableBase, ValidBuffer, (UINTN) CurrPtr - (UINTN) ValidBuffer);\r
     *LastVariableOffset = (UINTN) CurrPtr - (UINTN) ValidBuffer;\r
+    if (!IsVolatile) {\r
+      //\r
+      // Emulated non-volatile variable mode.\r
+      //\r
+      mVariableModuleGlobal->HwErrVariableTotalSize = HwErrVariableTotalSize;\r
+      mVariableModuleGlobal->CommonVariableTotalSize = CommonVariableTotalSize;\r
+      mVariableModuleGlobal->CommonUserVariableTotalSize = CommonUserVariableTotalSize;\r
+    }\r
     Status  = EFI_SUCCESS;\r
   } else {\r
     //\r
@@ -1200,7 +1222,7 @@ Reclaim (
   }\r
 \r
 Done:\r
-  if (IsVolatile) {\r
+  if (IsVolatile || mVariableModuleGlobal->VariableGlobal.EmuNvMode) {\r
     FreePool (ValidBuffer);\r
   } else {\r
     //\r
@@ -2147,7 +2169,7 @@ UpdateVariable (
   BOOLEAN                             IsCommonUserVariable;\r
   AUTHENTICATED_VARIABLE_HEADER       *AuthVariable;\r
 \r
-  if (mVariableModuleGlobal->FvbInstance == NULL) {\r
+  if (mVariableModuleGlobal->FvbInstance == NULL && !mVariableModuleGlobal->VariableGlobal.EmuNvMode) {\r
     //\r
     // The FVB protocol is not ready, so the EFI_VARIABLE_WRITE_ARCH_PROTOCOL is not installed.\r
     //\r
@@ -2566,85 +2588,106 @@ UpdateVariable (
       }\r
       goto Done;\r
     }\r
-    //\r
-    // Four steps\r
-    // 1. Write variable header\r
-    // 2. Set variable state to header valid\r
-    // 3. Write variable data\r
-    // 4. Set variable state to valid\r
-    //\r
-    //\r
-    // Step 1:\r
-    //\r
-    Status = UpdateVariableStore (\r
-               &mVariableModuleGlobal->VariableGlobal,\r
-               FALSE,\r
-               TRUE,\r
-               Fvb,\r
-               mVariableModuleGlobal->NonVolatileLastVariableOffset,\r
-               (UINT32) GetVariableHeaderSize (),\r
-               (UINT8 *) NextVariable\r
-               );\r
 \r
-    if (EFI_ERROR (Status)) {\r
-      goto Done;\r
-    }\r
+    if (!mVariableModuleGlobal->VariableGlobal.EmuNvMode) {\r
+      //\r
+      // Four steps\r
+      // 1. Write variable header\r
+      // 2. Set variable state to header valid\r
+      // 3. Write variable data\r
+      // 4. Set variable state to valid\r
+      //\r
+      //\r
+      // Step 1:\r
+      //\r
+      Status = UpdateVariableStore (\r
+                 &mVariableModuleGlobal->VariableGlobal,\r
+                 FALSE,\r
+                 TRUE,\r
+                 Fvb,\r
+                 mVariableModuleGlobal->NonVolatileLastVariableOffset,\r
+                 (UINT32) GetVariableHeaderSize (),\r
+                 (UINT8 *) NextVariable\r
+                 );\r
 \r
-    //\r
-    // Step 2:\r
-    //\r
-    NextVariable->State = VAR_HEADER_VALID_ONLY;\r
-    Status = UpdateVariableStore (\r
-               &mVariableModuleGlobal->VariableGlobal,\r
-               FALSE,\r
-               TRUE,\r
-               Fvb,\r
-               mVariableModuleGlobal->NonVolatileLastVariableOffset + OFFSET_OF (VARIABLE_HEADER, State),\r
-               sizeof (UINT8),\r
-               &NextVariable->State\r
-               );\r
+      if (EFI_ERROR (Status)) {\r
+        goto Done;\r
+      }\r
 \r
-    if (EFI_ERROR (Status)) {\r
-      goto Done;\r
-    }\r
-    //\r
-    // Step 3:\r
-    //\r
-    Status = UpdateVariableStore (\r
-               &mVariableModuleGlobal->VariableGlobal,\r
-               FALSE,\r
-               TRUE,\r
-               Fvb,\r
-               mVariableModuleGlobal->NonVolatileLastVariableOffset + GetVariableHeaderSize (),\r
-               (UINT32) (VarSize - GetVariableHeaderSize ()),\r
-               (UINT8 *) NextVariable + GetVariableHeaderSize ()\r
-               );\r
+      //\r
+      // Step 2:\r
+      //\r
+      NextVariable->State = VAR_HEADER_VALID_ONLY;\r
+      Status = UpdateVariableStore (\r
+                 &mVariableModuleGlobal->VariableGlobal,\r
+                 FALSE,\r
+                 TRUE,\r
+                 Fvb,\r
+                 mVariableModuleGlobal->NonVolatileLastVariableOffset + OFFSET_OF (VARIABLE_HEADER, State),\r
+                 sizeof (UINT8),\r
+                 &NextVariable->State\r
+                 );\r
 \r
-    if (EFI_ERROR (Status)) {\r
-      goto Done;\r
-    }\r
-    //\r
-    // Step 4:\r
-    //\r
-    NextVariable->State = VAR_ADDED;\r
-    Status = UpdateVariableStore (\r
-               &mVariableModuleGlobal->VariableGlobal,\r
-               FALSE,\r
-               TRUE,\r
-               Fvb,\r
-               mVariableModuleGlobal->NonVolatileLastVariableOffset + OFFSET_OF (VARIABLE_HEADER, State),\r
-               sizeof (UINT8),\r
-               &NextVariable->State\r
-               );\r
+      if (EFI_ERROR (Status)) {\r
+        goto Done;\r
+      }\r
+      //\r
+      // Step 3:\r
+      //\r
+      Status = UpdateVariableStore (\r
+                 &mVariableModuleGlobal->VariableGlobal,\r
+                 FALSE,\r
+                 TRUE,\r
+                 Fvb,\r
+                 mVariableModuleGlobal->NonVolatileLastVariableOffset + GetVariableHeaderSize (),\r
+                 (UINT32) (VarSize - GetVariableHeaderSize ()),\r
+                 (UINT8 *) NextVariable + GetVariableHeaderSize ()\r
+                 );\r
 \r
-    if (EFI_ERROR (Status)) {\r
-      goto Done;\r
-    }\r
+      if (EFI_ERROR (Status)) {\r
+        goto Done;\r
+      }\r
+      //\r
+      // Step 4:\r
+      //\r
+      NextVariable->State = VAR_ADDED;\r
+      Status = UpdateVariableStore (\r
+                 &mVariableModuleGlobal->VariableGlobal,\r
+                 FALSE,\r
+                 TRUE,\r
+                 Fvb,\r
+                 mVariableModuleGlobal->NonVolatileLastVariableOffset + OFFSET_OF (VARIABLE_HEADER, State),\r
+                 sizeof (UINT8),\r
+                 &NextVariable->State\r
+                 );\r
 \r
-    //\r
-    // Update the memory copy of Flash region.\r
-    //\r
-    CopyMem ((UINT8 *)mNvVariableCache + mVariableModuleGlobal->NonVolatileLastVariableOffset, (UINT8 *)NextVariable, VarSize);\r
+      if (EFI_ERROR (Status)) {\r
+        goto Done;\r
+      }\r
+\r
+      //\r
+      // Update the memory copy of Flash region.\r
+      //\r
+      CopyMem ((UINT8 *)mNvVariableCache + mVariableModuleGlobal->NonVolatileLastVariableOffset, (UINT8 *)NextVariable, VarSize);\r
+    } else {\r
+      //\r
+      // Emulated non-volatile variable mode.\r
+      //\r
+      NextVariable->State = VAR_ADDED;\r
+      Status = UpdateVariableStore (\r
+                 &mVariableModuleGlobal->VariableGlobal,\r
+                 FALSE,\r
+                 TRUE,\r
+                 Fvb,\r
+                 mVariableModuleGlobal->NonVolatileLastVariableOffset,\r
+                 (UINT32) VarSize,\r
+                 (UINT8 *) NextVariable\r
+                 );\r
+\r
+      if (EFI_ERROR (Status)) {\r
+        goto Done;\r
+      }\r
+    }\r
 \r
     mVariableModuleGlobal->NonVolatileLastVariableOffset += HEADER_ALIGN (VarSize);\r
 \r
@@ -3875,6 +3918,93 @@ InitRealNonVolatileVariableStore (
   return EFI_SUCCESS;\r
 }\r
 \r
+/**\r
+  Init emulated non-volatile variable store.\r
+\r
+  @param[out] VariableStoreBase Output pointer to emulated non-volatile variable store base.\r
+\r
+  @retval EFI_SUCCESS           Function successfully executed.\r
+  @retval EFI_OUT_OF_RESOURCES  Fail to allocate enough memory resource.\r
+\r
+**/\r
+EFI_STATUS\r
+InitEmuNonVolatileVariableStore (\r
+  EFI_PHYSICAL_ADDRESS  *VariableStoreBase\r
+  )\r
+{\r
+  VARIABLE_STORE_HEADER *VariableStore;\r
+  UINT32                VariableStoreLength;\r
+  BOOLEAN               FullyInitializeStore;\r
+  UINT32                HwErrStorageSize;\r
+\r
+  FullyInitializeStore = TRUE;\r
+\r
+  VariableStoreLength = PcdGet32 (PcdVariableStoreSize);\r
+  ASSERT (sizeof (VARIABLE_STORE_HEADER) <= VariableStoreLength);\r
+\r
+  //\r
+  // Allocate memory for variable store.\r
+  //\r
+  if (PcdGet64 (PcdEmuVariableNvStoreReserved) == 0) {\r
+    VariableStore = (VARIABLE_STORE_HEADER *) AllocateRuntimePool (VariableStoreLength);\r
+    if (VariableStore == NULL) {\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+  } else {\r
+    //\r
+    // A memory location has been reserved for the NV variable store.  Certain\r
+    // platforms may be able to preserve a memory range across system resets,\r
+    // thereby providing better NV variable emulation.\r
+    //\r
+    VariableStore =\r
+      (VARIABLE_STORE_HEADER *)(VOID*)(UINTN)\r
+        PcdGet64 (PcdEmuVariableNvStoreReserved);\r
+    if ((VariableStore->Size == VariableStoreLength) &&\r
+        (CompareGuid (&VariableStore->Signature, &gEfiAuthenticatedVariableGuid) ||\r
+         CompareGuid (&VariableStore->Signature, &gEfiVariableGuid)) &&\r
+        (VariableStore->Format == VARIABLE_STORE_FORMATTED) &&\r
+        (VariableStore->State == VARIABLE_STORE_HEALTHY)) {\r
+      DEBUG((\r
+        DEBUG_INFO,\r
+        "Variable Store reserved at %p appears to be valid\n",\r
+        VariableStore\r
+        ));\r
+      FullyInitializeStore = FALSE;\r
+    }\r
+  }\r
+\r
+  if (FullyInitializeStore) {\r
+    SetMem (VariableStore, VariableStoreLength, 0xff);\r
+    //\r
+    // Use gEfiAuthenticatedVariableGuid for potential auth variable support.\r
+    //\r
+    CopyGuid (&VariableStore->Signature, &gEfiAuthenticatedVariableGuid);\r
+    VariableStore->Size       = VariableStoreLength;\r
+    VariableStore->Format     = VARIABLE_STORE_FORMATTED;\r
+    VariableStore->State      = VARIABLE_STORE_HEALTHY;\r
+    VariableStore->Reserved   = 0;\r
+    VariableStore->Reserved1  = 0;\r
+  }\r
+\r
+  *VariableStoreBase = (EFI_PHYSICAL_ADDRESS) (UINTN) VariableStore;\r
+\r
+  HwErrStorageSize = PcdGet32 (PcdHwErrStorageSize);\r
+\r
+  //\r
+  // Note that in EdkII variable driver implementation, Hardware Error Record type variable\r
+  // is stored with common variable in the same NV region. So the platform integrator should\r
+  // ensure that the value of PcdHwErrStorageSize is less than the value of\r
+  // (VariableStoreLength - sizeof (VARIABLE_STORE_HEADER)).\r
+  //\r
+  ASSERT (HwErrStorageSize < (VariableStoreLength - sizeof (VARIABLE_STORE_HEADER)));\r
+\r
+  mVariableModuleGlobal->CommonVariableSpace = ((UINTN) VariableStoreLength - sizeof (VARIABLE_STORE_HEADER) - HwErrStorageSize);\r
+  mVariableModuleGlobal->CommonMaxUserVariableSpace = mVariableModuleGlobal->CommonVariableSpace;\r
+  mVariableModuleGlobal->CommonRuntimeVariableSpace = mVariableModuleGlobal->CommonVariableSpace;\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
 /**\r
   Init non-volatile variable store.\r
 \r
@@ -3894,9 +4024,19 @@ InitNonVolatileVariableStore (
   UINTN                                 VariableSize;\r
   EFI_STATUS                            Status;\r
 \r
-  Status = InitRealNonVolatileVariableStore (&VariableStoreBase);\r
-  if (EFI_ERROR (Status)) {\r
-    return Status;\r
+  if (PcdGetBool (PcdEmuVariableNvModeEnable)) {\r
+    Status = InitEmuNonVolatileVariableStore (&VariableStoreBase);\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+    mVariableModuleGlobal->VariableGlobal.EmuNvMode = TRUE;\r
+    DEBUG ((DEBUG_INFO, "Variable driver will work at emulated non-volatile variable mode!\n"));\r
+  } else {\r
+    Status = InitRealNonVolatileVariableStore (&VariableStoreBase);\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+    mVariableModuleGlobal->VariableGlobal.EmuNvMode = FALSE;\r
   }\r
 \r
   mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase = VariableStoreBase;\r