]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Universal/Variable/RuntimeDxe/VariableNonVolatile.c
MdeModulePkg/Variable: Add a file for NV variable functions
[mirror_edk2.git] / MdeModulePkg / Universal / Variable / RuntimeDxe / VariableNonVolatile.c
diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableNonVolatile.c b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableNonVolatile.c
new file mode 100644 (file)
index 0000000..0637a82
--- /dev/null
@@ -0,0 +1,334 @@
+/** @file\r
+  Common variable non-volatile store routines.\r
+\r
+Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>\r
+SPDX-License-Identifier: BSD-2-Clause-Patent\r
+\r
+**/\r
+\r
+#include "VariableNonVolatile.h"\r
+#include "VariableParsing.h"\r
+\r
+extern VARIABLE_MODULE_GLOBAL  *mVariableModuleGlobal;\r
+\r
+/**\r
+  Get non-volatile maximum variable size.\r
+\r
+  @return Non-volatile maximum variable size.\r
+\r
+**/\r
+UINTN\r
+GetNonVolatileMaxVariableSize (\r
+  VOID\r
+  )\r
+{\r
+  if (PcdGet32 (PcdHwErrStorageSize) != 0) {\r
+    return MAX (MAX (PcdGet32 (PcdMaxVariableSize), PcdGet32 (PcdMaxAuthVariableSize)),\r
+                PcdGet32 (PcdMaxHardwareErrorVariableSize));\r
+  } else {\r
+    return MAX (PcdGet32 (PcdMaxVariableSize), PcdGet32 (PcdMaxAuthVariableSize));\r
+  }\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
+  OUT 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 real non-volatile variable store.\r
+\r
+  @param[out] VariableStoreBase Output pointer to real 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
+  @retval EFI_VOLUME_CORRUPTED  Variable Store or Firmware Volume for Variable Store is corrupted.\r
+\r
+**/\r
+EFI_STATUS\r
+InitRealNonVolatileVariableStore (\r
+  OUT EFI_PHYSICAL_ADDRESS              *VariableStoreBase\r
+  )\r
+{\r
+  EFI_FIRMWARE_VOLUME_HEADER            *FvHeader;\r
+  VARIABLE_STORE_HEADER                 *VariableStore;\r
+  UINT32                                VariableStoreLength;\r
+  EFI_HOB_GUID_TYPE                     *GuidHob;\r
+  EFI_PHYSICAL_ADDRESS                  NvStorageBase;\r
+  UINT8                                 *NvStorageData;\r
+  UINT32                                NvStorageSize;\r
+  FAULT_TOLERANT_WRITE_LAST_WRITE_DATA  *FtwLastWriteData;\r
+  UINT32                                BackUpOffset;\r
+  UINT32                                BackUpSize;\r
+  UINT32                                HwErrStorageSize;\r
+  UINT32                                MaxUserNvVariableSpaceSize;\r
+  UINT32                                BoottimeReservedNvVariableSpaceSize;\r
+  EFI_STATUS                            Status;\r
+  VOID                                  *FtwProtocol;\r
+\r
+  mVariableModuleGlobal->FvbInstance = NULL;\r
+\r
+  //\r
+  // Allocate runtime memory used for a memory copy of the FLASH region.\r
+  // Keep the memory and the FLASH in sync as updates occur.\r
+  //\r
+  NvStorageSize = PcdGet32 (PcdFlashNvStorageVariableSize);\r
+  NvStorageData = AllocateRuntimeZeroPool (NvStorageSize);\r
+  if (NvStorageData == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  NvStorageBase = NV_STORAGE_VARIABLE_BASE;\r
+  ASSERT (NvStorageBase != 0);\r
+\r
+  //\r
+  // Copy NV storage data to the memory buffer.\r
+  //\r
+  CopyMem (NvStorageData, (UINT8 *) (UINTN) NvStorageBase, NvStorageSize);\r
+\r
+  Status = GetFtwProtocol ((VOID **)&FtwProtocol);\r
+  //\r
+  // If FTW protocol has been installed, no need to check FTW last write data hob.\r
+  //\r
+  if (EFI_ERROR (Status)) {\r
+    //\r
+    // Check the FTW last write data hob.\r
+    //\r
+    GuidHob = GetFirstGuidHob (&gEdkiiFaultTolerantWriteGuid);\r
+    if (GuidHob != NULL) {\r
+      FtwLastWriteData = (FAULT_TOLERANT_WRITE_LAST_WRITE_DATA *) GET_GUID_HOB_DATA (GuidHob);\r
+      if (FtwLastWriteData->TargetAddress == NvStorageBase) {\r
+        DEBUG ((DEBUG_INFO, "Variable: NV storage is backed up in spare block: 0x%x\n", (UINTN) FtwLastWriteData->SpareAddress));\r
+        //\r
+        // Copy the backed up NV storage data to the memory buffer from spare block.\r
+        //\r
+        CopyMem (NvStorageData, (UINT8 *) (UINTN) (FtwLastWriteData->SpareAddress), NvStorageSize);\r
+      } else if ((FtwLastWriteData->TargetAddress > NvStorageBase) &&\r
+                 (FtwLastWriteData->TargetAddress < (NvStorageBase + NvStorageSize))) {\r
+        //\r
+        // Flash NV storage from the Offset is backed up in spare block.\r
+        //\r
+        BackUpOffset = (UINT32) (FtwLastWriteData->TargetAddress - NvStorageBase);\r
+        BackUpSize = NvStorageSize - BackUpOffset;\r
+        DEBUG ((DEBUG_INFO, "Variable: High partial NV storage from offset: %x is backed up in spare block: 0x%x\n", BackUpOffset, (UINTN) FtwLastWriteData->SpareAddress));\r
+        //\r
+        // Copy the partial backed up NV storage data to the memory buffer from spare block.\r
+        //\r
+        CopyMem (NvStorageData + BackUpOffset, (UINT8 *) (UINTN) FtwLastWriteData->SpareAddress, BackUpSize);\r
+      }\r
+    }\r
+  }\r
+\r
+  FvHeader = (EFI_FIRMWARE_VOLUME_HEADER *) NvStorageData;\r
+\r
+  //\r
+  // Check if the Firmware Volume is not corrupted\r
+  //\r
+  if ((FvHeader->Signature != EFI_FVH_SIGNATURE) || (!CompareGuid (&gEfiSystemNvDataFvGuid, &FvHeader->FileSystemGuid))) {\r
+    FreePool (NvStorageData);\r
+    DEBUG ((DEBUG_ERROR, "Firmware Volume for Variable Store is corrupted\n"));\r
+    return EFI_VOLUME_CORRUPTED;\r
+  }\r
+\r
+  VariableStore = (VARIABLE_STORE_HEADER *) ((UINTN) FvHeader + FvHeader->HeaderLength);\r
+  VariableStoreLength = NvStorageSize - FvHeader->HeaderLength;\r
+  ASSERT (sizeof (VARIABLE_STORE_HEADER) <= VariableStoreLength);\r
+  ASSERT (VariableStore->Size == VariableStoreLength);\r
+\r
+  //\r
+  // Check if the Variable Store header is not corrupted\r
+  //\r
+  if (GetVariableStoreStatus (VariableStore) != EfiValid) {\r
+    FreePool (NvStorageData);\r
+    DEBUG((DEBUG_ERROR, "Variable Store header is corrupted\n"));\r
+    return EFI_VOLUME_CORRUPTED;\r
+  }\r
+\r
+  mNvFvHeaderCache = FvHeader;\r
+\r
+  *VariableStoreBase = (EFI_PHYSICAL_ADDRESS) (UINTN) VariableStore;\r
+\r
+  HwErrStorageSize = PcdGet32 (PcdHwErrStorageSize);\r
+  MaxUserNvVariableSpaceSize = PcdGet32 (PcdMaxUserNvVariableSpaceSize);\r
+  BoottimeReservedNvVariableSpaceSize = PcdGet32 (PcdBoottimeReservedNvVariableSpaceSize);\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
+  // Ensure that the value of PcdMaxUserNvVariableSpaceSize is less than the value of\r
+  // (VariableStoreLength - sizeof (VARIABLE_STORE_HEADER)) - PcdGet32 (PcdHwErrStorageSize).\r
+  //\r
+  ASSERT (MaxUserNvVariableSpaceSize < (VariableStoreLength - sizeof (VARIABLE_STORE_HEADER) - HwErrStorageSize));\r
+  //\r
+  // Ensure that the value of PcdBoottimeReservedNvVariableSpaceSize is less than the value of\r
+  // (VariableStoreLength - sizeof (VARIABLE_STORE_HEADER)) - PcdGet32 (PcdHwErrStorageSize).\r
+  //\r
+  ASSERT (BoottimeReservedNvVariableSpaceSize < (VariableStoreLength - sizeof (VARIABLE_STORE_HEADER) - HwErrStorageSize));\r
+\r
+  mVariableModuleGlobal->CommonVariableSpace = ((UINTN) VariableStoreLength - sizeof (VARIABLE_STORE_HEADER) - HwErrStorageSize);\r
+  mVariableModuleGlobal->CommonMaxUserVariableSpace = ((MaxUserNvVariableSpaceSize != 0) ? MaxUserNvVariableSpaceSize : mVariableModuleGlobal->CommonVariableSpace);\r
+  mVariableModuleGlobal->CommonRuntimeVariableSpace = mVariableModuleGlobal->CommonVariableSpace - BoottimeReservedNvVariableSpaceSize;\r
+\r
+  DEBUG ((\r
+    DEBUG_INFO,\r
+    "Variable driver common space: 0x%x 0x%x 0x%x\n",\r
+    mVariableModuleGlobal->CommonVariableSpace,\r
+    mVariableModuleGlobal->CommonMaxUserVariableSpace,\r
+    mVariableModuleGlobal->CommonRuntimeVariableSpace\r
+    ));\r
+\r
+  //\r
+  // The max NV variable size should be < (VariableStoreLength - sizeof (VARIABLE_STORE_HEADER)).\r
+  //\r
+  ASSERT (GetNonVolatileMaxVariableSize () < (VariableStoreLength - sizeof (VARIABLE_STORE_HEADER)));\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Init non-volatile variable store.\r
+\r
+  @retval EFI_SUCCESS           Function successfully executed.\r
+  @retval EFI_OUT_OF_RESOURCES  Fail to allocate enough memory resource.\r
+  @retval EFI_VOLUME_CORRUPTED  Variable Store or Firmware Volume for Variable Store is corrupted.\r
+\r
+**/\r
+EFI_STATUS\r
+InitNonVolatileVariableStore (\r
+  VOID\r
+  )\r
+{\r
+  VARIABLE_HEADER                       *Variable;\r
+  VARIABLE_HEADER                       *NextVariable;\r
+  EFI_PHYSICAL_ADDRESS                  VariableStoreBase;\r
+  UINTN                                 VariableSize;\r
+  EFI_STATUS                            Status;\r
+\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
+  mNvVariableCache = (VARIABLE_STORE_HEADER *) (UINTN) VariableStoreBase;\r
+  mVariableModuleGlobal->VariableGlobal.AuthFormat = (BOOLEAN)(CompareGuid (&mNvVariableCache->Signature, &gEfiAuthenticatedVariableGuid));\r
+\r
+  mVariableModuleGlobal->MaxVariableSize = PcdGet32 (PcdMaxVariableSize);\r
+  mVariableModuleGlobal->MaxAuthVariableSize = ((PcdGet32 (PcdMaxAuthVariableSize) != 0) ? PcdGet32 (PcdMaxAuthVariableSize) : mVariableModuleGlobal->MaxVariableSize);\r
+\r
+  //\r
+  // Parse non-volatile variable data and get last variable offset.\r
+  //\r
+  Variable  = GetStartPointer (mNvVariableCache);\r
+  while (IsValidVariableHeader (Variable, GetEndPointer (mNvVariableCache))) {\r
+    NextVariable = GetNextVariablePtr (Variable, mVariableModuleGlobal->VariableGlobal.AuthFormat);\r
+    VariableSize = (UINTN) NextVariable - (UINTN) Variable;\r
+    if ((Variable->Attributes & (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_HARDWARE_ERROR_RECORD)) == (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_HARDWARE_ERROR_RECORD)) {\r
+      mVariableModuleGlobal->HwErrVariableTotalSize += VariableSize;\r
+    } else {\r
+      mVariableModuleGlobal->CommonVariableTotalSize += VariableSize;\r
+    }\r
+\r
+    Variable = NextVariable;\r
+  }\r
+  mVariableModuleGlobal->NonVolatileLastVariableOffset = (UINTN) Variable - (UINTN) mNvVariableCache;\r
+\r
+  return EFI_SUCCESS;\r
+}\r