| /** @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; | |
| UINT64 NvStorageSize64; | |
| 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; | |
| Status = GetVariableFlashNvStorageInfo (&NvStorageBase, &NvStorageSize64); | |
| ASSERT_EFI_ERROR (Status); | |
| Status = SafeUint64ToUint32 (NvStorageSize64, &NvStorageSize); | |
| // This driver currently assumes the size will be UINT32 so assert the value is safe for now. | |
| ASSERT_EFI_ERROR (Status); | |
| ASSERT (NvStorageBase != 0); | |
| // | |
| // Allocate runtime memory used for a memory copy of the FLASH region. | |
| // Keep the memory and the FLASH in sync as updates occur. | |
| // | |
| NvStorageData = AllocateRuntimeZeroPool (NvStorageSize); | |
| if (NvStorageData == NULL) { | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| // | |
| // 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; | |
| } |