/** @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; | |
} |