//
// This file contains a 'Sample Driver' and is licensed as such
// under the terms of your license agreement with Intel or your
// vendor. This file may be modified by the user, subject to
// the additional terms of the license agreement
//
/**
Read and edit the EFI variable.
Copyright (c) 2014 - 2018, Intel Corporation. All rights reserved.
This software and associated documentation (if any) is furnished
under a license and may only be used or copied in accordance
with the terms of the license. Except as permitted by such
license, no part of this software or documentation may be
reproduced, stored in a retrieval system, or transmitted in any
form or by any means without the express written consent of
Intel Corporation.
**/
#include "KeyEnroll.h"
#include "VariableCommon.h"
#include "Variable.h"
#include
EFI_GUID gEfiVariableGuid = EFI_VARIABLE_GUID;
EFI_GUID gEfiAuthenticatedVariableGuid = EFI_AUTHENTICATED_VARIABLE_GUID;
EFI_GUID gEfiAuthenticatedVariableBasedTimeGuid = EFI_AUTHENTICATED_VARIABLE_BASED_TIME_GUID;
/**
Gets the pointer to the first variable header in given variable store area.
@param VarStoreHeader Pointer to the Variable Store Header.
@return Pointer to the first variable header.
**/
VARIABLE_HEADER *
GetStartPointer (
IN VARIABLE_STORE_HEADER *VarStoreHeader
)
{
//
// The end of variable store.
//
return (VARIABLE_HEADER *) HEADER_ALIGN (VarStoreHeader + 1);
}
/**
Gets the pointer to the end of the variable storage area.
This function gets pointer to the end of the variable storage
area, according to the input variable store header.
@param VarStoreHeader Pointer to the Variable Store Header.
@return Pointer to the end of the variable storage area.
**/
VARIABLE_HEADER *
GetEndPointer (
IN VARIABLE_STORE_HEADER *VarStoreHeader
)
{
//
// The end of variable store
//
return (VARIABLE_HEADER *) HEADER_ALIGN ((UINTN) VarStoreHeader + VarStoreHeader->Size);
}
/**
This code checks if variable header is valid or not.
@param Variable Pointer to the Variable Header.
@retval TRUE Variable header is valid.
@retval FALSE Variable header is not valid.
**/
BOOLEAN
IsValidVariableHeader (
IN VARIABLE_HEADER *Variable
)
{
if ((Variable == NULL) || (Variable->Common.StartId != VARIABLE_DATA)) {
return FALSE;
}
return TRUE;
}
/**
This code gets the size of name of variable.
@param Variable Pointer to the Variable Header.
@return UINTN Size of variable in bytes.
**/
UINTN
NameSizeOfVariable (
IN VARIABLE_HEADER *Variable,
IN VARIABLE_TYPE VariableType
)
{
switch (VariableType) {
case VariableTypeNormal:
if ((Variable->Normal.State == (UINT8) (-1)) ||
(Variable->Normal.DataSize == (UINT32) (-1)) ||
(Variable->Normal.NameSize == (UINT32) (-1)) ||
(Variable->Normal.Attributes == (UINT32) (-1))
) {
return 0;
}
return (UINTN) Variable->Normal.NameSize;
case VariableTypeCountBasedAuth:
if ((Variable->CountBasedAuth.State == (UINT8) (-1)) ||
(Variable->CountBasedAuth.DataSize == (UINT32) (-1)) ||
(Variable->CountBasedAuth.NameSize == (UINT32) (-1)) ||
(Variable->CountBasedAuth.Attributes == (UINT32) (-1))
) {
return 0;
}
return (UINTN) Variable->CountBasedAuth.NameSize;
case VariableTypeTimeBasedAuth:
if ((Variable->TimeBasedAuth.State == (UINT8) (-1)) ||
(Variable->TimeBasedAuth.DataSize == (UINT32) (-1)) ||
(Variable->TimeBasedAuth.NameSize == (UINT32) (-1)) ||
(Variable->TimeBasedAuth.Attributes == (UINT32) (-1))
) {
return 0;
}
return (UINTN) Variable->TimeBasedAuth.NameSize;
default:
return 0;
}
}
/**
This code gets the size of variable data.
@param Variable Pointer to the Variable Header.
@return Size of variable in bytes.
**/
UINTN
DataSizeOfVariable (
IN VARIABLE_HEADER *Variable,
IN VARIABLE_TYPE VariableType
)
{
switch (VariableType) {
case VariableTypeNormal:
if ((Variable->Normal.State == (UINT8) (-1)) ||
(Variable->Normal.DataSize == (UINT32) (-1)) ||
(Variable->Normal.NameSize == (UINT32) (-1)) ||
(Variable->Normal.Attributes == (UINT32) (-1))
) {
return 0;
}
return (UINTN) Variable->Normal.DataSize;
case VariableTypeCountBasedAuth:
if ((Variable->CountBasedAuth.State == (UINT8) (-1)) ||
(Variable->CountBasedAuth.DataSize == (UINT32) (-1)) ||
(Variable->CountBasedAuth.NameSize == (UINT32) (-1)) ||
(Variable->CountBasedAuth.Attributes == (UINT32) (-1))
) {
return 0;
}
return (UINTN) Variable->CountBasedAuth.DataSize;
case VariableTypeTimeBasedAuth:
if ((Variable->TimeBasedAuth.State == (UINT8) (-1)) ||
(Variable->TimeBasedAuth.DataSize == (UINT32) (-1)) ||
(Variable->TimeBasedAuth.NameSize == (UINT32) (-1)) ||
(Variable->TimeBasedAuth.Attributes == (UINT32) (-1))
) {
return 0;
}
return (UINTN) Variable->TimeBasedAuth.DataSize;
default:
return 0;
}
}
/**
This code gets the pointer to the variable name.
@param Variable Pointer to the Variable Header.
@return Pointer to Variable Name which is Unicode encoding.
**/
CHAR16 *
GetVariableNamePtr (
IN VARIABLE_HEADER *Variable,
IN VARIABLE_TYPE VariableType
)
{
switch (VariableType) {
case VariableTypeNormal:
return (CHAR16 *) ((UINTN)Variable + sizeof(VARIABLE_HEADER_NORMAL));
case VariableTypeCountBasedAuth:
return (CHAR16 *) ((UINTN)Variable + sizeof(VARIABLE_HEADER_COUNT_BASED_AUTH));
case VariableTypeTimeBasedAuth:
return (CHAR16 *) ((UINTN)Variable + sizeof(VARIABLE_HEADER_TIME_BASED_AUTH));
default:
return NULL;
}
}
/**
This code gets the pointer to the variable data.
@param Variable Pointer to the Variable Header.
@return Pointer to Variable Data.
**/
UINT8 *
GetVariableDataPtr (
IN VARIABLE_HEADER *Variable,
IN VARIABLE_TYPE VariableType
)
{
UINTN Value;
//
// Be careful about pad size for alignment.
//
Value = (UINTN) GetVariableNamePtr (Variable, VariableType);
Value += NameSizeOfVariable (Variable, VariableType);
Value += GET_PAD_SIZE (NameSizeOfVariable (Variable, VariableType));
return (UINT8 *) Value;
}
/**
This code gets the pointer to the next variable header.
@param Variable Pointer to the Variable Header.
@return Pointer to next variable header.
**/
VARIABLE_HEADER *
GetNextVariablePtr (
IN VARIABLE_HEADER *Variable,
IN VARIABLE_TYPE VariableType
)
{
UINTN Value;
if (!IsValidVariableHeader (Variable)) {
return NULL;
}
Value = (UINTN) GetVariableDataPtr (Variable, VariableType);
Value += DataSizeOfVariable (Variable, VariableType);
Value += GET_PAD_SIZE (DataSizeOfVariable (Variable, VariableType));
//
// Be careful about pad size for alignment.
//
return (VARIABLE_HEADER *) HEADER_ALIGN (Value);
}
/**
This code gets the pointer to the variable GUID.
@param Variable Pointer to the Variable Header.
@return Pointer to variable GUID.
**/
EFI_GUID *
GetVariableGuidPtr (
IN VARIABLE_HEADER *Variable,
IN VARIABLE_TYPE VariableType
)
{
switch (VariableType) {
case VariableTypeNormal:
return &Variable->Normal.VendorGuid;
case VariableTypeCountBasedAuth:
return &Variable->CountBasedAuth.VendorGuid;
case VariableTypeTimeBasedAuth:
return &Variable->TimeBasedAuth.VendorGuid;
default:
return NULL;
}
}
/**
This code gets the Attributes of variable data.
@param Variable Pointer to the Variable Header.
@return Attributes of variable.
**/
UINT32
AttributesOfVariable (
IN VARIABLE_HEADER *Variable,
IN VARIABLE_TYPE VariableType
)
{
return Variable->Normal.Attributes;
}
VOID
SetVariableAttributes (
IN VARIABLE_HEADER *Variable,
IN VARIABLE_TYPE VariableType,
IN UINT32 Attributes
)
{
Variable->Normal.Attributes = Attributes;
}
VOID
SetVariableNameSize (
IN VARIABLE_HEADER *Variable,
IN VARIABLE_TYPE VariableType,
IN UINT32 NameSize
)
{
switch (VariableType) {
case VariableTypeNormal:
Variable->Normal.NameSize = NameSize;
break;
case VariableTypeCountBasedAuth:
Variable->CountBasedAuth.NameSize = NameSize;
break;
case VariableTypeTimeBasedAuth:
Variable->TimeBasedAuth.NameSize = NameSize;
break;
default:
break;
}
}
VOID
SetVariableDataSize (
IN VARIABLE_HEADER *Variable,
IN VARIABLE_TYPE VariableType,
IN UINT32 DataSize
)
{
switch (VariableType) {
case VariableTypeNormal:
Variable->Normal.DataSize = DataSize;
break;
case VariableTypeCountBasedAuth:
Variable->CountBasedAuth.DataSize = DataSize;
break;
case VariableTypeTimeBasedAuth:
Variable->TimeBasedAuth.DataSize = DataSize;
break;
default:
break;
}
}
VOID
SetVariableGuid (
IN VARIABLE_HEADER *Variable,
IN VARIABLE_TYPE VariableType,
IN EFI_GUID *Guid
)
{
EFI_GUID *VendorGuid;
VendorGuid = GetVariableGuidPtr (Variable, VariableType);
if (VendorGuid != NULL) {
memcpy (VendorGuid, Guid, sizeof (EFI_GUID));
}
}
VOID
SetVariableName (
IN VARIABLE_HEADER *Variable,
IN VARIABLE_TYPE VariableType,
IN CHAR16 *Name,
IN UINTN NameSize
)
{
CHAR16 *VariableName;
VariableName = GetVariableNamePtr (Variable, VariableType);
if (VariableName != NULL) {
memcpy (VariableName, Name, NameSize);
}
}
VOID
SetVariableData (
IN VARIABLE_HEADER *Variable,
IN VARIABLE_TYPE VariableType,
IN VOID *Buffer,
IN UINTN BuffferSize
)
{
memcpy (GetVariableDataPtr (Variable, VariableType), Buffer, BuffferSize);
}
UINTN
SizeOfVariableHeader (
IN VARIABLE_TYPE VariableType
)
{
switch (VariableType) {
case VariableTypeNormal:
return sizeof(VARIABLE_HEADER_NORMAL);
case VariableTypeCountBasedAuth:
return sizeof(VARIABLE_HEADER_COUNT_BASED_AUTH);
case VariableTypeTimeBasedAuth:
return sizeof(VARIABLE_HEADER_TIME_BASED_AUTH);
default:
return 0;
}
}
/**
Search and get a free space in the EFI variable zone
@param VariableStoreHeader The start of a EFI variable zone.
@param VarListSize The size of a variables needs to be allocated.
@param FreeBeginVar The dual pointer to the free NV space.
@retval EFI_SUCCESS Return the beginning of a free variable space.
@retval RETURN_BUFFER_TOO_SMALL Failed.
**/
EFI_STATUS
GetVariableVar (
IN VARIABLE_STORE_HEADER *VariableStoreHeader,
IN VARIABLE_TYPE VariableType,
IN UINT32 VarListSize,
IN OUT CHAR8 **FreeBeginVar
)
{
BOOLEAN Flag;
VARIABLE_HEADER *Variable;
VARIABLE_HEADER *EndOfVariable;
CHAR8 *BeginVar;
BeginVar = NULL;
Flag = FALSE;
Variable = NULL;
EndOfVariable = NULL;
*FreeBeginVar = NULL;
if (VariableStoreHeader == NULL) {
*FreeBeginVar = NULL;
return RETURN_INVALID_PARAMETER;
}
Variable = GetStartPointer (VariableStoreHeader);
EndOfVariable = GetEndPointer(VariableStoreHeader);
//
//Search the beginning of free NV
//
while (Variable != EndOfVariable) {
BeginVar = (CHAR8 *)Variable;
Variable = GetNextVariablePtr (Variable, VariableType);
if (Variable == NULL) {
Flag = TRUE;
break;
}
}
//
// Check whether the free space is more than what we want
//
if ((CHAR8 *)BeginVar + VarListSize > (CHAR8 *)EndOfVariable) {
return RETURN_BUFFER_TOO_SMALL;
}
//
// If not find the available space, return NULL
//
if (!Flag) {
return RETURN_BUFFER_TOO_SMALL;
}
*FreeBeginVar = BeginVar;
return EFI_SUCCESS;
}
/**
Search whether the variable in VarList has existed in current NV.
Parse the FFS or Fd image, and find the valid variable pointer.
@param VariableStoreHeader The start of a EFI variable zone.
@param VarList The pointer to the VarList
@retval address If the variable existed in current NV, return address
@return NULL Otherwise, return NULL
**/
VARIABLE_HEADER *
FindVariableInNv (
IN VARIABLE_STORE_HEADER *VariableStoreHeader,
IN VARIABLE_TYPE VariableType,
IN VARIABLE_INFO_PRIVATE *Storage
)
{
BOOLEAN Flag;
VARIABLE_HEADER *Variable;
VARIABLE_HEADER *EndOfVariable;
CHAR16 *VariableName;
EFI_GUID *VendorGuid;
Flag = FALSE;
Variable = NULL;
EndOfVariable = NULL;
VariableName = NULL;
VendorGuid = NULL;
if ((VariableStoreHeader == NULL) || (Storage == NULL) || (Storage->Name == NULL)) {
return NULL;
}
Variable = GetStartPointer (VariableStoreHeader);
EndOfVariable = GetEndPointer(VariableStoreHeader);
//
// Parse and compare the variable in the NV space one by one
//
while ((Variable != EndOfVariable) && (Variable != NULL)) {
VariableName = (CHAR16 *)GetVariableNamePtr (Variable, VariableType);
if (NULL == VariableName) {
return NULL;
}
VendorGuid = GetVariableGuidPtr (Variable, VariableType);
if (NULL == VendorGuid) {
return NULL;
}
if ((CompareGuid (VendorGuid, &Storage->Guid) == 0) &&
(KeyEnrollStrCmp (Storage->Name, VariableName) == 0) &&
(Variable->Common.State == VAR_ADDED)) {
Flag = TRUE;
break;
}
Variable = GetNextVariablePtr (Variable, VariableType);
}
if (!Flag) {
return NULL;
}
return Variable;
}
/**
Exchange the data between Efi variable and the data of VarList when the
variable use the authenticated variable header
If VarToList is TRUE, copy the efi variable data to the VarList; Otherwise,
update the data from varlist to efi variable.
@param VarToList The flag to control the direction of exchange.
@param StorageListHead Decide which variale list be updated
@retval EFI_SUCCESS Get the address successfully.
@retval EFI_OUT_OF_RESOURCES No available in the EFI variable zone.
**/
EFI_STATUS
SynEfiVariable (
IN BOOLEAN VarToList,
IN VARIABLE_TYPE VariableType,
IN LIST_ENTRY *StorageListHead
)
{
EFI_FIRMWARE_VOLUME_HEADER *VarAddr;
LIST_ENTRY *StorageLink;
VARIABLE_INFO_PRIVATE *Storage;
EFI_STATUS Status;
CHAR8 *NewAvailableAddr;
VARIABLE_HEADER *VariableHeader;
VARIABLE_HEADER *EndOfVariable;
VARIABLE_STORE_HEADER *VariableStoreHeader;
UINTN VarNameSize;
CHAR16 *VariableName;
EFI_GUID *VendorGuid;
UINT32 Attributes;
EFI_TIME *TimeStamp;
VarAddr = (EFI_FIRMWARE_VOLUME_HEADER *) gEfiFdInfo.EfiVariableAddr;
VariableStoreHeader = (VARIABLE_STORE_HEADER *)((CHAR8 *)VarAddr + VarAddr->HeaderLength);
VariableHeader = GetStartPointer (VariableStoreHeader);
EndOfVariable = GetEndPointer(VariableStoreHeader);
if (VarToList) {
//
// Copy the data from NV to the VarList.
//
while ((UINTN)VariableHeader < (UINTN)EndOfVariable) {
if (!IsValidVariableHeader (VariableHeader)) {
break;
}
if (VariableHeader->Common.State != VAR_ADDED) {
VariableHeader = GetNextVariablePtr (VariableHeader, VariableType);
continue;
}
VariableName = GetVariableNamePtr (VariableHeader, VariableType);
VendorGuid = GetVariableGuidPtr (VariableHeader, VariableType);
if (NULL == VariableName || NULL == VendorGuid) {
return EFI_INVALID_PARAMETER;
}
Attributes = AttributesOfVariable (VariableHeader, VariableType);
TimeStamp = NULL;
if ((Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) != 0) {
TimeStamp = &VariableHeader->TimeBasedAuth.TimeStamp;
}
Status = CreateVariableList (
StorageListHead,
VariableName,
VendorGuid,
Attributes,
TimeStamp,
DataSizeOfVariable (VariableHeader, VariableType),
GetVariableDataPtr (VariableHeader, VariableType),
FALSE
);
if (EFI_ERROR (Status)) {
printf ("Error. CreateVariableList fail.\n");
return EFI_OUT_OF_RESOURCES;
}
VariableHeader = GetNextVariablePtr (VariableHeader, VariableType);
}
return EFI_SUCCESS;
} else {
//
// Clear Variable region, then copy the List data to the variable in NV directly.
//
memset (VariableHeader, 0xFF, (UINTN)EndOfVariable - (UINTN)VariableHeader);
NewAvailableAddr = NULL;
StorageLink = GetFirstNode (StorageListHead);
while (!IsNull (StorageListHead, StorageLink)) {
Storage = VARIABLE_INFO_PRIVATE_FROM_LINK (StorageLink);
//
// Report error, if the variable name is invalid.
//
if ((Storage->Name == NULL) || (KeyEnrollStrLen(Storage->Name) == 0)) {
printf ("Error. One variable name is NULL. Its GUID is: ");
PrintGuid(&(Storage->Guid));
return EFI_INVALID_PARAMETER;
}
//
// If existed, copy the List data to the variable in NV directly. If not found, create a new one.
//
VarNameSize = KeyEnrollStrSize (Storage->Name);
Status = GetVariableVar (
VariableStoreHeader,
VariableType,
GET_PAD_SIZE (KeyEnrollStrSize (Storage->Name)) + GET_PAD_SIZE (Storage->Size) + SizeOfVariableHeader (VariableType),
&NewAvailableAddr
);
if (EFI_ERROR (Status)) {
printf ("Error. No available space in NV ram.\n");
return EFI_OUT_OF_RESOURCES;
}
//
// Create the authenticated variable header
//
VariableHeader = (VARIABLE_HEADER *) NewAvailableAddr;
VariableHeader->Common.StartId = VARIABLE_DATA;
VariableHeader->Common.State = VAR_ADDED;
VariableHeader->Common.Reserved = 0x0;
switch (VariableType) {
case VariableTypeCountBasedAuth:
VariableHeader->CountBasedAuth.MonotonicCount = 0x0;
VariableHeader->CountBasedAuth.PubKeyIndex = 0x0;
break;
case VariableTypeTimeBasedAuth:
VariableHeader->TimeBasedAuth.MonotonicCount = 0x0;
VariableHeader->TimeBasedAuth.PubKeyIndex = 0x0;
memset (&(VariableHeader->TimeBasedAuth.TimeStamp), 0, sizeof (EFI_TIME));
if ((Storage->Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) != 0) {
CopyMem (&(VariableHeader->TimeBasedAuth.TimeStamp), &Storage->TimeStamp, sizeof (Storage->TimeStamp));
}
break;
default:
break;
}
SetVariableAttributes (VariableHeader, VariableType, Storage->Attributes);
SetVariableNameSize (VariableHeader, VariableType, VarNameSize);
SetVariableDataSize (VariableHeader, VariableType, Storage->Size);
//
//Copy the Guid, variable name, and data in sequence.
//
SetVariableGuid (VariableHeader, VariableType, &(Storage->Guid));
SetVariableName (VariableHeader, VariableType, Storage->Name, VarNameSize);
SetVariableData (VariableHeader, VariableType, Storage->Buffer, Storage->Size);
StorageLink = GetNextNode (StorageListHead, StorageLink);
}
return EFI_SUCCESS;
}
}
/**
Check the store variable type
@param VariableStoreHeader The pointer to the header of Variable Store.
@return VariableType VariableType
**/
UINT32
CheckVarStore (
IN VOID *VariableStoreHeader
)
{
if (CompareGuid (
&gEfiVariableGuid,
&((VARIABLE_STORE_HEADER *)VariableStoreHeader)->Signature) == 0) {
return VariableTypeNormal;
} else if (CompareGuid (
&gEfiAuthenticatedVariableGuid,
&((VARIABLE_STORE_HEADER *)VariableStoreHeader)->Signature) == 0) {
return VariableTypeCountBasedAuth;
} else if (CompareGuid (
&gEfiAuthenticatedVariableBasedTimeGuid,
&((VARIABLE_STORE_HEADER *)VariableStoreHeader)->Signature) == 0) {
return VariableTypeTimeBasedAuth;
} else {
return VariableTypeUnknown;
}
}