// // 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; } }