From 05b6f56ead11f6a530b0cbdbd60e45f3b3cfcb07 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Mon, 11 Dec 2017 20:10:20 +0100 Subject: efi_loader: use wide string do define firmware vendor As the U-Boot is compiled with -fshort-wchar we can define the firmware vendor constant as wide string. Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- lib/efi_loader/efi_boottime.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'lib/efi_loader/efi_boottime.c') diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index b90bd0b426..02bc9fdcf0 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -2201,8 +2201,7 @@ static const struct efi_boot_services efi_boot_services = { }; -static uint16_t __efi_runtime_data firmware_vendor[] = - { 'D','a','s',' ','U','-','b','o','o','t',0 }; +static uint16_t __efi_runtime_data firmware_vendor[] = L"Das U-Boot"; struct efi_system_table __efi_runtime_data systab = { .hdr = { -- cgit v1.2.3 From 727a1afb341b00cdb30c473a0edcb464fea75577 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Thu, 18 Jan 2018 20:28:43 +0100 Subject: efi_loader: correctly call images Avoid a failed assertion when an EFI app calls an EFI app. Avoid that the indent level increases when calling 'bootefi hello' repeatedly. Avoid negative indent level when an EFI app calls an EFI app that calls an EFI app (e.g. iPXE loads grub which starts the kernel). Return the status code of a loaded image that returns without calling the Exit boot service. Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- lib/efi_loader/efi_boottime.c | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) (limited to 'lib/efi_loader/efi_boottime.c') diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 02bc9fdcf0..965eb1f0c5 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -1371,6 +1371,7 @@ static efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle, { ulong (*entry)(void *image_handle, struct efi_system_table *st); struct efi_loaded_image *info = image_handle; + efi_status_t ret; EFI_ENTRY("%p, %p, %p", image_handle, exit_data_size, exit_data); entry = info->reserved; @@ -1379,18 +1380,37 @@ static efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle, /* call the image! */ if (setjmp(&info->exit_jmp)) { - /* We returned from the child image */ + /* + * We called the entry point of the child image with EFI_CALL + * in the lines below. The child image called the Exit() boot + * service efi_exit() which executed the long jump that brought + * us to the current line. This implies that the second half + * of the EFI_CALL macro has not been executed. + */ +#ifdef CONFIG_ARM + /* + * efi_exit() called efi_restore_gd(). We have to undo this + * otherwise __efi_entry_check() will put the wrong value into + * app_gd. + */ + gd = app_gd; +#endif + /* + * To get ready to call EFI_EXIT below we have to execute the + * 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)info->exit_status & + ~EFI_ERROR_MASK)); return EFI_EXIT(info->exit_status); } - __efi_nesting_dec(); - __efi_exit_check(); - entry(image_handle, &systab); - __efi_entry_check(); - __efi_nesting_inc(); + ret = EFI_CALL(entry(image_handle, &systab)); /* Should usually never get here */ - return EFI_EXIT(EFI_SUCCESS); + return EFI_EXIT(ret); } /* @@ -1427,7 +1447,7 @@ static efi_status_t EFIAPI efi_exit(efi_handle_t image_handle, exit_data_size, exit_data); /* Make sure entry/exit counts for EFI world cross-overs match */ - __efi_exit_check(); + EFI_EXIT(exit_status); /* * But longjmp out with the U-Boot gd, not the application's, as -- cgit v1.2.3 From 32e6fed6e93fad0b38aa5bdec085a3ae316aa97b Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Thu, 11 Jan 2018 08:15:55 +0100 Subject: efi_loader: simplify efi_remove_all_protocols Replace list_for_each_safe() and list_entry() by list_for_each_entry_safe(). Signed-off-by: Heinrich Schuchardt Reviewed-by: Simon Glass Signed-off-by: Alexander Graf --- lib/efi_loader/efi_boottime.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'lib/efi_loader/efi_boottime.c') diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 965eb1f0c5..f4c42eb9f9 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -425,18 +425,15 @@ efi_status_t efi_remove_protocol(const void *handle, const efi_guid_t *protocol, efi_status_t efi_remove_all_protocols(const void *handle) { struct efi_object *efiobj; - struct list_head *lhandle; - struct list_head *pos; + struct efi_handler *protocol; + struct efi_handler *pos; efiobj = efi_search_obj(handle); if (!efiobj) return EFI_INVALID_PARAMETER; - list_for_each_safe(lhandle, pos, &efiobj->protocols) { - struct efi_handler *protocol; + list_for_each_entry_safe(protocol, pos, &efiobj->protocols, link) { efi_status_t ret; - protocol = list_entry(lhandle, struct efi_handler, link); - ret = efi_remove_protocol(handle, protocol->guid, protocol->protocol_interface); if (ret != EFI_SUCCESS) -- cgit v1.2.3 From fe1599daf5d2ca26ed6468ac78a1253af5ac53ea Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Thu, 11 Jan 2018 08:15:57 +0100 Subject: efi_loader: list of open protocol infos Add a list of open protocol infos to each protocol of a handle. Provide helper functions to access the list items. Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- include/efi_loader.h | 15 ++++++++++++++- lib/efi_loader/efi_boottime.c | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) (limited to 'lib/efi_loader/efi_boottime.c') diff --git a/include/efi_loader.h b/include/efi_loader.h index 6185055e78..637e6e166d 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -96,15 +96,28 @@ extern const efi_guid_t efi_file_info_guid; extern unsigned int __efi_runtime_start, __efi_runtime_stop; extern unsigned int __efi_runtime_rel_start, __efi_runtime_rel_stop; +/* + * When a protocol is opened a open protocol info entry is created. + * These are maintained in a list. + */ +struct efi_open_protocol_info_item { + /* Link to the list of open protocol info entries of a protocol */ + struct list_head link; + struct efi_open_protocol_info_entry info; +}; + /* * When the UEFI payload wants to open a protocol on an object to get its * interface (usually a struct with callback functions), this struct maps the - * protocol GUID to the respective protocol interface */ + * protocol GUID to the respective protocol interface + */ struct efi_handler { /* Link to the list of protocols of a handle */ struct list_head link; const efi_guid_t *guid; void *protocol_interface; + /* Link to the list of open protocol info items */ + struct list_head open_infos; }; /* diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index f4c42eb9f9..8fb4e3ed23 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -814,6 +814,40 @@ struct efi_object *efi_search_obj(const void *handle) return NULL; } +/* + * Create open protocol info entry and add it to a protocol. + * + * @handler handler of a protocol + * @return open protocol info entry + */ +static struct efi_open_protocol_info_entry *efi_create_open_info( + struct efi_handler *handler) +{ + struct efi_open_protocol_info_item *item; + + item = calloc(1, sizeof(struct efi_open_protocol_info_item)); + if (!item) + return NULL; + /* Append the item to the open protocol info list. */ + list_add_tail(&item->link, &handler->open_infos); + + return &item->info; +} + +/* + * Remove an open protocol info entry from a protocol. + * + * @handler handler of a protocol + * @return status code + */ +static efi_status_t efi_delete_open_info( + struct efi_open_protocol_info_item *item) +{ + list_del(&item->link); + free(item); + return EFI_SUCCESS; +} + /* * Install new protocol on a handle. * @@ -840,6 +874,7 @@ efi_status_t efi_add_protocol(const void *handle, const efi_guid_t *protocol, return EFI_OUT_OF_RESOURCES; handler->guid = protocol; handler->protocol_interface = protocol_interface; + INIT_LIST_HEAD(&handler->open_infos); list_add_tail(&handler->link, &efiobj->protocols); return EFI_SUCCESS; } -- cgit v1.2.3 From 191a41cc3295f1f468f70fe52b4b2ec93992abce Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Thu, 11 Jan 2018 08:15:58 +0100 Subject: efi_loader: open_info in OpenProtocol efi_open_protocol has to keep track of opened protocols. OpenProtocol enters the agent and controller handle information into this list. A unit test is supplied with a subsequent patch. Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- lib/efi_loader/efi_boottime.c | 107 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 103 insertions(+), 4 deletions(-) (limited to 'lib/efi_loader/efi_boottime.c') diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 8fb4e3ed23..ce3ae23057 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -2120,6 +2120,101 @@ static void EFIAPI efi_set_mem(void *buffer, size_t size, uint8_t value) EFI_EXIT(EFI_SUCCESS); } +/* + * Open protocol interface on a handle. + * + * @handler handler of a protocol + * @protocol_interface interface implementing the protocol + * @agent_handle handle of the driver + * @controller_handle handle of the controller + * @attributes attributes indicating how to open the protocol + * @return status code + */ +static efi_status_t efi_protocol_open( + struct efi_handler *handler, + void **protocol_interface, void *agent_handle, + void *controller_handle, uint32_t attributes) +{ + struct efi_open_protocol_info_item *item; + struct efi_open_protocol_info_entry *match = NULL; + bool opened_by_driver = false; + bool opened_exclusive = false; + + /* If there is no agent, only return the interface */ + if (!agent_handle) + goto out; + + /* For TEST_PROTOCOL ignore interface attribute */ + if (attributes != EFI_OPEN_PROTOCOL_TEST_PROTOCOL) + *protocol_interface = NULL; + + /* + * Check if the protocol is already opened by a driver with the same + * attributes or opened exclusively + */ + list_for_each_entry(item, &handler->open_infos, link) { + if (item->info.agent_handle == agent_handle) { + if ((attributes & EFI_OPEN_PROTOCOL_BY_DRIVER) && + (item->info.attributes == attributes)) + return EFI_ALREADY_STARTED; + } + if (item->info.attributes & EFI_OPEN_PROTOCOL_EXCLUSIVE) + opened_exclusive = true; + } + + /* Only one controller can open the protocol exclusively */ + if (opened_exclusive && attributes & + (EFI_OPEN_PROTOCOL_EXCLUSIVE | EFI_OPEN_PROTOCOL_BY_DRIVER)) + return EFI_ACCESS_DENIED; + + /* Prepare exclusive opening */ + if (attributes & EFI_OPEN_PROTOCOL_EXCLUSIVE) { + /* Try to disconnect controllers */ + list_for_each_entry(item, &handler->open_infos, link) { + if (item->info.attributes == + EFI_OPEN_PROTOCOL_BY_DRIVER) + EFI_CALL(efi_disconnect_controller( + item->info.controller_handle, + item->info.agent_handle, + NULL)); + } + opened_by_driver = false; + /* Check if all controllers are disconnected */ + list_for_each_entry(item, &handler->open_infos, link) { + if (item->info.attributes & EFI_OPEN_PROTOCOL_BY_DRIVER) + opened_by_driver = true; + } + /* Only one controller can be conncected */ + if (opened_by_driver) + return EFI_ACCESS_DENIED; + } + + /* Find existing entry */ + list_for_each_entry(item, &handler->open_infos, link) { + if (item->info.agent_handle == agent_handle && + item->info.controller_handle == controller_handle) + match = &item->info; + } + /* None found, create one */ + if (!match) { + match = efi_create_open_info(handler); + if (!match) + return EFI_OUT_OF_RESOURCES; + } + + match->agent_handle = agent_handle; + match->controller_handle = controller_handle; + match->attributes = attributes; + match->open_count++; + +out: + /* For TEST_PROTOCOL ignore interface attribute. */ + if (attributes != EFI_OPEN_PROTOCOL_TEST_PROTOCOL) + *protocol_interface = handler->protocol_interface; + + return EFI_SUCCESS; +} + /* * Open protocol interface on a handle. * @@ -2161,12 +2256,16 @@ static efi_status_t EFIAPI efi_open_protocol( case EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER: if (controller_handle == handle) goto out; + /* fall-through */ case EFI_OPEN_PROTOCOL_BY_DRIVER: case EFI_OPEN_PROTOCOL_BY_DRIVER | EFI_OPEN_PROTOCOL_EXCLUSIVE: - if (controller_handle == NULL) + /* Check that the controller handle is valid */ + if (!efi_search_obj(controller_handle)) goto out; + /* fall-through */ case EFI_OPEN_PROTOCOL_EXCLUSIVE: - if (agent_handle == NULL) + /* Check that the agent handle is valid */ + if (!efi_search_obj(agent_handle)) goto out; break; default: @@ -2177,8 +2276,8 @@ static efi_status_t EFIAPI efi_open_protocol( if (r != EFI_SUCCESS) goto out; - if (attributes != EFI_OPEN_PROTOCOL_TEST_PROTOCOL) - *protocol_interface = handler->protocol_interface; + r = efi_protocol_open(handler, protocol_interface, agent_handle, + controller_handle, attributes); out: return EFI_EXIT(r); } -- cgit v1.2.3 From 3b8a489c9f9c4fd9c4c02a7fd8e76017b027ec94 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Thu, 11 Jan 2018 08:15:59 +0100 Subject: efi_loader: open_info in CloseProtocol efi_open_protocol and efi_close_protocol have to keep track of opened protocols. Check if the protocol was opened for the same agent and controller. Remove all open protocol information for this pair. Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- lib/efi_loader/efi_boottime.c | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) (limited to 'lib/efi_loader/efi_boottime.c') diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index ce3ae23057..f8cc318074 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -1695,9 +1695,33 @@ static efi_status_t EFIAPI efi_close_protocol(void *handle, void *agent_handle, void *controller_handle) { + struct efi_handler *handler; + struct efi_open_protocol_info_item *item; + struct efi_open_protocol_info_item *pos; + efi_status_t r; + EFI_ENTRY("%p, %pUl, %p, %p", handle, protocol, agent_handle, controller_handle); - return EFI_EXIT(EFI_NOT_FOUND); + + if (!agent_handle) { + r = EFI_INVALID_PARAMETER; + goto out; + } + r = efi_search_protocol(handle, protocol, &handler); + if (r != EFI_SUCCESS) + goto out; + + r = EFI_NOT_FOUND; + list_for_each_entry_safe(item, pos, &handler->open_infos, link) { + if (item->info.agent_handle == agent_handle && + item->info.controller_handle == controller_handle) { + efi_delete_open_info(item); + r = EFI_SUCCESS; + break; + } + } +out: + return EFI_EXIT(r); } /* -- cgit v1.2.3 From e3fbbc36f2d94f15d4d9bcd52d59d82d849a8e21 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Thu, 11 Jan 2018 08:16:00 +0100 Subject: efi_loader: implement OpenProtocolInformation efi_open_protocol_information provides the agent and controller handles as well as the attributes and open count of an protocol on a handle. Signed-off-by: Heinrich Schuchardt [agraf: fix counting error] Signed-off-by: Alexander Graf --- lib/efi_loader/efi_boottime.c | 42 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) (limited to 'lib/efi_loader/efi_boottime.c') diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index f8cc318074..7a6c282f81 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -1742,9 +1742,49 @@ static efi_status_t EFIAPI efi_open_protocol_information(efi_handle_t handle, struct efi_open_protocol_info_entry **entry_buffer, efi_uintn_t *entry_count) { + unsigned long buffer_size; + unsigned long count; + struct efi_handler *handler; + struct efi_open_protocol_info_item *item; + efi_status_t r; + EFI_ENTRY("%p, %pUl, %p, %p", handle, protocol, entry_buffer, entry_count); - return EFI_EXIT(EFI_NOT_FOUND); + + /* Check parameters */ + if (!entry_buffer) { + r = EFI_INVALID_PARAMETER; + goto out; + } + r = efi_search_protocol(handle, protocol, &handler); + if (r != EFI_SUCCESS) + goto out; + + /* Count entries */ + count = 0; + list_for_each_entry(item, &handler->open_infos, link) { + if (item->info.open_count) + ++count; + } + *entry_count = count; + *entry_buffer = NULL; + if (!count) { + r = EFI_SUCCESS; + goto out; + } + + /* Copy entries */ + buffer_size = count * sizeof(struct efi_open_protocol_info_entry); + r = efi_allocate_pool(EFI_ALLOCATE_ANY_PAGES, buffer_size, + (void **)entry_buffer); + if (r != EFI_SUCCESS) + goto out; + list_for_each_entry_reverse(item, &handler->open_infos, link) { + if (item->info.open_count) + (*entry_buffer)[--count] = item->info; + } +out: + return EFI_EXIT(r); } /* -- cgit v1.2.3 From d550414434f4250e80847382fac92f7e3891d887 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Thu, 11 Jan 2018 08:16:01 +0100 Subject: efi_loader: debug output installed device path When a device path protocol is installed write the device path to the console in debug mode. For printing the new macro EFI_PRINT is used, which can be reused for future diagnostic output. Remove unused EFI_PRINT_GUID macro Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- include/efi_loader.h | 7 ++++--- lib/efi_loader/efi_boottime.c | 2 ++ 2 files changed, 6 insertions(+), 3 deletions(-) (limited to 'lib/efi_loader/efi_boottime.c') diff --git a/include/efi_loader.h b/include/efi_loader.h index 637e6e166d..4198329230 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -69,10 +69,11 @@ const char *__efi_nesting_dec(void); } while(0) /* - * Write GUID + * Write an indented message with EFI prefix */ -#define EFI_PRINT_GUID(txt, guid) ({ \ - debug("%sEFI: %s %pUl\n", __efi_nesting(), txt, guid); \ +#define EFI_PRINT(format, ...) ({ \ + debug("%sEFI: " format, __efi_nesting(), \ + ##__VA_ARGS__); \ }) extern struct efi_runtime_services efi_runtime_services; diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 7a6c282f81..673c66a4aa 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -876,6 +876,8 @@ efi_status_t efi_add_protocol(const void *handle, const efi_guid_t *protocol, handler->protocol_interface = protocol_interface; INIT_LIST_HEAD(&handler->open_infos); list_add_tail(&handler->link, &efiobj->protocols); + if (!guidcmp(&efi_guid_device_path, protocol)) + EFI_PRINT("installed device path '%pD'\n", protocol_interface); return EFI_SUCCESS; } -- cgit v1.2.3 From f0959dbee2b043c4b1bf16a570be9242cf603819 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Thu, 11 Jan 2018 08:16:02 +0100 Subject: efi_loader: implement ConnectController Implement the ConnectController boot service. A unit test is supplied in a subsequent patch. Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- include/efi_api.h | 22 +++++ include/efi_loader.h | 2 + lib/efi_loader/efi_boottime.c | 187 ++++++++++++++++++++++++++++++++++++------ 3 files changed, 187 insertions(+), 24 deletions(-) (limited to 'lib/efi_loader/efi_boottime.c') diff --git a/include/efi_api.h b/include/efi_api.h index 46963f2891..81e580dbbc 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -805,4 +805,26 @@ struct efi_file_info { s16 file_name[0]; }; +#define EFI_DRIVER_BINDING_PROTOCOL_GUID \ + EFI_GUID(0x18a031ab, 0xb443, 0x4d1a,\ + 0xa5, 0xc0, 0x0c, 0x09, 0x26, 0x1e, 0x9f, 0x71) +struct efi_driver_binding_protocol { + efi_status_t (EFIAPI * supported)( + struct efi_driver_binding_protocol *this, + efi_handle_t controller_handle, + struct efi_device_path *remaining_device_path); + efi_status_t (EFIAPI * start)( + struct efi_driver_binding_protocol *this, + efi_handle_t controller_handle, + struct efi_device_path *remaining_device_path); + efi_status_t (EFIAPI * stop)( + struct efi_driver_binding_protocol *this, + efi_handle_t controller_handle, + efi_uintn_t number_of_children, + efi_handle_t *child_handle_buffer); + u32 version; + efi_handle_t image_handle; + efi_handle_t driver_binding_handle; +}; + #endif diff --git a/include/efi_loader.h b/include/efi_loader.h index 4198329230..6726c44c47 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -89,6 +89,8 @@ uint16_t *efi_dp_str(struct efi_device_path *dp); extern const efi_guid_t efi_global_variable_guid; extern const efi_guid_t efi_guid_console_control; extern const efi_guid_t efi_guid_device_path; +/* GUID of the EFI_DRIVER_BINDING_PROTOCOL */ +extern const efi_guid_t efi_guid_driver_binding_protocol; extern const efi_guid_t efi_guid_loaded_image; extern const efi_guid_t efi_guid_device_path_to_text_protocol; extern const efi_guid_t efi_simple_file_system_protocol_guid; diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 673c66a4aa..09d5168772 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -56,6 +56,9 @@ static volatile void *efi_gd, *app_gd; static int entry_count; static int nesting_level; +/* GUID of the EFI_DRIVER_BINDING_PROTOCOL */ +const efi_guid_t efi_guid_driver_binding_protocol = + EFI_DRIVER_BINDING_PROTOCOL_GUID; /* Called on every callback entry */ int __efi_entry_check(void) @@ -1634,30 +1637,6 @@ static efi_status_t EFIAPI efi_set_watchdog_timer(unsigned long timeout, return EFI_EXIT(efi_set_watchdog(timeout)); } -/* - * Connect a controller to a driver. - * - * This function implements the ConnectController service. - * See the Unified Extensible Firmware Interface (UEFI) specification - * for details. - * - * @controller_handle handle of the controller - * @driver_image_handle handle of the driver - * @remain_device_path device path of a child controller - * @recursive true to connect all child controllers - * @return status code - */ -static efi_status_t EFIAPI efi_connect_controller( - efi_handle_t controller_handle, - efi_handle_t *driver_image_handle, - struct efi_device_path *remain_device_path, - bool recursive) -{ - EFI_ENTRY("%p, %p, %p, %d", controller_handle, driver_image_handle, - remain_device_path, recursive); - return EFI_EXIT(EFI_NOT_FOUND); -} - /* * Disconnect a controller from a driver. * @@ -2368,6 +2347,166 @@ static efi_status_t EFIAPI efi_handle_protocol(void *handle, NULL, EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL); } +static efi_status_t efi_bind_controller( + efi_handle_t controller_handle, + efi_handle_t driver_image_handle, + struct efi_device_path *remain_device_path) +{ + struct efi_driver_binding_protocol *binding_protocol; + efi_status_t r; + + r = EFI_CALL(efi_open_protocol(driver_image_handle, + &efi_guid_driver_binding_protocol, + (void **)&binding_protocol, + driver_image_handle, NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL)); + if (r != EFI_SUCCESS) + return r; + r = EFI_CALL(binding_protocol->supported(binding_protocol, + controller_handle, + remain_device_path)); + if (r == EFI_SUCCESS) + r = EFI_CALL(binding_protocol->start(binding_protocol, + controller_handle, + remain_device_path)); + EFI_CALL(efi_close_protocol(driver_image_handle, + &efi_guid_driver_binding_protocol, + driver_image_handle, NULL)); + return r; +} + +static efi_status_t efi_connect_single_controller( + efi_handle_t controller_handle, + efi_handle_t *driver_image_handle, + struct efi_device_path *remain_device_path) +{ + efi_handle_t *buffer; + size_t count; + size_t i; + efi_status_t r; + size_t connected = 0; + + /* Get buffer with all handles with driver binding protocol */ + r = EFI_CALL(efi_locate_handle_buffer(BY_PROTOCOL, + &efi_guid_driver_binding_protocol, + NULL, &count, &buffer)); + if (r != EFI_SUCCESS) + return r; + + /* Context Override */ + if (driver_image_handle) { + for (; *driver_image_handle; ++driver_image_handle) { + for (i = 0; i < count; ++i) { + if (buffer[i] == *driver_image_handle) { + buffer[i] = NULL; + r = efi_bind_controller( + controller_handle, + *driver_image_handle, + remain_device_path); + /* + * For drivers that do not support the + * controller or are already connected + * we receive an error code here. + */ + if (r == EFI_SUCCESS) + ++connected; + } + } + } + } + + /* + * TODO: Some overrides are not yet implemented: + * - Platform Driver Override + * - Driver Family Override Search + * - Bus Specific Driver Override + */ + + /* Driver Binding Search */ + for (i = 0; i < count; ++i) { + if (buffer[i]) { + r = efi_bind_controller(controller_handle, + buffer[i], + remain_device_path); + if (r == EFI_SUCCESS) + ++connected; + } + } + + efi_free_pool(buffer); + if (!connected) + return EFI_NOT_FOUND; + return EFI_SUCCESS; +} + +/* + * Connect a controller to a driver. + * + * This function implements the ConnectController service. + * See the Unified Extensible Firmware Interface (UEFI) specification + * for details. + * + * First all driver binding protocol handles are tried for binding drivers. + * Afterwards all handles that have openened a protocol of the controller + * with EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER are connected to drivers. + * + * @controller_handle handle of the controller + * @driver_image_handle handle of the driver + * @remain_device_path device path of a child controller + * @recursive true to connect all child controllers + * @return status code + */ +static efi_status_t EFIAPI efi_connect_controller( + efi_handle_t controller_handle, + efi_handle_t *driver_image_handle, + struct efi_device_path *remain_device_path, + bool recursive) +{ + efi_status_t r; + efi_status_t ret = EFI_NOT_FOUND; + struct efi_object *efiobj; + + EFI_ENTRY("%p, %p, %p, %d", controller_handle, driver_image_handle, + remain_device_path, recursive); + + efiobj = efi_search_obj(controller_handle); + if (!efiobj) { + ret = EFI_INVALID_PARAMETER; + goto out; + } + + r = efi_connect_single_controller(controller_handle, + driver_image_handle, + remain_device_path); + if (r == EFI_SUCCESS) + ret = EFI_SUCCESS; + if (recursive) { + struct efi_handler *handler; + struct efi_open_protocol_info_item *item; + + list_for_each_entry(handler, &efiobj->protocols, link) { + list_for_each_entry(item, &handler->open_infos, link) { + if (item->info.attributes & + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) { + r = EFI_CALL(efi_connect_controller( + item->info.controller_handle, + driver_image_handle, + remain_device_path, + recursive)); + if (r == EFI_SUCCESS) + ret = EFI_SUCCESS; + } + } + } + } + /* 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; +out: + return EFI_EXIT(ret); +} + static const struct efi_boot_services efi_boot_services = { .hdr = { .headersize = sizeof(struct efi_table_hdr), -- cgit v1.2.3 From 3ebcd0071f2af8b05bc04c0fea7cd33a29aefb9e Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Thu, 11 Jan 2018 08:16:03 +0100 Subject: efi_loader: fix signature of efi_disconnect_controller Handles should be passed as efi_handle_t and not as void *. Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- include/efi_api.h | 6 ++++-- lib/efi_loader/efi_boottime.c | 7 ++++--- 2 files changed, 8 insertions(+), 5 deletions(-) (limited to 'lib/efi_loader/efi_boottime.c') diff --git a/include/efi_api.h b/include/efi_api.h index 81e580dbbc..7164492f83 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -125,8 +125,10 @@ struct efi_boot_services { efi_handle_t *driver_image_handle, struct efi_device_path *remaining_device_path, bool recursive); - efi_status_t (EFIAPI *disconnect_controller)(void *controller_handle, - void *driver_image_handle, void *child_handle); + efi_status_t (EFIAPI *disconnect_controller)( + efi_handle_t controller_handle, + efi_handle_t driver_image_handle, + efi_handle_t child_handle); #define EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL 0x00000001 #define EFI_OPEN_PROTOCOL_GET_PROTOCOL 0x00000002 #define EFI_OPEN_PROTOCOL_TEST_PROTOCOL 0x00000004 diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 09d5168772..fa00902c32 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -1649,9 +1649,10 @@ static efi_status_t EFIAPI efi_set_watchdog_timer(unsigned long timeout, * @child_handle handle of the child to destroy * @return status code */ -static efi_status_t EFIAPI efi_disconnect_controller(void *controller_handle, - void *driver_image_handle, - void *child_handle) +static efi_status_t EFIAPI efi_disconnect_controller( + efi_handle_t controller_handle, + efi_handle_t driver_image_handle, + efi_handle_t child_handle) { EFI_ENTRY("%p, %p, %p", controller_handle, driver_image_handle, child_handle); -- cgit v1.2.3 From 3f9b00425a59bbfffb1ac41b660c5ed477ce0da1 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Thu, 11 Jan 2018 08:16:04 +0100 Subject: efi_loader: implement DisconnectController Unfortunately we need a forward declaration because both OpenProtocol and CloseProtocol have to call DisconnectController. And DisconnectController calls both OpenProtcol and CloseProtocol. Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- lib/efi_loader/efi_boottime.c | 284 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 262 insertions(+), 22 deletions(-) (limited to 'lib/efi_loader/efi_boottime.c') diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index fa00902c32..6c272846ea 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -60,6 +60,10 @@ static int nesting_level; const efi_guid_t efi_guid_driver_binding_protocol = EFI_DRIVER_BINDING_PROTOCOL_GUID; +static efi_status_t EFIAPI efi_disconnect_controller(void *controller_handle, + void *driver_image_handle, + void *child_handle); + /* Called on every callback entry */ int __efi_entry_check(void) { @@ -953,6 +957,109 @@ static efi_status_t EFIAPI efi_reinstall_protocol_interface(void *handle, return EFI_EXIT(EFI_ACCESS_DENIED); } +/* + * Get all drivers associated to a controller. + * The allocated buffer has to be freed with free(). + * + * @efiobj handle of the controller + * @protocol protocol guid (optional) + * @number_of_drivers number of child controllers + * @driver_handle_buffer handles of the the drivers + * @return status code + */ +static efi_status_t efi_get_drivers(struct efi_object *efiobj, + const efi_guid_t *protocol, + efi_uintn_t *number_of_drivers, + efi_handle_t **driver_handle_buffer) +{ + struct efi_handler *handler; + struct efi_open_protocol_info_item *item; + efi_uintn_t count = 0, i; + bool duplicate; + + /* Count all driver associations */ + list_for_each_entry(handler, &efiobj->protocols, link) { + if (protocol && guidcmp(handler->guid, protocol)) + continue; + list_for_each_entry(item, &handler->open_infos, link) { + if (item->info.attributes & + EFI_OPEN_PROTOCOL_BY_DRIVER) + ++count; + } + } + /* + * Create buffer. In case of duplicate driver assignments the buffer + * will be too large. But that does not harm. + */ + *number_of_drivers = 0; + *driver_handle_buffer = calloc(count, sizeof(efi_handle_t)); + if (!*driver_handle_buffer) + return EFI_OUT_OF_RESOURCES; + /* Collect unique driver handles */ + list_for_each_entry(handler, &efiobj->protocols, link) { + if (protocol && guidcmp(handler->guid, protocol)) + continue; + list_for_each_entry(item, &handler->open_infos, link) { + if (item->info.attributes & + EFI_OPEN_PROTOCOL_BY_DRIVER) { + /* Check this is a new driver */ + duplicate = false; + for (i = 0; i < *number_of_drivers; ++i) { + if ((*driver_handle_buffer)[i] == + item->info.agent_handle) + duplicate = true; + } + /* Copy handle to buffer */ + if (!duplicate) { + i = (*number_of_drivers)++; + (*driver_handle_buffer)[i] = + item->info.agent_handle; + } + } + } + } + return EFI_SUCCESS; +} + +/* + * Disconnect all drivers from a controller. + * + * This function implements the DisconnectController service. + * See the Unified Extensible Firmware Interface (UEFI) specification + * for details. + * + * @efiobj handle of the controller + * @protocol protocol guid (optional) + * @child_handle handle of the child to destroy + * @return status code + */ +static efi_status_t efi_disconnect_all_drivers( + struct efi_object *efiobj, + const efi_guid_t *protocol, + efi_handle_t child_handle) +{ + efi_uintn_t number_of_drivers; + efi_handle_t *driver_handle_buffer; + efi_status_t r, ret; + + ret = efi_get_drivers(efiobj, protocol, &number_of_drivers, + &driver_handle_buffer); + if (ret != EFI_SUCCESS) + return ret; + + ret = EFI_NOT_FOUND; + while (number_of_drivers) { + r = EFI_CALL(efi_disconnect_controller( + efiobj->handle, + driver_handle_buffer[--number_of_drivers], + child_handle)); + if (r == EFI_SUCCESS) + ret = r; + } + free(driver_handle_buffer); + return ret; +} + /* * Uninstall protocol interface. * @@ -1637,28 +1744,6 @@ static efi_status_t EFIAPI efi_set_watchdog_timer(unsigned long timeout, return EFI_EXIT(efi_set_watchdog(timeout)); } -/* - * Disconnect a controller from a driver. - * - * This function implements the DisconnectController service. - * See the Unified Extensible Firmware Interface (UEFI) specification - * for details. - * - * @controller_handle handle of the controller - * @driver_image_handle handle of the driver - * @child_handle handle of the child to destroy - * @return status code - */ -static efi_status_t EFIAPI efi_disconnect_controller( - efi_handle_t controller_handle, - efi_handle_t driver_image_handle, - efi_handle_t child_handle) -{ - EFI_ENTRY("%p, %p, %p", controller_handle, driver_image_handle, - child_handle); - return EFI_EXIT(EFI_INVALID_PARAMETER); -} - /* * Close a protocol. * @@ -2508,6 +2593,161 @@ out: return EFI_EXIT(ret); } +/* + * Get all child controllers associated to a driver. + * The allocated buffer has to be freed with free(). + * + * @efiobj handle of the controller + * @driver_handle handle of the driver + * @number_of_children number of child controllers + * @child_handle_buffer handles of the the child controllers + */ +static efi_status_t efi_get_child_controllers( + struct efi_object *efiobj, + efi_handle_t driver_handle, + efi_uintn_t *number_of_children, + efi_handle_t **child_handle_buffer) +{ + struct efi_handler *handler; + struct efi_open_protocol_info_item *item; + efi_uintn_t count = 0, i; + bool duplicate; + + /* Count all child controller associations */ + list_for_each_entry(handler, &efiobj->protocols, link) { + list_for_each_entry(item, &handler->open_infos, link) { + if (item->info.agent_handle == driver_handle && + item->info.attributes & + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) + ++count; + } + } + /* + * Create buffer. In case of duplicate child controller assignments + * the buffer will be too large. But that does not harm. + */ + *number_of_children = 0; + *child_handle_buffer = calloc(count, sizeof(efi_handle_t)); + if (!*child_handle_buffer) + return EFI_OUT_OF_RESOURCES; + /* Copy unique child handles */ + list_for_each_entry(handler, &efiobj->protocols, link) { + list_for_each_entry(item, &handler->open_infos, link) { + if (item->info.agent_handle == driver_handle && + item->info.attributes & + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) { + /* Check this is a new child controller */ + duplicate = false; + for (i = 0; i < *number_of_children; ++i) { + if ((*child_handle_buffer)[i] == + item->info.controller_handle) + duplicate = true; + } + /* Copy handle to buffer */ + if (!duplicate) { + i = (*number_of_children)++; + (*child_handle_buffer)[i] = + item->info.controller_handle; + } + } + } + } + return EFI_SUCCESS; +} + +/* + * Disconnect a controller from a driver. + * + * This function implements the DisconnectController service. + * See the Unified Extensible Firmware Interface (UEFI) specification + * for details. + * + * @controller_handle handle of the controller + * @driver_image_handle handle of the driver + * @child_handle handle of the child to destroy + * @return status code + */ +static efi_status_t EFIAPI efi_disconnect_controller( + efi_handle_t controller_handle, + efi_handle_t driver_image_handle, + efi_handle_t child_handle) +{ + struct efi_driver_binding_protocol *binding_protocol; + efi_handle_t *child_handle_buffer = NULL; + size_t number_of_children = 0; + efi_status_t r; + size_t stop_count = 0; + struct efi_object *efiobj; + + EFI_ENTRY("%p, %p, %p", controller_handle, driver_image_handle, + child_handle); + + efiobj = efi_search_obj(controller_handle); + if (!efiobj) { + r = EFI_INVALID_PARAMETER; + goto out; + } + + if (child_handle && !efi_search_obj(child_handle)) { + r = EFI_INVALID_PARAMETER; + goto out; + } + + /* If no driver handle is supplied, disconnect all drivers */ + if (!driver_image_handle) { + r = efi_disconnect_all_drivers(efiobj, NULL, child_handle); + goto out; + } + + /* Create list of child handles */ + if (child_handle) { + number_of_children = 1; + child_handle_buffer = &child_handle; + } else { + efi_get_child_controllers(efiobj, + driver_image_handle, + &number_of_children, + &child_handle_buffer); + } + + /* Get the driver binding protocol */ + r = EFI_CALL(efi_open_protocol(driver_image_handle, + &efi_guid_driver_binding_protocol, + (void **)&binding_protocol, + driver_image_handle, NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL)); + if (r != EFI_SUCCESS) + goto out; + /* Remove the children */ + if (number_of_children) { + r = EFI_CALL(binding_protocol->stop(binding_protocol, + controller_handle, + number_of_children, + child_handle_buffer)); + if (r == EFI_SUCCESS) + ++stop_count; + } + /* Remove the driver */ + if (!child_handle) + r = EFI_CALL(binding_protocol->stop(binding_protocol, + controller_handle, + 0, NULL)); + if (r == EFI_SUCCESS) + ++stop_count; + EFI_CALL(efi_close_protocol(driver_image_handle, + &efi_guid_driver_binding_protocol, + driver_image_handle, NULL)); + + if (stop_count) + r = EFI_SUCCESS; + else + r = EFI_NOT_FOUND; +out: + if (!child_handle) + free(child_handle_buffer); + return EFI_EXIT(r); +} + static const struct efi_boot_services efi_boot_services = { .hdr = { .headersize = sizeof(struct efi_table_hdr), -- cgit v1.2.3 From ad97373ba9e9fc5c1854688993a31f5430853104 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Thu, 11 Jan 2018 08:16:05 +0100 Subject: efi_loader: disconnect controllers in UninstallProtocol The UninstallProtocol boot service should first try to disconnect controllers that have been connected with EFI_OPEN_PROTOCOL_BY_DRIVER. If the protocol is still opened by an agent, it should be closed. Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- lib/efi_loader/efi_boottime.c | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) (limited to 'lib/efi_loader/efi_boottime.c') diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 6c272846ea..47b3892f2d 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -1076,26 +1076,43 @@ static efi_status_t EFIAPI efi_uninstall_protocol_interface( void *handle, const efi_guid_t *protocol, void *protocol_interface) { + struct efi_object *efiobj; struct efi_handler *handler; + struct efi_open_protocol_info_item *item; + struct efi_open_protocol_info_item *pos; efi_status_t r; EFI_ENTRY("%p, %pUl, %p", handle, protocol, protocol_interface); - if (!handle || !protocol) { + /* Check handle */ + efiobj = efi_search_obj(handle); + if (!efiobj) { r = EFI_INVALID_PARAMETER; goto out; } - /* Find the protocol on the handle */ r = efi_search_protocol(handle, protocol, &handler); if (r != EFI_SUCCESS) goto out; - if (handler->protocol_interface) { - /* TODO disconnect controllers */ + /* Disconnect controllers */ + efi_disconnect_all_drivers(efiobj, protocol, NULL); + if (!list_empty(&handler->open_infos)) { r = EFI_ACCESS_DENIED; - } else { - r = efi_remove_protocol(handle, protocol, protocol_interface); + goto out; + } + /* Close protocol */ + list_for_each_entry_safe(item, pos, &handler->open_infos, link) { + if (item->info.attributes == + EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL || + item->info.attributes == EFI_OPEN_PROTOCOL_GET_PROTOCOL || + item->info.attributes == EFI_OPEN_PROTOCOL_TEST_PROTOCOL) + list_del(&item->link); + } + if (!list_empty(&handler->open_infos)) { + r = EFI_ACCESS_DENIED; + goto out; } + r = efi_remove_protocol(handle, protocol, protocol_interface); out: return EFI_EXIT(r); } -- cgit v1.2.3 From 2074f70064b5860db0d273ad3d050e87764c08e5 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Thu, 11 Jan 2018 08:16:09 +0100 Subject: efi_loader: consistently use efi_handle_t for handles We should consistently use the efi_handle_t typedef when referring to handles. Signed-off-by: Heinrich Schuchardt Reviewed-by: Simon Glass Signed-off-by: Alexander Graf --- cmd/bootefi.c | 10 ++++----- include/efi_api.h | 20 ++++++++++-------- include/efi_loader.h | 14 +++++++------ lib/efi_loader/efi_boottime.c | 49 +++++++++++++++++++++++-------------------- lib/efi_loader/efi_console.c | 6 +++--- 5 files changed, 53 insertions(+), 46 deletions(-) (limited to 'lib/efi_loader/efi_boottime.c') diff --git a/cmd/bootefi.c b/cmd/bootefi.c index 78ff109835..97a4f269ae 100644 --- a/cmd/bootefi.c +++ b/cmd/bootefi.c @@ -122,8 +122,8 @@ static void *copy_fdt(void *fdt) } static efi_status_t efi_do_enter( - void *image_handle, struct efi_system_table *st, - asmlinkage ulong (*entry)(void *image_handle, + efi_handle_t image_handle, struct efi_system_table *st, + asmlinkage ulong (*entry)(efi_handle_t image_handle, struct efi_system_table *st)) { efi_status_t ret = EFI_LOAD_ERROR; @@ -136,8 +136,8 @@ static efi_status_t efi_do_enter( #ifdef CONFIG_ARM64 static efi_status_t efi_run_in_el2(asmlinkage ulong (*entry)( - void *image_handle, struct efi_system_table *st), - void *image_handle, struct efi_system_table *st) + efi_handle_t image_handle, struct efi_system_table *st), + efi_handle_t image_handle, struct efi_system_table *st) { /* Enable caches again */ dcache_enable(); @@ -159,7 +159,7 @@ static efi_status_t do_bootefi_exec(void *efi, void *fdt, struct efi_device_path *memdp = NULL; ulong ret; - ulong (*entry)(void *image_handle, struct efi_system_table *st) + ulong (*entry)(efi_handle_t image_handle, struct efi_system_table *st) asmlinkage; ulong fdt_pages, fdt_size, fdt_start, fdt_end; const efi_guid_t fdt_guid = EFI_FDT_GUID; diff --git a/include/efi_api.h b/include/efi_api.h index 7164492f83..502fffed20 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -84,11 +84,12 @@ struct efi_boot_services { efi_status_t (EFIAPI *reinstall_protocol_interface)( void *handle, const efi_guid_t *protocol, void *old_interface, void *new_interface); - efi_status_t (EFIAPI *uninstall_protocol_interface)(void *handle, - const efi_guid_t *protocol, void *protocol_interface); - efi_status_t (EFIAPI *handle_protocol)(efi_handle_t, - const efi_guid_t *protocol, - void **protocol_interface); + efi_status_t (EFIAPI *uninstall_protocol_interface)( + efi_handle_t handle, const efi_guid_t *protocol, + void *protocol_interface); + efi_status_t (EFIAPI *handle_protocol)( + efi_handle_t handle, const efi_guid_t *protocol, + void **protocol_interface); void *reserved; efi_status_t (EFIAPI *register_protocol_notify)( const efi_guid_t *protocol, struct efi_event *event, @@ -113,7 +114,7 @@ struct efi_boot_services { efi_status_t (EFIAPI *exit)(efi_handle_t handle, efi_status_t exit_status, unsigned long exitdata_size, s16 *exitdata); - efi_status_t (EFIAPI *unload_image)(void *image_handle); + 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 *get_next_monotonic_count)(u64 *count); @@ -139,9 +140,10 @@ struct efi_boot_services { const efi_guid_t *protocol, void **interface, efi_handle_t agent_handle, efi_handle_t controller_handle, u32 attributes); - efi_status_t (EFIAPI *close_protocol)(void *handle, - const efi_guid_t *protocol, void *agent_handle, - void *controller_handle); + efi_status_t (EFIAPI *close_protocol)( + efi_handle_t handle, const efi_guid_t *protocol, + efi_handle_t agent_handle, + efi_handle_t controller_handle); efi_status_t(EFIAPI *open_protocol_information)(efi_handle_t handle, const efi_guid_t *protocol, struct efi_open_protocol_info_entry **entry_buffer, diff --git a/include/efi_loader.h b/include/efi_loader.h index 6726c44c47..37389c33cc 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -205,23 +205,25 @@ void efi_set_bootdev(const char *dev, const char *devnr, const char *path); /* Add a new object to the object list. */ void efi_add_handle(struct efi_object *obj); /* Create handle */ -efi_status_t efi_create_handle(void **handle); +efi_status_t efi_create_handle(efi_handle_t *handle); /* Delete handle */ void efi_delete_handle(struct efi_object *obj); /* Call this to validate a handle and find the EFI object for it */ -struct efi_object *efi_search_obj(const void *handle); +struct efi_object *efi_search_obj(const efi_handle_t handle); /* Find a protocol on a handle */ -efi_status_t efi_search_protocol(const void *handle, +efi_status_t efi_search_protocol(const efi_handle_t handle, const efi_guid_t *protocol_guid, struct efi_handler **handler); /* Install new protocol on a handle */ -efi_status_t efi_add_protocol(const void *handle, const efi_guid_t *protocol, +efi_status_t efi_add_protocol(const efi_handle_t handle, + const efi_guid_t *protocol, void *protocol_interface); /* Delete protocol from a handle */ -efi_status_t efi_remove_protocol(const void *handle, const efi_guid_t *protocol, +efi_status_t efi_remove_protocol(const efi_handle_t handle, + const efi_guid_t *protocol, void *protocol_interface); /* Delete all protocols from a handle */ -efi_status_t efi_remove_all_protocols(const void *handle); +efi_status_t efi_remove_all_protocols(const efi_handle_t handle); /* Call this to create an event */ efi_status_t efi_create_event(uint32_t type, efi_uintn_t notify_tpl, void (EFIAPI *notify_function) ( diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 47b3892f2d..1026049bf1 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -60,9 +60,10 @@ static int nesting_level; const efi_guid_t efi_guid_driver_binding_protocol = EFI_DRIVER_BINDING_PROTOCOL_GUID; -static efi_status_t EFIAPI efi_disconnect_controller(void *controller_handle, - void *driver_image_handle, - void *child_handle); +static efi_status_t EFIAPI efi_disconnect_controller( + efi_handle_t controller_handle, + efi_handle_t driver_image_handle, + efi_handle_t child_handle); /* Called on every callback entry */ int __efi_entry_check(void) @@ -351,7 +352,7 @@ void efi_add_handle(struct efi_object *obj) * @handle new handle * @return status code */ -efi_status_t efi_create_handle(void **handle) +efi_status_t efi_create_handle(efi_handle_t *handle) { struct efi_object *obj; efi_status_t r; @@ -374,7 +375,7 @@ efi_status_t efi_create_handle(void **handle) * @handler reference to the protocol * @return status code */ -efi_status_t efi_search_protocol(const void *handle, +efi_status_t efi_search_protocol(const efi_handle_t handle, const efi_guid_t *protocol_guid, struct efi_handler **handler) { @@ -407,7 +408,8 @@ efi_status_t efi_search_protocol(const void *handle, * @protocol_interface interface of the protocol implementation * @return status code */ -efi_status_t efi_remove_protocol(const void *handle, const efi_guid_t *protocol, +efi_status_t efi_remove_protocol(const efi_handle_t handle, + const efi_guid_t *protocol, void *protocol_interface) { struct efi_handler *handler; @@ -429,7 +431,7 @@ efi_status_t efi_remove_protocol(const void *handle, const efi_guid_t *protocol, * @handle handle from which the protocols shall be deleted * @return status code */ -efi_status_t efi_remove_all_protocols(const void *handle) +efi_status_t efi_remove_all_protocols(const efi_handle_t handle) { struct efi_object *efiobj; struct efi_handler *protocol; @@ -809,7 +811,7 @@ static efi_status_t EFIAPI efi_check_event(struct efi_event *event) * @handle handle to find * @return EFI object */ -struct efi_object *efi_search_obj(const void *handle) +struct efi_object *efi_search_obj(const efi_handle_t handle) { struct efi_object *efiobj; @@ -863,7 +865,8 @@ static efi_status_t efi_delete_open_info( * @protocol_interface interface of the protocol implementation * @return status code */ -efi_status_t efi_add_protocol(const void *handle, const efi_guid_t *protocol, +efi_status_t efi_add_protocol(const efi_handle_t handle, + const efi_guid_t *protocol, void *protocol_interface) { struct efi_object *efiobj; @@ -948,9 +951,9 @@ out: * @new_interface interface to be installed * @return status code */ -static efi_status_t EFIAPI efi_reinstall_protocol_interface(void *handle, - const efi_guid_t *protocol, void *old_interface, - void *new_interface) +static efi_status_t EFIAPI efi_reinstall_protocol_interface( + efi_handle_t handle, const efi_guid_t *protocol, + void *old_interface, void *new_interface) { EFI_ENTRY("%p, %pUl, %p, %p", handle, protocol, old_interface, new_interface); @@ -1073,7 +1076,7 @@ static efi_status_t efi_disconnect_all_drivers( * @return status code */ static efi_status_t EFIAPI efi_uninstall_protocol_interface( - void *handle, const efi_guid_t *protocol, + efi_handle_t handle, const efi_guid_t *protocol, void *protocol_interface) { struct efi_object *efiobj; @@ -1530,7 +1533,7 @@ static efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle, unsigned long *exit_data_size, s16 **exit_data) { - ulong (*entry)(void *image_handle, struct efi_system_table *st); + ulong (*entry)(efi_handle_t image_handle, struct efi_system_table *st); struct efi_loaded_image *info = image_handle; efi_status_t ret; @@ -1632,7 +1635,7 @@ static efi_status_t EFIAPI efi_exit(efi_handle_t image_handle, * @image_handle handle of the image to be unloaded * @return status code */ -static efi_status_t EFIAPI efi_unload_image(void *image_handle) +static efi_status_t EFIAPI efi_unload_image(efi_handle_t image_handle) { struct efi_object *efiobj; @@ -1670,7 +1673,7 @@ static void efi_exit_caches(void) * @map_key key of the memory map * @return status code */ -static efi_status_t EFIAPI efi_exit_boot_services(void *image_handle, +static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, unsigned long map_key) { int i; @@ -1774,10 +1777,10 @@ static efi_status_t EFIAPI efi_set_watchdog_timer(unsigned long timeout, * @controller_handle handle of the controller * @return status code */ -static efi_status_t EFIAPI efi_close_protocol(void *handle, +static efi_status_t EFIAPI efi_close_protocol(efi_handle_t handle, const efi_guid_t *protocol, - void *agent_handle, - void *controller_handle) + efi_handle_t agent_handle, + efi_handle_t controller_handle) { struct efi_handler *handler; struct efi_open_protocol_info_item *item; @@ -1883,8 +1886,8 @@ out: * @protocol_buffer_count number of entries in the buffer * @return status code */ -static efi_status_t EFIAPI efi_protocols_per_handle(void *handle, - efi_guid_t ***protocol_buffer, +static efi_status_t EFIAPI efi_protocols_per_handle( + efi_handle_t handle, efi_guid_t ***protocol_buffer, efi_uintn_t *protocol_buffer_count) { unsigned long buffer_size; @@ -1974,7 +1977,7 @@ static efi_status_t EFIAPI efi_locate_handle_buffer( r = efi_locate_handle(search_type, protocol, search_key, &buffer_size, *buffer); if (r == EFI_SUCCESS) - *no_handles = buffer_size / sizeof(void *); + *no_handles = buffer_size / sizeof(efi_handle_t); out: return EFI_EXIT(r); } @@ -2442,7 +2445,7 @@ out: * @protocol_interface interface implementing the protocol * @return status code */ -static efi_status_t EFIAPI efi_handle_protocol(void *handle, +static efi_status_t EFIAPI efi_handle_protocol(efi_handle_t handle, const efi_guid_t *protocol, void **protocol_interface) { diff --git a/lib/efi_loader/efi_console.c b/lib/efi_loader/efi_console.c index 98497db612..56b079cee8 100644 --- a/lib/efi_loader/efi_console.c +++ b/lib/efi_loader/efi_console.c @@ -503,21 +503,21 @@ int efi_console_register(void) struct efi_object *efi_console_input_obj; /* Create handles */ - r = efi_create_handle((void **)&efi_console_control_obj); + r = efi_create_handle((efi_handle_t *)&efi_console_control_obj); if (r != EFI_SUCCESS) goto out_of_memory; r = efi_add_protocol(efi_console_control_obj->handle, &efi_guid_console_control, &efi_console_control); if (r != EFI_SUCCESS) goto out_of_memory; - r = efi_create_handle((void **)&efi_console_output_obj); + r = efi_create_handle((efi_handle_t *)&efi_console_output_obj); if (r != EFI_SUCCESS) goto out_of_memory; r = efi_add_protocol(efi_console_output_obj->handle, &efi_guid_text_output_protocol, &efi_con_out); if (r != EFI_SUCCESS) goto out_of_memory; - r = efi_create_handle((void **)&efi_console_input_obj); + r = efi_create_handle((efi_handle_t *)&efi_console_input_obj); if (r != EFI_SUCCESS) goto out_of_memory; r = efi_add_protocol(efi_console_input_obj->handle, -- cgit v1.2.3 From 476ed96e01bf4b103a4bf09f1bbcb8335eb0eb07 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Fri, 19 Jan 2018 20:24:40 +0100 Subject: efi_loader: print device path when entering efi_load_image Use %pD to print the device path instead of its address when entering efi_load_image. Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- lib/efi_loader/efi_boottime.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/efi_loader/efi_boottime.c') diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 1026049bf1..ff11d028da 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -1474,7 +1474,7 @@ static efi_status_t EFIAPI efi_load_image(bool boot_policy, struct efi_object *obj; efi_status_t ret; - EFI_ENTRY("%d, %p, %p, %p, %ld, %p", boot_policy, parent_image, + EFI_ENTRY("%d, %p, %pD, %p, %ld, %p", boot_policy, parent_image, file_path, source_buffer, source_size, image_handle); info = calloc(1, sizeof(*info)); -- cgit v1.2.3 From 5e55543e65c3ada3d83d6f1b1ddc153ea64b0948 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Fri, 19 Jan 2018 20:24:43 +0100 Subject: efi_loader: fix StartImage bootservice The calling convention for the entry point of an EFI image is always 'asmlinkage'. Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- lib/efi_loader/efi_boottime.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lib/efi_loader/efi_boottime.c') diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index ff11d028da..029dc09570 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -1533,7 +1533,8 @@ static efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle, unsigned long *exit_data_size, s16 **exit_data) { - ulong (*entry)(efi_handle_t image_handle, struct efi_system_table *st); + asmlinkage ulong (*entry)(efi_handle_t image_handle, + struct efi_system_table *st); struct efi_loaded_image *info = image_handle; efi_status_t ret; -- cgit v1.2.3 From 9bc9664d5ee16155b42baf92282ba7198ef4e873 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Fri, 19 Jan 2018 20:24:51 +0100 Subject: efi_loader: add check_tpl parameter to efi_signal_event In ExitBootServices we need to signal events irrespective of the current TPL level. A new parameter check_tpl is added to efi_signal_event(). Function efi_console_timer_notify() gets some comments. Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- include/efi_loader.h | 2 +- lib/efi_loader/efi_boottime.c | 15 ++++++++------- lib/efi_loader/efi_console.c | 14 +++++++++++--- 3 files changed, 20 insertions(+), 11 deletions(-) (limited to 'lib/efi_loader/efi_boottime.c') diff --git a/include/efi_loader.h b/include/efi_loader.h index 2db4e9c09b..563c7ba3cf 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -240,7 +240,7 @@ efi_status_t efi_create_event(uint32_t type, efi_uintn_t notify_tpl, efi_status_t efi_set_timer(struct efi_event *event, enum efi_timer_delay type, uint64_t trigger_time); /* Call this to signal an event */ -void efi_signal_event(struct efi_event *event); +void efi_signal_event(struct efi_event *event, bool check_tpl); /* open file system: */ struct efi_simple_file_system_protocol *efi_simple_file_system( diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 029dc09570..8e5480ac17 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -149,13 +149,14 @@ const char *__efi_nesting_dec(void) * For the SignalEvent service see efi_signal_event_ext. * * @event event to signal + * @check_tpl check the TPL level */ -void efi_signal_event(struct efi_event *event) +void efi_signal_event(struct efi_event *event, bool check_tpl) { if (event->notify_function) { event->is_queued = true; /* Check TPL */ - if (efi_tpl >= event->notify_tpl) + if (check_tpl && efi_tpl >= event->notify_tpl) return; EFI_CALL_VOID(event->notify_function(event, event->notify_context)); @@ -565,7 +566,7 @@ void efi_timer_check(void) if (!efi_events[i].type) continue; if (efi_events[i].is_queued) - efi_signal_event(&efi_events[i]); + efi_signal_event(&efi_events[i], true); if (!(efi_events[i].type & EVT_TIMER) || now < efi_events[i].trigger_next) continue; @@ -581,7 +582,7 @@ void efi_timer_check(void) continue; } efi_events[i].is_signaled = true; - efi_signal_event(&efi_events[i]); + efi_signal_event(&efi_events[i], true); } WATCHDOG_RESET(); } @@ -690,7 +691,7 @@ known_event: if (!event[i]->type || event[i]->type & EVT_NOTIFY_SIGNAL) return EFI_EXIT(EFI_INVALID_PARAMETER); if (!event[i]->is_signaled) - efi_signal_event(event[i]); + efi_signal_event(event[i], true); } /* Wait for signal */ @@ -740,7 +741,7 @@ static efi_status_t EFIAPI efi_signal_event_ext(struct efi_event *event) break; event->is_signaled = true; if (event->type & EVT_NOTIFY_SIGNAL) - efi_signal_event(event); + efi_signal_event(event, true); break; } return EFI_EXIT(EFI_SUCCESS); @@ -797,7 +798,7 @@ static efi_status_t EFIAPI efi_check_event(struct efi_event *event) if (!event->type || event->type & EVT_NOTIFY_SIGNAL) break; if (!event->is_signaled) - efi_signal_event(event); + efi_signal_event(event, true); if (event->is_signaled) return EFI_EXIT(EFI_SUCCESS); return EFI_EXIT(EFI_NOT_READY); diff --git a/lib/efi_loader/efi_console.c b/lib/efi_loader/efi_console.c index 56b079cee8..28d63635ec 100644 --- a/lib/efi_loader/efi_console.c +++ b/lib/efi_loader/efi_console.c @@ -482,18 +482,26 @@ static void EFIAPI efi_key_notify(struct efi_event *event, void *context) { } +/* + * Notification function of the console timer event. + * + * event: console timer event + * context: not used + */ static void EFIAPI efi_console_timer_notify(struct efi_event *event, void *context) { EFI_ENTRY("%p, %p", event, context); + + /* Check if input is available */ if (tstc()) { + /* Queue the wait for key event */ efi_con_in.wait_for_key->is_signaled = true; - efi_signal_event(efi_con_in.wait_for_key); - } + efi_signal_event(efi_con_in.wait_for_key, true); + } EFI_EXIT(EFI_SUCCESS); } - /* This gets called from do_bootefi_exec(). */ int efi_console_register(void) { -- cgit v1.2.3 From cc20ed03f9887b45c834a066d0ecd098934fa43b Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Fri, 19 Jan 2018 20:24:52 +0100 Subject: efi_loader: fix ExitBootServices This patch lets the implementation of ExitBootServices conform to the UEFI standard. The timer events must be disabled before calling the notification functions of the exit boot services events. The boot services must be disabled in the system table. The handles in the system table should be defined as efi_handle_t. Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- include/efi_api.h | 6 +++--- lib/efi_loader/efi_boottime.c | 34 +++++++++++++++++++++++++++++----- 2 files changed, 32 insertions(+), 8 deletions(-) (limited to 'lib/efi_loader/efi_boottime.c') diff --git a/include/efi_api.h b/include/efi_api.h index 1ecde54152..205f8f1f70 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -247,11 +247,11 @@ struct efi_system_table { struct efi_table_hdr hdr; unsigned long fw_vendor; /* physical addr of wchar_t vendor string */ u32 fw_revision; - unsigned long con_in_handle; + efi_handle_t con_in_handle; struct efi_simple_input_interface *con_in; - unsigned long con_out_handle; + efi_handle_t con_out_handle; struct efi_simple_text_output_protocol *con_out; - unsigned long stderr_handle; + efi_handle_t stderr_handle; struct efi_simple_text_output_protocol *std_err; struct efi_runtime_services *runtime; struct efi_boot_services *boottime; diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 8e5480ac17..39d8511fe3 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -1665,12 +1665,16 @@ static void efi_exit_caches(void) } /* - * Stop boot services. + * Stop all boot services. * * This function implements the ExitBootServices service. * See the Unified Extensible Firmware Interface (UEFI) specification * for details. * + * All timer events are disabled. + * For exit boot services events the notification function is called. + * The boot services are disabled in the system table. + * * @image_handle handle of the loaded image * @map_key key of the memory map * @return status code @@ -1682,16 +1686,22 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, EFI_ENTRY("%p, %ld", image_handle, map_key); + /* Make sure that notification functions are not called anymore */ + efi_tpl = TPL_HIGH_LEVEL; + + /* Check if ExitBootServices has already been called */ + if (!systab.boottime) + return EFI_EXIT(EFI_SUCCESS); + /* Notify that ExitBootServices is invoked. */ for (i = 0; i < ARRAY_SIZE(efi_events); ++i) { if (efi_events[i].type != EVT_SIGNAL_EXIT_BOOT_SERVICES) continue; - efi_signal_event(&efi_events[i]); + efi_events[i].is_signaled = true; + efi_signal_event(&efi_events[i], false); } - /* Make sure that notification functions are not called anymore */ - efi_tpl = TPL_HIGH_LEVEL; - /* XXX Should persist EFI variables here */ + /* TODO Should persist EFI variables here */ board_quiesce_devices(); @@ -1701,6 +1711,20 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, /* This stops all lingering devices */ bootm_disable_interrupts(); + /* Disable boottime services */ + systab.con_in_handle = NULL; + systab.con_in = NULL; + systab.con_out_handle = NULL; + systab.con_out = NULL; + systab.stderr_handle = NULL; + systab.std_err = NULL; + systab.boottime = NULL; + + /* Recalculate CRC32 */ + systab.hdr.crc32 = 0; + systab.hdr.crc32 = crc32(0, (const unsigned char *)&systab, + sizeof(struct efi_system_table)); + /* Give the payload some time to boot */ efi_set_watchdog(0); WATCHDOG_RESET(); -- cgit v1.2.3