summaryrefslogtreecommitdiff
path: root/lib/efi_loader/efi_variable.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/efi_loader/efi_variable.c')
-rw-r--r--lib/efi_loader/efi_variable.c168
1 files changed, 142 insertions, 26 deletions
diff --git a/lib/efi_loader/efi_variable.c b/lib/efi_loader/efi_variable.c
index 40f7a0fb10d..0cbed53d1db 100644
--- a/lib/efi_loader/efi_variable.c
+++ b/lib/efi_loader/efi_variable.c
@@ -163,6 +163,7 @@ static efi_status_t efi_variable_authenticate(const u16 *variable,
break;
default:
/* TODO: support private authenticated variables */
+ ret = EFI_UNSUPPORTED;
goto err;
}
@@ -208,35 +209,44 @@ efi_get_variable_int(const u16 *variable_name, const efi_guid_t *vendor,
u32 *attributes, efi_uintn_t *data_size, void *data,
u64 *timep)
{
- return efi_get_variable_mem(variable_name, vendor, attributes, data_size, data, timep);
+ return efi_get_variable_mem(variable_name, vendor, attributes, data_size,
+ data, timep, 0);
}
efi_status_t __efi_runtime
efi_get_next_variable_name_int(efi_uintn_t *variable_name_size,
u16 *variable_name, efi_guid_t *vendor)
{
- return efi_get_next_variable_name_mem(variable_name_size, variable_name, vendor);
+ return efi_get_next_variable_name_mem(variable_name_size, variable_name,
+ vendor, 0);
}
-efi_status_t efi_set_variable_int(const u16 *variable_name,
- const efi_guid_t *vendor,
- u32 attributes, efi_uintn_t data_size,
- const void *data, bool ro_check)
+/**
+ * setvariable_allowed() - checks defined by the UEFI spec for setvariable
+ *
+ * @variable_name: name of the variable
+ * @vendor: vendor GUID
+ * @attributes: attributes of the variable
+ * @data_size: size of the buffer with the variable value
+ * @data: buffer with the variable value
+ * Return: status code
+ */
+static efi_status_t __efi_runtime
+setvariable_allowed(const u16 *variable_name, const efi_guid_t *vendor,
+ u32 attributes, efi_uintn_t data_size, const void *data)
{
- struct efi_var_entry *var;
- efi_uintn_t ret;
- bool append, delete;
- u64 time = 0;
- enum efi_auth_var_type var_type;
-
if (!variable_name || !*variable_name || !vendor)
return EFI_INVALID_PARAMETER;
if (data_size && !data)
return EFI_INVALID_PARAMETER;
- /* EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS is deprecated */
- if (attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS)
+ /*
+ * EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS is deprecated.
+ * We don't support EFI_VARIABLE_ENHANCED_AUTHENTICATED_ACCESS.
+ */
+ if (attributes & (EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS | \
+ EFI_VARIABLE_ENHANCED_AUTHENTICATED_ACCESS))
return EFI_UNSUPPORTED;
/* Make sure if runtime bit is set, boot service bit is set also */
@@ -256,10 +266,29 @@ efi_status_t efi_set_variable_int(const u16 *variable_name,
!(attributes & EFI_VARIABLE_BOOTSERVICE_ACCESS)))
return EFI_INVALID_PARAMETER;
+ return EFI_SUCCESS;
+}
+
+efi_status_t efi_set_variable_int(const u16 *variable_name,
+ const efi_guid_t *vendor,
+ u32 attributes, efi_uintn_t data_size,
+ const void *data, bool ro_check)
+{
+ struct efi_var_entry *var;
+ efi_uintn_t ret;
+ bool append, delete;
+ u64 time = 0;
+ enum efi_auth_var_type var_type;
+
+ ret = setvariable_allowed(variable_name, vendor, attributes, data_size,
+ data);
+ if (ret != EFI_SUCCESS)
+ return ret;
+
/* check if a variable exists */
var = efi_var_mem_find(vendor, variable_name, NULL);
append = !!(attributes & EFI_VARIABLE_APPEND_WRITE);
- attributes &= ~(u32)EFI_VARIABLE_APPEND_WRITE;
+ attributes &= ~EFI_VARIABLE_APPEND_WRITE;
delete = !append && (!data_size || !attributes);
/* check attributes */
@@ -276,17 +305,27 @@ efi_status_t efi_set_variable_int(const u16 *variable_name,
/* attributes won't be changed */
if (!delete &&
((ro_check && var->attr != attributes) ||
- (!ro_check && ((var->attr & ~(u32)EFI_VARIABLE_READ_ONLY)
- != (attributes & ~(u32)EFI_VARIABLE_READ_ONLY))))) {
+ (!ro_check && ((var->attr & ~EFI_VARIABLE_READ_ONLY)
+ != (attributes & ~EFI_VARIABLE_READ_ONLY))))) {
return EFI_INVALID_PARAMETER;
}
time = var->time;
} else {
- if (delete || append)
- /*
- * Trying to delete or to update a non-existent
- * variable.
- */
+ /*
+ * UEFI specification does not clearly describe the expected
+ * behavior of append write with data size 0, we follow
+ * the EDK II reference implementation.
+ */
+ if (append && !data_size)
+ return EFI_SUCCESS;
+
+ /*
+ * EFI_VARIABLE_APPEND_WRITE to non-existent variable is accepted
+ * and new variable is created in EDK II reference implementation.
+ * We follow it and only check the deletion here.
+ */
+ if (delete)
+ /* Trying to delete a non-existent variable. */
return EFI_NOT_FOUND;
}
@@ -329,7 +368,11 @@ efi_status_t efi_set_variable_int(const u16 *variable_name,
/* EFI_NOT_FOUND has been handled before */
attributes = var->attr;
ret = EFI_SUCCESS;
- } else if (append) {
+ } else if (append && var) {
+ /*
+ * data is appended if EFI_VARIABLE_APPEND_WRITE is set and
+ * variable exists.
+ */
u16 *old_data = var->name;
for (; *old_data; ++old_data)
@@ -342,11 +385,12 @@ efi_status_t efi_set_variable_int(const u16 *variable_name,
ret = efi_var_mem_ins(variable_name, vendor, attributes,
data_size, data, 0, NULL, time);
}
- efi_var_mem_del(var);
if (ret != EFI_SUCCESS)
return ret;
+ efi_var_mem_del(var);
+
if (var_type == EFI_AUTH_VAR_PK)
ret = efi_init_secure_state();
else
@@ -384,7 +428,7 @@ efi_status_t efi_query_variable_info_int(u32 attributes,
EFI_VARIABLE_RUNTIME_ACCESS)
return EFI_INVALID_PARAMETER;
- if (attributes & ~(u32)EFI_VARIABLE_MASK)
+ if (attributes & ~EFI_VARIABLE_MASK)
return EFI_INVALID_PARAMETER;
*maximum_variable_storage_size = EFI_VAR_BUF_SIZE -
@@ -434,7 +478,79 @@ efi_set_variable_runtime(u16 *variable_name, const efi_guid_t *vendor,
u32 attributes, efi_uintn_t data_size,
const void *data)
{
- return EFI_UNSUPPORTED;
+ struct efi_var_entry *var;
+ efi_uintn_t ret;
+ bool append, delete;
+ u64 time = 0;
+
+ if (!IS_ENABLED(CONFIG_EFI_RT_VOLATILE_STORE))
+ return EFI_UNSUPPORTED;
+
+ /*
+ * Authenticated variables are not supported. The EFI spec
+ * in ยง32.3.6 requires keys to be stored in non-volatile storage which
+ * is tamper and delete resistant.
+ * The rest of the checks are in setvariable_allowed()
+ */
+ if (attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS)
+ return EFI_INVALID_PARAMETER;
+
+ ret = setvariable_allowed(variable_name, vendor, attributes, data_size,
+ data);
+ if (ret != EFI_SUCCESS)
+ return ret;
+
+ /* check if a variable exists */
+ var = efi_var_mem_find(vendor, variable_name, NULL);
+ append = !!(attributes & EFI_VARIABLE_APPEND_WRITE);
+ attributes &= ~EFI_VARIABLE_APPEND_WRITE;
+ delete = !append && (!data_size || !attributes);
+
+ /* BS only variables are hidden deny writing them */
+ if (!delete && !(attributes & EFI_VARIABLE_RUNTIME_ACCESS))
+ return EFI_INVALID_PARAMETER;
+
+ if (var) {
+ if (var->attr & EFI_VARIABLE_READ_ONLY ||
+ !(var->attr & EFI_VARIABLE_NON_VOLATILE))
+ return EFI_WRITE_PROTECTED;
+
+ /* attributes won't be changed */
+ if (!delete && (((var->attr & ~EFI_VARIABLE_READ_ONLY) !=
+ (attributes & ~EFI_VARIABLE_READ_ONLY))))
+ return EFI_INVALID_PARAMETER;
+ time = var->time;
+ } else {
+ if (!(attributes & EFI_VARIABLE_NON_VOLATILE))
+ return EFI_INVALID_PARAMETER;
+ if (append && !data_size)
+ return EFI_SUCCESS;
+ if (delete)
+ return EFI_NOT_FOUND;
+ }
+
+ if (delete) {
+ /* EFI_NOT_FOUND has been handled before */
+ attributes = var->attr;
+ ret = EFI_SUCCESS;
+ } else if (append && var) {
+ u16 *old_data = (void *)((uintptr_t)var->name +
+ sizeof(u16) * (u16_strlen(var->name) + 1));
+
+ ret = efi_var_mem_ins(variable_name, vendor, attributes,
+ var->length, old_data, data_size, data,
+ time);
+ } else {
+ ret = efi_var_mem_ins(variable_name, vendor, attributes,
+ data_size, data, 0, NULL, time);
+ }
+
+ if (ret != EFI_SUCCESS)
+ return ret;
+ /* We are always inserting new variables, get rid of the old copy */
+ efi_var_mem_del(var);
+
+ return EFI_SUCCESS;
}
/**