Previous patches enabled SetVariableRT using a RAM backend.
Although EBBR [0] defines a variable format we can teach userspace tools
and write the altered variables, it's better if we skip the ABI
requirements completely.

So let's add a new variable, in its own namespace called "VarToFile"
which contains a binary dump of the updated RT, BS and, NV variables.

Some adjustments are needed to do that. Currently we discard BS-only
variables in EBS(). We need to preserve those on the OS RAM backend
that exposes the variables. Since BS-only variables can't appear at RT
we need to move the memory masking checks from efi_var_collect() to
efi_get_next_variable_name_mem()/efi_get_variable_mem() and do the
filtering at runtime. We also need to make efi_var_collect() available
at runtime, in order to construct the "VarToFile" buffer with BS, RT &
NV variables.

All users and applications (for linux) have to do when updating a variable
is dd that variable in the file described by "RTStorageVolatile".

Linux efivarfs uses a first 4 bytes of the output to represent attributes
in little-endian format. So, storing variables works like this:

$~ efibootmgr -n 0001
$~ dd 
if=/sys/firmware/efi/efivars/VarToFile-b2ac5fc9-92b7-4acd-aeac-11e818c3130c 
of=/boot/efi/ubootefi.var skip=4 bs=1

[0] 
https://arm-software.github.io/ebbr/index.html#document-chapter5-variable-storage

Suggested-by:Ard Biesheuvel <a...@kernel.org> # dumping all variables to a 
variable
Signed-off-by: Ilias Apalodimas <ilias.apalodi...@linaro.org>
---
 include/efi_variable.h            |  15 +++-
 lib/efi_loader/efi_boottime.c     |   2 +
 lib/efi_loader/efi_var_common.c   |  43 +++++------
 lib/efi_loader/efi_var_file.c     |   1 -
 lib/efi_loader/efi_var_mem.c      |  90 ++++++++++-------------
 lib/efi_loader/efi_variable.c     | 118 ++++++++++++++++++++++++------
 lib/efi_loader/efi_variable_tee.c |   1 -
 7 files changed, 164 insertions(+), 106 deletions(-)

diff --git a/include/efi_variable.h b/include/efi_variable.h
index 42a2b7c52bef..8963339b9bb6 100644
--- a/include/efi_variable.h
+++ b/include/efi_variable.h
@@ -271,13 +271,15 @@ 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
  * @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 +291,14 @@ 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
  * 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 +338,11 @@ efi_get_next_variable_name_runtime(efi_uintn_t 
*variable_name_size,
  */
 void efi_var_buf_update(struct efi_var_file *var_buf);

+/**
+ * efi_prealloced_rt_memory() - Get a pointer to preallocated EFI memory
+ *                              available at runtime
+ *
+ * Return: pointer to preallocated runtime usable buffer
+ */
+void __efi_runtime *efi_prealloced_rt_memory(void);
 #endif
diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
index 1951291747cd..39481c89a688 100644
--- a/lib/efi_loader/efi_boottime.c
+++ b/lib/efi_loader/efi_boottime.c
@@ -97,6 +97,8 @@ const efi_guid_t efi_guid_load_file_protocol = 
EFI_LOAD_FILE_PROTOCOL_GUID;
 const efi_guid_t efi_guid_load_file2_protocol = EFI_LOAD_FILE2_PROTOCOL_GUID;
 /* GUID of the SMBIOS table */
 const efi_guid_t smbios_guid = SMBIOS_TABLE_GUID;
+/* used by special U-Boot variables during SetVariableRT */
+const efi_guid_t efi_guid_efi_rt_var_file = U_BOOT_EFI_RT_VAR_FILE_GUID;

 static efi_status_t EFIAPI efi_disconnect_controller(
                                        efi_handle_t controller_handle,
diff --git a/lib/efi_loader/efi_var_common.c b/lib/efi_loader/efi_var_common.c
index 07b9603d49f3..4abc90e411e7 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);
 }

 /**
@@ -427,18 +429,15 @@ void *efi_get_var(const u16 *name, const efi_guid_t 
*vendor, efi_uintn_t *size)
  *
  * Return:     Status code
  */
-efi_status_t __maybe_unused efi_var_collect(struct efi_var_file **bufp, loff_t 
*lenp,
-                                           u32 check_attr_mask)
+efi_status_t __efi_runtime
+efi_var_collect(struct efi_var_file **bufp, loff_t *lenp, u32 check_attr_mask)
 {
        size_t len = EFI_VAR_BUF_SIZE;
        struct efi_var_file *buf;
        struct efi_var_entry *var, *old_var;
        size_t old_var_name_length = 2;

-       *bufp = NULL; /* Avoid double free() */
-       buf = calloc(1, len);
-       if (!buf)
-               return EFI_OUT_OF_RESOURCES;
+       buf = (struct efi_var_file *)efi_prealloced_rt_memory();
        var = buf->var;
        old_var = var;
        for (;;) {
@@ -451,32 +450,26 @@ efi_status_t __maybe_unused efi_var_collect(struct 
efi_var_file **bufp, loff_t *
                        return EFI_BUFFER_TOO_SMALL;

                var_name_length = (uintptr_t)buf + len - (uintptr_t)var->name;
-               memcpy(var->name, old_var->name, old_var_name_length);
-               guidcpy(&var->guid, &old_var->guid);
-               ret = efi_get_next_variable_name_int(
-                               &var_name_length, var->name, &var->guid);
+               efi_memcpy_runtime(var->name, old_var->name, 
old_var_name_length);
+               efi_memcpy_runtime(&var->guid, &old_var->guid, 
sizeof(efi_guid_t));
+               ret = efi_get_next_variable_name_mem(&var_name_length, 
var->name,
+                                                    &var->guid, 
check_attr_mask);
                if (ret == EFI_NOT_FOUND)
                        break;
-               if (ret != EFI_SUCCESS) {
-                       free(buf);
+               if (ret != EFI_SUCCESS)
                        return ret;
-               }
                old_var_name_length = var_name_length;
                old_var = var;

                data = (u8 *)var->name + old_var_name_length;
                data_length = (uintptr_t)buf + len - (uintptr_t)data;
-               ret = efi_get_variable_int(var->name, &var->guid,
+               ret = efi_get_variable_mem(var->name, &var->guid,
                                           &var->attr, &data_length, data,
-                                          &var->time);
-               if (ret != EFI_SUCCESS) {
-                       free(buf);
+                                          &var->time, check_attr_mask);
+               if (ret != EFI_SUCCESS)
                        return ret;
-               }
-               if ((var->attr & check_attr_mask) == check_attr_mask) {
-                       var->length = data_length;
-                       var = (struct efi_var_entry *)ALIGN((uintptr_t)data + 
data_length, 8);
-               }
+               var->length = data_length;
+               var = (struct efi_var_entry *)ALIGN((uintptr_t)data + 
data_length, 8);
        }

        buf->reserved = 0;
@@ -490,5 +483,3 @@ efi_status_t __maybe_unused efi_var_collect(struct 
efi_var_file **bufp, loff_t *

        return EFI_SUCCESS;
 }
-
-
diff --git a/lib/efi_loader/efi_var_file.c b/lib/efi_loader/efi_var_file.c
index 413e1794e88c..8614e3d34706 100644
--- a/lib/efi_loader/efi_var_file.c
+++ b/lib/efi_loader/efi_var_file.c
@@ -83,7 +83,6 @@ efi_status_t efi_var_to_file(void)
 error:
        if (ret != EFI_SUCCESS)
                log_err("Failed to persist EFI variables\n");
-       free(buf);
        return ret;
 #else
        return EFI_SUCCESS;
diff --git a/lib/efi_loader/efi_var_mem.c b/lib/efi_loader/efi_var_mem.c
index 6c21cec5d457..a7af0604733e 100644
--- a/lib/efi_loader/efi_var_mem.c
+++ b/lib/efi_loader/efi_var_mem.c
@@ -16,6 +16,7 @@
  * relocation during SetVirtualAddressMap().
  */
 static struct efi_var_file __efi_runtime_data *efi_var_buf;
+static void __efi_runtime_data *efi_rt_prealloced;
 static struct efi_var_entry __efi_runtime_data *efi_current_var;

 /**
@@ -184,53 +185,6 @@ u64 __efi_runtime efi_var_mem_free(void)
               sizeof(struct efi_var_entry);
 }

-/**
- * 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
  *
@@ -241,6 +195,7 @@ static void EFIAPI __efi_runtime
 efi_var_mem_notify_virtual_address_map(struct efi_event *event, void *context)
 {
        efi_convert_pointer(0, (void **)&efi_var_buf);
+       efi_convert_pointer(0, (void **)&efi_rt_prealloced);
        efi_current_var = NULL;
 }

@@ -261,13 +216,21 @@ 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);
+       /*
+        * efi_var_collect() needs to run at runtime and provide us
+        * copies of variables used for the VarToFile variable.
+        * Preallocate memory equal to the variable storage and
+        * preserve it to copy variables around
+        */
+       ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES,
+                                EFI_RUNTIME_SERVICES_DATA,
+                                efi_size_in_pages(EFI_VAR_BUF_SIZE),
+                                &memory);
        if (ret != EFI_SUCCESS)
                return ret;
+       efi_rt_prealloced = (void *)(uintptr_t)memory;
+
        ret = efi_create_event(EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE, TPL_CALLBACK,
                               efi_var_mem_notify_virtual_address_map, NULL,
                               NULL, &event);
@@ -279,7 +242,7 @@ efi_status_t efi_var_mem_init(void)
 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,6 +254,9 @@ efi_get_variable_mem(const u16 *variable_name, const 
efi_guid_t *vendor,
        if (!var)
                return EFI_NOT_FOUND;

+       if (mask && !((var->attr & mask) == mask))
+               return EFI_NOT_FOUND;
+
        if (attributes)
                *attributes = var->attr;
        if (timep)
@@ -315,7 +281,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 +291,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 +315,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;
 }

@@ -354,3 +327,14 @@ void efi_var_buf_update(struct efi_var_file *var_buf)
 {
        memcpy(efi_var_buf, var_buf, EFI_VAR_BUF_SIZE);
 }
+
+void __efi_runtime *efi_prealloced_rt_memory(void)
+{
+       char *s;
+       int count = EFI_VAR_BUF_SIZE;
+
+       s = (char *)efi_rt_prealloced;
+       while (count--)
+               *s++ = 0;
+       return efi_rt_prealloced;
+}
diff --git a/lib/efi_loader/efi_variable.c b/lib/efi_loader/efi_variable.c
index f97c8c57f75c..4f529169ea54 100644
--- a/lib/efi_loader/efi_variable.c
+++ b/lib/efi_loader/efi_variable.c
@@ -22,6 +22,8 @@
 #include <u-boot/crc.h>
 #include <asm/sections.h>

+static const efi_guid_t __efi_runtime_data efi_guid_efi_rt_var_file =
+                                               U_BOOT_EFI_RT_VAR_FILE_GUID;
 #ifdef CONFIG_EFI_SECURE_BOOT

 /**
@@ -208,14 +210,16 @@ 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);
 }

 /**
@@ -479,6 +483,8 @@ if (IS_ENABLED(CONFIG_EFI_RT_VOLATILE_STORE)) {
        efi_uintn_t ret;
        bool append, delete;
        u64 time = 0;
+       struct efi_var_file *buf;
+       loff_t len;

        /*
         * Authenticated variables are not supported the rest of the checks
@@ -520,30 +526,60 @@ if (IS_ENABLED(CONFIG_EFI_RT_VOLATILE_STORE)) {
                        return EFI_NOT_FOUND;
        }

-       if (delete) {
+       if (!delete) {
+               /*
+                * We always insert new variabes and delete the old one when
+                * appending
+                */
+               len = 2 * (u16_strlen(variable_name) + 1) + data_size +
+                       sizeof(struct efi_var_entry);
+               if (var && append)
+                        len += 2 * var->length;
+               /*
+                * We will copy the variable update into VarToFile,
+                * account for it twice
+                */
+               len *= 2;
+               if (len > efi_var_mem_free())
+                       return EFI_OUT_OF_RESOURCES;
+               if (append && var) {
+                       u16 *old_data = var->name;
+
+                       for (; *old_data; ++old_data)
+                               ;
+                       ++old_data;
+                       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);
+               }
+       } else {
                /* EFI_NOT_FOUND has been handled before */
                attributes = var->attr;
                ret = EFI_SUCCESS;
-       } else if (append && var) {
-               u16 *old_data = var->name;
-
-               for (; *old_data; ++old_data)
-                       ;
-               ++old_data;
-               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;
+       /*
+        * Create a volatile variable that userspace apps can dd and
+        * update the file contents
+        */
+       ret = efi_var_collect(&buf, &len, EFI_VARIABLE_NON_VOLATILE);
+       if (ret != EFI_SUCCESS)
+               return ret;
+       var = efi_var_mem_find(&efi_guid_efi_rt_var_file, u"VarToFile", NULL);
+       if (var)
+               efi_var_mem_del(var);
+
+       ret = efi_var_mem_ins(u"VarToFile", &efi_guid_efi_rt_var_file,
+                             EFI_VARIABLE_RUNTIME_ACCESS, len, buf, 0,
+                             NULL, time);
+       return ret;
 } else

        return EFI_UNSUPPORTED;
@@ -557,11 +593,11 @@ void efi_variables_boot_exit_notify(void)
        const efi_guid_t efi_guid_efi_rt_var_file = U_BOOT_EFI_RT_VAR_FILE_GUID;
        const efi_guid_t rt_prop_guid = EFI_RT_PROPERTIES_TABLE_GUID;
        efi_status_t ret;
+       struct efi_var_file *buf;
+       loff_t len;
+       bool fail = false;

        if (IS_ENABLED(CONFIG_EFI_RT_VOLATILE_STORE)) {
-               struct efi_rt_properties_table *rt_prop =
-                       efi_get_configuration_table(&rt_prop_guid);
-
                ret = efi_set_variable_int(u"RTStorageVolatile",
                                           &efi_guid_efi_rt_var_file,
                                           EFI_VARIABLE_BOOTSERVICE_ACCESS |
@@ -569,11 +605,47 @@ void efi_variables_boot_exit_notify(void)
                                           EFI_VARIABLE_READ_ONLY,
                                           sizeof(EFI_VAR_FILE_NAME),
                                           EFI_VAR_FILE_NAME, false);
+               if (ret != EFI_SUCCESS) {
+                       fail = true;
+                       goto out;
+               }
+
+               ret = efi_var_collect(&buf, &len, EFI_VARIABLE_NON_VOLATILE);
+               if (ret != EFI_SUCCESS) {
+                       fail = true;
+                       goto out;
+               }
+
+               ret = efi_set_variable_int(u"VarToFile",
+                                          &efi_guid_efi_rt_var_file,
+                                          EFI_VARIABLE_BOOTSERVICE_ACCESS |
+                                          EFI_VARIABLE_RUNTIME_ACCESS,
+                                          len,
+                                          buf, false);
                if (ret != EFI_SUCCESS)
-                       rt_prop->runtime_services_supported |= 
~EFI_RT_SUPPORTED_SET_VARIABLE;
-               else
-                       log_err("Can't RTStorage. SetVariableRT won't be 
available\n");
+                       fail = true;
+out:
+               if (fail) {
+                       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, 0,
+                                            false);
+                       efi_set_variable_int(u"VarToFile",
+                                            &efi_guid_efi_rt_var_file,
+                                            EFI_VARIABLE_BOOTSERVICE_ACCESS |
+                                            EFI_VARIABLE_RUNTIME_ACCESS, 0, 0,
+                                            false);
+               } else {
+                       struct efi_rt_properties_table *rt_prop =
+                               efi_get_configuration_table(&rt_prop_guid);
+
+                       rt_prop->runtime_services_supported |=
+                                       EFI_RT_SUPPORTED_SET_VARIABLE;
+               }
        }
+
        /* Switch variable services functions to runtime version */
        efi_runtime_services.get_variable = efi_get_variable_runtime;
        efi_runtime_services.get_next_variable_name =
diff --git a/lib/efi_loader/efi_variable_tee.c 
b/lib/efi_loader/efi_variable_tee.c
index dde135fd9f81..9d0e270591ea 100644
--- a/lib/efi_loader/efi_variable_tee.c
+++ b/lib/efi_loader/efi_variable_tee.c
@@ -969,7 +969,6 @@ void efi_variables_boot_exit_notify(void)
                log_err("Can't populate EFI variables. No runtime variables 
will be available\n");
        else
                efi_var_buf_update(var_buf);
-       free(var_buf);

        /* Update runtime service table */
        efi_runtime_services.query_variable_info =
--
2.37.2

Reply via email to