X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=lib%2Fefi_loader%2Fefi_boottime.c;h=c7e2ecbf00f60722c9ead08c3a4aacefb3fccce7;hb=daa3f8472acd44ad90d217f6c25f986bb0f3249c;hp=ef9e3781899c371e2c2220afcc40b40deb444d87;hpb=84a918e8ce6cdc968a17e91768650bfe9eb8844a;p=platform%2Fkernel%2Fu-boot.git diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index ef9e378..c7e2ecb 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -181,10 +181,12 @@ static void efi_queue_event(struct efi_event *event, bool check_tpl) /* Check TPL */ if (check_tpl && efi_tpl >= event->notify_tpl) return; + event->is_queued = false; EFI_CALL_VOID(event->notify_function(event, event->notify_context)); + } else { + event->is_queued = false; } - event->is_queued = false; } /** @@ -513,10 +515,8 @@ efi_status_t efi_remove_protocol(const efi_handle_t handle, ret = efi_search_protocol(handle, protocol, &handler); if (ret != EFI_SUCCESS) return ret; - if (guidcmp(handler->guid, protocol)) - return EFI_INVALID_PARAMETER; if (handler->protocol_interface != protocol_interface) - return EFI_INVALID_PARAMETER; + return EFI_NOT_FOUND; list_del(&handler->link); free(handler); return EFI_SUCCESS; @@ -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,20 @@ 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); + event->event->is_signaled = false; efi_signal_event(event->event, true); + } } if (!guidcmp(&efi_guid_device_path, protocol)) @@ -1134,11 +1154,15 @@ static efi_status_t efi_get_drivers(efi_handle_t handle, ++count; } } + *number_of_drivers = 0; + if (!count) { + *driver_handle_buffer = NULL; + return EFI_SUCCESS; + } /* * 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; @@ -1194,7 +1218,8 @@ static efi_status_t efi_disconnect_all_drivers &driver_handle_buffer); if (ret != EFI_SUCCESS) return ret; - + if (!number_of_drivers) + return EFI_SUCCESS; ret = EFI_NOT_FOUND; while (number_of_drivers) { r = EFI_CALL(efi_disconnect_controller( @@ -1241,10 +1266,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 == @@ -1332,6 +1353,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); @@ -1359,7 +1381,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); @@ -1370,6 +1391,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 @@ -1390,7 +1432,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) { @@ -1400,17 +1443,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) @@ -1421,14 +1456,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; @@ -1439,14 +1483,19 @@ static efi_status_t efi_locate_handle( *buffer_size = size; - /* The buffer size is sufficient but there is not buffer */ + /* The buffer size is sufficient but there is no buffer */ if (!buffer) 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; @@ -1781,6 +1830,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 */ @@ -1837,11 +1890,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) @@ -1912,10 +1965,17 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, static efi_status_t EFIAPI efi_get_next_monotonic_count(uint64_t *count) { static uint64_t mono; + efi_status_t ret; EFI_ENTRY("%p", count); + if (!count) { + ret = EFI_INVALID_PARAMETER; + goto out; + } *count = mono++; - return EFI_EXIT(EFI_SUCCESS); + ret = EFI_SUCCESS; +out: + return EFI_EXIT(ret); } /** @@ -1931,8 +1991,14 @@ static efi_status_t EFIAPI efi_get_next_monotonic_count(uint64_t *count) */ static efi_status_t EFIAPI efi_stall(unsigned long microseconds) { + u64 end_tick; + EFI_ENTRY("%ld", microseconds); - udelay(microseconds); + + end_tick = get_ticks() + usec_to_tick(microseconds); + while (get_ticks() < end_tick) + efi_timer_check(); + return EFI_EXIT(EFI_SUCCESS); } @@ -2002,7 +2068,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: @@ -2201,29 +2266,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); } /** @@ -2257,7 +2351,7 @@ static efi_status_t EFIAPI efi_locate_device_path( EFI_ENTRY("%pUl, %p, %p", protocol, device_path, device); - if (!protocol || !device_path || !*device_path || !device) { + if (!protocol || !device_path || !*device_path) { ret = EFI_INVALID_PARAMETER; goto out; } @@ -2290,6 +2384,10 @@ static efi_status_t EFIAPI efi_locate_device_path( /* Check if dp is a subpath of device_path */ if (memcmp(*device_path, dp, len_dp)) continue; + if (!device) { + ret = EFI_INVALID_PARAMETER; + goto out; + } *device = handles[i]; len_best = len_dp; } @@ -2326,6 +2424,7 @@ efi_status_t EFIAPI efi_install_multiple_protocol_interfaces efi_va_list argptr; const efi_guid_t *protocol; void *protocol_interface; + efi_handle_t old_handle; efi_status_t r = EFI_SUCCESS; int i = 0; @@ -2338,6 +2437,20 @@ efi_status_t EFIAPI efi_install_multiple_protocol_interfaces if (!protocol) break; protocol_interface = efi_va_arg(argptr, void*); + /* Check that a device path has not been installed before */ + if (!guidcmp(protocol, &efi_guid_device_path)) { + struct efi_device_path *dp = protocol_interface; + + r = EFI_CALL(efi_locate_device_path(protocol, &dp, + &old_handle)); + if (r == EFI_SUCCESS && + dp->type == DEVICE_PATH_TYPE_END) { + EFI_PRINT("Path %pD already installed\n", + protocol_interface); + r = EFI_ALREADY_STARTED; + break; + } + } r = EFI_CALL(efi_install_protocol_interface( handle, protocol, EFI_NATIVE_INTERFACE, @@ -2445,9 +2558,16 @@ static efi_status_t EFIAPI efi_calculate_crc32(const void *data, efi_uintn_t data_size, u32 *crc32_p) { + efi_status_t ret = EFI_SUCCESS; + EFI_ENTRY("%p, %zu", data, data_size); + if (!data || !data_size || !crc32_p) { + ret = EFI_INVALID_PARAMETER; + goto out; + } *crc32_p = crc32(0, data, data_size); - return EFI_EXIT(EFI_SUCCESS); +out: + return EFI_EXIT(ret); } /** @@ -2524,34 +2644,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; } @@ -2559,7 +2695,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 */ @@ -2743,12 +2880,46 @@ efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle, * @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) +static efi_status_t efi_delete_image + (struct efi_loaded_image_obj *image_obj, + struct efi_loaded_image *loaded_image_protocol) { + struct efi_object *efiobj; + efi_status_t r, ret = EFI_SUCCESS; + +close_next: + list_for_each_entry(efiobj, &efi_obj_list, link) { + struct efi_handler *protocol; + + list_for_each_entry(protocol, &efiobj->protocols, link) { + struct efi_open_protocol_info_item *info; + + list_for_each_entry(info, &protocol->open_infos, link) { + if (info->info.agent_handle != + (efi_handle_t)image_obj) + continue; + r = EFI_CALL(efi_close_protocol + (efiobj, protocol->guid, + info->info.agent_handle, + info->info.controller_handle + )); + if (r != EFI_SUCCESS) + ret = r; + /* + * Closing protocols may results in further + * items being deleted. To play it safe loop + * over all elements again. + */ + goto close_next; + } + } + } + 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); + + return ret; } /** @@ -2948,7 +3119,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); }