[U-Boot] [PATCH 0/5] efi_loader: implement event groups

The patch series provides support for event groups. If any event of the group is signaled all other events are signaled too.
The events are managed in a linked list instead of an array.
Some formatting errors are fixed.
Heinrich Schuchardt (5): efi_loader: fix formatting errors efi_loader: manage events in a linked list efi_loader: define GUIDS for event groups efi_loader: implement event groups efi_selftest: unit test for event groups
include/efi_api.h | 21 ++ include/efi_loader.h | 28 ++- lib/efi_loader/efi_boottime.c | 339 ++++++++++++++++----------- 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 +- lib/efi_selftest/Makefile | 1 + lib/efi_selftest/efi_selftest_event_groups.c | 140 +++++++++++ 9 files changed, 400 insertions(+), 152 deletions(-) create mode 100644 lib/efi_selftest/efi_selftest_event_groups.c

Fix formatting errors in efi_boottime.c indicated by scripts/checkpatch.py.
Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de --- lib/efi_loader/efi_boottime.c | 48 +++++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 22 deletions(-)
diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 15235f6d07..75f7c50295 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -123,6 +123,7 @@ static const char *indent_string(int level) { const char *indent = " "; const int max = strlen(indent); + level = min(max, level * 2); return &indent[max - level]; } @@ -257,7 +258,7 @@ static efi_status_t EFIAPI efi_free_pages_ext(uint64_t memory, { efi_status_t r;
- EFI_ENTRY("%"PRIx64", 0x%zx", memory, pages); + EFI_ENTRY("%" PRIx64 ", 0x%zx", memory, pages); r = efi_free_pages(memory, pages); return EFI_EXIT(r); } @@ -586,7 +587,6 @@ static efi_status_t EFIAPI efi_create_event_ext( notify_context, event)); }
- /* * Check if a timer event has occurred or a queued notification function should * be called. @@ -688,7 +688,7 @@ static efi_status_t EFIAPI efi_set_timer_ext(struct efi_event *event, enum efi_timer_delay type, uint64_t trigger_time) { - EFI_ENTRY("%p, %d, %"PRIx64, event, type, trigger_time); + EFI_ENTRY("%p, %d, %" PRIx64, event, type, trigger_time); return EFI_EXIT(efi_set_timer(event, type, trigger_time)); }
@@ -1264,7 +1264,7 @@ static efi_status_t efi_locate_handle( /* Count how much space we need */ list_for_each_entry(efiobj, &efi_obj_list, link) { if (!efi_search(search_type, protocol, search_key, efiobj)) - size += sizeof(void*); + size += sizeof(void *); }
if (*buffer_size < size) { @@ -1315,7 +1315,7 @@ static efi_status_t EFIAPI efi_locate_handle_ext( static void efi_remove_configuration_table(int i) { struct efi_configuration_table *this = &efi_conf_table[i]; - struct efi_configuration_table *next = &efi_conf_table[i+1]; + struct efi_configuration_table *next = &efi_conf_table[i + 1]; struct efi_configuration_table *end = &efi_conf_table[systab.nr_tables];
memmove(this, next, (ulong)end - (ulong)next); @@ -1332,7 +1332,8 @@ static void efi_remove_configuration_table(int i) * @table table to be installed * @return status code */ -efi_status_t efi_install_configuration_table(const efi_guid_t *guid, void *table) +efi_status_t efi_install_configuration_table(const efi_guid_t *guid, + void *table) { int i;
@@ -1638,8 +1639,9 @@ static efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle, * @return status code */ static efi_status_t EFIAPI efi_exit(efi_handle_t image_handle, - efi_status_t exit_status, unsigned long exit_data_size, - int16_t *exit_data) + efi_status_t exit_status, + unsigned long exit_data_size, + int16_t *exit_data) { /* * We require that the handle points to the original loaded @@ -1652,7 +1654,7 @@ static efi_status_t EFIAPI efi_exit(efi_handle_t image_handle, * TODO: We should call the unload procedure of the loaded * image protocol. */ - struct efi_loaded_image *loaded_image_info = (void*)image_handle; + struct efi_loaded_image *loaded_image_info = (void *)image_handle;
EFI_ENTRY("%p, %ld, %ld, %p", image_handle, exit_status, exit_data_size, exit_data); @@ -1789,7 +1791,8 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, */ static efi_status_t EFIAPI efi_get_next_monotonic_count(uint64_t *count) { - static uint64_t mono = 0; + static uint64_t mono; + EFI_ENTRY("%p", count); *count = mono++; return EFI_EXIT(EFI_SUCCESS); @@ -1830,7 +1833,7 @@ static efi_status_t EFIAPI efi_set_watchdog_timer(unsigned long timeout, unsigned long data_size, uint16_t *watchdog_data) { - EFI_ENTRY("%ld, 0x%"PRIx64", %ld, %p", timeout, watchdog_code, + EFI_ENTRY("%ld, 0x%" PRIx64 ", %ld, %p", timeout, watchdog_code, data_size, watchdog_data); return EFI_EXIT(efi_set_watchdog(timeout)); } @@ -1895,8 +1898,8 @@ out: * @entry_count number of entries available in the buffer * @return status code */ -static efi_status_t EFIAPI efi_open_protocol_information(efi_handle_t handle, - const efi_guid_t *protocol, +static efi_status_t EFIAPI efi_open_protocol_information( + efi_handle_t handle, const efi_guid_t *protocol, struct efi_open_protocol_info_entry **entry_buffer, efi_uintn_t *entry_count) { @@ -2881,15 +2884,16 @@ static const struct efi_boot_services efi_boot_services = { .protocols_per_handle = efi_protocols_per_handle, .locate_handle_buffer = efi_locate_handle_buffer, .locate_protocol = efi_locate_protocol, - .install_multiple_protocol_interfaces = efi_install_multiple_protocol_interfaces, - .uninstall_multiple_protocol_interfaces = efi_uninstall_multiple_protocol_interfaces, + .install_multiple_protocol_interfaces = + efi_install_multiple_protocol_interfaces, + .uninstall_multiple_protocol_interfaces = + efi_uninstall_multiple_protocol_interfaces, .calculate_crc32 = efi_calculate_crc32, .copy_mem = efi_copy_mem, .set_mem = efi_set_mem, .create_event_ex = efi_create_event_ex, };
- static uint16_t __efi_runtime_data firmware_vendor[] = L"Das U-Boot";
struct efi_system_table __efi_runtime_data systab = { @@ -2899,11 +2903,11 @@ struct efi_system_table __efi_runtime_data systab = { .headersize = sizeof(struct efi_table_hdr), }, .fw_vendor = (long)firmware_vendor, - .con_in = (void*)&efi_con_in, - .con_out = (void*)&efi_con_out, - .std_err = (void*)&efi_con_out, - .runtime = (void*)&efi_runtime_services, - .boottime = (void*)&efi_boot_services, + .con_in = (void *)&efi_con_in, + .con_out = (void *)&efi_con_out, + .std_err = (void *)&efi_con_out, + .runtime = (void *)&efi_runtime_services, + .boottime = (void *)&efi_boot_services, .nr_tables = 0, - .tables = (void*)efi_conf_table, + .tables = (void *)efi_conf_table, };

Lift the limit on the number of events by using a linked list.
This also allows to have events with type == 0.
This patch is based on Rob's patch efi_loader: fix events https://lists.denx.de/pipermail/u-boot/2017-October/309348.html
Suggested-by: Rob Clark robdclark@gmail.com Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de --- include/efi_loader.h | 11 +-- lib/efi_loader/efi_boottime.c | 192 +++++++++++++++++++----------------------- 2 files changed, 93 insertions(+), 110 deletions(-)
diff --git a/include/efi_loader.h b/include/efi_loader.h index 3ccbb98aaf..7f703c6ef7 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -146,17 +146,19 @@ struct efi_object { /** * struct efi_event * + * @link: Link to list of all events * @type: Type of event, see efi_create_event * @notify_tpl: Task priority level of notifications - * @trigger_time: Period of the timer - * @trigger_next: Next time to trigger the timer * @nofify_function: Function to call when the event is triggered * @notify_context: Data to be passed to the notify function + * @trigger_time: Period of the timer + * @trigger_next: Next time to trigger the timer * @trigger_type: Type of timer, see efi_set_timer - * @queued: The notification function is queued - * @signaled: The event occurred. The event is in the signaled state. + * @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; uint32_t type; efi_uintn_t notify_tpl; void (EFIAPI *notify_function)(struct efi_event *event, void *context); @@ -168,7 +170,6 @@ struct efi_event { bool is_signaled; };
- /* This list contains all UEFI objects we know of */ extern struct list_head efi_obj_list;
diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 75f7c50295..4acb1e092a 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -26,6 +26,9 @@ static efi_uintn_t efi_tpl = TPL_APPLICATION; /* This list contains all the EFI objects our payload has access to */ LIST_HEAD(efi_obj_list);
+/* List of all events */ +static LIST_HEAD(efi_events); + /* * If we're running on nasty systems (32bit ARM booting into non-EFI Linux) * we need to do trickery with caches. Since we don't want to break the EFI @@ -473,10 +476,23 @@ void efi_delete_handle(struct efi_object *obj) }
/* - * Our event capabilities are very limited. Only a small limited - * number of events is allowed to coexist. + * Check if a pointer is a valid event. + * + * @event pointer to check + * @return status code */ -static struct efi_event efi_events[16]; +static efi_status_t efi_is_event(const struct efi_event *event) +{ + const struct efi_event *evt; + + if (!event) + return EFI_INVALID_PARAMETER; + list_for_each_entry(evt, &efi_events, link) { + if (evt == event) + return EFI_SUCCESS; + } + return EFI_INVALID_PARAMETER; +}
/* * Create an event. @@ -499,7 +515,7 @@ efi_status_t efi_create_event(uint32_t type, efi_uintn_t notify_tpl, void *context), void *notify_context, struct efi_event **event) { - int i; + struct efi_event *evt;
if (event == NULL) return EFI_INVALID_PARAMETER; @@ -507,25 +523,24 @@ efi_status_t efi_create_event(uint32_t type, efi_uintn_t notify_tpl, if ((type & EVT_NOTIFY_SIGNAL) && (type & EVT_NOTIFY_WAIT)) return EFI_INVALID_PARAMETER;
- if ((type & (EVT_NOTIFY_SIGNAL|EVT_NOTIFY_WAIT)) && + if ((type & (EVT_NOTIFY_SIGNAL | EVT_NOTIFY_WAIT)) && notify_function == NULL) return EFI_INVALID_PARAMETER;
- for (i = 0; i < ARRAY_SIZE(efi_events); ++i) { - if (efi_events[i].type) - continue; - efi_events[i].type = type; - efi_events[i].notify_tpl = notify_tpl; - efi_events[i].notify_function = notify_function; - efi_events[i].notify_context = notify_context; - /* Disable timers on bootup */ - efi_events[i].trigger_next = -1ULL; - efi_events[i].is_queued = false; - efi_events[i].is_signaled = false; - *event = &efi_events[i]; - return EFI_SUCCESS; - } - return EFI_OUT_OF_RESOURCES; + evt = calloc(1, sizeof(struct efi_event)); + if (!evt) + return EFI_OUT_OF_RESOURCES; + evt->type = type; + evt->notify_tpl = notify_tpl; + evt->notify_function = notify_function; + evt->notify_context = notify_context; + /* Disable timers on bootup */ + evt->trigger_next = -1ULL; + evt->is_queued = false; + evt->is_signaled = false; + list_add_tail(&evt->link, &efi_events); + *event = evt; + return EFI_SUCCESS; }
/* @@ -596,30 +611,26 @@ static efi_status_t EFIAPI efi_create_event_ext( */ void efi_timer_check(void) { - int i; + struct efi_event *evt; u64 now = timer_get_us();
- for (i = 0; i < ARRAY_SIZE(efi_events); ++i) { - if (!efi_events[i].type) + list_for_each_entry(evt, &efi_events, link) { + if (evt->is_queued) + efi_signal_event(evt, true); + if (!(evt->type & EVT_TIMER) || now < evt->trigger_next) continue; - if (efi_events[i].is_queued) - efi_signal_event(&efi_events[i], true); - if (!(efi_events[i].type & EVT_TIMER) || - now < efi_events[i].trigger_next) - continue; - switch (efi_events[i].trigger_type) { + switch (evt->trigger_type) { case EFI_TIMER_RELATIVE: - efi_events[i].trigger_type = EFI_TIMER_STOP; + evt->trigger_type = EFI_TIMER_STOP; break; case EFI_TIMER_PERIODIC: - efi_events[i].trigger_next += - efi_events[i].trigger_time; + evt->trigger_next += evt->trigger_time; break; default: continue; } - efi_events[i].is_signaled = true; - efi_signal_event(&efi_events[i], true); + evt->is_signaled = true; + efi_signal_event(evt, true); } WATCHDOG_RESET(); } @@ -638,7 +649,9 @@ void efi_timer_check(void) efi_status_t efi_set_timer(struct efi_event *event, enum efi_timer_delay type, uint64_t trigger_time) { - int i; + /* Check that the event is valid */ + if (efi_is_event(event) != EFI_SUCCESS || !(event->type & EVT_TIMER)) + return EFI_INVALID_PARAMETER;
/* * The parameter defines a multiple of 100ns. @@ -646,30 +659,21 @@ efi_status_t efi_set_timer(struct efi_event *event, enum efi_timer_delay type, */ do_div(trigger_time, 10);
- for (i = 0; i < ARRAY_SIZE(efi_events); ++i) { - if (event != &efi_events[i]) - continue; - - if (!(event->type & EVT_TIMER)) - break; - switch (type) { - case EFI_TIMER_STOP: - event->trigger_next = -1ULL; - break; - case EFI_TIMER_PERIODIC: - case EFI_TIMER_RELATIVE: - event->trigger_next = - timer_get_us() + trigger_time; - break; - default: - return EFI_INVALID_PARAMETER; - } - event->trigger_type = type; - event->trigger_time = trigger_time; - event->is_signaled = false; - return EFI_SUCCESS; + switch (type) { + case EFI_TIMER_STOP: + event->trigger_next = -1ULL; + break; + case EFI_TIMER_PERIODIC: + case EFI_TIMER_RELATIVE: + event->trigger_next = timer_get_us() + trigger_time; + break; + default: + return EFI_INVALID_PARAMETER; } - return EFI_INVALID_PARAMETER; + event->trigger_type = type; + event->trigger_time = trigger_time; + event->is_signaled = false; + return EFI_SUCCESS; }
/* @@ -708,7 +712,7 @@ static efi_status_t EFIAPI efi_wait_for_event(efi_uintn_t num_events, struct efi_event **event, efi_uintn_t *index) { - int i, j; + int i;
EFI_ENTRY("%zd, %p, %p", num_events, event, index);
@@ -719,12 +723,8 @@ static efi_status_t EFIAPI efi_wait_for_event(efi_uintn_t num_events, if (efi_tpl != TPL_APPLICATION) return EFI_EXIT(EFI_UNSUPPORTED); for (i = 0; i < num_events; ++i) { - for (j = 0; j < ARRAY_SIZE(efi_events); ++j) { - if (event[i] == &efi_events[j]) - goto known_event; - } - return EFI_EXIT(EFI_INVALID_PARAMETER); -known_event: + if (efi_is_event(event[i]) != EFI_SUCCESS) + return EFI_EXIT(EFI_INVALID_PARAMETER); if (!event[i]->type || event[i]->type & EVT_NOTIFY_SIGNAL) return EFI_EXIT(EFI_INVALID_PARAMETER); if (!event[i]->is_signaled) @@ -768,18 +768,13 @@ out: */ static efi_status_t EFIAPI efi_signal_event_ext(struct efi_event *event) { - int i; - EFI_ENTRY("%p", event); - for (i = 0; i < ARRAY_SIZE(efi_events); ++i) { - if (event != &efi_events[i]) - continue; - if (event->is_signaled) - break; + 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); - break; } return EFI_EXIT(EFI_SUCCESS); } @@ -796,19 +791,12 @@ static efi_status_t EFIAPI efi_signal_event_ext(struct efi_event *event) */ static efi_status_t EFIAPI efi_close_event(struct efi_event *event) { - int i; - EFI_ENTRY("%p", event); - for (i = 0; i < ARRAY_SIZE(efi_events); ++i) { - if (event == &efi_events[i]) { - event->type = 0; - event->trigger_next = -1ULL; - event->is_queued = false; - event->is_signaled = false; - return EFI_EXIT(EFI_SUCCESS); - } - } - return EFI_EXIT(EFI_INVALID_PARAMETER); + if (efi_is_event(event) != EFI_SUCCESS) + return EFI_EXIT(EFI_INVALID_PARAMETER); + list_del(&event->link); + free(event); + return EFI_EXIT(EFI_SUCCESS); }
/* @@ -826,24 +814,18 @@ static efi_status_t EFIAPI efi_close_event(struct efi_event *event) */ static efi_status_t EFIAPI efi_check_event(struct efi_event *event) { - int i; - EFI_ENTRY("%p", event); efi_timer_check(); - for (i = 0; i < ARRAY_SIZE(efi_events); ++i) { - if (event != &efi_events[i]) - continue; - if (!event->type || event->type & EVT_NOTIFY_SIGNAL) - break; - if (!event->is_signaled) - efi_signal_event(event, true); - if (event->is_signaled) { - event->is_signaled = false; - return EFI_EXIT(EFI_SUCCESS); - } - return EFI_EXIT(EFI_NOT_READY); + if (efi_is_event(event) != EFI_SUCCESS || + event->type & EVT_NOTIFY_SIGNAL) + return EFI_EXIT(EFI_INVALID_PARAMETER); + if (!event->is_signaled) + efi_signal_event(event, true); + if (event->is_signaled) { + event->is_signaled = false; + return EFI_EXIT(EFI_SUCCESS); } - return EFI_EXIT(EFI_INVALID_PARAMETER); + return EFI_EXIT(EFI_NOT_READY); }
/* @@ -1729,7 +1711,7 @@ static void efi_exit_caches(void) static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, unsigned long map_key) { - int i; + struct efi_event *evt;
EFI_ENTRY("%p, %ld", image_handle, map_key);
@@ -1741,11 +1723,11 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, return EFI_EXIT(EFI_SUCCESS);
/* Notify that ExitBootServices is invoked. */ - for (i = 0; i < ARRAY_SIZE(efi_events); ++i) { - if (efi_events[i].type != EVT_SIGNAL_EXIT_BOOT_SERVICES) + list_for_each_entry(evt, &efi_events, link) { + if (evt->type != EVT_SIGNAL_EXIT_BOOT_SERVICES) continue; - efi_events[i].is_signaled = true; - efi_signal_event(&efi_events[i], false); + evt->is_signaled = true; + efi_signal_event(evt, false); }
/* TODO Should persist EFI variables here */

Event groups are used to signal multiple events at the same time. They are identified by GUIDs. This patch provided the predefined GUIDs of UEFI specification 2.7.
Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de --- include/efi_api.h | 21 +++++++++++++++++++++ include/efi_loader.h | 10 ++++++++++ lib/efi_loader/efi_boottime.c | 16 ++++++++++++++++ 3 files changed, 47 insertions(+)
diff --git a/include/efi_api.h b/include/efi_api.h index 13ed80ac97..d20058540a 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -244,6 +244,27 @@ struct efi_runtime_services { u64 maximum_variable_size); };
+/* EFI event group GUID definitions */ +#define EFI_EVENT_GROUP_EXIT_BOOT_SERVICES \ + EFI_GUID(0x27abf055, 0xb1b8, 0x4c26, 0x80, 0x48, \ + 0x74, 0x8f, 0x37, 0xba, 0xa2, 0xdf) + +#define EFI_EVENT_GROUP_VIRTUAL_ADDRESS_CHANGE \ + EFI_GUID(0x13fa7698, 0xc831, 0x49c7, 0x87, 0xea, \ + 0x8f, 0x43, 0xfc, 0xc2, 0x51, 0x96) + +#define EFI_EVENT_GROUP_MEMORY_MAP_CHANGE \ + EFI_GUID(0x78bee926, 0x692f, 0x48fd, 0x9e, 0xdb, \ + 0x01, 0x42, 0x2e, 0xf0, 0xd7, 0xab) + +#define EFI_EVENT_GROUP_READY_TO_BOOT \ + EFI_GUID(0x7ce88fb3, 0x4bd7, 0x4679, 0x87, 0xa8, \ + 0xa8, 0xd8, 0xde, 0xe5, 0x0d, 0x2b) + +#define EFI_EVENT_GROUP_RESET_SYSTEM \ + EFI_GUID(0x62da6a56, 0x13fb, 0x485a, 0xa8, 0xda, \ + 0xa3, 0xdd, 0x79, 0x12, 0xcb, 0x6b) + /* EFI Configuration Table and GUID definitions */ #define NULL_GUID \ EFI_GUID(0x00000000, 0x0000, 0x0000, 0x00, 0x00, \ diff --git a/include/efi_loader.h b/include/efi_loader.h index 7f703c6ef7..e2cd249171 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -93,6 +93,16 @@ extern const efi_guid_t efi_guid_console_control; extern const efi_guid_t efi_guid_device_path; /* GUID of the EFI_DRIVER_BINDING_PROTOCOL */ extern const efi_guid_t efi_guid_driver_binding_protocol; +/* event group ExitBootServices() invoked */ +extern const efi_guid_t efi_guid_event_group_exit_boot_services; +/* event group SetVirtualAddressMap() invoked */ +extern const efi_guid_t efi_guid_event_group_virtual_address_change; +/* event group memory map changed */ +extern const efi_guid_t efi_guid_event_group_memory_map_change; +/* event group boot manager about to boot */ +extern const efi_guid_t efi_guid_event_group_ready_to_boot; +/* event group ResetSystem() invoked (before ExitBootServices) */ +extern const efi_guid_t efi_guid_event_group_reset_system; /* GUID of the device tree table */ extern const efi_guid_t efi_guid_fdt; extern const efi_guid_t efi_guid_loaded_image; diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 4acb1e092a..da3c852b44 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -65,6 +65,22 @@ const efi_guid_t efi_guid_fdt = EFI_FDT_GUID; const efi_guid_t efi_guid_driver_binding_protocol = EFI_DRIVER_BINDING_PROTOCOL_GUID;
+/* event group ExitBootServices() invoked */ +const efi_guid_t efi_guid_event_group_exit_boot_services = + EFI_EVENT_GROUP_EXIT_BOOT_SERVICES; +/* event group SetVirtualAddressMap() invoked */ +const efi_guid_t efi_guid_event_group_virtual_address_change = + EFI_EVENT_GROUP_VIRTUAL_ADDRESS_CHANGE; +/* event group memory map changed */ +const efi_guid_t efi_guid_event_group_memory_map_change = + EFI_EVENT_GROUP_MEMORY_MAP_CHANGE; +/* event group boot manager about to boot */ +const efi_guid_t efi_guid_event_group_ready_to_boot = + EFI_EVENT_GROUP_READY_TO_BOOT; +/* event group ResetSystem() invoked (before ExitBootServices) */ +const efi_guid_t efi_guid_event_group_reset_system = + EFI_EVENT_GROUP_RESET_SYSTEM; + static efi_status_t EFIAPI efi_disconnect_controller( efi_handle_t controller_handle, efi_handle_t driver_image_handle,

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 --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");

Supply a unit test for event groups.
Create multiple events in an event group. Signal each event once and check that all events are notified once in each round.
Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de --- lib/efi_selftest/Makefile | 1 + lib/efi_selftest/efi_selftest_event_groups.c | 140 +++++++++++++++++++++++++++ 2 files changed, 141 insertions(+) create mode 100644 lib/efi_selftest/efi_selftest_event_groups.c
diff --git a/lib/efi_selftest/Makefile b/lib/efi_selftest/Makefile index b975da2955..51af1055e9 100644 --- a/lib/efi_selftest/Makefile +++ b/lib/efi_selftest/Makefile @@ -19,6 +19,7 @@ efi_selftest_controllers.o \ efi_selftest_console.o \ efi_selftest_devicepath.o \ efi_selftest_events.o \ +efi_selftest_event_groups.o \ efi_selftest_exitbootservices.o \ efi_selftest_fdt.o \ efi_selftest_gop.o \ diff --git a/lib/efi_selftest/efi_selftest_event_groups.c b/lib/efi_selftest/efi_selftest_event_groups.c new file mode 100644 index 0000000000..79e4ea1ce2 --- /dev/null +++ b/lib/efi_selftest/efi_selftest_event_groups.c @@ -0,0 +1,140 @@ +/* + * efi_selftest_event_groups + * + * Copyright (c) 2018 Heinrich Schuchardt xypron.glpk@gmx.de + * + * SPDX-License-Identifier: GPL-2.0+ + * + * This test checks the notification of group events and the + * following services: + * CreateEventEx, CloseEvent, SignalEvent, CheckEvent. + */ + +#include <efi_selftest.h> + +#define GROUP_SIZE 16 + +static struct efi_boot_services *boottime; +static efi_guid_t event_group = + EFI_GUID(0x2335905b, 0xc3b9, 0x4221, 0xa3, 0x71, + 0x0e, 0x5b, 0x45, 0xc0, 0x56, 0x91); + +/* + * Notification function, increments the notfication count if parameter + * context is provided. + * + * @event notified event + * @context pointer to the notification count + */ +static void EFIAPI notify(struct efi_event *event, void *context) +{ + unsigned int *count = context; + + if (count) + ++*count; +} + +/* + * Setup unit test. + * + * @handle: handle of the loaded image + * @systable: system table + * @return: EFI_ST_SUCCESS for success + */ +static int setup(const efi_handle_t handle, + const struct efi_system_table *systable) +{ + boottime = systable->boottime; + + return EFI_ST_SUCCESS; +} + +/* + * Execute unit test. + * + * Create multiple events in an event group. Signal each event once and check + * that all events are notified once in each round. + * + * @return: EFI_ST_SUCCESS for success + */ +static int execute(void) +{ + unsigned int counter[GROUP_SIZE] = {0}; + struct efi_event *events[GROUP_SIZE]; + size_t i, j; + efi_status_t ret; + + for (i = 0; i < GROUP_SIZE; ++i) { + ret = boottime->create_event_ex(0, TPL_NOTIFY, + notify, (void *)&counter[i], + &event_group, &events[i]); + if (ret != EFI_SUCCESS) { + efi_st_error("Failed to create event\n"); + return EFI_ST_FAILURE; + } + } + + for (i = 0; i < GROUP_SIZE; ++i) { + ret = boottime->signal_event(events[i]); + if (ret != EFI_SUCCESS) { + efi_st_error("Failed to signal event\n"); + return EFI_ST_FAILURE; + } + for (j = 0; j < GROUP_SIZE; ++j) { + if (counter[j] != i) { + 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"); + return EFI_ST_FAILURE; + } + /* Clear signaled state */ + ret = boottime->check_event(events[j]); + if (ret != EFI_SUCCESS) { + efi_st_error("Event was not signaled\n"); + return EFI_ST_FAILURE; + } + if (counter[j] != i) { + 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"); + return EFI_ST_FAILURE; + } + /* Call notification function */ + ret = boottime->check_event(events[j]); + if (ret != EFI_NOT_READY) { + efi_st_error( + "Signaled state not cleared\n"); + return EFI_ST_FAILURE; + } + if (counter[j] != 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( + "Nofification function not called\n"); + return EFI_ST_FAILURE; + } + } + } + + for (i = 0; i < GROUP_SIZE; ++i) { + ret = boottime->close_event(events[i]); + if (ret != EFI_SUCCESS) { + efi_st_error("Failed to close event\n"); + return EFI_ST_FAILURE; + } + } + + return EFI_ST_SUCCESS; +} + +EFI_UNIT_TEST(eventgoups) = { + .name = "event groups", + .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT, + .setup = setup, + .execute = execute, +};

On 02/18/2018 03:17 PM, Heinrich Schuchardt wrote:
Supply a unit test for event groups.
Create multiple events in an event group. Signal each event once and check that all events are notified once in each round.
Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de
Hello Alex,
you merged patch 1-4 of this series. Is there a problem with this patch providing a unit test?
Regards
Heinrich

On 12.03.18 18:59, Heinrich Schuchardt wrote:
On 02/18/2018 03:17 PM, Heinrich Schuchardt wrote:
Supply a unit test for event groups.
Create multiple events in an event group. Signal each event once and check that all events are notified once in each round.
Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de
Hello Alex,
you merged patch 1-4 of this series. Is there a problem with this patch providing a unit test?
Not at all, it probably just slipped through :)
Alex
participants (2)
-
Alexander Graf
-
Heinrich Schuchardt