[PATCH 10/16] efi_loader: UEFI variable persistence

Punit Agrawal
2020-03-27T08:07:30Z

Heinrich Schuchardt <xypron.glpk@gmx.de> writes:

> Persist non-volatile UEFI variables in a file on the EFI system partition.
>
> The file is written:
>
> * whenever a non-volatile UEFI variable is changed after initialization
> of the UEFI sub-system.
> * upon ExitBootServices()

I might be missing something but how does this cope with the ESP being
on a storage medium access to which is owned by the OS at runtime? e.g.,
partition on eMMC or SATA drive.

>
> The file is read during the UEFI sub-system initialization to restore
> non-volatile UEFI variables.
>
> Signed-off-by: Heinrich Schuchardt <xypron.glpk@gmx.de>
> ---
> include/efi_variable.h | 36 +++++
> lib/efi_loader/Kconfig | 8 +
> lib/efi_loader/Makefile | 1 +
> lib/efi_loader/efi_variable.c | 12 +-
> lib/efi_loader/efi_variables_file.c | 235 ++++++++++++++++++++++++++++
> 5 files changed, 291 insertions(+), 1 deletion(-)
> create mode 100644 include/efi_variable.h
> create mode 100644 lib/efi_loader/efi_variables_file.c
>

[...]

> diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
> index e10ca05549..41705fc252 100644
> --- a/lib/efi_loader/Kconfig
> +++ b/lib/efi_loader/Kconfig
> @@ -27,6 +27,14 @@ config EFI_LOADER
>
> if EFI_LOADER
>
> +config EFI_VARIABLE_FILE_STORE
> + bool "Store non-volatile UEFI variables as file"
> + depends on FAT_WRITE
> + default y
> + help
> + Select tis option if you want non-volatile UEFI variables to be stored

this

> + as file /ubootefi.var on the EFI system partition.
> +
> config EFI_GET_TIME
> bool "GetTime() runtime service"
> depends on DM_RTC

[...]

> diff --git a/lib/efi_loader/efi_variables_file.c b/lib/efi_loader/efi_variables_file.c
> new file mode 100644
> index 0000000000..4a918d3fde
> --- /dev/null
> +++ b/lib/efi_loader/efi_variables_file.c
> @@ -0,0 +1,235 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * File interface for UEFI variables
> + *
> + * Copyright (c) 2020, Heinrich Schuchardt
> + */
> +
> +#include <common.h>
> +#include <charset.h>
> +#include <fs.h>
> +#include <malloc.h>
> +#include <mapmem.h>
> +#include <efi_loader.h>
> +#include <efi_variable.h>
> +#include <u-boot/crc.h>
> +
> +#define PART_STR_LEN 10
> +
> +/**
> + * efi_set_blk_dev_to_system_partition() - select EFI system partition
> + *
> + * Set the EFI system partition as current block device.
> + *
> + * Return: status code
> + */
> +static efi_status_t __maybe_unused efi_set_blk_dev_to_system_partition(void)
> +{
> + char part_str[PART_STR_LEN];
> + int r;
> +
> + if (!efi_system_partition.if_type)
> + return EFI_NOT_FOUND;
> + snprintf(part_str, PART_STR_LEN, "%u:%u",
> + efi_system_partition.devnum, efi_system_partition.part);
> + r = fs_set_blk_dev(blk_get_if_type_name(efi_system_partition.if_type),
> + part_str, FS_TYPE_ANY);
> + if (r) {
> + printf("Cannot read EFI system partition\n");
> + return EFI_DEVICE_ERROR;
> + }
> + return EFI_SUCCESS;
> +}
> +
> +/**
> + * efi_var_collect() - collect non-volatile variables in buffer
> + *
> + * A buffer is allocated and filled with all non-volatile variables in a
> + * format ready to be written to disk.
> + *
> + * @bufp: pointer to pointer of buffer with collected variables
> + * @lenp: pointer to length of buffer
> + * Return: status code
> + */
> +static efi_status_t __maybe_unused efi_var_collect(struct efi_var_file **bufp,
> + loff_t *lenp)
> +{
> + 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;
> + var = buf->var;
> + old_var = var;
> + for (;;) {
> + efi_uintn_t data_length, var_name_length;
> + u8 *data;
> + efi_status_t ret;
> +
> + if ((uintptr_t)buf + len <=
> + (uintptr_t)var->name + old_var_name_length)
> + 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);
> + if (ret == EFI_NOT_FOUND)
> + break;
> + if (ret != EFI_SUCCESS) {
> + free(buf);
> + 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,
> + &var->attr, &data_length, data);
> + if (ret != EFI_SUCCESS) {
> + free(buf);
> + return ret;
> + }
> + if (!(var->attr & EFI_VARIABLE_NON_VOLATILE))
> + continue;
> + var->length = data_length;
> + var = (struct efi_var_entry *)
> + ALIGN((uintptr_t)data + data_length, 8);
> + }
> +
> + buf->reserved = 0;
> + buf->magic = EFI_VAR_FILE_MAGIC;
> + len = (uintptr_t)var - (uintptr_t)buf;
> + buf->crc32 = crc32(0, (u8 *)buf->var,
> + len - sizeof(struct efi_var_file));
> + buf->length = len;
> + *bufp = buf;
> + *lenp = len;
> +
> + return EFI_SUCCESS;
> +}
> +
> +/**
> + * efi_var_to_file() - save non-volatile variables as file
> + *
> + * File ubootefi.var is created on the EFI system partion.
> + *
> + * Return: status code
> + */

The return value doesn't seem to be used in this patch. Is it really
needed?

Thanks,
Punit

> +efi_status_t efi_var_to_file(void)
> +{
> +#ifdef CONFIG_EFI_VARIABLE_FILE_STORE
> + efi_status_t ret;
> + struct efi_var_file *buf;
> + loff_t len;
> + loff_t actlen;
> + int r;
> +
> + ret = efi_var_collect(&buf, &len);
> + if (ret != EFI_SUCCESS)
> + goto error;
> +
> + ret = efi_set_blk_dev_to_system_partition();
> + if (ret != EFI_SUCCESS)
> + goto error;
> +
> + r = fs_write(EFI_VAR_FILE_NAME, map_to_sysmem(buf), 0, len, &actlen);
> + if (r || len != actlen)
> + ret = EFI_DEVICE_ERROR;
> +
> +error:
> + if (ret != EFI_SUCCESS)
> + printf("Failed to persist EFI variables\n");
> + free(buf);
> + return ret;
> +#else
> + return EFI_SUCCESS;
> +#endif
> +}
> + …