From patchwork Thu Jan 11 07:16:04 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Heinrich Schuchardt X-Patchwork-Id: 858852 X-Patchwork-Delegate: agraf@suse.de Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.denx.de (client-ip=81.169.180.215; helo=lists.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Received: from lists.denx.de (dione.denx.de [81.169.180.215]) by ozlabs.org (Postfix) with ESMTP id 3zHHPM4RZwz9t3k for ; Thu, 11 Jan 2018 18:21:43 +1100 (AEDT) Received: by lists.denx.de (Postfix, from userid 105) id 4BC47C21E0F; Thu, 11 Jan 2018 07:19:51 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de X-Spam-Level: X-Spam-Status: No, score=-0.0 required=5.0 tests=FREEMAIL_FROM, RCVD_IN_DNSWL_BLOCKED,RCVD_IN_MSPIKE_H3,RCVD_IN_MSPIKE_WL autolearn=unavailable autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id E0117C2212F; Thu, 11 Jan 2018 07:16:31 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id CF7D5C21F0E; Thu, 11 Jan 2018 07:16:20 +0000 (UTC) Received: from mout.gmx.net (mout.gmx.net [212.227.17.21]) by lists.denx.de (Postfix) with ESMTPS id C32E2C21C50 for ; Thu, 11 Jan 2018 07:16:19 +0000 (UTC) Received: from workstation4.fritz.box ([94.114.42.150]) by mail.gmx.com (mrgmx103 [212.227.17.174]) with ESMTPSA (Nemesis) id 0LfTC1-1fFEeR2DAf-00p53Y; Thu, 11 Jan 2018 08:16:18 +0100 From: Heinrich Schuchardt To: Alexander Graf Date: Thu, 11 Jan 2018 08:16:04 +0100 Message-Id: <20180111071609.4058-12-xypron.glpk@gmx.de> X-Mailer: git-send-email 2.14.2 In-Reply-To: <20180111071609.4058-1-xypron.glpk@gmx.de> References: <20180111071609.4058-1-xypron.glpk@gmx.de> X-Provags-ID: V03:K0:KlM/saTOO+tnB4QjZDBcGxUYkT/DNseo1XjTSFLpicbRa/ogQLG PFEjYHSVUdIdkfHCq79mpks/U5zfZT1Gpeerl/XnxBF8W1MpJOS7HW6G8gkYs7JP85plYpk P9tVUnPRBlFiDO/BcCAyQOVbJKWSKI6lvwloxocXJLMAsfLdHNB8BbgxXMbBf/RcZk2HoDO EmAQXuvOldeOep628tfxw== X-UI-Out-Filterresults: notjunk:1; V01:K0:Ca5paBA3J8A=:aGSjIxNxcoL/1sLtTKYgOB bDxJ45UdpSkMo59JiocSAcNQg5keaa4LTNWrik5zCQXn0Ak/DgrkZ3LVBxXfDLsyzeAINdToA rtHvTVvjslEngmg6lGNyuGj/sIAcpSw9rcbUYxmoPNPGkdPdnS7NT6yOs30BruMVKShUistdz ELzxYqX83jqf+eYVmT2ZqkIlbVfHYy1NAEoVDHyfCGiQZXZOLN5vjstjpS2SLpHruPmycpQf8 E82vL2jnWHxGmheGgufLaGN7n7kVz1jM7yM8SzzL0bNe73+YjE3MF7p78dRj0kIVwxzrnIjEN TspqsGgfyZElIcGjP5MoznDqEtaYmm5mqD601/vi2cpRkDsM9JH4f+m9c9I4f2IabDqB5IFnu B5nOutmJn8rH04gghRks9ri56fVlGdfiHEscyDsa023n6kenA2lEBVhH++BYxDkuTvYzgBBNe NEncokRxz8prPft8v4Zj2vsCWinLPfFKkdAt2zWfhMl9rstpezH9wdpZ32f2jXpp7NZoP9BoJ nNFFo/B3Rv0U2IaiMws7juT5uMNgjIQUE5o8kJi5HKACJrsNspEZNFGkNi8LkON7T2XUdjZAK MzzBg27eZCWzpzf5iOkucXs1NWxjt5adK72cq7KjHe/H3mFjedEpbW8mTtu2z0im59uKvF964 GcKU7nQGMZtwirne5Mee/80X58T6fudtJNbzLUZnjufjnO4MIqvtA54N9KUJ3mVh49v6DJM/k /UHR4QTuv+a1GJWHPQJjEIsS95zsXVMcbWKev1uy5mW8mpGhnTLUOGtKr11I6owxt27WP2JBk mLtsM0gvFutgjqa13IamhBaUgTLdg== Cc: u-boot@lists.denx.de, Heinrich Schuchardt Subject: [U-Boot] [PATCH v3 11/16] efi_loader: implement DisconnectController X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.18 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" 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 --- v3 no change v2 Return EFI_NOT_FOUND in disconnect_all_drivers if no driver is disconnected. --- lib/efi_loader/efi_boottime.c | 284 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 262 insertions(+), 22 deletions(-) diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 8f9d24f6a7..7e16a3f2cf 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) { @@ -954,6 +958,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. * @@ -1618,28 +1725,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. * @@ -2488,6 +2573,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),