From b095f3c85fe08744d6081b98db65768f3421295e Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sun, 18 Feb 2018 15:17:52 +0100 Subject: [PATCH] efi_loader: implement event groups If an event of a group event is signaled all other events of the same group are signaled too. Function efi_signal_event is renamed to efi_queue_event. A new function efi_signal_event is introduced that checks if an event belongs to a group and than signals all events of the group. Event group notifciation is implemented for ExitBootServices, InstallConfigurationTable, and ResetSystem. Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- include/efi_loader.h | 7 ++- lib/efi_loader/efi_boottime.c | 99 +++++++++++++++++++++++++++-------- lib/efi_loader/efi_console.c | 6 +-- lib/efi_loader/efi_net.c | 4 +- lib/efi_loader/efi_runtime.c | 11 ++++ lib/efi_loader/efi_watchdog.c | 2 +- 6 files changed, 101 insertions(+), 28 deletions(-) diff --git a/include/efi_loader.h b/include/efi_loader.h index 8596c0e729..0df482ee21 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -165,6 +165,7 @@ struct efi_object { * @notify_tpl: Task priority level of notifications * @nofify_function: Function to call when the event is triggered * @notify_context: Data to be passed to the notify function + * @group: Event group * @trigger_time: Period of the timer * @trigger_next: Next time to trigger the timer * @trigger_type: Type of timer, see efi_set_timer @@ -177,6 +178,7 @@ struct efi_event { efi_uintn_t notify_tpl; void (EFIAPI *notify_function)(struct efi_event *event, void *context); void *notify_context; + const efi_guid_t *group; u64 trigger_next; u64 trigger_time; enum efi_timer_delay trigger_type; @@ -186,6 +188,8 @@ struct efi_event { /* This list contains all UEFI objects we know of */ extern struct list_head efi_obj_list; +/* List of all events */ +extern struct list_head efi_events; /* Called by bootefi to make console interface available */ int efi_console_register(void); @@ -252,7 +256,8 @@ efi_status_t efi_create_event(uint32_t type, efi_uintn_t notify_tpl, void (EFIAPI *notify_function) ( struct efi_event *event, void *context), - void *notify_context, struct efi_event **event); + void *notify_context, efi_guid_t *group, + struct efi_event **event); /* Call this to set a timer */ efi_status_t efi_set_timer(struct efi_event *event, enum efi_timer_delay type, uint64_t trigger_time); diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index d9abb3cced..1ff0568d47 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -27,7 +27,7 @@ static efi_uintn_t efi_tpl = TPL_APPLICATION; LIST_HEAD(efi_obj_list); /* List of all events */ -static LIST_HEAD(efi_events); +LIST_HEAD(efi_events); /* * If we're running on nasty systems (32bit ARM booting into non-EFI Linux) @@ -176,7 +176,7 @@ const char *__efi_nesting_dec(void) * @event event to signal * @check_tpl check the TPL level */ -void efi_signal_event(struct efi_event *event, bool check_tpl) +static void efi_queue_event(struct efi_event *event, bool check_tpl) { if (event->notify_function) { event->is_queued = true; @@ -189,6 +189,50 @@ void efi_signal_event(struct efi_event *event, bool check_tpl) event->is_queued = false; } +/* + * Signal an EFI event. + * + * 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 + * their notification function is queued. + * + * For the SignalEvent service see efi_signal_event_ext. + * + * @event event to signal + * @check_tpl check the TPL level + */ +void efi_signal_event(struct efi_event *event, bool check_tpl) +{ + if (event->group) { + struct efi_event *evt; + + /* + * The signaled state has to set before executing any + * notification function + */ + list_for_each_entry(evt, &efi_events, link) { + if (!evt->group || guidcmp(evt->group, event->group)) + continue; + 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); + } + } else if (!event->is_signaled) { + event->is_signaled = true; + if (event->type & EVT_NOTIFY_SIGNAL) + efi_queue_event(event, check_tpl); + } +} + /* * Raise the task priority level. * @@ -529,7 +573,8 @@ efi_status_t efi_create_event(uint32_t type, efi_uintn_t notify_tpl, void (EFIAPI *notify_function) ( struct efi_event *event, void *context), - void *notify_context, struct efi_event **event) + void *notify_context, efi_guid_t *group, + struct efi_event **event) { struct efi_event *evt; @@ -550,6 +595,7 @@ efi_status_t efi_create_event(uint32_t type, efi_uintn_t notify_tpl, evt->notify_tpl = notify_tpl; evt->notify_function = notify_function; evt->notify_context = notify_context; + evt->group = group; /* Disable timers on bootup */ evt->trigger_next = -1ULL; evt->is_queued = false; @@ -585,10 +631,8 @@ efi_status_t EFIAPI efi_create_event_ex(uint32_t type, efi_uintn_t notify_tpl, { EFI_ENTRY("%d, 0x%zx, %p, %p, %pUl", type, notify_tpl, notify_function, notify_context, event_group); - if (event_group) - return EFI_EXIT(EFI_UNSUPPORTED); return EFI_EXIT(efi_create_event(type, notify_tpl, notify_function, - notify_context, event)); + notify_context, event_group, event)); } /* @@ -615,7 +659,7 @@ static efi_status_t EFIAPI efi_create_event_ext( EFI_ENTRY("%d, 0x%zx, %p, %p", type, notify_tpl, notify_function, notify_context); return EFI_EXIT(efi_create_event(type, notify_tpl, notify_function, - notify_context, event)); + notify_context, NULL, event)); } /* @@ -632,7 +676,7 @@ void efi_timer_check(void) list_for_each_entry(evt, &efi_events, link) { if (evt->is_queued) - efi_signal_event(evt, true); + efi_queue_event(evt, true); if (!(evt->type & EVT_TIMER) || now < evt->trigger_next) continue; switch (evt->trigger_type) { @@ -645,7 +689,7 @@ void efi_timer_check(void) default: continue; } - evt->is_signaled = true; + evt->is_signaled = false; efi_signal_event(evt, true); } WATCHDOG_RESET(); @@ -744,7 +788,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_signal_event(event[i], true); + efi_queue_event(event[i], true); } /* Wait for signal */ @@ -787,11 +831,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); - if (!event->is_signaled) { - event->is_signaled = true; - if (event->type & EVT_NOTIFY_SIGNAL) - efi_signal_event(event, true); - } + efi_signal_event(event, true); return EFI_EXIT(EFI_SUCCESS); } @@ -836,7 +876,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_signal_event(event, true); + efi_queue_event(event, true); if (event->is_signaled) { event->is_signaled = false; return EFI_EXIT(EFI_SUCCESS); @@ -1333,6 +1373,7 @@ static void efi_remove_configuration_table(int i) efi_status_t efi_install_configuration_table(const efi_guid_t *guid, void *table) { + struct efi_event *evt; int i; if (!guid) @@ -1345,7 +1386,7 @@ efi_status_t efi_install_configuration_table(const efi_guid_t *guid, efi_conf_table[i].table = table; else efi_remove_configuration_table(i); - return EFI_SUCCESS; + goto out; } } @@ -1361,6 +1402,15 @@ efi_status_t efi_install_configuration_table(const efi_guid_t *guid, efi_conf_table[i].table = table; systab.nr_tables = i + 1; +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); + break; + } + } + return EFI_SUCCESS; } @@ -1764,12 +1814,19 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, if (!systab.boottime) return EFI_EXIT(EFI_SUCCESS); + /* Add related events to the event group */ + list_for_each_entry(evt, &efi_events, link) { + if (evt->type == EVT_SIGNAL_EXIT_BOOT_SERVICES) + evt->group = &efi_guid_event_group_exit_boot_services; + } /* Notify that ExitBootServices is invoked. */ list_for_each_entry(evt, &efi_events, link) { - if (evt->type != EVT_SIGNAL_EXIT_BOOT_SERVICES) - continue; - evt->is_signaled = true; - efi_signal_event(evt, false); + if (evt->group && + !guidcmp(evt->group, + &efi_guid_event_group_exit_boot_services)) { + efi_signal_event(evt, false); + break; + } } /* TODO Should persist EFI variables here */ diff --git a/lib/efi_loader/efi_console.c b/lib/efi_loader/efi_console.c index d0e8617d0d..5d1a9a8081 100644 --- a/lib/efi_loader/efi_console.c +++ b/lib/efi_loader/efi_console.c @@ -572,14 +572,14 @@ int efi_console_register(void) goto out_of_memory; /* Create console events */ - r = efi_create_event(EVT_NOTIFY_WAIT, TPL_CALLBACK, - efi_key_notify, NULL, &efi_con_in.wait_for_key); + r = efi_create_event(EVT_NOTIFY_WAIT, TPL_CALLBACK, efi_key_notify, + NULL, NULL, &efi_con_in.wait_for_key); if (r != EFI_SUCCESS) { printf("ERROR: Failed to register WaitForKey event\n"); return r; } r = efi_create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK, - efi_console_timer_notify, NULL, + efi_console_timer_notify, NULL, NULL, &console_timer_event); if (r != EFI_SUCCESS) { printf("ERROR: Failed to register console event\n"); diff --git a/lib/efi_loader/efi_net.c b/lib/efi_loader/efi_net.c index e7fed79450..b88dc91f58 100644 --- a/lib/efi_loader/efi_net.c +++ b/lib/efi_loader/efi_net.c @@ -341,7 +341,7 @@ efi_status_t efi_net_register(void) * Create WaitForPacket event. */ r = efi_create_event(EVT_NOTIFY_WAIT, TPL_CALLBACK, - efi_network_timer_notify, NULL, + efi_network_timer_notify, NULL, NULL, &wait_for_packet); if (r != EFI_SUCCESS) { printf("ERROR: Failed to register network event\n"); @@ -355,7 +355,7 @@ efi_status_t efi_net_register(void) * has been received. */ r = efi_create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK, - efi_network_timer_notify, NULL, + efi_network_timer_notify, NULL, NULL, &network_timer_event); if (r != EFI_SUCCESS) { printf("ERROR: Failed to register network event\n"); diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c index c59d161c8f..9e281728f9 100644 --- a/lib/efi_loader/efi_runtime.c +++ b/lib/efi_loader/efi_runtime.c @@ -74,9 +74,20 @@ static void EFIAPI efi_reset_system_boottime( efi_status_t reset_status, unsigned long data_size, void *reset_data) { + struct efi_event *evt; + EFI_ENTRY("%d %lx %lx %p", reset_type, reset_status, data_size, reset_data); + /* Notify reset */ + list_for_each_entry(evt, &efi_events, link) { + if (evt->group && + !guidcmp(evt->group, + &efi_guid_event_group_reset_system)) { + efi_signal_event(evt, false); + break; + } + } switch (reset_type) { case EFI_RESET_COLD: case EFI_RESET_WARM: diff --git a/lib/efi_loader/efi_watchdog.c b/lib/efi_loader/efi_watchdog.c index b1c35a8e29..d12e51da0a 100644 --- a/lib/efi_loader/efi_watchdog.c +++ b/lib/efi_loader/efi_watchdog.c @@ -67,7 +67,7 @@ efi_status_t efi_watchdog_register(void) * Create a timer event. */ r = efi_create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK, - efi_watchdog_timer_notify, NULL, + efi_watchdog_timer_notify, NULL, NULL, &watchdog_timer_event); if (r != EFI_SUCCESS) { printf("ERROR: Failed to register watchdog event\n"); -- 2.39.5