diff options
Diffstat (limited to 'lib/efi_loader/efi_boottime.c')
-rw-r--r-- | lib/efi_loader/efi_boottime.c | 188 |
1 files changed, 138 insertions, 50 deletions
diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 78c6076e28..7d1d6e9213 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -921,6 +921,14 @@ static efi_status_t EFIAPI efi_close_event(struct efi_event *event) list_for_each_entry_safe(item, next, &efi_register_notify_events, link) { if (event == item->event) { + struct efi_protocol_notification *hitem, *hnext; + + /* Remove signaled handles */ + list_for_each_entry_safe(hitem, hnext, &item->handles, + link) { + list_del(&hitem->link); + free(hitem); + } list_del(&item->link); free(item); } @@ -1049,8 +1057,19 @@ efi_status_t efi_add_protocol(const efi_handle_t handle, /* Notify registered events */ list_for_each_entry(event, &efi_register_notify_events, link) { - if (!guidcmp(protocol, &event->protocol)) + if (!guidcmp(protocol, &event->protocol)) { + struct efi_protocol_notification *notif; + + notif = calloc(1, sizeof(*notif)); + if (!notif) { + list_del(&handler->link); + free(handler); + return EFI_OUT_OF_RESOURCES; + } + notif->handle = handle; + list_add_tail(¬if->link, &event->handles); efi_signal_event(event->event, true); + } } if (!guidcmp(&efi_guid_device_path, protocol)) @@ -1246,10 +1265,6 @@ static efi_status_t efi_uninstall_protocol goto out; /* Disconnect controllers */ efi_disconnect_all_drivers(efiobj, protocol, NULL); - if (!list_empty(&handler->open_infos)) { - r = EFI_ACCESS_DENIED; - goto out; - } /* Close protocol */ list_for_each_entry_safe(item, pos, &handler->open_infos, link) { if (item->info.attributes == @@ -1337,6 +1352,7 @@ static efi_status_t EFIAPI efi_register_protocol_notify( item->event = event; memcpy(&item->protocol, protocol, sizeof(efi_guid_t)); + INIT_LIST_HEAD(&item->handles); list_add_tail(&item->link, &efi_register_notify_events); @@ -1364,7 +1380,6 @@ static int efi_search(enum efi_locate_search_type search_type, switch (search_type) { case ALL_HANDLES: return 0; - case BY_REGISTER_NOTIFY: case BY_PROTOCOL: ret = efi_search_protocol(handle, protocol, NULL); return (ret != EFI_SUCCESS); @@ -1375,6 +1390,27 @@ static int efi_search(enum efi_locate_search_type search_type, } /** + * efi_check_register_notify_event() - check if registration key is valid + * + * Check that a pointer is a valid registration key as returned by + * RegisterProtocolNotify(). + * + * @key: registration key + * Return: valid registration key or NULL + */ +static struct efi_register_notify_event *efi_check_register_notify_event + (void *key) +{ + struct efi_register_notify_event *event; + + list_for_each_entry(event, &efi_register_notify_events, link) { + if (event == (struct efi_register_notify_event *)key) + return event; + } + return NULL; +} + +/** * efi_locate_handle() - locate handles implementing a protocol * * @search_type: selection criterion @@ -1395,7 +1431,8 @@ static efi_status_t efi_locate_handle( { struct efi_object *efiobj; efi_uintn_t size = 0; - struct efi_register_notify_event *item, *event = NULL; + struct efi_register_notify_event *event; + struct efi_protocol_notification *handle = NULL; /* Check parameters */ switch (search_type) { @@ -1405,17 +1442,9 @@ static efi_status_t efi_locate_handle( if (!search_key) return EFI_INVALID_PARAMETER; /* 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; - } - } + event = efi_check_register_notify_event(search_key); if (!event) return EFI_INVALID_PARAMETER; - - protocol = &event->protocol; break; case BY_PROTOCOL: if (!protocol) @@ -1426,14 +1455,23 @@ 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, efiobj)) - size += sizeof(void *); + if (search_type == BY_REGISTER_NOTIFY) { + if (list_empty(&event->handles)) + return EFI_NOT_FOUND; + handle = list_first_entry(&event->handles, + struct efi_protocol_notification, + link); + efiobj = handle->handle; + size += sizeof(void *); + } else { + list_for_each_entry(efiobj, &efi_obj_list, link) { + if (!efi_search(search_type, protocol, efiobj)) + size += sizeof(void *); + } + if (size == 0) + return EFI_NOT_FOUND; } - if (size == 0) - return EFI_NOT_FOUND; - if (!buffer_size) return EFI_INVALID_PARAMETER; @@ -1449,9 +1487,14 @@ static efi_status_t efi_locate_handle( return EFI_INVALID_PARAMETER; /* Then fill the array */ - list_for_each_entry(efiobj, &efi_obj_list, link) { - if (!efi_search(search_type, protocol, efiobj)) - *buffer++ = efiobj; + if (search_type == BY_REGISTER_NOTIFY) { + *buffer = efiobj; + list_del(&handle->link); + } else { + list_for_each_entry(efiobj, &efi_obj_list, link) { + if (!efi_search(search_type, protocol, efiobj)) + *buffer++ = efiobj; + } } return EFI_SUCCESS; @@ -2024,7 +2067,6 @@ static efi_status_t EFIAPI efi_close_protocol(efi_handle_t handle, item->info.controller_handle == controller_handle) { efi_delete_open_info(item); r = EFI_SUCCESS; - break; } } out: @@ -2223,29 +2265,58 @@ static efi_status_t EFIAPI efi_locate_protocol(const efi_guid_t *protocol, void *registration, void **protocol_interface) { - struct list_head *lhandle; + struct efi_handler *handler; efi_status_t ret; + struct efi_object *efiobj; EFI_ENTRY("%pUl, %p, %p", protocol, registration, protocol_interface); + /* + * The UEFI spec explicitly requires a protocol even if a registration + * key is provided. This differs from the logic in LocateHandle(). + */ if (!protocol || !protocol_interface) return EFI_EXIT(EFI_INVALID_PARAMETER); - list_for_each(lhandle, &efi_obj_list) { - struct efi_object *efiobj; - struct efi_handler *handler; - - efiobj = list_entry(lhandle, struct efi_object, link); + if (registration) { + struct efi_register_notify_event *event; + struct efi_protocol_notification *handle; + event = efi_check_register_notify_event(registration); + if (!event) + return EFI_EXIT(EFI_INVALID_PARAMETER); + /* + * The UEFI spec requires to return EFI_NOT_FOUND if no + * protocol instance matches protocol and registration. + * So let's do the same for a mismatch between protocol and + * registration. + */ + if (guidcmp(&event->protocol, protocol)) + goto not_found; + if (list_empty(&event->handles)) + goto not_found; + handle = list_first_entry(&event->handles, + struct efi_protocol_notification, + link); + efiobj = handle->handle; + list_del(&handle->link); + free(handle); ret = efi_search_protocol(efiobj, protocol, &handler); - if (ret == EFI_SUCCESS) { - *protocol_interface = handler->protocol_interface; - return EFI_EXIT(EFI_SUCCESS); + if (ret == EFI_SUCCESS) + goto found; + } else { + list_for_each_entry(efiobj, &efi_obj_list, link) { + ret = efi_search_protocol(efiobj, protocol, &handler); + if (ret == EFI_SUCCESS) + goto found; } } +not_found: *protocol_interface = NULL; - return EFI_EXIT(EFI_NOT_FOUND); +found: + *protocol_interface = handler->protocol_interface; + return EFI_EXIT(EFI_SUCCESS); } /** @@ -2572,34 +2643,50 @@ static efi_status_t efi_protocol_open( if ((attributes & EFI_OPEN_PROTOCOL_BY_DRIVER) && (item->info.attributes == attributes)) return EFI_ALREADY_STARTED; + } else { + if (item->info.attributes & + EFI_OPEN_PROTOCOL_BY_DRIVER) + opened_by_driver = true; } 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; + if (attributes & EFI_OPEN_PROTOCOL_EXCLUSIVE) { + if (opened_exclusive) + return EFI_ACCESS_DENIED; + } else if (attributes & EFI_OPEN_PROTOCOL_BY_DRIVER) { + if (opened_exclusive || opened_by_driver) + return EFI_ACCESS_DENIED; + } /* Prepare exclusive opening */ if (attributes & EFI_OPEN_PROTOCOL_EXCLUSIVE) { /* Try to disconnect controllers */ +disconnect_next: + opened_by_driver = false; list_for_each_entry(item, &handler->open_infos, link) { + efi_status_t ret; + if (item->info.attributes == - EFI_OPEN_PROTOCOL_BY_DRIVER) - EFI_CALL(efi_disconnect_controller( + EFI_OPEN_PROTOCOL_BY_DRIVER) { + ret = EFI_CALL(efi_disconnect_controller( item->info.controller_handle, item->info.agent_handle, NULL)); + if (ret == EFI_SUCCESS) + /* + * Child controllers may have been + * removed from the open_infos list. So + * let's restart the loop. + */ + goto disconnect_next; + else + opened_by_driver = true; + } } - 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 connected */ + /* Only one driver can be connected */ if (opened_by_driver) return EFI_ACCESS_DENIED; } @@ -2607,7 +2694,8 @@ static efi_status_t efi_protocol_open( /* 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) + item->info.controller_handle == controller_handle && + item->info.attributes == attributes) match = &item->info; } /* None found, create one */ @@ -3030,7 +3118,7 @@ static efi_status_t EFIAPI efi_handle_protocol(efi_handle_t handle, const efi_guid_t *protocol, void **protocol_interface) { - return efi_open_protocol(handle, protocol, protocol_interface, NULL, + return efi_open_protocol(handle, protocol, protocol_interface, efi_root, NULL, EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL); } |