[PATCH 10/16] efi_loader: UEFI variable persistence

Heinrich Schuchardt
2020-03-27T10:30:47Z

On 3/27/20 9:07 AM, Punit Agrawal wrote:
> 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.

This development does not guard against manipulation by the OS.

Ilias is cureently working on a solution for ATF based devices that will
provide secure storage for variables.

>
>>
>> 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?

It can be remove.

Thanks for reviewing.

Best regards

Heinrich

>
> 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
>> +}
>> +
>
> [...]
>