summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/efi_loader.h6
-rw-r--r--lib/efi_loader/efi_boottime.c131
-rw-r--r--lib/efi_loader/efi_console.c4
-rw-r--r--lib/efi_loader/efi_memory.c2
-rw-r--r--lib/efi_loader/efi_runtime.c2
-rw-r--r--lib/efi_selftest/efi_selftest_event_groups.c9
6 files changed, 102 insertions, 52 deletions
diff --git a/include/efi_loader.h b/include/efi_loader.h
index 23ce732267..f0e1313f93 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -256,6 +256,7 @@ struct efi_loaded_image_obj {
* 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 @@ struct efi_loaded_image_obj {
* @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 @@ struct efi_event {
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_create_event(uint32_t type, efi_uintn_t notify_tpl,
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 7d1d6e9213..fa01bbda70 100644
--- a/lib/efi_loader/efi_boottime.c
+++ b/lib/efi_loader/efi_boottime.c
@@ -27,6 +27,12 @@ LIST_HEAD(efi_obj_list);
/* 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 @@ const char *__efi_nesting_dec(void)
}
/**
+ * 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_status_t is_valid_tpl(efi_uintn_t tpl)
/**
* 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 @@ efi_status_t is_valid_tpl(efi_uintn_t tpl)
*
* 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 @@ void efi_signal_event(struct efi_event *event, bool check_tpl)
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 @@ efi_status_t efi_create_event(uint32_t type, efi_uintn_t notify_tpl,
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 @@ void efi_timer_check(void)
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 @@ void efi_timer_check(void)
continue;
}
evt->is_signaled = false;
- efi_signal_event(evt, true);
+ efi_signal_event(evt);
}
+ efi_process_event_queue();
WATCHDOG_RESET();
}
@@ -850,7 +894,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_queue_event(event[i], true);
+ efi_queue_event(event[i]);
}
/* Wait for signal */
@@ -894,7 +938,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);
- efi_signal_event(event, true);
+ efi_signal_event(event);
return EFI_EXIT(EFI_SUCCESS);
}
@@ -933,6 +977,9 @@ static efi_status_t EFIAPI efi_close_event(struct efi_event *event)
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 @@ 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_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 @@ efi_status_t efi_add_protocol(const efi_handle_t handle,
}
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 @@ 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);
+ efi_signal_event(evt);
break;
}
}
@@ -1899,13 +1947,13 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle,
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 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle,
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 3b7578f3aa..2fc25e118f 100644
--- a/lib/efi_loader/efi_console.c
+++ b/lib/efi_loader/efi_console.c
@@ -704,7 +704,7 @@ static void efi_cin_check(void)
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 @@ static void efi_cin_check(void)
/* 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 386cf924fe..8d76851234 100644
--- a/lib/efi_loader/efi_memory.c
+++ b/lib/efi_loader/efi_memory.c
@@ -321,7 +321,7 @@ uint64_t efi_add_memory_map(uint64_t start, uint64_t pages, int memory_type,
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 9c50955c9b..432551d0c8 100644
--- a/lib/efi_loader/efi_runtime.c
+++ b/lib/efi_loader/efi_runtime.c
@@ -130,7 +130,7 @@ static void EFIAPI efi_reset_system_boottime(
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 5a7980c5d0..6dcde50648 100644
--- a/lib/efi_selftest/efi_selftest_event_groups.c
+++ b/lib/efi_selftest/efi_selftest_event_groups.c
@@ -80,12 +80,11 @@ static int execute(void)
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 @@ static int execute(void)
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 @@ static int execute(void)
"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]);