[U-Boot] [PATCH v2 0/7] efi_loader: event services

Currently U-Boot only allows a single event. This is sufficient to run grub but not for other workloads as iPXE.
The EFI_SIMPLE_TEXT_INPUT_PROTOCOL requires an event WaitForKey. This is used for instance by the iPXE shell.
The 1st patch replaces void * by struct efi_event * where applicable. This renders the code more readable.
The 2nd patch implements the support for multiple events.
The 3rd patch corrects the size of the tpl level function parameters to match UINTN.
The 4th patch makes efi_create_event internally callable.
The 5th patch makes efi_set_timer internally callable.
The 6th patch sets up events to track console input and to signal this to the WaitForKey event.
The 7th patch implements the conversion of multiples of 100ns to multiples of 1000 ns in efi_set_timer.
--- v2 Patches 1, 3, 4, 5, 7 are new. A patch to silence debug output for WaitForKey has been dropped. Patches 2 and 6 are completely reworked compared to v1 of the patch series. Multiple events in WaitForEvent are addressed. The serial console handling is move to dynamically allocated events. ---
Heinrich Schuchardt (7): efi_loader: use struct efi_event * instead of void * efi_loader: implement multiple event support efi_loader: correct size for tpl level efi_loader: refactor efi_create_event efi_loader: refactor efi_set_timer efi_console: set up events efi_loader: correctly implement 100ns conversion
cmd/bootefi.c | 1 + include/efi_api.h | 44 ++++--- include/efi_loader.h | 39 +++++- lib/efi_loader/efi_boottime.c | 298 +++++++++++++++++++++++++++++------------- lib/efi_loader/efi_console.c | 40 +++++- 5 files changed, 316 insertions(+), 106 deletions(-)

In our implementation the internal structure of events is known. So use the known type instead of void.
Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de --- v2 new patch --- include/efi_api.h | 23 +++++++++++++---------- lib/efi_loader/efi_boottime.c | 25 ++++++++++++++----------- 2 files changed, 27 insertions(+), 21 deletions(-)
diff --git a/include/efi_api.h b/include/efi_api.h index ea63e80b47..a1f8221111 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -31,6 +31,8 @@ enum efi_event_type { #define EVT_NOTIFY_WAIT 0x00000100 #define EVT_NOTIFY_SIGNAL 0x00000200
+struct efi_event; + /* EFI Boot Services table */ struct efi_boot_services { struct efi_table_hdr hdr; @@ -48,16 +50,17 @@ struct efi_boot_services {
efi_status_t (EFIAPI *create_event)(enum efi_event_type type, unsigned long notify_tpl, - void (EFIAPI *notify_function) (void *event, - void *context), - void *notify_context, void **event); - efi_status_t (EFIAPI *set_timer)(void *event, int type, + void (EFIAPI *notify_function) ( + struct efi_event *event, + void *context), + void *notify_context, struct efi_event **event); + efi_status_t (EFIAPI *set_timer)(struct efi_event *event, int type, uint64_t trigger_time); efi_status_t (EFIAPI *wait_for_event)(unsigned long number_of_events, - void *event, unsigned long *index); - efi_status_t (EFIAPI *signal_event)(void *event); - efi_status_t (EFIAPI *close_event)(void *event); - efi_status_t (EFIAPI *check_event)(void *event); + struct efi_event **event, unsigned long *index); + efi_status_t (EFIAPI *signal_event)(struct efi_event *event); + efi_status_t (EFIAPI *close_event)(struct efi_event *event); + efi_status_t (EFIAPI *check_event)(struct efi_event *event); #define EFI_NATIVE_INTERFACE 0x00000000 efi_status_t (EFIAPI *install_protocol_interface)( void **handle, efi_guid_t *protocol, @@ -71,7 +74,7 @@ struct efi_boot_services { void **); void *reserved; efi_status_t (EFIAPI *register_protocol_notify)( - efi_guid_t *protocol, void *event, + efi_guid_t *protocol, struct efi_event *event, void **registration); efi_status_t (EFIAPI *locate_handle)( enum efi_locate_search_type search_type, @@ -374,7 +377,7 @@ struct efi_simple_input_interface { efi_status_t(EFIAPI *read_key_stroke)( struct efi_simple_input_interface *this, struct efi_input_key *key); - void *wait_for_key; + struct efi_event *wait_for_key; };
#define CONSOLE_CONTROL_GUID \ diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index fa264f3f3d..0eda465359 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -166,13 +166,14 @@ static efi_status_t EFIAPI efi_free_pool_ext(void *buffer) * Our event capabilities are very limited. Only support a single * event to exist, so we don't need to maintain lists. */ -static struct { +static struct efi_event { enum efi_event_type type; u32 trigger_type; u32 trigger_time; u64 trigger_next; unsigned long notify_tpl; - void (EFIAPI *notify_function) (void *event, void *context); + void (EFIAPI *notify_function) (struct efi_event *event, + void *context); void *notify_context; } efi_event = { /* Disable timers on bootup */ @@ -181,9 +182,10 @@ static struct {
static efi_status_t EFIAPI efi_create_event( enum efi_event_type type, ulong notify_tpl, - void (EFIAPI *notify_function) (void *event, - void *context), - void *notify_context, void **event) + void (EFIAPI *notify_function) ( + struct efi_event *event, + void *context), + void *notify_context, struct efi_event **event) { EFI_ENTRY("%d, 0x%lx, %p, %p", type, notify_tpl, notify_function, notify_context); @@ -231,7 +233,7 @@ void efi_timer_check(void) WATCHDOG_RESET(); }
-static efi_status_t EFIAPI efi_set_timer(void *event, int type, +static efi_status_t EFIAPI efi_set_timer(struct efi_event *event, int type, uint64_t trigger_time) { /* We don't have 64bit division available everywhere, so limit timer @@ -268,7 +270,8 @@ static efi_status_t EFIAPI efi_set_timer(void *event, int type, }
static efi_status_t EFIAPI efi_wait_for_event(unsigned long num_events, - void *event, unsigned long *index) + struct efi_event **event, + unsigned long *index) { u64 now;
@@ -281,20 +284,20 @@ static efi_status_t EFIAPI efi_wait_for_event(unsigned long num_events, return EFI_EXIT(EFI_SUCCESS); }
-static efi_status_t EFIAPI efi_signal_event(void *event) +static efi_status_t EFIAPI efi_signal_event(struct efi_event *event) { EFI_ENTRY("%p", event); return EFI_EXIT(EFI_SUCCESS); }
-static efi_status_t EFIAPI efi_close_event(void *event) +static efi_status_t EFIAPI efi_close_event(struct efi_event *event) { EFI_ENTRY("%p", event); efi_event.trigger_next = -1ULL; return EFI_EXIT(EFI_SUCCESS); }
-static efi_status_t EFIAPI efi_check_event(void *event) +static efi_status_t EFIAPI efi_check_event(struct efi_event *event) { EFI_ENTRY("%p", event); return EFI_EXIT(EFI_NOT_READY); @@ -429,7 +432,7 @@ static efi_status_t EFIAPI efi_uninstall_protocol_interface_ext(void *handle, }
static efi_status_t EFIAPI efi_register_protocol_notify(efi_guid_t *protocol, - void *event, + struct efi_event *event, void **registration) { EFI_ENTRY("%p, %p, %p", protocol, event, registration);

Up to now the boot time supported only a single event. This patch now allows four events.
Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de --- v2 add TPL constants consider multiple events in efi_wait_for_event move notification to new function efi_signal_event --- include/efi_api.h | 13 ++- include/efi_loader.h | 24 ++++++ lib/efi_loader/efi_boottime.c | 195 ++++++++++++++++++++++++++++-------------- 3 files changed, 168 insertions(+), 64 deletions(-)
diff --git a/include/efi_api.h b/include/efi_api.h index a1f8221111..a3b8e04576 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -28,8 +28,17 @@ enum efi_event_type { EFI_TIMER_RELATIVE = 2 };
-#define EVT_NOTIFY_WAIT 0x00000100 -#define EVT_NOTIFY_SIGNAL 0x00000200 +#define EVT_TIMER 0x80000000 +#define EVT_RUNTIME 0x40000000 +#define EVT_NOTIFY_WAIT 0x00000100 +#define EVT_NOTIFY_SIGNAL 0x00000200 +#define EVT_SIGNAL_EXIT_BOOT_SERVICES 0x00000201 +#define EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE 0x60000202 + +#define TPL_APPLICATION 0x04 +#define TPL_CALLBACK 0x08 +#define TPL_NOTIFY 0x10 +#define TPL_HIGH_LEVEL 0x1F
struct efi_event;
diff --git a/include/efi_loader.h b/include/efi_loader.h index d7847d23e5..3d18bfbd2e 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -63,6 +63,30 @@ struct efi_object { void *handle; };
+/** + * struct efi_event + * + * @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_type: Type of timer, see efi_set_timer + * @signaled: The notify function was already called + */ +struct efi_event { + u32 type; + unsigned long notify_tpl; + void (EFIAPI *notify_function)(struct efi_event *event, void *context); + void *notify_context; + u64 trigger_next; + u64 trigger_time; + enum efi_event_type trigger_type; + int 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 0eda465359..a49acc8693 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -82,6 +82,18 @@ efi_status_t efi_exit_func(efi_status_t ret) return ret; }
+static void efi_signal_event(struct efi_event *event) +{ + if (event->signaled) + return; + event->signaled = 1; + if (event->type & EVT_NOTIFY_SIGNAL) { + EFI_EXIT(EFI_SUCCESS); + event->notify_function(event, event->notify_context); + EFI_ENTRY("returning from notification function"); + } +} + static efi_status_t efi_unsupported(const char *funcname) { debug("EFI: App called into unimplemented function %s\n", funcname); @@ -163,22 +175,10 @@ static efi_status_t EFIAPI efi_free_pool_ext(void *buffer) }
/* - * Our event capabilities are very limited. Only support a single - * event to exist, so we don't need to maintain lists. + * Our event capabilities are very limited. Only a small limited + * number of events is allowed to coexist. */ -static struct efi_event { - enum efi_event_type type; - u32 trigger_type; - u32 trigger_time; - u64 trigger_next; - unsigned long notify_tpl; - void (EFIAPI *notify_function) (struct efi_event *event, - void *context); - void *notify_context; -} efi_event = { - /* Disable timers on bootup */ - .trigger_next = -1ULL, -}; +static struct efi_event efi_events[16];
static efi_status_t EFIAPI efi_create_event( enum efi_event_type type, ulong notify_tpl, @@ -187,13 +187,10 @@ static efi_status_t EFIAPI efi_create_event( void *context), void *notify_context, struct efi_event **event) { + int i; + EFI_ENTRY("%d, 0x%lx, %p, %p", type, notify_tpl, notify_function, notify_context); - if (efi_event.notify_function) { - /* We only support one event at a time */ - return EFI_EXIT(EFI_OUT_OF_RESOURCES); - } - if (event == NULL) return EFI_EXIT(EFI_INVALID_PARAMETER);
@@ -204,13 +201,20 @@ static efi_status_t EFIAPI efi_create_event( notify_function == NULL) return EFI_EXIT(EFI_INVALID_PARAMETER);
- efi_event.type = type; - efi_event.notify_tpl = notify_tpl; - efi_event.notify_function = notify_function; - efi_event.notify_context = notify_context; - *event = &efi_event; - - return EFI_EXIT(EFI_SUCCESS); + 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].signaled = 0; + *event = &efi_events[i]; + return EFI_EXIT(EFI_SUCCESS); + } + return EFI_EXIT(EFI_OUT_OF_RESOURCES); }
/* @@ -219,17 +223,22 @@ static efi_status_t EFIAPI efi_create_event( */ void efi_timer_check(void) { + int i; u64 now = timer_get_us();
- if (now >= efi_event.trigger_next) { - /* Triggering! */ - if (efi_event.trigger_type == EFI_TIMER_PERIODIC) - efi_event.trigger_next += efi_event.trigger_time / 10; - if (efi_event.type & (EVT_NOTIFY_WAIT | EVT_NOTIFY_SIGNAL)) - efi_event.notify_function(&efi_event, - efi_event.notify_context); + 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 || + now < efi_events[i].trigger_next) + continue; + if (efi_events[i].trigger_type == EFI_TIMER_PERIODIC) { + efi_events[i].trigger_next += + efi_events[i].trigger_time / 10; + efi_events[i].signaled = 0; + } + efi_signal_event(&efi_events[i]); } - WATCHDOG_RESET(); }
@@ -239,6 +248,7 @@ static efi_status_t EFIAPI efi_set_timer(struct efi_event *event, int type, /* We don't have 64bit division available everywhere, so limit timer * distances to 32bit bits. */ u32 trigger32 = trigger_time; + int i;
EFI_ENTRY("%p, %d, %"PRIx64, event, type, trigger_time);
@@ -247,60 +257,121 @@ static efi_status_t EFIAPI efi_set_timer(struct efi_event *event, int type, trigger_time, trigger32); }
- if (event != &efi_event) { - /* We only support one event at a time */ - return EFI_EXIT(EFI_INVALID_PARAMETER); - } + for (i = 0; i < ARRAY_SIZE(efi_events); ++i) { + if (event != &efi_events[i]) + continue;
- switch (type) { - case EFI_TIMER_STOP: - efi_event.trigger_next = -1ULL; - break; - case EFI_TIMER_PERIODIC: - case EFI_TIMER_RELATIVE: - efi_event.trigger_next = timer_get_us() + (trigger32 / 10); - break; - default: - return EFI_EXIT(EFI_INVALID_PARAMETER); + 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() + (trigger32 / 10); + break; + default: + return EFI_EXIT(EFI_INVALID_PARAMETER); + } + event->trigger_type = type; + event->trigger_time = trigger_time; + return EFI_EXIT(EFI_SUCCESS); } - efi_event.trigger_type = type; - efi_event.trigger_time = trigger_time; - - return EFI_EXIT(EFI_SUCCESS); + return EFI_EXIT(EFI_INVALID_PARAMETER); }
static efi_status_t EFIAPI efi_wait_for_event(unsigned long num_events, struct efi_event **event, unsigned long *index) { - u64 now; + int i, j;
EFI_ENTRY("%ld, %p, %p", num_events, event, index);
- now = timer_get_us(); - while (now < efi_event.trigger_next) { } - efi_timer_check(); + /* Check parameters */ + if (!num_events || !event) + return EFI_EXIT(EFI_INVALID_PARAMETER); + 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 (!event[i]->type || event[i]->type & EVT_NOTIFY_SIGNAL) + return EFI_EXIT(EFI_INVALID_PARAMETER); + } + + /* Wait for signal */ + for (;;) { + for (i = 0; i < num_events; ++i) { + if (event[i]->signaled) + goto out; + } + /* Allow events to occur. */ + efi_timer_check(); + } + +out: + /* + * Reset the signal which is passed to the caller to allow periodic + * events to occur. + */ + event[i]->signaled = 0; + if (index) + *index = i;
return EFI_EXIT(EFI_SUCCESS); }
-static efi_status_t EFIAPI efi_signal_event(struct efi_event *event) +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; + efi_signal_event(event); + break; + } return EFI_EXIT(EFI_SUCCESS); }
static efi_status_t EFIAPI efi_close_event(struct efi_event *event) { + int i; + EFI_ENTRY("%p", event); - efi_event.trigger_next = -1ULL; - return EFI_EXIT(EFI_SUCCESS); + for (i = 0; i < ARRAY_SIZE(efi_events); ++i) { + if (event == &efi_events[i]) { + event->type = 0; + event->trigger_next = -1ULL; + event->signaled = 0; + return EFI_EXIT(EFI_SUCCESS); + } + } + return EFI_EXIT(EFI_INVALID_PARAMETER); }
static efi_status_t EFIAPI efi_check_event(struct efi_event *event) { + int i; + EFI_ENTRY("%p", event); - return EFI_EXIT(EFI_NOT_READY); + 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->signaled) + return EFI_EXIT(EFI_SUCCESS); + return EFI_EXIT(EFI_NOT_READY); + } + return EFI_EXIT(EFI_INVALID_PARAMETER); }
static efi_status_t EFIAPI efi_install_protocol_interface(void **handle, @@ -1032,7 +1103,7 @@ static const struct efi_boot_services efi_boot_services = { .create_event = efi_create_event, .set_timer = efi_set_timer, .wait_for_event = efi_wait_for_event, - .signal_event = efi_signal_event, + .signal_event = efi_signal_event_ext, .close_event = efi_close_event, .check_event = efi_check_event, .install_protocol_interface = efi_install_protocol_interface_ext,

On 07/18/2017 08:17 PM, Heinrich Schuchardt wrote:
Up to now the boot time supported only a single event. This patch now allows four events.
Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de
v2 add TPL constants consider multiple events in efi_wait_for_event move notification to new function efi_signal_event
include/efi_api.h | 13 ++- include/efi_loader.h | 24 ++++++ lib/efi_loader/efi_boottime.c | 195 ++++++++++++++++++++++++++++-------------- 3 files changed, 168 insertions(+), 64 deletions(-)
diff --git a/include/efi_api.h b/include/efi_api.h index a1f8221111..a3b8e04576 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -28,8 +28,17 @@ enum efi_event_type { EFI_TIMER_RELATIVE = 2 };
-#define EVT_NOTIFY_WAIT 0x00000100 -#define EVT_NOTIFY_SIGNAL 0x00000200 +#define EVT_TIMER 0x80000000 +#define EVT_RUNTIME 0x40000000 +#define EVT_NOTIFY_WAIT 0x00000100 +#define EVT_NOTIFY_SIGNAL 0x00000200 +#define EVT_SIGNAL_EXIT_BOOT_SERVICES 0x00000201 +#define EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE 0x60000202
+#define TPL_APPLICATION 0x04 +#define TPL_CALLBACK 0x08 +#define TPL_NOTIFY 0x10 +#define TPL_HIGH_LEVEL 0x1F
struct efi_event;
diff --git a/include/efi_loader.h b/include/efi_loader.h index d7847d23e5..3d18bfbd2e 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -63,6 +63,30 @@ struct efi_object { void *handle; };
+/**
- struct efi_event
- @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_type: Type of timer, see efi_set_timer
- @signaled: The notify function was already called
- */
+struct efi_event {
- u32 type;
- unsigned long notify_tpl;
- void (EFIAPI *notify_function)(struct efi_event *event, void *context);
- void *notify_context;
- u64 trigger_next;
- u64 trigger_time;
- enum efi_event_type trigger_type;
- int 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 0eda465359..a49acc8693 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -82,6 +82,18 @@ efi_status_t efi_exit_func(efi_status_t ret) return ret; }
+static void efi_signal_event(struct efi_event *event) +{
- if (event->signaled)
return;
- event->signaled = 1;
- if (event->type & EVT_NOTIFY_SIGNAL) {
EFI_EXIT(EFI_SUCCESS);
event->notify_function(event, event->notify_context);
EFI_ENTRY("returning from notification function");
- }
+}
- static efi_status_t efi_unsupported(const char *funcname) { debug("EFI: App called into unimplemented function %s\n", funcname);
@@ -163,22 +175,10 @@ static efi_status_t EFIAPI efi_free_pool_ext(void *buffer) }
/*
- Our event capabilities are very limited. Only support a single
- event to exist, so we don't need to maintain lists.
- Our event capabilities are very limited. Only a small limited
*/
- number of events is allowed to coexist.
-static struct efi_event {
- enum efi_event_type type;
- u32 trigger_type;
- u32 trigger_time;
- u64 trigger_next;
- unsigned long notify_tpl;
- void (EFIAPI *notify_function) (struct efi_event *event,
void *context);
- void *notify_context;
-} efi_event = {
- /* Disable timers on bootup */
- .trigger_next = -1ULL,
-}; +static struct efi_event efi_events[16];
static efi_status_t EFIAPI efi_create_event( enum efi_event_type type, ulong notify_tpl,
The argument type of "type" is (incorrectly) enum efi_event_type. Can you send a follow-up patch that changes it to u32 (or its own enum) to avoid confusion?
Alex

The first argument 'type' of CreateEvent is an 32bit unsigned integer bitmap and not an enum.
The second argument 'type' of SetTimer take values of an enum which is called EFI_TIMER_DELAY in the UEFI standard. To avoid confusion rename efi_event_type to efi_timer_delay.
Reported-by: Alexander Graf agraf@suse.de Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de --- include/efi_api.h | 9 +++++---- include/efi_loader.h | 8 ++++---- lib/efi_loader/efi_boottime.c | 11 ++++++----- 3 files changed, 15 insertions(+), 13 deletions(-)
diff --git a/include/efi_api.h b/include/efi_api.h index d52eea4086..8f881d2903 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -22,7 +22,7 @@ #endif
/* Types and defines for EFI CreateEvent */ -enum efi_event_type { +enum efi_timer_delay { EFI_TIMER_STOP = 0, EFI_TIMER_PERIODIC = 1, EFI_TIMER_RELATIVE = 2 @@ -59,14 +59,15 @@ struct efi_boot_services { efi_status_t (EFIAPI *allocate_pool)(int, unsigned long, void **); efi_status_t (EFIAPI *free_pool)(void *);
- efi_status_t (EFIAPI *create_event)(enum efi_event_type type, + efi_status_t (EFIAPI *create_event)(uint32_t type, UINTN notify_tpl, void (EFIAPI *notify_function) ( struct efi_event *event, void *context), void *notify_context, struct efi_event **event); - efi_status_t (EFIAPI *set_timer)(struct efi_event *event, int type, - uint64_t trigger_time); + efi_status_t (EFIAPI *set_timer)(struct efi_event *event, + enum efi_timer_delay type, + uint64_t trigger_time); efi_status_t (EFIAPI *wait_for_event)(unsigned long number_of_events, struct efi_event **event, unsigned long *index); efi_status_t (EFIAPI *signal_event)(struct efi_event *event); diff --git a/include/efi_loader.h b/include/efi_loader.h index 2abb6b87e1..70dc73bbbc 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -76,13 +76,13 @@ struct efi_object { * @signaled: The notify function was already called */ struct efi_event { - u32 type; + uint32_t type; UINTN notify_tpl; void (EFIAPI *notify_function)(struct efi_event *event, void *context); void *notify_context; u64 trigger_next; u64 trigger_time; - enum efi_event_type trigger_type; + enum efi_timer_delay trigger_type; int signaled; };
@@ -119,13 +119,13 @@ void efi_runtime_relocate(ulong offset, struct efi_mem_desc *map); /* Call this to set the current device name */ void efi_set_bootdev(const char *dev, const char *devnr, const char *path); /* Call this to create an event */ -efi_status_t efi_create_event(enum efi_event_type type, UINTN notify_tpl, +efi_status_t efi_create_event(uint32_t type, UINTN notify_tpl, void (EFIAPI *notify_function) ( struct efi_event *event, void *context), void *notify_context, struct efi_event **event); /* Call this to set a timer */ -efi_status_t efi_set_timer(struct efi_event *event, int type, +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); diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index ecd8c8fc71..468d14fda3 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -213,7 +213,7 @@ static efi_status_t EFIAPI efi_free_pool_ext(void *buffer) */ static struct efi_event efi_events[16];
-efi_status_t efi_create_event(enum efi_event_type type, UINTN notify_tpl, +efi_status_t efi_create_event(uint32_t type, UINTN notify_tpl, void (EFIAPI *notify_function) ( struct efi_event *event, void *context), @@ -248,7 +248,7 @@ efi_status_t efi_create_event(enum efi_event_type type, UINTN notify_tpl, }
static efi_status_t EFIAPI efi_create_event_ext( - enum efi_event_type type, UINTN notify_tpl, + uint32_t type, UINTN notify_tpl, void (EFIAPI *notify_function) ( struct efi_event *event, void *context), @@ -286,7 +286,7 @@ void efi_timer_check(void) WATCHDOG_RESET(); }
-efi_status_t efi_set_timer(struct efi_event *event, int type, +efi_status_t efi_set_timer(struct efi_event *event, enum efi_timer_delay type, uint64_t trigger_time) { int i; @@ -322,8 +322,9 @@ efi_status_t efi_set_timer(struct efi_event *event, int type, return EFI_INVALID_PARAMETER; }
-static efi_status_t EFIAPI efi_set_timer_ext(struct efi_event *event, int type, - uint64_t trigger_time) +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); return EFI_EXIT(efi_set_timer(event, type, trigger_time));

Hi,
On 18 July 2017 at 12:17, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
Up to now the boot time supported only a single event. This patch now allows four events.
Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de
v2 add TPL constants consider multiple events in efi_wait_for_event move notification to new function efi_signal_event
include/efi_api.h | 13 ++- include/efi_loader.h | 24 ++++++ lib/efi_loader/efi_boottime.c | 195 ++++++++++++++++++++++++++++-------------- 3 files changed, 168 insertions(+), 64 deletions(-)
Could this use driver model for the events? There is a notify method which could be a device operation.
Regards, Simon

On 07/28/2017 06:19 AM, Simon Glass wrote:
Hi,
On 18 July 2017 at 12:17, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
Up to now the boot time supported only a single event. This patch now allows four events.
Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de
v2 add TPL constants consider multiple events in efi_wait_for_event move notification to new function efi_signal_event
include/efi_api.h | 13 ++- include/efi_loader.h | 24 ++++++ lib/efi_loader/efi_boottime.c | 195 ++++++++++++++++++++++++++++-------------- 3 files changed, 168 insertions(+), 64 deletions(-)
Could this use driver model for the events? There is a notify method which could be a device operation.
Regards, Simon
UEFI events can be signaled between different parts of an UEFI application. This does not necessarily involve any drivers.
So I think this is not the right place to apply the driver model.
If you would like to move more UEFI coding to the driver model you could think about the UEFI device drivers (network, graphical output, console, file system). Having all UEFI device drivers in one uclass might be an interesting direction. Can you provide a design suggestion or patches?
Best regards
Heinrich

Hi,
On 28 July 2017 at 04:45, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 07/28/2017 06:19 AM, Simon Glass wrote:
Hi,
On 18 July 2017 at 12:17, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
Up to now the boot time supported only a single event. This patch now allows four events.
Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de
v2 add TPL constants consider multiple events in efi_wait_for_event move notification to new function efi_signal_event
include/efi_api.h | 13 ++- include/efi_loader.h | 24 ++++++ lib/efi_loader/efi_boottime.c | 195 ++++++++++++++++++++++++++++-------------- 3 files changed, 168 insertions(+), 64 deletions(-)
Could this use driver model for the events? There is a notify method which could be a device operation.
Regards, Simon
UEFI events can be signaled between different parts of an UEFI application. This does not necessarily involve any drivers.
So I think this is not the right place to apply the driver model.
If you would like to move more UEFI coding to the driver model you could think about the UEFI device drivers (network, graphical output, console, file system). Having all UEFI device drivers in one uclass might be an interesting direction. Can you provide a design suggestion or patches?
Perhaps we could start with just the console since it is simple.
One idea is to have a UEFI device as a child of the serial device, in a single UCLASS_EFI as you suggest. Then you can iterate through devices in that uclass to find those which are children of a serial port device (UCLASS_SERIAL).
I suspect that would allow the ad-hoc UEFI data structures to not be needed. Instead, information could be returned 'on the fly' by looking up DM data structures.
I have proposed this a few times. I worry that the direction the code is taking is leading to an unnecessary fork between UEFI support and the rest of U-Boot, which is moving to driver model. If this can be resolved, then it will be easier to adjust things now than later.
Regards, Simon

The UEFI standard defines the type for the tpl level as EFI_TPL alias UINTN.
UINTN is an integer is defined as an unsigned integer of native width. So we can use size_t for the definition.
Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de --- v2 new patch --- include/efi_api.h | 8 +++++--- include/efi_loader.h | 2 +- lib/efi_loader/efi_boottime.c | 12 ++++++------ 3 files changed, 12 insertions(+), 10 deletions(-)
diff --git a/include/efi_api.h b/include/efi_api.h index a3b8e04576..8e5342c79c 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -28,6 +28,8 @@ enum efi_event_type { EFI_TIMER_RELATIVE = 2 };
+#define UINTN size_t + #define EVT_TIMER 0x80000000 #define EVT_RUNTIME 0x40000000 #define EVT_NOTIFY_WAIT 0x00000100 @@ -45,8 +47,8 @@ struct efi_event; /* EFI Boot Services table */ struct efi_boot_services { struct efi_table_hdr hdr; - efi_status_t (EFIAPI *raise_tpl)(unsigned long new_tpl); - void (EFIAPI *restore_tpl)(unsigned long old_tpl); + efi_status_t (EFIAPI *raise_tpl)(UINTN new_tpl); + void (EFIAPI *restore_tpl)(UINTN old_tpl);
efi_status_t (EFIAPI *allocate_pages)(int, int, unsigned long, efi_physical_addr_t *); @@ -58,7 +60,7 @@ struct efi_boot_services { efi_status_t (EFIAPI *free_pool)(void *);
efi_status_t (EFIAPI *create_event)(enum efi_event_type type, - unsigned long notify_tpl, + UINTN notify_tpl, void (EFIAPI *notify_function) ( struct efi_event *event, void *context), diff --git a/include/efi_loader.h b/include/efi_loader.h index 3d18bfbd2e..b922720068 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -77,7 +77,7 @@ struct efi_object { */ struct efi_event { u32 type; - unsigned long notify_tpl; + UINTN notify_tpl; void (EFIAPI *notify_function)(struct efi_event *event, void *context); void *notify_context; u64 trigger_next; diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index a49acc8693..c3c00b7a87 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -105,15 +105,15 @@ static int guidcmp(const efi_guid_t *g1, const efi_guid_t *g2) return memcmp(g1, g2, sizeof(efi_guid_t)); }
-static unsigned long EFIAPI efi_raise_tpl(unsigned long new_tpl) +static unsigned long EFIAPI efi_raise_tpl(UINTN new_tpl) { - EFI_ENTRY("0x%lx", new_tpl); + EFI_ENTRY("0x%zx", new_tpl); return EFI_EXIT(0); }
-static void EFIAPI efi_restore_tpl(unsigned long old_tpl) +static void EFIAPI efi_restore_tpl(UINTN old_tpl) { - EFI_ENTRY("0x%lx", old_tpl); + EFI_ENTRY("0x%zx", old_tpl); EFI_EXIT(efi_unsupported(__func__)); }
@@ -181,7 +181,7 @@ static efi_status_t EFIAPI efi_free_pool_ext(void *buffer) static struct efi_event efi_events[16];
static efi_status_t EFIAPI efi_create_event( - enum efi_event_type type, ulong notify_tpl, + enum efi_event_type type, UINTN notify_tpl, void (EFIAPI *notify_function) ( struct efi_event *event, void *context), @@ -189,7 +189,7 @@ static efi_status_t EFIAPI efi_create_event( { int i;
- EFI_ENTRY("%d, 0x%lx, %p, %p", type, notify_tpl, notify_function, + EFI_ENTRY("%d, 0x%zx, %p, %p", type, notify_tpl, notify_function, notify_context); if (event == NULL) return EFI_EXIT(EFI_INVALID_PARAMETER);

efi_create_event is refactored to make it possible to call it internally. For EFI applications wrapper function efi_create_event_ext is created.
Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de --- v2 new patch --- include/efi_loader.h | 6 ++++++ lib/efi_loader/efi_boottime.c | 35 +++++++++++++++++++++++------------ 2 files changed, 29 insertions(+), 12 deletions(-)
diff --git a/include/efi_loader.h b/include/efi_loader.h index b922720068..c3640153e1 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -116,6 +116,12 @@ efi_status_t efi_exit_func(efi_status_t ret); void efi_runtime_relocate(ulong offset, struct efi_mem_desc *map); /* Call this to set the current device name */ void efi_set_bootdev(const char *dev, const char *devnr, const char *path); +/* Call this to create an event */ +efi_status_t efi_create_event(enum efi_event_type type, UINTN notify_tpl, + void (EFIAPI *notify_function) ( + struct efi_event *event, + void *context), + void *notify_context, struct efi_event **event);
/* Generic EFI memory allocator, call this to get memory */ void *efi_alloc(uint64_t len, int memory_type); diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index c3c00b7a87..e38da35a8a 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -180,26 +180,23 @@ static efi_status_t EFIAPI efi_free_pool_ext(void *buffer) */ static struct efi_event efi_events[16];
-static efi_status_t EFIAPI efi_create_event( - enum efi_event_type type, UINTN notify_tpl, - void (EFIAPI *notify_function) ( +efi_status_t efi_create_event(enum efi_event_type type, UINTN notify_tpl, + void (EFIAPI *notify_function) ( struct efi_event *event, void *context), - void *notify_context, struct efi_event **event) + void *notify_context, struct efi_event **event) { int i;
- EFI_ENTRY("%d, 0x%zx, %p, %p", type, notify_tpl, notify_function, - notify_context); if (event == NULL) - return EFI_EXIT(EFI_INVALID_PARAMETER); + return EFI_INVALID_PARAMETER;
if ((type & EVT_NOTIFY_SIGNAL) && (type & EVT_NOTIFY_WAIT)) - return EFI_EXIT(EFI_INVALID_PARAMETER); + return EFI_INVALID_PARAMETER;
if ((type & (EVT_NOTIFY_SIGNAL|EVT_NOTIFY_WAIT)) && notify_function == NULL) - return EFI_EXIT(EFI_INVALID_PARAMETER); + return EFI_INVALID_PARAMETER;
for (i = 0; i < ARRAY_SIZE(efi_events); ++i) { if (efi_events[i].type) @@ -212,11 +209,25 @@ static efi_status_t EFIAPI efi_create_event( efi_events[i].trigger_next = -1ULL; efi_events[i].signaled = 0; *event = &efi_events[i]; - return EFI_EXIT(EFI_SUCCESS); + return EFI_SUCCESS; } - return EFI_EXIT(EFI_OUT_OF_RESOURCES); + return EFI_OUT_OF_RESOURCES; }
+static efi_status_t EFIAPI efi_create_event_ext( + enum efi_event_type type, UINTN notify_tpl, + void (EFIAPI *notify_function) ( + struct efi_event *event, + void *context), + void *notify_context, struct efi_event **event) +{ + 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)); +} + + /* * Our timers have to work without interrupts, so we check whenever keyboard * input or disk accesses happen if enough time elapsed for it to fire. @@ -1100,7 +1111,7 @@ static const struct efi_boot_services efi_boot_services = { .get_memory_map = efi_get_memory_map_ext, .allocate_pool = efi_allocate_pool_ext, .free_pool = efi_free_pool_ext, - .create_event = efi_create_event, + .create_event = efi_create_event_ext, .set_timer = efi_set_timer, .wait_for_event = efi_wait_for_event, .signal_event = efi_signal_event_ext,

efi_set_timer is refactored to make the function callable internally. Wrapper function efi_set_timer_ext is provided for EFI applications.
Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de --- v2 new patch --- include/efi_loader.h | 3 +++ lib/efi_loader/efi_boottime.c | 21 +++++++++++++-------- 2 files changed, 16 insertions(+), 8 deletions(-)
diff --git a/include/efi_loader.h b/include/efi_loader.h index c3640153e1..342e960d14 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -122,6 +122,9 @@ efi_status_t efi_create_event(enum efi_event_type type, UINTN notify_tpl, struct efi_event *event, void *context), void *notify_context, struct efi_event **event); +/* Call this to set a timer */ +efi_status_t efi_set_timer(struct efi_event *event, int type, + uint64_t trigger_time);
/* Generic EFI memory allocator, call this to get memory */ void *efi_alloc(uint64_t len, int memory_type); diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index e38da35a8a..7f10b6090a 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -253,16 +253,14 @@ void efi_timer_check(void) WATCHDOG_RESET(); }
-static efi_status_t EFIAPI efi_set_timer(struct efi_event *event, int type, - uint64_t trigger_time) +efi_status_t efi_set_timer(struct efi_event *event, int type, + uint64_t trigger_time) { /* We don't have 64bit division available everywhere, so limit timer * distances to 32bit bits. */ u32 trigger32 = trigger_time; int i;
- EFI_ENTRY("%p, %d, %"PRIx64, event, type, trigger_time); - if (trigger32 < trigger_time) { printf("WARNING: Truncating timer from %"PRIx64" to %x\n", trigger_time, trigger32); @@ -284,13 +282,20 @@ static efi_status_t EFIAPI efi_set_timer(struct efi_event *event, int type, timer_get_us() + (trigger32 / 10); break; default: - return EFI_EXIT(EFI_INVALID_PARAMETER); + return EFI_INVALID_PARAMETER; } event->trigger_type = type; event->trigger_time = trigger_time; - return EFI_EXIT(EFI_SUCCESS); + return EFI_SUCCESS; } - return EFI_EXIT(EFI_INVALID_PARAMETER); + return EFI_INVALID_PARAMETER; +} + +static efi_status_t EFIAPI efi_set_timer_ext(struct efi_event *event, int type, + uint64_t trigger_time) +{ + EFI_ENTRY("%p, %d, %"PRIx64, event, type, trigger_time); + return EFI_EXIT(efi_set_timer(event, type, trigger_time)); }
static efi_status_t EFIAPI efi_wait_for_event(unsigned long num_events, @@ -1112,7 +1117,7 @@ static const struct efi_boot_services efi_boot_services = { .allocate_pool = efi_allocate_pool_ext, .free_pool = efi_free_pool_ext, .create_event = efi_create_event_ext, - .set_timer = efi_set_timer, + .set_timer = efi_set_timer_ext, .wait_for_event = efi_wait_for_event, .signal_event = efi_signal_event_ext, .close_event = efi_close_event,

Set up a timer event and the WaitForKey event. In the notify function of the timer event check for console input and signal the WaitForKey event accordingly.
Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de --- v2 dynamically register events to monitor the serial console input --- cmd/bootefi.c | 1 + include/efi_loader.h | 6 +++++- lib/efi_loader/efi_boottime.c | 2 +- lib/efi_loader/efi_console.c | 40 +++++++++++++++++++++++++++++++++++++++- 4 files changed, 46 insertions(+), 3 deletions(-)
diff --git a/cmd/bootefi.c b/cmd/bootefi.c index 966d001d41..cf717564c4 100644 --- a/cmd/bootefi.c +++ b/cmd/bootefi.c @@ -112,6 +112,7 @@ static void efi_init_obj_list(void) efi_obj_list_initalized = 1; list_add_tail(&loaded_image_info_obj.link, &efi_obj_list); list_add_tail(&bootefi_device_obj.link, &efi_obj_list); + efi_console_register(); #ifdef CONFIG_PARTITIONS efi_disk_register(); #endif diff --git a/include/efi_loader.h b/include/efi_loader.h index 342e960d14..2abb6b87e1 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -26,7 +26,7 @@ extern struct efi_runtime_services efi_runtime_services; extern struct efi_system_table systab;
extern const struct efi_simple_text_output_protocol efi_con_out; -extern const struct efi_simple_input_interface efi_con_in; +extern struct efi_simple_input_interface efi_con_in; extern const struct efi_console_control_protocol efi_console_control; extern const struct efi_device_path_to_text_protocol efi_device_path_to_text;
@@ -90,6 +90,8 @@ struct efi_event { /* This list contains all UEFI objects we know of */ extern struct list_head efi_obj_list;
+/* Called by bootefi to make console interface available */ +int efi_console_register(void); /* Called by bootefi to make all disk storage accessible as EFI objects */ int efi_disk_register(void); /* Called by bootefi to make GOP (graphical) interface available */ @@ -125,6 +127,8 @@ efi_status_t efi_create_event(enum efi_event_type type, UINTN notify_tpl, /* Call this to set a timer */ efi_status_t efi_set_timer(struct efi_event *event, int type, uint64_t trigger_time); +/* Call this to signal an event */ +void efi_signal_event(struct efi_event *event);
/* Generic EFI memory allocator, call this to get memory */ void *efi_alloc(uint64_t len, int memory_type); diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 7f10b6090a..137a4036c6 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -82,7 +82,7 @@ efi_status_t efi_exit_func(efi_status_t ret) return ret; }
-static void efi_signal_event(struct efi_event *event) +void efi_signal_event(struct efi_event *event) { if (event->signaled) return; diff --git a/lib/efi_loader/efi_console.c b/lib/efi_loader/efi_console.c index 8ef7326fef..dbe98ac08b 100644 --- a/lib/efi_loader/efi_console.c +++ b/lib/efi_loader/efi_console.c @@ -421,8 +421,46 @@ static efi_status_t EFIAPI efi_cin_read_key_stroke( return EFI_EXIT(EFI_SUCCESS); }
-const struct efi_simple_input_interface efi_con_in = { +struct efi_simple_input_interface efi_con_in = { .reset = efi_cin_reset, .read_key_stroke = efi_cin_read_key_stroke, .wait_for_key = NULL, }; + +static struct efi_event *console_timer_event; + +static void efi_key_notify(struct efi_event *event, void *context) +{ +} + +static void efi_console_timer_notify(struct efi_event *event, void *context) +{ + EFI_ENTRY("%p, %p", event, context); + if (tstc()) + efi_signal_event(efi_con_in.wait_for_key); + EFI_EXIT(EFI_SUCCESS); +} + +/* This gets called from do_bootefi_exec(). */ +int efi_console_register(void) +{ + efi_status_t r; + r = efi_create_event(EVT_NOTIFY_WAIT, TPL_CALLBACK, + efi_key_notify, 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, + &console_timer_event); + if (r != EFI_SUCCESS) { + printf("ERROR: Failed to register console event\n"); + return r; + } + /* 5000 ns cycle is sufficient for 2 MBaud */ + r = efi_set_timer(console_timer_event, EFI_TIMER_PERIODIC, 50); + if (r != EFI_SUCCESS) + printf("ERROR: Failed to set console timer\n"); + return r; +}

In efi_set_timer we receive the trigger time in intervals of 100 ns. We should convert it to intervals of 1000 ns by 64bit division.
The patch supplies function efi_div10 that uses multiplication to implement the missing 64 bit division.
Signed-off-by: Heinrich Schuchardt xypron.glpk@gmx.de --- v2 move this to separate patch implement a 64bit division by 10 --- lib/efi_loader/efi_boottime.c | 48 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 39 insertions(+), 9 deletions(-)
diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 137a4036c6..c58066474e 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -82,6 +82,39 @@ efi_status_t efi_exit_func(efi_status_t ret) return ret; }
+/* Low 32 bit */ +#define EFI_LOW32(a) (a & 0xFFFFFFFFULL) +/* High 32 bit */ +#define EFI_HIGH32(a) (a >> 32) + +/* + * 64bit division by 10 implemented as multiplication by 1 / 10 + * + * Decimals of one tenth: 0x1 / 0xA = 0x0.19999... + */ +#define EFI_TENTH 0x199999999999999A +static u64 efi_div10(u64 a) +{ + u64 prod; + u64 rem; + u64 ret; + + ret = EFI_HIGH32(a) * EFI_HIGH32(EFI_TENTH); + prod = EFI_HIGH32(a) * EFI_LOW32(EFI_TENTH); + rem = EFI_LOW32(prod); + ret += EFI_HIGH32(prod); + prod = EFI_LOW32(a) * EFI_HIGH32(EFI_TENTH); + rem += EFI_LOW32(prod); + ret += EFI_HIGH32(prod); + prod = EFI_LOW32(a) * EFI_LOW32(EFI_TENTH); + rem += EFI_HIGH32(prod); + ret += EFI_HIGH32(rem); + /* Round to nearest integer */ + if (rem >= (1 << 31)) + ++ret; + return ret; +} + void efi_signal_event(struct efi_event *event) { if (event->signaled) @@ -245,7 +277,7 @@ void efi_timer_check(void) continue; if (efi_events[i].trigger_type == EFI_TIMER_PERIODIC) { efi_events[i].trigger_next += - efi_events[i].trigger_time / 10; + efi_events[i].trigger_time; efi_events[i].signaled = 0; } efi_signal_event(&efi_events[i]); @@ -256,15 +288,13 @@ void efi_timer_check(void) efi_status_t efi_set_timer(struct efi_event *event, int type, uint64_t trigger_time) { - /* We don't have 64bit division available everywhere, so limit timer - * distances to 32bit bits. */ - u32 trigger32 = trigger_time; int i;
- if (trigger32 < trigger_time) { - printf("WARNING: Truncating timer from %"PRIx64" to %x\n", - trigger_time, trigger32); - } + /* + * The parameter defines a multiple of 100ns. + * We use multiples of 1000ns. So divide by 10. + */ + trigger_time = efi_div10(trigger_time);
for (i = 0; i < ARRAY_SIZE(efi_events); ++i) { if (event != &efi_events[i]) @@ -279,7 +309,7 @@ efi_status_t efi_set_timer(struct efi_event *event, int type, case EFI_TIMER_PERIODIC: case EFI_TIMER_RELATIVE: event->trigger_next = - timer_get_us() + (trigger32 / 10); + timer_get_us() + trigger_time; break; default: return EFI_INVALID_PARAMETER;
participants (3)
-
Alexander Graf
-
Heinrich Schuchardt
-
Simon Glass