efi_loader: disconnect controllers in UninstallProtocol
authorHeinrich Schuchardt <xypron.glpk@gmx.de>
Thu, 11 Jan 2018 07:16:05 +0000 (08:16 +0100)
committerAlexander Graf <agraf@suse.de>
Mon, 22 Jan 2018 22:09:13 +0000 (23:09 +0100)
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 <xypron.glpk@gmx.de>
Signed-off-by: Alexander Graf <agraf@suse.de>
lib/efi_loader/efi_boottime.c

index 6c272846ea5e2db275559df7c15e7829003246e4..47b3892f2db0c91360d429b36196aad455fc355e 100644 (file)
@@ -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);
 }