efi_loader: implement queueing of the notification function

For the correct implementation of the task priority level (TPL)
calling the notification function must be queued.

Add a status field 'queued' to events.

In function efi_signal_event set status queued if a notification
function exists and reset it after we have called the function.
A later patch will add a check of the TPL here.

In efi_create_event and efi_close_event unset the queued status.

In function efi_wait_for_event and efi_check_event
queue the notification function.

In efi_timer_check call the efi_notify_event
if the status queued is set.
For all timer events set status signaled.

In efi_console_timer_notify set the signaled state of the
WaitForKey event.

Signed-off-by: Heinrich Schuchardt <xypron.glpk@gmx.de>
Signed-off-by: Alexander Graf <agraf@suse.de>
diff --git a/include/efi_loader.h b/include/efi_loader.h
index f74b33d..25398ba 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -131,7 +131,8 @@
  * @nofify_function:	Function to call when the event is triggered
  * @notify_context:	Data to be passed to the notify function
  * @trigger_type:	Type of timer, see efi_set_timer
- * @signaled:		The notify function was already called
+ * @queued:		The notification functionis queued
+ * @signaled:		The event occured
  */
 struct efi_event {
 	uint32_t type;
@@ -141,6 +142,7 @@
 	u64 trigger_next;
 	u64 trigger_time;
 	enum efi_timer_delay trigger_type;
+	int queued;
 	int signaled;
 };
 
diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
index 2c9379a..408b4a9 100644
--- a/lib/efi_loader/efi_boottime.c
+++ b/lib/efi_loader/efi_boottime.c
@@ -159,13 +159,13 @@
 
 void efi_signal_event(struct efi_event *event)
 {
-	if (event->signaled)
-		return;
-	event->signaled = 1;
-	if (event->type & EVT_NOTIFY_SIGNAL) {
+	if (event->notify_function) {
+		event->queued = 1;
+		/* Put missing TPL check here */
 		EFI_CALL_VOID(event->notify_function(event,
 						     event->notify_context));
 	}
+	event->queued = 0;
 }
 
 static efi_status_t efi_unsupported(const char *funcname)
@@ -276,6 +276,7 @@
 		efi_events[i].notify_context = notify_context;
 		/* Disable timers on bootup */
 		efi_events[i].trigger_next = -1ULL;
+		efi_events[i].queued = 0;
 		efi_events[i].signaled = 0;
 		*event = &efi_events[i];
 		return EFI_SUCCESS;
@@ -307,16 +308,25 @@
 	u64 now = timer_get_us();
 
 	for (i = 0; i < ARRAY_SIZE(efi_events); ++i) {
-		if (!efi_events[i].type ||
-		    !(efi_events[i].type & EVT_TIMER) ||
-		    efi_events[i].trigger_type == EFI_TIMER_STOP ||
+		if (!efi_events[i].type)
+			continue;
+		if (efi_events[i].queued)
+			efi_signal_event(&efi_events[i]);
+		if (!(efi_events[i].type & EVT_TIMER) ||
 		    now < efi_events[i].trigger_next)
 			continue;
-		if (efi_events[i].trigger_type == EFI_TIMER_PERIODIC) {
+		switch (efi_events[i].trigger_type) {
+		case EFI_TIMER_RELATIVE:
+			efi_events[i].trigger_type = EFI_TIMER_STOP;
+			break;
+		case EFI_TIMER_PERIODIC:
 			efi_events[i].trigger_next +=
 				efi_events[i].trigger_time;
-			efi_events[i].signaled = 0;
+			break;
+		default:
+			continue;
 		}
+		efi_events[i].signaled = 1;
 		efi_signal_event(&efi_events[i]);
 	}
 	WATCHDOG_RESET();
@@ -377,6 +387,7 @@
 	/* Check parameters */
 	if (!num_events || !event)
 		return EFI_EXIT(EFI_INVALID_PARAMETER);
+	/* Put missing TPL check here */
 	for (i = 0; i < num_events; ++i) {
 		for (j = 0; j < ARRAY_SIZE(efi_events); ++j) {
 			if (event[i] == &efi_events[j])
@@ -386,6 +397,8 @@
 known_event:
 		if (!event[i]->type || event[i]->type & EVT_NOTIFY_SIGNAL)
 			return EFI_EXIT(EFI_INVALID_PARAMETER);
+		if (!event[i]->signaled)
+			efi_signal_event(event[i]);
 	}
 
 	/* Wait for signal */
@@ -418,7 +431,11 @@
 	for (i = 0; i < ARRAY_SIZE(efi_events); ++i) {
 		if (event != &efi_events[i])
 			continue;
-		efi_signal_event(event);
+		if (event->signaled)
+			break;
+		event->signaled = 1;
+		if (event->type & EVT_NOTIFY_SIGNAL)
+			efi_signal_event(event);
 		break;
 	}
 	return EFI_EXIT(EFI_SUCCESS);
@@ -433,6 +450,7 @@
 		if (event == &efi_events[i]) {
 			event->type = 0;
 			event->trigger_next = -1ULL;
+			event->queued = 0;
 			event->signaled = 0;
 			return EFI_EXIT(EFI_SUCCESS);
 		}
@@ -451,6 +469,8 @@
 			continue;
 		if (!event->type || event->type & EVT_NOTIFY_SIGNAL)
 			break;
+		if (!event->signaled)
+			efi_signal_event(event);
 		if (event->signaled)
 			return EFI_EXIT(EFI_SUCCESS);
 		return EFI_EXIT(EFI_NOT_READY);
diff --git a/lib/efi_loader/efi_console.c b/lib/efi_loader/efi_console.c
index 3fc82b8..65c07fd 100644
--- a/lib/efi_loader/efi_console.c
+++ b/lib/efi_loader/efi_console.c
@@ -426,8 +426,10 @@
 					    void *context)
 {
 	EFI_ENTRY("%p, %p", event, context);
-	if (tstc())
+	if (tstc()) {
+		efi_con_in.wait_for_key->signaled = 1;
 		efi_signal_event(efi_con_in.wait_for_key);
+		}
 	EFI_EXIT(EFI_SUCCESS);
 }