blob: 8df82e161a90772db6ff73e900c5920c81b5e57d [file] [log] [blame]
/** @file
Serialize Variables Library implementation
Copyright (c) 2004 - 2011, Intel Corporation. All rights reserved.<BR>
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",
__func__,
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",
__func__,
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;
}