Heinrich Schuchardt <xypron.g...@gmx.de> writes: > Saving UEFI variable as encoded U-Boot environment variables does not allow > support at runtime. > > Provide functions to manage a memory buffer with UEFI variables. > > Signed-off-by: Heinrich Schuchardt <xypron.g...@gmx.de> > --- > include/efi_variable.h | 16 ++ > lib/efi_loader/Makefile | 1 + > lib/efi_loader/efi_variables_mem.c | 317 +++++++++++++++++++++++++++++ > 3 files changed, 334 insertions(+) > create mode 100644 lib/efi_loader/efi_variables_mem.c
[...] > diff --git a/lib/efi_loader/efi_variables_mem.c > b/lib/efi_loader/efi_variables_mem.c > new file mode 100644 > index 0000000000..f70cc65f8b > --- /dev/null > +++ b/lib/efi_loader/efi_variables_mem.c > @@ -0,0 +1,317 @@ > +// SPDX-License-Identifier: GPL-2.0+ > +/* > + * File interface for UEFI variables > + * > + * Copyright (c) 2020, Heinrich Schuchardt > + */ > + > +#include <common.h> > +#include <efi_loader.h> > +#include <efi_variable.h> > +#include <u-boot/crc.h> > + > +static struct efi_var_file __efi_runtime_data *efi_var_buf; > +static struct efi_var_entry __efi_runtime_data *efi_current_var; > + > +/** > + * memcpy() - copy memory area > + * > + * At runtime memcpy() is not available. > + * > + * @dest: destination buffer > + * @src: source buffer > + * @n: number of bytes to copy > + * Return: pointer to destination buffer > + */ > +void __efi_runtime efi_var_mem_memcpy(void *dest, const void *src, size_t n) > +{ > + u8 *d = dest; > + const u8 *s = src; > + > + for (; n; --n) > + *d++ = *s++; > +} > + > +/** > + * efi_var_mem_compare() - compare GUID and name with a variable > + * > + * @var: variable to compare > + * @guid: GUID to compare > + * @name: variable name to compare > + * @next: pointer to next variable > + * Return: true if match > + */ > +static bool __efi_runtime > +efi_var_mem_compare(struct efi_var_entry *var, const efi_guid_t *guid, > + const u16 *name, struct efi_var_entry **next) > +{ > + int i; > + u8 *guid1, *guid2; > + const u16 *data, *var_name; > + bool match = true; > + > + for (guid1 = (u8 *)&var->guid, guid2 = (u8 *)guid, i = 0; > + i < sizeof(efi_guid_t) && match; ++i) > + match = (guid1[i] == guid2[i]); > + > + for (data = var->name, var_name = name;; ++data, ++var_name) { > + if (match) > + match = (*data == *var_name); > + if (!*data) > + break; > + } Don't roll out two different versions of memcmp() - make it a generic helper and use it here. > + > + ++data; > + > + if (next) > + *next = (struct efi_var_entry *) > + ALIGN((uintptr_t)data + var->length, 8); Also, instead of implementing iteration via carrying the iterator around, the readability of patches would be improved if this was done as a simple loop outside of this function. If for some reason, this is considered to be better, please add it as a comment in your next version. Thanks, Punit > + > + return match; > +} > + > +/** > + * efi_var_mem_find() - find a variable in the list > + * > + * @guid: GUID of the variable > + * @name: name of the variable > + * @next: on exit pointer to the next variable after the found one > + * Return: found variable > + */ > +struct efi_var_entry __efi_runtime > +*efi_var_mem_find(const efi_guid_t *guid, const u16 *name, > + struct efi_var_entry **next) > +{ > + struct efi_var_entry *var, *last; > + > + last = (struct efi_var_entry *) > + ((uintptr_t)efi_var_buf + efi_var_buf->length); > + > + if (!*name) { > + if (next) { > + *next = efi_var_buf->var; > + if (*next >= last) > + *next = NULL; > + } > + return NULL; > + } > + if (efi_current_var && > + efi_var_mem_compare(efi_current_var, guid, name, next)) { > + if (next && *next >= last) > + *next = NULL; > + return efi_current_var; > + } > + > + var = efi_var_buf->var; > + if (var < last) { > + for (; var;) { > + struct efi_var_entry *pos; > + bool match; > + > + match = efi_var_mem_compare(var, guid, name, &pos); > + if (pos >= last) > + pos = NULL; > + if (match) { > + if (next) > + *next = pos; > + return var; > + } > + var = pos; > + } > + } > + if (next) > + *next = NULL; > + return NULL; > +} > + > +/** > + * efi_var_mem_del() - delete a variable from the list of variables > + * > + * @var: variable to delete > + */ > +void __efi_runtime efi_var_mem_del(struct efi_var_entry *var) > +{ > + u16 *data = var->name; > + struct efi_var_entry *next, *last; > + u64 *from, *to; > + > + if (!var) > + return; > + > + last = (struct efi_var_entry *) > + ((uintptr_t)efi_var_buf + efi_var_buf->length); > + if (var < efi_current_var) > + efi_current_var = NULL; > + > + for (data = var->name; *data; ++data) > + ; > + ++data; > + next = (struct efi_var_entry *) > + ALIGN((uintptr_t)data + var->length, 8); > + efi_var_buf->length -= (uintptr_t)next - (uintptr_t)var; > + > + for (to = (u64 *)var, from = (u64 *)next; from < (u64 *)last; > + ++to, ++from) > + *to = *from; > + efi_var_buf->crc32 = crc32(0, (u8 *)efi_var_buf->var, > + efi_var_buf->length - > + sizeof(struct efi_var_file)); > +} > + > +/** > + * efi_var_mem_ins() - append a variable to the list of variables > + * > + * The variable is appended without checking if a variable of the same name > + * already exists. The two data buffers are concatenated. > + * > + * @name: variable name > + * @vendor: GUID > + * @attributes: variable attributes > + * @size1: size of the first data buffer > + * @data1: first data buffer > + * @size2: size of the second data field > + * @data2: second data buffer > + * Result: status code > + */ > +efi_status_t __efi_runtime efi_var_mem_ins( > + u16 *variable_name, > + const efi_guid_t *vendor, u32 attributes, > + const efi_uintn_t size1, const void *data1, > + const efi_uintn_t size2, const void *data2) > +{ > + u16 *data; > + struct efi_var_entry *var; > + u32 var_name_len; > + > + var = (struct efi_var_entry *) > + ((uintptr_t)efi_var_buf + efi_var_buf->length); > + for (var_name_len = 0; variable_name[var_name_len]; ++var_name_len) > + ; > + ++var_name_len; > + data = var->name + var_name_len; > + > + if ((uintptr_t)data - (uintptr_t)efi_var_buf + size1 + size2 > > + EFI_VAR_BUF_SIZE) > + return EFI_OUT_OF_RESOURCES; > + > + var->attr = attributes; > + var->length = size1 + size2; > + > + efi_var_mem_memcpy(&var->guid, vendor, sizeof(efi_guid_t)); > + efi_var_mem_memcpy(var->name, variable_name, > + sizeof(u16) * var_name_len); > + efi_var_mem_memcpy(data, data1, size1); > + efi_var_mem_memcpy((u8 *)data + size1, data2, size2); > + > + var = (struct efi_var_entry *) > + ALIGN((uintptr_t)data + var->length, 8); > + efi_var_buf->length = (uintptr_t)var - (uintptr_t)efi_var_buf; > + efi_var_buf->crc32 = crc32(0, (u8 *)efi_var_buf->var, > + efi_var_buf->length - > + sizeof(struct efi_var_file)); > + > + return EFI_SUCCESS; > +} > + > +/** > + * efi_var_mem_free() - determine free memory for variables > + * > + * Return: maximum data size plus variable name size > + */ > +u64 __efi_runtime efi_var_mem_free(void) > +{ > + return EFI_VAR_BUF_SIZE - efi_var_buf->length - > + 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_runtime > +efi_var_mem_notify_exit_boot_services(struct efi_event *event, void *context) > +{ > + /* Delete boot service only variables */ > + efi_var_mem_bs_del(); > +} > + > +/** > + * efi_var_mem_notify_exit_boot_services() - SetVirtualMemoryMap callback > + * > + * @event: callback event > + * @context: callback context > + */ > +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_var_mem_init() - set-up variable list > + * > + * Return: status code > + */ > +efi_status_t efi_var_mem_init(void) > +{ > + u64 memory; > + efi_status_t ret; > + struct efi_event *event; > + > + 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_var_buf = (struct efi_var_file *)(uintptr_t)memory; > + memset(efi_var_buf, 0, EFI_VAR_BUF_SIZE); > + 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, > + efi_var_mem_notify_virtual_address_map, NULL, > + NULL, &event); > + if (ret != EFI_SUCCESS) > + return ret; > + return ret; > +} > -- > 2.25.1