diff options
Diffstat (limited to 'lib/efi_loader/efi_boottime.c')
-rw-r--r-- | lib/efi_loader/efi_boottime.c | 174 |
1 files changed, 112 insertions, 62 deletions
diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 7d1d6e92138..d104cc6b316 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -27,6 +27,12 @@ LIST_HEAD(efi_obj_list); /* List of all events */ LIST_HEAD(efi_events); +/* List of queued events */ +LIST_HEAD(efi_event_queue); + +/* Flag to disable timer activity in ExitBootServices() */ +static bool timers_enabled = true; + /* List of all events registered by RegisterProtocolNotify() */ LIST_HEAD(efi_register_notify_events); @@ -161,32 +167,75 @@ const char *__efi_nesting_dec(void) } /** + * efi_event_is_queued() - check if an event is queued + * + * @event: event + * Return: true if event is queued + */ +static bool efi_event_is_queued(struct efi_event *event) +{ + return !!event->queue_link.next; +} + +/** + * efi_process_event_queue() - process event queue + */ +static void efi_process_event_queue(void) +{ + while (!list_empty(&efi_event_queue)) { + struct efi_event *event; + efi_uintn_t old_tpl; + + event = list_first_entry(&efi_event_queue, struct efi_event, + queue_link); + if (efi_tpl >= event->notify_tpl) + return; + list_del(&event->queue_link); + event->queue_link.next = NULL; + event->queue_link.prev = NULL; + /* Events must be executed at the event's TPL */ + old_tpl = efi_tpl; + efi_tpl = event->notify_tpl; + EFI_CALL_VOID(event->notify_function(event, + event->notify_context)); + efi_tpl = old_tpl; + if (event->type == EVT_NOTIFY_SIGNAL) + event->is_signaled = 0; + } +} + +/** * efi_queue_event() - queue an EFI event * @event: event to signal - * @check_tpl: check the TPL level * * This function queues the notification function of the event for future * execution. * - * The notification function is called if the task priority level of the event - * is higher than the current task priority level. - * - * For the SignalEvent service see efi_signal_event_ext. - * */ -static void efi_queue_event(struct efi_event *event, bool check_tpl) +static void efi_queue_event(struct efi_event *event) { - if (event->notify_function) { - event->is_queued = true; - /* 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; + struct efi_event *item = NULL; + + if (!event->notify_function) + return; + + if (!efi_event_is_queued(event)) { + /* + * Events must be notified in order of decreasing task priority + * level. Insert the new event accordingly. + */ + list_for_each_entry(item, &efi_event_queue, queue_link) { + if (item->notify_tpl < event->notify_tpl) { + list_add_tail(&event->queue_link, + &item->queue_link); + event = NULL; + break; + } + } + if (event) + list_add_tail(&event->queue_link, &efi_event_queue); } + efi_process_event_queue(); } /** @@ -211,7 +260,6 @@ efi_status_t is_valid_tpl(efi_uintn_t tpl) /** * efi_signal_event() - signal an EFI event * @event: event to signal - * @check_tpl: check the TPL level * * This function signals an event. If the event belongs to an event group all * events of the group are signaled. If they are of type EVT_NOTIFY_SIGNAL @@ -219,8 +267,10 @@ efi_status_t is_valid_tpl(efi_uintn_t tpl) * * For the SignalEvent service see efi_signal_event_ext. */ -void efi_signal_event(struct efi_event *event, bool check_tpl) +void efi_signal_event(struct efi_event *event) { + if (event->is_signaled) + return; if (event->group) { struct efi_event *evt; @@ -234,20 +284,15 @@ void efi_signal_event(struct efi_event *event, bool check_tpl) if (evt->is_signaled) continue; evt->is_signaled = true; - if (evt->type & EVT_NOTIFY_SIGNAL && - evt->notify_function) - evt->is_queued = true; } list_for_each_entry(evt, &efi_events, link) { if (!evt->group || guidcmp(evt->group, event->group)) continue; - if (evt->is_queued) - efi_queue_event(evt, check_tpl); + efi_queue_event(evt); } } else { event->is_signaled = true; - if (event->type & EVT_NOTIFY_SIGNAL) - efi_queue_event(event, check_tpl); + efi_queue_event(event); } } @@ -637,8 +682,6 @@ efi_status_t efi_create_event(uint32_t type, efi_uintn_t notify_tpl, evt->group = group; /* Disable timers on boot up */ evt->trigger_next = -1ULL; - evt->is_queued = false; - evt->is_signaled = false; list_add_tail(&evt->link, &efi_events); *event = evt; return EFI_SUCCESS; @@ -733,8 +776,8 @@ void efi_timer_check(void) u64 now = timer_get_us(); list_for_each_entry(evt, &efi_events, link) { - if (evt->is_queued) - efi_queue_event(evt, true); + if (!timers_enabled) + continue; if (!(evt->type & EVT_TIMER) || now < evt->trigger_next) continue; switch (evt->trigger_type) { @@ -748,8 +791,9 @@ void efi_timer_check(void) continue; } evt->is_signaled = false; - efi_signal_event(evt, true); + efi_signal_event(evt); } + efi_process_event_queue(); WATCHDOG_RESET(); } @@ -850,7 +894,7 @@ static efi_status_t EFIAPI efi_wait_for_event(efi_uintn_t num_events, if (!event[i]->type || event[i]->type & EVT_NOTIFY_SIGNAL) return EFI_EXIT(EFI_INVALID_PARAMETER); if (!event[i]->is_signaled) - efi_queue_event(event[i], true); + efi_queue_event(event[i]); } /* Wait for signal */ @@ -894,7 +938,7 @@ static efi_status_t EFIAPI efi_signal_event_ext(struct efi_event *event) EFI_ENTRY("%p", event); if (efi_is_event(event) != EFI_SUCCESS) return EFI_EXIT(EFI_INVALID_PARAMETER); - efi_signal_event(event, true); + efi_signal_event(event); return EFI_EXIT(EFI_SUCCESS); } @@ -933,6 +977,9 @@ static efi_status_t EFIAPI efi_close_event(struct efi_event *event) free(item); } } + /* Remove event from queue */ + if (efi_event_is_queued(event)) + list_del(&event->queue_link); list_del(&event->link); free(event); @@ -961,7 +1008,7 @@ static efi_status_t EFIAPI efi_check_event(struct efi_event *event) event->type & EVT_NOTIFY_SIGNAL) return EFI_EXIT(EFI_INVALID_PARAMETER); if (!event->is_signaled) - efi_queue_event(event, true); + efi_queue_event(event); if (event->is_signaled) { event->is_signaled = false; return EFI_EXIT(EFI_SUCCESS); @@ -1068,7 +1115,8 @@ efi_status_t efi_add_protocol(const efi_handle_t handle, } notif->handle = handle; list_add_tail(¬if->link, &event->handles); - efi_signal_event(event->event, true); + event->event->is_signaled = false; + efi_signal_event(event->event); } } @@ -1593,7 +1641,7 @@ out: /* Notify that the configuration table was changed */ list_for_each_entry(evt, &efi_events, link) { if (evt->group && !guidcmp(evt->group, guid)) { - efi_signal_event(evt, false); + efi_signal_event(evt); break; } } @@ -1731,7 +1779,7 @@ efi_status_t efi_load_image_from_path(struct efi_device_path *file_path, /* Open file */ f = efi_file_from_path(file_path); if (!f) - return EFI_DEVICE_ERROR; + return EFI_NOT_FOUND; /* Get file size */ bs = 0; @@ -1808,17 +1856,10 @@ efi_status_t EFIAPI efi_load_image(bool boot_policy, EFI_ENTRY("%d, %p, %pD, %p, %zd, %p", boot_policy, parent_image, file_path, source_buffer, source_size, image_handle); - if (!image_handle || !efi_search_obj(parent_image)) { - ret = EFI_INVALID_PARAMETER; - goto error; - } - - if (!source_buffer && !file_path) { - ret = EFI_NOT_FOUND; - goto error; - } - /* The parent image handle must refer to a loaded image */ - if (!parent_image->type) { + if (!image_handle || (!source_buffer && !file_path) || + !efi_search_obj(parent_image) || + /* The parent image handle must refer to a loaded image */ + !parent_image->type) { ret = EFI_INVALID_PARAMETER; goto error; } @@ -1892,19 +1933,22 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, efi_uintn_t map_key) { struct efi_event *evt; + efi_status_t ret = EFI_SUCCESS; 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) - return EFI_INVALID_PARAMETER; - - /* Make sure that notification functions are not called anymore */ - efi_tpl = TPL_HIGH_LEVEL; + if (map_key != efi_memory_map_key) { + ret = EFI_INVALID_PARAMETER; + goto out; + } /* Check if ExitBootServices has already been called */ if (!systab.boottime) - return EFI_EXIT(EFI_SUCCESS); + goto out; + + /* Stop all timer related activities */ + timers_enabled = false; /* Add related events to the event group */ list_for_each_entry(evt, &efi_events, link) { @@ -1916,11 +1960,14 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, if (evt->group && !guidcmp(evt->group, &efi_guid_event_group_exit_boot_services)) { - efi_signal_event(evt, false); + efi_signal_event(evt); break; } } + /* Make sure that notification functions are not called anymore */ + efi_tpl = TPL_HIGH_LEVEL; + /* TODO: Should persist EFI variables here */ board_quiesce_devices(); @@ -1946,8 +1993,8 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, /* Give the payload some time to boot */ efi_set_watchdog(0); WATCHDOG_RESET(); - - return EFI_EXIT(EFI_SUCCESS); +out: + return EFI_EXIT(ret); } /** @@ -2819,6 +2866,9 @@ efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle, EFI_ENTRY("%p, %p, %p", image_handle, exit_data_size, exit_data); /* Check parameters */ + if (image_obj->header.type != EFI_OBJECT_TYPE_LOADED_IMAGE) + return EFI_EXIT(EFI_INVALID_PARAMETER); + ret = EFI_CALL(efi_open_protocol(image_handle, &efi_guid_loaded_image, &info, NULL, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL)); @@ -3570,11 +3620,11 @@ struct efi_system_table __efi_runtime_data systab = { }, .fw_vendor = firmware_vendor, .fw_revision = FW_VERSION << 16 | FW_PATCHLEVEL << 8, - .con_in = (void *)&efi_con_in, - .con_out = (void *)&efi_con_out, - .std_err = (void *)&efi_con_out, - .runtime = (void *)&efi_runtime_services, - .boottime = (void *)&efi_boot_services, + .con_in = &efi_con_in, + .con_out = &efi_con_out, + .std_err = &efi_con_out, + .runtime = &efi_runtime_services, + .boottime = &efi_boot_services, .nr_tables = 0, .tables = NULL, }; |