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

777 lines
21 KiB
C

/** @file
Provide helper functions and misc functions for variable driver
;******************************************************************************
;* Copyright (c) 2015 - 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 "VarCheck.h"
#include "VariableLock.h"
#include "VariablePolicy.h"
#include "VariableCache.h"
#include <Protocol/SmmAccess2.h>
UINTN mSmramRangeCount;
EFI_SMRAM_DESCRIPTOR *mSmramRanges;
VAR_ERROR_FLAG mCurrentBootVarErrFlag = VAR_ERROR_FLAG_NO_ERROR;
BOOLEAN mReadyToBootEventSignaled;
/**
Initialize SMM RAM information.
@retval EFI_SUCCESS Initialize SMM RAM information successfully.
@retval EFI_OUT_OF_RESOURCES Allocate memory to save SMM RAM informaiton failed.
@retval Other Any error occurred during initialize SMM RAM information.
**/
EFI_STATUS
EFIAPI
SmmRamInfoInitialize (
VOID
)
{
EFI_STATUS Status;
UINTN Size;
EFI_SMM_ACCESS2_PROTOCOL *SmmAccess;
Status = gBS->LocateProtocol (&gEfiSmmAccess2ProtocolGuid, NULL, (VOID **)&SmmAccess);
ASSERT (Status == EFI_SUCCESS);
if (Status != EFI_SUCCESS) {
return Status;
}
Size = 0;
Status = SmmAccess->GetCapabilities (SmmAccess, &Size, NULL);
ASSERT (Status == EFI_BUFFER_TOO_SMALL);
if (Status != EFI_BUFFER_TOO_SMALL) {
return Status;
}
mSmramRanges = VariableAllocateZeroBuffer (Size, TRUE);
ASSERT (mSmramRanges != NULL);
if (mSmramRanges == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Status = SmmAccess->GetCapabilities (SmmAccess, &Size, mSmramRanges);
ASSERT_EFI_ERROR (Status);
if (Status != EFI_SUCCESS) {
return Status;
}
mSmramRangeCount = Size / sizeof (EFI_SMRAM_DESCRIPTOR);
return EFI_SUCCESS;
}
/**
Check the input memory buffer is whether overlap the SMRAM ranges.
@param[in] Buffer The pointer to the buffer to be checked.
@param[in] BufferSize The size in bytes of the input buffer
@retval TRUE The buffer overlaps SMRAM ranges.
@retval FALSE The buffer doesn't overlap SMRAM ranges.
**/
BOOLEAN
EFIAPI
BufferOverlapSmram (
IN VOID *Buffer,
IN UINTN BufferSize
)
{
UINTN Index;
EFI_PHYSICAL_ADDRESS BufferEnd;
EFI_PHYSICAL_ADDRESS BufferStart;
if (mSmst == NULL || Buffer == NULL || BufferSize == 0) {
return FALSE;
}
BufferStart = (EFI_PHYSICAL_ADDRESS) (UINTN) Buffer;
BufferEnd = (EFI_PHYSICAL_ADDRESS) ((UINTN) Buffer + BufferSize - 1);
//
// Integer overflow check
//
if (BufferEnd < BufferStart) {
return TRUE;
}
for (Index = 0; Index < mSmramRangeCount; Index ++) {
//
// The condition for two ranges doesn't overlap is:
// Buffer End is smaller than the range start or Buffer start is larger than the range end.
// so the overlap condition is above condition isn't satisfied.
//
if (!(BufferEnd < mSmramRanges[Index].CpuStart ||
BufferStart >= (mSmramRanges[Index].CpuStart + mSmramRanges[Index].PhysicalSize))) {
return TRUE;
}
}
return FALSE;
}
/**
Record variable error flag.
@param[in] Flag Variable error flag to record.
@param[in] VariableName Name of variable.
@param[in] VendorGuid Guid of variable.
@param[in] Attributes Attributes of the variable.
@param[in] VariableSize Size of the variable.
**/
VOID
RecordVarErrorFlag (
IN VAR_ERROR_FLAG Flag,
IN CHAR16 *VariableName,
IN EFI_GUID *VendorGuid,
IN UINT32 Attributes,
IN UINTN VariableSize
)
{
EFI_STATUS Status;
VARIABLE_POINTER_TRACK Variable;
VAR_ERROR_FLAG *VarErrFlag;
VAR_ERROR_FLAG TempFlag;
UINTN VariableCount;
if (!AfterEndOfDxe ()) {
//
// Before EndOfDxe, just record the current boot variable error flag to local variable,
// and leave the variable error flag in NV flash as the last boot variable error flag.
// After EndOfDxe in InitializeVarErrorFlag (), the variable error flag in NV flash
// will be initialized to this local current boot variable error flag.
//
mCurrentBootVarErrFlag &= Flag;
return;
}
//
// Record error flag (it should have be initialized).
//
VariableCount = 0;
Status = FindVariableByLifetime (
VAR_ERROR_FLAG_NAME,
&gEdkiiVarErrorFlagGuid,
&Variable,
&VariableCount,
&mVariableModuleGlobal->VariableBase
);
if (!EFI_ERROR (Status)) {
VarErrFlag = (VAR_ERROR_FLAG *) GetVariableDataPtr (Variable.CurrPtr);
TempFlag = *VarErrFlag;
TempFlag &= Flag;
if (TempFlag == *VarErrFlag) {
return;
}
Status = UpdateVariableStore (
&mVariableModuleGlobal->VariableBase,
FALSE,
FALSE,
(UINTN) VarErrFlag,
sizeof (TempFlag),
&TempFlag
);
if (!EFI_ERROR (Status)) {
//
// Update the data in NV cache.
//
*VarErrFlag = TempFlag;
}
}
}
/**
Get maximum user variable space which can be used.
@return maximum user variable space by bytes.
**/
UINTN
GetMaxUserVariableSpace (
VOID
)
{
if (mSmst == NULL) {
return mVariableModuleGlobal->CommonMaxUserVariableSpace;
}
return mSmmVariableGlobal->ProtectedModeVariableModuleGlobal->CommonMaxUserVariableSpace;
}
/**
Get total user variable size by bytes.
@return the size by bytes of user variable.
**/
UINTN
GetUserVariableTotalSize (
VOID
)
{
if (mSmst == NULL) {
return mVariableModuleGlobal->CommonUserVariableTotalSize;
}
return mSmmVariableGlobal->ProtectedModeVariableModuleGlobal->CommonUserVariableTotalSize;
}
/**
Is user variable?
@param[in] VariableName Name of Variable to be found.
@param[in] VendorGuid Variable vendor GUID.
@retval TRUE User variable.
@retval FALSE System variable.
**/
BOOLEAN
IsUserVariable (
IN CHAR16 *VariableName,
IN EFI_GUID *VendorGuid
)
{
VAR_CHECK_VARIABLE_PROPERTY Property;
//
// Only after End Of Dxe, the variables belong to system variable are fixed.
// If PcdMaxUserNvStorageVariableSize is 0, it means user variable share the same NV storage with system variable,
// then no need to check if the variable is user variable or not specially.
//
if (AfterEndOfDxe () && (GetMaxUserVariableSpace () != 0)) {
if (InternalVarCheckVariablePropertyGet (VariableName, VendorGuid, &Property) == EFI_NOT_FOUND) {
return TRUE;
}
}
return FALSE;
}
/**
Initialize variable error flag.
Before EndOfDxe, the variable indicates the last boot variable error flag,
then it means the last boot variable error flag must be got before EndOfDxe.
After EndOfDxe, the variable indicates the current boot variable error flag,
then it means the current boot variable error flag must be got after EndOfDxe.
**/
STATIC
VOID
InitializeVarErrorFlag (
VOID
)
{
EFI_STATUS Status;
VARIABLE_POINTER_TRACK Variable;
VAR_ERROR_FLAG Flag;
VAR_ERROR_FLAG VarErrFlag;
UINTN VariableCount;
if (!AfterEndOfDxe ()) {
return;
}
Flag = mCurrentBootVarErrFlag;
VariableCount = 0;
Status = FindVariableByLifetime (
VAR_ERROR_FLAG_NAME,
&gEdkiiVarErrorFlagGuid,
&Variable,
&VariableCount,
&mVariableModuleGlobal->VariableBase
);
if (!EFI_ERROR (Status)) {
VarErrFlag = *((VAR_ERROR_FLAG *) GetVariableDataPtr (Variable.CurrPtr));
if (VarErrFlag == Flag) {
return;
}
}
UpdateVariable (
VAR_ERROR_FLAG_NAME,
&gEdkiiVarErrorFlagGuid,
&Flag,
sizeof (Flag),
VARIABLE_ATTRIBUTE_NV_BS_RT,
0,
0,
&Variable,
NULL,
&mVariableModuleGlobal->VariableBase
);
}
/**
Calculate common user variable total size.
**/
STATIC
VOID
CalculateCommonUserVariableTotalSize (
VOID
)
{
VARIABLE_HEADER *Variable;
VARIABLE_HEADER *NextVariable;
UINTN VariableSize;
VAR_CHECK_VARIABLE_PROPERTY Property;
//
// Only after End Of Dxe, the variables belong to system variable are fixed.
// If PcdMaxUserNvStorageVariableSize is 0, it means user variable share the same NV storage with system variable,
// then no need to calculate the common user variable total size specially.
//
if (AfterEndOfDxe () && (GetMaxUserVariableSpace () != 0)) {
Variable = GetStartPointer ((VARIABLE_STORE_HEADER *) mVariableModuleGlobal->NonVolatileVariableCache);
while (IsValidVariableHeaderInVarRegion (Variable, GetNonVolatileEndPointer ((VARIABLE_STORE_HEADER *) mVariableModuleGlobal->NonVolatileVariableCache))) {
NextVariable = GetNextVariablePtr (Variable);
VariableSize = (UINTN) NextVariable - (UINTN) Variable;
if ((Variable->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) != EFI_VARIABLE_HARDWARE_ERROR_RECORD) {
if (InternalVarCheckVariablePropertyGet ((CHAR16 *) (Variable + 1), &Variable->VendorGuid, &Property) == EFI_NOT_FOUND) {
//
// No property, it is user variable.
//
if (mSmst == NULL) {
mVariableModuleGlobal->CommonUserVariableTotalSize += VariableSize;
} else {
mSmmVariableGlobal->ProtectedModeVariableModuleGlobal->CommonUserVariableTotalSize += VariableSize;
}
}
}
Variable = NextVariable;
}
}
}
/**
Initialize variable quota.
**/
VOID
InitializeVariableQuota (
VOID
)
{
STATIC BOOLEAN Initialized = FALSE;
if (!AfterEndOfDxe () || Initialized) {
return;
}
Initialized = TRUE;
InitializeVarErrorFlag ();
CalculateCommonUserVariableTotalSize ();
}
/**
Calculate the sum of all elements in a buffer in unit of UINT8.
During calculation, the carry bits are dropped.
@param Buffer Pointer to the buffer to carry out the sum operation.
@param Length The size, in bytes, of Buffer.
@return The sum of Buffer with carry bits dropped during additions.
**/
UINT16
EFIAPI
InternalCalculateSum16 (
IN CONST UINT8 *Buffer,
IN UINTN Length
)
{
UINT32 Sum;
UINTN Count;
for (Sum = 0, Count = 0; Count < Length; Count++) {
Sum = (UINT32) (Sum + *(Buffer + Count));
}
Sum = 0x10000 - (Sum & 0x0000ffff);
return (UINT16) Sum;
}
/**
Initialize authenticated services.
@param[in] Event Event whose notification function is being invoked.
@param[in] Context Pointer to the notification function's context.
**/
VOID
EFIAPI
CryptoCallback (
IN EFI_EVENT Event,
IN VOID *Context
)
{
EFI_STATUS Status;
STATIC BOOLEAN Initialized = FALSE;
if (!Initialized) {
Status = AuthenticatedVariableServiceInitialize (&mVariableModuleGlobal->VariableBase);
Initialized = TRUE;
ASSERT_EFI_ERROR (Status);
}
if (Event == NULL) {
return;
}
if (mSmst == NULL) {
gBS->CloseEvent (Event);
}
}
/**
The notification of gEfiCryptoServiceProtocolGuid protocol is installed
@param[in] Protocol Points to the protocol's unique identifier.
@param[in] Interface Points to the interface instance.
@param[in] Handle The handle on which the interface was installed.
**/
EFI_STATUS
EFIAPI
CryptoServicesInstalled (
IN CONST EFI_GUID *Protocol,
IN VOID *Interface,
IN EFI_HANDLE Handle
)
{
CryptoCallback (NULL, NULL);
return EFI_SUCCESS;
}
/**
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[in] Event Event whose notification function is being invoked.
@param[in] Context Pointer to the notification function's context.
**/
VOID
EFIAPI
OnEndOfDxe (
IN EFI_EVENT Event,
IN VOID *Context
)
{
if (!mVariableModuleGlobal->EndOfDxe) {
CreateVariableLockListThroughSmi ();
CreateVariableCheckListThroughSmi ();
}
mVariableModuleGlobal->EndOfDxe = TRUE;
if (PcdGetBool (PcdReclaimVariableSpaceAtEndOfDxe)) {
ReclaimForOS ();
}
InitializeVariableQuota ();
ProtocolLockVariablePolicy ();
}
/**
Callback function for ready to boot event.
@param[in] Event - Event whose notification function is being invoked.
@param[in] Context - Pointer to the notification function's context.
**/
VOID
EFIAPI
ReadyToBootCallback (
IN EFI_EVENT Event,
IN VOID *Context
)
{
if (mReadyToBootEventSignaled) {
return;
}
if (!mVariableModuleGlobal->EndOfDxe) {
CreateVariableLockListThroughSmi ();
CreateVariableCheckListThroughSmi ();
}
mVariableModuleGlobal->EndOfDxe = TRUE;
mReadyToBootEventSignaled = TRUE;
InitializeVariableQuota ();
ReclaimForOS ();
}
/**
Callback function for legacy boot event.
@param[in] Event Event whose notification function is being invoked.
@param[in] Context Pointer to the notification function's context.
**/
VOID
EFIAPI
LegacyBootCallback (
IN EFI_EVENT Event,
IN VOID *Context
)
{
DisableVariableCache ();
if (mVariableModuleGlobal->SmmCodeReady && mSmst == NULL) {
InitCommunicationBufferHeader ();
mVariableModuleGlobal->SmmVarBuf->Signature = SMM_LEGACY_BOOT_SIGNATURE;
mVariableModuleGlobal->SmmVarBuf->AccessType = LEGACY_BOOT_SMI_FUN_NUM;
SendCommunicateBuffer ();
}
gBS->CloseEvent (Event);
return;
}
/**
This function allows the caller to determine if UEFI ExitBootServices() has been called.
This function returns TRUE after all the EVT_SIGNAL_EXIT_BOOT_SERVICES functions have
executed as a result of the OS calling ExitBootServices(). Prior to this time FALSE
is returned. This function is used by runtime code to decide it is legal to access
services that go away after ExitBootServices().
@retval TRUE The system has finished executing the EVT_SIGNAL_EXIT_BOOT_SERVICES event.
@retval FALSE The system has not finished executing the EVT_SIGNAL_EXIT_BOOT_SERVICES event.
**/
BOOLEAN
EFIAPI
AfterEndOfDxe (
VOID
)
{
return mSmst == NULL ? mVariableModuleGlobal->EndOfDxe : mSmmVariableGlobal->ProtectedModeVariableModuleGlobal->EndOfDxe;
}
/**
This function allows the caller to determine if UEFI ExitBootServices() has been called.
This function returns TRUE after all the EVT_SIGNAL_EXIT_BOOT_SERVICES functions have
executed as a result of the OS calling ExitBootServices(). Prior to this time FALSE
is returned. This function is used by runtime code to decide it is legal to access
services that go away after ExitBootServices().
@retval TRUE The system has finished executing the EVT_SIGNAL_EXIT_BOOT_SERVICES event.
@retval FALSE The system has not finished executing the EVT_SIGNAL_EXIT_BOOT_SERVICES event.
**/
BOOLEAN
EFIAPI
VariableAtRuntime (
VOID
)
{
return mSmst == NULL ? mVariableModuleGlobal->AtRuntime : mSmmVariableGlobal->ProtectedModeVariableModuleGlobal->AtRuntime;
}
/**
Creates and returns a notification event and registers that event with all the protocol
instances specified by ProtocolGuid.
This function causes the notification function to be executed for every protocol of type
ProtocolGuid instance that exists in the system when this function is invoked. In addition,
every time a protocol of type ProtocolGuid instance is installed or reinstalled, the notification
function is also executed. This function returns the notification event that was created.
If ProtocolGuid is NULL, then ASSERT().
If NotifyTpl is not a legal TPL value, then ASSERT().
If NotifyFunction is NULL, then ASSERT().
If Registration is NULL, then ASSERT().
@param[in] ProtocolGuid Supplies GUID of the protocol upon whose installation the event is fired.
@param[in] NotifyTpl Supplies the task priority level of the event notifications.
@param[in] NotifyFunction Supplies the function to notify when the event is signaled.
@param[in] NotifyContext The context parameter to pass to NotifyFunction.
@param[in] Registration A pointer to a memory location to receive the registration value.
This value is passed to LocateHandle() to obtain new handles that
have been added that support the ProtocolGuid-specified protocol.
@return The notification event that was created.
**/
EFI_EVENT
EFIAPI
VariableCreateProtocolNotifyEvent (
IN EFI_GUID *ProtocolGuid,
IN EFI_TPL NotifyTpl,
IN EFI_EVENT_NOTIFY NotifyFunction,
IN VOID *NotifyContext, OPTIONAL
OUT VOID **Registration
)
{
EFI_STATUS Status;
EFI_EVENT Event;
ASSERT (ProtocolGuid != NULL);
ASSERT (NotifyFunction != NULL);
ASSERT (Registration != NULL);
//
// Create the event
//
Status = gBS->CreateEvent (
EVT_NOTIFY_SIGNAL,
NotifyTpl,
NotifyFunction,
NotifyContext,
&Event
);
ASSERT_EFI_ERROR (Status);
//
// Register for protocol notifications on this event
//
Status = gBS->RegisterProtocolNotify (
ProtocolGuid,
Event,
Registration
);
ASSERT_EFI_ERROR (Status);
return Event;
}
/**
According to system mode to allocate pool.
Allocate BootServicesData pool in protect mode if Runtime is FALSE.
Allocate EfiRuntimeServicesData pool in protect mode if Runtime is TRUE.
Allocate memory from SMM ram if system in SMM mode.
@param[in] Size The size of buffer to allocate
@param[in] Runtime Runtime Data or not.
@return Pointer to allocated memory buffer or NULL if allocate failed.
**/
VOID *
VariableAllocateZeroBuffer (
IN UINTN Size,
IN BOOLEAN Runtime
)
{
if (mSmst == NULL && Runtime == TRUE) {
return AllocateRuntimeZeroPool(Size);
} else {
return AllocateZeroPool(Size);
}
}
/**
Convert the standard Lib double linked list to a virtual mapping.
This service uses gRT->ConvertPointer() to walk a double linked list and convert all the link
pointers to their virtual mappings. This function is only guaranteed to work during the
EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event and calling it at other times has undefined results.
@param[in] ListHead Head of linked list to convert.
**/
VOID
ConvertList (
IN OUT LIST_ENTRY *ListHead
)
{
LIST_ENTRY *Link;
LIST_ENTRY *NextLink;
if (ListHead == NULL) {
return;
}
//
// Convert all the ForwardLink & BackLink pointers in the list
//
Link = ListHead;
do {
NextLink = Link->ForwardLink;
gRT->ConvertPointer (0, (VOID **) &Link->ForwardLink);
gRT->ConvertPointer (0, (VOID **) &Link->BackLink);
Link = NextLink;
} while (Link != NULL && Link != ListHead);
}
/**
Check the CRC32 for whole non-volatile variable cache is whether correct.
@retval TRUE The CRC32 value is correct
@retval FALSE The CRC32 value is incorrect
**/
BOOLEAN
CheckNonVolatileVariableCacheCrc32 (
VOID
)
{
UINT32 Crc32;
if (mVariableModuleGlobal->NonVolatileVariableCache == NULL) {
return FALSE;
}
if (mSmst == NULL) {
Crc32 = mVariableModuleGlobal->NonVolatileVariableCacheCrc32;
} else {
Crc32 = mSmmVariableGlobal->ProtectedModeVariableModuleGlobal->NonVolatileVariableCacheCrc32;
}
return (BOOLEAN) (Crc32 == CalculateCrc32 (
mVariableModuleGlobal->NonVolatileVariableCache,
mVariableModuleGlobal->NonVolatileVariableCacheSize
));
}
/**
Calculate the CRC32 for whole non-volatile variable cache.
@return The CRC32 for whole non-volatile variable cache.
**/
UINT32
CalculateNonVolatileVariableCacheCrc32 (
VOID
)
{
if (mVariableModuleGlobal->NonVolatileVariableCache == NULL) {
return 0;
}
return CalculateCrc32 (
mVariableModuleGlobal->NonVolatileVariableCache,
mVariableModuleGlobal->NonVolatileVariableCacheSize
);
}
/**
Calculate the CRC32 for whole non-volatile variable cache and set to NonVolatileVariableCacheCrc32.
**/
VOID
SetNonVolatileVariableCacheCrc32 (
VOID
)
{
UINT32 Crc32;
Crc32 = CalculateNonVolatileVariableCacheCrc32 ();
if (mSmst == NULL) {
mVariableModuleGlobal->NonVolatileVariableCacheCrc32 = Crc32;
} else {
mSmmVariableGlobal->ProtectedModeVariableModuleGlobal->NonVolatileVariableCacheCrc32 = Crc32;
}
}
/**
This service is consumed by the variable modules to place a barrier to stop
speculative execution.
Ensures that no later instruction will execute speculatively, until all prior
instructions have completed.
**/
VOID
VariableSpeculationBarrier (
VOID
)
{
if (mSmst != NULL) {
SpeculationBarrier ();
}
}