[U-Boot] [PATCH 07/11] efi_loader: fix events
Rob Clark
robdclark at gmail.com
Fri Oct 13 14:08:04 UTC 2017
On Fri, Oct 13, 2017 at 1:24 AM, Heinrich Schuchardt <xypron.glpk at gmx.de> wrote:
>
>
> On 10/10/2017 02:23 PM, Rob Clark wrote:
>>
>> An event can be created with type==0, Shell.efi does this for an event
>> that is set when Ctrl-C is typed. So our current approach of having a
>> fixed set of timer slots, and determining which slots are unused by
>> type==0 doesn't work so well. But we don't have any particularly good
>> reason to have a fixed table of events, so just dynamically allocate
>> them and keep a list.
>>
>> Also fixes an incorrect implementation of CheckEvent() which was (a)
>> incorrectly returning an error if type==0, and (b) didn't handle the
>> case of an unsignaled event with a notify callback.
>>
>> With these fixes (plus implementation of SIMPLE_TEXT_INPUT_EX protocol),
>> Ctrl-C works in Shell.efi.
>>
>> Signed-off-by: Rob Clark <robdclark at gmail.com>
>> ---
>> include/efi_loader.h | 1 +
>> lib/efi_loader/efi_boottime.c | 217
>> +++++++++++++++++++++---------------------
>> 2 files changed, 111 insertions(+), 107 deletions(-)
>>
>> diff --git a/include/efi_loader.h b/include/efi_loader.h
>> index e6e55d2cb4..2232caca44 100644
>> --- a/include/efi_loader.h
>> +++ b/include/efi_loader.h
>> @@ -154,6 +154,7 @@ struct efi_event {
>> enum efi_timer_delay trigger_type;
>> bool is_queued;
>> bool is_signaled;
>> + struct list_head link;
>> };
>> diff --git a/lib/efi_loader/efi_boottime.c
>> b/lib/efi_loader/efi_boottime.c
>> index 39dcc72648..19fafe546c 100644
>> --- a/lib/efi_loader/efi_boottime.c
>> +++ b/lib/efi_loader/efi_boottime.c
>> @@ -350,11 +350,26 @@ static efi_status_t efi_create_handle(void **handle)
>> return r;
>> }
>> +static LIST_HEAD(efi_events);
>> +
>> /*
>> - * 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.
>> + *
>> + * It might be nice at some point to extend this to a more general
>> + * mechanism to check if pointers passed from the EFI world are
>> + * valid objects of a particular type.
>> */
>> -static struct efi_event efi_events[16];
>> +static bool efi_is_event(const void *obj)
>> +{
>> + struct efi_event *evt;
>> +
>> + list_for_each_entry(evt, &efi_events, link) {
>> + if (evt == obj)
>> + return true;
>> + }
>> +
>> + return false;
>> +}
>> /*
>> * Create an event.
>> @@ -377,7 +392,7 @@ efi_status_t efi_create_event(uint32_t type, UINTN
>> notify_tpl,
>> void *context),
>> void *notify_context, struct efi_event
>> **event)
>> {
>> - int i;
>> + struct efi_event *evt;
>> if (event == NULL)
>> return EFI_INVALID_PARAMETER;
>> @@ -389,21 +404,24 @@ efi_status_t efi_create_event(uint32_t type, UINTN
>> notify_tpl,
>> 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(*evt));
>> + 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;
>> }
>> /*
>> @@ -443,30 +461,31 @@ 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)
>> - continue;
>> - if (efi_events[i].is_queued)
>> - efi_signal_event(&efi_events[i]);
>> - if (!(efi_events[i].type & EVT_TIMER) ||
>> - now < efi_events[i].trigger_next)
>> + /*
>> + * TODO perhaps optimize a bit and track the time of next
>> + * timer to expire?
>> + */
>> + list_for_each_entry(evt, &efi_events, link) {
>> + if (evt->is_queued)
>> + efi_signal_event(evt);
>> + if (!(evt->type & EVT_TIMER) ||
>> + now < evt->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]);
>> + evt->is_signaled = true;
>> + efi_signal_event(evt);
>> }
>> WATCHDOG_RESET();
>> }
>> @@ -485,7 +504,8 @@ 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;
>> + if (!efi_is_event(event))
>> + return EFI_INVALID_PARAMETER;
>> /*
>> * The parameter defines a multiple of 100ns.
>> @@ -493,30 +513,25 @@ 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))
>> + return 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() + 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;
>> }
>> /*
>> @@ -555,7 +570,7 @@ static efi_status_t EFIAPI efi_wait_for_event(unsigned
>> long num_events,
>> struct efi_event **event,
>> size_t *index)
>> {
>> - int i, j;
>> + int i;
>> EFI_ENTRY("%ld, %p, %p", num_events, event, index);
>> @@ -566,12 +581,8 @@ static efi_status_t EFIAPI
>> efi_wait_for_event(unsigned long 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]))
>> + 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)
>> @@ -614,19 +625,12 @@ 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;
>> - event->is_signaled = true;
>> - if (event->type & EVT_NOTIFY_SIGNAL)
>> - efi_signal_event(event);
>> - break;
>> - }
>> + if (!efi_is_event(event))
>> + return EFI_EXIT(EFI_INVALID_PARAMETER);
>> + event->is_signaled = true;
>> + if (event->type & EVT_NOTIFY_SIGNAL)
>> + efi_signal_event(event);
>> return EFI_EXIT(EFI_SUCCESS);
>> }
>> @@ -642,19 +646,10 @@ 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);
>> + list_del(&event->link);
>> + free(event);
>> + return EFI_EXIT(EFI_SUCCESS);
>> }
>> /*
>> @@ -664,29 +659,37 @@ static efi_status_t EFIAPI efi_close_event(struct
>> efi_event *event)
>> * See the Unified Extensible Firmware Interface (UEFI) specification
>> * for details.
>> *
>> - * If an event is not signaled yet the notification function is queued.
>> + * - If Event is in the signaled state, it is cleared and EFI_SUCCESS
>> + * is returned.
>> + *
>> + * - If Event is not in the signaled state and has no notification
>> + * function, EFI_NOT_READY is returned.
>> + *
>> + * - If Event is not in the signaled state but does have a notification
>> + * function, the notification function is queued at the event’s
>> + * notification task priority level. If the execution of the
>> + * notification function causes Event to be signaled, then the signaled
>> + * state is cleared and EFI_SUCCESS is returned; if the Event is not
>> + * signaled, then EFI_NOT_READY is returned.
>> *
>> * @event event to check
>> * @return status code
>> */
>> -static efi_status_t EFIAPI efi_check_event(struct efi_event *event)
>> +/*
>> + */
>> +static efi_status_t EFIAPI efi_check_event(struct efi_event *evt)
>> {
>> - int i;
>> -
>> - EFI_ENTRY("%p", event);
>> + EFI_ENTRY("%p", evt);
>> 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);
>> - if (event->is_signaled)
>> - return EFI_EXIT(EFI_SUCCESS);
>> - return EFI_EXIT(EFI_NOT_READY);
>> + if (!efi_is_event(evt) || (evt->type & EVT_NOTIFY_SIGNAL))
>> + return EFI_EXIT(EFI_INVALID_PARAMETER);
>> + if (!evt->is_signaled && evt->notify_function)
>> + EFI_CALL_VOID(evt->notify_function(evt,
>> evt->notify_context));
>
>
> Here you are doing the contrary of what you describe above:
>
> You do not check the task priority level. You call the notification function
> irrespective of the TPL.
>
> You should queue the notification function if the current TPL is greater or
> equal to the TPL of the notification functions. This is what the removed
> call to function efi_signal_event was doing before your patch.
>
> Could you, please, describe the rationale of this change. Where did you get
> problems with the queuing logic?
>
When I originally wrote this patch (prior to your addition of TPL
handling), we weren't handling the 3rd case mentioned in the comment I
added, IIRC. (We also were running into a problem with an event that
shell was creating with type==0 that it would signal when the user hit
ctrl-c to interrupt a command, which was what prompted the switch to a
list.)
Looks like you fixed the first issue in efi_signal_event(), which I
overlooked when rebasing this patch.
BR,
-R
More information about the U-Boot
mailing list