782 lines
25 KiB
C
782 lines
25 KiB
C
/** @file
|
|
Handles non-volatile variable store garbage collection, using FTW
|
|
(Fault Tolerant Write Lite) protocol.
|
|
|
|
;******************************************************************************
|
|
;* Copyright (c) 2012 - 2020, Insyde Software Corp. All Rights Reserved.
|
|
;*
|
|
;* You may not reproduce, distribute, publish, display, perform, modify, adapt,
|
|
;* transmit, broadcast, present, recite, release, license or otherwise exploit
|
|
;* any part of this publication in any form, by any means, without the prior
|
|
;* written permission of Insyde Software Corporation.
|
|
;*
|
|
;******************************************************************************
|
|
*/
|
|
|
|
|
|
#include "Variable.h"
|
|
#include "VariableCache.h"
|
|
#include <PostCode.h>
|
|
|
|
/**
|
|
Gets LBA of block and offset by given address."
|
|
|
|
This function gets the Logical Block Address (LBA) of a firmware
|
|
volume block containing the given address, and the offset of the
|
|
address on the block.
|
|
|
|
@param[in] Address Address which should be contained
|
|
by returned FVB handle.
|
|
@param[out] Lba Pointer to LBA for output.
|
|
@param[out] Offset Pointer to offset for output.
|
|
|
|
@retval EFI_SUCCESS LBA and offset successfully returned.
|
|
@retval EFI_NOT_FOUND Fail to find FVB handle by address.
|
|
@retval EFI_ABORTED Fail to find valid LBA and offset.
|
|
**/
|
|
EFI_STATUS
|
|
GetVariableStoreInfo (
|
|
IN EFI_PHYSICAL_ADDRESS VariableBase,
|
|
OUT EFI_PHYSICAL_ADDRESS *FvbBaseAddress,
|
|
OUT EFI_LBA *Lba,
|
|
OUT UINTN *Offset
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_HANDLE FvbHandle;
|
|
EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb;
|
|
EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader;
|
|
EFI_FV_BLOCK_MAP_ENTRY *FvbMapEntry;
|
|
UINT32 LbaIndex;
|
|
|
|
|
|
*Lba = (EFI_LBA) (-1);
|
|
*Offset = 0;
|
|
//
|
|
// Get the proper FVB
|
|
//
|
|
Status = GetFvbInfoByAddress (VariableBase, &FvbHandle, &Fvb);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
//
|
|
// Get the Base Address of FV
|
|
//
|
|
Status = Fvb->GetPhysicalAddress (Fvb, FvbBaseAddress);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *) ((UINTN) *FvbBaseAddress);
|
|
|
|
//
|
|
// Get the (LBA, Offset) of Address
|
|
//
|
|
if ((VariableBase >= *FvbBaseAddress) && (VariableBase <= (*FvbBaseAddress + FwVolHeader->FvLength))) {
|
|
if ((FwVolHeader->FvLength) > (FwVolHeader->HeaderLength)) {
|
|
//
|
|
// BUGBUG: Assume one FV has one type of BlockLength
|
|
//
|
|
FvbMapEntry = &FwVolHeader->BlockMap[0];
|
|
for (LbaIndex = 1; LbaIndex <= FvbMapEntry->NumBlocks; LbaIndex += 1) {
|
|
if (VariableBase < (*FvbBaseAddress + FvbMapEntry->Length * LbaIndex)) {
|
|
//
|
|
// Found the (Lba, Offset)
|
|
//
|
|
*Lba = LbaIndex - 1;
|
|
*Offset = (UINTN) (VariableBase - (*FvbBaseAddress + FvbMapEntry->Length * (LbaIndex - 1)));
|
|
return EFI_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return EFI_ABORTED;
|
|
}
|
|
|
|
|
|
/**
|
|
Writes a buffer to variable storage space, in the working block.
|
|
|
|
This function writes a buffer to variable storage space into a firmware
|
|
volume block device. The destination is specified by the parameter
|
|
VariableBase. Fault Tolerant Write protocol is used for writing.
|
|
|
|
@param[in] VariableBase Base address of the variable to write.
|
|
@param[in] Buffer Point to the data buffer.
|
|
|
|
@retval EFI_SUCCESS The function completed successfully.
|
|
@retval EFI_NOT_FOUND Fail to locate Fault Tolerant Write protocol.
|
|
@retval EFI_ABORTED The function could not complete successfully.
|
|
**/
|
|
EFI_STATUS
|
|
FtwVariableSpace (
|
|
IN EFI_PHYSICAL_ADDRESS VariableBase,
|
|
IN UINT8 *Buffer
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_FTW_LITE_PROTOCOL *FtwLiteProtocol;
|
|
EFI_LBA VarLba;
|
|
UINTN VarOffset;
|
|
UINT8 *FtwBuffer;
|
|
UINTN FtwBufferSize;
|
|
EFI_SMM_FTW_PROTOCOL *SmmFtwProtocol;
|
|
|
|
|
|
FtwBufferSize = GetNonVolatileVariableStoreSize ();
|
|
FtwBuffer = Buffer;
|
|
if (mSmst == NULL) {
|
|
//
|
|
// Prepare for the variable data
|
|
//
|
|
FtwLiteProtocol = mVariableReclaimInfo->FtwLiteProtocol;
|
|
VarOffset = mVariableReclaimInfo->VariableOffset;
|
|
VarLba = mVariableReclaimInfo->VariableLba;
|
|
//
|
|
// FTW write record
|
|
//
|
|
Status = FtwLiteProtocol->Write (
|
|
FtwLiteProtocol,
|
|
NULL,
|
|
VarLba, // LBA
|
|
VarOffset, // Offset
|
|
&FtwBufferSize, // NumBytes,
|
|
FtwBuffer
|
|
);
|
|
|
|
} else {
|
|
SmmFtwProtocol = NULL;
|
|
if (SmmFtwProtocol == NULL) {
|
|
Status = mSmst->SmmLocateProtocol (
|
|
&gEfiSmmFtwProtocolGuid,
|
|
NULL,
|
|
(VOID **)&SmmFtwProtocol
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
}
|
|
//
|
|
// FTW write record
|
|
//
|
|
Status = SmmFtwProtocol->Write(
|
|
SmmFtwProtocol,
|
|
mSmmVariableGlobal->VariableStoreInfo.FvbBaseAddress,
|
|
mSmmVariableGlobal->VariableStoreInfo.Lba,
|
|
mSmmVariableGlobal->VariableStoreInfo.Offset,
|
|
&FtwBufferSize,
|
|
FtwBuffer
|
|
);
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Variable store garbage collection and reclaim operation.
|
|
|
|
@param[in] VariableBase Base address of variable store
|
|
@param[in] LastVariableOffset Offset of last variable
|
|
@param[in] IsVolatile The variable store is volatile or not,
|
|
if it is non-volatile, need FTW
|
|
@param[in] CurrentVariable If it is not NULL, it means not to process
|
|
current variable for Reclaim.
|
|
|
|
@return EFI_SUCCESS Reclaim variable store successfully.
|
|
@return EFI_OUT_OF_RESOURCES Allocate pool failed or total variable size is large than variable store size.
|
|
@return Others Other error causes reclaim failed.
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
Reclaim (
|
|
IN EFI_PHYSICAL_ADDRESS VariableBase,
|
|
OUT UINTN *LastVariableOffset,
|
|
IN BOOLEAN IsVolatile,
|
|
IN OUT VARIABLE_HEADER **CurrentVariable OPTIONAL
|
|
)
|
|
{
|
|
VARIABLE_HEADER *Variable;
|
|
VARIABLE_HEADER *StartPtr;
|
|
VARIABLE_HEADER *EndPtr;
|
|
VARIABLE_HEADER *NextVariable;
|
|
VARIABLE_STORE_HEADER *VariableStoreHeader;
|
|
UINT8 *ValidBuffer;
|
|
UINTN VariableSize;
|
|
UINT8 *CurrPtr;
|
|
EFI_STATUS Status;
|
|
VARIABLE_HEADER *WorkingVariable;
|
|
UINTN TotalBufferSize;
|
|
UINTN CommonUserVariableTotalSize;
|
|
|
|
|
|
POST_CODE (DXE_VARIABLE_RECLAIM);
|
|
|
|
VariableStoreHeader = (VARIABLE_STORE_HEADER *) ((UINTN) VariableBase);
|
|
StartPtr = GetStartPointer (VariableStoreHeader);
|
|
Variable = StartPtr;
|
|
EndPtr = GetNonVolatileEndPointer (VariableStoreHeader);
|
|
|
|
CommonUserVariableTotalSize = 0;
|
|
//
|
|
// To make the reclaim, here we just allocate a memory that equal to the original memory
|
|
//
|
|
if (IsVolatile) {
|
|
TotalBufferSize = GetVariableStoreSize (VariableStoreHeader);
|
|
} else {
|
|
TotalBufferSize = GetNonVolatileVariableStoreSize ();
|
|
}
|
|
|
|
WorkingVariable = NULL;
|
|
ValidBuffer = (UINT8 *) mVariableReclaimInfo->BackupBuffer;
|
|
SetMem (ValidBuffer, TotalBufferSize, 0xff);
|
|
CurrPtr = ValidBuffer;
|
|
//
|
|
// Copy variable store header
|
|
//
|
|
CopyMem (CurrPtr, VariableStoreHeader, GetVariableStoreHeaderSize ());
|
|
CurrPtr += GetVariableStoreHeaderSize ();
|
|
for (Variable = GetStartPointer (VariableStoreHeader); IsValidVariableHeader (Variable); Variable = NextVariable) {
|
|
NextVariable = GetNextVariablePtr (Variable);
|
|
//
|
|
// Check the contents of this variable is in variable range.
|
|
//
|
|
if ((UINTN) NextVariable - (UINTN) VariableBase > TotalBufferSize) {
|
|
ASSERT (FALSE);
|
|
break;
|
|
}
|
|
//
|
|
// check variable name size is correct.
|
|
//
|
|
if (Variable->NameSize != StrSize (GET_VARIABLE_NAME_PTR (Variable))) {
|
|
ASSERT (FALSE);
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// State VAR_ADDED or VAR_IN_DELETED_TRANSITION are to kept,
|
|
// The CurrentVariable, is also saved, as SetVariable may fail duo to lack of space
|
|
//
|
|
if ((Variable->State == VAR_ADDED || Variable->State == (VAR_ADDED & VAR_IN_DELETED_TRANSITION)) &&
|
|
IsMostValueVariable (Variable, StartPtr, EndPtr)) {
|
|
VariableSize = (UINTN) NextVariable - (UINTN) Variable;
|
|
CopyMem (CurrPtr, (UINT8 *) Variable, VariableSize);
|
|
if (IsUserVariable ((CHAR16 *) (Variable + 1), &Variable->VendorGuid)) {
|
|
CommonUserVariableTotalSize += VariableSize;
|
|
}
|
|
//
|
|
// If CurrentVariable == Variable, mark as VAR_IN_DELETED_TRANSITION
|
|
//
|
|
if (Variable->State == (VAR_ADDED & VAR_IN_DELETED_TRANSITION) && CurrentVariable != NULL) {
|
|
if (Variable != *CurrentVariable){
|
|
((VARIABLE_HEADER *)CurrPtr)->State = VAR_ADDED;
|
|
} else {
|
|
WorkingVariable = (VARIABLE_HEADER *) ((UINTN) VariableBase + ((UINTN) CurrPtr - (UINTN) ValidBuffer));
|
|
}
|
|
} else {
|
|
((VARIABLE_HEADER *)CurrPtr)->State = VAR_ADDED;
|
|
}
|
|
CurrPtr += VariableSize;
|
|
}
|
|
}
|
|
|
|
if (IsVolatile) {
|
|
//
|
|
// If volatile variable store, just copy valid buffer
|
|
//
|
|
SetMem ((UINT8 *) (UINTN) VariableBase, GetVariableStoreSize (VariableStoreHeader), 0xff);
|
|
CopyMem ((UINT8 *) (UINTN) VariableBase, ValidBuffer, (UINTN) CurrPtr - (UINTN) ValidBuffer);
|
|
*LastVariableOffset = (UINTN) (CurrPtr - ValidBuffer);
|
|
Status = EFI_SUCCESS;
|
|
} else {
|
|
//
|
|
// If non-volatile variable store, perform FTW here.
|
|
//
|
|
Status = FtwVariableSpace (VariableBase, ValidBuffer);
|
|
if (!EFI_ERROR (Status)) {
|
|
if (mSmst == NULL) {
|
|
mVariableModuleGlobal->CommonUserVariableTotalSize = CommonUserVariableTotalSize;
|
|
} else {
|
|
mSmmVariableGlobal->ProtectedModeVariableModuleGlobal->CommonUserVariableTotalSize = CommonUserVariableTotalSize;
|
|
}
|
|
*LastVariableOffset = (UINTN) CurrPtr - (UINTN) ValidBuffer;
|
|
}
|
|
}
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
*LastVariableOffset = 0;
|
|
if (mSmst || !VariableAtRuntime ()) {
|
|
REPORT_STATUS_CODE (
|
|
EFI_ERROR_CODE,
|
|
EFI_SOFTWARE_EFI_RUNTIME_SERVICE | H2O_VARIABLE_RECLAIM_FAILED
|
|
);
|
|
}
|
|
}
|
|
if (!EFI_ERROR (Status) && CurrentVariable != NULL) {
|
|
*CurrentVariable = WorkingVariable;
|
|
}
|
|
if (!EFI_ERROR (Status) && !IsVolatile && mVariableModuleGlobal->NonVolatileVariableCache != NULL) {
|
|
SetMem (mVariableModuleGlobal->NonVolatileVariableCache, TotalBufferSize, 0xff);
|
|
CopyMem (
|
|
mVariableModuleGlobal->NonVolatileVariableCache,
|
|
(VOID *) (UINTN) VariableBase,
|
|
(UINTN) (CurrPtr - ValidBuffer)
|
|
);
|
|
SetNonVolatileVariableCacheCrc32 ();
|
|
} else if (!IsVolatile && mVariableModuleGlobal->NonVolatileVariableCache != NULL) {
|
|
//
|
|
// Turn off variable cache, if reclaim NV data failed
|
|
//
|
|
DisableVariableCache ();
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Check the variable is whether in the preserved variable table or not.
|
|
|
|
@param[In] Variable pointer to the variable
|
|
|
|
@retval TRUE the variable is in the preserved variable table
|
|
@retval FALSE the variable is not in the preserved variable table
|
|
|
|
**/
|
|
STATIC
|
|
BOOLEAN
|
|
IsVariableInKeepList (
|
|
VARIABLE_HEADER *Variable
|
|
)
|
|
{
|
|
UINTN Index;
|
|
|
|
for (Index = 0; mVariableModuleGlobal->PreservedVariableTable[Index].VariableName != NULL; Index++) {
|
|
if ((CompareGuid (&Variable->VendorGuid, &mVariableModuleGlobal->PreservedVariableTable[Index].VendorGuid)) &&
|
|
(StrCmp (GET_VARIABLE_NAME_PTR (Variable), mVariableModuleGlobal->PreservedVariableTable[Index].VariableName) == 0)) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
Check the variable is whether authenticated or not.
|
|
|
|
@param[In] Variable pointer to the variable
|
|
|
|
@retval TRUE the variable is authenticated
|
|
@retval FALSE the variable is not authenticated
|
|
|
|
**/
|
|
STATIC
|
|
BOOLEAN
|
|
IsAuthenticatedVariable (
|
|
VARIABLE_HEADER *Variable
|
|
)
|
|
{
|
|
|
|
if (((Variable->Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) == EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) ||
|
|
((Variable->Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) == EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS)) {
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/**
|
|
This function elimates all the variables except authenticated variables and
|
|
the variables form mVariableModuleGlobal->PreservedVariableTable.
|
|
|
|
@param[In] ValidBuffer pointer to the valid variable buffer
|
|
@param[In] TotalBufferSize the total size of the variable region
|
|
@param[Out] ValidVariableSize pointer to the size of the valid variable
|
|
|
|
**/
|
|
STATIC
|
|
VOID
|
|
ElimateVariableWhenRegionFull (
|
|
IN UINT8 *ValidBuffer,
|
|
IN UINTN TotalBufferSize,
|
|
OUT UINTN *ValidVariableSize
|
|
)
|
|
{
|
|
VARIABLE_HEADER *Variable;
|
|
VARIABLE_HEADER *NextVariable;
|
|
UINTN VariableSize;
|
|
UINT8 *ValidVariablePtr;
|
|
|
|
|
|
if (mVariableModuleGlobal->PreservedVariableTable == NULL) {
|
|
return;
|
|
}
|
|
|
|
Variable = (VARIABLE_HEADER *) (ValidBuffer + GetVariableStoreHeaderSize ());
|
|
|
|
ValidVariablePtr = (UINT8 *) Variable;
|
|
VariableSize = 0;
|
|
while (IsValidVariableHeader (Variable)) {
|
|
NextVariable = GetNextVariablePtr (Variable);
|
|
VariableSize = (UINTN) NextVariable - (UINTN) Variable;
|
|
if (IsVariableInKeepList (Variable) || IsAuthenticatedVariable (Variable)) {
|
|
if (ValidVariablePtr != (UINT8 *) Variable) {
|
|
CopyMem (ValidVariablePtr, Variable, VariableSize);
|
|
}
|
|
ValidVariablePtr = ValidVariablePtr + VariableSize;
|
|
}
|
|
Variable = NextVariable;
|
|
}
|
|
|
|
*ValidVariableSize = (UINTN) (ValidVariablePtr - ValidBuffer);
|
|
SetMem (ValidVariablePtr, TotalBufferSize - *ValidVariableSize, 0xFF);
|
|
}
|
|
|
|
|
|
/**
|
|
non-volatile variable store garbage collection and reclaim operation
|
|
|
|
@param[in] Variable Pointer to new added variable.
|
|
@param[in] VarSize The size of new added variable size.
|
|
@param[in] VariableBase Base address of variable store
|
|
@param[out] LastVariableOffset Offset of last variable
|
|
|
|
@retval EFI_SUCCESS Reclaim non-volatile variable store successfully.
|
|
@retval EFI_INVALID_PARAMETER Any input parameter is invalid.
|
|
@retval EFI_OUT_OF_RESOURCES Allocate pool failed or total variable size is large than variable store size.
|
|
@retval EFI_DEVICE_ERROR Unable to reclaim non-volatile variable caused by flash device error.
|
|
--*/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
ReclaimNonVolatileVariable (
|
|
IN VARIABLE_HEADER *NewVariable,
|
|
IN UINTN NewVarSize,
|
|
IN EFI_PHYSICAL_ADDRESS VariableBase,
|
|
OUT UINTN *LastVariableOffset
|
|
)
|
|
{
|
|
VARIABLE_HEADER *Variable;
|
|
VARIABLE_HEADER *StartPtr;
|
|
VARIABLE_HEADER *EndPtr;
|
|
VARIABLE_HEADER *NextVariable;
|
|
VARIABLE_STORE_HEADER *VariableStoreHeader;
|
|
UINT8 *ValidBuffer;
|
|
UINTN ValidBufferSize;
|
|
UINTN VariableSize;
|
|
UINTN TotalBufferSize;
|
|
UINT8 *CurrPtr;
|
|
EFI_STATUS Status;
|
|
UINTN CommonUserVariableTotalSize;
|
|
|
|
POST_CODE (DXE_VARIABLE_RECLAIM);
|
|
if (NewVariable == NULL || LastVariableOffset == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
CommonUserVariableTotalSize = 0;
|
|
TotalBufferSize = GetNonVolatileVariableStoreSize ();
|
|
if (mVariableModuleGlobal->NonVolatileVariableCache == NULL) {
|
|
VariableStoreHeader = (VARIABLE_STORE_HEADER *) ((UINTN) VariableBase);
|
|
} else {
|
|
CopyMem (
|
|
mVariableModuleGlobal->NonVolatileVariableCache,
|
|
(VOID *) (UINTN) VariableBase,
|
|
TotalBufferSize
|
|
);
|
|
VariableStoreHeader = (VARIABLE_STORE_HEADER *) ((UINTN) mVariableModuleGlobal->NonVolatileVariableCache);
|
|
SetNonVolatileVariableCacheCrc32 ();
|
|
}
|
|
StartPtr = GetStartPointer (VariableStoreHeader);
|
|
Variable = StartPtr;
|
|
EndPtr = GetNonVolatileEndPointer (VariableStoreHeader);
|
|
|
|
ValidBuffer = (UINT8 *) mVariableReclaimInfo->BackupBuffer;
|
|
SetMem (ValidBuffer, TotalBufferSize, 0xff);
|
|
|
|
CurrPtr = ValidBuffer;
|
|
|
|
//
|
|
// Copy variable store header
|
|
//
|
|
CopyMem (CurrPtr, VariableStoreHeader, GetVariableStoreHeaderSize ());
|
|
CurrPtr += GetVariableStoreHeaderSize ();
|
|
for (Variable = GetStartPointer (VariableStoreHeader); IsValidVariableHeader (Variable); Variable = NextVariable) {
|
|
NextVariable = GetNextVariablePtr (Variable);
|
|
//
|
|
// Check the contents of this variable is in variable range.
|
|
//
|
|
if ((UINTN) NextVariable - (UINTN) VariableStoreHeader > TotalBufferSize) {
|
|
ASSERT (FALSE);
|
|
break;
|
|
}
|
|
//
|
|
// check variable name size is correct.
|
|
//
|
|
if (Variable->NameSize != StrSize (GET_VARIABLE_NAME_PTR (Variable))) {
|
|
ASSERT (FALSE);
|
|
continue;
|
|
}
|
|
//
|
|
// State VAR_ADDED or VAR_IN_DELETED_TRANSITION are to kept,
|
|
// The CurrentVariable, is also saved, as SetVariable may fail due to lack of space
|
|
//
|
|
if ((Variable->State == VAR_ADDED || Variable->State == (VAR_ADDED & VAR_IN_DELETED_TRANSITION)) &&
|
|
!(CompareGuid (&NewVariable->VendorGuid, &Variable->VendorGuid) &&
|
|
!StrCmp (GET_VARIABLE_NAME_PTR (NewVariable), GET_VARIABLE_NAME_PTR (Variable))) &&
|
|
IsMostValueVariable (Variable, StartPtr, EndPtr)) {
|
|
VariableSize = (UINTN) NextVariable - (UINTN) Variable;
|
|
CopyMem (CurrPtr, (UINT8 *) Variable, VariableSize);
|
|
((VARIABLE_HEADER *) CurrPtr)->State = VAR_ADDED;
|
|
CurrPtr += VariableSize;
|
|
if (IsUserVariable ((CHAR16 *) (Variable + 1), &Variable->VendorGuid)) {
|
|
CommonUserVariableTotalSize += VariableSize;
|
|
}
|
|
}
|
|
}
|
|
|
|
ValidBufferSize = (UINTN) (CurrPtr - ValidBuffer);
|
|
if (ValidBufferSize + NewVarSize > TotalBufferSize) {
|
|
if (mSmst || !VariableAtRuntime ()) {
|
|
REPORT_STATUS_CODE (
|
|
EFI_ERROR_CODE,
|
|
EFI_SOFTWARE_EFI_RUNTIME_SERVICE | H2O_VARIABLE_STORE_FULL
|
|
);
|
|
}
|
|
ElimateVariableWhenRegionFull (ValidBuffer, TotalBufferSize, &ValidBufferSize);
|
|
if (ValidBufferSize + NewVarSize > TotalBufferSize) {
|
|
if (mSmst != NULL) {
|
|
EFI_FREE_POOL (ValidBuffer);
|
|
}
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
CurrPtr = ValidBuffer + ValidBufferSize;
|
|
}
|
|
|
|
|
|
CopyMem (CurrPtr, NewVariable, NewVarSize);
|
|
((VARIABLE_HEADER *) CurrPtr)->State &= VAR_ADDED;
|
|
CurrPtr += NewVarSize;
|
|
//
|
|
// If non-volatile variable store, perform FTW here.
|
|
//
|
|
Status = FtwVariableSpace (VariableBase, ValidBuffer);
|
|
*LastVariableOffset = 0;
|
|
if (!EFI_ERROR (Status)) {
|
|
if (mSmst == NULL) {
|
|
mVariableModuleGlobal->CommonUserVariableTotalSize = CommonUserVariableTotalSize;
|
|
} else {
|
|
mSmmVariableGlobal->ProtectedModeVariableModuleGlobal->CommonUserVariableTotalSize = CommonUserVariableTotalSize;
|
|
}
|
|
*LastVariableOffset = (UINTN) CurrPtr - (UINTN) ValidBuffer;
|
|
} else if (mSmst || !VariableAtRuntime ()) {
|
|
REPORT_STATUS_CODE (
|
|
EFI_ERROR_CODE,
|
|
EFI_SOFTWARE_EFI_RUNTIME_SERVICE | H2O_VARIABLE_RECLAIM_FAILED
|
|
);
|
|
}
|
|
|
|
if (!EFI_ERROR (Status) && mVariableModuleGlobal->NonVolatileVariableCache != NULL) {
|
|
SetMem (mVariableModuleGlobal->NonVolatileVariableCache, TotalBufferSize, 0xff);
|
|
CopyMem (
|
|
mVariableModuleGlobal->NonVolatileVariableCache,
|
|
(VOID *) (UINTN) VariableBase,
|
|
(UINTN) CurrPtr - (UINTN) ValidBuffer
|
|
);
|
|
SetNonVolatileVariableCacheCrc32 ();
|
|
} else if (mVariableModuleGlobal->NonVolatileVariableCache != NULL){
|
|
//
|
|
// reclaim failed, turn off variable chache to prevent from data corrupted
|
|
//
|
|
DisableVariableCache ();
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Internal function to check the existence of VARIABLE_RECLAIM_THRESHOLD variable.
|
|
|
|
@retval TRUE VARIABLE_RECLAIM_THRESHOLD variable exists.
|
|
@retval FALSE VARIABLE_RECLAIM_THRESHOLD variable doesn't exist.
|
|
**/
|
|
BOOLEAN
|
|
DoesNeedDoReclaimVariableExist (
|
|
VOID
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
VARIABLE_POINTER_TRACK Variable;
|
|
UINTN VariableCount;
|
|
|
|
|
|
Status = FindVariableByLifetime (
|
|
NEED_DO_RECLAIM_NAME,
|
|
&gEfiGenericVariableGuid,
|
|
&Variable,
|
|
&VariableCount,
|
|
&mVariableModuleGlobal->VariableBase
|
|
);
|
|
|
|
return EFI_ERROR (Status) ? FALSE : TRUE;
|
|
|
|
}
|
|
|
|
/**
|
|
Internal function to delete VARIABLE_RECLAIM_THRESHOLD variable.
|
|
|
|
@retval EFI_SUCCESS VARIABLE_RECLAIM_THRESHOLD variable exists.
|
|
@return Other VARIABLE_RECLAIM_THRESHOLD variable doesn't exist.
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
DeleteNeedDoReclaimVariable (
|
|
VOID
|
|
)
|
|
|
|
{
|
|
EFI_STATUS Status;
|
|
VARIABLE_POINTER_TRACK Variable;
|
|
UINTN VariableCount;
|
|
|
|
|
|
Status = FindVariableByLifetime (
|
|
NEED_DO_RECLAIM_NAME,
|
|
&gEfiGenericVariableGuid,
|
|
&Variable,
|
|
&VariableCount,
|
|
&mVariableModuleGlobal->VariableBase
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
return UpdateVariable (
|
|
NEED_DO_RECLAIM_NAME,
|
|
&gEfiGenericVariableGuid,
|
|
NULL,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
&Variable,
|
|
NULL,
|
|
&mVariableModuleGlobal->VariableBase
|
|
);
|
|
}
|
|
|
|
|
|
/**
|
|
Make sure the remaining non-volatile variable space is larger than
|
|
VARIABLE_RECLAIM_THRESHOLD before boot to OS
|
|
**/
|
|
VOID
|
|
EFIAPI
|
|
ReclaimForOS (
|
|
VOID
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
STATIC BOOLEAN Reclaimed;
|
|
|
|
//
|
|
// This function will be called only once at EndOfDxe or ReadyToBoot event.
|
|
//
|
|
if (Reclaimed) {
|
|
return;
|
|
}
|
|
Reclaimed = TRUE;
|
|
mVariableModuleGlobal->NonVolatileLastVariableOffset = GetCurrentNonVolatileOffset ();
|
|
//
|
|
// Check if the free area is blow a threshold
|
|
//
|
|
if ((GetNonVolatileVariableStoreSize () - mVariableModuleGlobal->NonVolatileLastVariableOffset) < VARIABLE_RECLAIM_THRESHOLD ||
|
|
DoesNeedDoReclaimVariableExist ()) {
|
|
Status = Reclaim (
|
|
mVariableModuleGlobal->VariableBase.NonVolatileVariableBase,
|
|
&mVariableModuleGlobal->NonVolatileLastVariableOffset,
|
|
FALSE,
|
|
NULL
|
|
);
|
|
ASSERT(!EFI_ERROR(Status));
|
|
if (!EFI_ERROR (Status) && DoesNeedDoReclaimVariableExist ()) {
|
|
DeleteNeedDoReclaimVariable ();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
Internal function to check VARIABLE_RECLAIM_THRESHOLD or set NEED_DO_RECLAIM_NAME variable to make sure system will
|
|
do reclaim in next boot.
|
|
|
|
@retval EFI_SUCCESS Nonvolatile variable free space is smaller than VARIABLE_RECLAIM_THRESHOLD or
|
|
set NEED_DO_RECLAIM_NAME variable successfully.
|
|
@return Other Any error occurred while writing NEED_DO_RECLAIM_NAME variable.
|
|
**/
|
|
EFI_STATUS
|
|
SetDoReclaimNextBoot (
|
|
VOID
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
VARIABLE_POINTER_TRACK Variable;
|
|
UINTN VariableCount;
|
|
UINT8 Data;
|
|
VARIABLE_GLOBAL *Global;
|
|
|
|
//
|
|
// System will do reclaim next boot if free area is below a threshold, so just return EFI_SUCCESS if free is below
|
|
// a threshold.
|
|
//
|
|
if ((GetNonVolatileVariableStoreSize () - GetCurrentNonVolatileOffset ()) < VARIABLE_RECLAIM_THRESHOLD) {
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// return EFI_SUCCESS directly if Need do reclaim variable is set
|
|
//
|
|
Global = &mVariableModuleGlobal->VariableBase;
|
|
Status = FindVariableByLifetime (
|
|
NEED_DO_RECLAIM_NAME,
|
|
&gEfiGenericVariableGuid,
|
|
&Variable,
|
|
&VariableCount,
|
|
Global
|
|
);
|
|
if (!EFI_ERROR (Status)) {
|
|
return EFI_SUCCESS;
|
|
}
|
|
//
|
|
// Write NEED_DO_RECLAIM_NAME variable to indicate system need do reclaim next POST.
|
|
//
|
|
Data = 1;
|
|
return UpdateVariable (
|
|
NEED_DO_RECLAIM_NAME,
|
|
&gEfiGenericVariableGuid,
|
|
&Data,
|
|
sizeof (UINT8),
|
|
EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
|
|
0,
|
|
0,
|
|
&Variable,
|
|
NULL,
|
|
Global
|
|
);
|
|
}
|
|
|
|
/**
|
|
Convert all of preserved variable table relative pointers to virtual address.
|
|
**/
|
|
VOID
|
|
PreservedTableAddressChange (
|
|
VOID
|
|
)
|
|
{
|
|
UINTN Index;
|
|
|
|
if (mVariableModuleGlobal->PreservedVariableTable == NULL) {
|
|
return;
|
|
}
|
|
|
|
for (Index = 0; mVariableModuleGlobal->PreservedVariableTable[Index].VariableName != NULL; Index++) {
|
|
gRT->ConvertPointer (0x0, (VOID **) &mVariableModuleGlobal->PreservedVariableTable[Index].VariableName);
|
|
}
|
|
gRT->ConvertPointer (0x0, (VOID **) &mVariableModuleGlobal->PreservedVariableTable);
|
|
} |