/** @file Serialize Variables Library implementation Copyright (c) 2004 - 2011, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "SerializeVariablesLib.h" /** Serialization format: The SerializeVariablesLib interface does not specify a format for the serialization of the variable data. This library uses a packed array of a non-uniformly sized data structure elements. Each variable is stored (packed) as: UINT32 VendorNameSize; // Name size in bytes CHAR16 VendorName[?]; // The variable unicode name including the // null terminating character. EFI_GUID VendorGuid; // The variable GUID UINT32 DataSize; // The size of variable data in bytes UINT8 Data[?]; // The variable data **/ /** Unpacks the next variable from the buffer @param[in] Buffer - Buffer pointing to the next variable instance On subsequent calls, the pointer should be incremented by the returned SizeUsed value. @param[in] MaxSize - Max allowable size for the variable data On subsequent calls, this should be decremented by the returned SizeUsed value. @param[out] Name - Variable name string (address in Buffer) @param[out] NameSize - Size of Name in bytes @param[out] Guid - GUID of variable (address in Buffer) @param[out] Attributes - Attributes of variable @param[out] Data - Buffer containing Data for variable (address in Buffer) @param[out] DataSize - Size of Data in bytes @param[out] SizeUsed - Total size used for this variable instance in Buffer @return EFI_STATUS based on the success or failure of the operation **/ STATIC EFI_STATUS UnpackVariableFromBuffer ( IN VOID *Buffer, IN UINTN MaxSize, OUT CHAR16 **Name, OUT UINT32 *NameSize, OUT EFI_GUID **Guid, OUT UINT32 *Attributes, OUT UINT32 *DataSize, OUT VOID **Data, OUT UINTN *SizeUsed ) { UINT8 *BytePtr; UINTN Offset; BytePtr = (UINT8*)Buffer; Offset = 0; *NameSize = *(UINT32*) (BytePtr + Offset); Offset = Offset + sizeof (UINT32); if (Offset > MaxSize) { return EFI_INVALID_PARAMETER; } *Name = (CHAR16*) (BytePtr + Offset); Offset = Offset + *(UINT32*)BytePtr; if (Offset > MaxSize) { return EFI_INVALID_PARAMETER; } *Guid = (EFI_GUID*) (BytePtr + Offset); Offset = Offset + sizeof (EFI_GUID); if (Offset > MaxSize) { return EFI_INVALID_PARAMETER; } *Attributes = *(UINT32*) (BytePtr + Offset); Offset = Offset + sizeof (UINT32); if (Offset > MaxSize) { return EFI_INVALID_PARAMETER; } *DataSize = *(UINT32*) (BytePtr + Offset); Offset = Offset + sizeof (UINT32); if (Offset > MaxSize) { return EFI_INVALID_PARAMETER; } *Data = (VOID*) (BytePtr + Offset); Offset = Offset + *DataSize; if (Offset > MaxSize) { return EFI_INVALID_PARAMETER; } *SizeUsed = Offset; return EFI_SUCCESS; } /** Iterates through the variables in the buffer, and calls a callback function for each variable found. @param[in] CallbackFunction - Function called for each variable instance @param[in] Context - Passed to each call of CallbackFunction @param[in] Buffer - Buffer containing serialized variables @param[in] MaxSize - Size of Buffer in bytes @return EFI_STATUS based on the success or failure of the operation **/ STATIC EFI_STATUS IterateVariablesInBuffer ( IN VARIABLE_SERIALIZATION_ITERATION_CALLBACK CallbackFunction, IN VOID *CallbackContext, IN VOID *Buffer, IN UINTN MaxSize ) { RETURN_STATUS Status; UINTN TotalSizeUsed; UINTN SizeUsed; CHAR16 *Name; UINT32 NameSize; CHAR16 *AlignedName; UINT32 AlignedNameMaxSize; EFI_GUID *Guid; UINT32 Attributes; UINT32 DataSize; VOID *Data; SizeUsed = 0; AlignedName = NULL; AlignedNameMaxSize = 0; Name = NULL; Guid = NULL; Attributes = 0; DataSize = 0; Data = NULL; for ( Status = EFI_SUCCESS, TotalSizeUsed = 0; !EFI_ERROR (Status) && (TotalSizeUsed < MaxSize); ) { Status = UnpackVariableFromBuffer ( (VOID*) ((UINT8*) Buffer + TotalSizeUsed), (MaxSize - TotalSizeUsed), &Name, &NameSize, &Guid, &Attributes, &DataSize, &Data, &SizeUsed ); if (EFI_ERROR (Status)) { return Status; } // // We copy the name to a separately allocated buffer, // to be sure it is 16-bit aligned. // if (NameSize > AlignedNameMaxSize) { if (AlignedName != NULL) { FreePool (AlignedName); } AlignedName = AllocatePool (NameSize); } if (AlignedName == NULL) { return EFI_OUT_OF_RESOURCES; } CopyMem (AlignedName, Name, NameSize); TotalSizeUsed = TotalSizeUsed + SizeUsed; // // Run the callback function // Status = (*CallbackFunction) ( CallbackContext, AlignedName, Guid, Attributes, DataSize, Data ); } if (AlignedName != NULL) { FreePool (AlignedName); } // // Make sure the entire buffer was used, or else return an error // if (TotalSizeUsed != MaxSize) { DEBUG (( DEBUG_ERROR, "Deserialize variables error: TotalSizeUsed(%Lu) != MaxSize(%Lu)\n", (UINT64)TotalSizeUsed, (UINT64)MaxSize )); return EFI_INVALID_PARAMETER; } return EFI_SUCCESS; } STATIC RETURN_STATUS EFIAPI IterateVariablesCallbackNop ( IN VOID *Context, IN CHAR16 *VariableName, IN EFI_GUID *VendorGuid, IN UINT32 Attributes, IN UINTN DataSize, IN VOID *Data ) { return RETURN_SUCCESS; } STATIC RETURN_STATUS EFIAPI IterateVariablesCallbackSetInInstance ( IN VOID *Context, IN CHAR16 *VariableName, IN EFI_GUID *VendorGuid, IN UINT32 Attributes, IN UINTN DataSize, IN VOID *Data ) { EFI_HANDLE Instance; Instance = (EFI_HANDLE) Context; return SerializeVariablesAddVariable ( Instance, VariableName, VendorGuid, Attributes, DataSize, Data ); } STATIC RETURN_STATUS EFIAPI IterateVariablesCallbackSetSystemVariable ( IN VOID *Context, IN CHAR16 *VariableName, IN EFI_GUID *VendorGuid, IN UINT32 Attributes, IN UINTN DataSize, IN VOID *Data ) { EFI_STATUS Status; STATIC CONST UINT32 AuthMask = EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS; Status = gRT->SetVariable ( VariableName, VendorGuid, Attributes, DataSize, Data ); if (Status == EFI_SECURITY_VIOLATION && (Attributes & AuthMask) != 0) { DEBUG ((DEBUG_WARN, "%a: setting authenticated variable \"%s\" " "failed with EFI_SECURITY_VIOLATION, ignoring\n", __FUNCTION__, VariableName)); Status = EFI_SUCCESS; } else if (Status == EFI_WRITE_PROTECTED) { DEBUG ((DEBUG_WARN, "%a: setting ReadOnly variable \"%s\" " "failed with EFI_WRITE_PROTECTED, ignoring\n", __FUNCTION__, VariableName)); Status = EFI_SUCCESS; } return Status; } STATIC RETURN_STATUS EnsureExtraBufferSpace ( IN SV_INSTANCE *Instance, IN UINTN Size ) { VOID *NewBuffer; UINTN NewSize; NewSize = Instance->DataSize + Size; if (NewSize <= Instance->BufferSize) { return RETURN_SUCCESS; } // // Double the required size to lessen the need to re-allocate in the future // NewSize = 2 * NewSize; NewBuffer = AllocatePool (NewSize); if (NewBuffer == NULL) { return RETURN_OUT_OF_RESOURCES; } if (Instance->BufferPtr != NULL) { CopyMem (NewBuffer, Instance->BufferPtr, Instance->DataSize); FreePool (Instance->BufferPtr); } Instance->BufferPtr = NewBuffer; Instance->BufferSize = NewSize; return RETURN_SUCCESS; } STATIC VOID AppendToBuffer ( IN SV_INSTANCE *Instance, IN VOID *Data, IN UINTN Size ) { UINTN NewSize; ASSERT (Instance != NULL); ASSERT (Data != NULL); NewSize = Instance->DataSize + Size; ASSERT ((Instance->DataSize + Size) <= Instance->BufferSize); CopyMem ( (VOID*) (((UINT8*) (Instance->BufferPtr)) + Instance->DataSize), Data, Size ); Instance->DataSize = NewSize; } /** Creates a new variable serialization instance @param[out] Handle - Handle for a variable serialization instance @retval RETURN_SUCCESS - The variable serialization instance was successfully created. @retval RETURN_OUT_OF_RESOURCES - There we not enough resources to create the variable serialization instance. **/ RETURN_STATUS EFIAPI SerializeVariablesNewInstance ( OUT EFI_HANDLE *Handle ) { SV_INSTANCE *New; New = AllocateZeroPool (sizeof (*New)); if (New == NULL) { return RETURN_OUT_OF_RESOURCES; } New->Signature = SV_SIGNATURE; *Handle = (EFI_HANDLE) New; return RETURN_SUCCESS; } /** Free memory associated with a variable serialization instance @param[in] Handle - Handle for a variable serialization instance @retval RETURN_SUCCESS - The variable serialization instance was successfully freed. @retval RETURN_INVALID_PARAMETER - Handle was not a valid variable serialization instance. **/ RETURN_STATUS EFIAPI SerializeVariablesFreeInstance ( IN EFI_HANDLE Handle ) { SV_INSTANCE *Instance; Instance = SV_FROM_HANDLE (Handle); if (Instance->Signature != SV_SIGNATURE) { return RETURN_INVALID_PARAMETER; } Instance->Signature = 0; if (Instance->BufferPtr != NULL) { FreePool (Instance->BufferPtr); } FreePool (Instance); return RETURN_SUCCESS; } /** Creates a new variable serialization instance using the given binary representation of the variables to fill the new instance @param[out] Handle - Handle for a variable serialization instance @param[in] Buffer - A buffer with the serialized representation of the variables. Must be the same format as produced by SerializeVariablesToBuffer. @param[in] Size - This is the size of the binary representation of the variables. @retval RETURN_SUCCESS - The binary representation was successfully imported into a new variable serialization instance @retval RETURN_OUT_OF_RESOURCES - There we not enough resources to create the new variable serialization instance **/ RETURN_STATUS EFIAPI SerializeVariablesNewInstanceFromBuffer ( OUT EFI_HANDLE *Handle, IN VOID *Buffer, IN UINTN Size ) { RETURN_STATUS Status; Status = SerializeVariablesNewInstance (Handle); if (RETURN_ERROR (Status)) { return Status; } Status = IterateVariablesInBuffer ( IterateVariablesCallbackNop, NULL, Buffer, Size ); if (RETURN_ERROR (Status)) { SerializeVariablesFreeInstance (*Handle); return Status; } Status = IterateVariablesInBuffer ( IterateVariablesCallbackSetInInstance, (VOID*) *Handle, Buffer, Size ); if (RETURN_ERROR (Status)) { SerializeVariablesFreeInstance (*Handle); return Status; } return Status; } /** Iterates all variables found with RuntimeServices GetNextVariableName @param[in] CallbackFunction - Function called for each variable instance @param[in] Context - Passed to each call of CallbackFunction @retval RETURN_SUCCESS - All variables were iterated without the CallbackFunction returning an error @retval RETURN_OUT_OF_RESOURCES - There we not enough resources to iterate through the variables @return Any of RETURN_ERROR indicates an error reading the variable or an error was returned from CallbackFunction **/ RETURN_STATUS EFIAPI SerializeVariablesIterateSystemVariables ( IN VARIABLE_SERIALIZATION_ITERATION_CALLBACK CallbackFunction, IN VOID *Context ) { RETURN_STATUS Status; UINTN VariableNameBufferSize; UINTN VariableNameSize; CHAR16 *VariableName; EFI_GUID VendorGuid; UINTN VariableDataBufferSize; UINTN VariableDataSize; VOID *VariableData; UINT32 VariableAttributes; VOID *NewBuffer; // // Initialize the variable name and data buffer variables. // VariableNameBufferSize = sizeof (CHAR16); VariableName = AllocateZeroPool (VariableNameBufferSize); VariableDataBufferSize = 0; VariableData = NULL; for (;;) { // // Get the next variable name and guid // VariableNameSize = VariableNameBufferSize; Status = gRT->GetNextVariableName ( &VariableNameSize, VariableName, &VendorGuid ); if (Status == EFI_BUFFER_TOO_SMALL) { // // The currently allocated VariableName buffer is too small, // so we allocate a larger buffer, and copy the old buffer // to it. // NewBuffer = AllocatePool (VariableNameSize); if (NewBuffer == NULL) { Status = EFI_OUT_OF_RESOURCES; break; } CopyMem (NewBuffer, VariableName, VariableNameBufferSize); if (VariableName != NULL) { FreePool (VariableName); } VariableName = NewBuffer; VariableNameBufferSize = VariableNameSize; // // Try to get the next variable name again with the larger buffer. // Status = gRT->GetNextVariableName ( &VariableNameSize, VariableName, &VendorGuid ); } if (EFI_ERROR (Status)) { if (Status == EFI_NOT_FOUND) { Status = EFI_SUCCESS; } break; } // // Get the variable data and attributes // VariableDataSize = VariableDataBufferSize; Status = gRT->GetVariable ( VariableName, &VendorGuid, &VariableAttributes, &VariableDataSize, VariableData ); if (Status == EFI_BUFFER_TOO_SMALL) { // // The currently allocated VariableData buffer is too small, // so we allocate a larger buffer. // if (VariableDataBufferSize != 0) { FreePool (VariableData); VariableData = NULL; VariableDataBufferSize = 0; } VariableData = AllocatePool (VariableDataSize); if (VariableData == NULL) { Status = EFI_OUT_OF_RESOURCES; break; } VariableDataBufferSize = VariableDataSize; // // Try to read the variable again with the larger buffer. // Status = gRT->GetVariable ( VariableName, &VendorGuid, &VariableAttributes, &VariableDataSize, VariableData ); } if (EFI_ERROR (Status)) { break; } // // Run the callback function // Status = (*CallbackFunction) ( Context, VariableName, &VendorGuid, VariableAttributes, VariableDataSize, VariableData ); if (EFI_ERROR (Status)) { break; } } if (VariableName != NULL) { FreePool (VariableName); } if (VariableData != NULL) { FreePool (VariableData); } return Status; } /** Iterates all variables found in the variable serialization instance @param[in] Handle - Handle for a variable serialization instance @param[in] CallbackFunction - Function called for each variable instance @param[in] Context - Passed to each call of CallbackFunction @retval RETURN_SUCCESS - All variables were iterated without the CallbackFunction returning an error @retval RETURN_OUT_OF_RESOURCES - There we not enough resources to iterate through the variables @return Any of RETURN_ERROR indicates an error reading the variable or an error was returned from CallbackFunction **/ RETURN_STATUS EFIAPI SerializeVariablesIterateInstanceVariables ( IN EFI_HANDLE Handle, IN VARIABLE_SERIALIZATION_ITERATION_CALLBACK CallbackFunction, IN VOID *Context ) { SV_INSTANCE *Instance; Instance = SV_FROM_HANDLE (Handle); if ((Instance->BufferPtr != NULL) && (Instance->DataSize != 0)) { return IterateVariablesInBuffer ( CallbackFunction, Context, Instance->BufferPtr, Instance->DataSize ); } else { return RETURN_SUCCESS; } } /** Sets all variables found in the variable serialization instance @param[in] Handle - Handle for a variable serialization instance @retval RETURN_SUCCESS - All variables were set successfully @retval RETURN_OUT_OF_RESOURCES - There we not enough resources to set all the variables @return Any of RETURN_ERROR indicates an error reading the variables or in attempting to set a variable **/ RETURN_STATUS EFIAPI SerializeVariablesSetSerializedVariables ( IN EFI_HANDLE Handle ) { return SerializeVariablesIterateInstanceVariables ( Handle, IterateVariablesCallbackSetSystemVariable, NULL ); } /** Adds a variable to the variable serialization instance @param[in] Handle - Handle for a variable serialization instance @param[in] VariableName - Refer to RuntimeServices GetVariable @param[in] VendorGuid - Refer to RuntimeServices GetVariable @param[in] Attributes - Refer to RuntimeServices GetVariable @param[in] DataSize - Refer to RuntimeServices GetVariable @param[in] Data - Refer to RuntimeServices GetVariable @retval RETURN_SUCCESS - All variables were set successfully @retval RETURN_OUT_OF_RESOURCES - There we not enough resources to add the variable @retval RETURN_INVALID_PARAMETER - Handle was not a valid variable serialization instance or VariableName, VariableGuid or Data are NULL. **/ RETURN_STATUS EFIAPI SerializeVariablesAddVariable ( IN EFI_HANDLE Handle, IN CHAR16 *VariableName, IN EFI_GUID *VendorGuid, IN UINT32 Attributes, IN UINTN DataSize, IN VOID *Data ) { RETURN_STATUS Status; SV_INSTANCE *Instance; UINT32 SerializedNameSize; UINT32 SerializedDataSize; UINTN SerializedSize; Instance = SV_FROM_HANDLE (Handle); if ((Instance->Signature != SV_SIGNATURE) || (VariableName == NULL) || (VendorGuid == NULL) || (Data == NULL)) { } SerializedNameSize = (UINT32) StrSize (VariableName); SerializedSize = sizeof (SerializedNameSize) + SerializedNameSize + sizeof (*VendorGuid) + sizeof (Attributes) + sizeof (SerializedDataSize) + DataSize; Status = EnsureExtraBufferSpace ( Instance, SerializedSize ); if (RETURN_ERROR (Status)) { return Status; } // // Add name size (UINT32) // AppendToBuffer (Instance, (VOID*) &SerializedNameSize, sizeof (SerializedNameSize)); // // Add variable unicode name string // AppendToBuffer (Instance, (VOID*) VariableName, SerializedNameSize); // // Add variable GUID // AppendToBuffer (Instance, (VOID*) VendorGuid, sizeof (*VendorGuid)); // // Add variable attributes // AppendToBuffer (Instance, (VOID*) &Attributes, sizeof (Attributes)); // // Add variable data size (UINT32) // SerializedDataSize = (UINT32) DataSize; AppendToBuffer (Instance, (VOID*) &SerializedDataSize, sizeof (SerializedDataSize)); // // Add variable data // AppendToBuffer (Instance, Data, DataSize); return RETURN_SUCCESS; } /** Serializes the variables known to this instance into the provided buffer. @param[in] Handle - Handle for a variable serialization instance @param[out] Buffer - A buffer to store the binary representation of the variables. @param[in,out] Size - On input this is the size of the buffer. On output this is the size of the binary representation of the variables. @retval RETURN_SUCCESS - The binary representation was successfully completed and returned in the buffer. @retval RETURN_OUT_OF_RESOURCES - There we not enough resources to save the variables to the buffer. @retval RETURN_INVALID_PARAMETER - Handle was not a valid variable serialization instance or Size or Buffer were NULL. @retval RETURN_BUFFER_TOO_SMALL - The Buffer size as indicated by the Size parameter was too small for the serialized variable data. Size is returned with the required size. **/ RETURN_STATUS EFIAPI SerializeVariablesToBuffer ( IN EFI_HANDLE Handle, OUT VOID *Buffer, IN OUT UINTN *Size ) { SV_INSTANCE *Instance; Instance = SV_FROM_HANDLE (Handle); if (Size == NULL) { return RETURN_INVALID_PARAMETER; } if (*Size < Instance->DataSize) { *Size = Instance->DataSize; return RETURN_BUFFER_TOO_SMALL; } if (Buffer == NULL) { return RETURN_INVALID_PARAMETER; } *Size = Instance->DataSize; CopyMem (Buffer, Instance->BufferPtr, Instance->DataSize); return RETURN_SUCCESS; }