efi_loader: implement event groups
authorHeinrich Schuchardt <xypron.glpk@gmx.de>
Sun, 18 Feb 2018 14:17:52 +0000 (15:17 +0100)
committerAlexander Graf <agraf@suse.de>
Wed, 4 Apr 2018 09:00:07 +0000 (11:00 +0200)
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>
include/efi_loader.h
lib/efi_loader/efi_boottime.c
lib/efi_loader/efi_console.c
lib/efi_loader/efi_net.c
lib/efi_loader/efi_runtime.c
lib/efi_loader/efi_watchdog.c

index 8596c0e..0df482e 100644 (file)
@@ -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);
index d9abb3c..1ff0568 100644 (file)
@@ -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 */
index d0e8617..5d1a9a8 100644 (file)
@@ -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");
index e7fed79..b88dc91 100644 (file)
@@ -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");
index c59d161..9e28172 100644 (file)
@@ -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:
index b1c35a8..d12e51d 100644 (file)
@@ -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");