Merge tag 'efi-2019-07-rc5' of git://git.denx.de/u-boot-efi

Pull request for UEFI sub-system for v2019.07-rc5

This pull request provides fixes for event services.
diff --git a/include/efi_loader.h b/include/efi_loader.h
index 23ce732..f0e1313 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -256,6 +256,7 @@
  * struct efi_event
  *
  * @link:		Link to list of all events
+ * @queue_link:		Link to the list of queued events
  * @type:		Type of event, see efi_create_event
  * @notify_tpl:		Task priority level of notifications
  * @nofify_function:	Function to call when the event is triggered
@@ -264,11 +265,11 @@
  * @trigger_time:	Period of the timer
  * @trigger_next:	Next time to trigger the timer
  * @trigger_type:	Type of timer, see efi_set_timer
- * @is_queued:		The notification function is queued
  * @is_signaled:	The event occurred. The event is in the signaled state.
  */
 struct efi_event {
 	struct list_head link;
+	struct list_head queue_link;
 	uint32_t type;
 	efi_uintn_t notify_tpl;
 	void (EFIAPI *notify_function)(struct efi_event *event, void *context);
@@ -277,7 +278,6 @@
 	u64 trigger_next;
 	u64 trigger_time;
 	enum efi_timer_delay trigger_type;
-	bool is_queued;
 	bool is_signaled;
 };
 
@@ -432,7 +432,7 @@
 efi_status_t efi_set_timer(struct efi_event *event, enum efi_timer_delay type,
 			   uint64_t trigger_time);
 /* Call this to signal an event */
-void efi_signal_event(struct efi_event *event, bool check_tpl);
+void efi_signal_event(struct efi_event *event);
 
 /* open file system: */
 struct efi_simple_file_system_protocol *efi_simple_file_system(
diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
index 7d1d6e9..fa01bbd 100644
--- a/lib/efi_loader/efi_boottime.c
+++ b/lib/efi_loader/efi_boottime.c
@@ -27,6 +27,12 @@
 /* List of all events */
 LIST_HEAD(efi_events);
 
+/* List of queued events */
+LIST_HEAD(efi_event_queue);
+
+/* Flag to disable timer activity in ExitBootServices() */
+static bool timers_enabled = true;
+
 /* List of all events registered by RegisterProtocolNotify() */
 LIST_HEAD(efi_register_notify_events);
 
@@ -161,32 +167,75 @@
 }
 
 /**
+ * efi_event_is_queued() - check if an event is queued
+ *
+ * @event:	event
+ * Return:	true if event is queued
+ */
+static bool efi_event_is_queued(struct efi_event *event)
+{
+	return !!event->queue_link.next;
+}
+
+/**
+ * efi_process_event_queue() - process event queue
+ */
+static void efi_process_event_queue(void)
+{
+	while (!list_empty(&efi_event_queue)) {
+		struct efi_event *event;
+		efi_uintn_t old_tpl;
+
+		event = list_first_entry(&efi_event_queue, struct efi_event,
+					 queue_link);
+		if (efi_tpl >= event->notify_tpl)
+			return;
+		list_del(&event->queue_link);
+		event->queue_link.next = NULL;
+		event->queue_link.prev = NULL;
+		/* Events must be executed at the event's TPL */
+		old_tpl = efi_tpl;
+		efi_tpl = event->notify_tpl;
+		EFI_CALL_VOID(event->notify_function(event,
+						     event->notify_context));
+		efi_tpl = old_tpl;
+		if (event->type == EVT_NOTIFY_SIGNAL)
+			event->is_signaled = 0;
+	}
+}
+
+/**
  * efi_queue_event() - queue an EFI event
  * @event:     event to signal
- * @check_tpl: check the TPL level
  *
  * This function queues the notification function of the event for future
  * execution.
  *
- * The notification function is called if the task priority level of the event
- * is higher than the current task priority level.
- *
- * For the SignalEvent service see efi_signal_event_ext.
- *
  */
-static void efi_queue_event(struct efi_event *event, bool check_tpl)
+static void efi_queue_event(struct efi_event *event)
 {
-	if (event->notify_function) {
-		event->is_queued = true;
-		/* Check TPL */
-		if (check_tpl && efi_tpl >= event->notify_tpl)
-			return;
-		event->is_queued = false;
-		EFI_CALL_VOID(event->notify_function(event,
-						     event->notify_context));
-	} else {
-		event->is_queued = false;
+	struct efi_event *item = NULL;
+
+	if (!event->notify_function)
+		return;
+
+	if (!efi_event_is_queued(event)) {
+		/*
+		 * Events must be notified in order of decreasing task priority
+		 * level. Insert the new event accordingly.
+		 */
+		list_for_each_entry(item, &efi_event_queue, queue_link) {
+			if (item->notify_tpl < event->notify_tpl) {
+				list_add_tail(&event->queue_link,
+					      &item->queue_link);
+				event = NULL;
+				break;
+			}
+		}
+		if (event)
+			list_add_tail(&event->queue_link, &efi_event_queue);
 	}
+	efi_process_event_queue();
 }
 
 /**
@@ -211,7 +260,6 @@
 /**
  * efi_signal_event() - signal an EFI event
  * @event:     event to signal
- * @check_tpl: check the TPL level
  *
  * 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
@@ -219,8 +267,10 @@
  *
  * For the SignalEvent service see efi_signal_event_ext.
  */
-void efi_signal_event(struct efi_event *event, bool check_tpl)
+void efi_signal_event(struct efi_event *event)
 {
+	if (event->is_signaled)
+		return;
 	if (event->group) {
 		struct efi_event *evt;
 
@@ -234,20 +284,15 @@
 			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);
+			efi_queue_event(evt);
 		}
 	} else {
 		event->is_signaled = true;
-		if (event->type & EVT_NOTIFY_SIGNAL)
-			efi_queue_event(event, check_tpl);
+		efi_queue_event(event);
 	}
 }
 
@@ -637,8 +682,6 @@
 	evt->group = group;
 	/* Disable timers on boot up */
 	evt->trigger_next = -1ULL;
-	evt->is_queued = false;
-	evt->is_signaled = false;
 	list_add_tail(&evt->link, &efi_events);
 	*event = evt;
 	return EFI_SUCCESS;
@@ -733,8 +776,8 @@
 	u64 now = timer_get_us();
 
 	list_for_each_entry(evt, &efi_events, link) {
-		if (evt->is_queued)
-			efi_queue_event(evt, true);
+		if (!timers_enabled)
+			continue;
 		if (!(evt->type & EVT_TIMER) || now < evt->trigger_next)
 			continue;
 		switch (evt->trigger_type) {
@@ -748,8 +791,9 @@
 			continue;
 		}
 		evt->is_signaled = false;
-		efi_signal_event(evt, true);
+		efi_signal_event(evt);
 	}
+	efi_process_event_queue();
 	WATCHDOG_RESET();
 }
 
@@ -850,7 +894,7 @@
 		if (!event[i]->type || event[i]->type & EVT_NOTIFY_SIGNAL)
 			return EFI_EXIT(EFI_INVALID_PARAMETER);
 		if (!event[i]->is_signaled)
-			efi_queue_event(event[i], true);
+			efi_queue_event(event[i]);
 	}
 
 	/* Wait for signal */
@@ -894,7 +938,7 @@
 	EFI_ENTRY("%p", event);
 	if (efi_is_event(event) != EFI_SUCCESS)
 		return EFI_EXIT(EFI_INVALID_PARAMETER);
-	efi_signal_event(event, true);
+	efi_signal_event(event);
 	return EFI_EXIT(EFI_SUCCESS);
 }
 
@@ -933,6 +977,9 @@
 			free(item);
 		}
 	}
+	/* Remove event from queue */
+	if (efi_event_is_queued(event))
+		list_del(&event->queue_link);
 
 	list_del(&event->link);
 	free(event);
@@ -961,7 +1008,7 @@
 	    event->type & EVT_NOTIFY_SIGNAL)
 		return EFI_EXIT(EFI_INVALID_PARAMETER);
 	if (!event->is_signaled)
-		efi_queue_event(event, true);
+		efi_queue_event(event);
 	if (event->is_signaled) {
 		event->is_signaled = false;
 		return EFI_EXIT(EFI_SUCCESS);
@@ -1068,7 +1115,8 @@
 			}
 			notif->handle = handle;
 			list_add_tail(&notif->link, &event->handles);
-			efi_signal_event(event->event, true);
+			event->event->is_signaled = false;
+			efi_signal_event(event->event);
 		}
 	}
 
@@ -1593,7 +1641,7 @@
 	/* 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);
+			efi_signal_event(evt);
 			break;
 		}
 	}
@@ -1899,13 +1947,13 @@
 	if (map_key != efi_memory_map_key)
 		return EFI_INVALID_PARAMETER;
 
-	/* Make sure that notification functions are not called anymore */
-	efi_tpl = TPL_HIGH_LEVEL;
-
 	/* Check if ExitBootServices has already been called */
 	if (!systab.boottime)
 		return EFI_EXIT(EFI_SUCCESS);
 
+	/* Stop all timer related activities */
+	timers_enabled = false;
+
 	/* Add related events to the event group */
 	list_for_each_entry(evt, &efi_events, link) {
 		if (evt->type == EVT_SIGNAL_EXIT_BOOT_SERVICES)
@@ -1916,11 +1964,14 @@
 		if (evt->group &&
 		    !guidcmp(evt->group,
 			     &efi_guid_event_group_exit_boot_services)) {
-			efi_signal_event(evt, false);
+			efi_signal_event(evt);
 			break;
 		}
 	}
 
+	/* Make sure that notification functions are not called anymore */
+	efi_tpl = TPL_HIGH_LEVEL;
+
 	/* TODO: Should persist EFI variables here */
 
 	board_quiesce_devices();
diff --git a/lib/efi_loader/efi_console.c b/lib/efi_loader/efi_console.c
index 3b7578f..2fc25e1 100644
--- a/lib/efi_loader/efi_console.c
+++ b/lib/efi_loader/efi_console.c
@@ -704,7 +704,7 @@
 	efi_status_t ret;
 
 	if (key_available) {
-		efi_signal_event(efi_con_in.wait_for_key, true);
+		efi_signal_event(efi_con_in.wait_for_key);
 		return;
 	}
 
@@ -718,7 +718,7 @@
 
 			/* Queue the wait for key event */
 			if (key_available)
-				efi_signal_event(efi_con_in.wait_for_key, true);
+				efi_signal_event(efi_con_in.wait_for_key);
 		}
 	}
 }
diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c
index 386cf92..8d76851 100644
--- a/lib/efi_loader/efi_memory.c
+++ b/lib/efi_loader/efi_memory.c
@@ -321,7 +321,7 @@
 		if (evt->group &&
 		    !guidcmp(evt->group,
 			     &efi_guid_event_group_memory_map_change)) {
-			efi_signal_event(evt, false);
+			efi_signal_event(evt);
 			break;
 		}
 	}
diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c
index 9c50955..432551d 100644
--- a/lib/efi_loader/efi_runtime.c
+++ b/lib/efi_loader/efi_runtime.c
@@ -130,7 +130,7 @@
 		if (evt->group &&
 		    !guidcmp(evt->group,
 			     &efi_guid_event_group_reset_system)) {
-			efi_signal_event(evt, false);
+			efi_signal_event(evt);
 			break;
 		}
 	}
diff --git a/lib/efi_selftest/efi_selftest_event_groups.c b/lib/efi_selftest/efi_selftest_event_groups.c
index 5a7980c..6dcde50 100644
--- a/lib/efi_selftest/efi_selftest_event_groups.c
+++ b/lib/efi_selftest/efi_selftest_event_groups.c
@@ -80,12 +80,11 @@
 			return EFI_ST_FAILURE;
 		}
 		for (j = 0; j < GROUP_SIZE; ++j) {
-			if (counter[j] != i) {
+			if (counter[j] != 2 * i + 1) {
 				efi_st_printf("i %u, j %u, count %u\n",
 					      (unsigned int)i, (unsigned int)j,
 					      (unsigned int)counter[j]);
-				efi_st_error(
-					"Notification function was called\n");
+				efi_st_error("Notification function was not called\n");
 				return EFI_ST_FAILURE;
 			}
 			/* Clear signaled state */
@@ -94,7 +93,7 @@
 				efi_st_error("Event was not signaled\n");
 				return EFI_ST_FAILURE;
 			}
-			if (counter[j] != i) {
+			if (counter[j] != 2 * i + 1) {
 				efi_st_printf("i %u, j %u, count %u\n",
 					      (unsigned int)i, (unsigned int)j,
 					      (unsigned int)counter[j]);
@@ -109,7 +108,7 @@
 					"Signaled state not cleared\n");
 				return EFI_ST_FAILURE;
 			}
-			if (counter[j] != i + 1) {
+			if (counter[j] != 2 * i + 2) {
 				efi_st_printf("i %u, j %u, count %u\n",
 					      (unsigned int)i, (unsigned int)j,
 					      (unsigned int)counter[j]);