/** @file | |
Implement all four UEFI Runtime Variable services for the nonvolatile | |
and volatile storage space and install variable architecture protocol. | |
Copyright (C) 2013, Red Hat, Inc. | |
Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.<BR> | |
(C) Copyright 2015 Hewlett Packard Enterprise Development LP<BR> | |
Copyright (c) Microsoft Corporation. | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include "Variable.h" | |
#include <Protocol/VariablePolicy.h> | |
#include <Library/VariablePolicyLib.h> | |
EFI_STATUS | |
EFIAPI | |
ProtocolIsVariablePolicyEnabled ( | |
OUT BOOLEAN *State | |
); | |
EFI_HANDLE mHandle = NULL; | |
EFI_EVENT mVirtualAddressChangeEvent = NULL; | |
VOID *mFtwRegistration = NULL; | |
VOID ***mVarCheckAddressPointer = NULL; | |
UINTN mVarCheckAddressPointerCount = 0; | |
EDKII_VARIABLE_LOCK_PROTOCOL mVariableLock = { VariableLockRequestToLock }; | |
EDKII_VARIABLE_POLICY_PROTOCOL mVariablePolicyProtocol = { | |
EDKII_VARIABLE_POLICY_PROTOCOL_REVISION, | |
DisableVariablePolicy, | |
ProtocolIsVariablePolicyEnabled, | |
RegisterVariablePolicy, | |
DumpVariablePolicy, | |
LockVariablePolicy, | |
GetVariablePolicyInfo, | |
GetLockOnVariableStateVariablePolicyInfo | |
}; | |
EDKII_VAR_CHECK_PROTOCOL mVarCheck = { | |
VarCheckRegisterSetVariableCheckHandler, | |
VarCheckVariablePropertySet, | |
VarCheckVariablePropertyGet | |
}; | |
/** | |
Some Secure Boot Policy Variable may update following other variable changes(SecureBoot follows PK change, etc). | |
Record their initial State when variable write service is ready. | |
**/ | |
VOID | |
EFIAPI | |
RecordSecureBootPolicyVarData ( | |
VOID | |
); | |
/** | |
Return TRUE if ExitBootServices () has been called. | |
@retval TRUE If ExitBootServices () has been called. | |
**/ | |
BOOLEAN | |
AtRuntime ( | |
VOID | |
) | |
{ | |
return EfiAtRuntime (); | |
} | |
/** | |
Initializes a basic mutual exclusion lock. | |
This function initializes a basic mutual exclusion lock to the released state | |
and returns the lock. Each lock provides mutual exclusion access at its task | |
priority level. Since there is no preemption or multiprocessor support in EFI, | |
acquiring the lock only consists of raising to the locks TPL. | |
If Lock is NULL, then ASSERT(). | |
If Priority is not a valid TPL value, then ASSERT(). | |
@param Lock A pointer to the lock data structure to initialize. | |
@param Priority EFI TPL is associated with the lock. | |
@return The lock. | |
**/ | |
EFI_LOCK * | |
InitializeLock ( | |
IN OUT EFI_LOCK *Lock, | |
IN EFI_TPL Priority | |
) | |
{ | |
return EfiInitializeLock (Lock, Priority); | |
} | |
/** | |
Acquires lock only at boot time. Simply returns at runtime. | |
This is a temperary function that will be removed when | |
EfiAcquireLock() in UefiLib can handle the call in UEFI | |
Runtimer driver in RT phase. | |
It calls EfiAcquireLock() at boot time, and simply returns | |
at runtime. | |
@param Lock A pointer to the lock to acquire. | |
**/ | |
VOID | |
AcquireLockOnlyAtBootTime ( | |
IN EFI_LOCK *Lock | |
) | |
{ | |
if (!AtRuntime ()) { | |
EfiAcquireLock (Lock); | |
} | |
} | |
/** | |
Releases lock only at boot time. Simply returns at runtime. | |
This is a temperary function which will be removed when | |
EfiReleaseLock() in UefiLib can handle the call in UEFI | |
Runtimer driver in RT phase. | |
It calls EfiReleaseLock() at boot time and simply returns | |
at runtime. | |
@param Lock A pointer to the lock to release. | |
**/ | |
VOID | |
ReleaseLockOnlyAtBootTime ( | |
IN EFI_LOCK *Lock | |
) | |
{ | |
if (!AtRuntime ()) { | |
EfiReleaseLock (Lock); | |
} | |
} | |
/** | |
Retrieve the Fault Tolerent Write protocol interface. | |
@param[out] FtwProtocol The interface of Ftw protocol | |
@retval EFI_SUCCESS The FTW protocol instance was found and returned in FtwProtocol. | |
@retval EFI_NOT_FOUND The FTW protocol instance was not found. | |
@retval EFI_INVALID_PARAMETER SarProtocol is NULL. | |
**/ | |
EFI_STATUS | |
GetFtwProtocol ( | |
OUT VOID **FtwProtocol | |
) | |
{ | |
EFI_STATUS Status; | |
// | |
// Locate Fault Tolerent Write protocol | |
// | |
Status = gBS->LocateProtocol ( | |
&gEfiFaultTolerantWriteProtocolGuid, | |
NULL, | |
FtwProtocol | |
); | |
return Status; | |
} | |
/** | |
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. | |
**/ | |
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 | |
); | |
} | |
/** | |
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. | |
**/ | |
EFI_STATUS | |
GetFvbCountAndBuffer ( | |
OUT UINTN *NumberHandles, | |
OUT EFI_HANDLE **Buffer | |
) | |
{ | |
EFI_STATUS Status; | |
// | |
// Locate all handles of Fvb protocol | |
// | |
Status = gBS->LocateHandleBuffer ( | |
ByProtocol, | |
&gEfiFirmwareVolumeBlockProtocolGuid, | |
NULL, | |
NumberHandles, | |
Buffer | |
); | |
return Status; | |
} | |
/** | |
Notification function of EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE. | |
This is a notification function registered on EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event. | |
It convers pointer to new virtual address. | |
@param Event Event whose notification function is being invoked. | |
@param Context Pointer to the notification function's context. | |
**/ | |
VOID | |
EFIAPI | |
VariableClassAddressChangeEvent ( | |
IN EFI_EVENT Event, | |
IN VOID *Context | |
) | |
{ | |
UINTN Index; | |
if (mVariableModuleGlobal->FvbInstance != NULL) { | |
EfiConvertPointer (0x0, (VOID **)&mVariableModuleGlobal->FvbInstance->GetBlockSize); | |
EfiConvertPointer (0x0, (VOID **)&mVariableModuleGlobal->FvbInstance->GetPhysicalAddress); | |
EfiConvertPointer (0x0, (VOID **)&mVariableModuleGlobal->FvbInstance->GetAttributes); | |
EfiConvertPointer (0x0, (VOID **)&mVariableModuleGlobal->FvbInstance->SetAttributes); | |
EfiConvertPointer (0x0, (VOID **)&mVariableModuleGlobal->FvbInstance->Read); | |
EfiConvertPointer (0x0, (VOID **)&mVariableModuleGlobal->FvbInstance->Write); | |
EfiConvertPointer (0x0, (VOID **)&mVariableModuleGlobal->FvbInstance->EraseBlocks); | |
EfiConvertPointer (0x0, (VOID **)&mVariableModuleGlobal->FvbInstance); | |
} | |
EfiConvertPointer (0x0, (VOID **)&mVariableModuleGlobal->PlatformLangCodes); | |
EfiConvertPointer (0x0, (VOID **)&mVariableModuleGlobal->LangCodes); | |
EfiConvertPointer (0x0, (VOID **)&mVariableModuleGlobal->PlatformLang); | |
EfiConvertPointer (0x0, (VOID **)&mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase); | |
EfiConvertPointer (0x0, (VOID **)&mVariableModuleGlobal->VariableGlobal.VolatileVariableBase); | |
EfiConvertPointer (0x0, (VOID **)&mVariableModuleGlobal->VariableGlobal.HobVariableBase); | |
EfiConvertPointer (0x0, (VOID **)&mVariableModuleGlobal); | |
EfiConvertPointer (0x0, (VOID **)&mNvVariableCache); | |
EfiConvertPointer (0x0, (VOID **)&mNvFvHeaderCache); | |
if (mAuthContextOut.AddressPointer != NULL) { | |
for (Index = 0; Index < mAuthContextOut.AddressPointerCount; Index++) { | |
EfiConvertPointer (0x0, (VOID **)mAuthContextOut.AddressPointer[Index]); | |
} | |
} | |
if (mVarCheckAddressPointer != NULL) { | |
for (Index = 0; Index < mVarCheckAddressPointerCount; Index++) { | |
EfiConvertPointer (0x0, (VOID **)mVarCheckAddressPointer[Index]); | |
} | |
} | |
} | |
/** | |
Notification function of EVT_GROUP_READY_TO_BOOT event group. | |
This is a notification function registered on EVT_GROUP_READY_TO_BOOT event group. | |
When the Boot Manager is about to load and execute a boot option, it reclaims variable | |
storage if free size is below the threshold. | |
@param Event Event whose notification function is being invoked. | |
@param Context Pointer to the notification function's context. | |
**/ | |
VOID | |
EFIAPI | |
OnReadyToBoot ( | |
EFI_EVENT Event, | |
VOID *Context | |
) | |
{ | |
EFI_STATUS Status; | |
if (!mEndOfDxe) { | |
MorLockInitAtEndOfDxe (); | |
Status = LockVariablePolicy (); | |
ASSERT_EFI_ERROR (Status); | |
// | |
// Set the End Of DXE bit in case the EFI_END_OF_DXE_EVENT_GROUP_GUID event is not signaled. | |
// | |
mEndOfDxe = TRUE; | |
mVarCheckAddressPointer = VarCheckLibInitializeAtEndOfDxe (&mVarCheckAddressPointerCount); | |
// | |
// The initialization for variable quota. | |
// | |
InitializeVariableQuota (); | |
} | |
ReclaimForOS (); | |
if (FeaturePcdGet (PcdVariableCollectStatistics)) { | |
if (mVariableModuleGlobal->VariableGlobal.AuthFormat) { | |
gBS->InstallConfigurationTable (&gEfiAuthenticatedVariableGuid, gVariableInfo); | |
} else { | |
gBS->InstallConfigurationTable (&gEfiVariableGuid, gVariableInfo); | |
} | |
} | |
gBS->CloseEvent (Event); | |
} | |
/** | |
Notification function of EFI_END_OF_DXE_EVENT_GROUP_GUID event group. | |
This is a notification function registered on EFI_END_OF_DXE_EVENT_GROUP_GUID event group. | |
@param Event Event whose notification function is being invoked. | |
@param Context Pointer to the notification function's context. | |
**/ | |
VOID | |
EFIAPI | |
OnEndOfDxe ( | |
EFI_EVENT Event, | |
VOID *Context | |
) | |
{ | |
EFI_STATUS Status; | |
DEBUG ((DEBUG_INFO, "[Variable]END_OF_DXE is signaled\n")); | |
MorLockInitAtEndOfDxe (); | |
Status = LockVariablePolicy (); | |
ASSERT_EFI_ERROR (Status); | |
mEndOfDxe = TRUE; | |
mVarCheckAddressPointer = VarCheckLibInitializeAtEndOfDxe (&mVarCheckAddressPointerCount); | |
// | |
// The initialization for variable quota. | |
// | |
InitializeVariableQuota (); | |
if (PcdGetBool (PcdReclaimVariableSpaceAtEndOfDxe)) { | |
ReclaimForOS (); | |
} | |
gBS->CloseEvent (Event); | |
} | |
/** | |
Initializes variable write service for DXE. | |
**/ | |
VOID | |
VariableWriteServiceInitializeDxe ( | |
VOID | |
) | |
{ | |
EFI_STATUS Status; | |
Status = VariableWriteServiceInitialize (); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_ERROR, "Variable write service initialization failed. Status = %r\n", Status)); | |
} | |
// | |
// Some Secure Boot Policy Var (SecureBoot, etc) updates following other | |
// Secure Boot Policy Variable change. Record their initial value. | |
// | |
RecordSecureBootPolicyVarData (); | |
// | |
// Install the Variable Write Architectural protocol. | |
// | |
Status = gBS->InstallProtocolInterface ( | |
&mHandle, | |
&gEfiVariableWriteArchProtocolGuid, | |
EFI_NATIVE_INTERFACE, | |
NULL | |
); | |
ASSERT_EFI_ERROR (Status); | |
} | |
/** | |
Fault Tolerant Write protocol notification event handler. | |
Non-Volatile variable write may needs FTW protocol to reclaim when | |
writting variable. | |
@param[in] Event Event whose notification function is being invoked. | |
@param[in] Context Pointer to the notification function's context. | |
**/ | |
VOID | |
EFIAPI | |
FtwNotificationEvent ( | |
IN EFI_EVENT Event, | |
IN VOID *Context | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvbProtocol; | |
EFI_FAULT_TOLERANT_WRITE_PROTOCOL *FtwProtocol; | |
EFI_PHYSICAL_ADDRESS NvStorageVariableBase; | |
EFI_GCD_MEMORY_SPACE_DESCRIPTOR GcdDescriptor; | |
EFI_PHYSICAL_ADDRESS BaseAddress; | |
UINT64 Length; | |
EFI_PHYSICAL_ADDRESS VariableStoreBase; | |
UINT64 VariableStoreLength; | |
UINTN FtwMaxBlockSize; | |
UINT32 NvStorageVariableSize; | |
UINT64 NvStorageVariableSize64; | |
// | |
// Ensure FTW protocol is installed. | |
// | |
Status = GetFtwProtocol ((VOID **)&FtwProtocol); | |
if (EFI_ERROR (Status)) { | |
return; | |
} | |
Status = GetVariableFlashNvStorageInfo (&NvStorageVariableBase, &NvStorageVariableSize64); | |
ASSERT_EFI_ERROR (Status); | |
Status = SafeUint64ToUint32 (NvStorageVariableSize64, &NvStorageVariableSize); | |
// This driver currently assumes the size will be UINT32 so assert the value is safe for now. | |
ASSERT_EFI_ERROR (Status); | |
VariableStoreBase = NvStorageVariableBase + mNvFvHeaderCache->HeaderLength; | |
Status = FtwProtocol->GetMaxBlockSize (FtwProtocol, &FtwMaxBlockSize); | |
if (!EFI_ERROR (Status)) { | |
ASSERT (NvStorageVariableSize <= FtwMaxBlockSize); | |
} | |
// | |
// Let NonVolatileVariableBase point to flash variable store base directly after FTW ready. | |
// | |
mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase = VariableStoreBase; | |
// | |
// Find the proper FVB protocol for variable. | |
// | |
Status = GetFvbInfoByAddress (NvStorageVariableBase, NULL, &FvbProtocol); | |
if (EFI_ERROR (Status)) { | |
return; | |
} | |
mVariableModuleGlobal->FvbInstance = FvbProtocol; | |
// | |
// Mark the variable storage region of the FLASH as RUNTIME. | |
// | |
VariableStoreLength = mNvVariableCache->Size; | |
BaseAddress = VariableStoreBase & (~EFI_PAGE_MASK); | |
Length = VariableStoreLength + (VariableStoreBase - BaseAddress); | |
Length = (Length + EFI_PAGE_SIZE - 1) & (~EFI_PAGE_MASK); | |
Status = gDS->GetMemorySpaceDescriptor (BaseAddress, &GcdDescriptor); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_WARN, "Variable driver failed to get flash memory attribute.\n")); | |
} else { | |
if ((GcdDescriptor.Attributes & EFI_MEMORY_RUNTIME) == 0) { | |
Status = gDS->SetMemorySpaceAttributes ( | |
BaseAddress, | |
Length, | |
GcdDescriptor.Attributes | EFI_MEMORY_RUNTIME | |
); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_WARN, "Variable driver failed to add EFI_MEMORY_RUNTIME attribute to Flash.\n")); | |
} | |
} | |
} | |
// | |
// Initializes variable write service after FTW was ready. | |
// | |
VariableWriteServiceInitializeDxe (); | |
// | |
// Close the notify event to avoid install gEfiVariableWriteArchProtocolGuid again. | |
// | |
gBS->CloseEvent (Event); | |
} | |
/** | |
This API function returns whether or not the policy engine is | |
currently being enforced. | |
@param[out] State Pointer to a return value for whether the policy enforcement | |
is currently enabled. | |
@retval EFI_SUCCESS | |
@retval Others An error has prevented this command from completing. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
ProtocolIsVariablePolicyEnabled ( | |
OUT BOOLEAN *State | |
) | |
{ | |
*State = IsVariablePolicyEnabled (); | |
return EFI_SUCCESS; | |
} | |
/** | |
Variable Driver main entry point. The Variable driver places the 4 EFI | |
runtime services in the EFI System Table and installs arch protocols | |
for variable read and write services being available. It also registers | |
a notification function for an EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event. | |
@param[in] ImageHandle The firmware allocated handle for the EFI image. | |
@param[in] SystemTable A pointer to the EFI System Table. | |
@retval EFI_SUCCESS Variable service successfully initialized. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
VariableServiceInitialize ( | |
IN EFI_HANDLE ImageHandle, | |
IN EFI_SYSTEM_TABLE *SystemTable | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_EVENT ReadyToBootEvent; | |
EFI_EVENT EndOfDxeEvent; | |
Status = VariableCommonInitialize (); | |
ASSERT_EFI_ERROR (Status); | |
Status = gBS->InstallMultipleProtocolInterfaces ( | |
&mHandle, | |
&gEdkiiVariableLockProtocolGuid, | |
&mVariableLock, | |
NULL | |
); | |
ASSERT_EFI_ERROR (Status); | |
Status = gBS->InstallMultipleProtocolInterfaces ( | |
&mHandle, | |
&gEdkiiVarCheckProtocolGuid, | |
&mVarCheck, | |
NULL | |
); | |
ASSERT_EFI_ERROR (Status); | |
SystemTable->RuntimeServices->GetVariable = VariableServiceGetVariable; | |
SystemTable->RuntimeServices->GetNextVariableName = VariableServiceGetNextVariableName; | |
SystemTable->RuntimeServices->SetVariable = VariableServiceSetVariable; | |
SystemTable->RuntimeServices->QueryVariableInfo = VariableServiceQueryVariableInfo; | |
// | |
// Now install the Variable Runtime Architectural protocol on a new handle. | |
// | |
Status = gBS->InstallProtocolInterface ( | |
&mHandle, | |
&gEfiVariableArchProtocolGuid, | |
EFI_NATIVE_INTERFACE, | |
NULL | |
); | |
ASSERT_EFI_ERROR (Status); | |
if (!PcdGetBool (PcdEmuVariableNvModeEnable)) { | |
// | |
// Register FtwNotificationEvent () notify function. | |
// | |
EfiCreateProtocolNotifyEvent ( | |
&gEfiFaultTolerantWriteProtocolGuid, | |
TPL_CALLBACK, | |
FtwNotificationEvent, | |
(VOID *)SystemTable, | |
&mFtwRegistration | |
); | |
} else { | |
// | |
// Emulated non-volatile variable mode does not depend on FVB and FTW. | |
// | |
VariableWriteServiceInitializeDxe (); | |
} | |
Status = gBS->CreateEventEx ( | |
EVT_NOTIFY_SIGNAL, | |
TPL_NOTIFY, | |
VariableClassAddressChangeEvent, | |
NULL, | |
&gEfiEventVirtualAddressChangeGuid, | |
&mVirtualAddressChangeEvent | |
); | |
ASSERT_EFI_ERROR (Status); | |
// | |
// Register the event handling function to reclaim variable for OS usage. | |
// | |
Status = EfiCreateEventReadyToBootEx ( | |
TPL_NOTIFY, | |
OnReadyToBoot, | |
NULL, | |
&ReadyToBootEvent | |
); | |
ASSERT_EFI_ERROR (Status); | |
// | |
// Register the event handling function to set the End Of DXE flag. | |
// | |
Status = gBS->CreateEventEx ( | |
EVT_NOTIFY_SIGNAL, | |
TPL_CALLBACK, | |
OnEndOfDxe, | |
NULL, | |
&gEfiEndOfDxeEventGroupGuid, | |
&EndOfDxeEvent | |
); | |
ASSERT_EFI_ERROR (Status); | |
// Register and initialize the VariablePolicy engine. | |
Status = InitVariablePolicyLib (VariableServiceGetVariable); | |
ASSERT_EFI_ERROR (Status); | |
Status = VarCheckRegisterSetVariableCheckHandler (ValidateSetVariable); | |
ASSERT_EFI_ERROR (Status); | |
Status = gBS->InstallMultipleProtocolInterfaces ( | |
&mHandle, | |
&gEdkiiVariablePolicyProtocolGuid, | |
&mVariablePolicyProtocol, | |
NULL | |
); | |
ASSERT_EFI_ERROR (Status); | |
return EFI_SUCCESS; | |
} |