[U-Boot] [PATCH 0/2] efi_loader: EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE

Implement the EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event. This requires allocating the event and the event list from runtime data.
Provide a unit test for SetVirtualAddressMap() and ConvertPointer(). As ConvertPointer() is not implemented yet this will spit out some warnings.
Heinrich Schuchardt (2): efi_loader: EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE efi_selftest: EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE
lib/efi_loader/efi_boottime.c | 25 ++- lib/efi_loader/efi_runtime.c | 14 +- lib/efi_selftest/Makefile | 1 + .../efi_selftest_set_virtual_address_map.c | 192 ++++++++++++++++++ 4 files changed, 224 insertions(+), 8 deletions(-) create mode 100644 lib/efi_selftest/efi_selftest_set_virtual_address_map.c
-- 2.20.1

Implement the EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event.
This requires allocating the event and the event list from runtime data.
Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de --- lib/efi_loader/efi_boottime.c | 25 +++++++++++++++++++------ lib/efi_loader/efi_runtime.c | 14 ++++++++++++-- 2 files changed, 31 insertions(+), 8 deletions(-)
diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index c2f89805c7..62e4994671 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -25,7 +25,7 @@ static efi_uintn_t efi_tpl = TPL_APPLICATION; LIST_HEAD(efi_obj_list);
/* List of all events */ -LIST_HEAD(efi_events); +__efi_runtime_data LIST_HEAD(efi_events);
/* List of queued events */ LIST_HEAD(efi_event_queue); @@ -650,6 +650,8 @@ efi_status_t efi_create_event(uint32_t type, efi_uintn_t notify_tpl, struct efi_event **event) { struct efi_event *evt; + efi_status_t ret; + int pool_type;
if (event == NULL) return EFI_INVALID_PARAMETER; @@ -662,7 +664,10 @@ efi_status_t efi_create_event(uint32_t type, efi_uintn_t notify_tpl, case EVT_NOTIFY_WAIT: case EVT_TIMER | EVT_NOTIFY_WAIT: case EVT_SIGNAL_EXIT_BOOT_SERVICES: + pool_type = EFI_BOOT_SERVICES_DATA; + break; case EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE: + pool_type = EFI_RUNTIME_SERVICES_DATA; break; default: return EFI_INVALID_PARAMETER; @@ -672,9 +677,11 @@ efi_status_t efi_create_event(uint32_t type, efi_uintn_t notify_tpl, (!notify_function || is_valid_tpl(notify_tpl) != EFI_SUCCESS)) return EFI_INVALID_PARAMETER;
- evt = calloc(1, sizeof(struct efi_event)); - if (!evt) - return EFI_OUT_OF_RESOURCES; + ret = efi_allocate_pool(pool_type, sizeof(struct efi_event), + (void **)&evt); + if (ret != EFI_SUCCESS) + return ret; + memset(evt, 0, sizeof(struct efi_event)); evt->type = type; evt->notify_tpl = notify_tpl; evt->notify_function = notify_function; @@ -982,7 +989,7 @@ static efi_status_t EFIAPI efi_close_event(struct efi_event *event) list_del(&event->queue_link);
list_del(&event->link); - free(event); + efi_free_pool(event); return EFI_EXIT(EFI_SUCCESS); }
@@ -1932,7 +1939,7 @@ static void efi_exit_caches(void) static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, efi_uintn_t map_key) { - struct efi_event *evt; + struct efi_event *evt, *next_event; efi_status_t ret = EFI_SUCCESS;
EFI_ENTRY("%p, %zx", image_handle, map_key); @@ -1971,6 +1978,12 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, /* Notify variable services */ efi_variables_boot_exit_notify();
+ /* Remove all events except EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE */ + list_for_each_entry_safe(evt, next_event, &efi_events, link) { + if (evt->type != EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE) + list_del(&evt->link); + } + board_quiesce_devices();
/* Patch out unsupported runtime function */ diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c index 94765569b6..9f4ba2f977 100644 --- a/lib/efi_loader/efi_runtime.c +++ b/lib/efi_loader/efi_runtime.c @@ -391,8 +391,10 @@ efi_status_t __weak __efi_runtime EFIAPI efi_set_time(struct efi_time *time) */ static bool efi_is_runtime_service_pointer(void *p) { - return p >= (void *)&efi_runtime_services.get_time && - p <= (void *)&efi_runtime_services.query_variable_info; + return (p >= (void *)&efi_runtime_services.get_time && + p <= (void *)&efi_runtime_services.query_variable_info) || + p == (void *)&efi_events.prev || + p == (void *)&efi_events.next; }
/** @@ -577,6 +579,7 @@ static efi_status_t EFIAPI efi_set_virtual_address_map( int n = memory_map_size / descriptor_size; int i; int rt_code_sections = 0; + struct efi_event *event;
EFI_ENTRY("%lx %lx %x %p", memory_map_size, descriptor_size, descriptor_version, virtmap); @@ -610,6 +613,13 @@ static efi_status_t EFIAPI efi_set_virtual_address_map( return EFI_EXIT(EFI_INVALID_PARAMETER); }
+ /* Notify EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE */ + list_for_each_entry(event, &efi_events, link) { + if (event->notify_function) + EFI_CALL_VOID(event->notify_function( + event, event->notify_context)); + } + /* Rebind mmio pointers */ for (i = 0; i < n; i++) { struct efi_mem_desc *map = (void*)virtmap + -- 2.20.1

Provide a unit test for SetVirtualAddressMap() and ConvertPointer(). As ConvertPointer() is not implemented yet this will spit out some warnings.
Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de --- lib/efi_selftest/Makefile | 1 + .../efi_selftest_set_virtual_address_map.c | 192 ++++++++++++++++++ 2 files changed, 193 insertions(+) create mode 100644 lib/efi_selftest/efi_selftest_set_virtual_address_map.c
diff --git a/lib/efi_selftest/Makefile b/lib/efi_selftest/Makefile index 88678755cc..5d7a1643ef 100644 --- a/lib/efi_selftest/Makefile +++ b/lib/efi_selftest/Makefile @@ -28,6 +28,7 @@ efi_selftest_manageprotocols.o \ efi_selftest_memory.o \ efi_selftest_open_protocol.o \ efi_selftest_register_notify.o \ +efi_selftest_set_virtual_address_map.o \ efi_selftest_snp.o \ efi_selftest_textinput.o \ efi_selftest_textinputex.o \ diff --git a/lib/efi_selftest/efi_selftest_set_virtual_address_map.c b/lib/efi_selftest/efi_selftest_set_virtual_address_map.c new file mode 100644 index 0000000000..5ebcdd2428 --- /dev/null +++ b/lib/efi_selftest/efi_selftest_set_virtual_address_map.c @@ -0,0 +1,192 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * efi_selftest_set_virtual_address_map.c + * + * Copyright (c) 2019 Heinrich Schuchardt xypron.glpk@gmx.de + * + * This test checks the notification of EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE + * and the following services: SetVirtualAddressMap, ConvertPointer. + */ + +#include <efi_selftest.h> + +static const struct efi_boot_services *boottime; +static const struct efi_runtime_services *runtime; +static struct efi_event *event; +static struct efi_mem_desc *memory_map; +static efi_uintn_t map_size; +static efi_uintn_t desc_size; +static u32 desc_version; +static u64 page1; +static u64 page2; +static u32 notify_call_count; + +/** + * notify () - notification function + * + * This function is called when the EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event + * occurs. The correct output of ConvertPointer() is checked. + * + * @event notified event + * @context pointer to the notification count + */ +static void EFIAPI notify(struct efi_event *event, void *context) +{ + void *addr; + efi_status_t ret; + + ++notify_call_count; + + addr = (void *)(uintptr_t)page1; + ret = runtime->convert_pointer(0, &addr); + if (ret != EFI_SUCCESS) + efi_st_todo("ConvertPointer failed\n"); + if ((uintptr_t)addr != page1 + EFI_PAGE_SIZE) + efi_st_todo("ConvertPointer wrong address\n"); + + addr = (void *)(uintptr_t)page2; + ret = runtime->convert_pointer(0, &addr); + if (ret != EFI_SUCCESS) + efi_st_todo("ConvertPointer failed\n"); + if ((uintptr_t)addr != page2 + 2 * EFI_PAGE_SIZE) + efi_st_todo("ConvertPointer wrong address\n"); +} + +/** + * setup() - setup unit test + * + * The memory map is read. Boottime only entries are deleted. Two entries for + * newly allocated pages are added. For these virtual addresses deviating from + * the physical addresses are set. + * + * @handle: handle of the loaded image + * @systable: system table + * @return: EFI_ST_SUCCESS for success + */ +static int setup(const efi_handle_t handle, + const struct efi_system_table *systable) +{ + efi_uintn_t map_key; + efi_status_t ret; + struct efi_mem_desc *end, *pos1, *pos2; + + boottime = systable->boottime; + runtime = systable->runtime; + + ret = boottime->create_event(EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE, + TPL_CALLBACK, notify, NULL, + &event); + if (ret != EFI_SUCCESS) { + efi_st_error("could not create event\n"); + return EFI_ST_FAILURE; + } + + ret = boottime->get_memory_map(&map_size, NULL, &map_key, &desc_size, + &desc_version); + if (ret != EFI_BUFFER_TOO_SMALL) { + efi_st_error( + "GetMemoryMap did not return EFI_BUFFER_TOO_SMALL\n"); + return EFI_ST_FAILURE; + } + /* Allocate extra space for newly allocated memory */ + map_size += 3 * sizeof(struct efi_mem_desc); + ret = boottime->allocate_pool(EFI_BOOT_SERVICES_DATA, map_size, + (void **)&memory_map); + if (ret != EFI_SUCCESS) { + efi_st_error("AllocatePool failed\n"); + return EFI_ST_FAILURE; + } + ret = boottime->get_memory_map(&map_size, memory_map, &map_key, + &desc_size, &desc_version); + if (ret != EFI_SUCCESS) { + efi_st_error("GetMemoryMap failed\n"); + return EFI_ST_FAILURE; + } + ret = boottime->allocate_pages(EFI_ALLOCATE_ANY_PAGES, + EFI_BOOT_SERVICES_DATA, 2, &page1); + if (ret != EFI_SUCCESS) { + efi_st_error("AllocatePages failed\n"); + return EFI_ST_FAILURE; + } + ret = boottime->allocate_pages(EFI_ALLOCATE_ANY_PAGES, + EFI_BOOT_SERVICES_DATA, 3, &page2); + if (ret != EFI_SUCCESS) { + efi_st_error("AllocatePages failed\n"); + return EFI_ST_FAILURE; + } + /* Remove entries not relevant for runtime from map */ + end = (struct efi_mem_desc *)((u8 *)memory_map + map_size); + for (pos1 = memory_map, pos2 = memory_map; + pos2 < end; ++pos2) { + switch (pos2->type) { + case EFI_LOADER_CODE: + case EFI_LOADER_DATA: + case EFI_BOOT_SERVICES_CODE: + case EFI_BOOT_SERVICES_DATA: + continue; + } + memcpy(pos1, pos2, desc_size); + ++pos1; + } + + /* + * Add entries with virtual addresses deviating from the physical + * addresses. By choosing virtual address ranges within the allocated + * physical pages address space collisions are avoided. + */ + pos1->type = EFI_RUNTIME_SERVICES_DATA; + pos1->reserved = 0; + pos1->physical_start = page1; + pos1->virtual_start = page1 + EFI_PAGE_SIZE; + pos1->num_pages = 1; + pos1->attribute = EFI_MEMORY_RUNTIME; + ++pos1; + + pos1->type = EFI_RUNTIME_SERVICES_DATA; + pos1->reserved = 0; + pos1->physical_start = page2; + pos1->virtual_start = page2 + 2 * EFI_PAGE_SIZE; + pos1->num_pages = 1; + pos1->attribute = EFI_MEMORY_RUNTIME; + ++pos1; + + map_size = (u8 *)pos1 - (u8 *)memory_map; + + return EFI_ST_SUCCESS; +} + +/** + * execute() - execute unit test + * + * SetVirtualAddressMap() is called with the memory map prepared in setup(). + * + * The triggering of the EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event is checked via + * the call count of the notification function. + * + * @return: EFI_ST_SUCCESS for success + */ +static int execute(void) +{ + efi_status_t ret; + + ret = runtime->set_virtual_address_map(map_size, desc_size, + desc_version, memory_map); + if (ret != EFI_SUCCESS) { + efi_st_error("SetVirtualAddressMap failed\n"); + return EFI_ST_FAILURE; + } + if (notify_call_count != 1) { + efi_st_error("EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE triggered %d times\n", + notify_call_count); + return EFI_ST_FAILURE; + } + + return EFI_ST_SUCCESS; +} + +EFI_UNIT_TEST(virtaddrmap) = { + .name = "virtual address map", + .phase = EFI_SETUP_BEFORE_BOOTTIME_EXIT, + .setup = setup, + .execute = execute, +}; -- 2.20.1
participants (1)
-
Heinrich Schuchardt