--- /dev/null
+/** @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