diff mbox series

[U-Boot,4/5] efi_loader: implement event groups

Message ID 20180218141753.9621-5-xypron.glpk@gmx.de
State Accepted
Delegated to: Alexander Graf
Headers show
Series efi_loader: implement event groups | expand

Commit Message

Heinrich Schuchardt Feb. 18, 2018, 2:17 p.m. UTC
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>
---
 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 mbox series

Patch

diff --git a/include/efi_loader.h b/include/efi_loader.h
index e2cd249171..b1999f08c6 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -161,6 +161,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
@@ -173,6 +174,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;
@@ -182,6 +184,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);
@@ -248,7 +252,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 da3c852b44..32b8149fe7 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;
 }
 
@@ -1738,12 +1788,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 3cb580e3ed..d35893dc2a 100644
--- a/lib/efi_loader/efi_console.c
+++ b/lib/efi_loader/efi_console.c
@@ -488,14 +488,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 e2b86828cd..d0f213ea4f 100644
--- a/lib/efi_loader/efi_runtime.c
+++ b/lib/efi_loader/efi_runtime.c
@@ -78,9 +78,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");