summaryrefslogtreecommitdiff
path: root/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableNonVolatile.c
diff options
context:
space:
mode:
authorMichael Kubacki <michael.a.kubacki@intel.com>2019-09-27 15:34:14 -0700
committerMichael Kubacki <michael.a.kubacki@intel.com>2019-11-05 21:55:54 -0800
commit6b0d7b013e5e58963db7cab7911c95ce06c509fa (patch)
tree7c76532814f02fc5322a6b096ec0f71026de2e57 /MdeModulePkg/Universal/Variable/RuntimeDxe/VariableNonVolatile.c
parent044010234e481e2d6167ff0d19f82a7ac863926c (diff)
MdeModulePkg/Variable: Add a file for NV variable functions
This change adds a dedicated file for variable operations specific to non-volatile variables. This decreases the overall length of the relatively large Variable.c file. Cc: Dandan Bi <dandan.bi@intel.com> Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org> Cc: Eric Dong <eric.dong@intel.com> Cc: Laszlo Ersek <lersek@redhat.com> Cc: Liming Gao <liming.gao@intel.com> Cc: Michael D Kinney <michael.d.kinney@intel.com> Cc: Ray Ni <ray.ni@intel.com> Cc: Jian J Wang <jian.j.wang@intel.com> Cc: Hao A Wu <hao.a.wu@intel.com> Cc: Jiewen Yao <jiewen.yao@intel.com> Signed-off-by: Michael Kubacki <michael.a.kubacki@intel.com> Reviewed-by: Jian J Wang <jian.j.wang@intel.com> Reviewed-by: Hao A Wu <hao.a.wu@intel.com>
Diffstat (limited to 'MdeModulePkg/Universal/Variable/RuntimeDxe/VariableNonVolatile.c')
-rw-r--r--MdeModulePkg/Universal/Variable/RuntimeDxe/VariableNonVolatile.c334
1 files changed, 334 insertions, 0 deletions
diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableNonVolatile.c b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableNonVolatile.c
new file mode 100644
index 0000000000..0637a828b3
--- /dev/null
+++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableNonVolatile.c
@@ -0,0 +1,334 @@
+/** @file
+ Common variable non-volatile store routines.
+
+Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "VariableNonVolatile.h"
+#include "VariableParsing.h"
+
+extern VARIABLE_MODULE_GLOBAL *mVariableModuleGlobal;
+
+/**
+ Get non-volatile maximum variable size.
+
+ @return Non-volatile maximum variable size.
+
+**/
+UINTN
+GetNonVolatileMaxVariableSize (
+ VOID
+ )
+{
+ if (PcdGet32 (PcdHwErrStorageSize) != 0) {
+ return MAX (MAX (PcdGet32 (PcdMaxVariableSize), PcdGet32 (PcdMaxAuthVariableSize)),
+ PcdGet32 (PcdMaxHardwareErrorVariableSize));
+ } else {
+ return MAX (PcdGet32 (PcdMaxVariableSize), PcdGet32 (PcdMaxAuthVariableSize));
+ }
+}
+
+/**
+ Init emulated non-volatile variable store.
+
+ @param[out] VariableStoreBase Output pointer to emulated non-volatile variable store base.
+
+ @retval EFI_SUCCESS Function successfully executed.
+ @retval EFI_OUT_OF_RESOURCES Fail to allocate enough memory resource.
+
+**/
+EFI_STATUS
+InitEmuNonVolatileVariableStore (
+ OUT EFI_PHYSICAL_ADDRESS *VariableStoreBase
+ )
+{
+ VARIABLE_STORE_HEADER *VariableStore;
+ UINT32 VariableStoreLength;
+ BOOLEAN FullyInitializeStore;
+ UINT32 HwErrStorageSize;
+
+ FullyInitializeStore = TRUE;
+
+ VariableStoreLength = PcdGet32 (PcdVariableStoreSize);
+ ASSERT (sizeof (VARIABLE_STORE_HEADER) <= VariableStoreLength);
+
+ //
+ // Allocate memory for variable store.
+ //
+ if (PcdGet64 (PcdEmuVariableNvStoreReserved) == 0) {
+ VariableStore = (VARIABLE_STORE_HEADER *) AllocateRuntimePool (VariableStoreLength);
+ if (VariableStore == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ } else {
+ //
+ // A memory location has been reserved for the NV variable store. Certain
+ // platforms may be able to preserve a memory range across system resets,
+ // thereby providing better NV variable emulation.
+ //
+ VariableStore =
+ (VARIABLE_STORE_HEADER *)(VOID*)(UINTN)
+ PcdGet64 (PcdEmuVariableNvStoreReserved);
+ if ((VariableStore->Size == VariableStoreLength) &&
+ (CompareGuid (&VariableStore->Signature, &gEfiAuthenticatedVariableGuid) ||
+ CompareGuid (&VariableStore->Signature, &gEfiVariableGuid)) &&
+ (VariableStore->Format == VARIABLE_STORE_FORMATTED) &&
+ (VariableStore->State == VARIABLE_STORE_HEALTHY)) {
+ DEBUG((
+ DEBUG_INFO,
+ "Variable Store reserved at %p appears to be valid\n",
+ VariableStore
+ ));
+ FullyInitializeStore = FALSE;
+ }
+ }
+
+ if (FullyInitializeStore) {
+ SetMem (VariableStore, VariableStoreLength, 0xff);
+ //
+ // Use gEfiAuthenticatedVariableGuid for potential auth variable support.
+ //
+ CopyGuid (&VariableStore->Signature, &gEfiAuthenticatedVariableGuid);
+ VariableStore->Size = VariableStoreLength;
+ VariableStore->Format = VARIABLE_STORE_FORMATTED;
+ VariableStore->State = VARIABLE_STORE_HEALTHY;
+ VariableStore->Reserved = 0;
+ VariableStore->Reserved1 = 0;
+ }
+
+ *VariableStoreBase = (EFI_PHYSICAL_ADDRESS) (UINTN) VariableStore;
+
+ HwErrStorageSize = PcdGet32 (PcdHwErrStorageSize);
+
+ //
+ // Note that in EdkII variable driver implementation, Hardware Error Record type variable
+ // is stored with common variable in the same NV region. So the platform integrator should
+ // ensure that the value of PcdHwErrStorageSize is less than the value of
+ // (VariableStoreLength - sizeof (VARIABLE_STORE_HEADER)).
+ //
+ ASSERT (HwErrStorageSize < (VariableStoreLength - sizeof (VARIABLE_STORE_HEADER)));
+
+ mVariableModuleGlobal->CommonVariableSpace = ((UINTN) VariableStoreLength - sizeof (VARIABLE_STORE_HEADER) - HwErrStorageSize);
+ mVariableModuleGlobal->CommonMaxUserVariableSpace = mVariableModuleGlobal->CommonVariableSpace;
+ mVariableModuleGlobal->CommonRuntimeVariableSpace = mVariableModuleGlobal->CommonVariableSpace;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Init real non-volatile variable store.
+
+ @param[out] VariableStoreBase Output pointer to real non-volatile variable store base.
+
+ @retval EFI_SUCCESS Function successfully executed.
+ @retval EFI_OUT_OF_RESOURCES Fail to allocate enough memory resource.
+ @retval EFI_VOLUME_CORRUPTED Variable Store or Firmware Volume for Variable Store is corrupted.
+
+**/
+EFI_STATUS
+InitRealNonVolatileVariableStore (
+ OUT EFI_PHYSICAL_ADDRESS *VariableStoreBase
+ )
+{
+ EFI_FIRMWARE_VOLUME_HEADER *FvHeader;
+ VARIABLE_STORE_HEADER *VariableStore;
+ UINT32 VariableStoreLength;
+ EFI_HOB_GUID_TYPE *GuidHob;
+ EFI_PHYSICAL_ADDRESS NvStorageBase;
+ UINT8 *NvStorageData;
+ UINT32 NvStorageSize;
+ FAULT_TOLERANT_WRITE_LAST_WRITE_DATA *FtwLastWriteData;
+ UINT32 BackUpOffset;
+ UINT32 BackUpSize;
+ UINT32 HwErrStorageSize;
+ UINT32 MaxUserNvVariableSpaceSize;
+ UINT32 BoottimeReservedNvVariableSpaceSize;
+ EFI_STATUS Status;
+ VOID *FtwProtocol;
+
+ mVariableModuleGlobal->FvbInstance = NULL;
+
+ //
+ // Allocate runtime memory used for a memory copy of the FLASH region.
+ // Keep the memory and the FLASH in sync as updates occur.
+ //
+ NvStorageSize = PcdGet32 (PcdFlashNvStorageVariableSize);
+ NvStorageData = AllocateRuntimeZeroPool (NvStorageSize);
+ if (NvStorageData == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ NvStorageBase = NV_STORAGE_VARIABLE_BASE;
+ ASSERT (NvStorageBase != 0);
+
+ //
+ // Copy NV storage data to the memory buffer.
+ //
+ CopyMem (NvStorageData, (UINT8 *) (UINTN) NvStorageBase, NvStorageSize);
+
+ Status = GetFtwProtocol ((VOID **)&FtwProtocol);
+ //
+ // If FTW protocol has been installed, no need to check FTW last write data hob.
+ //
+ if (EFI_ERROR (Status)) {
+ //
+ // Check the FTW last write data hob.
+ //
+ GuidHob = GetFirstGuidHob (&gEdkiiFaultTolerantWriteGuid);
+ if (GuidHob != NULL) {
+ FtwLastWriteData = (FAULT_TOLERANT_WRITE_LAST_WRITE_DATA *) GET_GUID_HOB_DATA (GuidHob);
+ if (FtwLastWriteData->TargetAddress == NvStorageBase) {
+ DEBUG ((DEBUG_INFO, "Variable: NV storage is backed up in spare block: 0x%x\n", (UINTN) FtwLastWriteData->SpareAddress));
+ //
+ // Copy the backed up NV storage data to the memory buffer from spare block.
+ //
+ CopyMem (NvStorageData, (UINT8 *) (UINTN) (FtwLastWriteData->SpareAddress), NvStorageSize);
+ } else if ((FtwLastWriteData->TargetAddress > NvStorageBase) &&
+ (FtwLastWriteData->TargetAddress < (NvStorageBase + NvStorageSize))) {
+ //
+ // Flash NV storage from the Offset is backed up in spare block.
+ //
+ BackUpOffset = (UINT32) (FtwLastWriteData->TargetAddress - NvStorageBase);
+ BackUpSize = NvStorageSize - BackUpOffset;
+ DEBUG ((DEBUG_INFO, "Variable: High partial NV storage from offset: %x is backed up in spare block: 0x%x\n", BackUpOffset, (UINTN) FtwLastWriteData->SpareAddress));
+ //
+ // Copy the partial backed up NV storage data to the memory buffer from spare block.
+ //
+ CopyMem (NvStorageData + BackUpOffset, (UINT8 *) (UINTN) FtwLastWriteData->SpareAddress, BackUpSize);
+ }
+ }
+ }
+
+ FvHeader = (EFI_FIRMWARE_VOLUME_HEADER *) NvStorageData;
+
+ //
+ // Check if the Firmware Volume is not corrupted
+ //
+ if ((FvHeader->Signature != EFI_FVH_SIGNATURE) || (!CompareGuid (&gEfiSystemNvDataFvGuid, &FvHeader->FileSystemGuid))) {
+ FreePool (NvStorageData);
+ DEBUG ((DEBUG_ERROR, "Firmware Volume for Variable Store is corrupted\n"));
+ return EFI_VOLUME_CORRUPTED;
+ }
+
+ VariableStore = (VARIABLE_STORE_HEADER *) ((UINTN) FvHeader + FvHeader->HeaderLength);
+ VariableStoreLength = NvStorageSize - FvHeader->HeaderLength;
+ ASSERT (sizeof (VARIABLE_STORE_HEADER) <= VariableStoreLength);
+ ASSERT (VariableStore->Size == VariableStoreLength);
+
+ //
+ // Check if the Variable Store header is not corrupted
+ //
+ if (GetVariableStoreStatus (VariableStore) != EfiValid) {
+ FreePool (NvStorageData);
+ DEBUG((DEBUG_ERROR, "Variable Store header is corrupted\n"));
+ return EFI_VOLUME_CORRUPTED;
+ }
+
+ mNvFvHeaderCache = FvHeader;
+
+ *VariableStoreBase = (EFI_PHYSICAL_ADDRESS) (UINTN) VariableStore;
+
+ HwErrStorageSize = PcdGet32 (PcdHwErrStorageSize);
+ MaxUserNvVariableSpaceSize = PcdGet32 (PcdMaxUserNvVariableSpaceSize);
+ BoottimeReservedNvVariableSpaceSize = PcdGet32 (PcdBoottimeReservedNvVariableSpaceSize);
+
+ //
+ // Note that in EdkII variable driver implementation, Hardware Error Record type variable
+ // is stored with common variable in the same NV region. So the platform integrator should
+ // ensure that the value of PcdHwErrStorageSize is less than the value of
+ // (VariableStoreLength - sizeof (VARIABLE_STORE_HEADER)).
+ //
+ ASSERT (HwErrStorageSize < (VariableStoreLength - sizeof (VARIABLE_STORE_HEADER)));
+ //
+ // Ensure that the value of PcdMaxUserNvVariableSpaceSize is less than the value of
+ // (VariableStoreLength - sizeof (VARIABLE_STORE_HEADER)) - PcdGet32 (PcdHwErrStorageSize).
+ //
+ ASSERT (MaxUserNvVariableSpaceSize < (VariableStoreLength - sizeof (VARIABLE_STORE_HEADER) - HwErrStorageSize));
+ //
+ // Ensure that the value of PcdBoottimeReservedNvVariableSpaceSize is less than the value of
+ // (VariableStoreLength - sizeof (VARIABLE_STORE_HEADER)) - PcdGet32 (PcdHwErrStorageSize).
+ //
+ ASSERT (BoottimeReservedNvVariableSpaceSize < (VariableStoreLength - sizeof (VARIABLE_STORE_HEADER) - HwErrStorageSize));
+
+ mVariableModuleGlobal->CommonVariableSpace = ((UINTN) VariableStoreLength - sizeof (VARIABLE_STORE_HEADER) - HwErrStorageSize);
+ mVariableModuleGlobal->CommonMaxUserVariableSpace = ((MaxUserNvVariableSpaceSize != 0) ? MaxUserNvVariableSpaceSize : mVariableModuleGlobal->CommonVariableSpace);
+ mVariableModuleGlobal->CommonRuntimeVariableSpace = mVariableModuleGlobal->CommonVariableSpace - BoottimeReservedNvVariableSpaceSize;
+
+ DEBUG ((
+ DEBUG_INFO,
+ "Variable driver common space: 0x%x 0x%x 0x%x\n",
+ mVariableModuleGlobal->CommonVariableSpace,
+ mVariableModuleGlobal->CommonMaxUserVariableSpace,
+ mVariableModuleGlobal->CommonRuntimeVariableSpace
+ ));
+
+ //
+ // The max NV variable size should be < (VariableStoreLength - sizeof (VARIABLE_STORE_HEADER)).
+ //
+ ASSERT (GetNonVolatileMaxVariableSize () < (VariableStoreLength - sizeof (VARIABLE_STORE_HEADER)));
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Init non-volatile variable store.
+
+ @retval EFI_SUCCESS Function successfully executed.
+ @retval EFI_OUT_OF_RESOURCES Fail to allocate enough memory resource.
+ @retval EFI_VOLUME_CORRUPTED Variable Store or Firmware Volume for Variable Store is corrupted.
+
+**/
+EFI_STATUS
+InitNonVolatileVariableStore (
+ VOID
+ )
+{
+ VARIABLE_HEADER *Variable;
+ VARIABLE_HEADER *NextVariable;
+ EFI_PHYSICAL_ADDRESS VariableStoreBase;
+ UINTN VariableSize;
+ EFI_STATUS Status;
+
+ if (PcdGetBool (PcdEmuVariableNvModeEnable)) {
+ Status = InitEmuNonVolatileVariableStore (&VariableStoreBase);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ mVariableModuleGlobal->VariableGlobal.EmuNvMode = TRUE;
+ DEBUG ((DEBUG_INFO, "Variable driver will work at emulated non-volatile variable mode!\n"));
+ } else {
+ Status = InitRealNonVolatileVariableStore (&VariableStoreBase);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ mVariableModuleGlobal->VariableGlobal.EmuNvMode = FALSE;
+ }
+
+ mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase = VariableStoreBase;
+ mNvVariableCache = (VARIABLE_STORE_HEADER *) (UINTN) VariableStoreBase;
+ mVariableModuleGlobal->VariableGlobal.AuthFormat = (BOOLEAN)(CompareGuid (&mNvVariableCache->Signature, &gEfiAuthenticatedVariableGuid));
+
+ mVariableModuleGlobal->MaxVariableSize = PcdGet32 (PcdMaxVariableSize);
+ mVariableModuleGlobal->MaxAuthVariableSize = ((PcdGet32 (PcdMaxAuthVariableSize) != 0) ? PcdGet32 (PcdMaxAuthVariableSize) : mVariableModuleGlobal->MaxVariableSize);
+
+ //
+ // Parse non-volatile variable data and get last variable offset.
+ //
+ Variable = GetStartPointer (mNvVariableCache);
+ while (IsValidVariableHeader (Variable, GetEndPointer (mNvVariableCache))) {
+ NextVariable = GetNextVariablePtr (Variable, mVariableModuleGlobal->VariableGlobal.AuthFormat);
+ VariableSize = (UINTN) NextVariable - (UINTN) Variable;
+ if ((Variable->Attributes & (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_HARDWARE_ERROR_RECORD)) == (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_HARDWARE_ERROR_RECORD)) {
+ mVariableModuleGlobal->HwErrVariableTotalSize += VariableSize;
+ } else {
+ mVariableModuleGlobal->CommonVariableTotalSize += VariableSize;
+ }
+
+ Variable = NextVariable;
+ }
+ mVariableModuleGlobal->NonVolatileLastVariableOffset = (UINTN) Variable - (UINTN) mNvVariableCache;
+
+ return EFI_SUCCESS;
+}