From 12dfc68c204e908fc4bac9e32a982cdfa23f6d55 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Wed, 17 Apr 2024 10:06:12 +0200 Subject: [PATCH 01/10] doc: sort env sub-commands alphabetically The 'env' man-page is currently only partially sorted. Signed-off-by: Heinrich Schuchardt --- doc/usage/cmd/env.rst | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/doc/usage/cmd/env.rst b/doc/usage/cmd/env.rst index a7e21693a67..60a294328e8 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,11 @@ the UEFI variables. The next commands are kept as alias and for compatibility: ++ *askenv* = *env ask* + *editenv* = *env edit* + *grepenv* = *env grep* -+ *setenv* = *env set* -+ *askenv* = *env ask* + *run* = *env run* ++ *setenv* = *env set* Ask ~~~ @@ -103,6 +103,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 +209,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 +245,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 +350,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 From bf92db1033d9ef20243f30751169e2e5b302c2ad Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Wed, 17 Apr 2024 10:06:13 +0200 Subject: [PATCH 02/10] doc: update list of aliases for the env command * add link to askenv man-page * add printenv Signed-off-by: Heinrich Schuchardt --- doc/usage/cmd/env.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/usage/cmd/env.rst b/doc/usage/cmd/env.rst index 60a294328e8..040076bcc03 100644 --- a/doc/usage/cmd/env.rst +++ b/doc/usage/cmd/env.rst @@ -40,9 +40,10 @@ the UEFI variables. The next commands are kept as alias and for compatibility: -+ *askenv* = *env ask* ++ :doc:`askenv ` = *env ask* + *editenv* = *env edit* + *grepenv* = *env grep* ++ :doc:`printenv ` = *env print* + *run* = *env run* + *setenv* = *env set* From 8afeab4cdf5e7babdf86c2c86d527badada7da1d Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Wed, 17 Apr 2024 18:01:25 +0200 Subject: [PATCH 03/10] cmd: eficonfig: check initrd path allocation After allocating memory for the initrd file path we need to check the initrd buffer pointer is not NULL. Fixes: 87d791423ac6 ("eficonfig: menu-driven addition of UEFI boot option") Signed-off-by: Heinrich Schuchardt --- cmd/eficonfig.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/eficonfig.c b/cmd/eficonfig.c index 8234e602b8f..0ba92c60e03 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; } From 2608015921f61425f09e3a413f22be6c155a13a7 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Thu, 18 Apr 2024 04:32:02 +0200 Subject: [PATCH 04/10] efi_loader: typo mstching %s/mstching/matching/ Reported-by: E Shattow Signed-off-by: Heinrich Schuchardt Reviewed-by: Ilias Apalodimas --- lib/efi_loader/efi_var_common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/efi_loader/efi_var_common.c b/lib/efi_loader/efi_var_common.c index aa8feffd3ec..ed53bcf3939 100644 --- a/lib/efi_loader/efi_var_common.c +++ b/lib/efi_loader/efi_var_common.c @@ -419,7 +419,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 From 05bf7adf87b02feb593fb553e377cd196a4598f0 Mon Sep 17 00:00:00 2001 From: Masahisa Kojima Date: Mon, 4 Dec 2023 13:30:14 +0900 Subject: [PATCH 05/10] efi_loader: use event callback for initrd deregistration Currently efi_initrd_deregister() is called in bootefi.c when the image started from bootefi command returns. Since efi_guid_event_group_return_to_efibootmgr event is implemented, so let's use this event for invoking initrd deregistration. Signed-off-by: Masahisa Kojima Reviewed-by: Ilias Apalodimas Tested-by: Ilias Apalodimas Signed-off-by: Heinrich Schuchardt --- lib/efi_loader/efi_helper.c | 5 -- lib/efi_loader/efi_load_initrd.c | 98 ++++++++++++++++++++------------ 2 files changed, 63 insertions(+), 40 deletions(-) diff --git a/lib/efi_loader/efi_helper.c b/lib/efi_loader/efi_helper.c index 58761fae784..6918fd5e48a 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 2b467b55481..67d1f75d525 100644 --- a/lib/efi_loader/efi_load_initrd.c +++ b/lib/efi_loader/efi_load_initrd.c @@ -183,41 +183,6 @@ out: return ret; } -/** - * efi_initrd_register() - create handle for loading initial RAM disk - * - * This function creates a new handle and installs a Linux specific vendor - * device path and an EFI_LOAD_FILE2_PROTOCOL. Linux uses the device path - * to identify the handle and then calls the LoadFile service of the - * EFI_LOAD_FILE2_PROTOCOL to read the initial RAM disk. - * - * Return: status code - */ -efi_status_t efi_initrd_register(void) -{ - efi_status_t ret; - - /* - * Allow the user to continue if Boot#### file path is not set for - * an initrd - */ - ret = check_initrd(); - if (ret == EFI_INVALID_PARAMETER) - return EFI_SUCCESS; - if (ret != EFI_SUCCESS) - return ret; - - ret = efi_install_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); - - return ret; -} - /** * efi_initrd_deregister() - delete the handle for loading initial RAM disk * @@ -245,3 +210,66 @@ efi_status_t efi_initrd_deregister(void) 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 + * device path and an EFI_LOAD_FILE2_PROTOCOL. Linux uses the device path + * to identify the handle and then calls the LoadFile service of the + * EFI_LOAD_FILE2_PROTOCOL to read the initial RAM disk. + * + * Return: status code + */ +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 + * an initrd + */ + ret = check_initrd(); + if (ret == EFI_INVALID_PARAMETER) + return EFI_SUCCESS; + if (ret != EFI_SUCCESS) + return ret; + + ret = efi_install_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); + if (ret != EFI_SUCCESS) { + log_err("installing EFI_LOAD_FILE2_PROTOCOL failed\n"); + return ret; + } + + 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; +} From c28d32f946f0d1c0780a02ab49dc2c1835370501 Mon Sep 17 00:00:00 2001 From: Ilias Apalodimas Date: Thu, 18 Apr 2024 15:54:50 +0300 Subject: [PATCH 06/10] efi_loader: conditionally enable SetvariableRT When we store EFI variables on file we don't allow SetVariable at runtime, since the OS doesn't know how to access or write that file. At the same time keeping the U-Boot drivers alive in runtime sections and performing writes from the firmware is dangerous -- if at all possible. For GetVariable at runtime we copy runtime variables in RAM and expose them to the OS. Add a Kconfig option and provide SetVariable at runtime using the same memory backend. The OS will be responsible for syncing the RAM contents to the file, otherwise any changes made during runtime won't persist reboots. It's worth noting that the variable store format is defined in EBBR [0] and authenticated variables are explicitly prohibited, since they have to be stored on a medium that's tamper and rollback protected. - pre-patch $~ mount | grep efiva efivarfs on /sys/firmware/efi/efivars type efivarfs (ro,nosuid,nodev,noexec,relatime) $~ efibootmgr -n 0001 Could not set BootNext: Read-only file system - post-patch $~ mount | grep efiva efivarfs on /sys/firmware/efi/efivars type efivarfs (rw,nosuid,nodev,noexec,relatime) $~ efibootmgr -n 0001 BootNext: 0001 BootCurrent: 0000 BootOrder: 0000,0001 Boot0000* debian HD(1,GPT,bdae5610-3331-4e4d-9466-acb5caf0b4a6,0x800,0x100000)/File(EFI\debian\grubaa64.efi) Boot0001* virtio 0 VenHw(e61d73b9-a384-4acc-aeab-82e828f3628b,0000000000000000)/VenHw(e61d73b9-a384-4acc-aeab-82e828f3628b,850000001f000000)/VenHw(e61d73b9-a384-4acc-aeab-82e828f3628b,1600850000000000){auto_created_boot_option} $~ efivar -p -n 8be4df61-93ca-11d2-aa0d-00e098032b8c-BootNext GUID: 8be4df61-93ca-11d2-aa0d-00e098032b8c Name: "BootNext" Attributes: Non-Volatile Boot Service Access Runtime Service Access Value: 00000000 01 00 FWTS runtime results Skipped tests are for SetVariable which is now supported 'Passed' test is for QueryVariableInfo which is not yet supported Test: UEFI miscellaneous runtime service interface tests. Test for UEFI miscellaneous runtime service interfaces 6 skipped Stress test for UEFI miscellaneous runtime service i.. 1 skipped Test GetNextHighMonotonicCount with invalid NULL par.. 1 skipped Test UEFI miscellaneous runtime services unsupported.. 1 passed Test: UEFI Runtime service variable interface tests. Test UEFI RT service get variable interface. 1 passed Test UEFI RT service get next variable name interface. 4 passed Test UEFI RT service set variable interface. 8 passed Test UEFI RT service query variable info interface. 1 skipped Test UEFI RT service variable interface stress test. 2 passed Test UEFI RT service set variable interface stress t.. 4 passed Test UEFI RT service query variable info interface s.. 1 skipped Test UEFI RT service get variable interface, invalid.. 5 passed Test UEFI RT variable services unsupported status. 1 passed, 3 skipped [0] https://arm-software.github.io/ebbr/index.html#document-chapter5-variable-storage Reviewed-by: Heinrich Schuchardt Signed-off-by: Ilias Apalodimas --- lib/efi_loader/Kconfig | 16 +++ lib/efi_loader/efi_runtime.c | 4 + lib/efi_loader/efi_variable.c | 116 ++++++++++++++++-- .../efi_selftest_variables_runtime.c | 14 ++- 4 files changed, 136 insertions(+), 14 deletions(-) diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index e13a6f9f4c3..cc8371a3bb4 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_runtime.c b/lib/efi_loader/efi_runtime.c index a61c9a77b13..dde083b0966 100644 --- a/lib/efi_loader/efi_runtime.c +++ b/lib/efi_loader/efi_runtime.c @@ -127,6 +127,10 @@ 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)) + 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_variable.c b/lib/efi_loader/efi_variable.c index e6c1219a11c..47bb7992057 100644 --- a/lib/efi_loader/efi_variable.c +++ b/lib/efi_loader/efi_variable.c @@ -219,17 +219,20 @@ efi_get_next_variable_name_int(efi_uintn_t *variable_name_size, return efi_get_next_variable_name_mem(variable_name_size, variable_name, vendor); } -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 +264,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 +476,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_selftest/efi_selftest_variables_runtime.c b/lib/efi_selftest/efi_selftest_variables_runtime.c index 4700d942410..986924b881d 100644 --- a/lib/efi_selftest/efi_selftest_variables_runtime.c +++ b/lib/efi_selftest/efi_selftest_variables_runtime.c @@ -62,9 +62,17 @@ 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)) { + /* At runtime only non-volatile variables may be set. */ + if (ret != EFI_INVALID_PARAMETER) { + efi_st_error("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, From bc3dd2493ef8c3c646aaeb3854a3f83558c50102 Mon Sep 17 00:00:00 2001 From: Ilias Apalodimas Date: Thu, 18 Apr 2024 15:54:51 +0300 Subject: [PATCH 07/10] efi_loader: Add OS notifications for SetVariable at runtime Previous patches enable SetVariable at runtime using a volatile storage backend using EFI_RUNTIME_SERVICES_DATA allocared memory. Since there's no recommendation from the spec on how to notify the OS, add a volatile EFI variable that contains the filename relative to the ESP. OS'es can use that file and update it at runtime $~ efivar -p -n b2ac5fc9-92b7-4acd-aeac-11e818c3130c-RTStorageVolatile GUID: b2ac5fc9-92b7-4acd-aeac-11e818c3130c Name: "RTStorageVolatile" Attributes: Boot Service Access Runtime Service Access Value: 00000000 75 62 6f 6f 74 65 66 69 2e 76 61 72 00 |ubootefi.var. | Reviewed-by: Heinrich Schuchardt Signed-off-by: Ilias Apalodimas --- include/efi_loader.h | 4 ++++ lib/efi_loader/efi_runtime.c | 19 ++++++++++++++++--- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/include/efi_loader.h b/include/efi_loader.h index bb51c028177..69442f4e58d 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/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c index dde083b0966..c8f7a88ba8d 100644 --- a/lib/efi_loader/efi_runtime.c +++ b/lib/efi_loader/efi_runtime.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -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,9 +129,20 @@ 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)) - rt_table->runtime_services_supported |= - EFI_RT_SUPPORTED_SET_VARIABLE; + if (IS_ENABLED(CONFIG_EFI_RT_VOLATILE_STORE)) { + 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; + } + rt_table->runtime_services_supported |= EFI_RT_SUPPORTED_SET_VARIABLE; + } /* * This value must be synced with efi_runtime_detach_list From 00da8d65a3baea8c3745367bea99b1d76f8f129c Mon Sep 17 00:00:00 2001 From: Ilias Apalodimas Date: Thu, 18 Apr 2024 15:54:52 +0300 Subject: [PATCH 08/10] efi_loader: add an EFI variable with the file contents 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 and will be updated when GetVariable is called. Some adjustments are needed to do that. Currently we discard BS-only variables in EBS(). We need to preserve those on the RAM backend that exposes the variables. Since BS-only variables can't appear at runtime 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 an efi_var_collect() variant available at runtime, in order to construct the "VarToFile" buffer on the fly. 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 # dumping all variables to a variable Co-developed-by: Heinrich Schuchardt # contributed on efi_var_collect_mem() Signed-off-by: Heinrich Schuchardt Signed-off-by: Ilias Apalodimas --- include/efi_variable.h | 16 +++- lib/charset.c | 2 +- lib/efi_loader/efi_runtime.c | 25 +++++ lib/efi_loader/efi_var_common.c | 6 +- lib/efi_loader/efi_var_mem.c | 151 +++++++++++++++++++----------- lib/efi_loader/efi_variable.c | 6 +- lib/efi_loader/efi_variable_tee.c | 5 - 7 files changed, 146 insertions(+), 65 deletions(-) diff --git a/include/efi_variable.h b/include/efi_variable.h index 42a2b7c52be..223bb9a4a5b 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 df4f0407485..182c92a50c4 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/efi_runtime.c b/lib/efi_loader/efi_runtime.c index c8f7a88ba8d..73831c527e0 100644 --- a/lib/efi_loader/efi_runtime.c +++ b/lib/efi_loader/efi_runtime.c @@ -130,6 +130,8 @@ efi_status_t efi_init_runtime_supported(void) 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 | @@ -141,6 +143,29 @@ efi_status_t efi_init_runtime_supported(void) 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; } diff --git a/lib/efi_loader/efi_var_common.c b/lib/efi_loader/efi_var_common.c index ed53bcf3939..961139f005a 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); } /** diff --git a/lib/efi_loader/efi_var_mem.c b/lib/efi_loader/efi_var_mem.c index 6c21cec5d45..940ab663882 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) @@ -184,53 +201,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 * @@ -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 47bb7992057..0cbed53d1db 100644 --- a/lib/efi_loader/efi_variable.c +++ b/lib/efi_loader/efi_variable.c @@ -209,14 +209,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); } /** diff --git a/lib/efi_loader/efi_variable_tee.c b/lib/efi_loader/efi_variable_tee.c index dde135fd9f8..4f1aa298da1 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"); From 85177b2cf4e9e92eb8454430ed64ecad69df9f92 Mon Sep 17 00:00:00 2001 From: Ilias Apalodimas Date: Thu, 18 Apr 2024 15:54:53 +0300 Subject: [PATCH 09/10] efi_selftest: add tests for setvariableRT Since we support SetVariableRT now add the relevant tests - Search for the RTStorageVolatile and VarToFile variables after EBS - Try to update with invalid variales (BS, RT only) - Try to write a variable bigger than our backend storage - Write a variable that fits and check VarToFile has been updated correclty - Append to the variable and check VarToFile changes - Try to delete VarToFile which is write protected - Try to add/delete runtime variables - Verify VarToFile contains a valid file format Signed-off-by: Ilias Apalodimas Reviewed-by: Heinrich Schuchardt --- .../efi_selftest_variables_runtime.c | 208 +++++++++++++++++- 1 file changed, 207 insertions(+), 1 deletion(-) diff --git a/lib/efi_selftest/efi_selftest_variables_runtime.c b/lib/efi_selftest/efi_selftest_variables_runtime.c index 986924b881d..afa91be62c8 100644 --- a/lib/efi_selftest/efi_selftest_variables_runtime.c +++ b/lib/efi_selftest/efi_selftest_variables_runtime.c @@ -10,6 +10,8 @@ */ #include +#include +#include #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); @@ -63,11 +70,210 @@ static int execute(void) EFI_VARIABLE_RUNTIME_ACCESS, 3, v + 4); 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"); From 52c62acc349a0ec1ba26ae497913ad34ee3de733 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Tue, 2 Jan 2024 00:11:44 +0100 Subject: [PATCH 10/10] x86: all firmware tables must be paragraph aligned On qemu-x86_64_defconfig the following was observed: => efidebug tables 00000000000f0074 eb9d2d31-2d88-11d3-9a16-0090273fc14d SMBIOS table The SMBIOS configuration table does not point to a paragraph-aligned (16 byte aligned) address. The reason is that in write_tables() rom_addr is not aligned and copied to gd->arch.smbios_start. The Simple Firmware Interface requires that the SFI table is paragraph- aligned but our code does not guarantee this. As all tables written in write_tables() must be paragraph-aligned, we should implement the address rounding in write_tables() and not in table specific routines like copy_pirq_routing_table(). Add paragraph-alignment in write_tables(). Signed-off-by: Heinrich Schuchardt Reviewed-by: Simon Glass --- arch/x86/lib/tables.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/x86/lib/tables.c b/arch/x86/lib/tables.c index 12eae17c396..1095dc92c5a 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;