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 <xypron.glpk@gmx.de>
Signed-off-by: Alexander Graf <agraf@suse.de>
This commit is contained in:
Heinrich Schuchardt
2018-02-18 15:17:52 +01:00
committed by Alexander Graf
parent a3a28f5f0c
commit b095f3c85f
6 changed files with 101 additions and 28 deletions

View File

@ -165,6 +165,7 @@ struct efi_object {
* @notify_tpl: Task priority level of notifications * @notify_tpl: Task priority level of notifications
* @nofify_function: Function to call when the event is triggered * @nofify_function: Function to call when the event is triggered
* @notify_context: Data to be passed to the notify function * @notify_context: Data to be passed to the notify function
* @group: Event group
* @trigger_time: Period of the timer * @trigger_time: Period of the timer
* @trigger_next: Next time to trigger the timer * @trigger_next: Next time to trigger the timer
* @trigger_type: Type of timer, see efi_set_timer * @trigger_type: Type of timer, see efi_set_timer
@ -177,6 +178,7 @@ struct efi_event {
efi_uintn_t notify_tpl; efi_uintn_t notify_tpl;
void (EFIAPI *notify_function)(struct efi_event *event, void *context); void (EFIAPI *notify_function)(struct efi_event *event, void *context);
void *notify_context; void *notify_context;
const efi_guid_t *group;
u64 trigger_next; u64 trigger_next;
u64 trigger_time; u64 trigger_time;
enum efi_timer_delay trigger_type; enum efi_timer_delay trigger_type;
@ -186,6 +188,8 @@ struct efi_event {
/* This list contains all UEFI objects we know of */ /* This list contains all UEFI objects we know of */
extern struct list_head efi_obj_list; 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 */ /* Called by bootefi to make console interface available */
int efi_console_register(void); 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) ( void (EFIAPI *notify_function) (
struct efi_event *event, struct efi_event *event,
void *context), 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 */ /* Call this to set a timer */
efi_status_t efi_set_timer(struct efi_event *event, enum efi_timer_delay type, efi_status_t efi_set_timer(struct efi_event *event, enum efi_timer_delay type,
uint64_t trigger_time); uint64_t trigger_time);

View File

@ -27,7 +27,7 @@ static efi_uintn_t efi_tpl = TPL_APPLICATION;
LIST_HEAD(efi_obj_list); LIST_HEAD(efi_obj_list);
/* List of all events */ /* 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) * 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 * @event event to signal
* @check_tpl check the TPL level * @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) { if (event->notify_function) {
event->is_queued = true; event->is_queued = true;
@ -189,6 +189,50 @@ void efi_signal_event(struct efi_event *event, bool check_tpl)
event->is_queued = false; 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. * 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) ( void (EFIAPI *notify_function) (
struct efi_event *event, struct efi_event *event,
void *context), void *context),
void *notify_context, struct efi_event **event) void *notify_context, efi_guid_t *group,
struct efi_event **event)
{ {
struct efi_event *evt; 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_tpl = notify_tpl;
evt->notify_function = notify_function; evt->notify_function = notify_function;
evt->notify_context = notify_context; evt->notify_context = notify_context;
evt->group = group;
/* Disable timers on bootup */ /* Disable timers on bootup */
evt->trigger_next = -1ULL; evt->trigger_next = -1ULL;
evt->is_queued = false; 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, EFI_ENTRY("%d, 0x%zx, %p, %p, %pUl", type, notify_tpl, notify_function,
notify_context, event_group); notify_context, event_group);
if (event_group)
return EFI_EXIT(EFI_UNSUPPORTED);
return EFI_EXIT(efi_create_event(type, notify_tpl, notify_function, 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, EFI_ENTRY("%d, 0x%zx, %p, %p", type, notify_tpl, notify_function,
notify_context); notify_context);
return EFI_EXIT(efi_create_event(type, notify_tpl, notify_function, 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) { list_for_each_entry(evt, &efi_events, link) {
if (evt->is_queued) if (evt->is_queued)
efi_signal_event(evt, true); efi_queue_event(evt, true);
if (!(evt->type & EVT_TIMER) || now < evt->trigger_next) if (!(evt->type & EVT_TIMER) || now < evt->trigger_next)
continue; continue;
switch (evt->trigger_type) { switch (evt->trigger_type) {
@ -645,7 +689,7 @@ void efi_timer_check(void)
default: default:
continue; continue;
} }
evt->is_signaled = true; evt->is_signaled = false;
efi_signal_event(evt, true); efi_signal_event(evt, true);
} }
WATCHDOG_RESET(); 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) if (!event[i]->type || event[i]->type & EVT_NOTIFY_SIGNAL)
return EFI_EXIT(EFI_INVALID_PARAMETER); return EFI_EXIT(EFI_INVALID_PARAMETER);
if (!event[i]->is_signaled) if (!event[i]->is_signaled)
efi_signal_event(event[i], true); efi_queue_event(event[i], true);
} }
/* Wait for signal */ /* Wait for signal */
@ -787,11 +831,7 @@ static efi_status_t EFIAPI efi_signal_event_ext(struct efi_event *event)
EFI_ENTRY("%p", event); EFI_ENTRY("%p", event);
if (efi_is_event(event) != EFI_SUCCESS) if (efi_is_event(event) != EFI_SUCCESS)
return EFI_EXIT(EFI_INVALID_PARAMETER); return EFI_EXIT(EFI_INVALID_PARAMETER);
if (!event->is_signaled) { efi_signal_event(event, true);
event->is_signaled = true;
if (event->type & EVT_NOTIFY_SIGNAL)
efi_signal_event(event, true);
}
return EFI_EXIT(EFI_SUCCESS); 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) event->type & EVT_NOTIFY_SIGNAL)
return EFI_EXIT(EFI_INVALID_PARAMETER); return EFI_EXIT(EFI_INVALID_PARAMETER);
if (!event->is_signaled) if (!event->is_signaled)
efi_signal_event(event, true); efi_queue_event(event, true);
if (event->is_signaled) { if (event->is_signaled) {
event->is_signaled = false; event->is_signaled = false;
return EFI_EXIT(EFI_SUCCESS); 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, efi_status_t efi_install_configuration_table(const efi_guid_t *guid,
void *table) void *table)
{ {
struct efi_event *evt;
int i; int i;
if (!guid) if (!guid)
@ -1345,7 +1386,7 @@ efi_status_t efi_install_configuration_table(const efi_guid_t *guid,
efi_conf_table[i].table = table; efi_conf_table[i].table = table;
else else
efi_remove_configuration_table(i); 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; efi_conf_table[i].table = table;
systab.nr_tables = i + 1; 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; return EFI_SUCCESS;
} }
@ -1764,12 +1814,19 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle,
if (!systab.boottime) if (!systab.boottime)
return EFI_EXIT(EFI_SUCCESS); 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. */ /* Notify that ExitBootServices is invoked. */
list_for_each_entry(evt, &efi_events, link) { list_for_each_entry(evt, &efi_events, link) {
if (evt->type != EVT_SIGNAL_EXIT_BOOT_SERVICES) if (evt->group &&
continue; !guidcmp(evt->group,
evt->is_signaled = true; &efi_guid_event_group_exit_boot_services)) {
efi_signal_event(evt, false); efi_signal_event(evt, false);
break;
}
} }
/* TODO Should persist EFI variables here */ /* TODO Should persist EFI variables here */

View File

@ -572,14 +572,14 @@ int efi_console_register(void)
goto out_of_memory; goto out_of_memory;
/* Create console events */ /* Create console events */
r = efi_create_event(EVT_NOTIFY_WAIT, TPL_CALLBACK, r = efi_create_event(EVT_NOTIFY_WAIT, TPL_CALLBACK, efi_key_notify,
efi_key_notify, NULL, &efi_con_in.wait_for_key); NULL, NULL, &efi_con_in.wait_for_key);
if (r != EFI_SUCCESS) { if (r != EFI_SUCCESS) {
printf("ERROR: Failed to register WaitForKey event\n"); printf("ERROR: Failed to register WaitForKey event\n");
return r; return r;
} }
r = efi_create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK, 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); &console_timer_event);
if (r != EFI_SUCCESS) { if (r != EFI_SUCCESS) {
printf("ERROR: Failed to register console event\n"); printf("ERROR: Failed to register console event\n");

View File

@ -341,7 +341,7 @@ efi_status_t efi_net_register(void)
* Create WaitForPacket event. * Create WaitForPacket event.
*/ */
r = efi_create_event(EVT_NOTIFY_WAIT, TPL_CALLBACK, r = efi_create_event(EVT_NOTIFY_WAIT, TPL_CALLBACK,
efi_network_timer_notify, NULL, efi_network_timer_notify, NULL, NULL,
&wait_for_packet); &wait_for_packet);
if (r != EFI_SUCCESS) { if (r != EFI_SUCCESS) {
printf("ERROR: Failed to register network event\n"); printf("ERROR: Failed to register network event\n");
@ -355,7 +355,7 @@ efi_status_t efi_net_register(void)
* has been received. * has been received.
*/ */
r = efi_create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK, 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); &network_timer_event);
if (r != EFI_SUCCESS) { if (r != EFI_SUCCESS) {
printf("ERROR: Failed to register network event\n"); printf("ERROR: Failed to register network event\n");

View File

@ -74,9 +74,20 @@ static void EFIAPI efi_reset_system_boottime(
efi_status_t reset_status, efi_status_t reset_status,
unsigned long data_size, void *reset_data) unsigned long data_size, void *reset_data)
{ {
struct efi_event *evt;
EFI_ENTRY("%d %lx %lx %p", reset_type, reset_status, data_size, EFI_ENTRY("%d %lx %lx %p", reset_type, reset_status, data_size,
reset_data); 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) { switch (reset_type) {
case EFI_RESET_COLD: case EFI_RESET_COLD:
case EFI_RESET_WARM: case EFI_RESET_WARM:

View File

@ -67,7 +67,7 @@ efi_status_t efi_watchdog_register(void)
* Create a timer event. * Create a timer event.
*/ */
r = efi_create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK, 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); &watchdog_timer_event);
if (r != EFI_SUCCESS) { if (r != EFI_SUCCESS) {
printf("ERROR: Failed to register watchdog event\n"); printf("ERROR: Failed to register watchdog event\n");