diff mbox series

[U-Boot,07/11] efi_loader: fix events

Message ID 20171010122309.25313-8-robdclark@gmail.com
State Superseded, archived
Delegated to: Alexander Graf
Headers show
Series efi_loader: patches for Shell.efi | expand

Commit Message

Rob Clark Oct. 10, 2017, 12:23 p.m. UTC
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@gmail.com>
---
 include/efi_loader.h          |   1 +
 lib/efi_loader/efi_boottime.c | 217 +++++++++++++++++++++---------------------
 2 files changed, 111 insertions(+), 107 deletions(-)

Comments

Heinrich Schuchardt Oct. 10, 2017, 10:40 p.m. UTC | #1
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@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));
> +	if (evt->is_signaled) {
> +		evt->is_signaled = true;
> +		return EFI_EXIT(EFI_SUCCESS);
>   	}
> -	return EFI_EXIT(EFI_INVALID_PARAMETER);
> +	return EFI_EXIT(EFI_NOT_READY);
>   }
>   
>   /*
> @@ -1440,15 +1443,15 @@ static void efi_exit_caches(void)
>   static efi_status_t EFIAPI efi_exit_boot_services(void *image_handle,
>   						  unsigned long map_key)
>   {
> -	int i;
> +	struct efi_event *evt;
>   
>   	EFI_ENTRY("%p, %ld", image_handle, map_key);
>   
>   	/* 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_signal_event(&efi_events[i]);
> +		efi_signal_event(evt);
>   	}
>   	/* Make sure that notification functions are not called anymore */
>   	efi_tpl = TPL_HIGH_LEVEL;
> 

Thanks for adding efi_is_event().

Tested-by: Heinrich Schuchardt <xypron.glpk@gmx.de>
Acked-by: Heinrich Schuchardt <xypron.glpk@gmx.de>
Alexander Graf Oct. 11, 2017, 2:49 p.m. UTC | #2
On 10.10.17 14:23, 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@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)
> +/*
> + */

I assume this is one comment block too much?

> +static efi_status_t EFIAPI efi_check_event(struct efi_event *evt)
>  {


Alex
Rob Clark Oct. 11, 2017, 10:09 p.m. UTC | #3
On Wed, Oct 11, 2017 at 10:49 AM, Alexander Graf <agraf@suse.de> wrote:
>
>
> On 10.10.17 14:23, 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@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)
>> +/*
>> + */
>
> I assume this is one comment block too much?

ugg, looks like I screwed that up when rebase/mergetool'ing.. can you
drop that when applying or do you want me to resend?

BR,
-R

>> +static efi_status_t EFIAPI efi_check_event(struct efi_event *evt)
>>  {
>
>
> Alex
>
>
Heinrich Schuchardt Oct. 13, 2017, 5:24 a.m. UTC | #4
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@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?

Regards

Heinrich

> +	if (evt->is_signaled) {
> +		evt->is_signaled = true;
> +		return EFI_EXIT(EFI_SUCCESS);
>   	}
> -	return EFI_EXIT(EFI_INVALID_PARAMETER);
> +	return EFI_EXIT(EFI_NOT_READY);
>   }
>   
>   /*
> @@ -1440,15 +1443,15 @@ static void efi_exit_caches(void)
>   static efi_status_t EFIAPI efi_exit_boot_services(void *image_handle,
>   						  unsigned long map_key)
>   {
> -	int i;
> +	struct efi_event *evt;
>   
>   	EFI_ENTRY("%p, %ld", image_handle, map_key);
>   
>   	/* 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_signal_event(&efi_events[i]);
> +		efi_signal_event(evt);
>   	}
>   	/* Make sure that notification functions are not called anymore */
>   	efi_tpl = TPL_HIGH_LEVEL;
>
Rob Clark Oct. 13, 2017, 2:08 p.m. UTC | #5
On Fri, Oct 13, 2017 at 1:24 AM, Heinrich Schuchardt <xypron.glpk@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@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
diff mbox series

Patch

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));
+	if (evt->is_signaled) {
+		evt->is_signaled = true;
+		return EFI_EXIT(EFI_SUCCESS);
 	}
-	return EFI_EXIT(EFI_INVALID_PARAMETER);
+	return EFI_EXIT(EFI_NOT_READY);
 }
 
 /*
@@ -1440,15 +1443,15 @@  static void efi_exit_caches(void)
 static efi_status_t EFIAPI efi_exit_boot_services(void *image_handle,
 						  unsigned long map_key)
 {
-	int i;
+	struct efi_event *evt;
 
 	EFI_ENTRY("%p, %ld", image_handle, map_key);
 
 	/* 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_signal_event(&efi_events[i]);
+		efi_signal_event(evt);
 	}
 	/* Make sure that notification functions are not called anymore */
 	efi_tpl = TPL_HIGH_LEVEL;