From 52cbac9b06bf247dda8ff261b15e3c13f88188db Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 4 May 2019 11:47:48 +0200 Subject: [PATCH 01/17] efi_loader: clean up UEFI sub-system initialization allow_unaligned(), switch_to_non_secure_mode(), and efi_init_obj_list() are called in sequence in multiple places. Move calls to allow_unaligned() and switch_to_non_secure_mode() to efi_init_obj_list(). Remove unused includes. Signed-off-by: Heinrich Schuchardt --- cmd/bootefi.c | 27 --------------------------- lib/efi_loader/efi_setup.c | 16 ++++++++++++++++ 2 files changed, 16 insertions(+), 27 deletions(-) diff --git a/cmd/bootefi.c b/cmd/bootefi.c index f1d7d8bc66..52116b308c 100644 --- a/cmd/bootefi.c +++ b/cmd/bootefi.c @@ -6,7 +6,6 @@ */ #include -#include #include #include #include @@ -17,9 +16,7 @@ #include #include #include -#include #include -#include #include DECLARE_GLOBAL_DATA_PTR; @@ -27,15 +24,6 @@ DECLARE_GLOBAL_DATA_PTR; static struct efi_device_path *bootefi_image_path; static struct efi_device_path *bootefi_device_path; -/* - * Allow unaligned memory access. - * - * This routine is overridden by architectures providing this feature. - */ -void __weak allow_unaligned(void) -{ -} - /* * Set the load options of an image from an environment variable. * @@ -338,11 +326,6 @@ static int do_efibootmgr(const char *fdt_opt) efi_handle_t handle; efi_status_t ret; - /* Allow unaligned memory access */ - allow_unaligned(); - - switch_to_non_secure_mode(); - /* Initialize EFI drivers */ ret = efi_init_obj_list(); if (ret != EFI_SUCCESS) { @@ -391,11 +374,6 @@ static int do_bootefi_image(const char *image_opt, const char *fdt_opt) efi_handle_t mem_handle = NULL, handle; efi_status_t ret; - /* Allow unaligned memory access */ - allow_unaligned(); - - switch_to_non_secure_mode(); - /* Initialize EFI drivers */ ret = efi_init_obj_list(); if (ret != EFI_SUCCESS) { @@ -582,11 +560,6 @@ static int do_efi_selftest(const char *fdt_opt) struct efi_loaded_image *loaded_image_info; efi_status_t ret; - /* Allow unaligned memory access */ - allow_unaligned(); - - switch_to_non_secure_mode(); - /* Initialize EFI drivers */ ret = efi_init_obj_list(); if (ret != EFI_SUCCESS) { diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c index 87db51cbb7..8691d686d2 100644 --- a/lib/efi_loader/efi_setup.c +++ b/lib/efi_loader/efi_setup.c @@ -6,12 +6,22 @@ */ #include +#include #include #define OBJ_LIST_NOT_INITIALIZED 1 static efi_status_t efi_obj_list_initialized = OBJ_LIST_NOT_INITIALIZED; +/* + * Allow unaligned memory access. + * + * This routine is overridden by architectures providing this feature. + */ +void __weak allow_unaligned(void) +{ +} + /** * efi_init_platform_lang() - define supported languages * @@ -86,6 +96,12 @@ efi_status_t efi_init_obj_list(void) if (efi_obj_list_initialized != OBJ_LIST_NOT_INITIALIZED) return efi_obj_list_initialized; + /* Allow unaligned memory access */ + allow_unaligned(); + + /* On ARM switch from EL3 or secure mode to EL2 or non-secure mode */ + switch_to_non_secure_mode(); + /* Define supported languages */ ret = efi_init_platform_lang(); if (ret != EFI_SUCCESS) From 1884512b77ce54fffa50bb07cf065d4831171a6d Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 4 May 2019 10:12:50 +0200 Subject: [PATCH 02/17] efi_loader: allowable event types in CreateEventEx() CreateEventEx() does not allow the following event types: * EVT_SIGNAL_EXIT_BOOT_SERVICES * EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE This check is needed to pass the UEFI SCT conformance test. Signed-off-by: Heinrich Schuchardt Reviewed-by: Alexander Graf --- lib/efi_loader/efi_boottime.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index e5c46e9f08..3ed08e7c37 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -664,10 +664,26 @@ efi_status_t EFIAPI efi_create_event_ex(uint32_t type, efi_uintn_t notify_tpl, efi_guid_t *event_group, struct efi_event **event) { + efi_status_t ret; + EFI_ENTRY("%d, 0x%zx, %p, %p, %pUl", type, notify_tpl, notify_function, notify_context, event_group); - return EFI_EXIT(efi_create_event(type, notify_tpl, notify_function, - notify_context, event_group, event)); + + /* + * The allowable input parameters are the same as in CreateEvent() + * except for the following two disallowed event types. + */ + switch (type) { + case EVT_SIGNAL_EXIT_BOOT_SERVICES: + case EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE: + ret = EFI_INVALID_PARAMETER; + goto out; + } + + ret = efi_create_event(type, notify_tpl, notify_function, + notify_context, event_group, event); +out: + return EFI_EXIT(ret); } /** From 8dfb5e6e211fb9d6e1c6650617377a99342224ae Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 4 May 2019 17:37:32 +0200 Subject: [PATCH 03/17] efi_loader: correct parameter check in LocateHandle() If LocateHandle() does not find an entry EFI_NOT_FOUND has to be returned even if BufferSize is NULL. Signed-off-by: Heinrich Schuchardt --- lib/efi_loader/efi_boottime.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 3ed08e7c37..0c92cc1807 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -1364,28 +1364,28 @@ static efi_status_t efi_locate_handle( return EFI_INVALID_PARAMETER; } - /* - * efi_locate_handle_buffer uses this function for - * the calculation of the necessary buffer size. - * So do not require a buffer for buffersize == 0. - */ - if (!buffer_size || (*buffer_size && !buffer)) - return EFI_INVALID_PARAMETER; - /* Count how much space we need */ list_for_each_entry(efiobj, &efi_obj_list, link) { if (!efi_search(search_type, protocol, search_key, efiobj)) size += sizeof(void *); } + if (size == 0) + return EFI_NOT_FOUND; + + if (!buffer_size) + return EFI_INVALID_PARAMETER; + if (*buffer_size < size) { *buffer_size = size; return EFI_BUFFER_TOO_SMALL; } *buffer_size = size; - if (size == 0) - return EFI_NOT_FOUND; + + /* The buffer size is sufficient but there is not buffer */ + if (!buffer) + return EFI_INVALID_PARAMETER; /* Then fill the array */ list_for_each_entry(efiobj, &efi_obj_list, link) { From cd73aba656b19d795eaf5756d5d4815d299cf07f Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Wed, 1 May 2019 14:20:18 +0200 Subject: [PATCH 04/17] efi_loader: mark started images In UnloadImage() we need to know if an image is already started. Add a field to the handle structure identifying loaded and started images. Signed-off-by: Heinrich Schuchardt --- include/efi_loader.h | 13 +++++++++++++ lib/efi_loader/efi_boottime.c | 2 ++ 2 files changed, 15 insertions(+) diff --git a/include/efi_loader.h b/include/efi_loader.h index 7af3f16ef8..3b50cd28ef 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -179,6 +179,18 @@ struct efi_handler { struct list_head open_infos; }; +/** + * enum efi_object_type - type of EFI object + * + * In UnloadImage we must be able to identify if the handle relates to a + * started image. + */ +enum efi_object_type { + EFI_OBJECT_TYPE_UNDEFINED = 0, + EFI_OBJECT_TYPE_LOADED_IMAGE, + EFI_OBJECT_TYPE_STARTED_IMAGE, +}; + /** * struct efi_object - dereferenced EFI handle * @@ -201,6 +213,7 @@ struct efi_object { struct list_head link; /* The list of protocols */ struct list_head protocols; + enum efi_object_type type; }; /** diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 0c92cc1807..45e4d98708 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -1554,6 +1554,7 @@ efi_status_t efi_setup_loaded_image(struct efi_device_path *device_path, free(info); return EFI_OUT_OF_RESOURCES; } + obj->header.type = EFI_OBJECT_TYPE_LOADED_IMAGE; /* Add internal object to object list */ efi_add_handle(&obj->header); @@ -2678,6 +2679,7 @@ efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle, } current_image = image_handle; + image_obj->header.type = EFI_OBJECT_TYPE_STARTED_IMAGE; EFI_PRINT("Jumping into 0x%p\n", image_obj->entry); ret = EFI_CALL(image_obj->entry(image_handle, &systab)); From 46e99a9cf7ea06837184da766b5a43065c523d10 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Wed, 1 May 2019 19:04:32 +0200 Subject: [PATCH 05/17] efi_loader: move efi_unload_image() down in source Move efi_unload_image() down in source to avoid forward declaration in follwing page. Signed-off-by: Heinrich Schuchardt --- lib/efi_loader/efi_boottime.c | 46 +++++++++++++++++------------------ 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 45e4d98708..8f2c610b86 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -1744,29 +1744,6 @@ error: return EFI_EXIT(ret); } -/** - * efi_unload_image() - unload an EFI image - * @image_handle: handle of the image to be unloaded - * - * This function implements the UnloadImage service. - * - * See the Unified Extensible Firmware Interface (UEFI) specification for - * details. - * - * Return: status code - */ -efi_status_t EFIAPI efi_unload_image(efi_handle_t image_handle) -{ - struct efi_object *efiobj; - - EFI_ENTRY("%p", image_handle); - efiobj = efi_search_obj(image_handle); - if (efiobj) - list_del(&efiobj->link); - - return EFI_EXIT(EFI_SUCCESS); -} - /** * efi_exit_caches() - fix up caches for EFI payloads if necessary */ @@ -2692,6 +2669,29 @@ efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle, return EFI_CALL(systab.boottime->exit(image_handle, ret, 0, NULL)); } +/** + * efi_unload_image() - unload an EFI image + * @image_handle: handle of the image to be unloaded + * + * This function implements the UnloadImage service. + * + * See the Unified Extensible Firmware Interface (UEFI) specification for + * details. + * + * Return: status code + */ +efi_status_t EFIAPI efi_unload_image(efi_handle_t image_handle) +{ + struct efi_object *efiobj; + + EFI_ENTRY("%p", image_handle); + efiobj = efi_search_obj(image_handle); + if (efiobj) + list_del(&efiobj->link); + + return EFI_EXIT(EFI_SUCCESS); +} + /** * efi_update_exit_data() - fill exit data parameters of StartImage() * From df116e81ea62cf2fcc0f0f89ed328fe27e64ca67 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Wed, 1 May 2019 18:25:45 +0200 Subject: [PATCH 06/17] efi_loader: implement UnloadImage() Implement the UnloadImage() boot service Signed-off-by: Heinrich Schuchardt --- include/efi_api.h | 2 +- lib/efi_loader/efi_boottime.c | 55 ++++++++++++++++++++++++++++++++--- 2 files changed, 52 insertions(+), 5 deletions(-) diff --git a/include/efi_api.h b/include/efi_api.h index 472160cb30..b2ae279747 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -348,7 +348,7 @@ struct efi_loaded_image { aligned_u64 image_size; unsigned int image_code_type; unsigned int image_data_type; - unsigned long unload; + efi_status_t (EFIAPI *unload)(efi_handle_t image_handle); }; #define EFI_DEVICE_PATH_PROTOCOL_GUID \ diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 8f2c610b86..0385883ded 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -2669,6 +2669,20 @@ efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle, return EFI_CALL(systab.boottime->exit(image_handle, ret, 0, NULL)); } +/** + * efi_delete_image() - delete loaded image from memory) + * + * @image_obj: handle of the loaded image + * @loaded_image_protocol: loaded image protocol + */ +static void efi_delete_image(struct efi_loaded_image_obj *image_obj, + struct efi_loaded_image *loaded_image_protocol) +{ + efi_free_pages((uintptr_t)loaded_image_protocol->image_base, + efi_size_in_pages(loaded_image_protocol->image_size)); + efi_delete_handle(&image_obj->header); +} + /** * efi_unload_image() - unload an EFI image * @image_handle: handle of the image to be unloaded @@ -2682,14 +2696,47 @@ efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle, */ efi_status_t EFIAPI efi_unload_image(efi_handle_t image_handle) { + efi_status_t ret = EFI_SUCCESS; struct efi_object *efiobj; + struct efi_loaded_image *loaded_image_protocol; EFI_ENTRY("%p", image_handle); - efiobj = efi_search_obj(image_handle); - if (efiobj) - list_del(&efiobj->link); - return EFI_EXIT(EFI_SUCCESS); + efiobj = efi_search_obj(image_handle); + if (!efiobj) { + ret = EFI_INVALID_PARAMETER; + goto out; + } + /* Find the loaded image protocol */ + ret = EFI_CALL(efi_open_protocol(image_handle, &efi_guid_loaded_image, + (void **)&loaded_image_protocol, + NULL, NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL)); + if (ret != EFI_SUCCESS) { + ret = EFI_INVALID_PARAMETER; + goto out; + } + switch (efiobj->type) { + case EFI_OBJECT_TYPE_STARTED_IMAGE: + /* Call the unload function */ + if (!loaded_image_protocol->unload) { + ret = EFI_UNSUPPORTED; + goto out; + } + ret = EFI_CALL(loaded_image_protocol->unload(image_handle)); + if (ret != EFI_SUCCESS) + goto out; + break; + case EFI_OBJECT_TYPE_LOADED_IMAGE: + break; + default: + ret = EFI_INVALID_PARAMETER; + goto out; + } + efi_delete_image((struct efi_loaded_image_obj *)efiobj, + loaded_image_protocol); +out: + return EFI_EXIT(ret); } /** From 126a43f15b3627d39e71636f93c500d57adeb28a Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Wed, 1 May 2019 20:07:04 +0200 Subject: [PATCH 07/17] efi_loader: unload applications upon Exit() Implement unloading of images in the Exit() boot services: * unload images that are not yet started, * unload started applications, * unload drivers returning an error. Signed-off-by: Heinrich Schuchardt --- include/efi_loader.h | 1 + lib/efi_loader/efi_boottime.c | 36 +++++++++++++++++++++++++------ lib/efi_loader/efi_image_loader.c | 2 ++ 3 files changed, 33 insertions(+), 6 deletions(-) diff --git a/include/efi_loader.h b/include/efi_loader.h index 3b50cd28ef..4e4cffa799 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -234,6 +234,7 @@ struct efi_loaded_image_obj { struct jmp_buf_data exit_jmp; EFIAPI efi_status_t (*entry)(efi_handle_t image_handle, struct efi_system_table *st); + u16 image_type; }; /** diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 0385883ded..1ea96dab6c 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -13,6 +13,7 @@ #include #include #include +#include #include DECLARE_GLOBAL_DATA_PTR; @@ -2798,7 +2799,7 @@ static efi_status_t EFIAPI efi_exit(efi_handle_t image_handle, * image protocol. */ efi_status_t ret; - void *info; + struct efi_loaded_image *loaded_image_protocol; struct efi_loaded_image_obj *image_obj = (struct efi_loaded_image_obj *)image_handle; @@ -2806,13 +2807,33 @@ static efi_status_t EFIAPI efi_exit(efi_handle_t image_handle, exit_data_size, exit_data); /* Check parameters */ - if (image_handle != current_image) - goto out; ret = EFI_CALL(efi_open_protocol(image_handle, &efi_guid_loaded_image, - &info, NULL, NULL, + (void **)&loaded_image_protocol, + NULL, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL)); - if (ret != EFI_SUCCESS) + if (ret != EFI_SUCCESS) { + ret = EFI_INVALID_PARAMETER; goto out; + } + + /* Unloading of unstarted images */ + switch (image_obj->header.type) { + case EFI_OBJECT_TYPE_STARTED_IMAGE: + break; + case EFI_OBJECT_TYPE_LOADED_IMAGE: + efi_delete_image(image_obj, loaded_image_protocol); + ret = EFI_SUCCESS; + goto out; + default: + /* Handle does not refer to loaded image */ + ret = EFI_INVALID_PARAMETER; + goto out; + } + /* A started image can only be unloaded it is the last one started. */ + if (image_handle != current_image) { + ret = EFI_INVALID_PARAMETER; + goto out; + } /* Exit data is only foreseen in case of failure. */ if (exit_status != EFI_SUCCESS) { @@ -2822,6 +2843,9 @@ static efi_status_t EFIAPI efi_exit(efi_handle_t image_handle, if (ret != EFI_SUCCESS) EFI_PRINT("%s: out of memory\n", __func__); } + if (image_obj->image_type == IMAGE_SUBSYSTEM_EFI_APPLICATION || + exit_status != EFI_SUCCESS) + efi_delete_image(image_obj, loaded_image_protocol); /* Make sure entry/exit counts for EFI world cross-overs match */ EFI_EXIT(exit_status); @@ -2837,7 +2861,7 @@ static efi_status_t EFIAPI efi_exit(efi_handle_t image_handle, panic("EFI application exited"); out: - return EFI_EXIT(EFI_INVALID_PARAMETER); + return EFI_EXIT(ret); } /** diff --git a/lib/efi_loader/efi_image_loader.c b/lib/efi_loader/efi_image_loader.c index f8092b6202..13541cfa7a 100644 --- a/lib/efi_loader/efi_image_loader.c +++ b/lib/efi_loader/efi_image_loader.c @@ -273,6 +273,7 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi, IMAGE_OPTIONAL_HEADER64 *opt = &nt64->OptionalHeader; image_base = opt->ImageBase; efi_set_code_and_data_type(loaded_image_info, opt->Subsystem); + handle->image_type = opt->Subsystem; efi_reloc = efi_alloc(virt_size, loaded_image_info->image_code_type); if (!efi_reloc) { @@ -288,6 +289,7 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi, IMAGE_OPTIONAL_HEADER32 *opt = &nt->OptionalHeader; image_base = opt->ImageBase; efi_set_code_and_data_type(loaded_image_info, opt->Subsystem); + handle->image_type = opt->Subsystem; efi_reloc = efi_alloc(virt_size, loaded_image_info->image_code_type); if (!efi_reloc) { From 8101dd3dfaa9bf85a80b76fc2be1ae59bd051bc9 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 4 May 2019 19:48:38 +0200 Subject: [PATCH 08/17] efi_selftest: remove redundant function efi_st_memcmp() Function memcmp() is available in efi_freestanding.c. So we do not remove a further implementation. Replace all usages of efi_st_memcmp() by memcmp(). Signed-off-by: Heinrich Schuchardt --- include/efi_selftest.h | 11 ----------- lib/efi_selftest/efi_selftest_block_device.c | 6 +++--- lib/efi_selftest/efi_selftest_config_table.c | 12 ++++++------ lib/efi_selftest/efi_selftest_loaded_image.c | 5 ++--- lib/efi_selftest/efi_selftest_loadimage.c | 7 +++---- lib/efi_selftest/efi_selftest_manageprotocols.c | 8 ++++---- lib/efi_selftest/efi_selftest_memory.c | 4 ++-- lib/efi_selftest/efi_selftest_snp.c | 7 +++---- lib/efi_selftest/efi_selftest_startimage_exit.c | 2 +- lib/efi_selftest/efi_selftest_util.c | 14 -------------- lib/efi_selftest/efi_selftest_variables.c | 10 +++++----- 11 files changed, 29 insertions(+), 57 deletions(-) diff --git a/include/efi_selftest.h b/include/efi_selftest.h index dd42e49023..eaee188de7 100644 --- a/include/efi_selftest.h +++ b/include/efi_selftest.h @@ -92,17 +92,6 @@ u16 *efi_st_translate_char(u16 code); */ u16 *efi_st_translate_code(u16 code); -/* - * Compare memory. - * We cannot use lib/string.c due to different CFLAGS values. - * - * @buf1: first buffer - * @buf2: second buffer - * @length: number of bytes to compare - * @return: 0 if both buffers contain the same bytes - */ -int efi_st_memcmp(const void *buf1, const void *buf2, size_t length); - /* * Compare an u16 string to a char string. * diff --git a/lib/efi_selftest/efi_selftest_block_device.c b/lib/efi_selftest/efi_selftest_block_device.c index 29ac0ce651..644c5ade21 100644 --- a/lib/efi_selftest/efi_selftest_block_device.c +++ b/lib/efi_selftest/efi_selftest_block_device.c @@ -337,7 +337,7 @@ static int execute(void) } if (len >= dp_size(dp_partition)) continue; - if (efi_st_memcmp(dp, dp_partition, len)) + if (memcmp(dp, dp_partition, len)) continue; handle_partition = handles[i]; break; @@ -409,7 +409,7 @@ static int execute(void) (unsigned int)buf_size); return EFI_ST_FAILURE; } - if (efi_st_memcmp(buf, "ello world!", 11)) { + if (memcmp(buf, "ello world!", 11)) { efi_st_error("Unexpected file content\n"); return EFI_ST_FAILURE; } @@ -480,7 +480,7 @@ static int execute(void) (unsigned int)buf_size); return EFI_ST_FAILURE; } - if (efi_st_memcmp(buf, "U-Boot", 7)) { + if (memcmp(buf, "U-Boot", 7)) { efi_st_error("Unexpected file content %s\n", buf); return EFI_ST_FAILURE; } diff --git a/lib/efi_selftest/efi_selftest_config_table.c b/lib/efi_selftest/efi_selftest_config_table.c index 0bc5da6b0c..4467f492ac 100644 --- a/lib/efi_selftest/efi_selftest_config_table.c +++ b/lib/efi_selftest/efi_selftest_config_table.c @@ -153,8 +153,8 @@ static int execute(void) } table = NULL; for (i = 0; i < sys_table->nr_tables; ++i) { - if (!efi_st_memcmp(&sys_table->tables[i].guid, &table_guid, - sizeof(efi_guid_t))) + if (!memcmp(&sys_table->tables[i].guid, &table_guid, + sizeof(efi_guid_t))) table = sys_table->tables[i].table; } if (!table) { @@ -192,8 +192,8 @@ static int execute(void) table = NULL; tabcnt = 0; for (i = 0; i < sys_table->nr_tables; ++i) { - if (!efi_st_memcmp(&sys_table->tables[i].guid, &table_guid, - sizeof(efi_guid_t))) { + if (!memcmp(&sys_table->tables[i].guid, &table_guid, + sizeof(efi_guid_t))) { table = sys_table->tables[i].table; ++tabcnt; } @@ -235,8 +235,8 @@ static int execute(void) } table = NULL; for (i = 0; i < sys_table->nr_tables; ++i) { - if (!efi_st_memcmp(&sys_table->tables[i].guid, &table_guid, - sizeof(efi_guid_t))) { + if (!memcmp(&sys_table->tables[i].guid, &table_guid, + sizeof(efi_guid_t))) { table = sys_table->tables[i].table; } } diff --git a/lib/efi_selftest/efi_selftest_loaded_image.c b/lib/efi_selftest/efi_selftest_loaded_image.c index ea2b380a77..5889ab1261 100644 --- a/lib/efi_selftest/efi_selftest_loaded_image.c +++ b/lib/efi_selftest/efi_selftest_loaded_image.c @@ -60,9 +60,8 @@ static int execute(void) efi_st_printf("%u protocols installed on image handle\n", (unsigned int)protocol_buffer_count); for (i = 0; i < protocol_buffer_count; ++i) { - if (efi_st_memcmp(protocol_buffer[i], - &loaded_image_protocol_guid, - sizeof(efi_guid_t))) + if (memcmp(protocol_buffer[i], &loaded_image_protocol_guid, + sizeof(efi_guid_t))) found = true; } if (!found) { diff --git a/lib/efi_selftest/efi_selftest_loadimage.c b/lib/efi_selftest/efi_selftest_loadimage.c index 449b6bfcac..06a87df862 100644 --- a/lib/efi_selftest/efi_selftest_loadimage.c +++ b/lib/efi_selftest/efi_selftest_loadimage.c @@ -322,8 +322,7 @@ static efi_status_t EFIAPI getinfo efi_uintn_t *buffer_size, void *buffer) { if (this == &file) { - if (efi_st_memcmp(info_type, &guid_file_info, - sizeof(efi_guid_t))) + if (memcmp(info_type, &guid_file_info, sizeof(efi_guid_t))) return EFI_INVALID_PARAMETER; if (*buffer_size >= sizeof(struct file_info)) { boottime->copy_mem(buffer, file_info, @@ -333,8 +332,8 @@ static efi_status_t EFIAPI getinfo return EFI_BUFFER_TOO_SMALL; } } else if (this == &volume) { - if (efi_st_memcmp(info_type, &guid_file_system_info, - sizeof(efi_guid_t))) + if (memcmp(info_type, &guid_file_system_info, + sizeof(efi_guid_t))) return EFI_INVALID_PARAMETER; if (*buffer_size >= sizeof(struct file_system_info)) { boottime->copy_mem(buffer, file_system_info, diff --git a/lib/efi_selftest/efi_selftest_manageprotocols.c b/lib/efi_selftest/efi_selftest_manageprotocols.c index 0ff35cec8a..8edb1e4d46 100644 --- a/lib/efi_selftest/efi_selftest_manageprotocols.c +++ b/lib/efi_selftest/efi_selftest_manageprotocols.c @@ -332,13 +332,13 @@ static int execute(void) efi_st_error("Failed to get protocols per handle\n"); return EFI_ST_FAILURE; } - if (efi_st_memcmp(prot_buffer[0], &guid1, 16) && - efi_st_memcmp(prot_buffer[1], &guid1, 16)) { + if (memcmp(prot_buffer[0], &guid1, 16) && + memcmp(prot_buffer[1], &guid1, 16)) { efi_st_error("Failed to get protocols per handle\n"); return EFI_ST_FAILURE; } - if (efi_st_memcmp(prot_buffer[0], &guid3, 16) && - efi_st_memcmp(prot_buffer[1], &guid3, 16)) { + if (memcmp(prot_buffer[0], &guid3, 16) && + memcmp(prot_buffer[1], &guid3, 16)) { efi_st_error("Failed to get protocols per handle\n"); return EFI_ST_FAILURE; } diff --git a/lib/efi_selftest/efi_selftest_memory.c b/lib/efi_selftest/efi_selftest_memory.c index 5eeb42a9be..e71732dc6d 100644 --- a/lib/efi_selftest/efi_selftest_memory.c +++ b/lib/efi_selftest/efi_selftest_memory.c @@ -33,8 +33,8 @@ static int setup(const efi_handle_t handle, boottime = systable->boottime; for (i = 0; i < systable->nr_tables; ++i) { - if (!efi_st_memcmp(&systable->tables[i].guid, &fdt_guid, - sizeof(efi_guid_t))) { + if (!memcmp(&systable->tables[i].guid, &fdt_guid, + sizeof(efi_guid_t))) { if (fdt_addr) { efi_st_error("Duplicate device tree\n"); return EFI_ST_FAILURE; diff --git a/lib/efi_selftest/efi_selftest_snp.c b/lib/efi_selftest/efi_selftest_snp.c index d7350e2158..4c26619001 100644 --- a/lib/efi_selftest/efi_selftest_snp.c +++ b/lib/efi_selftest/efi_selftest_snp.c @@ -334,9 +334,8 @@ static int execute(void) * Unfortunately QEMU ignores the broadcast flag. * So we have to check for broadcasts too. */ - if (efi_st_memcmp(&destaddr, &net->mode->current_address, - ARP_HLEN) && - efi_st_memcmp(&destaddr, BROADCAST_MAC, ARP_HLEN)) + if (memcmp(&destaddr, &net->mode->current_address, ARP_HLEN) && + memcmp(&destaddr, BROADCAST_MAC, ARP_HLEN)) continue; /* * Check this is a DHCP reply @@ -360,7 +359,7 @@ static int execute(void) addr = (u8 *)&buffer.p.ip_udp.ip_src; efi_st_printf("DHCP reply received from %u.%u.%u.%u (%pm) ", addr[0], addr[1], addr[2], addr[3], &srcaddr); - if (!efi_st_memcmp(&destaddr, BROADCAST_MAC, ARP_HLEN)) + if (!memcmp(&destaddr, BROADCAST_MAC, ARP_HLEN)) efi_st_printf("as broadcast message.\n"); else efi_st_printf("as unicast message.\n"); diff --git a/lib/efi_selftest/efi_selftest_startimage_exit.c b/lib/efi_selftest/efi_selftest_startimage_exit.c index 96049dea86..11207b8162 100644 --- a/lib/efi_selftest/efi_selftest_startimage_exit.c +++ b/lib/efi_selftest/efi_selftest_startimage_exit.c @@ -139,7 +139,7 @@ static int execute(void) return EFI_ST_FAILURE; } if (!exit_data || exit_data_size != sizeof(expected_text) || - efi_st_memcmp(exit_data, expected_text, sizeof(expected_text))) { + memcmp(exit_data, expected_text, sizeof(expected_text))) { efi_st_error("Incorrect exit data\n"); return EFI_ST_FAILURE; } diff --git a/lib/efi_selftest/efi_selftest_util.c b/lib/efi_selftest/efi_selftest_util.c index 96a964c863..ea73c25220 100644 --- a/lib/efi_selftest/efi_selftest_util.c +++ b/lib/efi_selftest/efi_selftest_util.c @@ -102,20 +102,6 @@ u16 *efi_st_translate_code(u16 code) return efi_st_unknown; } -int efi_st_memcmp(const void *buf1, const void *buf2, size_t length) -{ - const u8 *pos1 = buf1; - const u8 *pos2 = buf2; - - for (; length; --length) { - if (*pos1 != *pos2) - return *pos1 - *pos2; - ++pos1; - ++pos2; - } - return 0; -} - int efi_st_strcmp_16_8(const u16 *buf1, const char *buf2) { for (; *buf1 || *buf2; ++buf1, ++buf2) { diff --git a/lib/efi_selftest/efi_selftest_variables.c b/lib/efi_selftest/efi_selftest_variables.c index 47a8e7fb95..b028c64bbc 100644 --- a/lib/efi_selftest/efi_selftest_variables.c +++ b/lib/efi_selftest/efi_selftest_variables.c @@ -78,7 +78,7 @@ static int execute(void) efi_st_error("GetVariable failed\n"); return EFI_ST_FAILURE; } - if (efi_st_memcmp(data, v + 4, 3)) { + if (memcmp(data, v + 4, 3)) { efi_st_error("GetVariable returned wrong value\n"); return EFI_ST_FAILURE; } @@ -106,7 +106,7 @@ static int execute(void) (unsigned int)len); return EFI_ST_FAILURE; } - if (efi_st_memcmp(data, v, 8)) { + if (memcmp(data, v, 8)) { efi_st_error("GetVariable returned wrong value\n"); return EFI_ST_FAILURE; } @@ -129,7 +129,7 @@ static int execute(void) if (len != 15) efi_st_todo("GetVariable returned wrong length %u\n", (unsigned int)len); - if (efi_st_memcmp(data, v, len)) + if (memcmp(data, v, len)) efi_st_todo("GetVariable returned wrong value\n"); /* Enumerate variables */ boottime->set_mem(&guid, 16, 0); @@ -145,10 +145,10 @@ static int execute(void) (unsigned int)ret); return EFI_ST_FAILURE; } - if (!efi_st_memcmp(&guid, &guid_vendor0, sizeof(efi_guid_t)) && + if (!memcmp(&guid, &guid_vendor0, sizeof(efi_guid_t)) && !efi_st_strcmp_16_8(varname, "efi_st_var0")) flag |= 1; - if (!efi_st_memcmp(&guid, &guid_vendor1, sizeof(efi_guid_t)) && + if (!memcmp(&guid, &guid_vendor1, sizeof(efi_guid_t)) && !efi_st_strcmp_16_8(varname, "efi_st_var1")) flag |= 2; } From 3626e539903f05565e0f6b54fae7f19da7a0bf34 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sun, 5 May 2019 00:07:34 +0200 Subject: [PATCH 09/17] efi_loader: EVT_NOTIFY_SIGNAL events The notification function of events of type EVT_NOTIFY_SIGNAL should always be queued when SignalEvent() is called. Signed-off-by: Heinrich Schuchardt --- lib/efi_loader/efi_boottime.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 1ea96dab6c..b6dfb3916f 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -239,7 +239,7 @@ void efi_signal_event(struct efi_event *event, bool check_tpl) if (evt->is_queued) efi_queue_event(evt, check_tpl); } - } else if (!event->is_signaled) { + } else { event->is_signaled = true; if (event->type & EVT_NOTIFY_SIGNAL) efi_queue_event(event, check_tpl); From ab15d4149ee950820f65e5a5235b1e6b745fc678 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 4 May 2019 17:27:54 +0200 Subject: [PATCH 10/17] efi_loader: implement RegisterProtocolNotify() The RegisterProtocolNotify() boot service registers an event to be notified upon the installation of a protocol interface with the specified GUID. Add the missing implementation. Signed-off-by: Heinrich Schuchardt --- include/efi_loader.h | 19 ++++++++ lib/efi_loader/efi_boottime.c | 83 +++++++++++++++++++++++++++++------ 2 files changed, 88 insertions(+), 14 deletions(-) diff --git a/include/efi_loader.h b/include/efi_loader.h index 4e4cffa799..d3a1d4c465 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -271,6 +271,25 @@ extern struct list_head efi_obj_list; /* List of all events */ extern struct list_head efi_events; +/** + * efi_register_notify_event - event registered by RegisterProtocolNotify() + * + * The address of this structure serves as registration value. + * + * @link: link to list of all registered events + * @event: registered event. The same event may registered for + * multiple GUIDs. + * @protocol: protocol for which the event is registered + */ +struct efi_register_notify_event { + struct list_head link; + struct efi_event *event; + efi_guid_t protocol; +}; + +/* List of all events registered by RegisterProtocolNotify() */ +extern struct list_head efi_register_notify_events; + /* Initialize efi execution environment */ efi_status_t efi_init_obj_list(void); /* Called by bootefi to initialize root node */ diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index b6dfb3916f..194df5a180 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -27,6 +27,9 @@ LIST_HEAD(efi_obj_list); /* List of all events */ LIST_HEAD(efi_events); +/* List of all events registered by RegisterProtocolNotify() */ +LIST_HEAD(efi_register_notify_events); + /* Handle of the currently executing image */ static efi_handle_t current_image; @@ -908,9 +911,21 @@ static efi_status_t EFIAPI efi_signal_event_ext(struct efi_event *event) */ static efi_status_t EFIAPI efi_close_event(struct efi_event *event) { + struct efi_register_notify_event *item, *next; + EFI_ENTRY("%p", event); if (efi_is_event(event) != EFI_SUCCESS) return EFI_EXIT(EFI_INVALID_PARAMETER); + + /* Remove protocol notify registrations for the event */ + list_for_each_entry_safe(item, next, &efi_register_notify_events, + link) { + if (event == item->event) { + list_del(&item->link); + free(item); + } + } + list_del(&event->link); free(event); return EFI_EXIT(EFI_SUCCESS); @@ -1014,6 +1029,7 @@ efi_status_t efi_add_protocol(const efi_handle_t handle, struct efi_object *efiobj; struct efi_handler *handler; efi_status_t ret; + struct efi_register_notify_event *event; efiobj = efi_search_obj(handle); if (!efiobj) @@ -1028,6 +1044,13 @@ efi_status_t efi_add_protocol(const efi_handle_t handle, handler->protocol_interface = protocol_interface; INIT_LIST_HEAD(&handler->open_infos); list_add_tail(&handler->link, &efiobj->protocols); + + /* Notify registered events */ + list_for_each_entry(event, &efi_register_notify_events, link) { + if (!guidcmp(protocol, &event->protocol)) + efi_signal_event(event->event, true); + } + if (!guidcmp(&efi_guid_device_path, protocol)) EFI_PRINT("installed device path '%pD'\n", protocol_interface); return EFI_SUCCESS; @@ -1291,8 +1314,30 @@ static efi_status_t EFIAPI efi_register_protocol_notify( struct efi_event *event, void **registration) { + struct efi_register_notify_event *item; + efi_status_t ret = EFI_SUCCESS; + EFI_ENTRY("%pUl, %p, %p", protocol, event, registration); - return EFI_EXIT(EFI_OUT_OF_RESOURCES); + + if (!protocol || !event || !registration) { + ret = EFI_INVALID_PARAMETER; + goto out; + } + + item = calloc(1, sizeof(struct efi_register_notify_event)); + if (!item) { + ret = EFI_OUT_OF_RESOURCES; + goto out; + } + + item->event = event; + memcpy(&item->protocol, protocol, sizeof(efi_guid_t)); + + list_add_tail(&item->link, &efi_register_notify_events); + + *registration = item; +out: + return EFI_EXIT(ret); } /** @@ -1307,8 +1352,7 @@ static efi_status_t EFIAPI efi_register_protocol_notify( * Return: 0 if the handle implements the protocol */ static int efi_search(enum efi_locate_search_type search_type, - const efi_guid_t *protocol, void *search_key, - efi_handle_t handle) + const efi_guid_t *protocol, efi_handle_t handle) { efi_status_t ret; @@ -1316,8 +1360,6 @@ static int efi_search(enum efi_locate_search_type search_type, case ALL_HANDLES: return 0; case BY_REGISTER_NOTIFY: - /* TODO: RegisterProtocolNotify is not implemented yet */ - return -1; case BY_PROTOCOL: ret = efi_search_protocol(handle, protocol, NULL); return (ret != EFI_SUCCESS); @@ -1329,11 +1371,12 @@ static int efi_search(enum efi_locate_search_type search_type, /** * efi_locate_handle() - locate handles implementing a protocol - * @search_type: selection criterion - * @protocol: GUID of the protocol - * @search_key: registration key - * @buffer_size: size of the buffer to receive the handles in bytes - * @buffer: buffer to receive the relevant handles + * + * @search_type: selection criterion + * @protocol: GUID of the protocol + * @search_key: registration key + * @buffer_size: size of the buffer to receive the handles in bytes + * @buffer: buffer to receive the relevant handles * * This function is meant for U-Boot internal calls. For the API implementation * of the LocateHandle service see efi_locate_handle_ext. @@ -1347,6 +1390,7 @@ static efi_status_t efi_locate_handle( { struct efi_object *efiobj; efi_uintn_t size = 0; + struct efi_register_notify_event *item, *event = NULL; /* Check parameters */ switch (search_type) { @@ -1355,8 +1399,19 @@ static efi_status_t efi_locate_handle( case BY_REGISTER_NOTIFY: if (!search_key) return EFI_INVALID_PARAMETER; - /* RegisterProtocolNotify is not implemented yet */ - return EFI_UNSUPPORTED; + /* Check that the registration key is valid */ + list_for_each_entry(item, &efi_register_notify_events, link) { + if (item == + (struct efi_register_notify_event *)search_key) { + event = item; + break; + } + } + if (!event) + return EFI_INVALID_PARAMETER; + + protocol = &event->protocol; + break; case BY_PROTOCOL: if (!protocol) return EFI_INVALID_PARAMETER; @@ -1367,7 +1422,7 @@ static efi_status_t efi_locate_handle( /* Count how much space we need */ list_for_each_entry(efiobj, &efi_obj_list, link) { - if (!efi_search(search_type, protocol, search_key, efiobj)) + if (!efi_search(search_type, protocol, efiobj)) size += sizeof(void *); } @@ -1390,7 +1445,7 @@ static efi_status_t efi_locate_handle( /* Then fill the array */ list_for_each_entry(efiobj, &efi_obj_list, link) { - if (!efi_search(search_type, protocol, search_key, efiobj)) + if (!efi_search(search_type, protocol, efiobj)) *buffer++ = efiobj; } From 24f6be7ada05f24f18aec6651dd393e82136342a Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 4 May 2019 22:44:13 +0200 Subject: [PATCH 11/17] efi_selftest: unit test for RegisterProtocolNotify() Provide a unit test for the RegisterProtocolNotify() boot service. Signed-off-by: Heinrich Schuchardt --- lib/efi_selftest/Makefile | 1 + .../efi_selftest_register_notify.c | 231 ++++++++++++++++++ 2 files changed, 232 insertions(+) create mode 100644 lib/efi_selftest/efi_selftest_register_notify.c diff --git a/lib/efi_selftest/Makefile b/lib/efi_selftest/Makefile index 4945691e67..c69ad7a9c0 100644 --- a/lib/efi_selftest/Makefile +++ b/lib/efi_selftest/Makefile @@ -27,6 +27,7 @@ efi_selftest_gop.o \ efi_selftest_loaded_image.o \ efi_selftest_manageprotocols.o \ efi_selftest_memory.o \ +efi_selftest_register_notify.o \ efi_selftest_rtc.o \ efi_selftest_snp.o \ efi_selftest_textinput.o \ diff --git a/lib/efi_selftest/efi_selftest_register_notify.c b/lib/efi_selftest/efi_selftest_register_notify.c new file mode 100644 index 0000000000..ee0ef395de --- /dev/null +++ b/lib/efi_selftest/efi_selftest_register_notify.c @@ -0,0 +1,231 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * efi_selftest_register_notify + * + * Copyright (c) 2019 Heinrich Schuchardt + * + * This unit test checks the following protocol services: + * InstallProtocolInterface, UninstallProtocolInterface, + * RegisterProtocolNotify, CreateEvent, CloseEvent. + */ + +#include + +/* + * The test currently does not actually call the interface function. + * So this is just a dummy structure. + */ +struct interface { + void (EFIAPI * inc)(void); +}; + +struct context { + void *registration_key; + efi_uintn_t notify_count; + efi_uintn_t handle_count; + efi_handle_t *handles; +}; + +static struct efi_boot_services *boottime; +static efi_guid_t guid1 = + EFI_GUID(0x2e7ca819, 0x21d3, 0x0a3a, + 0xf7, 0x91, 0x82, 0x1f, 0x7a, 0x83, 0x67, 0xaf); +static efi_guid_t guid2 = + EFI_GUID(0xf909f2bb, 0x90a8, 0x0d77, + 0x94, 0x0c, 0x3e, 0xa8, 0xea, 0x38, 0xd6, 0x6f); +static struct context context; +static struct efi_event *event; + +/* + * Notification function, increments the notification count if parameter + * context is provided. + * + * @event notified event + * @context pointer to the notification count + */ +static void EFIAPI notify(struct efi_event *event, void *context) +{ + struct context *cp = context; + efi_status_t ret; + + cp->notify_count++; + + ret = boottime->locate_handle_buffer(BY_REGISTER_NOTIFY, NULL, + cp->registration_key, + &cp->handle_count, + &cp->handles); + if (ret != EFI_SUCCESS) + cp->handle_count = 0; +} + +/* + * 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) +{ + efi_status_t ret; + + boottime = systable->boottime; + + ret = boottime->create_event(EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, notify, &context, + &event); + if (ret != EFI_SUCCESS) { + efi_st_error("could not create event\n"); + return EFI_ST_FAILURE; + } + + ret = boottime->register_protocol_notify(&guid1, event, + &context.registration_key); + if (ret != EFI_SUCCESS) { + efi_st_error("could not register event\n"); + return EFI_ST_FAILURE; + } + + return EFI_ST_SUCCESS; +} + +/* + * Tear down unit test. + * + */ +static int teardown(void) +{ + efi_status_t ret; + + if (event) { + ret = boottime->close_event(event); + event = NULL; + if (ret != EFI_SUCCESS) { + efi_st_error("could not close event\n"); + return EFI_ST_FAILURE; + } + } + + return EFI_ST_SUCCESS; +} + +/* + * Execute unit test. + * + */ +static int execute(void) +{ + efi_status_t ret; + efi_handle_t handle1 = NULL, handle2 = NULL; + struct interface interface1, interface2; + + ret = boottime->install_protocol_interface(&handle1, &guid1, + EFI_NATIVE_INTERFACE, + &interface1); + if (ret != EFI_SUCCESS) { + efi_st_error("could not install interface\n"); + return EFI_ST_FAILURE; + } + if (!context.notify_count) { + efi_st_error("install was not notified\n"); + return EFI_ST_FAILURE; + } + if (context.notify_count > 1) { + efi_st_error("install was notified too often\n"); + return EFI_ST_FAILURE; + } + if (context.handle_count != 1) { + efi_st_error("LocateHandle failed\n"); + return EFI_ST_FAILURE; + } + ret = boottime->free_pool(context.handles); + if (ret != EFI_SUCCESS) { + efi_st_error("FreePool failed\n"); + return EFI_ST_FAILURE; + } + context.notify_count = 0; + ret = boottime->install_protocol_interface(&handle1, &guid2, + EFI_NATIVE_INTERFACE, + &interface1); + if (ret != EFI_SUCCESS) { + efi_st_error("could not install interface\n"); + return EFI_ST_FAILURE; + } + if (context.notify_count) { + efi_st_error("wrong protocol was notified\n"); + return EFI_ST_FAILURE; + } + context.notify_count = 0; + ret = boottime->reinstall_protocol_interface(handle1, &guid1, + &interface1, &interface2); + if (ret != EFI_SUCCESS) { + efi_st_error("could not reinstall interface\n"); + return EFI_ST_FAILURE; + } + if (!context.notify_count) { + efi_st_error("reinstall was not notified\n"); + return EFI_ST_FAILURE; + } + if (context.notify_count > 1) { + efi_st_error("reinstall was notified too often\n"); + return EFI_ST_FAILURE; + } + if (context.handle_count != 1) { + efi_st_error("LocateHandle failed\n"); + return EFI_ST_FAILURE; + } + ret = boottime->free_pool(context.handles); + if (ret != EFI_SUCCESS) { + efi_st_error("FreePool failed\n"); + return EFI_ST_FAILURE; + } + context.notify_count = 0; + ret = boottime->install_protocol_interface(&handle2, &guid1, + EFI_NATIVE_INTERFACE, + &interface1); + if (ret != EFI_SUCCESS) { + efi_st_error("could not install interface\n"); + return EFI_ST_FAILURE; + } + if (!context.notify_count) { + efi_st_error("install was not notified\n"); + return EFI_ST_FAILURE; + } + if (context.notify_count > 1) { + efi_st_error("install was notified too often\n"); + return EFI_ST_FAILURE; + } + if (context.handle_count != 2) { + efi_st_error("LocateHandle failed\n"); + return EFI_ST_FAILURE; + } + ret = boottime->free_pool(context.handles); + if (ret != EFI_SUCCESS) { + efi_st_error("FreePool failed\n"); + return EFI_ST_FAILURE; + } + + ret = boottime->uninstall_multiple_protocol_interfaces + (handle1, &guid1, &interface2, + &guid2, &interface1, NULL); + if (ret != EFI_SUCCESS) { + efi_st_error("UninstallMultipleProtocolInterfaces failed\n"); + return EFI_ST_FAILURE; + } + ret = boottime->uninstall_multiple_protocol_interfaces + (handle2, &guid1, &interface1, NULL); + if (ret != EFI_SUCCESS) { + efi_st_error("UninstallMultipleProtocolInterfaces failed\n"); + return EFI_ST_FAILURE; + } + + return EFI_ST_SUCCESS; +} + +EFI_UNIT_TEST(regprotnot) = { + .name = "register protocol notify", + .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT, + .setup = setup, + .execute = execute, + .teardown = teardown, +}; From ec163faa1b7e0314ae5703c7c2203b1615e78c88 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sun, 5 May 2019 10:37:51 +0200 Subject: [PATCH 12/17] efi_loader: CloseProtocol() requires valid agent handle Return EFI_INVALID_PARAMETER from CloseProtcol() if the agent handle is not valid. Return EFI_INVALID_PARAMETER if the optional controller handle is not valid. Return immediately from efi_search_obj if the handle is NULL. Signed-off-by: Heinrich Schuchardt --- lib/efi_loader/efi_boottime.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 194df5a180..967b39b223 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -971,11 +971,13 @@ struct efi_object *efi_search_obj(const efi_handle_t handle) { struct efi_object *efiobj; + if (!handle) + return NULL; + list_for_each_entry(efiobj, &efi_obj_list, link) { if (efiobj == handle) return efiobj; } - return NULL; } @@ -1982,7 +1984,8 @@ static efi_status_t EFIAPI efi_close_protocol(efi_handle_t handle, EFI_ENTRY("%p, %pUl, %p, %p", handle, protocol, agent_handle, controller_handle); - if (!agent_handle) { + if (!efi_search_obj(agent_handle) || + (controller_handle && !efi_search_obj(controller_handle))) { r = EFI_INVALID_PARAMETER; goto out; } From 529886a097c2f4e3b7692dcbd4da8d2b4dcf0c2a Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sun, 5 May 2019 11:56:23 +0200 Subject: [PATCH 13/17] efi_loader: use EFI_PRINT instead of debug For correct indentation of messages in the UEFI API implementation use EFI_PRINT() instead of debug(). Signed-off-by: Heinrich Schuchardt --- lib/efi_loader/efi_boottime.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 967b39b223..ab299aa17f 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -267,7 +267,7 @@ static unsigned long EFIAPI efi_raise_tpl(efi_uintn_t new_tpl) EFI_ENTRY("0x%zx", new_tpl); if (new_tpl < efi_tpl) - debug("WARNING: new_tpl < current_tpl in %s\n", __func__); + EFI_PRINT("WARNING: new_tpl < current_tpl in %s\n", __func__); efi_tpl = new_tpl; if (efi_tpl > TPL_HIGH_LEVEL) efi_tpl = TPL_HIGH_LEVEL; @@ -290,7 +290,7 @@ static void EFIAPI efi_restore_tpl(efi_uintn_t old_tpl) EFI_ENTRY("0x%zx", old_tpl); if (old_tpl > efi_tpl) - debug("WARNING: old_tpl > current_tpl in %s\n", __func__); + EFI_PRINT("WARNING: old_tpl > current_tpl in %s\n", __func__); efi_tpl = old_tpl; if (efi_tpl > TPL_HIGH_LEVEL) efi_tpl = TPL_HIGH_LEVEL; @@ -1093,11 +1093,9 @@ static efi_status_t EFIAPI efi_install_protocol_interface( r = efi_create_handle(handle); if (r != EFI_SUCCESS) goto out; - debug("%sEFI: new handle %p\n", indent_string(nesting_level), - *handle); + EFI_PRINT("new handle %p\n", *handle); } else { - debug("%sEFI: handle %p\n", indent_string(nesting_level), - *handle); + EFI_PRINT("handle %p\n", *handle); } /* Add new protocol */ r = efi_add_protocol(*handle, protocol, protocol_interface); @@ -2706,10 +2704,9 @@ efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle, * missed out steps of EFI_CALL. */ assert(__efi_entry_check()); - debug("%sEFI: %lu returned by started image\n", - __efi_nesting_dec(), - (unsigned long)((uintptr_t)image_obj->exit_status & - ~EFI_ERROR_MASK)); + EFI_PRINT("%lu returned by started image\n", + (unsigned long)((uintptr_t)image_obj->exit_status & + ~EFI_ERROR_MASK)); current_image = parent_image; return EFI_EXIT(image_obj->exit_status); } From e7c3cd6b67d87383fa48b41c1b4037b75ceab597 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sun, 5 May 2019 11:24:53 +0200 Subject: [PATCH 14/17] efi_loader: HandleProtocol parameter checks HandleProtocol() and OpenProtocol() have to return EFI_UNSUPPORTED if the protocol is not installed on the handle. Signed-off-by: Heinrich Schuchardt --- lib/efi_loader/efi_boottime.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index ab299aa17f..6d86dafc16 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -2636,8 +2636,15 @@ static efi_status_t EFIAPI efi_open_protocol } r = efi_search_protocol(handle, protocol, &handler); - if (r != EFI_SUCCESS) + switch (r) { + case EFI_SUCCESS: + break; + case EFI_NOT_FOUND: + r = EFI_UNSUPPORTED; goto out; + default: + goto out; + } r = efi_protocol_open(handler, protocol_interface, agent_handle, controller_handle, attributes); From 84a918e8ce6cdc968a17e91768650bfe9eb8844a Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sun, 5 May 2019 16:55:06 +0200 Subject: [PATCH 15/17] efi_loader: LoadImage() check parent image If the parent image handle does not refer to a loaded image return EFI_INVALID_PARAMETER. (UEFI SCT II 2017: 3.4.1 LoadImage() - 5.1.4.1.1) Mark our root node as a loaded image to avoid an error when using it as parent image. Signed-off-by: Heinrich Schuchardt --- include/efi_loader.h | 1 + lib/efi_loader/efi_boottime.c | 7 ++++- lib/efi_loader/efi_root_node.c | 48 ++++++++++++++++++---------------- 3 files changed, 33 insertions(+), 23 deletions(-) diff --git a/include/efi_loader.h b/include/efi_loader.h index d3a1d4c465..07ef14ba1c 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -187,6 +187,7 @@ struct efi_handler { */ enum efi_object_type { EFI_OBJECT_TYPE_UNDEFINED = 0, + EFI_OBJECT_TYPE_U_BOOT_FIRMWARE, EFI_OBJECT_TYPE_LOADED_IMAGE, EFI_OBJECT_TYPE_STARTED_IMAGE, }; diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 6d86dafc16..ef9e378189 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -1760,7 +1760,7 @@ efi_status_t EFIAPI efi_load_image(bool boot_policy, EFI_ENTRY("%d, %p, %pD, %p, %zd, %p", boot_policy, parent_image, file_path, source_buffer, source_size, image_handle); - if (!image_handle || !parent_image) { + if (!image_handle || !efi_search_obj(parent_image)) { ret = EFI_INVALID_PARAMETER; goto error; } @@ -1769,6 +1769,11 @@ efi_status_t EFIAPI efi_load_image(bool boot_policy, ret = EFI_NOT_FOUND; goto error; } + /* The parent image handle must refer to a loaded image */ + if (!parent_image->type) { + ret = EFI_INVALID_PARAMETER; + goto error; + } if (!source_buffer) { ret = efi_load_image_from_path(file_path, &dest_buffer, diff --git a/lib/efi_loader/efi_root_node.c b/lib/efi_loader/efi_root_node.c index e0fcbb85a4..38514e0820 100644 --- a/lib/efi_loader/efi_root_node.c +++ b/lib/efi_loader/efi_root_node.c @@ -28,6 +28,7 @@ struct efi_root_dp { */ efi_status_t efi_root_node_register(void) { + efi_status_t ret; struct efi_root_dp *dp; /* Create device path protocol */ @@ -47,28 +48,31 @@ efi_status_t efi_root_node_register(void) dp->end.length = sizeof(struct efi_device_path); /* Create root node and install protocols */ - return EFI_CALL(efi_install_multiple_protocol_interfaces(&efi_root, - /* Device path protocol */ - &efi_guid_device_path, dp, - /* Device path to text protocol */ - &efi_guid_device_path_to_text_protocol, - (void *)&efi_device_path_to_text, - /* Device path utilities protocol */ - &efi_guid_device_path_utilities_protocol, - (void *)&efi_device_path_utilities, - /* Unicode collation protocol */ - &efi_guid_unicode_collation_protocol, - (void *)&efi_unicode_collation_protocol, + ret = EFI_CALL(efi_install_multiple_protocol_interfaces + (&efi_root, + /* Device path protocol */ + &efi_guid_device_path, dp, + /* Device path to text protocol */ + &efi_guid_device_path_to_text_protocol, + (void *)&efi_device_path_to_text, + /* Device path utilities protocol */ + &efi_guid_device_path_utilities_protocol, + (void *)&efi_device_path_utilities, + /* Unicode collation protocol */ + &efi_guid_unicode_collation_protocol, + (void *)&efi_unicode_collation_protocol, #if CONFIG_IS_ENABLED(EFI_LOADER_HII) - /* HII string protocol */ - &efi_guid_hii_string_protocol, - (void *)&efi_hii_string, - /* HII database protocol */ - &efi_guid_hii_database_protocol, - (void *)&efi_hii_database, - /* HII configuration routing protocol */ - &efi_guid_hii_config_routing_protocol, - (void *)&efi_hii_config_routing, + /* HII string protocol */ + &efi_guid_hii_string_protocol, + (void *)&efi_hii_string, + /* HII database protocol */ + &efi_guid_hii_database_protocol, + (void *)&efi_hii_database, + /* HII configuration routing protocol */ + &efi_guid_hii_config_routing_protocol, + (void *)&efi_hii_config_routing, #endif - NULL)); + NULL)); + efi_root->type = EFI_OBJECT_TYPE_U_BOOT_FIRMWARE; + return ret; } From 470dfa50da799720ed6513b89b9237064415cb73 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sun, 5 May 2019 16:55:06 +0200 Subject: [PATCH 16/17] efi_loader: LoadImage() check source size If the size of the source buffer is 0, return EFI_LOAD_ERROR. (UEFI SCT II 2017: 3.4.1 LoadImage() - 5.1.4.1.6) Signed-off-by: Heinrich Schuchardt --- lib/efi_loader/efi_boottime.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index ef9e378189..89966a2f12 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -1781,6 +1781,10 @@ efi_status_t EFIAPI efi_load_image(bool boot_policy, if (ret != EFI_SUCCESS) goto error; } else { + if (!source_size) { + ret = EFI_LOAD_ERROR; + goto error; + } dest_buffer = source_buffer; } /* split file_path which contains both the device and file parts */ From b015ab57bf558daa1c768995a7a7f1df2d40191e Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sun, 5 May 2019 21:58:35 +0200 Subject: [PATCH 17/17] efi_loader: signature of ExitBootServices() Consistently use efi_uintn_t as type of memory keys. Signed-off-by: Heinrich Schuchardt Reviewed-by: Alexander Graf --- include/efi_api.h | 3 ++- lib/efi_loader/efi_boottime.c | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/include/efi_api.h b/include/efi_api.h index b2ae279747..755c405793 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -128,7 +128,8 @@ struct efi_boot_services { efi_status_t exit_status, efi_uintn_t exitdata_size, u16 *exitdata); efi_status_t (EFIAPI *unload_image)(efi_handle_t image_handle); - efi_status_t (EFIAPI *exit_boot_services)(efi_handle_t, unsigned long); + efi_status_t (EFIAPI *exit_boot_services)(efi_handle_t image_handle, + efi_uintn_t map_key); efi_status_t (EFIAPI *get_next_monotonic_count)(u64 *count); efi_status_t (EFIAPI *stall)(unsigned long usecs); diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 89966a2f12..b97d55cb45 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -1841,11 +1841,11 @@ static void efi_exit_caches(void) * Return: status code */ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, - unsigned long map_key) + efi_uintn_t map_key) { struct efi_event *evt; - EFI_ENTRY("%p, %ld", image_handle, map_key); + EFI_ENTRY("%p, %zx", image_handle, map_key); /* Check that the caller has read the current memory map */ if (map_key != efi_memory_map_key)