summaryrefslogtreecommitdiff
path: root/lib/efi_loader
diff options
context:
space:
mode:
authorHeinrich Schuchardt <xypron.glpk@gmx.de>2018-02-18 15:17:52 +0100
committerAlexander Graf <agraf@suse.de>2018-04-04 11:00:07 +0200
commitb095f3c85fe08744d6081b98db65768f3421295e (patch)
tree63fde74b6d856104bd4b630648f54244ab2d3c6c /lib/efi_loader
parenta3a28f5f0c39d94733d23e2bbbb53e6e4ddb46c6 (diff)
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>
Diffstat (limited to 'lib/efi_loader')
-rw-r--r--lib/efi_loader/efi_boottime.c99
-rw-r--r--lib/efi_loader/efi_console.c6
-rw-r--r--lib/efi_loader/efi_net.c4
-rw-r--r--lib/efi_loader/efi_runtime.c11
-rw-r--r--lib/efi_loader/efi_watchdog.c2
5 files changed, 95 insertions, 27 deletions
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;
@@ -190,6 +190,50 @@ void efi_signal_event(struct efi_event *event, bool check_tpl)
}
/*
+ * 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.
*
* This function implements the RaiseTpl service.
@@ -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");