diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/efi_loader/Kconfig | 6 | ||||
-rw-r--r-- | lib/efi_loader/efi_boottime.c | 27 | ||||
-rw-r--r-- | lib/efi_loader/efi_runtime.c | 244 | ||||
-rw-r--r-- | lib/efi_loader/efi_setup.c | 5 | ||||
-rw-r--r-- | lib/efi_loader/efi_variable.c | 81 | ||||
-rw-r--r-- | lib/efi_selftest/Kconfig | 8 | ||||
-rw-r--r-- | lib/efi_selftest/Makefile | 1 | ||||
-rw-r--r-- | lib/efi_selftest/efi_selftest_variables_runtime.c | 94 |
8 files changed, 316 insertions, 150 deletions
diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index cd5436c576..a7f2c68fa9 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -101,4 +101,10 @@ config EFI_PLATFORM_LANG_CODES RFC 4646 format, e.g. "en-US;de-DE". The first language code is used to initialize the PlatformLang variable. +config EFI_HAVE_RUNTIME_RESET + # bool "Reset runtime service is available" + bool + default y + depends on ARCH_BCM283X || FSL_LAYERSCAPE || PSCI_RESET || SYSRESET_X86 + endif diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index d104cc6b31..c2f89805c7 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -1,8 +1,8 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * EFI application boot time services + * EFI application boot time services * - * Copyright (c) 2016 Alexander Graf + * Copyright (c) 2016 Alexander Graf */ #include <common.h> @@ -1968,10 +1968,14 @@ 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(); + /* Patch out unsupported runtime function */ + efi_runtime_detach(); + /* Fix up caches for EFI payloads if necessary */ efi_exit_caches(); @@ -3234,7 +3238,7 @@ static efi_status_t efi_connect_single_controller( if (r != EFI_SUCCESS) return r; - /* Context Override */ + /* Context Override */ if (driver_image_handle) { for (; *driver_image_handle; ++driver_image_handle) { for (i = 0; i < count; ++i) { @@ -3341,7 +3345,7 @@ static efi_status_t EFIAPI efi_connect_controller( } } } - /* Check for child controller specified by end node */ + /* Check for child controller specified by end node */ if (ret != EFI_SUCCESS && remain_device_path && remain_device_path->type == DEVICE_PATH_TYPE_END) ret = EFI_SUCCESS; @@ -3620,11 +3624,7 @@ struct efi_system_table __efi_runtime_data systab = { }, .fw_vendor = firmware_vendor, .fw_revision = FW_VERSION << 16 | FW_PATCHLEVEL << 8, - .con_in = &efi_con_in, - .con_out = &efi_con_out, - .std_err = &efi_con_out, .runtime = &efi_runtime_services, - .boottime = &efi_boot_services, .nr_tables = 0, .tables = NULL, }; @@ -3644,6 +3644,15 @@ efi_status_t efi_initialize_system_table(void) sizeof(struct efi_configuration_table), (void **)&systab.tables); + /* + * These entries will be set to NULL in ExitBootServices(). To avoid + * relocation in SetVirtualAddressMap(), set them dynamically. + */ + systab.con_in = &efi_con_in; + systab.con_out = &efi_con_out; + systab.std_err = &efi_con_out; + systab.boottime = &efi_boot_services; + /* Set CRC32 field in table headers */ efi_update_table_header_crc32(&systab.hdr); efi_update_table_header_crc32(&efi_runtime_services.hdr); diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c index 40fdc0ea92..7a64dd42ca 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 @@ -97,10 +95,7 @@ efi_status_t efi_init_runtime_supported(void) * This value must be synced with efi_runtime_detach_list * as well as efi_runtime_services. */ -#if CONFIG_IS_ENABLED(ARCH_BCM283X) || \ - CONFIG_IS_ENABLED(FSL_LAYERSCAPE) || \ - CONFIG_IS_ENABLED(SYSRESET_X86) || \ - CONFIG_IS_ENABLED(PSCI_RESET) +#ifdef CONFIG_EFI_HAVE_RUNTIME_RESET efi_runtime_services_supported |= EFI_RT_SUPPORTED_RESET_SYSTEM; #endif efi_runtime_services_supported |= @@ -387,80 +382,102 @@ efi_status_t __weak __efi_runtime EFIAPI efi_set_time(struct efi_time *time) return EFI_UNSUPPORTED; } -struct efi_runtime_detach_list_struct { - void *ptr; - void *patchto; -}; - -static const struct efi_runtime_detach_list_struct efi_runtime_detach_list[] = { - { - /* do_reset is gone */ - .ptr = &efi_runtime_services.reset_system, - .patchto = efi_reset_system, - }, { - /* invalidate_*cache_all are gone */ - .ptr = &efi_runtime_services.set_virtual_address_map, - .patchto = &efi_unimplemented, - }, { - /* RTC accessors are gone */ - .ptr = &efi_runtime_services.get_time, - .patchto = &efi_get_time, - }, { - .ptr = &efi_runtime_services.set_time, - .patchto = &efi_set_time, - }, { - /* Clean up system table */ - .ptr = &systab.con_in, - .patchto = NULL, - }, { - /* Clean up system table */ - .ptr = &systab.con_out, - .patchto = NULL, - }, { - /* Clean up system table */ - .ptr = &systab.std_err, - .patchto = NULL, - }, { - /* 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, - } -}; - -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; + return p >= (void *)&efi_runtime_services.get_time && + p <= (void *)&efi_runtime_services.query_variable_info; +} - for (i = 0; i < ARRAY_SIZE(efi_runtime_detach_list); i++) - if (efi_runtime_detach_list[i].ptr == p) - return true; +/** + * efi_runtime_detach() - detach unimplemented runtime functions + */ +void efi_runtime_detach(void) +{ + efi_runtime_services.reset_system = efi_reset_system; + efi_runtime_services.get_time = efi_get_time; + efi_runtime_services.set_time = efi_set_time; - return false; + /* Update CRC32 */ + efi_update_table_header_crc32(&efi_runtime_services.hdr); } -static void efi_runtime_detach(ulong offset) +/** + * efi_set_virtual_address_map_runtime() - change from physical to virtual + * mapping + * + * This function implements the SetVirtualAddressMap() runtime service after + * it is first called. + * + * See the Unified Extensible Firmware Interface (UEFI) specification for + * details. + * + * @memory_map_size: size of the virtual map + * @descriptor_size: size of an entry in the map + * @descriptor_version: version of the map entries + * @virtmap: virtual address mapping information + * Return: status code EFI_UNSUPPORTED + */ +static efi_status_t EFIAPI efi_set_virtual_address_map_runtime( + unsigned long memory_map_size, + unsigned long descriptor_size, + uint32_t descriptor_version, + struct efi_mem_desc *virtmap) { - int i; - ulong patchoff = offset - (ulong)gd->relocaddr; + return EFI_UNSUPPORTED; +} - 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; +/** + * efi_convert_pointer_runtime() - convert from physical to virtual pointer + * + * This function implements the ConvertPointer() runtime service after + * the first call to SetVirtualAddressMap(). + * + * See the Unified Extensible Firmware Interface (UEFI) specification for + * details. + * + * @debug_disposition: indicates if pointer may be converted to NULL + * @address: pointer to be converted + * Return: status code EFI_UNSUPPORTED + */ +static __efi_runtime efi_status_t EFIAPI efi_convert_pointer_runtime( + efi_uintn_t debug_disposition, void **address) +{ + return EFI_UNSUPPORTED; +} - debug("%s: Setting %p to %lx\n", __func__, p, newaddr); - *p = newaddr; +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) { + if (*pos) + *pos += patchoff; } + /* + * The entry for SetVirtualAddress() must point to a physical address. + * After the first execution the service must return EFI_UNSUPPORTED. + */ + efi_runtime_services.set_virtual_address_map = + &efi_set_virtual_address_map_runtime; + + /* + * The entry for ConvertPointer() must point to a physical address. + * The service is not usable after SetVirtualAddress(). + */ + efi_runtime_services.convert_pointer = &efi_convert_pointer_runtime; + /* Update CRC32 */ efi_update_table_header_crc32(&efi_runtime_services.hdr); } @@ -483,6 +500,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 +527,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 +536,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 +642,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 +659,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); } } @@ -711,34 +736,6 @@ static efi_status_t __efi_runtime EFIAPI efi_unimplemented(void) } /** - * 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 * * This function implements the UpdateCapsule() runtime service. @@ -782,33 +779,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, @@ -820,11 +790,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, diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c index bfb57836fa..de7b616c6d 100644 --- a/lib/efi_loader/efi_setup.c +++ b/lib/efi_loader/efi_setup.c @@ -102,6 +102,11 @@ 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(); + /* 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 d6b75ca02e..bc8ed678c9 100644 --- a/lib/efi_loader/efi_variable.c +++ b/lib/efi_loader/efi_variable.c @@ -520,3 +520,84 @@ 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; +} + +/** + * 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 + * + * Return: status code + */ +efi_status_t efi_init_variables(void) +{ + return EFI_SUCCESS; +} diff --git a/lib/efi_selftest/Kconfig b/lib/efi_selftest/Kconfig index 59f9f36801..d20f5899af 100644 --- a/lib/efi_selftest/Kconfig +++ b/lib/efi_selftest/Kconfig @@ -1,9 +1,9 @@ config CMD_BOOTEFI_SELFTEST - bool "Allow booting an EFI efi_selftest" + bool "UEFI unit tests" depends on CMD_BOOTEFI imply FAT imply FAT_WRITE help - This adds an EFI test application to U-Boot that can be executed - with the 'bootefi selftest' command. It provides extended tests of - the EFI API implementation. + This adds a UEFI test application to U-Boot that can be executed + via the 'bootefi selftest' command. It provides extended tests of + the UEFI API implementation. diff --git a/lib/efi_selftest/Makefile b/lib/efi_selftest/Makefile index 3bebd0f573..88678755cc 100644 --- a/lib/efi_selftest/Makefile +++ b/lib/efi_selftest/Makefile @@ -35,6 +35,7 @@ efi_selftest_textoutput.o \ efi_selftest_tpl.o \ efi_selftest_util.o \ efi_selftest_variables.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, +}; |