blob: 9e2d8fe0fe0cc1ccd2608775c6bbadaf1abf484d [file] [log] [blame]
/** @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;
}