aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/x86/lib/tables.c2
-rw-r--r--cmd/eficonfig.c2
-rw-r--r--doc/usage/cmd/env.rst35
-rw-r--r--include/efi_loader.h4
-rw-r--r--include/efi_variable.h16
-rw-r--r--lib/charset.c2
-rw-r--r--lib/efi_loader/Kconfig16
-rw-r--r--lib/efi_loader/efi_helper.c5
-rw-r--r--lib/efi_loader/efi_load_initrd.c82
-rw-r--r--lib/efi_loader/efi_runtime.c42
-rw-r--r--lib/efi_loader/efi_var_common.c8
-rw-r--r--lib/efi_loader/efi_var_mem.c151
-rw-r--r--lib/efi_loader/efi_variable.c122
-rw-r--r--lib/efi_loader/efi_variable_tee.c5
-rw-r--r--lib/efi_selftest/efi_selftest_variables_runtime.c222
15 files changed, 583 insertions, 131 deletions
diff --git a/arch/x86/lib/tables.c b/arch/x86/lib/tables.c
index 12eae17..1095dc9 100644
--- a/arch/x86/lib/tables.c
+++ b/arch/x86/lib/tables.c
@@ -98,6 +98,8 @@ int write_tables(void)
int size = table->size ? : CONFIG_ROM_TABLE_SIZE;
u32 rom_table_end;
+ rom_addr = ALIGN(rom_addr, 16);
+
if (!strcmp("smbios", table->name))
gd->arch.smbios_start = rom_addr;
diff --git a/cmd/eficonfig.c b/cmd/eficonfig.c
index 8234e60..0ba92c6 100644
--- a/cmd/eficonfig.c
+++ b/cmd/eficonfig.c
@@ -1419,7 +1419,7 @@ static efi_status_t eficonfig_edit_boot_option(u16 *varname, struct eficonfig_bo
}
bo->initrd_info.current_path = calloc(1, EFICONFIG_FILE_PATH_BUF_SIZE);
- if (!bo->file_info.current_path) {
+ if (!bo->initrd_info.current_path) {
ret = EFI_OUT_OF_RESOURCES;
goto out;
}
diff --git a/doc/usage/cmd/env.rst b/doc/usage/cmd/env.rst
index a7e2169..040076b 100644
--- a/doc/usage/cmd/env.rst
+++ b/doc/usage/cmd/env.rst
@@ -16,18 +16,18 @@ Synopsis
env default [-f] (-a | var [...])
env delete [-f] var [...]
env edit name
+ env erase
env exists name
env export [-t | -b | -c] [-s size] addr [var ...]
env flags
env grep [-e] [-n | -v | -b] string [...]
env import [-d] [-t [-r] | -b | -c] addr [size] [var ...]
env info [-d] [-p] [-q]
+ env load
env print [-a | name ...]
env print -e [-guid guid] [-n] [name ...]
env run var [...]
env save
- env erase
- env load
env select [target]
env set [-f] name [value]
env set -e [-nv][-bs][-rt][-at][-a][-i addr:size][-v] name [value]
@@ -40,11 +40,12 @@ the UEFI variables.
The next commands are kept as alias and for compatibility:
++ :doc:`askenv <askenv>` = *env ask*
+ *editenv* = *env edit*
+ *grepenv* = *env grep*
-+ *setenv* = *env set*
-+ *askenv* = *env ask*
++ :doc:`printenv <printenv>` = *env print*
+ *run* = *env run*
++ *setenv* = *env set*
Ask
~~~
@@ -103,6 +104,11 @@ The *env edit* command edits an environment variable.
name
name of the variable.
+Erase
+~~~~~
+
+The *env erase* command erases the U-Boot environment.
+
Exists
~~~~~~
@@ -204,6 +210,11 @@ environment information.
quiet output, use only for command result, by example with
'test' command.
+Load
+~~~~
+
+The *env load* command loads the U-Boot environment from persistent storage.
+
Print
~~~~~
@@ -235,16 +246,6 @@ Save
The *env save* command saves the U-Boot environment in persistent storage.
-Erase
-~~~~~
-
-The *env erase* command erases the U-Boot environment.
-
-Load
-~~~~
-
-The *env load* command loads the U-Boot environment from persistent storage.
-
Select
~~~~~~
@@ -350,15 +351,15 @@ edit
exists
CONFIG_CMD_ENV_EXISTS
-flags
- CONFIG_CMD_ENV_FLAGS
-
erase
CONFIG_CMD_ERASEENV
export
CONFIG_CMD_EXPORTENV
+flags
+ CONFIG_CMD_ENV_FLAGS
+
grep
CONFIG_CMD_GREPENV, CONFIG_REGEX for '-e' option
diff --git a/include/efi_loader.h b/include/efi_loader.h
index bb51c02..69442f4 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -159,6 +159,10 @@ static inline void efi_set_bootdev(const char *dev, const char *devnr,
#define EFICONFIG_AUTO_GENERATED_ENTRY_GUID \
EFI_GUID(0x8108ac4e, 0x9f11, 0x4d59, \
0x85, 0x0e, 0xe2, 0x1a, 0x52, 0x2c, 0x59, 0xb2)
+#define U_BOOT_EFI_RT_VAR_FILE_GUID \
+ EFI_GUID(0xb2ac5fc9, 0x92b7, 0x4acd, \
+ 0xae, 0xac, 0x11, 0xe8, 0x18, 0xc3, 0x13, 0x0c)
+
/* Use internal device tree when starting UEFI application */
#define EFI_FDT_USE_INTERNAL NULL
diff --git a/include/efi_variable.h b/include/efi_variable.h
index 42a2b7c..223bb9a 100644
--- a/include/efi_variable.h
+++ b/include/efi_variable.h
@@ -271,13 +271,16 @@ const efi_guid_t *efi_auth_var_get_guid(const u16 *name);
*
* @variable_name_size: size of variable_name buffer in bytes
* @variable_name: name of uefi variable's name in u16
+ * @mask: bitmask with required attributes of variables to be collected.
+ * variables are only collected if all of the required
+ * attributes match. Use 0 to skip matching
* @vendor: vendor's guid
*
* Return: status code
*/
efi_status_t __efi_runtime
efi_get_next_variable_name_mem(efi_uintn_t *variable_name_size, u16 *variable_name,
- efi_guid_t *vendor);
+ efi_guid_t *vendor, u32 mask);
/**
* efi_get_variable_mem() - Runtime common code across efi variable
* implementations for GetVariable() from
@@ -289,12 +292,15 @@ efi_get_next_variable_name_mem(efi_uintn_t *variable_name_size, u16 *variable_na
* @data_size: size of the buffer to which the variable value is copied
* @data: buffer to which the variable value is copied
* @timep: authentication time (seconds since start of epoch)
+ * @mask: bitmask with required attributes of variables to be collected.
+ * variables are only collected if all of the required
+ * attributes match. Use 0 to skip matching
* Return: status code
*/
efi_status_t __efi_runtime
efi_get_variable_mem(const u16 *variable_name, const efi_guid_t *vendor,
u32 *attributes, efi_uintn_t *data_size, void *data,
- u64 *timep);
+ u64 *timep, u32 mask);
/**
* efi_get_variable_runtime() - runtime implementation of GetVariable()
@@ -334,4 +340,10 @@ efi_get_next_variable_name_runtime(efi_uintn_t *variable_name_size,
*/
void efi_var_buf_update(struct efi_var_file *var_buf);
+efi_status_t __efi_runtime efi_var_collect_mem(struct efi_var_file *buf,
+ efi_uintn_t *lenp,
+ u32 check_attr_mask);
+
+u32 efi_var_entry_len(struct efi_var_entry *var);
+
#endif
diff --git a/lib/charset.c b/lib/charset.c
index df4f040..182c92a 100644
--- a/lib/charset.c
+++ b/lib/charset.c
@@ -387,7 +387,7 @@ int u16_strcasecmp(const u16 *s1, const u16 *s2)
* > 0 if the first different u16 in s1 is greater than the
* corresponding u16 in s2
*/
-int u16_strncmp(const u16 *s1, const u16 *s2, size_t n)
+int __efi_runtime u16_strncmp(const u16 *s1, const u16 *s2, size_t n)
{
int ret = 0;
diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
index e13a6f9..cc8371a 100644
--- a/lib/efi_loader/Kconfig
+++ b/lib/efi_loader/Kconfig
@@ -62,6 +62,22 @@ config EFI_VARIABLE_FILE_STORE
Select this option if you want non-volatile UEFI variables to be
stored as file /ubootefi.var on the EFI system partition.
+config EFI_RT_VOLATILE_STORE
+ bool "Allow variable runtime services in volatile storage (e.g RAM)"
+ depends on EFI_VARIABLE_FILE_STORE
+ help
+ When EFI variables are stored on file we don't allow SetVariableRT,
+ since the OS doesn't know how to write that file. At he same time
+ we copy runtime variables in DRAM and support GetVariableRT
+
+ Enable this option to allow SetVariableRT on the RAM backend of
+ the EFI variable storage. The OS will be responsible for syncing
+ the RAM contents to the file, otherwise any changes made during
+ runtime won't persist reboots.
+ Authenticated variables are not supported. Note that this will
+ violate the EFI spec since writing auth variables will return
+ EFI_INVALID_PARAMETER
+
config EFI_MM_COMM_TEE
bool "UEFI variables storage service via the trusted world"
depends on OPTEE
diff --git a/lib/efi_loader/efi_helper.c b/lib/efi_loader/efi_helper.c
index 58761fa..6918fd5 100644
--- a/lib/efi_loader/efi_helper.c
+++ b/lib/efi_loader/efi_helper.c
@@ -549,11 +549,6 @@ efi_status_t do_bootefi_exec(efi_handle_t handle, void *load_options)
out:
free(load_options);
- if (IS_ENABLED(CONFIG_EFI_LOAD_FILE2_INITRD)) {
- if (efi_initrd_deregister() != EFI_SUCCESS)
- log_err("Failed to remove loadfile2 for initrd\n");
- }
-
/* Notify EFI_EVENT_GROUP_RETURN_TO_EFIBOOTMGR event group. */
list_for_each_entry(evt, &efi_events, link) {
if (evt->group &&
diff --git a/lib/efi_loader/efi_load_initrd.c b/lib/efi_loader/efi_load_initrd.c
index 2b467b5..67d1f75 100644
--- a/lib/efi_loader/efi_load_initrd.c
+++ b/lib/efi_loader/efi_load_initrd.c
@@ -184,6 +184,50 @@ out:
}
/**
+ * efi_initrd_deregister() - delete the handle for loading initial RAM disk
+ *
+ * This will delete the handle containing the Linux specific vendor device
+ * path and EFI_LOAD_FILE2_PROTOCOL for loading an initrd
+ *
+ * Return: status code
+ */
+efi_status_t efi_initrd_deregister(void)
+{
+ efi_status_t ret;
+
+ if (!efi_initrd_handle)
+ return EFI_SUCCESS;
+
+ ret = efi_uninstall_multiple_protocol_interfaces(efi_initrd_handle,
+ /* initramfs */
+ &efi_guid_device_path,
+ &dp_lf2_handle,
+ /* LOAD_FILE2 */
+ &efi_guid_load_file2_protocol,
+ &efi_lf2_protocol,
+ NULL);
+ efi_initrd_handle = NULL;
+
+ return ret;
+}
+
+/**
+ * efi_initrd_return_notify() - return to efibootmgr callback
+ *
+ * @event: the event for which this notification function is registered
+ * @context: event context
+ */
+static void EFIAPI efi_initrd_return_notify(struct efi_event *event,
+ void *context)
+{
+ efi_status_t ret;
+
+ EFI_ENTRY("%p, %p", event, context);
+ ret = efi_initrd_deregister();
+ EFI_EXIT(ret);
+}
+
+/**
* efi_initrd_register() - create handle for loading initial RAM disk
*
* This function creates a new handle and installs a Linux specific vendor
@@ -196,6 +240,7 @@ out:
efi_status_t efi_initrd_register(void)
{
efi_status_t ret;
+ struct efi_event *event;
/*
* Allow the user to continue if Boot#### file path is not set for
@@ -214,34 +259,17 @@ efi_status_t efi_initrd_register(void)
&efi_guid_load_file2_protocol,
&efi_lf2_protocol,
NULL);
+ if (ret != EFI_SUCCESS) {
+ log_err("installing EFI_LOAD_FILE2_PROTOCOL failed\n");
+ return ret;
+ }
- return ret;
-}
-
-/**
- * efi_initrd_deregister() - delete the handle for loading initial RAM disk
- *
- * This will delete the handle containing the Linux specific vendor device
- * path and EFI_LOAD_FILE2_PROTOCOL for loading an initrd
- *
- * Return: status code
- */
-efi_status_t efi_initrd_deregister(void)
-{
- efi_status_t ret;
-
- if (!efi_initrd_handle)
- return EFI_SUCCESS;
-
- ret = efi_uninstall_multiple_protocol_interfaces(efi_initrd_handle,
- /* initramfs */
- &efi_guid_device_path,
- &dp_lf2_handle,
- /* LOAD_FILE2 */
- &efi_guid_load_file2_protocol,
- &efi_lf2_protocol,
- NULL);
- efi_initrd_handle = NULL;
+ ret = efi_create_event(EVT_NOTIFY_SIGNAL, TPL_CALLBACK,
+ efi_initrd_return_notify, NULL,
+ &efi_guid_event_group_return_to_efibootmgr,
+ &event);
+ if (ret != EFI_SUCCESS)
+ log_err("Creating event failed\n");
return ret;
}
diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c
index a61c9a7..73831c5 100644
--- a/lib/efi_loader/efi_runtime.c
+++ b/lib/efi_loader/efi_runtime.c
@@ -10,6 +10,7 @@
#include <dm.h>
#include <elf.h>
#include <efi_loader.h>
+#include <efi_variable.h>
#include <log.h>
#include <malloc.h>
#include <rtc.h>
@@ -110,6 +111,7 @@ static __efi_runtime_data efi_uintn_t efi_descriptor_size;
*/
efi_status_t efi_init_runtime_supported(void)
{
+ const efi_guid_t efi_guid_efi_rt_var_file = U_BOOT_EFI_RT_VAR_FILE_GUID;
efi_status_t ret;
struct efi_rt_properties_table *rt_table;
@@ -127,6 +129,46 @@ efi_status_t efi_init_runtime_supported(void)
EFI_RT_SUPPORTED_SET_VIRTUAL_ADDRESS_MAP |
EFI_RT_SUPPORTED_CONVERT_POINTER;
+ if (IS_ENABLED(CONFIG_EFI_RT_VOLATILE_STORE)) {
+ u8 s = 0;
+
+ ret = efi_set_variable_int(u"RTStorageVolatile",
+ &efi_guid_efi_rt_var_file,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS |
+ EFI_VARIABLE_READ_ONLY,
+ sizeof(EFI_VAR_FILE_NAME),
+ EFI_VAR_FILE_NAME, false);
+ if (ret != EFI_SUCCESS) {
+ log_err("Failed to set RTStorageVolatile\n");
+ return ret;
+ }
+ /*
+ * This variable needs to be visible so users can read it,
+ * but the real contents are going to be filled during
+ * GetVariable
+ */
+ ret = efi_set_variable_int(u"VarToFile",
+ &efi_guid_efi_rt_var_file,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS |
+ EFI_VARIABLE_READ_ONLY,
+ sizeof(s),
+ &s, false);
+ if (ret != EFI_SUCCESS) {
+ log_err("Failed to set VarToFile\n");
+ efi_set_variable_int(u"RTStorageVolatile",
+ &efi_guid_efi_rt_var_file,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS |
+ EFI_VARIABLE_READ_ONLY,
+ 0, NULL, false);
+
+ return ret;
+ }
+ rt_table->runtime_services_supported |= EFI_RT_SUPPORTED_SET_VARIABLE;
+ }
+
/*
* This value must be synced with efi_runtime_detach_list
* as well as efi_runtime_services.
diff --git a/lib/efi_loader/efi_var_common.c b/lib/efi_loader/efi_var_common.c
index aa8feff..961139f 100644
--- a/lib/efi_loader/efi_var_common.c
+++ b/lib/efi_loader/efi_var_common.c
@@ -182,7 +182,8 @@ efi_get_variable_runtime(u16 *variable_name, const efi_guid_t *guid,
{
efi_status_t ret;
- ret = efi_get_variable_mem(variable_name, guid, attributes, data_size, data, NULL);
+ ret = efi_get_variable_mem(variable_name, guid, attributes, data_size,
+ data, NULL, EFI_VARIABLE_RUNTIME_ACCESS);
/* Remove EFI_VARIABLE_READ_ONLY flag */
if (attributes)
@@ -195,7 +196,8 @@ efi_status_t __efi_runtime EFIAPI
efi_get_next_variable_name_runtime(efi_uintn_t *variable_name_size,
u16 *variable_name, efi_guid_t *guid)
{
- return efi_get_next_variable_name_mem(variable_name_size, variable_name, guid);
+ return efi_get_next_variable_name_mem(variable_name_size, variable_name,
+ guid, EFI_VARIABLE_RUNTIME_ACCESS);
}
/**
@@ -419,7 +421,7 @@ void *efi_get_var(const u16 *name, const efi_guid_t *vendor, efi_uintn_t *size)
}
/**
- * efi_var_collect() - Copy EFI variables mstching attributes mask
+ * efi_var_collect() - Copy EFI variables matching attributes mask
*
* @bufp: buffer containing variable collection
* @lenp: buffer length
diff --git a/lib/efi_loader/efi_var_mem.c b/lib/efi_loader/efi_var_mem.c
index 6c21cec..940ab66 100644
--- a/lib/efi_loader/efi_var_mem.c
+++ b/lib/efi_loader/efi_var_mem.c
@@ -61,6 +61,23 @@ efi_var_mem_compare(struct efi_var_entry *var, const efi_guid_t *guid,
return match;
}
+/**
+ * efi_var_entry_len() - Get the entry len including headers & name
+ *
+ * @var: pointer to variable start
+ *
+ * Return: 8-byte aligned variable entry length
+ */
+
+u32 __efi_runtime efi_var_entry_len(struct efi_var_entry *var)
+{
+ if (!var)
+ return 0;
+
+ return ALIGN((sizeof(u16) * (u16_strlen(var->name) + 1)) +
+ var->length + sizeof(*var), 8);
+}
+
struct efi_var_entry __efi_runtime
*efi_var_mem_find(const efi_guid_t *guid, const u16 *name,
struct efi_var_entry **next)
@@ -185,53 +202,6 @@ u64 __efi_runtime efi_var_mem_free(void)
}
/**
- * efi_var_mem_bs_del() - delete boot service only variables
- */
-static void efi_var_mem_bs_del(void)
-{
- struct efi_var_entry *var = efi_var_buf->var;
-
- for (;;) {
- struct efi_var_entry *last;
-
- last = (struct efi_var_entry *)
- ((uintptr_t)efi_var_buf + efi_var_buf->length);
- if (var >= last)
- break;
- if (var->attr & EFI_VARIABLE_RUNTIME_ACCESS) {
- u16 *data;
-
- /* skip variable */
- for (data = var->name; *data; ++data)
- ;
- ++data;
- var = (struct efi_var_entry *)
- ALIGN((uintptr_t)data + var->length, 8);
- } else {
- /* delete variable */
- efi_var_mem_del(var);
- }
- }
-}
-
-/**
- * efi_var_mem_notify_exit_boot_services() - ExitBootService callback
- *
- * @event: callback event
- * @context: callback context
- */
-static void EFIAPI
-efi_var_mem_notify_exit_boot_services(struct efi_event *event, void *context)
-{
- EFI_ENTRY("%p, %p", event, context);
-
- /* Delete boot service only variables */
- efi_var_mem_bs_del();
-
- EFI_EXIT(EFI_SUCCESS);
-}
-
-/**
* efi_var_mem_notify_exit_boot_services() - SetVirtualMemoryMap callback
*
* @event: callback event
@@ -261,11 +231,7 @@ efi_status_t efi_var_mem_init(void)
efi_var_buf->magic = EFI_VAR_FILE_MAGIC;
efi_var_buf->length = (uintptr_t)efi_var_buf->var -
(uintptr_t)efi_var_buf;
- /* crc32 for 0 bytes = 0 */
- ret = efi_create_event(EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_CALLBACK,
- efi_var_mem_notify_exit_boot_services, NULL,
- NULL, &event);
if (ret != EFI_SUCCESS)
return ret;
ret = efi_create_event(EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE, TPL_CALLBACK,
@@ -276,10 +242,71 @@ efi_status_t efi_var_mem_init(void)
return ret;
}
+/**
+ * efi_var_collect_mem() - Copy EFI variables matching attributes mask from
+ * efi_var_buf
+ *
+ * @buf: buffer containing variable collection
+ * @lenp: buffer length
+ * @mask: mask of matched attributes
+ *
+ * Return: Status code
+ */
+efi_status_t __efi_runtime
+efi_var_collect_mem(struct efi_var_file *buf, efi_uintn_t *lenp, u32 mask)
+{
+ static struct efi_var_file __efi_runtime_data hdr = {
+ .magic = EFI_VAR_FILE_MAGIC,
+ };
+ struct efi_var_entry *last, *var, *var_to;
+
+ hdr.length = sizeof(struct efi_var_file);
+
+ var = efi_var_buf->var;
+ last = (struct efi_var_entry *)
+ ((uintptr_t)efi_var_buf + efi_var_buf->length);
+ if (buf)
+ var_to = buf->var;
+
+ while (var < last) {
+ u32 len = efi_var_entry_len(var);
+
+ if ((var->attr & mask) != mask) {
+ var = (void *)((uintptr_t)var + len);
+ continue;
+ }
+
+ hdr.length += len;
+
+ if (buf && hdr.length <= *lenp) {
+ efi_memcpy_runtime(var_to, var, len);
+ var_to = (void *)var_to + len;
+ }
+ var = (void *)var + len;
+ }
+
+ if (!buf && hdr.length <= *lenp) {
+ *lenp = hdr.length;
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (!buf || hdr.length > *lenp) {
+ *lenp = hdr.length;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+ hdr.crc32 = crc32(0, (u8 *)buf->var,
+ hdr.length - sizeof(struct efi_var_file));
+
+ efi_memcpy_runtime(buf, &hdr, sizeof(hdr));
+ *lenp = hdr.length;
+
+ return EFI_SUCCESS;
+}
+
efi_status_t __efi_runtime
efi_get_variable_mem(const u16 *variable_name, const efi_guid_t *vendor,
u32 *attributes, efi_uintn_t *data_size, void *data,
- u64 *timep)
+ u64 *timep, u32 mask)
{
efi_uintn_t old_size;
struct efi_var_entry *var;
@@ -291,11 +318,22 @@ efi_get_variable_mem(const u16 *variable_name, const efi_guid_t *vendor,
if (!var)
return EFI_NOT_FOUND;
+ /*
+ * This function is used at runtime to dump EFI variables.
+ * The memory backend we keep around has BS-only variables as
+ * well. At runtime we filter them here
+ */
+ if (mask && !((var->attr & mask) == mask))
+ return EFI_NOT_FOUND;
+
if (attributes)
*attributes = var->attr;
if (timep)
*timep = var->time;
+ if (!u16_strcmp(variable_name, u"VarToFile"))
+ return efi_var_collect_mem(data, data_size, EFI_VARIABLE_NON_VOLATILE);
+
old_size = *data_size;
*data_size = var->length;
if (old_size < var->length)
@@ -315,7 +353,8 @@ efi_get_variable_mem(const u16 *variable_name, const efi_guid_t *vendor,
efi_status_t __efi_runtime
efi_get_next_variable_name_mem(efi_uintn_t *variable_name_size,
- u16 *variable_name, efi_guid_t *vendor)
+ u16 *variable_name, efi_guid_t *vendor,
+ u32 mask)
{
struct efi_var_entry *var;
efi_uintn_t len, old_size;
@@ -324,6 +363,7 @@ efi_get_next_variable_name_mem(efi_uintn_t *variable_name_size,
if (!variable_name_size || !variable_name || !vendor)
return EFI_INVALID_PARAMETER;
+skip:
len = *variable_name_size >> 1;
if (u16_strnlen(variable_name, len) == len)
return EFI_INVALID_PARAMETER;
@@ -347,6 +387,11 @@ efi_get_next_variable_name_mem(efi_uintn_t *variable_name_size,
efi_memcpy_runtime(variable_name, var->name, *variable_name_size);
efi_memcpy_runtime(vendor, &var->guid, sizeof(efi_guid_t));
+ if (mask && !((var->attr & mask) == mask)) {
+ *variable_name_size = old_size;
+ goto skip;
+ }
+
return EFI_SUCCESS;
}
diff --git a/lib/efi_loader/efi_variable.c b/lib/efi_loader/efi_variable.c
index e6c1219..0cbed53 100644
--- a/lib/efi_loader/efi_variable.c
+++ b/lib/efi_loader/efi_variable.c
@@ -209,27 +209,32 @@ 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;
@@ -261,6 +266,25 @@ 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);
@@ -454,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;
}
/**
diff --git a/lib/efi_loader/efi_variable_tee.c b/lib/efi_loader/efi_variable_tee.c
index dde135f..4f1aa29 100644
--- a/lib/efi_loader/efi_variable_tee.c
+++ b/lib/efi_loader/efi_variable_tee.c
@@ -959,11 +959,6 @@ void efi_variables_boot_exit_notify(void)
log_err("Unable to notify the MM partition for ExitBootServices\n");
free(comm_buf);
- /*
- * Populate the list for runtime variables.
- * asking EFI_VARIABLE_RUNTIME_ACCESS is redundant, since
- * efi_var_mem_notify_exit_boot_services will clean those, but that's fine
- */
ret = efi_var_collect(&var_buf, &len, EFI_VARIABLE_RUNTIME_ACCESS);
if (ret != EFI_SUCCESS)
log_err("Can't populate EFI variables. No runtime variables will be available\n");
diff --git a/lib/efi_selftest/efi_selftest_variables_runtime.c b/lib/efi_selftest/efi_selftest_variables_runtime.c
index 4700d94..afa91be 100644
--- a/lib/efi_selftest/efi_selftest_variables_runtime.c
+++ b/lib/efi_selftest/efi_selftest_variables_runtime.c
@@ -10,6 +10,8 @@
*/
#include <efi_selftest.h>
+#include <efi_variable.h>
+#include <u-boot/crc.h>
#define EFI_ST_MAX_DATA_SIZE 16
#define EFI_ST_MAX_VARNAME_SIZE 40
@@ -17,6 +19,8 @@
static struct efi_boot_services *boottime;
static struct efi_runtime_services *runtime;
static const efi_guid_t guid_vendor0 = EFI_GLOBAL_VARIABLE_GUID;
+static const efi_guid_t __efi_runtime_data efi_rt_var_guid =
+ U_BOOT_EFI_RT_VAR_FILE_GUID;
/*
* Setup unit test.
@@ -41,15 +45,18 @@ static int setup(const efi_handle_t img_handle,
static int execute(void)
{
efi_status_t ret;
- efi_uintn_t len;
+ efi_uintn_t len, avail, append_len = 17;
u32 attr;
u8 v[16] = {0x5d, 0xd1, 0x5e, 0x51, 0x5a, 0x05, 0xc7, 0x0c,
0x35, 0x4a, 0xae, 0x87, 0xa5, 0xdf, 0x0f, 0x65,};
+ u8 v2[CONFIG_EFI_VAR_BUF_SIZE];
u8 data[EFI_ST_MAX_DATA_SIZE];
+ u8 data2[CONFIG_EFI_VAR_BUF_SIZE];
u16 varname[EFI_ST_MAX_VARNAME_SIZE];
efi_guid_t guid;
u64 max_storage, rem_storage, max_size;
+ memset(v2, 0x1, sizeof(v2));
ret = runtime->query_variable_info(EFI_VARIABLE_BOOTSERVICE_ACCESS,
&max_storage, &rem_storage,
&max_size);
@@ -62,9 +69,216 @@ static int execute(void)
EFI_VARIABLE_BOOTSERVICE_ACCESS |
EFI_VARIABLE_RUNTIME_ACCESS,
3, v + 4);
- if (ret != EFI_UNSUPPORTED) {
- efi_st_error("SetVariable failed\n");
- return EFI_ST_FAILURE;
+ if (IS_ENABLED(CONFIG_EFI_RT_VOLATILE_STORE)) {
+ efi_uintn_t prev_len, delta;
+ struct efi_var_entry *var;
+ struct efi_var_file *hdr;
+
+ /* At runtime only non-volatile variables may be set. */
+ if (ret != EFI_INVALID_PARAMETER) {
+ efi_st_error("SetVariable failed\n");
+ return EFI_ST_FAILURE;
+ }
+
+ /* runtime atttribute must be set */
+ ret = runtime->set_variable(u"efi_st_var0", &guid_vendor0,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_NON_VOLATILE,
+ 3, v + 4);
+ if (ret != EFI_INVALID_PARAMETER) {
+ efi_st_error("SetVariable failed\n");
+ return EFI_ST_FAILURE;
+ }
+
+ len = sizeof(data);
+ ret = runtime->get_variable(u"RTStorageVolatile",
+ &efi_rt_var_guid,
+ &attr, &len, data);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("GetVariable failed\n");
+ return EFI_ST_FAILURE;
+ }
+
+ if (len != sizeof(EFI_VAR_FILE_NAME) ||
+ memcmp(data, EFI_VAR_FILE_NAME, sizeof(EFI_VAR_FILE_NAME))) {
+ data[len - 1] = 0;
+ efi_st_error("RTStorageVolatile = %s\n", data);
+ return EFI_ST_FAILURE;
+ }
+
+ len = sizeof(data2);
+ ret = runtime->get_variable(u"VarToFile", &efi_rt_var_guid,
+ &attr, &len, data2);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("GetVariable failed\n");
+ return EFI_ST_FAILURE;
+ }
+ /*
+ * VarToFile size must change once a variable is inserted
+ * Store it now, we'll use it later
+ */
+ prev_len = len;
+ ret = runtime->set_variable(u"efi_st_var0", &guid_vendor0,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS |
+ EFI_VARIABLE_NON_VOLATILE,
+ sizeof(v2),
+ v2);
+ /*
+ * This will try to update VarToFile as well and must fail,
+ * without changing or deleting VarToFile
+ */
+ if (ret != EFI_OUT_OF_RESOURCES) {
+ efi_st_error("SetVariable failed\n");
+ return EFI_ST_FAILURE;
+ }
+ len = sizeof(data2);
+ ret = runtime->get_variable(u"VarToFile", &efi_rt_var_guid,
+ &attr, &len, data2);
+ if (ret != EFI_SUCCESS || prev_len != len) {
+ efi_st_error("Get/SetVariable failed\n");
+ return EFI_ST_FAILURE;
+ }
+
+ /* Add an 8byte aligned variable */
+ ret = runtime->set_variable(u"efi_st_var0", &guid_vendor0,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS |
+ EFI_VARIABLE_NON_VOLATILE,
+ sizeof(v), v);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("SetVariable failed\n");
+ return EFI_ST_FAILURE;
+ }
+
+ /* Delete it by setting the attrs to 0 */
+ ret = runtime->set_variable(u"efi_st_var0", &guid_vendor0,
+ 0, sizeof(v), v);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("SetVariable failed\n");
+ return EFI_ST_FAILURE;
+ }
+
+ /* Add it back */
+ ret = runtime->set_variable(u"efi_st_var0", &guid_vendor0,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS |
+ EFI_VARIABLE_NON_VOLATILE,
+ sizeof(v), v);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("SetVariable failed\n");
+ return EFI_ST_FAILURE;
+ }
+
+ /* Delete it again by setting the size to 0 */
+ ret = runtime->set_variable(u"efi_st_var0", &guid_vendor0,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS |
+ EFI_VARIABLE_NON_VOLATILE,
+ 0, NULL);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("SetVariable failed\n");
+ return EFI_ST_FAILURE;
+ }
+
+ /* Delete it again and make sure it's not there */
+ ret = runtime->set_variable(u"efi_st_var0", &guid_vendor0,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS |
+ EFI_VARIABLE_NON_VOLATILE,
+ 0, NULL);
+ if (ret != EFI_NOT_FOUND) {
+ efi_st_error("SetVariable failed\n");
+ return EFI_ST_FAILURE;
+ }
+
+ /*
+ * Add a non-aligned variable
+ * VarToFile updates must include efi_st_var0
+ */
+ ret = runtime->set_variable(u"efi_st_var0", &guid_vendor0,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS |
+ EFI_VARIABLE_NON_VOLATILE,
+ 9, v + 4);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("SetVariable failed\n");
+ return EFI_ST_FAILURE;
+ }
+ var = efi_var_mem_find(&guid_vendor0, u"efi_st_var0", NULL);
+ if (!var) {
+ efi_st_error("GetVariable failed\n");
+ return EFI_ST_FAILURE;
+ }
+ delta = efi_var_entry_len(var);
+ len = sizeof(data2);
+ ret = runtime->get_variable(u"VarToFile", &efi_rt_var_guid,
+ &attr, &len, data2);
+ if (ret != EFI_SUCCESS || prev_len + delta != len) {
+ efi_st_error("Get/SetVariable failed\n");
+ return EFI_ST_FAILURE;
+ }
+
+ /*
+ * Append on an existing variable must update VarToFile
+ * Our variable entries are 8-byte aligned.
+ * Adding a single byte will fit on the existing space
+ */
+ prev_len = len;
+ avail = efi_var_entry_len(var) -
+ (sizeof(u16) * (u16_strlen(var->name) + 1) + sizeof(*var)) -
+ var->length;
+ if (avail >= append_len)
+ delta = 0;
+ else
+ delta = ALIGN(append_len - avail, 8);
+ ret = runtime->set_variable(u"efi_st_var0", &guid_vendor0,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS |
+ EFI_VARIABLE_APPEND_WRITE |
+ EFI_VARIABLE_NON_VOLATILE,
+ append_len, v2);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("SetVariable failed\n");
+ return EFI_ST_FAILURE;
+ }
+ len = sizeof(data2);
+ ret = runtime->get_variable(u"VarToFile", &efi_rt_var_guid,
+ &attr, &len, data2);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("GetVariable failed\n");
+ return EFI_ST_FAILURE;
+ }
+ if (prev_len + delta != len) {
+ efi_st_error("Unexpected VarToFile size");
+ return EFI_ST_FAILURE;
+ }
+
+ /* Make sure that variable contains a valid file */
+ hdr = (struct efi_var_file *)data2;
+ if (hdr->magic != EFI_VAR_FILE_MAGIC ||
+ len != hdr->length ||
+ hdr->crc32 != crc32(0, (u8 *)((uintptr_t)data2 + sizeof(struct efi_var_file)),
+ len - sizeof(struct efi_var_file))) {
+ efi_st_error("VarToFile invalid header\n");
+ return EFI_ST_FAILURE;
+ }
+
+ /* Variables that are BS, RT and volatile are RO after EBS */
+ ret = runtime->set_variable(u"VarToFile", &efi_rt_var_guid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS |
+ EFI_VARIABLE_NON_VOLATILE,
+ sizeof(v), v);
+ if (ret != EFI_WRITE_PROTECTED) {
+ efi_st_error("Get/SetVariable failed\n");
+ return EFI_ST_FAILURE;
+ }
+ } else {
+ if (ret != EFI_UNSUPPORTED) {
+ efi_st_error("SetVariable failed\n");
+ return EFI_ST_FAILURE;
+ }
}
len = EFI_ST_MAX_DATA_SIZE;
ret = runtime->get_variable(u"PlatformLangCodes", &guid_vendor0,