/** @file
UEFI variable support functions for Firmware Management Protocol based
firmware updates.
Copyright (c) 2016, Microsoft Corporation. All rights reserved.
Copyright (c) 2018, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include
#include
#include
#include
#include
#include
#include
#include "VariableSupport.h"
///
/// Array of UEFI variable names that are locked in LockAllFmpVariables().
///
const CHAR16 *mFmpVariableLockList[] = {
VARNAME_VERSION,
VARNAME_LSV,
VARNAME_LASTATTEMPTSTATUS,
VARNAME_LASTATTEMPTVERSION
};
/**
Returns the value used to fill in the Version field of the
EFI_FIRMWARE_IMAGE_DESCRIPTOR structure that is returned by the GetImageInfo()
service of the Firmware Management Protocol. The value is read from a UEFI
variable. If the UEFI variables does not exist, then a default version value
is returned.
UEFI Variable accessed: GUID = gEfiCallerIdGuid, Name = L"FmpVersion"
@return The version of the firmware image in the firmware device.
**/
UINT32
GetVersionFromVariable (
VOID
)
{
EFI_STATUS Status;
UINT32 *Value;
UINTN Size;
UINT32 Version;
Value = NULL;
Size = 0;
Version = DEFAULT_VERSION;
Status = GetVariable2 (VARNAME_VERSION, &gEfiCallerIdGuid, (VOID **)&Value, &Size);
if (EFI_ERROR (Status) || (Value == NULL)) {
DEBUG ((DEBUG_ERROR, "Failed to get the Version from variable. Status = %r\n", Status));
return Version;
}
//
// No error from call
//
if (Size == sizeof (*Value)) {
//
// Successful read
//
Version = *Value;
} else {
//
// Return default since size was unknown
//
DEBUG ((DEBUG_ERROR, "Getting version Variable returned a size different than expected. Size = 0x%x\n", Size));
}
FreePool (Value);
return Version;
}
/**
Returns the value used to fill in the LowestSupportedVersion field of the
EFI_FIRMWARE_IMAGE_DESCRIPTOR structure that is returned by the GetImageInfo()
service of the Firmware Management Protocol. The value is read from a UEFI
variable. If the UEFI variables does not exist, then a default lowest
supported version value is returned.
UEFI Variable accessed: GUID = gEfiCallerIdGuid, Name = L"FmpLsv"
@return The lowest supported version of the firmware image in the firmware
device.
**/
UINT32
GetLowestSupportedVersionFromVariable (
VOID
)
{
EFI_STATUS Status;
UINT32 *Value;
UINTN Size;
UINT32 Version;
Value = NULL;
Size = 0;
Version = DEFAULT_LOWESTSUPPORTEDVERSION;
Status = GetVariable2 (VARNAME_LSV, &gEfiCallerIdGuid, (VOID **)&Value, &Size);
if (EFI_ERROR (Status) || (Value == NULL)) {
DEBUG ((DEBUG_WARN, "Warning: Failed to get the Lowest Supported Version from variable. Status = %r\n", Status));
return Version;
}
//
// No error from call
//
if (Size == sizeof (*Value)) {
//
// Successful read
//
Version = *Value;
} else {
//
// Return default since size was unknown
//
DEBUG ((DEBUG_ERROR, "Getting LSV Variable returned a size different than expected. Size = 0x%x\n", Size));
}
FreePool (Value);
return Version;
}
/**
Returns the value used to fill in the LastAttemptStatus field of the
EFI_FIRMWARE_IMAGE_DESCRIPTOR structure that is returned by the GetImageInfo()
service of the Firmware Management Protocol. The value is read from a UEFI
variable. If the UEFI variables does not exist, then a default last attempt
status value is returned.
UEFI Variable accessed: GUID = gEfiCallerIdGuid, Name = L"LastAttemptStatus"
@return The last attempt status value for the most recent capsule update.
**/
UINT32
GetLastAttemptStatusFromVariable (
VOID
)
{
EFI_STATUS Status;
UINT32 *Value;
UINTN Size;
UINT32 LastAttemptStatus;
Value = NULL;
Size = 0;
LastAttemptStatus = DEFAULT_LASTATTEMPT;
Status = GetVariable2 (VARNAME_LASTATTEMPTSTATUS, &gEfiCallerIdGuid, (VOID **)&Value, &Size);
if (EFI_ERROR (Status) || (Value == NULL)) {
DEBUG ((DEBUG_WARN, "Warning: Failed to get the Last Attempt Status from variable. Status = %r\n", Status));
return LastAttemptStatus;
}
//
// No error from call
//
if (Size == sizeof (*Value)) {
//
// Successful read
//
LastAttemptStatus = *Value;
} else {
//
// Return default since size was unknown
//
DEBUG (
(DEBUG_ERROR,
"Getting Last Attempt Status Variable returned a size different than expected. Size = 0x%x\n",
Size)
);
}
FreePool (Value);
return LastAttemptStatus;
}
/**
Returns the value used to fill in the LastAttemptVersion field of the
EFI_FIRMWARE_IMAGE_DESCRIPTOR structure that is returned by the GetImageInfo()
service of the Firmware Management Protocol. The value is read from a UEFI
variable. If the UEFI variables does not exist, then a default last attempt
version value is returned.
UEFI Variable accessed: GUID = gEfiCallerIdGuid, Name = L"LastAttemptVersion"
@return The last attempt version value for the most recent capsule update.
**/
UINT32
GetLastAttemptVersionFromVariable (
VOID
)
{
EFI_STATUS Status;
UINT32 *Value;
UINTN Size;
UINT32 Version;
Value = NULL;
Size = 0;
Version = DEFAULT_LASTATTEMPT;
Status = GetVariable2 (VARNAME_LASTATTEMPTVERSION, &gEfiCallerIdGuid, (VOID **)&Value, &Size);
if (EFI_ERROR (Status) || (Value == NULL)) {
DEBUG ((DEBUG_WARN, "Warning: Failed to get the Last Attempt Version from variable. Status = %r\n", Status));
return Version;
}
//
// No error from call
//
if (Size == sizeof (*Value)) {
//
// Successful read
//
Version = *Value;
} else {
//
// Return default since size was unknown
//
DEBUG (
(DEBUG_ERROR,
"Getting Last Attempt Version variable returned a size different than expected. Size = 0x%x\n",
Size)
);
}
FreePool (Value);
return Version;
}
/**
Saves the version current of the firmware image in the firmware device to a
UEFI variable.
UEFI Variable accessed: GUID = gEfiCallerIdGuid, Name = L"FmpVersion"
@param[in] Version The version of the firmware image in the firmware device.
**/
VOID
SetVersionInVariable (
UINT32 Version
)
{
EFI_STATUS Status;
UINT32 Current;
Status = EFI_SUCCESS;
Current = GetVersionFromVariable();
if (Current != Version) {
Status = gRT->SetVariable (
VARNAME_VERSION,
&gEfiCallerIdGuid,
EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
sizeof (Version),
&Version
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "Failed to set the Version into a variable. Status = %r\n", Status));
}
} else {
DEBUG ((DEBUG_INFO, "Version variable doesn't need to update. Same value as before.\n"));
}
}
/**
Saves the lowest supported version current of the firmware image in the
firmware device to a UEFI variable.
UEFI Variable accessed: GUID = gEfiCallerIdGuid, Name = L"FmpLsv"
@param[in] LowestSupportedVersion The lowest supported version of the firmware image
in the firmware device.
**/
VOID
SetLowestSupportedVersionInVariable (
UINT32 LowestSupportedVersion
)
{
EFI_STATUS Status;
UINT32 Current;
Status = EFI_SUCCESS;
Current = GetLowestSupportedVersionFromVariable();
if (LowestSupportedVersion > Current) {
Status = gRT->SetVariable (
VARNAME_LSV,
&gEfiCallerIdGuid,
EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
sizeof (LowestSupportedVersion), &LowestSupportedVersion
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "Failed to set the LSV into a variable. Status = %r\n", Status));
}
} else {
DEBUG ((DEBUG_INFO, "LSV variable doesn't need to update. Same value as before.\n"));
}
}
/**
Saves the last attempt status value of the most recent FMP capsule update to a
UEFI variable.
UEFI Variable accessed: GUID = gEfiCallerIdGuid, Name = L"LastAttemptStatus"
@param[in] LastAttemptStatus The last attempt status of the most recent FMP
capsule update.
**/
VOID
SetLastAttemptStatusInVariable (
UINT32 LastAttemptStatus
)
{
EFI_STATUS Status;
UINT32 Current;
Status = EFI_SUCCESS;
Current = GetLastAttemptStatusFromVariable();
if (Current != LastAttemptStatus) {
Status = gRT->SetVariable (
VARNAME_LASTATTEMPTSTATUS,
&gEfiCallerIdGuid,
EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
sizeof (LastAttemptStatus),
&LastAttemptStatus
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "Failed to set the LastAttemptStatus into a variable. Status = %r\n", Status));
}
} else {
DEBUG ((DEBUG_INFO, "LastAttemptStatus variable doesn't need to update. Same value as before.\n"));
}
}
/**
Saves the last attempt version value of the most recent FMP capsule update to
a UEFI variable.
UEFI Variable accessed: GUID = gEfiCallerIdGuid, Name = L"LastAttemptVersion"
@param[in] LastAttemptVersion The last attempt version value of the most
recent FMP capsule update.
**/
VOID
SetLastAttemptVersionInVariable (
UINT32 LastAttemptVersion
)
{
EFI_STATUS Status;
UINT32 Current;
Status = EFI_SUCCESS;
Current = GetLastAttemptVersionFromVariable();
if (Current != LastAttemptVersion) {
Status = gRT->SetVariable (
VARNAME_LASTATTEMPTVERSION,
&gEfiCallerIdGuid,
EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
sizeof (LastAttemptVersion),
&LastAttemptVersion
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "Failed to set the LastAttemptVersion into a variable. Status = %r\n", Status));
}
} else {
DEBUG ((DEBUG_INFO, "LastAttemptVersion variable doesn't need to update. Same value as before.\n"));
}
}
/**
Locks all the UEFI Variables used by this module.
@retval EFI_SUCCESS All UEFI variables are locked.
@retval EFI_UNSUPPORTED Variable Lock Protocol not found.
@retval Other One of the UEFI variables could not be locked.
**/
EFI_STATUS
LockAllFmpVariables (
VOID
)
{
EFI_STATUS Status;
EDKII_VARIABLE_LOCK_PROTOCOL *VariableLock;
EFI_STATUS ReturnStatus;
UINTN Index;
VariableLock = NULL;
Status = gBS->LocateProtocol (
&gEdkiiVariableLockProtocolGuid,
NULL,
(VOID **)&VariableLock
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "FmpDxe: Failed to locate Variable Lock Protocol (%r).\n", Status));
return EFI_UNSUPPORTED;
}
ReturnStatus = EFI_SUCCESS;
for (Index = 0; Index < ARRAY_SIZE (mFmpVariableLockList); Index++) {
Status = VariableLock->RequestToLock (
VariableLock,
(CHAR16 *)mFmpVariableLockList[Index],
&gEfiCallerIdGuid
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "FmpDxe: Failed to lock variable %g %s. Status = %r\n",
&gEfiCallerIdGuid,
mFmpVariableLockList[Index],
Status
));
if (!EFI_ERROR (ReturnStatus)) {
ReturnStatus = Status;
}
}
}
return ReturnStatus;
}