/** @file Common variable non-volatile store routines. Copyright (c) 2019, Intel Corporation. All rights reserved.
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; }