[U-Boot] [PATCH 0/7] efi_loader: variable services

Currently our UEFI variables are stored in U-Boot variables. Runtime support for variables is missing. This patch series prepares for alternative implementations of the variable services.
* QueryVariableInfo() is moved into the same file as the other variable services. * For booting via GRUB into Linux UEFI variables are not necessary. An alternative implementation of the variable services is provided which only returns EFI_UNSUPPORTED. * The detaching of runtime services is changed to allow the move to ExitBootServices(). * The detaching of variable services is moved to ExitBootServices(). * Unimplemented runtime services must return EFI_UNIMPLEMENTED. * Provide a unit test for variable services at runtime.
Heinrich Schuchardt (7): efi_loader: move efi_query_variable_info() efi_debug: make variable support customizable efi_loader: initialization of variable services efi_loader: clean up runtime detaching efi_loader: let the variable driver patch out the runtime efi_loader: unimplemented runtime services efi_selftest: test variable services at runtime
include/efi_loader.h | 9 ++ lib/efi_loader/Kconfig | 18 +++ lib/efi_loader/Makefile | 3 +- lib/efi_loader/efi_boottime.c | 3 +- lib/efi_loader/efi_runtime.c | 148 +++++++----------- lib/efi_loader/efi_setup.c | 14 +- lib/efi_loader/efi_variable.c | 81 ++++++++++ lib/efi_loader/efi_variable_null.c | 113 +++++++++++++ lib/efi_selftest/Makefile | 6 +- .../efi_selftest_variables_runtime.c | 94 +++++++++++ 10 files changed, 394 insertions(+), 95 deletions(-) create mode 100644 lib/efi_loader/efi_variable_null.c create mode 100644 lib/efi_selftest/efi_selftest_variables_runtime.c
-- 2.20.1

Let's keep similar things together.
Move efi_query_variable_info() to lib/efi_loader/efi_variable.c
Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de --- include/efi_loader.h | 5 +++++ lib/efi_loader/efi_runtime.c | 27 --------------------------- lib/efi_loader/efi_variable.c | 27 +++++++++++++++++++++++++++ 3 files changed, 32 insertions(+), 27 deletions(-)
diff --git a/include/efi_loader.h b/include/efi_loader.h index b07155cecb..de1e67fd40 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -618,6 +618,11 @@ efi_status_t EFIAPI efi_set_variable(u16 *variable_name, const efi_guid_t *vendor, u32 attributes, efi_uintn_t data_size, const void *data);
+efi_status_t EFIAPI efi_query_variable_info( + u32 attributes, u64 *maximum_variable_storage_size, + u64 *remaining_variable_storage_size, + u64 *maximum_variable_size); + /* * See section 3.1.3 in the v2.7 UEFI spec for more details on * the layout of EFI_LOAD_OPTION. In short it is: diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c index 40fdc0ea92..dd91880ad6 100644 --- a/lib/efi_loader/efi_runtime.c +++ b/lib/efi_loader/efi_runtime.c @@ -782,33 +782,6 @@ efi_status_t __efi_runtime EFIAPI efi_query_capsule_caps( return EFI_UNSUPPORTED; }
-/** - * efi_query_variable_info() - get information about EFI variables - * - * This function implements the QueryVariableInfo() runtime service. - * - * See the Unified Extensible Firmware Interface (UEFI) specification for - * details. - * - * @attributes: bitmask to select variables to be - * queried - * @maximum_variable_storage_size: maximum size of storage area for the - * selected variable types - * @remaining_variable_storage_size: remaining size of storage are for the - * selected variable types - * @maximum_variable_size: maximum size of a variable of the - * selected type - * Returns: status code - */ -efi_status_t __efi_runtime EFIAPI efi_query_variable_info( - u32 attributes, - u64 *maximum_variable_storage_size, - u64 *remaining_variable_storage_size, - u64 *maximum_variable_size) -{ - return EFI_UNSUPPORTED; -} - struct efi_runtime_services __efi_runtime_data efi_runtime_services = { .hdr = { .signature = EFI_RUNTIME_SERVICES_SIGNATURE, diff --git a/lib/efi_loader/efi_variable.c b/lib/efi_loader/efi_variable.c index d6b75ca02e..f71dc29ee9 100644 --- a/lib/efi_loader/efi_variable.c +++ b/lib/efi_loader/efi_variable.c @@ -520,3 +520,30 @@ out:
return EFI_EXIT(ret); } + +/** + * efi_query_variable_info() - get information about EFI variables + * + * This function implements the QueryVariableInfo() runtime service. + * + * See the Unified Extensible Firmware Interface (UEFI) specification for + * details. + * + * @attributes: bitmask to select variables to be + * queried + * @maximum_variable_storage_size: maximum size of storage area for the + * selected variable types + * @remaining_variable_storage_size: remaining size of storage are for the + * selected variable types + * @maximum_variable_size: maximum size of a variable of the + * selected type + * Returns: status code + */ +efi_status_t __efi_runtime EFIAPI efi_query_variable_info( + u32 attributes, + u64 *maximum_variable_storage_size, + u64 *remaining_variable_storage_size, + u64 *maximum_variable_size) +{ + return EFI_UNSUPPORTED; +} -- 2.20.1

Provide a customization option to define how UEFI variables are implemented.
This patch provides a dummy implementation without any variable support and provides the choice between this dummy and the legacy implementation.
Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de --- lib/efi_loader/Kconfig | 18 ++++++ lib/efi_loader/Makefile | 3 +- lib/efi_loader/efi_setup.c | 10 +++- lib/efi_loader/efi_variable_null.c | 96 ++++++++++++++++++++++++++++++ lib/efi_selftest/Makefile | 5 +- 5 files changed, 128 insertions(+), 4 deletions(-) create mode 100644 lib/efi_loader/efi_variable_null.c
diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index cd5436c576..97c2ca9820 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -34,6 +34,24 @@ config EFI_SET_TIME Provide the SetTime() runtime service at boottime. This service can be used by an EFI application to adjust the real time clock.
+choice + prompt "Variable storage" + default EFI_VARIABLES_UBOOT + help + Define where UEFI variables are stored. + +config EFI_VARIABLES_UBOOT + bool "U-Boot environment" + help + UEFI variables are stored in the U-Boot environment. + +config EFI_VARIABLES_NULL + bool "UEFI variables are not available" + help + UEFI variables are not supported. + +endchoice + config EFI_DEVICE_PATH_TO_TEXT bool "Device path to text protocol" default y diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile index 01769ea58b..e244aee2f5 100644 --- a/lib/efi_loader/Makefile +++ b/lib/efi_loader/Makefile @@ -31,7 +31,8 @@ obj-y += efi_root_node.o obj-y += efi_runtime.o obj-y += efi_setup.o obj-$(CONFIG_EFI_UNICODE_COLLATION_PROTOCOL2) += efi_unicode_collation.o -obj-y += efi_variable.o +obj-$(CONFIG_EFI_VARIABLES_UBOOT) += efi_variable.o +obj-$(CONFIG_EFI_VARIABLES_NULL) += efi_variable_null.o obj-y += efi_watchdog.o obj-$(CONFIG_LCD) += efi_gop.o obj-$(CONFIG_DM_VIDEO) += efi_gop.o diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c index bfb57836fa..a3f59506d8 100644 --- a/lib/efi_loader/efi_setup.c +++ b/lib/efi_loader/efi_setup.c @@ -22,6 +22,8 @@ void __weak allow_unaligned(void) { }
+#ifndef CONFIG_EFI_VARIABLES_NULL + /** * efi_init_platform_lang() - define supported languages * @@ -82,6 +84,8 @@ out: return ret; }
+#endif + /** * efi_init_obj_list() - Initialize and populate EFI object list * @@ -89,7 +93,9 @@ out: */ efi_status_t efi_init_obj_list(void) { +#ifndef CONFIG_EFI_VARIABLES_NULL u64 os_indications_supported = 0; /* None */ +#endif efi_status_t ret = EFI_SUCCESS;
/* Initialize once only */ @@ -101,7 +107,7 @@ efi_status_t efi_init_obj_list(void)
/* On ARM switch from EL3 or secure mode to EL2 or non-secure mode */ switch_to_non_secure_mode(); - +#ifndef CONFIG_EFI_VARIABLES_NULL /* Define supported languages */ ret = efi_init_platform_lang(); if (ret != EFI_SUCCESS) @@ -121,7 +127,7 @@ efi_status_t efi_init_obj_list(void) ret = efi_init_runtime_supported(); if (ret != EFI_SUCCESS) goto out; - +#endif /* Initialize system table */ ret = efi_initialize_system_table(); if (ret != EFI_SUCCESS) diff --git a/lib/efi_loader/efi_variable_null.c b/lib/efi_loader/efi_variable_null.c new file mode 100644 index 0000000000..f4c96c90a3 --- /dev/null +++ b/lib/efi_loader/efi_variable_null.c @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Dummy driver for UEFI variables. None of the variable services is supported. + * + * Copyright (c) 2019 Heinrich Schuchardt + */ + +#include <efi_loader.h> + +/** + * efi_get_variable() - retrieve value of a UEFI variable + * + * This function implements the GetVariable runtime service. + * + * See the Unified Extensible Firmware Interface (UEFI) specification for + * details. + * + * @variable_name: name of the variable + * @vendor: vendor GUID + * @attributes: attributes of the variable + * @data_size: size of the buffer to which the variable value is copied + * @data: buffer to which the variable value is copied + * Return: status code + */ +efi_status_t __efi_runtime EFIAPI +efi_get_variable(u16 *variable_name, const efi_guid_t *vendor, u32 *attributes, + efi_uintn_t *data_size, void *data) +{ + return EFI_UNSUPPORTED; +} + +/** + * efi_get_next_variable_name() - enumerate the current variable names + * @variable_name_size: size of variable_name buffer in byte + * @variable_name: name of uefi variable's name in u16 + * @vendor: vendor's guid + * + * This function implements the GetNextVariableName service. + * + * See the Unified Extensible Firmware Interface (UEFI) specification for + * details: http://wiki.phoenix.com/wiki/index.php/ + * EFI_RUNTIME_SERVICES#GetNextVariableName.28.29 + * + * Return: status code + */ +efi_status_t __efi_runtime EFIAPI +efi_get_next_variable_name(efi_uintn_t *variable_name_size, u16 *variable_name, + const efi_guid_t *vendor) +{ + return EFI_UNSUPPORTED; +} + +/** + * efi_set_variable() - set value of a UEFI variable + * + * This function implements the SetVariable runtime service. + * + * See the Unified Extensible Firmware Interface (UEFI) specification for + * details. + * + * @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 + */ +efi_status_t __efi_runtime EFIAPI +efi_set_variable(u16 *variable_name, const efi_guid_t *vendor, u32 attributes, + efi_uintn_t data_size, const void *data) +{ + return EFI_UNSUPPORTED; +} + +/** + * efi_query_variable_info() - query information the variable store + * + * This function implements the QueryVariableInfo runtime service. + * + * See the Unified Extensible Firmware Interface (UEFI) specification for + * details. + * + * @attributes: type of variables for which info shall + * be provided + * @maximum_variable_storage_size: total available storage for variables + * @remaining_variable_storage_size: remaining available storage + * @maximum_variable_size: maximum size of a variable + * Return: status code + */ +efi_status_t __efi_runtime EFIAPI +efi_query_variable_info(u32 attributes, u64 *maximum_variable_storage_size, + u64 *remaining_variable_storage_size, + u64 *maximum_variable_size) +{ + return EFI_UNSUPPORTED; +} diff --git a/lib/efi_selftest/Makefile b/lib/efi_selftest/Makefile index 3bebd0f573..bb3ebd7fa7 100644 --- a/lib/efi_selftest/Makefile +++ b/lib/efi_selftest/Makefile @@ -34,7 +34,6 @@ efi_selftest_textinputex.o \ efi_selftest_textoutput.o \ efi_selftest_tpl.o \ efi_selftest_util.o \ -efi_selftest_variables.o \ efi_selftest_watchdog.o
obj-$(CONFIG_EFI_DEVICE_PATH_TO_TEXT) += efi_selftest_devicepath.o @@ -45,6 +44,10 @@ obj-$(CONFIG_CPU_V7) += efi_selftest_unaligned.o obj-$(CONFIG_EFI_LOADER_HII) += efi_selftest_hii.o obj-$(CONFIG_EFI_GET_TIME) += efi_selftest_rtc.o
+ifeq ($(CONFIG_EFI_VARIABLES_NULL),) +obj-y += efi_selftest_variables.o +endif + ifeq ($(CONFIG_GENERATE_ACPI_TABLE),) obj-y += efi_selftest_fdt.o endif -- 2.20.1

Provide an initialization routine for variable services.
Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de --- include/efi_loader.h | 2 ++ lib/efi_loader/efi_setup.c | 6 ++++++ lib/efi_loader/efi_variable.c | 10 ++++++++++ 3 files changed, 18 insertions(+)
diff --git a/include/efi_loader.h b/include/efi_loader.h index de1e67fd40..d30c4e8ef3 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -325,6 +325,8 @@ extern struct list_head efi_register_notify_events;
/* Initialize efi execution environment */ efi_status_t efi_init_obj_list(void); +/* Initialize variable services */ +efi_status_t efi_init_variables(void); /* Called by bootefi to initialize root node */ efi_status_t efi_root_node_register(void); /* Called by bootefi to initialize runtime */ diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c index a3f59506d8..d73a3dd706 100644 --- a/lib/efi_loader/efi_setup.c +++ b/lib/efi_loader/efi_setup.c @@ -108,6 +108,12 @@ efi_status_t efi_init_obj_list(void) /* On ARM switch from EL3 or secure mode to EL2 or non-secure mode */ switch_to_non_secure_mode(); #ifndef CONFIG_EFI_VARIABLES_NULL + + /* Initialize variable services */ + ret = efi_init_variables(); + if (ret != EFI_SUCCESS) + goto out; + /* Define supported languages */ ret = efi_init_platform_lang(); if (ret != EFI_SUCCESS) diff --git a/lib/efi_loader/efi_variable.c b/lib/efi_loader/efi_variable.c index f71dc29ee9..6210425f5e 100644 --- a/lib/efi_loader/efi_variable.c +++ b/lib/efi_loader/efi_variable.c @@ -547,3 +547,13 @@ efi_status_t __efi_runtime EFIAPI efi_query_variable_info( { return EFI_UNSUPPORTED; } + +/** + * efi_init_variables() - initialize variable services + * + * Return: status code + */ +efi_status_t efi_init_variables(void) +{ + return EFI_SUCCESS; +} -- 2.20.1

The detaching of the runtime will have to move to ExitBootServices() to encompass operating system that do not call SetVirtualAddressMap().
This patch changes the logic for the relocation of the pointers in the runtime table such that the relocation becomes independent of the entries in the detach list.
Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de --- lib/efi_loader/efi_runtime.c | 78 +++++++++++++++++++++++++----------- 1 file changed, 55 insertions(+), 23 deletions(-)
diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c index dd91880ad6..1c8f9b8218 100644 --- a/lib/efi_loader/efi_runtime.c +++ b/lib/efi_loader/efi_runtime.c @@ -436,29 +436,53 @@ static const struct efi_runtime_detach_list_struct efi_runtime_detach_list[] = { } };
-static bool efi_runtime_tobedetached(void *p) +/** + * efi_is_runtime_service_pointer() - check if pointer points to runtime table + * + * @p: pointer to check + * Return: true if the pointer points to a service function pointer in the + * runtime table + */ +static bool efi_is_runtime_service_pointer(void *p) { - int i; - - for (i = 0; i < ARRAY_SIZE(efi_runtime_detach_list); i++) - if (efi_runtime_detach_list[i].ptr == p) - return true; - - return false; + return p >= (void *)&efi_runtime_services.get_time && + p <= (void *)&efi_runtime_services.query_variable_info; }
-static void efi_runtime_detach(ulong offset) +static __efi_runtime void efi_runtime_detach(void) { int i; - ulong patchoff = offset - (ulong)gd->relocaddr;
+ /* + * Replace boottime functions by runtime functions + * TODO: move this step to ExitBootServices() + */ for (i = 0; i < ARRAY_SIZE(efi_runtime_detach_list); i++) { ulong patchto = (ulong)efi_runtime_detach_list[i].patchto; ulong *p = efi_runtime_detach_list[i].ptr; - ulong newaddr = patchto ? (patchto + patchoff) : 0;
- debug("%s: Setting %p to %lx\n", __func__, p, newaddr); - *p = newaddr; + debug("%s: Setting %p to %lx\n", __func__, p, patchto); + *p = patchto; + } +} + +static __efi_runtime void efi_relocate_runtime_table(ulong offset) +{ + ulong patchoff; + void **pos; + + /* Relocate the runtime services pointers */ + patchoff = offset - gd->relocaddr; + for (pos = (void **)&efi_runtime_services.get_time; + pos <= (void **)&efi_runtime_services.query_variable_info; ++pos) { + /* + * The UEFI spec requires not to update VirtualAddressMap() + * and ConvertPointer(). + */ + if (*pos && pos != + (void **)&efi_runtime_services.set_virtual_address_map && + pos != (void **)&efi_runtime_services.convert_pointer) + *pos += patchoff; }
/* Update CRC32 */ @@ -483,6 +507,10 @@ void efi_runtime_relocate(ulong offset, struct efi_mem_desc *map)
p = (void*)((ulong)rel->offset - base) + gd->relocaddr;
+ /* The runtime services are updated in efi_runtime_detach() */ + if (map && efi_is_runtime_service_pointer(p)) + continue; + debug("%s: rel->info=%#lx *p=%#lx rel->offset=%p\n", __func__, rel->info, *p, rel->offset);
@@ -506,9 +534,8 @@ void efi_runtime_relocate(ulong offset, struct efi_mem_desc *map) } #endif default: - if (!efi_runtime_tobedetached(p)) - printf("%s: Unknown relocation type %llx\n", - __func__, rel->info & R_MASK); + printf("%s: Unknown relocation type %llx\n", + __func__, rel->info & R_MASK); continue; }
@@ -516,9 +543,8 @@ void efi_runtime_relocate(ulong offset, struct efi_mem_desc *map) if (map && ((newaddr < map->virtual_start) || newaddr > (map->virtual_start + (map->num_pages << EFI_PAGE_SHIFT)))) { - if (!efi_runtime_tobedetached(p)) - printf("%s: Relocation at %p is out of " - "range (%lx)\n", __func__, p, newaddr); + printf("%s: Relocation at %p is out of range (%lx)\n", + __func__, p, newaddr); continue; }
@@ -623,7 +649,15 @@ static efi_status_t EFIAPI efi_set_virtual_address_map( } }
- /* Move the actual runtime code over */ + /* + * Some runtime services are implemented in a way that we can only offer + * them at boottime. Replace those function pointers. + * + * TODO: move this call to ExitBootServices(). + */ + efi_runtime_detach(); + + /* Relocate the runtime. See TODO above */ for (i = 0; i < n; i++) { struct efi_mem_desc *map;
@@ -632,10 +666,8 @@ static efi_status_t EFIAPI efi_set_virtual_address_map( ulong new_offset = map->virtual_start - map->physical_start + gd->relocaddr;
+ efi_relocate_runtime_table(new_offset); efi_runtime_relocate(new_offset, map); - /* Once we're virtual, we can no longer handle - complex callbacks */ - efi_runtime_detach(new_offset); return EFI_EXIT(EFI_SUCCESS); } } -- 2.20.1

Our variable services are only provided at boottime. Therefore when leaving boottime the variable function are replaced by dummy functions returning EFI_UNSUPPORTED. Move this patching of the runtime table to the variable services implementation. Executed it in ExitBootServices().
Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de --- include/efi_loader.h | 2 ++ lib/efi_loader/efi_boottime.c | 3 +- lib/efi_loader/efi_runtime.c | 9 ------ lib/efi_loader/efi_variable.c | 44 ++++++++++++++++++++++++++++++ lib/efi_loader/efi_variable_null.c | 17 ++++++++++++ 5 files changed, 65 insertions(+), 10 deletions(-)
diff --git a/include/efi_loader.h b/include/efi_loader.h index d30c4e8ef3..8d75dde569 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -327,6 +327,8 @@ extern struct list_head efi_register_notify_events; efi_status_t efi_init_obj_list(void); /* Initialize variable services */ efi_status_t efi_init_variables(void); +/* Notify ExitBootServices() is called */ +void efi_variables_boot_exit_notify(void); /* Called by bootefi to initialize root node */ efi_status_t efi_root_node_register(void); /* Called by bootefi to initialize runtime */ diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index d104cc6b31..a929bbb671 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -1968,7 +1968,8 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, /* Make sure that notification functions are not called anymore */ efi_tpl = TPL_HIGH_LEVEL;
- /* TODO: Should persist EFI variables here */ + /* Notify variable services */ + efi_variables_boot_exit_notify();
board_quiesce_devices();
diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c index 1c8f9b8218..551470b9ac 100644 --- a/lib/efi_loader/efi_runtime.c +++ b/lib/efi_loader/efi_runtime.c @@ -424,15 +424,6 @@ static const struct efi_runtime_detach_list_struct efi_runtime_detach_list[] = { /* Clean up system table */ .ptr = &systab.boottime, .patchto = NULL, - }, { - .ptr = &efi_runtime_services.get_variable, - .patchto = &efi_device_error, - }, { - .ptr = &efi_runtime_services.get_next_variable_name, - .patchto = &efi_device_error, - }, { - .ptr = &efi_runtime_services.set_variable, - .patchto = &efi_device_error, } };
diff --git a/lib/efi_loader/efi_variable.c b/lib/efi_loader/efi_variable.c index 6210425f5e..bc8ed678c9 100644 --- a/lib/efi_loader/efi_variable.c +++ b/lib/efi_loader/efi_variable.c @@ -548,6 +548,50 @@ efi_status_t __efi_runtime EFIAPI efi_query_variable_info( return EFI_UNSUPPORTED; }
+/** + * efi_get_variable_runtime() - runtime implementation of GetVariable() + */ +static efi_status_t __efi_runtime EFIAPI +efi_get_variable_runtime(u16 *variable_name, const efi_guid_t *vendor, + u32 *attributes, efi_uintn_t *data_size, void *data) +{ + return EFI_UNSUPPORTED; +} + +/** + * efi_get_next_variable_name_runtime() - runtime implementation of + * GetNextVariable() + */ +static efi_status_t __efi_runtime EFIAPI +efi_get_next_variable_name_runtime(efi_uintn_t *variable_name_size, + u16 *variable_name, const efi_guid_t *vendor) +{ + return EFI_UNSUPPORTED; +} + +/** + * efi_set_variable_runtime() - runtime implementation of SetVariable() + */ +static efi_status_t __efi_runtime EFIAPI +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; +} + +/** + * efi_variables_boot_exit_notify() - notify ExitBootServices() is called + */ +void efi_variables_boot_exit_notify(void) +{ + efi_runtime_services.get_variable = efi_get_variable_runtime; + efi_runtime_services.get_next_variable_name = + efi_get_next_variable_name_runtime; + efi_runtime_services.set_variable = efi_set_variable_runtime; + efi_update_table_header_crc32(&efi_runtime_services.hdr); +} + /** * efi_init_variables() - initialize variable services * diff --git a/lib/efi_loader/efi_variable_null.c b/lib/efi_loader/efi_variable_null.c index f4c96c90a3..1cbfe4d8a0 100644 --- a/lib/efi_loader/efi_variable_null.c +++ b/lib/efi_loader/efi_variable_null.c @@ -94,3 +94,20 @@ efi_query_variable_info(u32 attributes, u64 *maximum_variable_storage_size, { return EFI_UNSUPPORTED; } + +/** + * efi_variables_boot_exit_notify() - notify ExitBootServices() is called + */ +void efi_variables_boot_exit_notify(void); +{ +} + +/** + * efi_init_variables() - initialize variable services + * + * Return: status code + */ +efi_status_t efi_init_variables(void) +{ + return EFI_SUCCESS; +} -- 2.20.1

Unimplemented runtime services should always return EFI_UNSUPPORTED as described in the UEFI 2.8 spec.
Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de --- lib/efi_loader/efi_runtime.c | 34 ++-------------------------------- 1 file changed, 2 insertions(+), 32 deletions(-)
diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c index 551470b9ac..3b58b87eab 100644 --- a/lib/efi_loader/efi_runtime.c +++ b/lib/efi_loader/efi_runtime.c @@ -26,8 +26,6 @@ struct efi_runtime_mmio_list { LIST_HEAD(efi_runtime_mmio);
static efi_status_t __efi_runtime EFIAPI efi_unimplemented(void); -static efi_status_t __efi_runtime EFIAPI efi_device_error(void); -static efi_status_t __efi_runtime EFIAPI efi_invalid_parameter(void);
/* * TODO(sjg@chromium.org): These defines and structures should come from the ELF @@ -733,34 +731,6 @@ static efi_status_t __efi_runtime EFIAPI efi_unimplemented(void) return EFI_UNSUPPORTED; }
-/** - * efi_device_error() - replacement function, returns EFI_DEVICE_ERROR - * - * This function is used after SetVirtualAddressMap() is called as replacement - * for services that are not available anymore due to constraints of the U-Boot - * implementation. - * - * Return: EFI_DEVICE_ERROR - */ -static efi_status_t __efi_runtime EFIAPI efi_device_error(void) -{ - return EFI_DEVICE_ERROR; -} - -/** - * efi_invalid_parameter() - replacement function, returns EFI_INVALID_PARAMETER - * - * This function is used after SetVirtualAddressMap() is called as replacement - * for services that are not available anymore due to constraints of the U-Boot - * implementation. - * - * Return: EFI_INVALID_PARAMETER - */ -static efi_status_t __efi_runtime EFIAPI efi_invalid_parameter(void) -{ - return EFI_INVALID_PARAMETER; -} - /** * efi_update_capsule() - process information from operating system * @@ -816,11 +786,11 @@ struct efi_runtime_services __efi_runtime_data efi_runtime_services = { .get_wakeup_time = (void *)&efi_unimplemented, .set_wakeup_time = (void *)&efi_unimplemented, .set_virtual_address_map = &efi_set_virtual_address_map, - .convert_pointer = (void *)&efi_invalid_parameter, + .convert_pointer = (void *)&efi_unimplemented, .get_variable = efi_get_variable, .get_next_variable_name = efi_get_next_variable_name, .set_variable = efi_set_variable, - .get_next_high_mono_count = (void *)&efi_device_error, + .get_next_high_mono_count = (void *)&efi_unimplemented, .reset_system = &efi_reset_system_boottime, .update_capsule = efi_update_capsule, .query_capsule_caps = efi_query_capsule_caps, -- 2.20.1

Provide a unit test for the variable services at runtime.
Currently we expect EFI_UNSUPPORTED to be returned as the runtime implementation is still missing.
Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de --- lib/efi_selftest/Makefile | 1 + .../efi_selftest_variables_runtime.c | 94 +++++++++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 lib/efi_selftest/efi_selftest_variables_runtime.c
diff --git a/lib/efi_selftest/Makefile b/lib/efi_selftest/Makefile index bb3ebd7fa7..f322f8a27d 100644 --- a/lib/efi_selftest/Makefile +++ b/lib/efi_selftest/Makefile @@ -34,6 +34,7 @@ efi_selftest_textinputex.o \ efi_selftest_textoutput.o \ efi_selftest_tpl.o \ efi_selftest_util.o \ +efi_selftest_variables_runtime.o \ efi_selftest_watchdog.o
obj-$(CONFIG_EFI_DEVICE_PATH_TO_TEXT) += efi_selftest_devicepath.o diff --git a/lib/efi_selftest/efi_selftest_variables_runtime.c b/lib/efi_selftest/efi_selftest_variables_runtime.c new file mode 100644 index 0000000000..b3b40ad2cf --- /dev/null +++ b/lib/efi_selftest/efi_selftest_variables_runtime.c @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * efi_selftest_variables_runtime + * + * Copyright (c) 2019 Heinrich Schuchardt xypron.glpk@gmx.de + * + * This unit test checks the runtime services for variables after + * ExitBootServices(): + * GetVariable, GetNextVariableName, SetVariable, QueryVariableInfo. + */ + +#include <efi_selftest.h> + +#define EFI_ST_MAX_DATA_SIZE 16 +#define EFI_ST_MAX_VARNAME_SIZE 40 + +static struct efi_boot_services *boottime; +static struct efi_runtime_services *runtime; +static const efi_guid_t guid_vendor0 = + EFI_GUID(0x67029eb5, 0x0af2, 0xf6b1, + 0xda, 0x53, 0xfc, 0xb5, 0x66, 0xdd, 0x1c, 0xe6); + +/* + * Setup unit test. + * + * @handle handle of the loaded image + * @systable system table + */ +static int setup(const efi_handle_t img_handle, + const struct efi_system_table *systable) +{ + boottime = systable->boottime; + runtime = systable->runtime; + + return EFI_ST_SUCCESS; +} + +/** + * execute() - execute unit test + * + * As runtime support is not implmented expect EFI_UNSUPPORTED to be returned. + */ +static int execute(void) +{ + efi_status_t ret; + efi_uintn_t len; + u32 attr; + u8 v[16] = {0x5d, 0xd1, 0x5e, 0x51, 0x5a, 0x05, 0xc7, 0x0c, + 0x35, 0x4a, 0xae, 0x87, 0xa5, 0xdf, 0x0f, 0x65,}; + u8 data[EFI_ST_MAX_DATA_SIZE]; + u16 varname[EFI_ST_MAX_VARNAME_SIZE]; + efi_guid_t guid; + u64 max_storage, rem_storage, max_size; + + ret = runtime->query_variable_info(EFI_VARIABLE_BOOTSERVICE_ACCESS, + &max_storage, &rem_storage, + &max_size); + if (ret != EFI_UNSUPPORTED) { + efi_st_error("QueryVariableInfo failed\n"); + return EFI_ST_FAILURE; + } + + ret = runtime->set_variable(L"efi_st_var0", &guid_vendor0, + 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; + } + len = 3; + ret = runtime->get_variable(L"efi_st_var0", &guid_vendor0, + &attr, &len, data); + if (ret != EFI_UNSUPPORTED) { + efi_st_error("GetVariable failed\n"); + return EFI_ST_FAILURE; + } + memset(&guid, 0, 16); + *varname = 0; + ret = runtime->get_next_variable_name(&len, varname, &guid); + if (ret != EFI_UNSUPPORTED) { + efi_st_error("GetNextVariableName failed\n"); + return EFI_ST_FAILURE; + } + + return EFI_ST_SUCCESS; +} + +EFI_UNIT_TEST(variables_run) = { + .name = "variables at runtime", + .phase = EFI_SETUP_BEFORE_BOOTTIME_EXIT, + .setup = setup, + .execute = execute, +}; -- 2.20.1

Currently our UEFI variables are stored in U-Boot variables. Runtime support for variables is missing. This patch series prepares for alternative implementations of the variable services.
* QueryVariableInfo() is moved into the same file as the other variable services. * For booting via GRUB into Linux UEFI variables are not necessary. An alternative implementation of the variable services is provided which only returns EFI_UNSUPPORTED. * The detaching of runtime services is changed to allow the move to ExitBootServices(). * The detaching of variable services is moved to ExitBootServices(). * Unimplemented runtime services must return EFI_UNIMPLEMENTED. * Provide a unit test for variable services at runtime.
Heinrich Schuchardt (7): efi_loader: move efi_query_variable_info() efi_debug: make variable support customizable efi_loader: initialization of variable services efi_loader: clean up runtime detaching efi_loader: let the variable driver patch out the runtime efi_loader: unimplemented runtime services efi_selftest: test variable services at runtime
include/efi_loader.h | 9 ++ lib/efi_loader/Kconfig | 18 +++ lib/efi_loader/Makefile | 3 +- lib/efi_loader/efi_boottime.c | 3 +- lib/efi_loader/efi_runtime.c | 148 +++++++----------- lib/efi_loader/efi_setup.c | 14 +- lib/efi_loader/efi_variable.c | 81 ++++++++++ lib/efi_loader/efi_variable_null.c | 113 +++++++++++++ lib/efi_selftest/Makefile | 6 +- .../efi_selftest_variables_runtime.c | 94 +++++++++++ 10 files changed, 394 insertions(+), 95 deletions(-) create mode 100644 lib/efi_loader/efi_variable_null.c create mode 100644 lib/efi_selftest/efi_selftest_variables_runtime.c
-- 2.20.1

Let's keep similar things together.
Move efi_query_variable_info() to lib/efi_loader/efi_variable.c
Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de --- include/efi_loader.h | 5 +++++ lib/efi_loader/efi_runtime.c | 27 --------------------------- lib/efi_loader/efi_variable.c | 27 +++++++++++++++++++++++++++ 3 files changed, 32 insertions(+), 27 deletions(-)
diff --git a/include/efi_loader.h b/include/efi_loader.h index b07155cecb..de1e67fd40 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -618,6 +618,11 @@ efi_status_t EFIAPI efi_set_variable(u16 *variable_name, const efi_guid_t *vendor, u32 attributes, efi_uintn_t data_size, const void *data);
+efi_status_t EFIAPI efi_query_variable_info( + u32 attributes, u64 *maximum_variable_storage_size, + u64 *remaining_variable_storage_size, + u64 *maximum_variable_size); + /* * See section 3.1.3 in the v2.7 UEFI spec for more details on * the layout of EFI_LOAD_OPTION. In short it is: diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c index 40fdc0ea92..dd91880ad6 100644 --- a/lib/efi_loader/efi_runtime.c +++ b/lib/efi_loader/efi_runtime.c @@ -782,33 +782,6 @@ efi_status_t __efi_runtime EFIAPI efi_query_capsule_caps( return EFI_UNSUPPORTED; }
-/** - * efi_query_variable_info() - get information about EFI variables - * - * This function implements the QueryVariableInfo() runtime service. - * - * See the Unified Extensible Firmware Interface (UEFI) specification for - * details. - * - * @attributes: bitmask to select variables to be - * queried - * @maximum_variable_storage_size: maximum size of storage area for the - * selected variable types - * @remaining_variable_storage_size: remaining size of storage are for the - * selected variable types - * @maximum_variable_size: maximum size of a variable of the - * selected type - * Returns: status code - */ -efi_status_t __efi_runtime EFIAPI efi_query_variable_info( - u32 attributes, - u64 *maximum_variable_storage_size, - u64 *remaining_variable_storage_size, - u64 *maximum_variable_size) -{ - return EFI_UNSUPPORTED; -} - struct efi_runtime_services __efi_runtime_data efi_runtime_services = { .hdr = { .signature = EFI_RUNTIME_SERVICES_SIGNATURE, diff --git a/lib/efi_loader/efi_variable.c b/lib/efi_loader/efi_variable.c index d6b75ca02e..f71dc29ee9 100644 --- a/lib/efi_loader/efi_variable.c +++ b/lib/efi_loader/efi_variable.c @@ -520,3 +520,30 @@ out:
return EFI_EXIT(ret); } + +/** + * efi_query_variable_info() - get information about EFI variables + * + * This function implements the QueryVariableInfo() runtime service. + * + * See the Unified Extensible Firmware Interface (UEFI) specification for + * details. + * + * @attributes: bitmask to select variables to be + * queried + * @maximum_variable_storage_size: maximum size of storage area for the + * selected variable types + * @remaining_variable_storage_size: remaining size of storage are for the + * selected variable types + * @maximum_variable_size: maximum size of a variable of the + * selected type + * Returns: status code + */ +efi_status_t __efi_runtime EFIAPI efi_query_variable_info( + u32 attributes, + u64 *maximum_variable_storage_size, + u64 *remaining_variable_storage_size, + u64 *maximum_variable_size) +{ + return EFI_UNSUPPORTED; +} -- 2.20.1

Provide a customization option to define how UEFI variables are implemented.
This patch provides a dummy implementation without any variable support and provides the choice between this dummy and the legacy implementation.
Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de --- lib/efi_loader/Kconfig | 18 ++++++ lib/efi_loader/Makefile | 3 +- lib/efi_loader/efi_setup.c | 10 +++- lib/efi_loader/efi_variable_null.c | 96 ++++++++++++++++++++++++++++++ lib/efi_selftest/Makefile | 5 +- 5 files changed, 128 insertions(+), 4 deletions(-) create mode 100644 lib/efi_loader/efi_variable_null.c
diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index cd5436c576..97c2ca9820 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -34,6 +34,24 @@ config EFI_SET_TIME Provide the SetTime() runtime service at boottime. This service can be used by an EFI application to adjust the real time clock.
+choice + prompt "Variable storage" + default EFI_VARIABLES_UBOOT + help + Define where UEFI variables are stored. + +config EFI_VARIABLES_UBOOT + bool "U-Boot environment" + help + UEFI variables are stored in the U-Boot environment. + +config EFI_VARIABLES_NULL + bool "UEFI variables are not available" + help + UEFI variables are not supported. + +endchoice + config EFI_DEVICE_PATH_TO_TEXT bool "Device path to text protocol" default y diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile index 01769ea58b..e244aee2f5 100644 --- a/lib/efi_loader/Makefile +++ b/lib/efi_loader/Makefile @@ -31,7 +31,8 @@ obj-y += efi_root_node.o obj-y += efi_runtime.o obj-y += efi_setup.o obj-$(CONFIG_EFI_UNICODE_COLLATION_PROTOCOL2) += efi_unicode_collation.o -obj-y += efi_variable.o +obj-$(CONFIG_EFI_VARIABLES_UBOOT) += efi_variable.o +obj-$(CONFIG_EFI_VARIABLES_NULL) += efi_variable_null.o obj-y += efi_watchdog.o obj-$(CONFIG_LCD) += efi_gop.o obj-$(CONFIG_DM_VIDEO) += efi_gop.o diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c index bfb57836fa..a3f59506d8 100644 --- a/lib/efi_loader/efi_setup.c +++ b/lib/efi_loader/efi_setup.c @@ -22,6 +22,8 @@ void __weak allow_unaligned(void) { }
+#ifndef CONFIG_EFI_VARIABLES_NULL + /** * efi_init_platform_lang() - define supported languages * @@ -82,6 +84,8 @@ out: return ret; }
+#endif + /** * efi_init_obj_list() - Initialize and populate EFI object list * @@ -89,7 +93,9 @@ out: */ efi_status_t efi_init_obj_list(void) { +#ifndef CONFIG_EFI_VARIABLES_NULL u64 os_indications_supported = 0; /* None */ +#endif efi_status_t ret = EFI_SUCCESS;
/* Initialize once only */ @@ -101,7 +107,7 @@ efi_status_t efi_init_obj_list(void)
/* On ARM switch from EL3 or secure mode to EL2 or non-secure mode */ switch_to_non_secure_mode(); - +#ifndef CONFIG_EFI_VARIABLES_NULL /* Define supported languages */ ret = efi_init_platform_lang(); if (ret != EFI_SUCCESS) @@ -121,7 +127,7 @@ efi_status_t efi_init_obj_list(void) ret = efi_init_runtime_supported(); if (ret != EFI_SUCCESS) goto out; - +#endif /* Initialize system table */ ret = efi_initialize_system_table(); if (ret != EFI_SUCCESS) diff --git a/lib/efi_loader/efi_variable_null.c b/lib/efi_loader/efi_variable_null.c new file mode 100644 index 0000000000..f4c96c90a3 --- /dev/null +++ b/lib/efi_loader/efi_variable_null.c @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Dummy driver for UEFI variables. None of the variable services is supported. + * + * Copyright (c) 2019 Heinrich Schuchardt + */ + +#include <efi_loader.h> + +/** + * efi_get_variable() - retrieve value of a UEFI variable + * + * This function implements the GetVariable runtime service. + * + * See the Unified Extensible Firmware Interface (UEFI) specification for + * details. + * + * @variable_name: name of the variable + * @vendor: vendor GUID + * @attributes: attributes of the variable + * @data_size: size of the buffer to which the variable value is copied + * @data: buffer to which the variable value is copied + * Return: status code + */ +efi_status_t __efi_runtime EFIAPI +efi_get_variable(u16 *variable_name, const efi_guid_t *vendor, u32 *attributes, + efi_uintn_t *data_size, void *data) +{ + return EFI_UNSUPPORTED; +} + +/** + * efi_get_next_variable_name() - enumerate the current variable names + * @variable_name_size: size of variable_name buffer in byte + * @variable_name: name of uefi variable's name in u16 + * @vendor: vendor's guid + * + * This function implements the GetNextVariableName service. + * + * See the Unified Extensible Firmware Interface (UEFI) specification for + * details: http://wiki.phoenix.com/wiki/index.php/ + * EFI_RUNTIME_SERVICES#GetNextVariableName.28.29 + * + * Return: status code + */ +efi_status_t __efi_runtime EFIAPI +efi_get_next_variable_name(efi_uintn_t *variable_name_size, u16 *variable_name, + const efi_guid_t *vendor) +{ + return EFI_UNSUPPORTED; +} + +/** + * efi_set_variable() - set value of a UEFI variable + * + * This function implements the SetVariable runtime service. + * + * See the Unified Extensible Firmware Interface (UEFI) specification for + * details. + * + * @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 + */ +efi_status_t __efi_runtime EFIAPI +efi_set_variable(u16 *variable_name, const efi_guid_t *vendor, u32 attributes, + efi_uintn_t data_size, const void *data) +{ + return EFI_UNSUPPORTED; +} + +/** + * efi_query_variable_info() - query information the variable store + * + * This function implements the QueryVariableInfo runtime service. + * + * See the Unified Extensible Firmware Interface (UEFI) specification for + * details. + * + * @attributes: type of variables for which info shall + * be provided + * @maximum_variable_storage_size: total available storage for variables + * @remaining_variable_storage_size: remaining available storage + * @maximum_variable_size: maximum size of a variable + * Return: status code + */ +efi_status_t __efi_runtime EFIAPI +efi_query_variable_info(u32 attributes, u64 *maximum_variable_storage_size, + u64 *remaining_variable_storage_size, + u64 *maximum_variable_size) +{ + return EFI_UNSUPPORTED; +} diff --git a/lib/efi_selftest/Makefile b/lib/efi_selftest/Makefile index 3bebd0f573..bb3ebd7fa7 100644 --- a/lib/efi_selftest/Makefile +++ b/lib/efi_selftest/Makefile @@ -34,7 +34,6 @@ efi_selftest_textinputex.o \ efi_selftest_textoutput.o \ efi_selftest_tpl.o \ efi_selftest_util.o \ -efi_selftest_variables.o \ efi_selftest_watchdog.o
obj-$(CONFIG_EFI_DEVICE_PATH_TO_TEXT) += efi_selftest_devicepath.o @@ -45,6 +44,10 @@ obj-$(CONFIG_CPU_V7) += efi_selftest_unaligned.o obj-$(CONFIG_EFI_LOADER_HII) += efi_selftest_hii.o obj-$(CONFIG_EFI_GET_TIME) += efi_selftest_rtc.o
+ifeq ($(CONFIG_EFI_VARIABLES_NULL),) +obj-y += efi_selftest_variables.o +endif + ifeq ($(CONFIG_GENERATE_ACPI_TABLE),) obj-y += efi_selftest_fdt.o endif -- 2.20.1

Provide an initialization routine for variable services.
Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de --- include/efi_loader.h | 2 ++ lib/efi_loader/efi_setup.c | 6 ++++++ lib/efi_loader/efi_variable.c | 10 ++++++++++ 3 files changed, 18 insertions(+)
diff --git a/include/efi_loader.h b/include/efi_loader.h index de1e67fd40..d30c4e8ef3 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -325,6 +325,8 @@ extern struct list_head efi_register_notify_events;
/* Initialize efi execution environment */ efi_status_t efi_init_obj_list(void); +/* Initialize variable services */ +efi_status_t efi_init_variables(void); /* Called by bootefi to initialize root node */ efi_status_t efi_root_node_register(void); /* Called by bootefi to initialize runtime */ diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c index a3f59506d8..d73a3dd706 100644 --- a/lib/efi_loader/efi_setup.c +++ b/lib/efi_loader/efi_setup.c @@ -108,6 +108,12 @@ efi_status_t efi_init_obj_list(void) /* On ARM switch from EL3 or secure mode to EL2 or non-secure mode */ switch_to_non_secure_mode(); #ifndef CONFIG_EFI_VARIABLES_NULL + + /* Initialize variable services */ + ret = efi_init_variables(); + if (ret != EFI_SUCCESS) + goto out; + /* Define supported languages */ ret = efi_init_platform_lang(); if (ret != EFI_SUCCESS) diff --git a/lib/efi_loader/efi_variable.c b/lib/efi_loader/efi_variable.c index f71dc29ee9..6210425f5e 100644 --- a/lib/efi_loader/efi_variable.c +++ b/lib/efi_loader/efi_variable.c @@ -547,3 +547,13 @@ efi_status_t __efi_runtime EFIAPI efi_query_variable_info( { return EFI_UNSUPPORTED; } + +/** + * efi_init_variables() - initialize variable services + * + * Return: status code + */ +efi_status_t efi_init_variables(void) +{ + return EFI_SUCCESS; +} -- 2.20.1

The detaching of the runtime will have to move to ExitBootServices() to encompass operating system that do not call SetVirtualAddressMap().
This patch changes the logic for the relocation of the pointers in the runtime table such that the relocation becomes independent of the entries in the detach list.
Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de --- lib/efi_loader/efi_runtime.c | 78 +++++++++++++++++++++++++----------- 1 file changed, 55 insertions(+), 23 deletions(-)
diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c index dd91880ad6..1c8f9b8218 100644 --- a/lib/efi_loader/efi_runtime.c +++ b/lib/efi_loader/efi_runtime.c @@ -436,29 +436,53 @@ static const struct efi_runtime_detach_list_struct efi_runtime_detach_list[] = { } };
-static bool efi_runtime_tobedetached(void *p) +/** + * efi_is_runtime_service_pointer() - check if pointer points to runtime table + * + * @p: pointer to check + * Return: true if the pointer points to a service function pointer in the + * runtime table + */ +static bool efi_is_runtime_service_pointer(void *p) { - int i; - - for (i = 0; i < ARRAY_SIZE(efi_runtime_detach_list); i++) - if (efi_runtime_detach_list[i].ptr == p) - return true; - - return false; + return p >= (void *)&efi_runtime_services.get_time && + p <= (void *)&efi_runtime_services.query_variable_info; }
-static void efi_runtime_detach(ulong offset) +static __efi_runtime void efi_runtime_detach(void) { int i; - ulong patchoff = offset - (ulong)gd->relocaddr;
+ /* + * Replace boottime functions by runtime functions + * TODO: move this step to ExitBootServices() + */ for (i = 0; i < ARRAY_SIZE(efi_runtime_detach_list); i++) { ulong patchto = (ulong)efi_runtime_detach_list[i].patchto; ulong *p = efi_runtime_detach_list[i].ptr; - ulong newaddr = patchto ? (patchto + patchoff) : 0;
- debug("%s: Setting %p to %lx\n", __func__, p, newaddr); - *p = newaddr; + debug("%s: Setting %p to %lx\n", __func__, p, patchto); + *p = patchto; + } +} + +static __efi_runtime void efi_relocate_runtime_table(ulong offset) +{ + ulong patchoff; + void **pos; + + /* Relocate the runtime services pointers */ + patchoff = offset - gd->relocaddr; + for (pos = (void **)&efi_runtime_services.get_time; + pos <= (void **)&efi_runtime_services.query_variable_info; ++pos) { + /* + * The UEFI spec requires not to update VirtualAddressMap() + * and ConvertPointer(). + */ + if (*pos && pos != + (void **)&efi_runtime_services.set_virtual_address_map && + pos != (void **)&efi_runtime_services.convert_pointer) + *pos += patchoff; }
/* Update CRC32 */ @@ -483,6 +507,10 @@ void efi_runtime_relocate(ulong offset, struct efi_mem_desc *map)
p = (void*)((ulong)rel->offset - base) + gd->relocaddr;
+ /* The runtime services are updated in efi_runtime_detach() */ + if (map && efi_is_runtime_service_pointer(p)) + continue; + debug("%s: rel->info=%#lx *p=%#lx rel->offset=%p\n", __func__, rel->info, *p, rel->offset);
@@ -506,9 +534,8 @@ void efi_runtime_relocate(ulong offset, struct efi_mem_desc *map) } #endif default: - if (!efi_runtime_tobedetached(p)) - printf("%s: Unknown relocation type %llx\n", - __func__, rel->info & R_MASK); + printf("%s: Unknown relocation type %llx\n", + __func__, rel->info & R_MASK); continue; }
@@ -516,9 +543,8 @@ void efi_runtime_relocate(ulong offset, struct efi_mem_desc *map) if (map && ((newaddr < map->virtual_start) || newaddr > (map->virtual_start + (map->num_pages << EFI_PAGE_SHIFT)))) { - if (!efi_runtime_tobedetached(p)) - printf("%s: Relocation at %p is out of " - "range (%lx)\n", __func__, p, newaddr); + printf("%s: Relocation at %p is out of range (%lx)\n", + __func__, p, newaddr); continue; }
@@ -623,7 +649,15 @@ static efi_status_t EFIAPI efi_set_virtual_address_map( } }
- /* Move the actual runtime code over */ + /* + * Some runtime services are implemented in a way that we can only offer + * them at boottime. Replace those function pointers. + * + * TODO: move this call to ExitBootServices(). + */ + efi_runtime_detach(); + + /* Relocate the runtime. See TODO above */ for (i = 0; i < n; i++) { struct efi_mem_desc *map;
@@ -632,10 +666,8 @@ static efi_status_t EFIAPI efi_set_virtual_address_map( ulong new_offset = map->virtual_start - map->physical_start + gd->relocaddr;
+ efi_relocate_runtime_table(new_offset); efi_runtime_relocate(new_offset, map); - /* Once we're virtual, we can no longer handle - complex callbacks */ - efi_runtime_detach(new_offset); return EFI_EXIT(EFI_SUCCESS); } } -- 2.20.1

Our variable services are only provided at boottime. Therefore when leaving boottime the variable function are replaced by dummy functions returning EFI_UNSUPPORTED. Move this patching of the runtime table to the variable services implementation. Executed it in ExitBootServices().
Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de --- include/efi_loader.h | 2 ++ lib/efi_loader/efi_boottime.c | 3 +- lib/efi_loader/efi_runtime.c | 9 ------ lib/efi_loader/efi_variable.c | 44 ++++++++++++++++++++++++++++++ lib/efi_loader/efi_variable_null.c | 17 ++++++++++++ 5 files changed, 65 insertions(+), 10 deletions(-)
diff --git a/include/efi_loader.h b/include/efi_loader.h index d30c4e8ef3..8d75dde569 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -327,6 +327,8 @@ extern struct list_head efi_register_notify_events; efi_status_t efi_init_obj_list(void); /* Initialize variable services */ efi_status_t efi_init_variables(void); +/* Notify ExitBootServices() is called */ +void efi_variables_boot_exit_notify(void); /* Called by bootefi to initialize root node */ efi_status_t efi_root_node_register(void); /* Called by bootefi to initialize runtime */ diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index d104cc6b31..a929bbb671 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -1968,7 +1968,8 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, /* Make sure that notification functions are not called anymore */ efi_tpl = TPL_HIGH_LEVEL;
- /* TODO: Should persist EFI variables here */ + /* Notify variable services */ + efi_variables_boot_exit_notify();
board_quiesce_devices();
diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c index 1c8f9b8218..551470b9ac 100644 --- a/lib/efi_loader/efi_runtime.c +++ b/lib/efi_loader/efi_runtime.c @@ -424,15 +424,6 @@ static const struct efi_runtime_detach_list_struct efi_runtime_detach_list[] = { /* Clean up system table */ .ptr = &systab.boottime, .patchto = NULL, - }, { - .ptr = &efi_runtime_services.get_variable, - .patchto = &efi_device_error, - }, { - .ptr = &efi_runtime_services.get_next_variable_name, - .patchto = &efi_device_error, - }, { - .ptr = &efi_runtime_services.set_variable, - .patchto = &efi_device_error, } };
diff --git a/lib/efi_loader/efi_variable.c b/lib/efi_loader/efi_variable.c index 6210425f5e..bc8ed678c9 100644 --- a/lib/efi_loader/efi_variable.c +++ b/lib/efi_loader/efi_variable.c @@ -548,6 +548,50 @@ efi_status_t __efi_runtime EFIAPI efi_query_variable_info( return EFI_UNSUPPORTED; }
+/** + * efi_get_variable_runtime() - runtime implementation of GetVariable() + */ +static efi_status_t __efi_runtime EFIAPI +efi_get_variable_runtime(u16 *variable_name, const efi_guid_t *vendor, + u32 *attributes, efi_uintn_t *data_size, void *data) +{ + return EFI_UNSUPPORTED; +} + +/** + * efi_get_next_variable_name_runtime() - runtime implementation of + * GetNextVariable() + */ +static efi_status_t __efi_runtime EFIAPI +efi_get_next_variable_name_runtime(efi_uintn_t *variable_name_size, + u16 *variable_name, const efi_guid_t *vendor) +{ + return EFI_UNSUPPORTED; +} + +/** + * efi_set_variable_runtime() - runtime implementation of SetVariable() + */ +static efi_status_t __efi_runtime EFIAPI +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; +} + +/** + * efi_variables_boot_exit_notify() - notify ExitBootServices() is called + */ +void efi_variables_boot_exit_notify(void) +{ + efi_runtime_services.get_variable = efi_get_variable_runtime; + efi_runtime_services.get_next_variable_name = + efi_get_next_variable_name_runtime; + efi_runtime_services.set_variable = efi_set_variable_runtime; + efi_update_table_header_crc32(&efi_runtime_services.hdr); +} + /** * efi_init_variables() - initialize variable services * diff --git a/lib/efi_loader/efi_variable_null.c b/lib/efi_loader/efi_variable_null.c index f4c96c90a3..1cbfe4d8a0 100644 --- a/lib/efi_loader/efi_variable_null.c +++ b/lib/efi_loader/efi_variable_null.c @@ -94,3 +94,20 @@ efi_query_variable_info(u32 attributes, u64 *maximum_variable_storage_size, { return EFI_UNSUPPORTED; } + +/** + * efi_variables_boot_exit_notify() - notify ExitBootServices() is called + */ +void efi_variables_boot_exit_notify(void); +{ +} + +/** + * efi_init_variables() - initialize variable services + * + * Return: status code + */ +efi_status_t efi_init_variables(void) +{ + return EFI_SUCCESS; +} -- 2.20.1

Unimplemented runtime services should always return EFI_UNSUPPORTED as described in the UEFI 2.8 spec.
Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de --- lib/efi_loader/efi_runtime.c | 34 ++-------------------------------- 1 file changed, 2 insertions(+), 32 deletions(-)
diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c index 551470b9ac..3b58b87eab 100644 --- a/lib/efi_loader/efi_runtime.c +++ b/lib/efi_loader/efi_runtime.c @@ -26,8 +26,6 @@ struct efi_runtime_mmio_list { LIST_HEAD(efi_runtime_mmio);
static efi_status_t __efi_runtime EFIAPI efi_unimplemented(void); -static efi_status_t __efi_runtime EFIAPI efi_device_error(void); -static efi_status_t __efi_runtime EFIAPI efi_invalid_parameter(void);
/* * TODO(sjg@chromium.org): These defines and structures should come from the ELF @@ -733,34 +731,6 @@ static efi_status_t __efi_runtime EFIAPI efi_unimplemented(void) return EFI_UNSUPPORTED; }
-/** - * efi_device_error() - replacement function, returns EFI_DEVICE_ERROR - * - * This function is used after SetVirtualAddressMap() is called as replacement - * for services that are not available anymore due to constraints of the U-Boot - * implementation. - * - * Return: EFI_DEVICE_ERROR - */ -static efi_status_t __efi_runtime EFIAPI efi_device_error(void) -{ - return EFI_DEVICE_ERROR; -} - -/** - * efi_invalid_parameter() - replacement function, returns EFI_INVALID_PARAMETER - * - * This function is used after SetVirtualAddressMap() is called as replacement - * for services that are not available anymore due to constraints of the U-Boot - * implementation. - * - * Return: EFI_INVALID_PARAMETER - */ -static efi_status_t __efi_runtime EFIAPI efi_invalid_parameter(void) -{ - return EFI_INVALID_PARAMETER; -} - /** * efi_update_capsule() - process information from operating system * @@ -816,11 +786,11 @@ struct efi_runtime_services __efi_runtime_data efi_runtime_services = { .get_wakeup_time = (void *)&efi_unimplemented, .set_wakeup_time = (void *)&efi_unimplemented, .set_virtual_address_map = &efi_set_virtual_address_map, - .convert_pointer = (void *)&efi_invalid_parameter, + .convert_pointer = (void *)&efi_unimplemented, .get_variable = efi_get_variable, .get_next_variable_name = efi_get_next_variable_name, .set_variable = efi_set_variable, - .get_next_high_mono_count = (void *)&efi_device_error, + .get_next_high_mono_count = (void *)&efi_unimplemented, .reset_system = &efi_reset_system_boottime, .update_capsule = efi_update_capsule, .query_capsule_caps = efi_query_capsule_caps, -- 2.20.1

Provide a unit test for the variable services at runtime.
Currently we expect EFI_UNSUPPORTED to be returned as the runtime implementation is still missing.
Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de --- lib/efi_selftest/Makefile | 1 + .../efi_selftest_variables_runtime.c | 94 +++++++++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 lib/efi_selftest/efi_selftest_variables_runtime.c
diff --git a/lib/efi_selftest/Makefile b/lib/efi_selftest/Makefile index bb3ebd7fa7..f322f8a27d 100644 --- a/lib/efi_selftest/Makefile +++ b/lib/efi_selftest/Makefile @@ -34,6 +34,7 @@ efi_selftest_textinputex.o \ efi_selftest_textoutput.o \ efi_selftest_tpl.o \ efi_selftest_util.o \ +efi_selftest_variables_runtime.o \ efi_selftest_watchdog.o
obj-$(CONFIG_EFI_DEVICE_PATH_TO_TEXT) += efi_selftest_devicepath.o diff --git a/lib/efi_selftest/efi_selftest_variables_runtime.c b/lib/efi_selftest/efi_selftest_variables_runtime.c new file mode 100644 index 0000000000..b3b40ad2cf --- /dev/null +++ b/lib/efi_selftest/efi_selftest_variables_runtime.c @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * efi_selftest_variables_runtime + * + * Copyright (c) 2019 Heinrich Schuchardt xypron.glpk@gmx.de + * + * This unit test checks the runtime services for variables after + * ExitBootServices(): + * GetVariable, GetNextVariableName, SetVariable, QueryVariableInfo. + */ + +#include <efi_selftest.h> + +#define EFI_ST_MAX_DATA_SIZE 16 +#define EFI_ST_MAX_VARNAME_SIZE 40 + +static struct efi_boot_services *boottime; +static struct efi_runtime_services *runtime; +static const efi_guid_t guid_vendor0 = + EFI_GUID(0x67029eb5, 0x0af2, 0xf6b1, + 0xda, 0x53, 0xfc, 0xb5, 0x66, 0xdd, 0x1c, 0xe6); + +/* + * Setup unit test. + * + * @handle handle of the loaded image + * @systable system table + */ +static int setup(const efi_handle_t img_handle, + const struct efi_system_table *systable) +{ + boottime = systable->boottime; + runtime = systable->runtime; + + return EFI_ST_SUCCESS; +} + +/** + * execute() - execute unit test + * + * As runtime support is not implmented expect EFI_UNSUPPORTED to be returned. + */ +static int execute(void) +{ + efi_status_t ret; + efi_uintn_t len; + u32 attr; + u8 v[16] = {0x5d, 0xd1, 0x5e, 0x51, 0x5a, 0x05, 0xc7, 0x0c, + 0x35, 0x4a, 0xae, 0x87, 0xa5, 0xdf, 0x0f, 0x65,}; + u8 data[EFI_ST_MAX_DATA_SIZE]; + u16 varname[EFI_ST_MAX_VARNAME_SIZE]; + efi_guid_t guid; + u64 max_storage, rem_storage, max_size; + + ret = runtime->query_variable_info(EFI_VARIABLE_BOOTSERVICE_ACCESS, + &max_storage, &rem_storage, + &max_size); + if (ret != EFI_UNSUPPORTED) { + efi_st_error("QueryVariableInfo failed\n"); + return EFI_ST_FAILURE; + } + + ret = runtime->set_variable(L"efi_st_var0", &guid_vendor0, + 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; + } + len = 3; + ret = runtime->get_variable(L"efi_st_var0", &guid_vendor0, + &attr, &len, data); + if (ret != EFI_UNSUPPORTED) { + efi_st_error("GetVariable failed\n"); + return EFI_ST_FAILURE; + } + memset(&guid, 0, 16); + *varname = 0; + ret = runtime->get_next_variable_name(&len, varname, &guid); + if (ret != EFI_UNSUPPORTED) { + efi_st_error("GetNextVariableName failed\n"); + return EFI_ST_FAILURE; + } + + return EFI_ST_SUCCESS; +} + +EFI_UNIT_TEST(variables_run) = { + .name = "variables at runtime", + .phase = EFI_SETUP_BEFORE_BOOTTIME_EXIT, + .setup = setup, + .execute = execute, +}; -- 2.20.1
participants (1)
-
Heinrich Schuchardt