alder_lake_bios/Insyde/InsydeModulePkg/Universal/Variable/VariableRuntimeDxe/Variable.c

4641 lines
162 KiB
C

/** @file
Provide support functions for variable services.
;******************************************************************************
;* Copyright (c) 2012 - 2021, 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 "AuthService.h"
#include "InsydeSecureVariable.h"
#include "VariableLock.h"
#include "VarCheck.h"
#include "SensitiveVariable.h"
#include "VariableCache.h"
#include "RestoreFactoryDefault.h"
#include "VarDefault.h"
#include "VariablePolicy.h"
#include <PostCode.h>
#include <Library/FlashRegionLib.h>
#define VARIABLE_WORKING_REGION_HOB_GUID {0x92888eba, 0xe125, 0x4c41, 0xbc, 0x9d, 0x68, 0x3e, 0x8f, 0x7e, 0x61, 0x18}
STATIC EFI_GUID mVariableWorkingRegionHobGuid = VARIABLE_WORKING_REGION_HOB_GUID;
extern EFI_GUID gEfiAlternateFvBlockGuid;
UINTN mCommunicateBuffer = SMM_COMMUNICATE_BUFFER_SIZE;
typedef struct {
EFI_PHYSICAL_ADDRESS VariableWorkingRegionStart;
EFI_PHYSICAL_ADDRESS VariableWorkingRegionLength;
} VARIABLE_WORKING_REGION_INFO;
EFI_SMM_COMMUNICATE_HEADER *mSmmPhyCommunicationBuffer;
VARIABLE_RECLAIM_INFO *mVariableReclaimInfo;
BOOLEAN mDxeSmmReadyToLockBeforeTriggered;
EFI_SMM_COMMUNICATION_PROTOCOL *mSmmCommunication;
/**
Delete specific variable in HOB
@param[in] VariableName Name of variable has been updated or deleted.
@param[in] VendorGuid Guid of variable has been updated or deleted.
**/
STATIC
VOID
DeleteVariableInHob (
IN CONST CHAR16 *VariableName,
IN CONST EFI_GUID *VendorGuid
)
{
VARIABLE_STORE_HEADER *VariableStoreHeader;
VARIABLE_HEADER *Variable;
if (VariableName == NULL || VendorGuid == NULL) {
return;
}
if (mVariableModuleGlobal->HobVariableBase == 0) {
return;
}
VariableStoreHeader = (VARIABLE_STORE_HEADER *) (UINTN) mVariableModuleGlobal->HobVariableBase;
for (Variable = GetStartPointer (VariableStoreHeader);
IsValidVariableHeader (Variable) && Variable < GetEndPointer (VariableStoreHeader);
Variable = GetNextVariablePtr (Variable)) {
if (CompareGuid (VendorGuid, &Variable->VendorGuid) &&
StrCmp (VariableName, GET_VARIABLE_NAME_PTR (Variable)) == 0) {
Variable->State &= VAR_DELETED;
return;
}
}
return;
}
/**
Function to check the input address is whether in variable HOB region.
@param[in] Address Input physical address to check.
@retval TRUE The address is in the variable default region.
@retval FALSE The address isn't in the variable default region.
**/
STATIC
BOOLEAN
AddressInVariableHobRegion (
IN PHYSICAL_ADDRESS Address
)
{
if (mVariableModuleGlobal->HobVariableBase == 0) {
return FALSE;
}
if (Address >= (UINTN) mVariableModuleGlobal->HobVariableBase &&
Address < (UINTN) mVariableModuleGlobal->HobVariableBase + mVariableModuleGlobal->HobVariableSize) {
return TRUE;
}
return FALSE;
}
/**
This code gets the size of non-volatile variable store.
@return UINTN The size of non-volatile variable store.
**/
UINTN
GetNonVolatileVariableStoreSize (
VOID
)
{
return mVariableModuleGlobal->NonVolatileVariableCacheSize;
}
/**
This code gets the pointer to the last variable memory pointer byte
@param [in] VarStoreHeader Pointer to the Variable Store Header.
@return Pointer to last unavailable Variable Header.
**/
VARIABLE_HEADER *
GetNonVolatileEndPointer (
IN VARIABLE_STORE_HEADER *VarStoreHeader
)
{
return (VARIABLE_HEADER *) ((UINTN) VarStoreHeader + GetNonVolatileVariableStoreSize ());
}
/**
This code checks if variable header is valid or not and is whether whole data in variable region.
@param[in] Variable Pointer to the Variable Header.
@param[in] VariableStoreEnd Pointer to the Variable store end.
@retval TRUE Variable header is valid and in variable region.
@retval FALSE Variable header is not valid or isn't in variable region.
**/
BOOLEAN
IsValidVariableHeaderInVarRegion (
IN CONST VARIABLE_HEADER *Variable,
IN CONST VARIABLE_HEADER *VariableStoreEnd
)
{
if (Variable == NULL || VariableStoreEnd == NULL) {
return FALSE;
}
if (!IsValidVariableHeader (Variable) || (UINTN) GetNextVariablePtr (Variable) > (UINTN) VariableStoreEnd) {
return FALSE;
}
return TRUE;
}
/**
Check the saved data and the input variable data is whether the same.
@param[in] VariableBase Base address of variable store
@param[in] VariableOffset The offset of current non-volatile variable
@param[in] Variable Pointer to the Variable Store Header.
@retval TRUE This variable is correct.
@retval FALSE This variable is incorrect.
**/
BOOLEAN
EFIAPI
IsCorrectVariable (
IN EFI_PHYSICAL_ADDRESS VariableBase,
IN UINTN VariableOffset,
IN VARIABLE_HEADER *Variable
)
{
VARIABLE_HEADER *CurrentVariable;
EFI_PHYSICAL_ADDRESS ReadPtr;
UINT32 WholeVariableSize;
//
// Initialize local variable
//
CurrentVariable = Variable;
ReadPtr = VariableBase + VariableOffset;
//
// Calculate variable size from current variable
//
WholeVariableSize = 0;
WholeVariableSize = sizeof (VARIABLE_HEADER) + CurrentVariable->NameSize + CurrentVariable->DataSize;
//
// Check variable is whether correct
//
return (CompareMem ((UINT8 *) ((UINTN) ReadPtr), Variable, WholeVariableSize) == 0) ? TRUE : FALSE;
}
/**
Internal function to get the start address of most value variable.
The most value variable rule is:
1. If sysmte has the specific variable with VAR_ADDED state, using the the last variable with VAR_ADDED state.
2. If system doesn't have specific variable with VAR_ADDED but has the same variable with VAR_ADDED & VAR_IN_DELETED_TRANSITION
state, using the last variable with VAR_ADDED & VAR_IN_DELETED_TRANSITION state.
3. Otherwise, return NULL.
@param[in] Variable Pointer to start position
@return Pointer to the most value variable or return NULL if not found.
**/
STATIC
VARIABLE_HEADER *
GetMostValueVariable (
IN CONST VARIABLE_POINTER_TRACK *Variable
)
{
VARIABLE_HEADER *CurrentHeader;
VARIABLE_HEADER *MostValueHeader;
MostValueHeader = NULL;
CurrentHeader = Variable->StartPtr;
while (IsValidVariableHeaderInVarRegion (CurrentHeader, Variable->EndPtr)) {
if (CurrentHeader->State == VAR_ADDED || CurrentHeader->State == (VAR_ADDED & VAR_IN_DELETED_TRANSITION)) {
if (CompareGuid (&Variable->CurrPtr->VendorGuid, &CurrentHeader->VendorGuid) &&
StrCmp (GET_VARIABLE_NAME_PTR (Variable->CurrPtr), GET_VARIABLE_NAME_PTR (CurrentHeader)) == 0) {
if (MostValueHeader == NULL) {
MostValueHeader = CurrentHeader;
} else {
if (MostValueHeader->State == VAR_ADDED) {
if (CurrentHeader->State == VAR_ADDED) {
MostValueHeader = CurrentHeader;
if (mSmst || !VariableAtRuntime ()) {
REPORT_STATUS_CODE (
EFI_ERROR_CODE,
EFI_SOFTWARE_EFI_RUNTIME_SERVICE | H2O_VARIABLE_DUPLICATION
);
}
}
} else {
MostValueHeader = CurrentHeader;
}
}
}
}
CurrentHeader = GetNextVariablePtr (CurrentHeader);
}
return MostValueHeader;
}
/**
This function writes data to the FWH at the correct LBA even if the LBAs
are fragmented.
@param[in] Global Pointer to VARIABLE_GLOBAL structure.
@param[in] Volatile If the Variable is Volatile or Non-Volatile
@param[in] SetByIndex TRUE: Target pointer is given as index.
FALSE: Target pointer is absolute.
@param[in] DataPtrIndex Pointer to the Data from the end of VARIABLE_STORE_HEADER
structure.
@param[in] DataSize Size of data to be written.
@param[in] Buffer Pointer to the buffer from which data is written.
@retval EFI_SUCCESS Variable store successfully updated.
@retval EFI_INVALID_PARAMETER Parameters not valid.
--*/
EFI_STATUS
UpdateVariableStore (
IN VARIABLE_GLOBAL *Global,
IN BOOLEAN Volatile,
IN BOOLEAN SetByIndex,
IN UINTN DataPtrIndex,
IN UINT32 DataSize,
IN UINT8 *Buffer
)
{
EFI_FV_BLOCK_MAP_ENTRY *PtrBlockMapEntry;
UINTN BlockIndex2;
UINTN LinearOffset;
UINTN CurrWriteSize;
UINTN CurrWritePtr;
UINT8 *CurrBuffer;
EFI_LBA LbaNumber;
UINTN Size;
EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader;
VARIABLE_STORE_HEADER *VolatileBase;
EFI_PHYSICAL_ADDRESS FvVolHdr;
EFI_PHYSICAL_ADDRESS DataPtr;
EFI_STATUS Status;
UINTN WriteDataSize;
BOOLEAN FvFound;
EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb;
Status = EFI_SUCCESS;
DataPtr = DataPtrIndex;
if (Volatile) {
//
// Update volatile variable in SMM mode or protected mode
//
Status = EFI_INVALID_PARAMETER;
VolatileBase = (VARIABLE_STORE_HEADER *) ((UINTN) Global->VolatileVariableBase);
if (SetByIndex) {
DataPtr += Global->VolatileVariableBase;
}
if ((DataPtr + DataSize) <= ((UINTN)VolatileBase + GetVariableStoreSize (VolatileBase))) {
//
// If Volatile Variable just do a simple mem copy.
//
CopyMem ((UINT8 *) ((UINTN) DataPtr), Buffer, DataSize);
Status = EFI_SUCCESS;
}
} else if (mSmst == NULL) {
//
// update non-volatile variable in protected mode
//
Fvb = mVariableModuleGlobal->FvbInstance;
Status = Fvb->GetPhysicalAddress(Fvb, &FvVolHdr);
ASSERT_EFI_ERROR (Status);
FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *) ((UINTN) FvVolHdr);
//
// Data Pointer should point to the actual Address where data is to be
// written
//
if (SetByIndex) {
DataPtr += Global->NonVolatileVariableBase;
}
if ((DataPtr + DataSize) > ((EFI_PHYSICAL_ADDRESS) (UINTN) ((UINT8 *) FwVolHeader + FwVolHeader->FvLength))) {
return EFI_INVALID_PARAMETER;
}
//
// If we are here we are dealing with Non-Volatile Variables
//
LinearOffset = (UINTN) FwVolHeader;
CurrWritePtr = (UINTN) DataPtr;
CurrWriteSize = DataSize;
CurrBuffer = Buffer;
LbaNumber = 0;
if (CurrWritePtr < LinearOffset) {
return EFI_INVALID_PARAMETER;
}
FvFound = FALSE;
for (PtrBlockMapEntry = FwVolHeader->BlockMap; PtrBlockMapEntry->NumBlocks != 0; PtrBlockMapEntry++) {
for (BlockIndex2 = 0; BlockIndex2 < PtrBlockMapEntry->NumBlocks; BlockIndex2++) {
//
// Check to see if the Variable Writes are spanning through multiple
// blocks.
//
if ((CurrWritePtr >= LinearOffset) && (CurrWritePtr < LinearOffset + PtrBlockMapEntry->Length)) {
FvFound = TRUE;
if ((CurrWritePtr + CurrWriteSize) <= (LinearOffset + PtrBlockMapEntry->Length)) {
Status = Fvb->Write (
Fvb,
LbaNumber,
(UINTN) (CurrWritePtr - LinearOffset),
&CurrWriteSize,
CurrBuffer
);
break;
} else {
Size = (UINT32) (LinearOffset + PtrBlockMapEntry->Length - CurrWritePtr);
Status = Fvb->Write (
Fvb,
LbaNumber,
(UINTN) (CurrWritePtr - LinearOffset),
&Size,
CurrBuffer
);
if (EFI_ERROR (Status)) {
break;
}
CurrWritePtr = LinearOffset + PtrBlockMapEntry->Length;
CurrBuffer = CurrBuffer + Size;
CurrWriteSize = CurrWriteSize - Size;
}
}
LinearOffset += PtrBlockMapEntry->Length;
LbaNumber++;
}
if (FvFound) {
break;
}
}
} else {
//
// update non-volatile variable in SMM mode
//
if (SetByIndex) {
DataPtr += Global->NonVolatileVariableBase;
}
WriteDataSize = (UINTN) DataSize;
Status = mSmmVariableGlobal->SmmFwbService->Write (
mSmmVariableGlobal->SmmFwbService,
(UINTN ) DataPtr,
&WriteDataSize,
Buffer
);
}
if (mVariableModuleGlobal->NonVolatileVariableCache != NULL && !Volatile) {
if (!EFI_ERROR (Status)) {
CopyMem (
(VOID *) ((UINTN) DataPtr - (UINTN) Global->NonVolatileVariableBase + (UINTN) mVariableModuleGlobal->NonVolatileVariableCache),
Buffer,
DataSize
);
SetNonVolatileVariableCacheCrc32 ();
} else {
//
// If updating NV data failed, turn off variable chache to prevent from data corrupted
//
DisableVariableCache ();
}
}
return Status;
}
/**
This code finds variable in storage blocks (Volatile or Non-Volatile)
@param[in] VariableName Name of the variable to be found
@param[in] VendorGuid Vendor GUID to be found.
@param[in] PtrTrack Variable Track Pointer structure that contains Variable Information.
@param[in] VariableCount The number of found variable.
@param[in] Global VARIABLE_GLOBAL pointer.
@param[in] CovertPointer Convert pointer from cache to MMIO or not.
@retval EFI_SUCCESS Variable successfully found.
@retval EFI_INVALID_PARAMETER If VariableName is not an empty string, while
VendorGuid is NULL.
@retval EFI_NOT_FOUND Variable not found
**/
EFI_STATUS
FindVariableEx (
IN CONST CHAR16 *VariableName,
IN CONST EFI_GUID *VendorGuid,
OUT VARIABLE_POINTER_TRACK *PtrTrack,
OUT UINTN *VariableCount,
IN VARIABLE_GLOBAL *Global,
IN BOOLEAN CovertPointer
)
{
VARIABLE_HEADER *Variable[VariableStoreTypeMax];
VARIABLE_STORE_HEADER *VariableStoreHeader[VariableStoreTypeMax];
VARIABLE_STORE_TYPE Type;
VARIABLE_HEADER *InDeletedVariable;
VARIABLE_STORE_TYPE InDeletedType;
BOOLEAN VariableFound;
EFI_STATUS Status;
EFI_HOB_GUID_TYPE *GuidHob;
BOOLEAN ForceDefaults;
InDeletedVariable = NULL;
InDeletedType = VariableStoreTypeMax;
*VariableCount = 0;
//
// 0: HOB 1: Non-Volatile, 2: Volatile
//
VariableStoreHeader[VariableStoreTypeHob] = (VARIABLE_STORE_HEADER *)((UINTN)mVariableModuleGlobal->HobVariableBase);
if (mVariableModuleGlobal->NonVolatileVariableCache == NULL) {
VariableStoreHeader[VariableStoreTypeNv] = (VARIABLE_STORE_HEADER *)((UINTN)Global->NonVolatileVariableBase);
} else {
VariableStoreHeader[VariableStoreTypeNv] = (VARIABLE_STORE_HEADER *)mVariableModuleGlobal->NonVolatileVariableCache;
}
VariableStoreHeader[VariableStoreTypeVolatile] = (VARIABLE_STORE_HEADER *)((UINTN)Global->VolatileVariableBase);
//
// Start Pointers for the variable.
// Actual Data Pointer where data can be written.
//
for (Type = (VARIABLE_STORE_TYPE)0; Type < VariableStoreTypeMax; Type++) {
if (VariableStoreHeader[Type] == NULL) {
Variable[Type] = NULL;
continue;
}
Variable[Type] = GetStartPointer (VariableStoreHeader[Type]);
}
if (VariableName[0] != 0 && VendorGuid == NULL) {
return EFI_INVALID_PARAMETER;
}
//
// Check whether variable defaults were forced during PEI
//
ForceDefaults = FALSE;
if (mSmst == NULL && !mVariableModuleGlobal->VariableWriteReady) {
GuidHob = GetFirstGuidHob (&gH2OFlashMapRegionVarGuid);
if (GuidHob != NULL) {
ForceDefaults = TRUE;
}
}
VariableFound = FALSE;
//
// Find the variable by walk through HOB, non-volatile and volatile variable store
// Note: In theory, the search order should be volatile -> HOB -> non-volatile
// for performance consideration, we change the order to
// HOB -> non-volatile -> volatile. This is because most variable want to
// searched is non-volatile and the change can prevent from always search
// variable from volatile region first.
//
for (Type = (VARIABLE_STORE_TYPE)0; Type < VariableStoreTypeMax; Type++) {
if (VariableStoreHeader[Type] == NULL) {
continue;
}
PtrTrack->StartPtr = GetStartPointer (VariableStoreHeader[Type]);
if (Type == VariableStoreTypeNv) {
//
// Skip non-volatile variable store if defaults were forced.
//
if (ForceDefaults) {
continue;
}
PtrTrack->EndPtr = GetNonVolatileEndPointer (VariableStoreHeader[Type]);
} else {
PtrTrack->EndPtr = GetEndPointer (VariableStoreHeader[Type]);
}
while (IsValidVariableHeaderInVarRegion (Variable[Type], PtrTrack->EndPtr)) {
if (Variable[Type]->State == VAR_ADDED) {
PtrTrack->CurrPtr = Variable[Type];
if (VariableName[0] == 0 && Variable[Type] == GetMostValueVariable (PtrTrack)) {
VariableFound = TRUE;
break;
} else if (CompareGuid (VendorGuid, &Variable[Type]->VendorGuid) &&
!StrCmp (VariableName, GET_VARIABLE_NAME_PTR (Variable[Type]))) {
VariableFound = TRUE;
break;
}
} else if (Variable[Type]->State == (VAR_ADDED & VAR_IN_DELETED_TRANSITION)) {
//
// VAR_IN_DELETED_TRANSITION should also be checked.
//
if (VariableName[0] == 0) {
InDeletedVariable = Variable[Type];
InDeletedType = Type;
(*VariableCount)++;
} else if (CompareGuid (VendorGuid, &Variable[Type]->VendorGuid) &&
!StrCmp (VariableName, GET_VARIABLE_NAME_PTR (Variable[Type]))) {
InDeletedVariable = Variable[Type];
InDeletedType = Type;
(*VariableCount)++;
}
}
Variable[Type] = GetNextVariablePtr (Variable[Type]);
}
if (VariableFound) {
break;
}
}
if (VariableFound) {
PtrTrack->CurrPtr = Variable[Type];
PtrTrack->Volatile = (BOOLEAN)(Type == VariableStoreTypeVolatile);
(*VariableCount)++;
} else if (InDeletedVariable != NULL) {
PtrTrack->CurrPtr = InDeletedVariable;
PtrTrack->Volatile = (BOOLEAN)(InDeletedType == VariableStoreTypeVolatile);
if (InDeletedType == VariableStoreTypeNv) {
PtrTrack->StartPtr = GetStartPointer (VariableStoreHeader[VariableStoreTypeNv]);
PtrTrack->EndPtr = GetNonVolatileEndPointer (VariableStoreHeader[VariableStoreTypeNv]);
}
VariableFound = TRUE;
}
Status = EFI_SUCCESS;
if (VariableFound) {
if (CovertPointer) {
ConvertCacheAddressToPhysicalAddress (Global, PtrTrack);
//
// If non-volatile variable data is invalid, we should flush variable cache to synchronize non-volatile variable data.
//
if (!IsValidVariableHeader (PtrTrack->CurrPtr)) {
if (mSmst || !VariableAtRuntime ()) {
REPORT_STATUS_CODE (
EFI_ERROR_CODE,
EFI_SOFTWARE_EFI_RUNTIME_SERVICE | H2O_VARIABLE_CACHE_CORRUPTION
);
}
ASSERT (FALSE);
Status = FlushVariableCache (
mVariableModuleGlobal->NonVolatileVariableCache,
(UINT8 *) (UINTN) mVariableModuleGlobal->VariableBase.NonVolatileVariableBase,
mVariableModuleGlobal->NonVolatileVariableCacheSize,
&mVariableModuleGlobal->NonVolatileLastVariableOffset
);
SetNonVolatileVariableCacheCrc32 ();
if (!EFI_ERROR (Status)) {
Status = FindVariableByLifetime (
VariableName,
VendorGuid,
PtrTrack,
VariableCount,
Global
);
}
}
}
} else {
PtrTrack->CurrPtr = NULL;
Status = EFI_NOT_FOUND;
}
return Status;
}
/**
This code finds variable in storage blocks (Volatile or Non-Volatile)
@param VariableName Name of the variable to be found
@param VendorGuid Vendor GUID to be found.
@param PtrTrack Variable Track Pointer structure that contains Variable Information.
@param VariableCount The number of found variable.
@param Global VARIABLE_GLOBAL pointer.
@retval EFI_INVALID_PARAMETER If VariableName is not an empty string, while
VendorGuid is NULL.
@retval EFI_SUCCESS Variable successfully found.
@retval EFI_NOT_FOUND Variable not found
**/
EFI_STATUS
FindVariable (
IN CONST CHAR16 *VariableName,
IN CONST EFI_GUID *VendorGuid,
OUT VARIABLE_POINTER_TRACK *PtrTrack,
OUT UINTN *VariableCount,
IN VARIABLE_GLOBAL *Global
)
{
return FindVariableEx (VariableName, VendorGuid, PtrTrack, VariableCount, Global, TRUE);
}
/**
This code finds variable in all of storage blocks (Volatile, Non-Volatile variable store and
variable default store)
@param VariableName Name of the variable to be found
@param VendorGuid Vendor GUID to be found.
@param PtrTrack Variable Track Pointer structure that contains Variable Information.
@param VariableCount The number of found variable.
@param Global VARIABLE_GLOBAL pointer.
@retval EFI_INVALID_PARAMETER If VariableName is not an empty string, while
VendorGuid is NULL.
@retval EFI_SUCCESS Variable successfully found.
@retval EFI_NOT_FOUND Variable not found
**/
EFI_STATUS
FindVariableInAllRegions (
IN CONST CHAR16 *VariableName,
IN CONST EFI_GUID *VendorGuid,
OUT VARIABLE_POINTER_TRACK *PtrTrack,
OUT UINTN *VariableCount,
IN VARIABLE_GLOBAL *Global
)
{
EFI_STATUS Status;
Status = FindVariable (VariableName, VendorGuid, PtrTrack, VariableCount, Global);
if (Status != EFI_NOT_FOUND) {
return Status;
}
return GetVariableFromVariableDefault (VariableName, VendorGuid, PtrTrack);
}
/**
Delete all variables which the state is VAR_ADDED or VAR_ADDED & VAR_IN_DELETED_TRANSITION and
attribute is non-volatile.
@param VariableName Name of the variable which will be deleted
@param VendorGuid GUID of the variable which will be deleted
@param Global VARIABLE_GLOBAL pointer
@retval EFI_SUCCESS Delete variable successfully.
@return others Unable to delete variable.
**/
EFI_STATUS
DeleteAllOldVariable (
IN CHAR16 *VariableName,
IN EFI_GUID *VendorGuid,
IN VARIABLE_GLOBAL *Global
)
{
VARIABLE_HEADER *Variable;
VARIABLE_HEADER *StartPtr;
VARIABLE_HEADER *EndPtr;
VARIABLE_STORE_HEADER *VariableStoreHeader;
UINT8 State;
EFI_STATUS Status;
if (VariableName == NULL || VariableName[0] == 0 || VendorGuid == NULL) {
return EFI_INVALID_PARAMETER;
}
VariableStoreHeader = (VARIABLE_STORE_HEADER *) ((UINTN) Global->NonVolatileVariableBase);
StartPtr = GetStartPointer (VariableStoreHeader);
Variable = StartPtr;
EndPtr = GetNonVolatileEndPointer (VariableStoreHeader);
while (IsValidVariableHeaderInVarRegion (Variable, EndPtr)) {
if ((Variable->State == VAR_ADDED || Variable->State == (VAR_ADDED & VAR_IN_DELETED_TRANSITION)) &&
!(VariableAtRuntime () && !(Variable->Attributes & EFI_VARIABLE_RUNTIME_ACCESS))) {
if (CompareGuid (VendorGuid, &Variable->VendorGuid) &&
StrCmp (VariableName, GET_VARIABLE_NAME_PTR (Variable)) == 0 &&
!IsMostValueVariable (Variable, StartPtr, EndPtr)) {
State = Variable->State;
State &= VAR_DELETED;
Status = UpdateVariableStore (
Global,
FALSE,
FALSE,
(UINTN) &Variable->State,
sizeof (UINT8),
&State
);
if (EFI_ERROR (Status)) {
return Status;
}
}
}
Variable = GetNextVariablePtr (Variable);
}
return EFI_SUCCESS;
}
/**
This code finds variable in storage blocks (Volatile or Non-Volatile)
This code finds variable in storage blocks of volatile and non-volatile storage areas.
If VariableName is an empty string, then we just return the first
qualified variable without comparing VariableName and VendorGuid.
Otherwise, VariableName and VendorGuid are compared.
@param[in] VariableName Name of the variable to be found
@param[in] VendorGuid Vendor GUID to be found.
@param[out] PtrTrack Variable Track Pointer structure that contains Variable Information.
@param[out] VariableCount The number of found variable.
@param[in] Global VARIABLE_GLOBAL pointer.
@param[in] CovertPointer Convert pointer from cache to MMIO or not.
@retval EFI_SUCCESS Variable successfully found.
@retval EFI_INVALID_PARAMETER If VariableName is not an empty string, while VendorGuid is NULL.
@retval EFI_NOT_FOUND Variable not found
**/
EFI_STATUS
FindVariableByLifetimeEx (
IN CONST CHAR16 *VariableName,
IN CONST EFI_GUID *VendorGuid,
OUT VARIABLE_POINTER_TRACK *PtrTrack,
OUT UINTN *VariableCount,
IN VARIABLE_GLOBAL *Global,
IN BOOLEAN ConvertPointer
)
{
EFI_STATUS Status;
Status = FindVariableEx (
VariableName,
VendorGuid,
PtrTrack,
VariableCount,
Global,
ConvertPointer
);
if (EFI_ERROR (Status)) {
return Status;
}
if (VariableAtRuntime () && (PtrTrack->CurrPtr->Attributes & EFI_VARIABLE_RUNTIME_ACCESS) != EFI_VARIABLE_RUNTIME_ACCESS) {
PtrTrack->CurrPtr = NULL;
return EFI_NOT_FOUND;
}
return EFI_SUCCESS;
}
/**
This code finds variable in storage blocks (Volatile or Non-Volatile)
This code finds variable in storage blocks of volatile and non-volatile storage areas.
If VariableName is an empty string, then we just return the first
qualified variable without comparing VariableName and VendorGuid.
Otherwise, VariableName and VendorGuid are compared.
@param[in] VariableName Name of the variable to be found
@param[in] VendorGuid Vendor GUID to be found.
@param[out] PtrTrack Variable Track Pointer structure that contains Variable Information.
@param[out] VariableCount The number of found variable.
@param[in] Global VARIABLE_GLOBAL pointer
@retval EFI_SUCCESS Variable successfully found.
@retval EFI_INVALID_PARAMETER If VariableName is not an empty string, while VendorGuid is NULL.
@retval EFI_NOT_FOUND Variable not found
**/
EFI_STATUS
FindVariableByLifetime (
IN CONST CHAR16 *VariableName,
IN CONST EFI_GUID *VendorGuid,
OUT VARIABLE_POINTER_TRACK *PtrTrack,
OUT UINTN *VariableCount,
IN VARIABLE_GLOBAL *Global
)
{
return FindVariableByLifetimeEx (VariableName, VendorGuid, PtrTrack, VariableCount, Global, TRUE);
}
/**
This code finds variable in all of storage blocks (Volatile, Non-Volatile variable store and
variable default store)
@param[in] VariableName Name of the variable to be found
@param[in] VendorGuid Vendor GUID to be found.
@param[out] PtrTrack Variable Track Pointer structure that contains Variable Information.
@param[out] VariableCount The number of found variable.
@param[in] Global VARIABLE_GLOBAL pointer
@retval EFI_SUCCESS Variable successfully found.
@retval EFI_INVALID_PARAMETER If VariableName is not an empty string, while VendorGuid is NULL.
@retval EFI_NOT_FOUND Variable not found
**/
EFI_STATUS
FindVariableByLifetimeInAllRegions (
IN CONST CHAR16 *VariableName,
IN CONST EFI_GUID *VendorGuid,
OUT VARIABLE_POINTER_TRACK *PtrTrack,
OUT UINTN *VariableCount,
IN VARIABLE_GLOBAL *Global
)
{
EFI_STATUS Status;
Status = FindVariableInAllRegions (
VariableName,
VendorGuid,
PtrTrack,
VariableCount,
Global
);
if (EFI_ERROR (Status)) {
return Status;
}
if (VariableAtRuntime () && (PtrTrack->CurrPtr->Attributes & EFI_VARIABLE_RUNTIME_ACCESS) != EFI_VARIABLE_RUNTIME_ACCESS) {
PtrTrack->CurrPtr = NULL;
return EFI_NOT_FOUND;
}
return EFI_SUCCESS;
}
/**
Internal function to get current nonvolatile offset from physical hardware device.
@return UINTN The offset of the first free nonvolatile variable space.ata into this region.
**/
UINTN
GetCurrentNonVolatileOffset (
VOID
)
{
UINT8 *CurrPtr;
VARIABLE_HEADER *LastVariable;
CurrPtr = (UINT8 *) ((UINTN) mVariableModuleGlobal->VariableBase.NonVolatileVariableBase);
//
// Update NonVolatileOffset to make sure the the NonVolatileOffset is correct
//
LastVariable = (VARIABLE_HEADER *) (CurrPtr + GetVariableStoreHeaderSize ());
while (IsValidVariableHeader (LastVariable)) {
LastVariable = GetNextVariablePtr (LastVariable);
}
return ((UINTN) LastVariable - (UINTN) CurrPtr);
}
/**
Update the variable region with Variable information. If EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS is set,
index of associated public key is needed.
@param[in] VariableName Name of variable.
@param[in] VendorGuid Guid of variable.
@param[in] Data Variable data.
@param[in] DataSize Size of data. 0 means delete.
@param[in] Attributes Attributes of the variable.
@param[in] KeyIndex Index of associated public key.
@param[in] MonotonicCount Value of associated monotonic count.
@param[in] Variable The variable information that is used to keep track of variable usage.
@param[in] TimeStamp Value of associated TimeStamp.
@param[in] Global Pointer to VARIABLE_GLOBAL
@retval EFI_SUCCESS The update operation is success.
@retval EFI_OUT_OF_RESOURCES Variable region is full, cannot write other data into this region.
**/
EFI_STATUS
UpdateVariable (
IN CONST CHAR16 *VariableName,
IN CONST EFI_GUID *VendorGuid,
IN CONST VOID *Data,
IN UINTN DataSize,
IN UINT32 Attributes,
IN UINT32 KeyIndex OPTIONAL,
IN UINT64 MonotonicCount OPTIONAL,
IN VARIABLE_POINTER_TRACK *Variable,
IN EFI_TIME *TimeStamp OPTIONAL,
IN VARIABLE_GLOBAL *Global
)
{
EFI_STATUS Status;
VARIABLE_HEADER *NextVariable;
UINTN NonVolatileVarableStoreSize;
UINTN VarNameOffset;
UINTN VarDataOffset;
UINTN VarNameSize;
UINTN VarSize;
BOOLEAN Volatile;
UINT8 State;
UINTN BufSize;
UINTN DataOffset;
UINT8 *WriteBuffer;
UINTN TrySetVariableTime;
BOOLEAN SetVariableSuccess;
UINTN VariableCount;
UINTN *VolatileLastVariableOffset;
EFI_PHYSICAL_ADDRESS HobVariableBase;
WriteBuffer = NULL;
// for Secure Boot variables, perform sanity check of incoming data since the structure is known to firmware
//
if (IsSecureDatabaseVariable (VariableName, VendorGuid)) {
Status = CheckSecureBootVarData (VariableName, VendorGuid, Data, DataSize);
if (EFI_ERROR (Status)) {
return Status;
}
}
//
// Convert varialbe pointer from Variable HOB to non-volatile region to set variable
// to non-volatile region.
//
if (AddressInVariableHobRegion((EFI_PHYSICAL_ADDRESS) (UINTN) Variable->CurrPtr)) {
HobVariableBase = mVariableModuleGlobal->HobVariableBase;
mVariableModuleGlobal->HobVariableBase = 0;
VariableCount = 0;
FindVariableByLifetime (
VariableName,
VendorGuid,
Variable,
&VariableCount,
Global
);
mVariableModuleGlobal->HobVariableBase = HobVariableBase;
}
if (Variable->CurrPtr != NULL) {
if (!AddressInVariableDefaultRegion ((EFI_PHYSICAL_ADDRESS) (UINTN) Variable->CurrPtr)) {
if (VariableAtRuntime () && !IsReadOnlyVariable (VariableName, VendorGuid)) {
//
// If AtRuntime and the variable is Volatile and Runtime Access,
// the volatile is ReadOnly, and SetVariable should be aborted and
// return EFI_WRITE_PROTECTED.
//
if (Variable->Volatile) {
Status = EFI_WRITE_PROTECTED;
goto Done;
}
//
// Only variable that have NV attributes can be updated/deleted in Runtime.
//
if ((Variable->CurrPtr->Attributes & EFI_VARIABLE_NON_VOLATILE) == 0) {
Status = EFI_INVALID_PARAMETER;
goto Done;
}
}
}
//
// Setting a data variable with no access, or zero DataSize attributes
// specified causes it to be deleted.
//
if ((((Attributes & EFI_VARIABLE_APPEND_WRITE) == 0) && (DataSize == 0)) || ((Attributes & (EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS)) == 0)) {
if (AddressInVariableDefaultRegion ((EFI_PHYSICAL_ADDRESS) (UINTN) Variable->CurrPtr)) {
Status = EFI_WRITE_PROTECTED;
goto Done;
}
State = Variable->CurrPtr->State;
State &= VAR_DELETED;
Status = UpdateVariableStore (
Global,
Variable->Volatile,
FALSE,
(UINTN) &Variable->CurrPtr->State,
sizeof (UINT8),
&State
);
if (Status == EFI_SUCCESS && !Variable->Volatile && IsUserVariable ((CHAR16 *) (Variable->CurrPtr + 1), &Variable->CurrPtr->VendorGuid)) {
VarSize = ((UINTN) GetNextVariablePtr (Variable->CurrPtr) - (UINTN) Variable->CurrPtr);
if (mSmst == NULL) {
mVariableModuleGlobal->CommonUserVariableTotalSize -= VarSize;
} else {
mSmmVariableGlobal->ProtectedModeVariableModuleGlobal->CommonUserVariableTotalSize -= VarSize;
}
}
goto Done;
}
//
// If the variable is marked valid, and the same data has been passed in,
// then return to the caller immediately.
//
if (!AddressInVariableDefaultRegion ((EFI_PHYSICAL_ADDRESS) (UINTN) Variable->CurrPtr) &&
DataSizeOfVariable (Variable->CurrPtr) == DataSize &&
(CompareMem (Data, GetVariableDataPtr (Variable->CurrPtr), DataSize) == 0) &&
((Attributes & EFI_VARIABLE_APPEND_WRITE) == 0) && (TimeStamp == NULL || CompareTimeStamp (TimeStamp, &Variable->CurrPtr->TimeStamp))) {
Status = EFI_SUCCESS;
goto Done;
}
//
// EFI_VARIABLE_APPEND_WRITE attribute only effects for existing variable
// FIXUP - need to check for duplicate data before appending data, also need to append to correct
// list, assuming this is a cert, or a PubKey!
//
if ((Attributes & EFI_VARIABLE_APPEND_WRITE) != 0) {
BufSize = Variable->CurrPtr->DataSize + DataSize;
SetMem (mStorageArea, APPEND_BUFF_SIZE, 0xff);
//
// Cache the previous variable data into StorageArea.
//
DataOffset = sizeof (VARIABLE_HEADER) + Variable->CurrPtr->NameSize + GET_PAD_SIZE (Variable->CurrPtr->NameSize);
CopyMem (mStorageArea, (UINT8*)((UINTN)Variable->CurrPtr + DataOffset), Variable->CurrPtr->DataSize);
//
// Append the new data to the end of previous data.
// for special secure boot variables, need to check for duplicate data before append
//
if ((DataSize > 0) && ((IsKekVariable (VariableName, VendorGuid)) ||
(IsImageSecureDatabaseVariable (VariableName, VendorGuid)))) {
BufSize = Variable->CurrPtr->DataSize;
Status = AppendSignatureList (DataSize, Data, MAX_VARIABLE_SIZE, &BufSize, mStorageArea);
if (EFI_ERROR (Status)) {
goto Done;
}
//
// If appended signature is already existence, just return SUCCESS;
//
if ((BufSize == Variable->CurrPtr->DataSize) &&
(TimeStamp == NULL || CompareTimeStamp (TimeStamp, &Variable->CurrPtr->TimeStamp))) {
Status = EFI_SUCCESS;
goto Done;
}
} else {
//
// If appended data size is 0, just return EFI_SUCCESS
//
if ((DataSize == 0) &&
(TimeStamp == NULL || CompareTimeStamp (TimeStamp, &Variable->CurrPtr->TimeStamp))) {
Status = EFI_SUCCESS;
goto Done;
}
if (BufSize > APPEND_BUFF_SIZE) {
//
// If variable size (previous + current) is bigger than 4K that was reserved in runtime,
// return EFI_OUT_OF_RESOURCES.
//
return EFI_OUT_OF_RESOURCES;
}
CopyMem ((UINT8*)((UINTN)mStorageArea + Variable->CurrPtr->DataSize), Data, DataSize);
}
//
// Override Data and DataSize which are used for combined data area including previous and new data.
//
Data = mStorageArea;
DataSize = BufSize;
}
if (!AddressInVariableDefaultRegion ((EFI_PHYSICAL_ADDRESS) (UINTN) Variable->CurrPtr) &&
((Variable->CurrPtr->State == VAR_ADDED) ||
(Variable->CurrPtr->State == (VAR_ADDED & VAR_IN_DELETED_TRANSITION)))) {
//
// Mark the old variable as in delete transition.
//
State = Variable->CurrPtr->State;
State &= VAR_IN_DELETED_TRANSITION;
Status = UpdateVariableStore (
Global,
Variable->Volatile,
FALSE,
(UINTN) &Variable->CurrPtr->State,
sizeof (UINT8),
&State
);
if (EFI_ERROR (Status)) {
goto Done;
}
}
//
// Not found existing variable. Create a new variable.
//
} else {
//
// Make sure we are trying to create a new variable.
// Setting a data variable with no access, or zero DataSize attributes means to delete it.
//
if (DataSize == 0 || (Attributes & (EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS)) == 0) {
Status = EFI_NOT_FOUND;
goto Done;
}
//
// Only variable have NV|RT attribute can be created in Runtime.
//
if (VariableAtRuntime () &&
(((Attributes & EFI_VARIABLE_RUNTIME_ACCESS) == 0) || ((Attributes & EFI_VARIABLE_NON_VOLATILE) == 0))) {
Status = EFI_INVALID_PARAMETER;
goto Done;
}
}
//
// Tricky part: Use scratch data area at the end of volatile variable store
// as a temporary storage.
//
if (mSmst == NULL) {
NextVariable = GetEndPointer ((VARIABLE_STORE_HEADER *) ((UINTN) Global->VolatileVariableBase));
} else {
WriteBuffer = VariableAllocateZeroBuffer (SCRATCH_SIZE, FALSE);
if (WriteBuffer == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto Done;
}
NextVariable = (VARIABLE_HEADER *) WriteBuffer;
}
SetMem (NextVariable, SCRATCH_SIZE, 0xff);
NextVariable->StartId = VARIABLE_DATA;
NextVariable->Attributes = Attributes;
//
// NextVariable->State = VAR_ADDED;
//
NextVariable->Reserved = 0;
NextVariable->PubKeyIndex = KeyIndex;
NextVariable->MonotonicCount = MonotonicCount;
SetMem (&NextVariable->TimeStamp, sizeof (EFI_TIME), 0);
//
// if creating a new variable, use the passed in timestamp, if available
//
if ((TimeStamp != NULL) && (Variable->CurrPtr == NULL)) {
CopyMem (&NextVariable->TimeStamp, TimeStamp, sizeof (EFI_TIME));
}
//
// for UEFI 2.3.1
//
if (((Attributes & EFI_VARIABLE_APPEND_WRITE) == 0) &&
((Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) ==
EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS)) {
ASSERT (TimeStamp != NULL);
if (TimeStamp != NULL) {
CopyMem (&NextVariable->TimeStamp, TimeStamp, sizeof (EFI_TIME));
}
} else if (((Attributes & EFI_VARIABLE_APPEND_WRITE) != 0) &&
((Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) != 0) &&
(Variable->CurrPtr != NULL)) {
//
// In the case when the EFI_VARIABLE_APPEND_WRITE attribute is set, only
// when the new TimeStamp value is later than the current timestamp associated
// with the variable, we need associate the new timestamp with the updated value.
//
CopyMem (&NextVariable->TimeStamp, &Variable->CurrPtr->TimeStamp, sizeof (EFI_TIME));
if (TimeStamp != NULL && !CompareTimeStamp (TimeStamp, &Variable->CurrPtr->TimeStamp)) {
CopyMem (&NextVariable->TimeStamp, TimeStamp, sizeof (EFI_TIME));
}
}
//
// The EFI_VARIABLE_APPEND_WRITE attribute will never be set in the returned
// Attributes bitmask parameter of a GetVariable() call.
//
NextVariable->Attributes = Attributes & (~EFI_VARIABLE_APPEND_WRITE);
VarNameOffset = sizeof (VARIABLE_HEADER);
VarNameSize = StrSize (VariableName);
CopyMem (
(UINT8 *) ((UINTN) NextVariable + VarNameOffset),
VariableName,
VarNameSize
);
VarDataOffset = VarNameOffset + VarNameSize + GET_PAD_SIZE (VarNameSize);
CopyMem (
(UINT8 *) ((UINTN) NextVariable + VarDataOffset),
Data,
DataSize
);
CopyMem (&NextVariable->VendorGuid, VendorGuid, sizeof (EFI_GUID));
//
// There will be pad bytes after Data, the NextVariable->NameSize and
// NextVariable->DataSize should not include pad size so that variable
// service can get actual size in GetVariable.
//
NextVariable->NameSize = (UINT32)VarNameSize;
NextVariable->DataSize = (UINT32)DataSize;
//
// The actual size of the variable that stores in storage should
// include pad size.
//
VarSize = VarDataOffset + DataSize + GET_PAD_SIZE (DataSize);
if ((Attributes & EFI_VARIABLE_NON_VOLATILE) != 0) {
//
// Sync mVariableModuleGlobal->NonVolatileLastVariableOffset first. This is caused by we
// may invoke this function between two different modes (protected mode and SMM mode).
//
mVariableModuleGlobal->NonVolatileLastVariableOffset = GetCurrentNonVolatileOffset ();
//
// If variable cache is enabled, we should check variable data and variable variable is consistent before we write
// to non-volatile storage.
//
if (mVariableModuleGlobal->NonVolatileVariableCache != NULL && !CheckNonVolatileVariableCacheCrc32 ()) {
ASSERT (FALSE);
if (mSmst || !VariableAtRuntime ()) {
REPORT_STATUS_CODE (
EFI_ERROR_CODE,
EFI_SOFTWARE_EFI_RUNTIME_SERVICE | H2O_VARIABLE_CACHE_CORRUPTION
);
}
Status = FlushVariableCache (
mVariableModuleGlobal->NonVolatileVariableCache,
(UINT8 *) (UINTN) mVariableModuleGlobal->VariableBase.NonVolatileVariableBase,
mVariableModuleGlobal->NonVolatileVariableCacheSize,
&mVariableModuleGlobal->NonVolatileLastVariableOffset
);
SetNonVolatileVariableCacheCrc32 ();
//
// Try to find exist variable after flush variable cache to mark the exist variable
//
VariableCount = 0;
Status = FindVariableByLifetime (VariableName, VendorGuid, Variable, &VariableCount, Global);
if (Variable->CurrPtr != NULL && Variable->CurrPtr->State == VAR_ADDED) {
State = (VAR_ADDED & VAR_IN_DELETED_TRANSITION);
Status = UpdateVariableStore (
Global,
Variable->Volatile,
FALSE,
(UINTN) &Variable->CurrPtr->State,
sizeof (UINT8),
&State
);
if (EFI_ERROR (Status)) {
goto Done;
}
}
}
//
// Create a nonvolatile variable.
//
Volatile = FALSE;
NonVolatileVarableStoreSize = GetNonVolatileVariableStoreSize ();
if ((UINT32) (VarSize + mVariableModuleGlobal->NonVolatileLastVariableOffset) >
NonVolatileVarableStoreSize
) {
if (DoesNeedDoReclaimVariableExist ()) {
Status = EFI_OUT_OF_RESOURCES;
goto Done;
}
if (VariableAtRuntime () && !PcdGetBool(PcdRuntimeReclaimSupported)) {
SetDoReclaimNextBoot ();
Status = EFI_OUT_OF_RESOURCES;
goto Done;
}
//
// Perform garbage collection & reclaim operation.
//
Status = ReclaimNonVolatileVariable (
NextVariable,
VarSize,
Global->NonVolatileVariableBase,
&mVariableModuleGlobal->NonVolatileLastVariableOffset
);
if (EFI_ERROR (Status)) {
SetDoReclaimNextBoot ();
}
goto Done;
}
//
// Start try to set non-volatile variable and use do-while to
// make sure at least set variable once.
//
TrySetVariableTime = 0;
SetVariableSuccess = FALSE;
//
// Four steps
// 1. Write variable header
// 2. Set variable state to header valid
// 3. Write variable data
// 4. Set variable state to valid
//
//
// Step 1:
do {
NextVariable->State = 0xFF;
Status = UpdateVariableStore (
Global,
FALSE,
TRUE,
mVariableModuleGlobal->NonVolatileLastVariableOffset,
sizeof (VARIABLE_HEADER),
(UINT8 *) NextVariable
);
if (EFI_ERROR (Status)) {
goto Done;
}
//
// Step 2:
//
NextVariable->State = VAR_HEADER_VALID_ONLY;
Status = UpdateVariableStore (
Global,
FALSE,
TRUE,
mVariableModuleGlobal->NonVolatileLastVariableOffset + OFFSET_OF (VARIABLE_HEADER, State),
sizeof (UINT8),
&NextVariable->State
);
if (EFI_ERROR (Status)) {
goto Done;
}
//
// Step 3:
//
Status = UpdateVariableStore (
Global,
FALSE,
TRUE,
mVariableModuleGlobal->NonVolatileLastVariableOffset + sizeof (VARIABLE_HEADER),
(UINT32) VarSize - sizeof (VARIABLE_HEADER),
(UINT8 *) NextVariable + sizeof (VARIABLE_HEADER)
);
if (EFI_ERROR (Status)) {
goto Done;
}
TrySetVariableTime++;
//
//if update SetVariableSuccess to TRUE
//
if (IsCorrectVariable (Global->NonVolatileVariableBase, mVariableModuleGlobal->NonVolatileLastVariableOffset, NextVariable)) {
SetVariableSuccess = TRUE;
} else {
if (VariableAtRuntime () && !PcdGetBool(PcdRuntimeReclaimSupported)) {
Status = EFI_OUT_OF_RESOURCES;
goto Done;
}
NextVariable->State &= VAR_DELETED;
Status = UpdateVariableStore (
Global,
FALSE,
TRUE,
mVariableModuleGlobal->NonVolatileLastVariableOffset + OFFSET_OF (VARIABLE_HEADER, State),
sizeof (UINT8),
&NextVariable->State
);
//
// reclaim non-volatile variable : skip this setting variable
//
Status = Reclaim (
Global->NonVolatileVariableBase,
&mVariableModuleGlobal->NonVolatileLastVariableOffset,
FALSE,
AddressInVariableDefaultRegion ((EFI_PHYSICAL_ADDRESS) (UINTN) Variable->CurrPtr) ? NULL : &Variable->CurrPtr
);
if (EFI_ERROR (Status)) {
goto Done;
}
}
} while (!SetVariableSuccess && (TrySetVariableTime < MAX_TRY_SET_VARIABLE_TIMES));
if (!SetVariableSuccess) {
if (mSmst || !VariableAtRuntime ()) {
REPORT_STATUS_CODE (
EFI_ERROR_CODE,
EFI_SOFTWARE_EFI_RUNTIME_SERVICE | H2O_VARIABLE_WRITE_NON_VOLATILE_FAILED
);
}
Status = EFI_DEVICE_ERROR;
goto Done;
}
//
// Step 4:write the VAR_ADDED, until make sure data is correct
// Set variable state to valid
//
NextVariable->State = VAR_ADDED;
Status = UpdateVariableStore (
Global,
FALSE,
TRUE,
mVariableModuleGlobal->NonVolatileLastVariableOffset + OFFSET_OF (VARIABLE_HEADER, State),
sizeof (UINT8),
&NextVariable->State
);
if (EFI_ERROR (Status)) {
goto Done;
}
mVariableModuleGlobal->NonVolatileLastVariableOffset += (VarSize);
if (IsUserVariable ((CHAR16 *) (NextVariable + 1), &NextVariable->VendorGuid)) {
if (mSmst == NULL) {
mVariableModuleGlobal->CommonUserVariableTotalSize += VarSize;
} else {
mSmmVariableGlobal->ProtectedModeVariableModuleGlobal->CommonUserVariableTotalSize += VarSize;
}
}
} else {
//
// Create a volatile variable.
//
Volatile = TRUE;
VolatileLastVariableOffset = NULL;
if (mSmst == NULL) {
VolatileLastVariableOffset = &mVariableModuleGlobal->VolatileLastVariableOffset;
} else {
VolatileLastVariableOffset = &mSmmVariableGlobal->ProtectedModeVariableModuleGlobal->VolatileLastVariableOffset;
}
if ((UINT32) (VarSize + *VolatileLastVariableOffset) >
GetVariableStoreSize ((VARIABLE_STORE_HEADER *) (UINTN) (Global->VolatileVariableBase))) {
//
// Perform garbage collection & reclaim operation.
//
Status = Reclaim (
Global->VolatileVariableBase,
VolatileLastVariableOffset,
TRUE,
&Variable->CurrPtr
);
if (EFI_ERROR (Status)) {
goto Done;
}
//
// If still no enough space, return out of resources.
//
if ((UINT32) (VarSize + *VolatileLastVariableOffset) >
GetVariableStoreSize ((VARIABLE_STORE_HEADER *) (UINTN) (Global->VolatileVariableBase))
) {
Status = EFI_OUT_OF_RESOURCES;
goto Done;
}
}
NextVariable->State = VAR_ADDED;
Status = UpdateVariableStore (
Global,
TRUE,
TRUE,
*VolatileLastVariableOffset,
(UINT32) VarSize,
(UINT8 *) NextVariable
);
if (EFI_ERROR (Status)) {
goto Done;
}
*VolatileLastVariableOffset += (VarSize);
}
//
// Mark the old variable as deleted.
//
if (!EFI_ERROR (Status) && Variable->CurrPtr != NULL &&
!AddressInVariableDefaultRegion ((EFI_PHYSICAL_ADDRESS) (UINTN) Variable->CurrPtr)) {
State = Variable->CurrPtr->State;
State &= VAR_DELETED;
Status = UpdateVariableStore (
Global,
Variable->Volatile,
FALSE,
(UINTN) &Variable->CurrPtr->State,
sizeof (UINT8),
&State
);
if (Status == EFI_SUCCESS && !Variable->Volatile && IsUserVariable ((CHAR16 *) (Variable->CurrPtr + 1), &Variable->CurrPtr->VendorGuid)) {
VarSize = ((UINTN) GetNextVariablePtr (Variable->CurrPtr) - (UINTN) Variable->CurrPtr);
if (mSmst == NULL) {
mVariableModuleGlobal->CommonUserVariableTotalSize -= VarSize;
} else {
mSmmVariableGlobal->ProtectedModeVariableModuleGlobal->CommonUserVariableTotalSize -= VarSize;
}
}
}
Done:
if (Status == EFI_SUCCESS && !Variable->Volatile) {
DeleteVariableInHob (VariableName, VendorGuid);
}
if (mSmst != NULL && WriteBuffer != NULL) {
EFI_FREE_POOL (WriteBuffer);
}
return Status;
}
/**
Routine to confirm the existence of factory default.
@retval TRUE The factory default data exists
@retval FALSE The factory default data doesn't exist.
**/
STATIC
BOOLEAN
DoesFactoryDefaultExist (
VOID
)
{
EFI_STATUS Status;
UINTN DataSize;
DataSize = 0;
Status = GetSecureDatabaseDefaultVariables (EFI_PK_DEFAULT_VARIABLE_NAME, &gEfiGlobalVariableGuid, NULL, &DataSize, NULL);
return Status == EFI_BUFFER_TOO_SMALL;
}
/**
This code sets authenticated variable in storage blocks (Volatile or Non-Volatile).
@param VariableName Name of Variable to be found.
@param VendorGuid Variable vendor GUID.
@param Attributes Attribute value of the variable found
@param DataSize Size of Data found. If size is less than the
data, this value contains the required size.
@param Data Data pointer.
@param Global Pointer to VARIABLE_GLOBAL structure
@retval EFI_INVALID_PARAMETER Invalid parameter.
@retval EFI_SUCCESS Set successfully.
@retval EFI_OUT_OF_RESOURCES Resource not enough to set variable.
@retval EFI_NOT_FOUND Not found.
@retval EFI_WRITE_PROTECTED Variable is read-only.
**/
EFI_STATUS
EFIAPI
SetAuthVariable (
IN CHAR16 *VariableName,
IN EFI_GUID *VendorGuid,
IN UINT32 Attributes,
IN UINTN DataSize,
IN VOID *Data,
IN VARIABLE_GLOBAL *Global
)
{
VARIABLE_POINTER_TRACK Variable;
EFI_STATUS Status;
UINTN PayloadSize;
UINTN VariableCount;
if (!FeaturePcdGet (PcdH2OSecureBootSupported) && IsSecureDatabaseVariable (VariableName, VendorGuid)) {
return EFI_WRITE_PROTECTED;
}
//
// Check input parameters.
//
if (VariableName == NULL || VariableName[0] == 0 || VendorGuid == NULL) {
return EFI_INVALID_PARAMETER;
}
if (DataSize != 0 && Data == NULL) {
return EFI_INVALID_PARAMETER;
}
//
// UEFI 2.3.1 - check for incompatible attributes
//
if ((Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) &&
(Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS)) {
return EFI_INVALID_PARAMETER;
}
//
// Make sure if runtime bit is set, boot service bit is set also.
//
if ((Attributes & (EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS)) == EFI_VARIABLE_RUNTIME_ACCESS) {
return EFI_INVALID_PARAMETER;
}
if (IsInsydeSecureVariable (Attributes, DataSize, Data)) {
if (!IsValidInsydeSecureVariable (VariableName, VendorGuid, Attributes, DataSize, Data)) {
return EFI_SECURITY_VIOLATION;
}
PayloadSize = DataSize;
} else if ((Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) == EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) {
if (DataSize < AUTHINFO_SIZE) {
//
// Try to write Authenticated Variable without AuthInfo.
//
return EFI_SECURITY_VIOLATION;
}
PayloadSize = DataSize - AUTHINFO_SIZE;
} else if ((Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) == EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) {
if (DataSize < AUTHINFO2_SIZE (Data)) {
//
// Try to write Authenticated Variable without AuthInfo.
//
return EFI_SECURITY_VIOLATION;
}
PayloadSize = DataSize - AUTHINFO2_SIZE (Data);
} else {
PayloadSize = DataSize;
}
//
// The size of the VariableName, including the Unicode Null in bytes plus
// the DataSize is limited to maximum size of PcdGet32 (PcdMaxHardwareErrorVariableSize)
// bytes for HwErrRec, and PcdGet32 (PcdMaxVariableSize) bytes for the others.
//
if ((Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == EFI_VARIABLE_HARDWARE_ERROR_RECORD) {
if ((PayloadSize > MAX_HARDWARE_ERROR_VARIABLE_SIZE) ||
(sizeof (VARIABLE_HEADER) + StrSize (VariableName) + PayloadSize > MAX_HARDWARE_ERROR_VARIABLE_SIZE)) {
return EFI_INVALID_PARAMETER;
}
//
// According to UEFI spec, HARDWARE_ERROR_RECORD variable name convention should be L"HwErrRecXXXX".
//
if (StrnCmp (VariableName, L"HwErrRec", StrLen (L"HwErrRec")) != 0) {
return EFI_INVALID_PARAMETER;
}
} else {
//
// The size of the VariableName, including the Unicode Null in bytes plus
// the DataSize is limited to maximum size of PcdGet32 (PcdMaxVariableSize) bytes.
//
if ((PayloadSize > MAX_VARIABLE_SIZE) ||
(sizeof (VARIABLE_HEADER) + StrSize (VariableName) + PayloadSize > MAX_VARIABLE_SIZE)) {
return EFI_INVALID_PARAMETER;
}
}
//
// Check whether the input variable is already existed.
//
VariableCount = 0;
Status = FindVariableByLifetimeInAllRegions (VariableName, VendorGuid, &Variable, &VariableCount, Global);
//
// Process different authenticated variables
//
if (IsPkVariable (VariableName, VendorGuid)){
Status = ProcessVarWithPk (VariableName, VendorGuid, Data, DataSize, &Variable, Attributes, TRUE, Global);
if (!EFI_ERROR (Status) && DoesFactoryDefaultExist ()) {
//
// Set "CustomSecurity" variable to 1 indicates secure boot database has been modified by user and machine isn't in factory.
//
Status = UpdateCustomSecurityStatus (1);
}
} else if (IsKekVariable (VariableName, VendorGuid)) {
Status = ProcessVarWithPk (VariableName, VendorGuid, Data, DataSize, &Variable, Attributes, FALSE, Global);
} else if (IsImageSecureDatabaseVariable (VariableName, VendorGuid)) {
if (IsDbtVariable (VariableName, VendorGuid) && (PcdGet64 (PcdOsIndicationsSupported) & EFI_OS_INDICATIONS_TIMESTAMP_REVOCATION) == 0) {
return EFI_WRITE_PROTECTED;
}
if (IsDbrVariable (VariableName, VendorGuid) && (PcdGet64 (PcdOsIndicationsSupported) & EFI_OS_INDICATIONS_START_OS_RECOVERY) == 0) {
return EFI_WRITE_PROTECTED;
}
Status = ProcessVarWithPk (VariableName, VendorGuid, Data, DataSize, &Variable, Attributes, FALSE, Global);
if (EFI_ERROR (Status)) {
Status = ProcessVarWithKek (VariableName, VendorGuid, Data, DataSize, &Variable, Attributes, Global);
}
} else if (IsOsRecoveryOrderVariable (VariableName, VendorGuid) || IsOsRecoveryVariable (VariableName)) {
if ((PcdGet64 (PcdOsIndicationsSupported) & EFI_OS_INDICATIONS_START_OS_RECOVERY) == 0) {
return EFI_WRITE_PROTECTED;
}
Status = ProcessVarWithPk (VariableName, VendorGuid, Data, DataSize, &Variable, Attributes, FALSE, Global);
if (EFI_ERROR (Status)) {
Status = ProcessVarWithKek (VariableName, VendorGuid, Data, DataSize, &Variable, Attributes, Global);
}
if (EFI_ERROR (Status)) {
Status = ProcessVarWithDbr (VariableName, VendorGuid, Data, DataSize, &Variable, Attributes, Global);
}
} else if (IsInsydeSecureVariable (Attributes, DataSize, Data)) {
Status = ProcessInsydeSecureVariable (VariableName, VendorGuid, Data, DataSize, &Variable, Attributes, Global);
} else {
Status = ProcessVariable (VariableName, VendorGuid, Data, DataSize, &Variable, Attributes, Global);
}
if (Status == EFI_SECURITY_VIOLATION && (mSmst || !VariableAtRuntime ())) {
REPORT_STATUS_CODE (
EFI_ERROR_CODE,
EFI_SOFTWARE_EFI_RUNTIME_SERVICE | H2O_AUTH_VARIABLE_VERIFY_FAILED
);
}
return Status;
}
/**
Internal function for set variable through SMI.
@param VariableName Name of Variable to be found
@param VendorGuid Variable vendor GUID
@param Attributes Attribute value of the variable found
@param DataSize Size of Data found. If size is less than the
data, this value contains the required size.
@param Data Data pointer
@retval EFI_INVALID_PARAMETER Invalid parameter
@retval EFI_SUCCESS Set successfully
@retval EFI_OUT_OF_RESOURCES Resource not enough to set variable
@retval EFI_NOT_FOUND Not found
@retval EFI_SECURITY_VIOLATION The variable could not be written due to EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS
set but the AuthInfo does NOT pass the validation check carried out by the firmware.
**/
EFI_STATUS
EFIAPI
SetVariableThroughSmi (
IN CHAR16 *VariableName,
IN EFI_GUID *VendorGuid,
IN UINT32 Attributes,
IN UINTN DataSize,
IN VOID *Data
)
{
UINT8 *WorkingBuf;
UINTN SmmBufSize;
InitCommunicationBufferHeader ();
SmmBufSize = sizeof (SMM_VAR_BUFFER) + MAX_VARIABLE_NAME_SIZE + MAX_VARIABLE_SIZE;
SetMem (mVariableModuleGlobal->SmmVarBuf, SmmBufSize, 0x0);
mVariableModuleGlobal->SmmVarBuf->Signature = SMM_VARIABLE_SIGNATURE;
mVariableModuleGlobal->SmmVarBuf->AccessType = SMM_SET_VARIABLE_SMI_FUN_NUM;
CopyMem (&mVariableModuleGlobal->SmmVarBuf->VarGuid, VendorGuid, sizeof (EFI_GUID));
mVariableModuleGlobal->SmmVarBuf->Attributes = Attributes;
mVariableModuleGlobal->SmmVarBuf->VariableNameSize = StrSize (VariableName);
mVariableModuleGlobal->SmmVarBuf->DataSize = DataSize;
mVariableModuleGlobal->SmmVarBuf->VarChecksum = InternalCalculateSum16 ((UINT8 *)Data, DataSize);
WorkingBuf = (UINT8 *) (mVariableModuleGlobal->SmmVarBuf + 1);
StrCpyS ((CHAR16 *) WorkingBuf, (MAX_VARIABLE_NAME_SIZE + MAX_VARIABLE_SIZE) / sizeof(CHAR16), VariableName);
WorkingBuf += mVariableModuleGlobal->SmmVarBuf->VariableNameSize;
CopyMem (WorkingBuf, Data, DataSize);
mVariableModuleGlobal->SmmVarBuf->Status = EFI_UNSUPPORTED;
SendCommunicateBuffer ();
return mVariableModuleGlobal->SmmVarBuf->Status;
}
/**
Function uses to check BootOrder variable hook mechanism is whether enabled.
@retval TRUE BootOrder variable hook mechanism is enabled.
@return FALSE BootOrder variable hook mechanism is disabled.
**/
STATIC
BOOLEAN
IsBootOrderHookEnabled (
VOID
)
{
return mSmst ? mSmmVariableGlobal->ProtectedModeVariableModuleGlobal->BootOrderVariableHook : mVariableModuleGlobal->BootOrderVariableHook;
}
/**
According to variable name and GUID to Determine the variable is BoorOrder or not.
@param[in] VariableName Name of Variable to be found.
@param[in] VendorGuid Variable vendor GUID.
@retval TRUE This is BoorOrder variable.
@return FALSE This isn't BoorOrder variable.
**/
STATIC
BOOLEAN
IsBootOrderVariable (
IN CHAR16 *VariableName,
IN EFI_GUID *VendorGuid
)
{
if (VariableName == NULL || VendorGuid == NULL) {
return FALSE;
}
if (StrCmp (VariableName, L"BootOrder") == 0 && CompareGuid (VendorGuid, &gEfiGlobalVariableGuid)) {
return TRUE;
}
return FALSE;
}
/**
Get the specific config data from MultiConfig region.
@param RequireKind Find Setup Setting for SETUP_FOR_BIOS_POST or SETUP_FOR_LOAD_DEFAULT.
@param VariableName A pointer to a null-terminated string that is the variable's name.
@param VariableGuid A pointer to an EFI_GUID that is the variable's GUID. The combination of
VariableGuid and VariableName must be unique.
@param Attributes If non-NULL, on return, points to the variable's attributes.
@param DataSize On entry, points to the size in bytes of the Data buffer.
On return, points to the size of the data returned in Data.
@param Data Points to the buffer which will hold the returned variable value.
@retval EFI_SUCCESS Config data found successfully
@retval EFI_NOT_FOUND Config data not found
@retval EFI_INVALID_PARAMETER Data is NULL
@retval EFI_BUFFER_TOO_SMALL The DataSize is too small for the resulting data.
DataSize is updated with the size required for
the specified variable.
**/
EFI_STATUS
GetConfigData (
IN CONST UINT8 RequireKind,
IN CHAR16 *VariableName,
IN EFI_GUID *VariableGuid,
OUT UINT32 *Attributes,
IN OUT UINTN *DataSize,
OUT VOID *Data
)
{
EFI_STATUS Status;
UINT16 ConfigCount;
UINT16 Index;
VOID *NamePtr;
VOID *DataPtr;
UINTN VarDataSize;
Status = EFI_NOT_FOUND;
Index = 0;
ConfigCount = GetConfigCount();
DataPtr = NULL;
for ( ; Index < ConfigCount; Index ++) {
//
// Get Active (Attribute: ACTIVE & BIOS_POST) Full Setup Setting from Multi Config Region
//
Status = GetFullSetupSetting (
RequireKind,
&Index,
*DataSize,
&DataPtr,
NULL,
NULL
);
if (!EFI_ERROR (Status) && DataPtr != NULL) {
NamePtr = (VOID *) GET_VARIABLE_NAME_PTR (DataPtr);
if (CompareMem (VariableName, NamePtr, NameSizeOfVariable (DataPtr)) == 0) {
//
// Get data size
//
VarDataSize = DataSizeOfVariable (DataPtr);
if (*DataSize >= VarDataSize) {
if (Data == NULL) {
return EFI_INVALID_PARAMETER;
}
CopyMem (Data, GetVariableDataPtr (DataPtr), VarDataSize);
if (Attributes != NULL) {
*Attributes = ((VARIABLE_HEADER *)DataPtr)->Attributes;
}
*DataSize = VarDataSize;
Status = VariableServicesSetVariable (
VariableName,
VariableGuid,
((VARIABLE_HEADER *)DataPtr)->Attributes,
*DataSize,
Data
);
return Status;
} else {
*DataSize = VarDataSize;
return EFI_BUFFER_TOO_SMALL;
}
}
}
}
return EFI_NOT_FOUND;
}
/**
This code finds variable in storage blocks (Volatile or Non-Volatile).
@param[in] VariableName Name of Variable to be found.
@param[in] VendorGuid Variable vendor GUID.
@param[out] Attributes Attribute value of the variable found.
@param[in, out] DataSize Size of Data found. If size is less than the
data, this value contains the required size.
@param[out] Data Data pointer.
@retval EFI_INVALID_PARAMETE Invalid parameter.
@retval EFI_SUCCESS Find the specified variable.
@retval EFI_NOT_FOUND Not found.
@retval EFI_BUFFER_TO_SMALL DataSize is too small for the result.
**/
EFI_STATUS
EFIAPI
VariableServicesGetVariable (
IN CHAR16 *VariableName,
IN EFI_GUID * VendorGuid,
OUT UINT32 *Attributes OPTIONAL,
IN OUT UINTN *DataSize,
OUT VOID *Data OPTIONAL
)
{
VARIABLE_POINTER_TRACK Variable;
UINTN VarDataSize;
EFI_STATUS Status;
UINTN VariableCount;
VARIABLE_GLOBAL *Global;
BOOLEAN InteruptEnabled;
if (IsBootOrderHookEnabled () && IsBootOrderVariable (VariableName, VendorGuid)) {
return VariableServicesGetVariable (
L"PhysicalBootOrder",
&gEfiGenericVariableGuid,
Attributes,
DataSize,
Data
);
}
if (VariableName == NULL || VendorGuid == NULL || DataSize == NULL || VariableName[0] == 0) {
return EFI_INVALID_PARAMETER;
}
Global = &mVariableModuleGlobal->VariableBase;
if (VariableAtRuntime () &&
GetVariableStoreSize ((VARIABLE_STORE_HEADER *)(UINTN)Global->NonVolatileVariableBase) == 0xFFFFFFFF) {
return EFI_DEVICE_ERROR;
}
InteruptEnabled = GetInterruptState ();
if (VariableAtRuntime () && InteruptEnabled) {
DisableInterrupts ();
}
if (VariableAtRuntime () && FeaturePcdGet (PcdH2OVariableFlushCacheAtRuntime)) {
WriteBackInvalidateDataCacheRange (
mVariableModuleGlobal->NonVolatileVariableCache,
mVariableModuleGlobal->NonVolatileVariableCacheSize + mVariableModuleGlobal->FactoryDefaultSize + mVariableModuleGlobal->VariableDefaultSize
);
}
AcquireLockOnlyAtBootTime (&mVariableModuleGlobal->VariableBase.VariableServicesLock);
//
// Check Secure Database Default variable first.
//
if (FeaturePcdGet (PcdH2OSecureBootSupported) && IsSecureDatabaseDefaultVariable (VariableName, VendorGuid)) {
Status = GetSecureDatabaseDefaultVariables (VariableName, VendorGuid, Attributes, DataSize, Data);
goto Done;
}
//
// Find existing variable
//
VariableCount = 0;
Status = FindVariableByLifetimeInAllRegions (VariableName, VendorGuid, &Variable, &VariableCount, Global);
if (!(VariableAtRuntime ()) && FeaturePcdGet (PcdMultiConfigSupported) && (Status == EFI_NOT_FOUND)) {
Status = GetConfigData (SETUP_FOR_BIOS_POST, VariableName, VendorGuid, Attributes, DataSize, Data);
if (Status == EFI_NOT_FOUND) {
Status = GetConfigData (SETUP_FOR_LOAD_DEFAULT, VariableName, VendorGuid, Attributes, DataSize, Data);
}
if (!EFI_ERROR (Status)) {
goto Done;
}
}
if (Variable.CurrPtr == NULL || EFI_ERROR (Status)) {
goto Done;
}
if (IsAdminPasswordVariable (VariableName, VendorGuid)) {
//
// Set data size to 1 and contents of data to 1 to indicate the existence of admin password.
//
VarDataSize = sizeof (UINT8);
} else {
VarDataSize = Variable.CurrPtr->DataSize;
}
if (*DataSize >= VarDataSize) {
if (Data == NULL) {
Status = EFI_INVALID_PARAMETER;
goto Done;
}
if (IsAdminPasswordVariable (VariableName, VendorGuid)) {
//
// Set data size to 1 and contents of data to 1 to indicate the existence of admin password.
//
SetMem (Data, sizeof (UINT8), 0x01);
} else {
CopyMem (Data, GetVariableDataPtr (Variable.CurrPtr), VarDataSize);
}
*DataSize = VarDataSize;
Status = EFI_SUCCESS;
} else {
*DataSize = VarDataSize;
Status = EFI_BUFFER_TOO_SMALL;
}
if (Attributes != NULL && Variable.CurrPtr != NULL) {
//
// According to AuditMode and DeployedMode should return EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS.
// But they stored in non-volatile storage to make sure them can be still available after reset system.
//
if (IsReadOnlyVariable (VariableName, VendorGuid) ||
(FeaturePcdGet (PcdH2OCustomizedSecureBootSupported) &&
(IsAuditModeVariable (VariableName, VendorGuid) || IsDeployedModeVariable (VariableName, VendorGuid)))) {
//
// According EFI spec 3.2 Globally defined variables, convert all of read-only variables to
// fit specification definition.
//
*Attributes = EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS;
} else {
*Attributes = (Variable.CurrPtr->Attributes & ~(EFI_VARIABLE_DEFAULT_READY_ONLY | EFI_VARIABLE_INSYDE_AUTHENTICATED_WRITE_ACCESS));
}
}
Done:
ReleaseLockOnlyAtBootTime (&mVariableModuleGlobal->VariableBase.VariableServicesLock);
if (VariableAtRuntime () && InteruptEnabled) {
EnableInterrupts ();
}
return Status;
}
/**
This code Finds the Next available secure boot database default variable.
@param[in, out] VariableNameSize Size of the variable.
@param[in, out] VariableName Pointer to variable name.
@param[in, out] VendorGuid Variable Vendor Guid.
@retval EFI_SUCCESS Invalid parameter.
@retval EFI_BUFFER_TOO_SMALL Find the specified variable.
@retval EFI_NOT_FOUND Not found.
**/
STATIC
EFI_STATUS
GetNextSecureBootDefaultVariableName (
IN OUT UINTN *VariableNameSize,
IN OUT CHAR16 *VariableName,
IN OUT EFI_GUID *VendorGuid
)
{
EFI_STATUS Status;
UINTN Index;
UINTN CurrentIndex;
UINTN DefaultVariableCount;
CHAR16 *DefaultVariableName[] = {
EFI_PK_DEFAULT_VARIABLE_NAME,
EFI_KEK_DEFAULT_VARIABLE_NAME,
EFI_DB_DEFAULT_VARIABLE_NAME,
EFI_DBX_DEFAULT_VARIABLE_NAME,
EFI_DBT_DEFAULT_VARIABLE_NAME,
EFI_DBR_DEFAULT_VARIABLE_NAME
};
Index = 0;
DefaultVariableCount = sizeof (DefaultVariableName) / sizeof (CHAR16 *);
if (VariableName[0] != 0) {
for (Index = 0; Index < DefaultVariableCount; Index++) {
if (StrCmp (DefaultVariableName[Index], VariableName) == 0) {
Index++;
break;
}
}
}
for (CurrentIndex = Index; CurrentIndex < DefaultVariableCount; CurrentIndex++) {
Status = FindSecureDatabaseDefaultVariables (DefaultVariableName[CurrentIndex], &gEfiGlobalVariableGuid, NULL, NULL);
if (!EFI_ERROR (Status)) {
if (StrSize (DefaultVariableName[CurrentIndex]) > *VariableNameSize) {
*VariableNameSize = StrSize (DefaultVariableName[CurrentIndex]);
return EFI_BUFFER_TOO_SMALL;
} else {
*VariableNameSize = StrSize (DefaultVariableName[CurrentIndex]);
CopyMem (VariableName, DefaultVariableName[CurrentIndex], *VariableNameSize);
CopyMem (VendorGuid, &gEfiGlobalVariableGuid, sizeof (EFI_GUID));
return EFI_SUCCESS;
}
}
}
return EFI_NOT_FOUND;
}
/**
This code Finds the first available secure boot database default variable.
@param[in, out] VariableNameSize Size of the variable.
@param[in, out] VariableName Pointer to variable name.
@param[in, out] VendorGuid Variable Vendor Guid.
@retval EFI_SUCCESS Invalid parameter.
@retval EFI_BUFFER_TOO_SMALL Find the specified variable.
@retval EFI_NOT_FOUND Not found.
**/
STATIC
EFI_STATUS
GetFirstSecureBootDefaultVariableName (
IN OUT UINTN *VariableNameSize,
IN OUT CHAR16 *VariableName,
IN OUT EFI_GUID *VendorGuid
)
{
CHAR16 SavedChar;
EFI_STATUS Status;
SavedChar = VariableName[0];
VariableName[0] = 0;
Status = GetNextSecureBootDefaultVariableName (VariableNameSize, VariableName, VendorGuid);
if (EFI_ERROR (Status)) {
VariableName[0] = SavedChar;
}
return Status;
}
/**
Check the variable which pointed by CurrPtr is whether a most value variable.
@param[in, out] CurrPtr Pointer to the header of current variable
@param[in, out] StartPtr Pointer to the start address of variable header.
@param[in, out] EndPtr Pointer to the end address of variable header.
@retval TRUE The input variable is most value variable.
@retval FALSE The input variable isn't most value variable.
**/
BOOLEAN
IsMostValueVariable (
IN VARIABLE_HEADER *CurrPtr,
IN VARIABLE_HEADER *StartPtr,
IN VARIABLE_HEADER *EndPtr
)
{
VARIABLE_POINTER_TRACK Variable;
if (CurrPtr == NULL || StartPtr == NULL || EndPtr == NULL) {
return FALSE;
}
Variable.CurrPtr = CurrPtr;
Variable.StartPtr = StartPtr;
Variable.EndPtr = EndPtr;
return (BOOLEAN) (GetMostValueVariable (&Variable) == CurrPtr);
}
/**
Get the variable count of input variable in variable cache. If the variable
cache is NULL, the variable count is 0.
@param[in] VariableName Name of Variable to be found.
@param[in] VendorGuid Variable vendor GUID.
@return The variable count of input variable.
**/
STATIC
UINTN
GetVariableCountFromVariableCache (
IN CONST CHAR16 *VariableName,
IN CONST EFI_GUID *VendorGuid
)
{
UINTN VariableCount;
VARIABLE_HEADER *Variable;
VARIABLE_HEADER *EndPtr;
if (mVariableModuleGlobal->NonVolatileVariableCache == 0) {
return 0;
}
VariableCount = 0;
Variable = GetStartPointer ((VARIABLE_STORE_HEADER *) mVariableModuleGlobal->NonVolatileVariableCache);
EndPtr = GetNonVolatileEndPointer ((VARIABLE_STORE_HEADER *) mVariableModuleGlobal->NonVolatileVariableCache);
while (IsValidVariableHeaderInVarRegion (Variable, EndPtr)) {
if (Variable->State == VAR_ADDED || Variable->State == (VAR_ADDED & VAR_IN_DELETED_TRANSITION)) {
if (CompareGuid (VendorGuid, &Variable->VendorGuid) &&
StrCmp (VariableName, GET_VARIABLE_NAME_PTR (Variable)) == 0) {
VariableCount++;
}
}
Variable = GetNextVariablePtr (Variable);
}
return VariableCount;
}
/**
Internal function to check if the specific variable can be found in variable HOB.
@param[in] VariableName Name of Variable.
@param[in] VendorGuid Variable vendor GUID.
@retval TRUE Variable can be found in variable HOB.
@retval FALSE Variable is not found in variable HOB.
**/
STATIC
BOOLEAN
IsVariableInHob (
IN CHAR16 *VariableName,
IN EFI_GUID *VendorGuid
)
{
VARIABLE_HEADER *Variable;
VARIABLE_HEADER *EndPtr;
if (VariableName == NULL || VendorGuid == NULL) {
return FALSE;
}
if (mVariableModuleGlobal->HobVariableBase == 0) {
return FALSE;
}
Variable = GetStartPointer ((VARIABLE_STORE_HEADER *)(UINTN)mVariableModuleGlobal->HobVariableBase);
EndPtr = GetNonVolatileEndPointer ((VARIABLE_STORE_HEADER *)(UINTN)mVariableModuleGlobal->HobVariableBase);
while (IsValidVariableHeaderInVarRegion (Variable, EndPtr)) {
if (CompareGuid (VendorGuid, &Variable->VendorGuid) && StrCmp (VariableName, GET_VARIABLE_NAME_PTR (Variable)) == 0 &&
Variable->State == VAR_ADDED) {
return TRUE;
}
Variable = GetNextVariablePtr (Variable);
}
return FALSE;
}
/**
This code Finds the Next available variable.
@param[in, out] VariableNameSize Size of the variable name buffer.
@param[in, out] VariableName On input, supplies the last VariableName that was returned by GetNextVariableName().
On output, returns the Null-terminated string of the current variable.
@param[in, out] VendorGuid On input, supplies the last VendorGuid that was returned by GetNextVariableName().
On output, returns the VendorGuid of the current variable.
@retval EFI_SUCCESS Find the specified variable.
@retval EFI_INVALID_PARAMETER Invalid parameter.
@retval EFI_NOT_FOUND Not found.
@retval EFI_BUFFER_TO_SMALL DataSize is too small for the result.
**/
EFI_STATUS
EFIAPI
VariableServicesGetNextVariableName (
IN OUT UINTN *VariableNameSize,
IN OUT CHAR16 *VariableName,
IN OUT EFI_GUID *VendorGuid
)
{
VARIABLE_POINTER_TRACK Variable;
UINTN VarNameSize;
EFI_STATUS Status;
UINTN VariableCount;
VARIABLE_GLOBAL *Global;
BOOLEAN InteruptEnabled;
UINTN MaxLen;
VARIABLE_STORE_TYPE Type;
VARIABLE_STORE_HEADER *VariableStoreHeader[VariableStoreTypeMax];
if (VariableNameSize == NULL || VariableName == NULL || VendorGuid == NULL) {
return EFI_INVALID_PARAMETER;
}
MaxLen = *VariableNameSize / sizeof (CHAR16);
if ((MaxLen == 0) || (StrnLenS (VariableName, MaxLen) == MaxLen)) {
return EFI_INVALID_PARAMETER;
}
InteruptEnabled = GetInterruptState ();
if (VariableAtRuntime () && InteruptEnabled) {
DisableInterrupts ();
}
if (VariableAtRuntime () && FeaturePcdGet (PcdH2OVariableFlushCacheAtRuntime)) {
WriteBackInvalidateDataCacheRange (
mVariableModuleGlobal->NonVolatileVariableCache,
mVariableModuleGlobal->NonVolatileVariableCacheSize + mVariableModuleGlobal->FactoryDefaultSize + mVariableModuleGlobal->VariableDefaultSize
);
}
AcquireLockOnlyAtBootTime (&mVariableModuleGlobal->VariableBase.VariableServicesLock);
if (FeaturePcdGet (PcdH2OSecureBootSupported) && IsSecureDatabaseDefaultVariable (VariableName, VendorGuid)) {
Status = GetNextSecureBootDefaultVariableName (VariableNameSize, VariableName, VendorGuid);
goto Done;
}
Global = &mVariableModuleGlobal->VariableBase;
ZeroMem (&Variable, sizeof (Variable));
VariableCount = 0;
Status = FindVariableByLifetimeEx (VariableName, VendorGuid, &Variable, &VariableCount, Global, FALSE);
if (Variable.CurrPtr == NULL || EFI_ERROR (Status)) {
Status = GetNextDefaultVariableName (VariableNameSize, VariableName, VendorGuid);
//
// According UEFI specification 2.7. The input values of VariableName and VendorGuid are not a name
// and GUID of an existing variable should return EFI_INVALID_PARAMETER.
//
if (Status == EFI_SUCCESS || Status == EFI_BUFFER_TOO_SMALL || Status == EFI_INVALID_PARAMETER) {
goto Done;
}
if (!FeaturePcdGet (PcdH2OSecureBootSupported)) {
Status = EFI_NOT_FOUND;
goto Done;
}
Status = GetFirstSecureBootDefaultVariableName (VariableNameSize, VariableName, VendorGuid);
goto Done;
}
//
// Get variable count from variable cache first and only try to update variable pointer if system
// has multiple same variables. It can prevent from always getting variable from MMIO.
//
if ((Variable.CurrPtr->Attributes & EFI_VARIABLE_NON_VOLATILE) == EFI_VARIABLE_NON_VOLATILE &&
GetVariableCountFromVariableCache (GET_VARIABLE_NAME_PTR (Variable.CurrPtr), &Variable.CurrPtr->VendorGuid) > 1) {
Variable.CurrPtr = GetMostValueVariable (&Variable);
}
if (VariableName[0] != 0) {
//
// If variable name is not NULL, get next variable
//
Variable.CurrPtr = GetNextVariablePtr (Variable.CurrPtr);
}
//
// 0: HOB, 1: Non-Volatile, 2: Volatile.
// The index and attributes mapping must be kept in this order as FindVariable
// makes use of this mapping to implement search algorithm.
//
VariableStoreHeader[VariableStoreTypeHob] = (VARIABLE_STORE_HEADER *)((UINTN)mVariableModuleGlobal->HobVariableBase);
if (mVariableModuleGlobal->NonVolatileVariableCache == NULL) {
VariableStoreHeader[VariableStoreTypeNv] = (VARIABLE_STORE_HEADER *)((UINTN)Global->NonVolatileVariableBase);
} else {
VariableStoreHeader[VariableStoreTypeNv] = (VARIABLE_STORE_HEADER *)mVariableModuleGlobal->NonVolatileVariableCache;
}
VariableStoreHeader[VariableStoreTypeVolatile] = (VARIABLE_STORE_HEADER *)((UINTN)Global->VolatileVariableBase);
while (TRUE) {
//
// Switch from HOB to Non-Volatile to Volatile.
//
while ((Variable.CurrPtr >= Variable.EndPtr) ||
(Variable.CurrPtr == NULL) ||
!IsValidVariableHeader (Variable.CurrPtr)
) {
//
// Find current storage index
//
for (Type = (VARIABLE_STORE_TYPE) 0; Type < VariableStoreTypeMax; Type++) {
if ((VariableStoreHeader[Type] != NULL) && (Variable.StartPtr == GetStartPointer (VariableStoreHeader[Type]))) {
break;
}
}
ASSERT (Type < VariableStoreTypeMax);
//
// Switch to next storage
//
for (Type++; Type < VariableStoreTypeMax; Type++) {
if (VariableStoreHeader[Type] != NULL) {
break;
}
}
//
// Capture the case that
// 1. current storage is the last one, or
// 2. no further storage
//
if (Type == VariableStoreTypeMax) {
//
// Skip finding security default variables if PcdH2OSecureBootSupported PCD is FALSE.
//
Status = GetFirstDefaultVariableName (VariableNameSize, VariableName, VendorGuid);
if (Status == EFI_SUCCESS || Status == EFI_BUFFER_TOO_SMALL) {
goto Done;
}
if (!FeaturePcdGet (PcdH2OSecureBootSupported)) {
Status = EFI_NOT_FOUND;
goto Done;
}
Status = GetFirstSecureBootDefaultVariableName (VariableNameSize, VariableName, VendorGuid);
goto Done;
}
Variable.StartPtr = GetStartPointer (VariableStoreHeader[Type]);
Variable.EndPtr = GetEndPointer (VariableStoreHeader[Type]);
Variable.CurrPtr = Variable.StartPtr;
}
//
// Variable is found
// Get variable count from variable cache first and only try to variable pointer if system
// has multiple same variables. It can prevent from always getting variable from MMIO.
//
if (IsValidVariableHeaderInVarRegion (Variable.CurrPtr, Variable.EndPtr) &&
(Variable.CurrPtr->State == VAR_ADDED || Variable.CurrPtr->State == (VAR_ADDED & VAR_IN_DELETED_TRANSITION)) &&
(GetVariableCountFromVariableCache (GET_VARIABLE_NAME_PTR (Variable.CurrPtr), &Variable.CurrPtr->VendorGuid) == 1 ||
IsMostValueVariable (Variable.CurrPtr, Variable.StartPtr, Variable.EndPtr))) {
if (Variable.StartPtr == GetStartPointer (VariableStoreHeader[VariableStoreTypeNv]) &&
IsVariableInHob (GET_VARIABLE_NAME_PTR (Variable.CurrPtr), &Variable.CurrPtr->VendorGuid)) {
Variable.CurrPtr = GetNextVariablePtr (Variable.CurrPtr);
continue;
}
ASSERT (Variable.CurrPtr->NameSize == StrSize (GET_VARIABLE_NAME_PTR (Variable.CurrPtr)));
if (!(VariableAtRuntime () && !(Variable.CurrPtr->Attributes & EFI_VARIABLE_RUNTIME_ACCESS)) &&
Variable.CurrPtr->NameSize == StrSize (GET_VARIABLE_NAME_PTR (Variable.CurrPtr))) {
VarNameSize = Variable.CurrPtr->NameSize;
if (VarNameSize <= *VariableNameSize) {
CopyMem (
VariableName,
GET_VARIABLE_NAME_PTR (Variable.CurrPtr),
VarNameSize
);
CopyMem (
VendorGuid,
&Variable.CurrPtr->VendorGuid,
sizeof (EFI_GUID)
);
Status = EFI_SUCCESS;
} else {
Status = EFI_BUFFER_TOO_SMALL;
}
*VariableNameSize = VarNameSize;
goto Done;
}
}
Variable.CurrPtr = GetNextVariablePtr (Variable.CurrPtr);
}
Status = EFI_NOT_FOUND;
Done:
ReleaseLockOnlyAtBootTime (&mVariableModuleGlobal->VariableBase.VariableServicesLock);
if (VariableAtRuntime () && InteruptEnabled) {
EnableInterrupts ();
}
return Status;
}
/**
No access attribute (0) is used to delete variable. This function is used update no access
attribute to correct attribute if variable exists and is authenticated variable and the
variable data format is correct.
@param VariableName Name of Variable to be found.
@param VendorGuid Variable vendor GUID.
@param DataSize Size of Data found. If size is less than the
data, this value contains the required size.
@param Data Data pointer.
@param Attributes Pointer for attributes.
[in]: must be 0 to indicate this is no access attributes.
[out]: the attributes of pre-exist variable.
@retval EFI_SUCCESS Update attribute successful.
@retval EFI_INVALID_PARAMETER Any input parameter is invalid.
@retval EFI_NOT_FOUND Cannot find pre-exist variable.
**/
EFI_STATUS
UpdateNoAccessAttribute (
IN CHAR16 *VariableName,
IN EFI_GUID *VendorGuid,
IN UINTN DataSize,
IN VOID *Data,
IN OUT UINT32 *Attributes
)
{
UINTN VariableCount;
VARIABLE_POINTER_TRACK Variable;
EFI_STATUS Status;
BOOLEAN IsAuthenVariable;
if (VariableName == NULL || VariableName[0] == 0 || VendorGuid == NULL ||
Attributes == NULL || *Attributes != 0) {
return EFI_INVALID_PARAMETER;
}
VariableCount = 0;
Status = FindVariableByLifetimeInAllRegions (VariableName, VendorGuid, &Variable, &VariableCount, &mVariableModuleGlobal->VariableBase);
if (!EFI_ERROR (Status)) {
IsAuthenVariable = FALSE;
if ((Variable.CurrPtr->Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) == EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) {
IsAuthenVariable = TRUE;
if (DataSize != AUTHINFO_SIZE) {
return EFI_INVALID_PARAMETER;
}
} else if ((Variable.CurrPtr->Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) == EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) {
IsAuthenVariable = TRUE;
//
// Check datasize to prevent from reading data from no access address. If we don't do this check, any access
// this no access address in runtime will cause blue screen. (ex: WHCK8324 SecureBoot manual test 05-ClearTestConfiguration)
//
if (DataSize == 0 || Data == NULL || DataSize != AUTHINFO2_SIZE (Data)) {
return EFI_INVALID_PARAMETER;
}
} else if ((Variable.CurrPtr->Attributes & EFI_VARIABLE_DEFAULT_READY_ONLY) == EFI_VARIABLE_DEFAULT_READY_ONLY) {
return EFI_WRITE_PROTECTED;
}
if (IsAuthenVariable) {
*Attributes = Variable.CurrPtr->Attributes;
}
}
return Status;
}
/**
Internal function to check system is whether executing runtime code in SMM.
@retval TRUE System executes runtime code in SMM.
@retval FALSE System isn't in SMM or run SMM code in SMM.
**/
STATIC
BOOLEAN
DoesRunRuntimeCodeInSmm (
VOID
)
{
EFI_SMM_BASE2_PROTOCOL *SmmBase;
BOOLEAN InSmm;
EFI_STATUS Status;
if (mSmst != NULL) {
return FALSE;
}
if (VariableAtRuntime ()) {
return FALSE;
}
Status = gBS->LocateProtocol (
&gEfiSmmBase2ProtocolGuid,
NULL,
(VOID **) &SmmBase
);
if (EFI_ERROR (Status)) {
return FALSE;
}
InSmm = FALSE;
SmmBase->InSmm (SmmBase, &InSmm);
return InSmm;
}
/**
Internal function to check is whether PK variable exist.
@retval TRUE PK variable exist.
@retval FALSE PK variable doesn't exist.
**/
BOOLEAN
DoesPkExist (
VOID
)
{
VARIABLE_POINTER_TRACK Variable;
EFI_STATUS Status;
UINTN VariableCount;
Status = FindVariableInAllRegions (
EFI_PLATFORM_KEY_NAME,
&gEfiGlobalVariableGuid,
&Variable,
&VariableCount,
&mVariableModuleGlobal->VariableBase
);
return Status == EFI_SUCCESS;
}
/**
Update related variables if any of secure modes variable is changed.
@param[in] VariableName A Null-terminated string that is the name of the vendor's variable.
@param[in] VendorGuid A unique identifier for the vendor.
**/
VOID
UpdateStateForModeVariablesChanged (
IN CHAR16 *VariableName,
IN EFI_GUID *VendorGuid
)
{
VARIABLE_POINTER_TRACK Variable;
EFI_STATUS Status;
UINTN VariableCount;
UINT8 *AuditMode;
UINT8 *DeployedMode;
UINT8 *SetupMode;
if ((!FeaturePcdGet (PcdH2OSecureBootSupported)) ||
(!IsAuditModeVariable (VariableName, VendorGuid) && !IsDeployedModeVariable (VariableName, VendorGuid))) {
return;
}
//
// Needn't do anything if user deletes AuditMode or DeployedMode variable.
// (Not allow to delete AuditMode or DeployedMode even if the value of these variables is 0).
//
Status = FindVariableByLifetime (VariableName, VendorGuid, &Variable, &VariableCount, &mVariableModuleGlobal->VariableBase);
if (Status != EFI_SUCCESS) {
return;
}
if (IsAuditModeVariable (VariableName, VendorGuid)) {
//
// Needn't do anything if user creates new AuditMode variable with value 0.
// It means the secure boot modes isn't changed.
//
AuditMode = GetVariableDataPtr (Variable.CurrPtr);
if (*AuditMode == 0) {
return;
}
//
// Change related variable if system wants to operate in audit mode.
//
Status = FindVariableByLifetime (
EFI_SETUP_MODE_NAME,
&gEfiGlobalVariableGuid,
&Variable,
&VariableCount,
&mVariableModuleGlobal->VariableBase
);
if (Status != EFI_SUCCESS) {
return;
}
SetupMode = GetVariableDataPtr (Variable.CurrPtr);
//
// 1. Update AuditMode and DeployedMode to read-only if system wants to operate in audit mode.
//
UpdateAuditModeProperty (VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY);
UpdateDeployedModeProperty (VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY);
if (*SetupMode == 0) {
//
// 2.If system operates in user mode previously, need delete PK variable, change SetupMode
// to 1 and SecureBoot to 0.
//
Status = FindVariableByLifetime (
EFI_PLATFORM_KEY_NAME,
&gEfiGlobalVariableGuid,
&Variable,
&VariableCount,
&mVariableModuleGlobal->VariableBase
);
ASSERT (Status == EFI_SUCCESS);
Status = UpdateVariable (
EFI_PLATFORM_KEY_NAME,
&gEfiGlobalVariableGuid,
NULL,
0,
0,
0,
0,
&Variable,
NULL,
&mVariableModuleGlobal->VariableBase
);
ASSERT (Status == EFI_SUCCESS);
UpdatePlatformMode (SETUP_MODE, &mVariableModuleGlobal->VariableBase);
UpdatePlatformBootMode (SECURE_BOOT_MODE_DISABLE, &mVariableModuleGlobal->VariableBase);
//
// Update status to indicate security signature database is modified.
//
UpdateCustomSecurityStatus (1);
}
} else if (IsDeployedModeVariable (VariableName, VendorGuid)) {
//
// Needn't do anything if user creates new Deployed variable with value 0.
// It means the secure boot modes isn't changed.
//
DeployedMode = GetVariableDataPtr (Variable.CurrPtr);
if (*DeployedMode == 0) {
return;
}
//
// Update AuditMode and DeployedMode to read-only if system wants to operate in deployed mode.
//
UpdateAuditModeProperty (VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY);
UpdateDeployedModeProperty (VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY);
}
}
/**
This code sets variable in storage blocks (Volatile or Non-Volatile).
@param[in] VariableName Name of Variable to be found.
@param[in] VendorGuid Variable vendor GUID.
@param[in] Attributes Attribute value of the variable found
@param[in] DataSize Size of Data to be set.
@param[in] Data Data pointer.
@retval EFI_INVALID_PARAMETER Invalid parameter.
@retval EFI_SUCCESS Set successfully.
@retval EFI_OUT_OF_RESOURCES Resource not enough to set variable.
@retval EFI_NOT_FOUND Not found.
@retval EFI_WRITE_PROTECTED Variable is read-only.
**/
EFI_STATUS
EFIAPI
VariableServicesSetVariable (
IN CHAR16 *VariableName,
IN EFI_GUID *VendorGuid,
IN UINT32 Attributes,
IN UINTN DataSize,
IN VOID *Data
)
{
VARIABLE_POINTER_TRACK Variable;
EFI_STATUS Status;
UINTN VariableCount;
VARIABLE_GLOBAL *Global;
UINTN PayloadSize;
if (mSmst == NULL && !mVariableModuleGlobal->VariableWriteReady && (Attributes & EFI_VARIABLE_NON_VOLATILE) != 0) {
return EFI_NOT_READY;
}
if (IsBootOrderHookEnabled () && IsBootOrderVariable (VariableName, VendorGuid)) {
return VariableServicesSetVariable (
L"PhysicalBootOrder",
&gEfiGenericVariableGuid,
Attributes,
DataSize,
Data
);
}
if (VariableName == NULL || VariableName[0] == 0 || VendorGuid == NULL) {
return EFI_INVALID_PARAMETER;
}
Global = &mVariableModuleGlobal->VariableBase;
if (VariableAtRuntime () &&
GetVariableStoreSize ((VARIABLE_STORE_HEADER *)(UINTN)Global->NonVolatileVariableBase) == 0xFFFFFFFF) {
return EFI_DEVICE_ERROR;
}
//
// Check for reserved bit in variable attribute.
// EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS is deprecated but we still allow
// the delete operation of common authenticated variable at user physical presence.
// So leave EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS attribute check to AuthVariableLib
//
if ((Attributes & (~(EFI_VARIABLE_ATTRIBUTES_MASK | EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS))) != 0) {
return EFI_INVALID_PARAMETER;
}
//
// Make sure if runtime bit is set, boot service bit is set also
//
if ((Attributes & (EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS)) == EFI_VARIABLE_RUNTIME_ACCESS) {
if ((Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) != 0) {
return EFI_UNSUPPORTED;
} else {
return EFI_INVALID_PARAMETER;
}
}
//
// EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS and EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS attribute
// cannot be set both.
//
if (((Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) == EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS)
&& ((Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) == EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS)) {
return EFI_UNSUPPORTED;
}
PayloadSize = DataSize;
//
// The size of the VariableName, including the Unicode Null in bytes plus
// the DataSize is limited to maximum size of MAX_HARDWARE_ERROR_VARIABLE_SIZE
// bytes for HwErrRec, and MAX_VARIABLE_SIZE bytes for the others.
//
if ((Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == EFI_VARIABLE_HARDWARE_ERROR_RECORD) {
if ((DataSize > MAX_HARDWARE_ERROR_VARIABLE_SIZE) ||
(sizeof (VARIABLE_HEADER) + StrSize (VariableName) + DataSize > MAX_HARDWARE_ERROR_VARIABLE_SIZE)) {
return EFI_INVALID_PARAMETER;
}
} else {
if ((Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) == EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) {
if (!IsInsydeSecureVariable (Attributes, DataSize, Data)) {
return EFI_UNSUPPORTED;
} else {
if (DataSize < sizeof (WIN_CERTIFICATE_UEFI_GUID) - sizeof (UINT8)) {
return EFI_SECURITY_VIOLATION;
}
PayloadSize = DataSize - (sizeof (WIN_CERTIFICATE_UEFI_GUID) - sizeof (UINT8));
}
} else if ((Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) == EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) {
//
// Sanity check for EFI_VARIABLE_AUTHENTICATION_2 descriptor.
//
if (DataSize < OFFSET_OF_AUTHINFO2_CERT_DATA ||
((EFI_VARIABLE_AUTHENTICATION_2 *) Data)->AuthInfo.Hdr.dwLength > DataSize - (OFFSET_OF (EFI_VARIABLE_AUTHENTICATION_2, AuthInfo)) ||
((EFI_VARIABLE_AUTHENTICATION_2 *) Data)->AuthInfo.Hdr.dwLength < OFFSET_OF (WIN_CERTIFICATE_UEFI_GUID, CertData)) {
return EFI_SECURITY_VIOLATION;
}
//
// The VariableSpeculationBarrier() call here is to ensure the above sanity
// check for the EFI_VARIABLE_AUTHENTICATION_2 descriptor has been completed
// before the execution of subsequent codes.
//
VariableSpeculationBarrier ();
PayloadSize = DataSize - AUTHINFO2_SIZE (Data);
}
if ((UINTN)(~0) - PayloadSize < StrSize(VariableName)){
//
// Prevent whole variable size overflow
//
return EFI_INVALID_PARAMETER;
}
//
// The size of the VariableName, including the Unicode Null in bytes plus
// the DataSize is limited to maximum size of MAX_VARIABLE_SIZE bytes.
//
if (sizeof (VARIABLE_HEADER) + StrSize (VariableName) + PayloadSize > MAX_VARIABLE_SIZE) {
return EFI_INVALID_PARAMETER;
}
if (GetMaxUserVariableSpace () != 0 && (Attributes & EFI_VARIABLE_NON_VOLATILE) == EFI_VARIABLE_NON_VOLATILE &&
IsUserVariable (VariableName, VendorGuid)) {
if (sizeof (VARIABLE_HEADER) + StrSize (VariableName) + PayloadSize + GetUserVariableTotalSize () > GetMaxUserVariableSpace ()) {
RecordVarErrorFlag (VAR_ERROR_FLAG_SYSTEM_ERROR, VariableName, VendorGuid, Attributes, PayloadSize);
return EFI_OUT_OF_RESOURCES;
}
}
}
if (IsVariableLocked (VariableName, VendorGuid)) {
return EFI_WRITE_PROTECTED;
}
if (mSmst == NULL) {
Status = ValidateSetVariable (
VariableName,
VendorGuid,
Attributes,
DataSize,
Data
);
if (EFI_ERROR (Status)) {
return Status;
}
}
Status = InternalVarCheckSetVariableCheck (VariableName, VendorGuid, Attributes, PayloadSize, (VOID *) ((UINTN) Data + DataSize - PayloadSize));
if (EFI_ERROR (Status)) {
return Status;
}
AcquireLockOnlyAtBootTime (&mVariableModuleGlobal->VariableBase.VariableServicesLock);
if (VariableAtRuntime () && FeaturePcdGet (PcdH2OVariableFlushCacheAtRuntime)) {
WriteBackInvalidateDataCacheRange (
mVariableModuleGlobal->NonVolatileVariableCache,
mVariableModuleGlobal->NonVolatileVariableCacheSize + mVariableModuleGlobal->FactoryDefaultSize + mVariableModuleGlobal->VariableDefaultSize
);
}
if (Global->ReentrantState != 0) {
ReleaseLockOnlyAtBootTime (&mVariableModuleGlobal->VariableBase.VariableServicesLock);
return EFI_ACCESS_DENIED;
}
Global->ReentrantState++;
if (FeaturePcdGet (PcdH2OCustomizedSecureBootSupported) &&
(IsAuditModeVariable (VariableName, VendorGuid) || IsDeployedModeVariable (VariableName, VendorGuid))) {
//
// For AuditMode and DeployedMode variables the value should be 0 or 1
//
if ((Attributes == 0 || DataSize == 0) || (*((UINT8 *) Data) != 0 && *((UINT8 *) Data) != 1)) {
Status = EFI_INVALID_PARAMETER;
goto Done;
}
Attributes = VARIABLE_ATTRIBUTE_NV_BS_RT;
}
//
// Some CPU spends much more time to access normal memory region if system is in SMM. Therefore, we check
// normal nonvolatile variable data in protected mode and don't call SetupVariable in SMM if variable data
// and attributes are all the same.
//
if ((mDxeSmmReadyToLockBeforeTriggered || mReadyToBootEventSignaled) && mSmst == NULL && mVariableModuleGlobal->SmmCodeReady &&
(Attributes == (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS) ||
Attributes == (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS))) {
Status = FindVariable (
VariableName,
VendorGuid,
&Variable,
&VariableCount,
Global
);
if (!EFI_ERROR (Status) && Variable.CurrPtr->Attributes == Attributes &&
!AddressInVariableHobRegion ((EFI_PHYSICAL_ADDRESS)(UINTN)Variable.CurrPtr) &&
DataSizeOfVariable (Variable.CurrPtr) == DataSize && (CompareMem (Data, GetVariableDataPtr (Variable.CurrPtr), DataSize) == 0)) {
Status = EFI_SUCCESS;
goto Done;
}
}
//
// Some CPU spends much more time to access normal memory region if system is in SMM. Therefore, we always set
// volatile variable in protected mode to enhance performance.
//
if ((mDxeSmmReadyToLockBeforeTriggered || mReadyToBootEventSignaled || IsInsydeSecureVariable (Attributes, DataSize, Data) ||
IsAdminPasswordVariable (VariableName, VendorGuid) || IsAnyMorVariable (VariableName, VendorGuid)) &&
mSmst == NULL && mSmmCommunication != NULL && mVariableModuleGlobal->SmmCodeReady &&
((Attributes & EFI_VARIABLE_NON_VOLATILE) == EFI_VARIABLE_NON_VOLATILE || Attributes == 0)) {
if (Attributes != 0 && (IsAuditModeVariable (VariableName, VendorGuid) || IsDeployedModeVariable (VariableName, VendorGuid))) {
Attributes = VARIABLE_ATTRIBUTE_BS_RT;
}
if (!DoesRunRuntimeCodeInSmm ()) {
Status = SetVariableThroughSmi (
VariableName,
VendorGuid,
Attributes,
DataSize,
Data
);
} else {
Status = mVariableModuleGlobal->SmmSetVariable (
VariableName,
VendorGuid,
Attributes,
DataSize,
Data
);
}
goto Done;
}
if (mSmst != NULL || mVariableModuleGlobal->VariableWriteReady) {
SyncAuthData (Global);
}
//
// Special Handling for MOR Lock variable.
//
Status = SetVariableCheckHandlerMor (VariableName, VendorGuid, Attributes, DataSize, Data);
if (Status == EFI_ALREADY_STARTED) {
//
// EFI_ALREADY_STARTED means the SetVariable() action is handled inside of SetVariableCheckHandlerMor().
// Variable driver can just return SUCCESS.
//
Status = EFI_SUCCESS;
goto Done;
}
if (EFI_ERROR (Status)) {
goto Done;
}
if (VariableAtRuntime () || Attributes != 0) {
VariableCount = 0;
Status = FindVariableInAllRegions (
VariableName,
VendorGuid,
&Variable,
&VariableCount,
Global
);
if (!EFI_ERROR (Status)) {
//
// Add policy to make sure we cannot update any variable which attribute doesn't have
// EFI_VARIABLE_RUNTIME_ACCESS at runtime.
//
if (VariableAtRuntime () && (Variable.CurrPtr->Attributes & EFI_VARIABLE_RUNTIME_ACCESS) == 0) {
Status = EFI_WRITE_PROTECTED;
goto Done;
}
//
// To prevent updating variable which with RO attribute in variable default region,
// return EFI_WRITE_PROTECTED directly in this case.
//
if ((Variable.CurrPtr->Attributes & EFI_VARIABLE_DEFAULT_READY_ONLY) == EFI_VARIABLE_DEFAULT_READY_ONLY) {
Status = EFI_WRITE_PROTECTED;
goto Done;
}
//
// If a preexisting variable is rewritten with different attributes, SetVariable() shall not
// modify the variable and shall return EFI_INVALID_PARAMETER. Two exceptions to this rule:
// 1. No access attributes specified
// 2. The only attribute differing is EFI_VARIABLE_APPEND_WRITE
// 3. Previous variable with EFI_VARIABLE_DEFAULT_READY_ONLY attribute (this attribtue must be different)
//
if (Attributes != 0 && (Attributes & (~EFI_VARIABLE_APPEND_WRITE)) != Variable.CurrPtr->Attributes) {
Status = EFI_INVALID_PARAMETER;
goto Done;
}
}
}
if (Attributes == 0) {
Status = UpdateNoAccessAttribute (
VariableName,
VendorGuid,
DataSize,
Data,
&Attributes
);
if (EFI_ERROR (Status)) {
goto Done;
}
if (mSmst == NULL && !mVariableModuleGlobal->VariableWriteReady && (Attributes & EFI_VARIABLE_NON_VOLATILE) != 0) {
return EFI_NOT_READY;
}
}
VariableCount = 0;
Status = FindVariableByLifetimeInAllRegions (VariableName, VendorGuid, &Variable, &VariableCount, Global);
if (Status == EFI_INVALID_PARAMETER) {
goto Done;
}
//
// To prevent calling DeleteAllOldVariable () spend too much time, Getting variable count from variable cache
// to detremine need call DeleteAllOldVariable () or not.
//
if (Variable.CurrPtr != NULL && (Variable.CurrPtr->Attributes & EFI_VARIABLE_NON_VOLATILE) &&
GetVariableCountFromVariableCache (VariableName, VendorGuid) > 1) {
DeleteAllOldVariable (VariableName, VendorGuid, Global);
if (Variable.CurrPtr != GetMostValueVariable (&Variable)) {
FindVariableByLifetimeInAllRegions (VariableName, VendorGuid, &Variable, &VariableCount, Global);
}
}
if (VariableAtRuntime () && Attributes && (!(Attributes & EFI_VARIABLE_RUNTIME_ACCESS) || !(Attributes & EFI_VARIABLE_NON_VOLATILE))) {
//
// Runtime but Attribute is not Runtime or is volatile
//
Status = EFI_INVALID_PARAMETER;
} else if ((Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) || (Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) ||
IsSecureDatabaseVariable (VariableName, VendorGuid)) {
Status = SetAuthVariable (
VariableName,
VendorGuid,
Attributes,
DataSize,
Data,
Global
);
} else {
//
// Only can update variable directly when variable doesn't exist or exist variable doesn't authenticated variable
//
Status = EFI_WRITE_PROTECTED;
if ((Variable.CurrPtr == NULL) || (Variable.CurrPtr != NULL && (Variable.CurrPtr->Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) == 0 &&
(Variable.CurrPtr->Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) == 0)) {
Status = UpdateVariable (
VariableName,
VendorGuid,
Data,
DataSize,
Attributes,
0,
0,
&Variable,
0,
Global
);
if (FeaturePcdGet (PcdH2OCustomizedSecureBootSupported) && Status == EFI_SUCCESS) {
UpdateStateForModeVariablesChanged (VariableName, VendorGuid);
}
}
}
Done:
Global->ReentrantState--;
ReleaseLockOnlyAtBootTime (&mVariableModuleGlobal->VariableBase.VariableServicesLock);
return Status;
}
/**
This code returns information about the EFI variables.
@param Attributes Attributes bitmask to specify the type of variables
on which to return information.
@param MaximumVariableStorageSize Pointer to the maximum size of the storage space available
for the EFI variables associated with the attributes specified.
@param RemainingVariableStorageSize Pointer to the remaining size of the storage space available
for EFI variables associated with the attributes specified.
@param MaximumVariableSize Pointer to the maximum size of an individual EFI variables
@retval EFI_INVALID_PARAMETER An invalid combination of attribute bits was supplied.
@retval EFI_SUCCESS Query successfully.
@retval EFI_UNSUPPORTED The attribute is not supported on this platform.
**/
EFI_STATUS
EFIAPI
VariableServicesQueryVariableInfo (
IN UINT32 Attributes,
OUT UINT64 *MaximumVariableStorageSize,
OUT UINT64 *RemainingVariableStorageSize,
OUT UINT64 *MaximumVariableSize
)
{
VARIABLE_HEADER *Variable;
VARIABLE_HEADER *NextVariable;
VARIABLE_HEADER *VariableEndPointer;
UINT64 VariableSize;
VARIABLE_STORE_HEADER *VariableStoreHeader;
VARIABLE_GLOBAL *Global;
UINTN VariableStoreSize;
BOOLEAN InteruptEnabled;
if(MaximumVariableStorageSize == NULL || RemainingVariableStorageSize == NULL || MaximumVariableSize == NULL || Attributes == 0) {
return EFI_INVALID_PARAMETER;
}
if ((Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) != 0) {
//
// Deprecated attribute, make this check as highest priority.
//
return EFI_UNSUPPORTED;
}
if((Attributes & (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_HARDWARE_ERROR_RECORD)) == 0) {
//
// Make sure the Attributes combination is supported by the platform.
//
return EFI_UNSUPPORTED;
} else if ((Attributes & (EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS)) == EFI_VARIABLE_RUNTIME_ACCESS) {
//
// Make sure if runtime bit is set, boot service bit is set also.
//
return EFI_INVALID_PARAMETER;
} else if (VariableAtRuntime () && !(Attributes & EFI_VARIABLE_RUNTIME_ACCESS)) {
//
// Make sure RT Attribute is set if we are in Runtime phase.
//
return EFI_INVALID_PARAMETER;
}
//
// In current code doesn't support EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS.
//
else if ((Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) != 0) {
return EFI_INVALID_PARAMETER;
} else if (Attributes == EFI_VARIABLE_NON_VOLATILE) {
return EFI_INVALID_PARAMETER;
}
InteruptEnabled = GetInterruptState ();
if (VariableAtRuntime () && InteruptEnabled) {
DisableInterrupts ();
}
if (VariableAtRuntime () && FeaturePcdGet (PcdH2OVariableFlushCacheAtRuntime)) {
WriteBackInvalidateDataCacheRange (
mVariableModuleGlobal->NonVolatileVariableCache,
mVariableModuleGlobal->NonVolatileVariableCacheSize + mVariableModuleGlobal->FactoryDefaultSize + mVariableModuleGlobal->VariableDefaultSize
);
}
AcquireLockOnlyAtBootTime (&mVariableModuleGlobal->VariableBase.VariableServicesLock);
Global = &mVariableModuleGlobal->VariableBase;
if((Attributes & EFI_VARIABLE_NON_VOLATILE) == 0) {
//
// Query is Volatile related.
//
VariableStoreHeader = (VARIABLE_STORE_HEADER *) ((UINTN) Global->VolatileVariableBase);
VariableEndPointer = GetEndPointer (VariableStoreHeader);
VariableStoreSize = GetVariableStoreSize (VariableStoreHeader);
} else {
//
// Query is Non-Volatile related.
//
VariableStoreHeader = (VARIABLE_STORE_HEADER *) ((UINTN) Global->NonVolatileVariableBase);
VariableEndPointer = GetNonVolatileEndPointer (VariableStoreHeader);
VariableStoreSize = GetNonVolatileVariableStoreSize ();
}
//
// Now let's fill *MaximumVariableStorageSize *RemainingVariableStorageSize
// with the storage size (excluding the storage header size).
//
*MaximumVariableStorageSize = VariableStoreSize - GetVariableStoreHeaderSize ();
*RemainingVariableStorageSize = VariableStoreSize - GetVariableStoreHeaderSize ();
//
// Let *MaximumVariableSize be MAX_VARIABLE_SIZE with the exception of the variable header size.
//
*MaximumVariableSize = MAX_VARIABLE_SIZE - sizeof (VARIABLE_HEADER);
//
// Harware error record variable needs larger size.
//
if ((Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == EFI_VARIABLE_HARDWARE_ERROR_RECORD) {
*MaximumVariableSize = MAX_HARDWARE_ERROR_VARIABLE_SIZE - sizeof (VARIABLE_HEADER);
}
//
// Point to the starting address of the variables.
//
Variable = GetStartPointer (VariableStoreHeader);
//
// Now walk through the related variable store.
//
while (IsValidVariableHeaderInVarRegion (Variable, VariableEndPointer)) {
NextVariable = GetNextVariablePtr (Variable);
VariableSize = (UINT64) (UINTN) NextVariable - (UINT64) (UINTN) Variable;
if (VariableAtRuntime ()) {
//
// we don't take the state of the variables in mind
// when calculating RemainingVariableStorageSize,
// since the space occupied by variables not marked with
// VAR_ADDED is not allowed to be reclaimed in Runtime.
//
*RemainingVariableStorageSize -= VariableSize;
} else {
//
// Only care about Variables with State VAR_ADDED,because
// the space not marked as VAR_ADDED is reclaimable now.
//
if ((Variable->State == VAR_ADDED) ||
(Variable->State == (VAR_ADDED & VAR_IN_DELETED_TRANSITION))) {
*RemainingVariableStorageSize -= VariableSize;
}
}
//
// Go to the next one
//
Variable = NextVariable;
}
if (*RemainingVariableStorageSize < sizeof (VARIABLE_HEADER)) {
*MaximumVariableSize = 0;
} else if ((*RemainingVariableStorageSize - sizeof (VARIABLE_HEADER)) < *MaximumVariableSize) {
*MaximumVariableSize = *RemainingVariableStorageSize - sizeof (VARIABLE_HEADER);
}
ReleaseLockOnlyAtBootTime (&mVariableModuleGlobal->VariableBase.VariableServicesLock);
if (VariableAtRuntime () && InteruptEnabled) {
EnableInterrupts ();
}
return EFI_SUCCESS;
}
/**
This function uses to restore default variable store header to non-volatile sotrage
@param VariableStoreHeader pointer the variable store header in non-volatile sotrage.
@retval EFI_SUCCESS The restore process is success.
@retval EFI_INVALID_PARAMETER VariableStoreHeader parameter is NULL.
@return EFI_VOLUME_CORRUPTED Firmware Volume is corrupted.
--*/
EFI_STATUS
RestoreDefaultVariableStoreHeader (
IN VARIABLE_STORE_HEADER *VariableStoreHeader
)
{
VARIABLE_STORE_HEADER WorkingStoreHeader;
ECP_VARIABLE_STORE_HEADER EcpVarStoreHeader;
EFI_STATUS Status;
UINT32 DataSize;
UINT8 *Buffer;
if (VariableStoreHeader == NULL) {
return EFI_INVALID_PARAMETER;
}
if (PcdGetBool (PcdUseEcpVariableStoreHeader)) {
SetMem (&EcpVarStoreHeader, sizeof (EcpVarStoreHeader), 0xFF);
EcpVarStoreHeader.Signature = ECP_VARIABLE_STORE_SIGNATURE;
EcpVarStoreHeader.Format = VARIABLE_STORE_FORMATTED;
EcpVarStoreHeader.State = VARIABLE_STORE_HEALTHY;
EcpVarStoreHeader.DefaultId = 0;
EcpVarStoreHeader.BoardId = 0;
EcpVarStoreHeader.Flags = 0;
EcpVarStoreHeader.ExtHeaderSize = 0;
DataSize = sizeof (EcpVarStoreHeader);
Buffer = (UINT8 *)&EcpVarStoreHeader;
} else {
SetMem (&WorkingStoreHeader, sizeof (WorkingStoreHeader), 0xFF);
CopyMem (
&WorkingStoreHeader.Signature,
&gEfiAuthenticatedVariableGuid,
sizeof (EFI_GUID)
);
WorkingStoreHeader.Format = VARIABLE_STORE_FORMATTED;
WorkingStoreHeader.State = VARIABLE_STORE_HEALTHY;
WorkingStoreHeader.DefaultId = 0;
WorkingStoreHeader.BoardId = 0;
WorkingStoreHeader.Flags = 0;
WorkingStoreHeader.ExtHeaderSize = 0;
DataSize = sizeof (WorkingStoreHeader);
Buffer = (UINT8 *)&WorkingStoreHeader;
}
Status = UpdateVariableStore (
&mVariableModuleGlobal->VariableBase,
FALSE,
FALSE,
(UINTN) VariableStoreHeader,
DataSize,
Buffer
);
if (EFI_ERROR (Status) || CompareMem (Buffer, VariableStoreHeader, DataSize) != 0) {
return EFI_VOLUME_CORRUPTED;
}
return EFI_SUCCESS;
}
/**
Internal function to clear variable storage firmware volume
@param [in] FvHeader Pointer to firmware volume header
@param [in] VariableStoreLength Variable storage length in bytes.
**/
STATIC
VOID
ResetFirmwareVolume (
IN EFI_FIRMWARE_VOLUME_HEADER *FvHeader,
IN UINTN VariableStoreLength
)
{
EFI_STATUS Status;
EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvbProtocol;
Status = GetFvbInfoByAddress ((EFI_PHYSICAL_ADDRESS) (UINTN) FvHeader, NULL, &FvbProtocol);
if (EFI_ERROR (Status)) {
return;
}
//
// Variable Store header is not reliable, clean up whole variable region.
// EFI_FIRMWARE_VOLUME_HEADER is reliable since it passed by FVB driver.
// Temp to use VariableStoreLength/BlockLength as num of LBA.
//
Status = FvbProtocol->EraseBlocks (FvbProtocol, 0, VariableStoreLength / FvHeader->BlockMap[0].Length, EFI_LBA_LIST_TERMINATOR);
//
// force system reboot to recovery!!!
//
gST->RuntimeServices->ResetSystem (EfiResetCold, EFI_SUCCESS, 0, NULL);
}
/**
Determine need write back to variable defaults or not.
@retval TRUE System need write back to variable defaults.
@retval FALSE System need not write back to variable defaults.
**/
STATIC
BOOLEAN
WriteVariableDefaults (
VOID
)
{
EFI_HOB_GUID_TYPE *GuidHob;
H2O_DXE_CP_WRITE_VARIABLE_DEFAULTS_DATA WriteVariableDefaultsData;
GuidHob = GetFirstGuidHob (&gH2OFlashMapRegionVarGuid);
if (GuidHob == NULL) {
return FALSE;
}
if (FeaturePcdGet (PcdH2ODxeCpWriteVariableDefaultsSupported)) {
WriteVariableDefaultsData.Size = sizeof (H2O_DXE_CP_WRITE_VARIABLE_DEFAULTS_DATA);
WriteVariableDefaultsData.Status = H2O_CP_TASK_NORMAL;
DEBUG_CP ((DEBUG_INFO, "Checkpoint Trigger: %g\n", &gH2ODxeCpWriteVariableDefaultsGuid));
H2OCpTrigger (&gH2ODxeCpWriteVariableDefaultsGuid, &WriteVariableDefaultsData);
DEBUG_CP ((DEBUG_INFO, "Checkpoint Result: %x\n", WriteVariableDefaultsData.Status));
if (WriteVariableDefaultsData.Status == H2O_CP_TASK_SKIP) {
return FALSE;
}
}
return TRUE;
}
/**
Internal function to restore whole variable storage region to defaults.
@param [in] FvHeader Pointer to firmware volume header
@param [in] VariableStoreLength Variable storage length in bytes.
**/
STATIC
VOID
WriteBackToVariableDefaults (
IN EFI_FIRMWARE_VOLUME_HEADER *FvHeader,
IN UINTN VariableStoreLength
)
{
EFI_STATUS Status;
UINT8 *FvData;
UINTN HeaderLength;
EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvbProtocol;
VARIABLE_STORE_HEADER *VariableStoreHeader;
HeaderLength = FvHeader->HeaderLength;
FvData = AllocatePool (HeaderLength);
if (FvData == NULL) {
return;
}
CopyMem (FvData, FvHeader, HeaderLength);
Status = GetFvbInfoByAddress ((EFI_PHYSICAL_ADDRESS) (UINTN) FvHeader, NULL, &FvbProtocol);
if (EFI_ERROR (Status)) {
return;
}
FvbProtocol->EraseBlocks (FvbProtocol, 0, (VariableStoreLength + HeaderLength) / FvHeader->BlockMap[0].Length, EFI_LBA_LIST_TERMINATOR);
FvbProtocol->Write (FvbProtocol, 0, 0, &HeaderLength, FvData);
VariableStoreHeader = (VARIABLE_STORE_HEADER *)((UINT8 *) FvHeader + FvHeader->HeaderLength);
RestoreDefaultVariableStoreHeader (VariableStoreHeader);
UpdateVariableStore (
&mVariableModuleGlobal->VariableBase,
FALSE,
FALSE,
PcdGetBool (PcdUseEcpVariableStoreHeader) ? (UINTN) &((ECP_VARIABLE_STORE_HEADER *) VariableStoreHeader)->Size : (UINTN) &VariableStoreHeader->Size,
sizeof (UINT32),
(UINT8 *) &VariableStoreLength
);
FreePool (FvData);
Status = FlushVariableCache (
mVariableModuleGlobal->NonVolatileVariableCache,
(UINT8 *) (UINTN) mVariableModuleGlobal->VariableBase.NonVolatileVariableBase,
mVariableModuleGlobal->NonVolatileVariableCacheSize,
&mVariableModuleGlobal->NonVolatileLastVariableOffset
);
SetNonVolatileVariableCacheCrc32 ();
}
/**
This function uses to initialize volatile variable store header
@param VolatileVariableStore pointer to volatile variable store which want to initialize
@retval EFI_SUCCESS Initialize volatile variable store successful.
@retval EFI_INVALID_PARAMETER VolatileVariableStore pointer to NULL.
**/
STATIC
EFI_STATUS
InitializeVolatileVariableStoreHeader (
IN OUT VARIABLE_STORE_HEADER *VolatileVariableStore
)
{
ECP_VARIABLE_STORE_HEADER *EcpVarStoreHeader;
if (VolatileVariableStore == NULL) {
return EFI_INVALID_PARAMETER;
}
if (PcdGetBool (PcdUseEcpVariableStoreHeader)) {
EcpVarStoreHeader = (ECP_VARIABLE_STORE_HEADER *) VolatileVariableStore;
EcpVarStoreHeader->Signature = ECP_VARIABLE_STORE_SIGNATURE;
EcpVarStoreHeader->Size = VARIABLE_STORE_SIZE;
EcpVarStoreHeader->Format = VARIABLE_STORE_FORMATTED;
EcpVarStoreHeader->State = VARIABLE_STORE_HEALTHY;
EcpVarStoreHeader->DefaultId = 0;
EcpVarStoreHeader->BoardId = 0;
EcpVarStoreHeader->Flags = 0;
EcpVarStoreHeader->ExtHeaderSize = 0;
} else {
CopyGuid (&VolatileVariableStore->Signature, &gEfiAuthenticatedVariableGuid);
VolatileVariableStore->Size = VARIABLE_STORE_SIZE;
VolatileVariableStore->Format = VARIABLE_STORE_FORMATTED;
VolatileVariableStore->State = VARIABLE_STORE_HEALTHY;
VolatileVariableStore->DefaultId = 0;
VolatileVariableStore->BoardId = 0;
VolatileVariableStore->Flags = 0;
VolatileVariableStore->ExtHeaderSize = 0;
}
mVariableModuleGlobal->VariableBase.VolatileVariableBase = (EFI_PHYSICAL_ADDRESS) (UINTN) VolatileVariableStore;
mVariableModuleGlobal->VolatileLastVariableOffset = GetVariableStoreHeaderSize ();
return EFI_SUCCESS;
}
/**
Build the variable working region record for use of other drivers.
@param[in] Address
@param[in] Length
@retval EFI_SUCCESS Build variable working region record successful.
@retval EFI_OUT_OF_RESOURCES Allocate memory failed.
@retval Other Install configuration table failed.
**/
EFI_STATUS
BuildVariableWorkingRegionRecord (
IN EFI_PHYSICAL_ADDRESS Address,
IN EFI_PHYSICAL_ADDRESS Length
)
{
VARIABLE_WORKING_REGION_INFO *VariableWorkingRegionInfo;
VariableWorkingRegionInfo = VariableAllocateZeroBuffer (sizeof(VARIABLE_WORKING_REGION_INFO), TRUE);
if (VariableWorkingRegionInfo == NULL) {
return EFI_OUT_OF_RESOURCES;
}
VariableWorkingRegionInfo->VariableWorkingRegionStart = Address;
VariableWorkingRegionInfo->VariableWorkingRegionLength = Length;
return gBS->InstallConfigurationTable (&mVariableWorkingRegionHobGuid, VariableWorkingRegionInfo);
}
/**
Check the integrity of firmware volume header.
@param[in] FwVolHeader A pointer to a firmware volume header.
@retval TRUE The firmware volume is consistent.
@retval FALSE The firmware volume has corrupted. So it is not an FV.
**/
STATIC
BOOLEAN
ValidateFvHeader (
IN EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader
)
{
//
// Verify the header revision, header signature, length
// Length of FvBlock cannot be 2**64-1
// HeaderLength cannot be an odd number
//
if ((FwVolHeader == NULL) ||
(FwVolHeader->Revision != EFI_FVH_REVISION) ||
(FwVolHeader->Signature != EFI_FVH_SIGNATURE) ||
(FwVolHeader->FvLength == ((UINTN) -1)) ||
((FwVolHeader->HeaderLength & 0x01) != 0)) {
return FALSE;
}
//
// Verify the header checksum
//
if (CalculateCheckSum16 ((UINT16 *) FwVolHeader, FwVolHeader->HeaderLength) != 0) {
return FALSE;
}
return TRUE;
}
/**
This function uses to flush current variable default data to variable default cache
@param CacheBaseAddress pointer to variable default cache base address.
@param VarDefaultBaseAddress pointer to variable default base address.
@param CacheSize Variable default cache size.
@return EFI_SUCCESS Flush variable default data to variable default cache successful.
@return EFI_INVALID_PARAMETER CacheBaseAddress or VarDefaultBaseAddress is NULL.
@return EFI_BUFFER_TOO_SMALL CacheSize is too small.
**/
STATIC
EFI_STATUS
FlushVariableDefaultCache (
IN UINT8 *CacheBaseAddress,
IN UINT8 *VarDefaultBaseAddress,
IN UINTN CacheSize
)
{
UINTN LastOffset;
VARIABLE_STORE_HEADER *VarStoreHeader;
if (CacheBaseAddress == NULL || VarDefaultBaseAddress == NULL) {
return EFI_INVALID_PARAMETER;
}
for (VarStoreHeader = (VARIABLE_STORE_HEADER *) VarDefaultBaseAddress;
GetVariableStoreStatus (VarStoreHeader) == EfiValid;
VarStoreHeader = (VARIABLE_STORE_HEADER *) ((UINTN) VarStoreHeader + GetVariableStoreSize (VarStoreHeader)));
LastOffset = (UINTN) VarStoreHeader - (UINTN) VarDefaultBaseAddress;
if (CacheSize < LastOffset) {
return EFI_BUFFER_TOO_SMALL;
}
SetMem (CacheBaseAddress, CacheSize, 0xff);
CopyMem (CacheBaseAddress, VarDefaultBaseAddress, LastOffset);
return EFI_SUCCESS;
}
/**
This function uses to flush current factory default data to factory default cache
@param CacheBaseAddress pointer to factory default cache base address.
@param FactoryDefaultBaseAddress pointer to factory default base address.
@param CacheSize Variable cache size.
@return EFI_SUCCESS Flush factory default data to factory default cache successful.
@return EFI_INVALID_PARAMETER CacheBaseAddress or FactoryDefaultBaseAddress is NULL.
@return EFI_BUFFER_TOO_SMALL CacheSize is too small.
**/
STATIC
EFI_STATUS
FlushFactoryDefaultCache (
IN UINT8 *CacheBaseAddress,
IN UINT8 *FactoryDefaultBaseAddress,
IN UINTN CacheSize
)
{
VARIABLE_HEADER *NextVariable;
UINTN HeaderSize;
UINTN LastOffset;
if (CacheBaseAddress == NULL || FactoryDefaultBaseAddress == NULL) {
return EFI_INVALID_PARAMETER;
}
HeaderSize = sizeof (UINT64) + sizeof (EFI_FIRMWARE_VOLUME_HEADER) + sizeof (EFI_FV_BLOCK_MAP_ENTRY) + GetVariableStoreHeaderSize ();
NextVariable = (VARIABLE_HEADER *) (FactoryDefaultBaseAddress + HeaderSize);
while (IsValidVariableHeader (NextVariable)) {
NextVariable = GetNextVariablePtr (NextVariable);
}
LastOffset = (UINTN) NextVariable - (UINTN) FactoryDefaultBaseAddress;
if (CacheSize < LastOffset) {
return EFI_BUFFER_TOO_SMALL;
}
SetMem (CacheBaseAddress, CacheSize, 0xff);
CopyMem (CacheBaseAddress, FactoryDefaultBaseAddress, LastOffset);
return EFI_SUCCESS;
}
/**
Internal function to initialize variable information base address.
@retval EFI_SUCCESS Init variable information related base address successfully.
@retval EFI_NOT_READY firmware volume to store variable data is corrupted.
@retval EFI_BUFFER_TOO_SMALL Unable flush variable data to variable cache because
all of variable data size is larger than variable cache size.
@retval EFI_OUT_OF_RESOURCES There are not enough memory to allocate.
@return Other Other error occurred in this function.
**/
EFI_STATUS
InitVariableBaseAddress (
VOID
)
{
EFI_FIRMWARE_VOLUME_HEADER *FvHeader;
VARIABLE_STORE_HEADER *VariableStoreHeader;
UINT64 VariableStoreLength;
EFI_STATUS Status;
VARIABLE_STORE_HEADER *VolatileVariableStore;
UINTN AllocateSize;
//
// memory address has been initialized so return directly.
//
if (mVariableModuleGlobal->VariableBase.NonVolatileVariableBase != 0) {
return EFI_SUCCESS;
}
//
// firmware volume header is invalid return directly. expected this function will be invoked
// after restoring correct firmware volume header.
//
FvHeader = (EFI_FIRMWARE_VOLUME_HEADER *) (UINTN) FdmGetNAtAddr (&gH2OFlashMapRegionVarGuid, 1);
if (FvHeader == NULL) {
return EFI_NOT_FOUND;
}
if (!ValidateFvHeader (FvHeader)) {
return EFI_NOT_READY;
}
VariableStoreHeader = (VARIABLE_STORE_HEADER *) ((UINT8 *) FvHeader + FvHeader->HeaderLength);
mVariableModuleGlobal->VariableBase.NonVolatileVariableBase = (EFI_PHYSICAL_ADDRESS) (UINTN) VariableStoreHeader;
VariableStoreLength = FdmGetNAtSize (&gH2OFlashMapRegionVarGuid, 1) - FvHeader->HeaderLength;
//
// Since memory for non-volatile variable cache and volatile variable may be modified after start image, so combine these two
// memory and save the start address and length in configuration table for other driver use.
//
AllocateSize = VARIABLE_STORE_SIZE + SCRATCH_SIZE + (UINTN) VariableStoreLength + mVariableModuleGlobal->FactoryDefaultSize + mVariableModuleGlobal->VariableDefaultSize;
VolatileVariableStore = VariableAllocateZeroBuffer (AllocateSize, TRUE);
if (VolatileVariableStore == NULL) {
POST_CODE (DXE_VARIABLE_INIT_FAIL);
Status = EFI_OUT_OF_RESOURCES;
goto Done;
}
Status = BuildVariableWorkingRegionRecord (
(EFI_PHYSICAL_ADDRESS) (UINTN) VolatileVariableStore,
(EFI_PHYSICAL_ADDRESS) AllocateSize
);
if (EFI_ERROR (Status)) {
goto Done;
}
SetMem (VolatileVariableStore, VARIABLE_STORE_SIZE + SCRATCH_SIZE, 0xff);
Status = InitializeVolatileVariableStoreHeader (VolatileVariableStore);
ASSERT_EFI_ERROR (Status);
//
// Allocate EfiRuntimeServicesData memory type for variable cache, and then we can use this memory as variable cache in
// POST time and runtime. system will pass this variable cache address to SMM mode driver. Finally, system can access this
// variable cache in SMM mode and protected mode. But we must disable cache mechanism if LegacyBoot event is signaled, due to
// EfiRuntimeServicesData memory is used by legacy OS.
//
mVariableModuleGlobal->NonVolatileVariableCache = (UINT8 *) VolatileVariableStore + VARIABLE_STORE_SIZE + SCRATCH_SIZE;
mVariableModuleGlobal->NonVolatileVariableCacheSize = (UINTN) VariableStoreLength;
Status = FlushVariableCache (
mVariableModuleGlobal->NonVolatileVariableCache,
(UINT8 *) (UINTN) mVariableModuleGlobal->VariableBase.NonVolatileVariableBase,
mVariableModuleGlobal->NonVolatileVariableCacheSize,
&mVariableModuleGlobal->NonVolatileLastVariableOffset
);
SetNonVolatileVariableCacheCrc32 ();
mVariableModuleGlobal->FactoryDefaultCache = mVariableModuleGlobal->NonVolatileVariableCache + mVariableModuleGlobal->NonVolatileVariableCacheSize;
Status = FlushFactoryDefaultCache (
mVariableModuleGlobal->FactoryDefaultCache,
(UINT8 *) (UINTN) mVariableModuleGlobal->FactoryDefaultBase,
mVariableModuleGlobal->FactoryDefaultSize
);
mVariableModuleGlobal->VariableDefaultCache = mVariableModuleGlobal->FactoryDefaultCache + mVariableModuleGlobal->FactoryDefaultSize;
Status = FlushVariableDefaultCache (
mVariableModuleGlobal->VariableDefaultCache,
(UINT8 *) (UINTN) mVariableModuleGlobal->VariableDefaultBase,
mVariableModuleGlobal->VariableDefaultSize
);
if (!FeaturePcdGet (PcdEnableVariableRuntimeCache)) {
mVariableModuleGlobal->NonVolatileVariableCache = NULL;
}
Done:
if (Status != EFI_SUCCESS && Status != EFI_BUFFER_TOO_SMALL) {
mVariableModuleGlobal->VariableBase.NonVolatileVariableBase = 0;
}
return Status;
}
/**
Flush the HOB variable to flash.
Note: Flush data to flash only if the variable alreay exist in flash.
**/
STATIC
VOID
FlushHobVariableToFlash (
VOID
)
{
EFI_STATUS Status;
VARIABLE_STORE_HEADER *VariableStoreHeader;
VARIABLE_HEADER *Variable;
VARIABLE_POINTER_TRACK VariableTrack;
UINTN VariableCount;
if (mVariableModuleGlobal->HobVariableBase == 0) {
return;
}
VariableStoreHeader = (VARIABLE_STORE_HEADER *) (UINTN) mVariableModuleGlobal->HobVariableBase;
//
// Set HobVariableBase to 0, it can avoid SetVariable to call back.
//
mVariableModuleGlobal->HobVariableBase = 0;
for (Variable = GetStartPointer (VariableStoreHeader);
IsValidVariableHeader (Variable) && Variable < GetEndPointer (VariableStoreHeader);
Variable = GetNextVariablePtr (Variable)) {
if (Variable->State != VAR_ADDED) {
//
// The HOB variable has been set to DELETED state in local.
//
continue;
}
ASSERT ((Variable->Attributes & EFI_VARIABLE_NON_VOLATILE) != 0);
FindVariableByLifetime (
GET_VARIABLE_NAME_PTR (Variable),
&Variable->VendorGuid,
&VariableTrack,
&VariableCount,
&mVariableModuleGlobal->VariableBase
);
if (VariableTrack.CurrPtr == NULL) {
continue;
}
Status = UpdateVariable (
GET_VARIABLE_NAME_PTR (Variable),
&Variable->VendorGuid,
GetVariableDataPtr (Variable),
DataSizeOfVariable (Variable),
Variable->Attributes,
0,
0,
&VariableTrack,
NULL,
&mVariableModuleGlobal->VariableBase
);
if (!EFI_ERROR (Status)) {
Variable->State &= VAR_DELETED;
}
}
//
// We still have HOB variable(s) not flushed in flash.
//
mVariableModuleGlobal->HobVariableBase = (EFI_PHYSICAL_ADDRESS) (UINTN) VariableStoreHeader;
}
/**
Get HOB variable store.
@retval EFI_SUCCESS Function successfully executed.
@retval EFI_OUT_OF_RESOURCES Fail to allocate enough memory resource.
**/
STATIC
EFI_STATUS
GetHobVariableStore (
VOID
)
{
VARIABLE_STORE_HEADER *VariableStoreHeader;
UINTN VariableStoreLength;
EFI_HOB_GUID_TYPE *GuidHob;
//
// Make sure there is no more than one Variable HOB.
//
DEBUG_CODE (
GuidHob = GetFirstGuidHob (&gEfiAuthenticatedVariableGuid);
if (GuidHob != NULL) {
if ((GetNextGuidHob (&gEfiAuthenticatedVariableGuid, GET_NEXT_HOB (GuidHob)) != NULL)) {
DEBUG ((DEBUG_ERROR, "ERROR: Found two Auth Variable HOBs\n"));
ASSERT (FALSE);
}
}
);
GuidHob = GetFirstGuidHob (&gEfiAuthenticatedVariableGuid);
if (GuidHob == NULL) {
return EFI_SUCCESS;
}
VariableStoreHeader = GET_GUID_HOB_DATA (GuidHob);
VariableStoreLength = GuidHob->Header.HobLength - sizeof (EFI_HOB_GUID_TYPE);
if (GetVariableStoreStatus (VariableStoreHeader) != EfiValid) {
return EFI_SUCCESS;
}
mVariableModuleGlobal->HobVariableBase = (UINTN)VariableAllocateZeroBuffer (VariableStoreLength, TRUE);
if (mVariableModuleGlobal->HobVariableBase == 0) {
return EFI_OUT_OF_RESOURCES;
}
CopyMem ((VOID *)(UINTN)mVariableModuleGlobal->HobVariableBase, (VOID *)VariableStoreHeader, VariableStoreLength);
mVariableModuleGlobal->HobVariableSize = VariableStoreLength;
return EFI_SUCCESS;
}
/**
Initializes read-only variable services.
@retval EFI_SUCCESS Function successfully executed.
@retval EFI_OUT_OF_RESOURCES Fail to allocate enough memory resource.
@return Others Ohter error occurred in this function.
**/
EFI_STATUS
VariableReadyOnlyInitialize (
VOID
)
{
EFI_GUID VariableDefaultId = FDM_VARIABLE_DEFAULT_ID_VARIABLE_DEFAULT;
mVariableModuleGlobal = VariableAllocateZeroBuffer (sizeof (ESAL_VARIABLE_GLOBAL), TRUE);
if (mVariableModuleGlobal == NULL) {
return EFI_OUT_OF_RESOURCES;
}
EfiInitializeLock (&mVariableModuleGlobal->VariableBase.VariableServicesLock, TPL_NOTIFY);
mVariableModuleGlobal->FactoryDefaultBase = FdmGetAddressById (&gH2OFlashMapRegionVarDefaultGuid, &gH2OFlashMapRegionFactoryCopyGuid, 1);
mVariableModuleGlobal->FactoryDefaultSize = (UINTN) FdmGetSizeById (&gH2OFlashMapRegionVarDefaultGuid, &gH2OFlashMapRegionFactoryCopyGuid, 1);
mVariableModuleGlobal->VariableDefaultBase = FdmGetAddressById (&gH2OFlashMapRegionVarDefaultGuid, &VariableDefaultId, 1);
mVariableModuleGlobal->VariableDefaultSize = (UINTN) FdmGetSizeById (&gH2OFlashMapRegionVarDefaultGuid, &VariableDefaultId, 1);
GetHobVariableStore ();
InitVariableBaseAddress ();
return EFI_SUCCESS;
}
/**
Function to provide runtime type memory to store variable data which can be used to
communicate with variable code in SMM.
@param[out] Buffer Pointer to the return runtime type memory.
@param[out] BufferSize The size of the return buffer.
@retval EFI_SUCCESS Get runtime type memory successfully.
@retval EFI_NOT_FOUND Cannot find runtime type memory.
@retval EFI_INVALID_PARAMETER Buffer is NULL or BufferSize is NULL.
**/
EFI_STATUS
GetRuntimeVariableBuffer (
OUT VOID **Buffer,
OUT UINTN *BufferSize
)
{
if (Buffer == NULL || BufferSize == NULL) {
return EFI_OUT_OF_RESOURCES;
}
if (mVariableModuleGlobal->SmmCommunicationBuffer == NULL) {
return EFI_NOT_FOUND;
}
*Buffer = mVariableModuleGlobal->SmmCommunicationBuffer;
*BufferSize = mCommunicateBuffer;
return EFI_SUCCESS;
}
/**
gH2OBdsCpDxeSmmReadyToLockBeforeGuid CP handler to notify the gH2OBdsCpDxeSmmReadyToLockBeforeGuid
CP is triggered.
@param[in] Event A pointer to the Event that triggered the callback.
@param[in] Handle Checkpoint handle.
**/
VOID
EFIAPI
VariableDxeSmmReadyToLockCpHandler (
IN EFI_EVENT Event,
IN H2O_CP_HANDLE Handle
)
{
H2OCpUnregisterHandler (Handle);
mDxeSmmReadyToLockBeforeTriggered = TRUE;
}
/**
Callback function to locate SMM communication protocol.
@param[in] Event Event whose notification function is being invoked.
@param[in] Context Pointer to the notification function's context.
**/
VOID
EFIAPI
SmmCommunicationProtocolCallback (
IN EFI_EVENT Event,
IN VOID *Context
)
{
EFI_STATUS Status;
Status = gBS->LocateProtocol (&gEfiSmmCommunicationProtocolGuid, NULL, (VOID **) &mSmmCommunication);
if (!EFI_ERROR (Status)) {
gBS->CloseEvent (Event);
}
}
/**
Initializes variable store area for non-volatile and volatile variable.
@retval EFI_SUCCESS Function successfully executed.
@retval EFI_OUT_OF_RESOURCES Fail to allocate enough memory resource.
@retval EFI_ABORTED Variable region does not exist.
**/
EFI_STATUS
VariableCommonInitialize (
VOID
)
{
EFI_STATUS Status;
EFI_STATUS InfoStatus;
VARIABLE_STORE_HEADER *VariableStoreHeader;
UINT64 VariableStoreLength;
EFI_HANDLE NewHandle;
EFI_EVENT Event;
EFI_FIRMWARE_VOLUME_HEADER *FvHeader;
EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvbProtocol;
EFI_PHYSICAL_ADDRESS FvBaseAddress;
UINT32 MaxUserNvVariableSpaceSize;
H2O_CP_HANDLE CpHandle;
VOID *Registration;
if (mVariableModuleGlobal == NULL) {
return EFI_ABORTED;
}
mVariableModuleGlobal->SmmCommunicationBuffer = VariableAllocateZeroBuffer (SMM_COMMUNICATE_BUFFER_SIZE, TRUE);
if (mVariableModuleGlobal->SmmCommunicationBuffer == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto Shutdown;
}
mSmmPhyCommunicationBuffer = mVariableModuleGlobal->SmmCommunicationBuffer;
mVariableModuleGlobal->SmmVarBuf = (SMM_VAR_BUFFER *)mVariableModuleGlobal->SmmCommunicationBuffer->Data;
EfiCreateProtocolNotifyEvent (
&gEfiSmmCommunicationProtocolGuid,
TPL_NOTIFY,
SmmCommunicationProtocolCallback,
NULL,
&Registration
);
//
// Reserved MAX_VARIABLE_SIZE runtime buffer for "Append" operation in virtual mode.
//
mStorageArea = VariableAllocateZeroBuffer (APPEND_BUFF_SIZE, TRUE);
if (mStorageArea == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto Shutdown;
}
//
// Reserved MAX_VARIABLE_SIZE runtime buffer for certificated database list (normal time based authenticated variable)
// operation in virtual mode.
//
mCertDbList = VariableAllocateZeroBuffer (MAX_VARIABLE_SIZE, TRUE);
if (mCertDbList == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto Shutdown;
}
//
// Allocate memory for volatile variable store
//
POST_CODE (DXE_VARIABLE_INIT);
mVariableModuleGlobal->VarCheckVariableList = &mVarCheckVariableList;
FvHeader = (EFI_FIRMWARE_VOLUME_HEADER *) (UINTN) FdmGetNAtAddr (&gH2OFlashMapRegionVarGuid, 1);
if (FvHeader == NULL) {
return EFI_ABORTED;
}
VariableStoreHeader = (VARIABLE_STORE_HEADER *) ((UINT8 *) FvHeader + FvHeader->HeaderLength);
VariableStoreLength = FdmGetNAtSize (&gH2OFlashMapRegionVarGuid, 1) - FvHeader->HeaderLength;
Status = GetFvbInfoByAddress ((EFI_PHYSICAL_ADDRESS) (UINTN) FvHeader, NULL, &FvbProtocol);
if (EFI_ERROR (Status)) {
goto Shutdown;
}
mVariableModuleGlobal->FvbInstance = FvbProtocol;
//
// To prevent from variable store header is corrupted (may be caused by other tool),
// restore to default variable store header to make sure variable service can work
// properly.
//
if (GetVariableStoreStatus (VariableStoreHeader) != EfiValid) {
Status = RestoreDefaultVariableStoreHeader (VariableStoreHeader);
if (EFI_ERROR (Status)) {
ResetFirmwareVolume (FvHeader, (UINTN) (VariableStoreLength + FvHeader->HeaderLength));
goto Shutdown;
}
}
if (~GetVariableStoreSize (VariableStoreHeader) == 0) {
Status = UpdateVariableStore (
&mVariableModuleGlobal->VariableBase,
FALSE,
FALSE,
PcdGetBool (PcdUseEcpVariableStoreHeader) ? (UINTN) &((ECP_VARIABLE_STORE_HEADER *) VariableStoreHeader)->Size : (UINTN) &VariableStoreHeader->Size,
sizeof (UINT32),
(UINT8 *) &VariableStoreLength
);
if (EFI_ERROR (Status)) {
goto Shutdown;
}
}
if (WriteVariableDefaults ()) {
REPORT_STATUS_CODE (
EFI_PROGRESS_CODE,
EFI_SOFTWARE_EFI_RUNTIME_SERVICE | EFI_SW_EC_CFG_CLR_REQUEST
);
WriteBackToVariableDefaults (FvHeader, (UINTN)VariableStoreLength);
}
InitializeInsydeVariableLockedState ();
MaxUserNvVariableSpaceSize = PcdGet32 (PcdMaxUserNvVariableSpaceSize);
ASSERT (MaxUserNvVariableSpaceSize < (VariableStoreLength - sizeof (VARIABLE_STORE_HEADER)));
mVariableModuleGlobal->CommonMaxUserVariableSpace = MaxUserNvVariableSpaceSize;
//
// Allocate reclaim related memory before doing reclaim
//
mVariableReclaimInfo = VariableAllocateZeroBuffer (sizeof (VARIABLE_RECLAIM_INFO), TRUE);
if (mVariableReclaimInfo == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto Shutdown;
}
mVariableModuleGlobal->ReclaimInfo = mVariableReclaimInfo;
Status = gBS->LocateProtocol (
&gEfiFaultTolerantWriteLiteProtocolGuid,
NULL,
(VOID **)&mVariableReclaimInfo->FtwLiteProtocol
);
if (EFI_ERROR (Status)) {
goto Shutdown;
}
Status = InitVariableBaseAddress ();
if (Status != EFI_SUCCESS && Status != EFI_BUFFER_TOO_SMALL) {
goto Shutdown;
}
mVariableReclaimInfo->BackupBuffer = VariableAllocateZeroBuffer (GetNonVolatileVariableStoreSize () + GetVariableStoreHeaderSize (), TRUE);
if (mVariableReclaimInfo->BackupBuffer == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto Shutdown;
}
InfoStatus = GetVariableStoreInfo (
mVariableModuleGlobal->VariableBase.NonVolatileVariableBase,
&FvBaseAddress,
&mVariableReclaimInfo->VariableLba,
&mVariableReclaimInfo->VariableOffset
);
if (InfoStatus != EFI_SUCCESS) {
goto Shutdown;
}
if (Status == EFI_BUFFER_TOO_SMALL) {
ASSERT (FALSE);
//
// If variable data size is large than cache size, there are something wrong in variable data.
// Try to use reclaim mechanism to fix this error.
//
Status = Reclaim (
mVariableModuleGlobal->VariableBase.NonVolatileVariableBase,
&mVariableModuleGlobal->NonVolatileLastVariableOffset,
FALSE,
NULL
);
if (EFI_ERROR (Status)) {
goto Shutdown;
}
Status = FlushVariableCache (
mVariableModuleGlobal->NonVolatileVariableCache,
(UINT8 *) (UINTN) mVariableModuleGlobal->VariableBase.NonVolatileVariableBase,
mVariableModuleGlobal->NonVolatileVariableCacheSize,
&mVariableModuleGlobal->NonVolatileLastVariableOffset
);
SetNonVolatileVariableCacheCrc32 ();
}
if (EFI_ERROR (Status)) {
goto Shutdown;
}
FlushHobVariableToFlash ();
//
// Reclaim if the free area is blow a threshold to release some non-volatile space
//
if (GetNonVolatileVariableStoreSize () - mVariableModuleGlobal->NonVolatileLastVariableOffset < VARIABLE_RECLAIM_THRESHOLD) {
Status = Reclaim (
mVariableModuleGlobal->VariableBase.NonVolatileVariableBase,
&mVariableModuleGlobal->NonVolatileLastVariableOffset,
FALSE,
NULL
);
if (EFI_ERROR (Status)) {
goto Shutdown;
}
}
mVariableModuleGlobal->GlobalVariableList = (VOID *) mGlobalVariableList;
mVariableModuleGlobal->NonVolatileVariableProtocol.GetRuntimeVariableBuffer = GetRuntimeVariableBuffer;
//
// Install gEfiNonVolatileVariableProtocolGuid to let platform know support
// non-volatile variable and also passes some information to SMM variable driver.
//
NewHandle = NULL;
Status = gBS->InstallProtocolInterface (
&NewHandle,
&gEfiNonVolatileVariableProtocolGuid,
EFI_NATIVE_INTERFACE,
mVariableModuleGlobal
);
if (EFI_ERROR (Status)) {
goto Shutdown;
}
RegisterEventToDisableSecureBoot ();
Status = EfiCreateEventReadyToBootEx (
TPL_NOTIFY,
ReadyToBootCallback,
NULL,
&Event
);
if (EFI_ERROR (Status)) {
goto Shutdown;
}
Status = EfiCreateEventLegacyBootEx (
TPL_NOTIFY,
LegacyBootCallback,
NULL,
&Event
);
if (EFI_ERROR (Status)) {
goto Shutdown;
}
InitializeRestoreVariableDefaultService ();
MorLockInit ();
//
// Register the event handling function to set the End Of DXE flag.
//
Status = gBS->CreateEventEx (
EVT_NOTIFY_SIGNAL,
TPL_NOTIFY,
OnEndOfDxe,
NULL,
&gEfiEndOfDxeEventGroupGuid,
&Event
);
ASSERT_EFI_ERROR (Status);
mVariableModuleGlobal->VariableWriteReady = TRUE;
if (FeaturePcdGet (PcdH2OBdsCpDxeSmmReadyToLockBeforeSupported)) {
//
// Register checkpoint of gH2OBdsCpDxeSmmReadyToLockBeforeGuid to lockdown flash part protection
//
Status = H2OCpRegisterHandler (
&gH2OBdsCpDxeSmmReadyToLockBeforeGuid,
VariableDxeSmmReadyToLockCpHandler,
H2O_CP_MEDIUM,
&CpHandle
);
if (EFI_ERROR (Status)) {
DEBUG_CP ((DEBUG_ERROR, "Checkpoint Register Fail: %g (%r)\n", &gH2OBdsCpDxeSmmReadyToLockBeforeGuid, Status));
return Status;
}
DEBUG_CP ((DEBUG_INFO, "Checkpoint Registered: %g (%r)\n", &gH2OBdsCpDxeSmmReadyToLockBeforeGuid, Status));
}
return Status;
Shutdown:
if (mVariableModuleGlobal != NULL) {
if (mVariableModuleGlobal->VariableBase.VolatileVariableBase != 0) {
FreePool ((VOID *) (UINTN) mVariableModuleGlobal->VariableBase.VolatileVariableBase);
}
if (mVariableModuleGlobal->SmmCommunicationBuffer != NULL) {
EFI_FREE_POOL (mVariableModuleGlobal->SmmCommunicationBuffer);
}
EFI_FREE_POOL (mVariableModuleGlobal);
}
if (mStorageArea != NULL) {
EFI_FREE_POOL (mStorageArea);
}
if (mVariableReclaimInfo != NULL) {
if (mVariableReclaimInfo->BackupBuffer != NULL) {
EFI_FREE_POOL (mVariableReclaimInfo->BackupBuffer);
}
EFI_FREE_POOL (mVariableReclaimInfo);
}
return Status;
}
/**
Communication service SMI Handler entry.
This SMI handler provides services for administer secure boot through SMI.
@param[in] DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister().
@param[in] Context Points to an optional handler context which was specified when the
handler was registered.
@param[in,out] CommBuffer A pointer to a collection of data in memory that will
be conveyed from a non-SMM environment into an SMM environment.
@param[in,out] CommBufferSize The size of the CommBuffer.
@retval EFI_SUCCESS The interrupt was handled and quiesced. No other handlers
should still be called.
@retval EFI_WARN_INTERRUPT_SOURCE_QUIESCED The interrupt has been quiesced but other handlers should
still be called.
@retval EFI_WARN_INTERRUPT_SOURCE_PENDING The interrupt is still pending and other handlers should still
be called.
@retval EFI_INTERRUPT_PENDING The interrupt could not be quiesced.
**/
EFI_STATUS
EFIAPI
SecureBootHandler (
IN EFI_HANDLE DispatchHandle,
IN CONST VOID *Context OPTIONAL,
IN OUT VOID *CommBuffer OPTIONAL,
IN OUT UINTN *CommBufferSize OPTIONAL
)
{
SMM_VAR_BUFFER *SmmVarBuffer;
EFI_STATUS Status;
EFI_SMM_COMMUNICATE_HEADER *SmmCommunicationBuffer;
UINTN Index;
if (CommBuffer == NULL || CommBufferSize == NULL ) {
return EFI_SUCCESS;
}
SmmCommunicationBuffer = mVariableModuleGlobal->SmmCommunicationBuffer;
//
// Check buffer size, address, not overlap SMMRAM and the signature to make sure the communication buffer is correct.
//
if (*CommBufferSize != SMM_COMMUNICATE_BUFFER_SIZE - sizeof (EFI_SMM_COMMUNICATE_HEADER) + sizeof (UINT8) ||
(UINTN)CommBuffer != (UINTN)SmmCommunicationBuffer + sizeof (EFI_SMM_COMMUNICATE_HEADER) - sizeof (UINT8) ||
BufferOverlapSmram (CommBuffer, *CommBufferSize)) {
return EFI_SUCCESS;
}
//
// Copy SMM_VAR_BUFFER to SMRAM to prevent from TOCTOU attack.
//
SmmVarBuffer = AllocateCopyPool (sizeof (SMM_VAR_BUFFER), CommBuffer);
if (SmmVarBuffer == NULL) {
return EFI_SUCCESS;
}
//
// Make sure DataSize isn't larger than buffer size.
//
if (SmmVarBuffer->DataSize + SmmVarBuffer->VariableNameSize > *CommBufferSize - sizeof (SMM_VAR_BUFFER)) {
return EFI_SUCCESS;
}
Status = EFI_UNSUPPORTED;
for (Index = 0; mSecureBootFunctionsTable[Index].SmiSubFunction != NULL ; Index++) {
if (SmmVarBuffer->AccessType == mSecureBootFunctionsTable[Index].FunNum) {
if (mReadyToBootEventSignaled && !mSecureBootFunctionsTable[Index].SupportedAfterReadyToBoot) {
break;
}
Status = mSecureBootFunctionsTable[Index].SmiSubFunction ();
break;
}
}
SmmVarBuffer->Status = Status;
//
// Restore data from SMRAM to parameter communication buffer.
//
CopyMem (CommBuffer, SmmVarBuffer, sizeof (SMM_VAR_BUFFER));
FreePool (SmmVarBuffer);
return EFI_SUCCESS;
}
/**
Copy protected mode information from EfiRuntimeServicesData to SMM Ram.
We only need invoke this function when Legacy Boot event is signaled and system is in SMM mode.
EfiRuntimeServicesData memory is used by legacy OS, so we must copy memory to SMM Ram
to prevent from data corrupted by OS.
@retval EFI_SUCCESS Restore information to SMM Ram successful.
@retval EFI_UNSUPPORTED System isn't in SMM mode.
@retval EFI_OUT_OF_RESOURCES Allocate memory failed.
--*/
STATIC
EFI_STATUS
RestoreProtectedModeInfoToSmmRam (
VOID
)
{
ESAL_VARIABLE_GLOBAL *VariableGlobal;
if (mSmst == NULL) {
return EFI_UNSUPPORTED;
}
VariableGlobal = VariableAllocateZeroBuffer (sizeof (ESAL_VARIABLE_GLOBAL), TRUE);
if (VariableGlobal == NULL) {
return EFI_OUT_OF_RESOURCES;
}
CopyMem (VariableGlobal, mSmmVariableGlobal->ProtectedModeVariableModuleGlobal, sizeof (ESAL_VARIABLE_GLOBAL));
mSmmVariableGlobal->ProtectedModeVariableModuleGlobal = VariableGlobal;
return EFI_SUCCESS;
}
/**
This function uses to do specific action when legacy boot event is signaled.
@retval EFI_SUCCESS All of action for legacy boot event in SMM mode is successful.
@retval Other Any error occurred.
--*/
EFI_STATUS
SmmLegacyBootEvent (
VOID
)
{
SMM_VAR_BUFFER *VariableBuffer;
STATIC BOOLEAN Initialized = FALSE;
if (Initialized) {
return EFI_ALREADY_STARTED;
}
VariableBuffer = mVariableModuleGlobal->SmmVarBuf;
if (VariableBuffer->Signature != SMM_LEGACY_BOOT_SIGNATURE || VariableBuffer->DataSize != 0) {
return EFI_UNSUPPORTED;
}
Initialized = TRUE;
return RestoreProtectedModeInfoToSmmRam ();
}
/**
This function uses to invoke SMM mode SetVariable ()
@retval EFI_SUCCESS Calling SMM mode SetVariable () successful.
@return Other Any error occurred while setting variable.
**/
EFI_STATUS
SmmInternalSetVariable (
VOID
)
{
SMM_VAR_BUFFER *SmmVarBuffer;
CHAR16 *VariableName;
UINT8 *VariableBuffer;
EFI_STATUS Status;
SmmVarBuffer = mVariableModuleGlobal->SmmVarBuf;
if (SmmVarBuffer->Signature != SMM_VARIABLE_SIGNATURE) {
return EFI_UNSUPPORTED;
}
VariableName = (CHAR16 *) (SmmVarBuffer + 1);
VariableBuffer = ((UINT8 *) (SmmVarBuffer + 1)) + SmmVarBuffer->VariableNameSize;
if (InternalCalculateSum16 (VariableBuffer, SmmVarBuffer->DataSize) != SmmVarBuffer->VarChecksum) {
return EFI_UNSUPPORTED;
}
Status = VariableServicesSetVariable (
VariableName,
&SmmVarBuffer->VarGuid,
SmmVarBuffer->Attributes,
SmmVarBuffer->DataSize,
VariableBuffer
);
return Status;
}
/**
Check this input handle is whether a NV store FVB handle.
@param[in] Handle Input EFI_HANDLE instance
@retval TRUE This is NV storage FVB handle.
@retval FALSE This isn't NV storage FVB handle.
**/
STATIC
BOOLEAN
IsNvStorageHandle (
EFI_HANDLE Handle
)
{
EFI_STATUS Status;
Status = gBS->OpenProtocol (
Handle,
&gEfiFirmwareVolumeBlockProtocolGuid,
NULL,
NULL,
NULL,
EFI_OPEN_PROTOCOL_TEST_PROTOCOL
);
if (EFI_ERROR (Status)) {
return FALSE;
}
Status = gBS->OpenProtocol (
Handle,
&gEfiAlternateFvBlockGuid,
NULL,
NULL,
NULL,
EFI_OPEN_PROTOCOL_TEST_PROTOCOL
);
return (BOOLEAN) (Status == EFI_SUCCESS);
}
/**
Function returns an array of handles that support the FVB protocol
in a buffer allocated from pool.
@param[out] NumberHandles The number of handles returned in Buffer.
@param[out] Buffer A pointer to the buffer to return the requested
array of handles that support FVB protocol.
@retval EFI_SUCCESS The array of handles was returned in Buffer, and the number of
handles in Buffer was returned in NumberHandles.
@retval EFI_NOT_FOUND No FVB handle was found.
@retval EFI_OUT_OF_RESOURCES There is not enough pool memory to store the matching results.
@retval EFI_INVALID_PARAMETER NumberHandles is NULL or Buffer is NULL.
**/
STATIC
EFI_STATUS
GetFvbCountAndBuffer (
OUT UINTN *NumberHandles,
OUT EFI_HANDLE **Buffer
)
{
//
// Locate all handles of Fvb protocol
//
return gBS->LocateHandleBuffer (
ByProtocol,
&gEfiFirmwareVolumeBlockProtocolGuid,
NULL,
NumberHandles,
Buffer
);
}
/**
Retrieve the FVB protocol interface by HANDLE.
@param[in] FvBlockHandle The handle of FVB protocol that provides services for
reading, writing, and erasing the target block.
@param[out] FvBlock The interface of FVB protocol
@retval EFI_SUCCESS The interface information for the specified protocol was returned.
@retval EFI_UNSUPPORTED The device does not support the FVB protocol.
@retval EFI_INVALID_PARAMETER FvBlockHandle is not a valid EFI_HANDLE or FvBlock is NULL.
**/
STATIC
EFI_STATUS
GetFvbByHandle (
IN EFI_HANDLE FvBlockHandle,
OUT EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL **FvBlock
)
{
//
// To get the FVB protocol interface on the handle
//
return gBS->HandleProtocol (
FvBlockHandle,
&gEfiFirmwareVolumeBlockProtocolGuid,
(VOID **) FvBlock
);
}
/**
Get the proper fvb handle and/or fvb protocol by the given Flash address.
@param[in] Address The Flash address.
@param[out] FvbHandle In output, if it is not NULL, it points to the proper FVB handle.
@param[out] FvbProtocol In output, if it is not NULL, it points to the proper FVB protocol.
@retval EFI_SUCCESS Get fvb handle and/or fvb protocol successfully.
@retval EFI_NOT_FOUND Cannot find fvb handle and/or fvb protocol.
**/
EFI_STATUS
GetFvbInfoByAddress (
IN EFI_PHYSICAL_ADDRESS Address,
OUT EFI_HANDLE *FvbHandle OPTIONAL,
OUT EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL **FvbProtocol OPTIONAL
)
{
EFI_STATUS Status;
EFI_HANDLE *HandleBuffer;
UINTN HandleCount;
UINTN Index;
EFI_PHYSICAL_ADDRESS FvbBaseAddress;
EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb;
EFI_FVB_ATTRIBUTES_2 Attributes;
UINTN BlockSize;
UINTN NumberOfBlocks;
//
// Get all FVB handles.
//
Status = GetFvbCountAndBuffer (&HandleCount, &HandleBuffer);
if (EFI_ERROR (Status)) {
return EFI_NOT_FOUND;
}
//
// Get the FVB to access variable store.
//
Fvb = NULL;
for (Index = 0; Index < HandleCount; Index += 1, Status = EFI_NOT_FOUND, Fvb = NULL) {
Status = GetFvbByHandle (HandleBuffer[Index], &Fvb);
if (EFI_ERROR (Status)) {
Status = EFI_NOT_FOUND;
break;
}
//
// Ensure this FVB protocol supported Write operation.
//
Status = Fvb->GetAttributes (Fvb, &Attributes);
if (EFI_ERROR (Status) || ((Attributes & EFI_FVB2_WRITE_STATUS) == 0)) {
continue;
}
//
// Compare the address and select the right one.
//
Status = Fvb->GetPhysicalAddress (Fvb, &FvbBaseAddress);
if (EFI_ERROR (Status)) {
continue;
}
//
// Assume one FVB has one type of BlockSize.
//
Status = Fvb->GetBlockSize (Fvb, 0, &BlockSize, &NumberOfBlocks);
if (EFI_ERROR (Status)) {
continue;
}
if ((Address >= FvbBaseAddress) && (Address < (FvbBaseAddress + BlockSize * NumberOfBlocks)) && IsNvStorageHandle (HandleBuffer[Index])) {
if (FvbHandle != NULL) {
*FvbHandle = HandleBuffer[Index];
}
if (FvbProtocol != NULL) {
*FvbProtocol = Fvb;
}
Status = EFI_SUCCESS;
break;
}
}
FreePool (HandleBuffer);
if (Fvb == NULL) {
Status = EFI_NOT_FOUND;
}
return Status;
}