diff mbox

[U-Boot,1/3] efi_loader: implement multiple event support

Message ID 20170705174715.28626-2-xypron.glpk@gmx.de
State Superseded
Delegated to: Alexander Graf
Headers show

Commit Message

Heinrich Schuchardt July 5, 2017, 5:47 p.m. UTC
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>
---
 include/efi_api.h             |   8 ++-
 include/efi_loader.h          |  23 ++++++
 lib/efi_loader/efi_boottime.c | 161 ++++++++++++++++++++++++++----------------
 3 files changed, 131 insertions(+), 61 deletions(-)

Comments

Alexander Graf July 12, 2017, 10:55 a.m. UTC | #1
On 05.07.17 19:47, 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>
> ---
>   include/efi_api.h             |   8 ++-
>   include/efi_loader.h          |  23 ++++++
>   lib/efi_loader/efi_boottime.c | 161 ++++++++++++++++++++++++++----------------
>   3 files changed, 131 insertions(+), 61 deletions(-)
> 
> diff --git a/include/efi_api.h b/include/efi_api.h
> index 42cd47ff08..18bef722c6 100644
> --- a/include/efi_api.h
> +++ b/include/efi_api.h
> @@ -28,8 +28,12 @@ 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
>   
>   /* EFI Boot Services table */
>   struct efi_boot_services {
> diff --git a/include/efi_loader.h b/include/efi_loader.h
> index c620652307..a35b971f7e 100644
> --- a/include/efi_loader.h
> +++ b/include/efi_loader.h
> @@ -71,6 +71,29 @@ struct efi_object {
>   	void *handle;
>   };
>   
> +/**
> + * struct efi_event
> + *
> + * @type:		Type of event
> + * @trigger_type:	Type of timer
> + * @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
> + * @signaled:		The notify function was already called
> + */
> +struct efi_event {
> +	u32 type;
> +	unsigned long notify_tpl;
> +	void (EFIAPI *notify_function)(void *event, void *context);
> +	void *notify_context;
> +	u64 trigger_next;
> +	u32 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 3060c25a2a..ef3e7d9d52 100644
> --- a/lib/efi_loader/efi_boottime.c
> +++ b/lib/efi_loader/efi_boottime.c
> @@ -162,21 +162,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 {
> -	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 *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,
> @@ -184,13 +173,10 @@ static efi_status_t EFIAPI efi_create_event(
>   							void *context),
>   			void *notify_context, void **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);
>   
> @@ -201,13 +187,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)

Please explicitly check for EFI_TIMER_STOP here.

> +			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);
>   }
>   
>   /*
> @@ -216,17 +209,25 @@ 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 & EVT_TIMER) ||
> +		    efi_events[i].trigger_type == EFI_TIMER_STOP ||
> +		    now < efi_events[i].trigger_next)
> +			continue;

This if clause is quite hard to read. I don't think it would hurt to 
just unfold it into individual cases? You can then also document for 
each why they should get ignored.

> +		if (efi_events[i].trigger_type == EFI_TIMER_PERIODIC)
> +			efi_events[i].trigger_next +=
> +				efi_events[i].trigger_time / 10;

I stumbled over my own code here - awesome :). Can you please put the 
division into a static function that tells the reader from which number 
space into which number space we're converting (100ns)?

> +		efi_events[i].signaled = 1;
> +		if (efi_events[i].type & EVT_NOTIFY_SIGNAL) {
> +			EFI_EXIT(EFI_SUCCESS);

This is quite dangerous...

> +			efi_events[i].notify_function(&efi_events[i],
> +					efi_events[i].notify_context);
> +			efi_restore_gd();

... I see that you do restore gd but please document this heavily with a 
big, obvious comment and ideally even use EFI_ENTRY() again here instead 
of hard coding efi_restore_gd().

> +			}

wrong indentation

>   	}
> -
>   	WATCHDOG_RESET();
>   }
>   
> @@ -236,67 +237,109 @@ static efi_status_t EFIAPI efi_set_timer(void *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);
>   
>   	if (trigger32 < trigger_time) {
> +		trigger32 = 0xffffffff;

This change should be a separate patch I suppose. Makes bisecting things 
easier.

>   		printf("WARNING: Truncating timer from %"PRIx64" to %x\n",
>   		       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);
> +		switch (type) {
> +		case EFI_TIMER_STOP:
> +			efi_events[i].trigger_next = -1ULL;
> +			break;
> +		case EFI_TIMER_PERIODIC:
> +		case EFI_TIMER_RELATIVE:
> +			efi_events[i].trigger_next =
> +				timer_get_us() + (trigger32 / 10);
> +			break;
> +		default:
> +			return EFI_EXIT(EFI_INVALID_PARAMETER);
> +		}
> +		efi_events[i].trigger_type = type;
> +		efi_events[i].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,
>   					      void *event, unsigned long *index)
>   {
> -	u64 now;
> +	int i;
>   
>   	EFI_ENTRY("%ld, %p, %p", num_events, event, index);
>   
> -	now = timer_get_us();
> -	while (now < efi_event.trigger_next) { }
> -	efi_timer_check();
> -
> +	for (i = 0; i < ARRAY_SIZE(efi_events); ++i) {
> +		if (event != &efi_events[i])
> +			continue;

We also need to check if EVT_NOTIFY_SIGNAL is set and error out in that 
case, no?

> +		while (!efi_events[i].signaled)
> +			efi_timer_check();

Ah, nice, so your new code also resets the watchdog. Very good :).

> +		efi_events[i].signaled = 0;

Please document that line with reference to the spec.

> +		break;
> +	}
>   	return EFI_EXIT(EFI_SUCCESS);
>   }
>   
>   static efi_status_t EFIAPI efi_signal_event(void *event)
>   {
> +	int i;
> +
>   	EFI_ENTRY("%p", event);
> +	for (i = 0; i < ARRAY_SIZE(efi_events); ++i) {
> +		if (event != &efi_events[i])
> +			continue;
> +		if (efi_events[i].signaled)
> +			break;
> +		efi_events[i].signaled = 1;
> +		if (efi_events[i].type & EVT_NOTIFY_SIGNAL) {
> +			EFI_EXIT(EFI_SUCCESS);
> +			efi_events[i].notify_function(&efi_events[i],
> +					efi_events[i].notify_context);
> +			efi_restore_gd();

This looks like code repetition. Can you fold it in with the same code 
above?

> +		}
> +		break;
> +	}
>   	return EFI_EXIT(EFI_SUCCESS);
>   }
>   
>   static efi_status_t EFIAPI efi_close_event(void *event)
>   {
> +	int i;
> +
>   	EFI_ENTRY("%p", event);
> -	efi_event.trigger_next = -1ULL;
> +	for (i = 0; i < ARRAY_SIZE(efi_events); ++i) {
> +		if (event != &efi_events[i])
> +			continue;
> +		efi_events[i].type = 0;

Use STOPPED here.

> +		efi_events[i].trigger_next = -1ULL;
> +		efi_events[i].signaled = 0;
> +		break;
> +	}
>   	return EFI_EXIT(EFI_SUCCESS);
>   }
>   
>   static efi_status_t EFIAPI efi_check_event(void *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) {

Here to we need to check for EVT_NOTIFY_SIGNAL and error out, no?


Alex

> +		if (event != &efi_events[i])
> +			continue;
> +		if (efi_events[i].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,
>
Heinrich Schuchardt July 15, 2017, 11:43 a.m. UTC | #2
On 07/12/2017 12:55 PM, Alexander Graf wrote:
> 
> 
> On 05.07.17 19:47, 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>
>> ---
>>   include/efi_api.h             |   8 ++-
>>   include/efi_loader.h          |  23 ++++++
>>   lib/efi_loader/efi_boottime.c | 161
>> ++++++++++++++++++++++++++----------------
>>   3 files changed, 131 insertions(+), 61 deletions(-)
>>
>> diff --git a/include/efi_api.h b/include/efi_api.h
>> index 42cd47ff08..18bef722c6 100644
>> --- a/include/efi_api.h
>> +++ b/include/efi_api.h
>> @@ -28,8 +28,12 @@ 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
>>     /* EFI Boot Services table */
>>   struct efi_boot_services {
>> diff --git a/include/efi_loader.h b/include/efi_loader.h
>> index c620652307..a35b971f7e 100644
>> --- a/include/efi_loader.h
>> +++ b/include/efi_loader.h
>> @@ -71,6 +71,29 @@ struct efi_object {
>>       void *handle;
>>   };
>>   +/**
>> + * struct efi_event
>> + *
>> + * @type:        Type of event
>> + * @trigger_type:    Type of timer
>> + * @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
>> + * @signaled:        The notify function was already called
>> + */
>> +struct efi_event {
>> +    u32 type;
>> +    unsigned long notify_tpl;
>> +    void (EFIAPI *notify_function)(void *event, void *context);
>> +    void *notify_context;
>> +    u64 trigger_next;
>> +    u32 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 3060c25a2a..ef3e7d9d52 100644
>> --- a/lib/efi_loader/efi_boottime.c
>> +++ b/lib/efi_loader/efi_boottime.c
>> @@ -162,21 +162,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 {
>> -    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 *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,
>> @@ -184,13 +173,10 @@ static efi_status_t EFIAPI efi_create_event(
>>                               void *context),
>>               void *notify_context, void **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);
>>   @@ -201,13 +187,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)
> 
> Please explicitly check for EFI_TIMER_STOP here.

TimerCancel (alias EFI_TIMER_STOP) is set in TriggerTimer.
If the status of a timer is set to TimerCancel this does not imply that
the event can be deleted (i.e. the slot can be reused).

The owner of the event might decide to call TriggerTimer for the same
event at a later time with TimerPeriodic or TimerRelative.

So I do not understand why I should check for EFI_TIMER_STOP here.

> 
>> +            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);
>>   }
>>     /*
>> @@ -216,17 +209,25 @@ 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 & EVT_TIMER) ||
>> +            efi_events[i].trigger_type == EFI_TIMER_STOP ||
>> +            now < efi_events[i].trigger_next)
>> +            continue;
> 
> This if clause is quite hard to read. I don't think it would hurt to
> just unfold it into individual cases? You can then also document for
> each why they should get ignored.
> 
>> +        if (efi_events[i].trigger_type == EFI_TIMER_PERIODIC)
>> +            efi_events[i].trigger_next +=
>> +                efi_events[i].trigger_time / 10;
> 
> I stumbled over my own code here - awesome :). Can you please put the
> division into a static function that tells the reader from which number
> space into which number space we're converting (100ns)?
> 
>> +        efi_events[i].signaled = 1;
>> +        if (efi_events[i].type & EVT_NOTIFY_SIGNAL) {
>> +            EFI_EXIT(EFI_SUCCESS);
> 
> This is quite dangerous...

What do you mean by "quite dangerous"?

We have to call the EFI application here.
Thus we have to switch the gd context.

> 
>> +            efi_events[i].notify_function(&efi_events[i],
>> +                    efi_events[i].notify_context);
>> +            efi_restore_gd();
> 
> ... I see that you do restore gd but please document this heavily with a
> big, obvious comment and ideally even use EFI_ENTRY() again here instead
> of hard coding efi_restore_gd().

I used efi_restore_gd() to see less messages in debug mode.

In the reply to another patch you suggested to provide a configurable
verbosity level for EFI_ENTRY. So we can use EFI_ENTRY here.

> 
>> +            }
> 
> wrong indentation

ok

> 
>>       }
>> -
>>       WATCHDOG_RESET();
>>   }
>>   @@ -236,67 +237,109 @@ static efi_status_t EFIAPI efi_set_timer(void
>> *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);
>>         if (trigger32 < trigger_time) {
>> +        trigger32 = 0xffffffff;
> 
> This change should be a separate patch I suppose. Makes bisecting things
> easier.

ok

> 
>>           printf("WARNING: Truncating timer from %"PRIx64" to %x\n",
>>                  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);
>> +        switch (type) {
>> +        case EFI_TIMER_STOP:
>> +            efi_events[i].trigger_next = -1ULL;
>> +            break;
>> +        case EFI_TIMER_PERIODIC:
>> +        case EFI_TIMER_RELATIVE:
>> +            efi_events[i].trigger_next =
>> +                timer_get_us() + (trigger32 / 10);
>> +            break;
>> +        default:
>> +            return EFI_EXIT(EFI_INVALID_PARAMETER);
>> +        }
>> +        efi_events[i].trigger_type = type;
>> +        efi_events[i].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,
>>                             void *event, unsigned long *index)
>>   {
>> -    u64 now;
>> +    int i;
>>         EFI_ENTRY("%ld, %p, %p", num_events, event, index);
>>   -    now = timer_get_us();
>> -    while (now < efi_event.trigger_next) { }
>> -    efi_timer_check();
>> -
>> +    for (i = 0; i < ARRAY_SIZE(efi_events); ++i) {
>> +        if (event != &efi_events[i])
>> +            continue;
> 
> We also need to check if EVT_NOTIFY_SIGNAL is set and error out in that
> case, no?

Yes according to the UEFI spec.

> 
>> +        while (!efi_events[i].signaled)
>> +            efi_timer_check();
> 
> Ah, nice, so your new code also resets the watchdog. Very good :).
> 
>> +        efi_events[i].signaled = 0;
> 
> Please document that line with reference to the spec.

ok

> 
>> +        break;
>> +    }
>>       return EFI_EXIT(EFI_SUCCESS);
>>   }
>>     static efi_status_t EFIAPI efi_signal_event(void *event)
>>   {
>> +    int i;
>> +
>>       EFI_ENTRY("%p", event);
>> +    for (i = 0; i < ARRAY_SIZE(efi_events); ++i) {
>> +        if (event != &efi_events[i])
>> +            continue;
>> +        if (efi_events[i].signaled)
>> +            break;
>> +        efi_events[i].signaled = 1;
>> +        if (efi_events[i].type & EVT_NOTIFY_SIGNAL) {
>> +            EFI_EXIT(EFI_SUCCESS);
>> +            efi_events[i].notify_function(&efi_events[i],
>> +                    efi_events[i].notify_context);
>> +            efi_restore_gd();
> 
> This looks like code repetition. Can you fold it in with the same code
> above?

ok
> 
>> +        }
>> +        break;
>> +    }
>>       return EFI_EXIT(EFI_SUCCESS);
>>   }
>>     static efi_status_t EFIAPI efi_close_event(void *event)
>>   {
>> +    int i;
>> +
>>       EFI_ENTRY("%p", event);
>> -    efi_event.trigger_next = -1ULL;
>> +    for (i = 0; i < ARRAY_SIZE(efi_events); ++i) {
>> +        if (event != &efi_events[i])
>> +            continue;
>> +        efi_events[i].type = 0;
> 
> Use STOPPED here.

This is not the trigger_type field (where we could set EFI_TIMER_STOP).

> 
>> +        efi_events[i].trigger_next = -1ULL;
>> +        efi_events[i].signaled = 0;
>> +        break;
>> +    }
>>       return EFI_EXIT(EFI_SUCCESS);
>>   }
>>     static efi_status_t EFIAPI efi_check_event(void *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) {
> 
> Here to we need to check for EVT_NOTIFY_SIGNAL and error out, no?

No. You may want to check if the event available even if you could wait
for it.

E.g. the application sets up an event for console input.
If there is some other work to do, just poll the event with CheckEvent.
If all work is done, use WaitForEvent.

Thanks for reviewing

Regards

Heinrich

>> +        if (event != &efi_events[i])
>> +            continue;
>> +        if (efi_events[i].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,
>>
>
Alexander Graf July 16, 2017, 7:25 a.m. UTC | #3
On 15.07.17 13:43, Heinrich Schuchardt wrote:
> On 07/12/2017 12:55 PM, Alexander Graf wrote:
>>
>>
>> On 05.07.17 19:47, 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>
>>> ---
>>>    include/efi_api.h             |   8 ++-
>>>    include/efi_loader.h          |  23 ++++++
>>>    lib/efi_loader/efi_boottime.c | 161
>>> ++++++++++++++++++++++++++----------------
>>>    3 files changed, 131 insertions(+), 61 deletions(-)
>>>
>>> diff --git a/include/efi_api.h b/include/efi_api.h
>>> index 42cd47ff08..18bef722c6 100644
>>> --- a/include/efi_api.h
>>> +++ b/include/efi_api.h
>>> @@ -28,8 +28,12 @@ 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
>>>      /* EFI Boot Services table */
>>>    struct efi_boot_services {
>>> diff --git a/include/efi_loader.h b/include/efi_loader.h
>>> index c620652307..a35b971f7e 100644
>>> --- a/include/efi_loader.h
>>> +++ b/include/efi_loader.h
>>> @@ -71,6 +71,29 @@ struct efi_object {
>>>        void *handle;
>>>    };
>>>    +/**
>>> + * struct efi_event
>>> + *
>>> + * @type:        Type of event
>>> + * @trigger_type:    Type of timer
>>> + * @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
>>> + * @signaled:        The notify function was already called
>>> + */
>>> +struct efi_event {
>>> +    u32 type;
>>> +    unsigned long notify_tpl;
>>> +    void (EFIAPI *notify_function)(void *event, void *context);
>>> +    void *notify_context;
>>> +    u64 trigger_next;
>>> +    u32 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 3060c25a2a..ef3e7d9d52 100644
>>> --- a/lib/efi_loader/efi_boottime.c
>>> +++ b/lib/efi_loader/efi_boottime.c
>>> @@ -162,21 +162,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 {
>>> -    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 *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,
>>> @@ -184,13 +173,10 @@ static efi_status_t EFIAPI efi_create_event(
>>>                                void *context),
>>>                void *notify_context, void **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);
>>>    @@ -201,13 +187,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)
>>
>> Please explicitly check for EFI_TIMER_STOP here.
> 
> TimerCancel (alias EFI_TIMER_STOP) is set in TriggerTimer.
> If the status of a timer is set to TimerCancel this does not imply that
> the event can be deleted (i.e. the slot can be reused).
> 
> The owner of the event might decide to call TriggerTimer for the same
> event at a later time with TimerPeriodic or TimerRelative.
> 
> So I do not understand why I should check for EFI_TIMER_STOP here.

Simply because you're checking for it already, but implicitly:

enum efi_event_type {
         EFI_TIMER_STOP = 0,
         EFI_TIMER_PERIODIC = 1,
         EFI_TIMER_RELATIVE = 2
};

and to avoid confusion, I prefer to call out enums when we match them. 
Otherwise someone will think that this check will include EFI_TIMER_STOP 
one day ;).

> 
>>
>>> +            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);
>>>    }
>>>      /*
>>> @@ -216,17 +209,25 @@ 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 & EVT_TIMER) ||
>>> +            efi_events[i].trigger_type == EFI_TIMER_STOP ||
>>> +            now < efi_events[i].trigger_next)
>>> +            continue;
>>
>> This if clause is quite hard to read. I don't think it would hurt to
>> just unfold it into individual cases? You can then also document for
>> each why they should get ignored.
>>
>>> +        if (efi_events[i].trigger_type == EFI_TIMER_PERIODIC)
>>> +            efi_events[i].trigger_next +=
>>> +                efi_events[i].trigger_time / 10;
>>
>> I stumbled over my own code here - awesome :). Can you please put the
>> division into a static function that tells the reader from which number
>> space into which number space we're converting (100ns)?
>>
>>> +        efi_events[i].signaled = 1;
>>> +        if (efi_events[i].type & EVT_NOTIFY_SIGNAL) {
>>> +            EFI_EXIT(EFI_SUCCESS);
>>
>> This is quite dangerous...
> 
> What do you mean by "quite dangerous"?
> 
> We have to call the EFI application here.
> Thus we have to switch the gd context.

Yes, but switching context is always tricky :).

> 
>>
>>> +            efi_events[i].notify_function(&efi_events[i],
>>> +                    efi_events[i].notify_context);
>>> +            efi_restore_gd();
>>
>> ... I see that you do restore gd but please document this heavily with a
>> big, obvious comment and ideally even use EFI_ENTRY() again here instead
>> of hard coding efi_restore_gd().
> 
> I used efi_restore_gd() to see less messages in debug mode.
> 
> In the reply to another patch you suggested to provide a configurable
> verbosity level for EFI_ENTRY. So we can use EFI_ENTRY here.

That works too, but for clarity it's usually much nicer to have a full 
function start with ENTRY and end with EXIT. That way things become more 
obvious. I've wasted way too many hours on debugging ENTRY/EXIT 
misplacement.

One thing that would already help readability would be to just move the 
whole exit/entry dance into a separate function. The compiler will 
inline it, so the compiled code should be the same. But at least for the 
reader it's obvious what happens:

static void efi_call_notify(...)
{
	/*
	 * Description about the necessity of this function
	 */

	EFI_EXIT();

	notifier(...);

	/* Go back to U-Boot space */
	EFI_ENTRY();
}

> 
>>
>>> +            }
>>
>> wrong indentation
> 
> ok
> 
>>
>>>        }
>>> -
>>>        WATCHDOG_RESET();
>>>    }
>>>    @@ -236,67 +237,109 @@ static efi_status_t EFIAPI efi_set_timer(void
>>> *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);
>>>          if (trigger32 < trigger_time) {
>>> +        trigger32 = 0xffffffff;
>>
>> This change should be a separate patch I suppose. Makes bisecting things
>> easier.
> 
> ok
> 
>>
>>>            printf("WARNING: Truncating timer from %"PRIx64" to %x\n",
>>>                   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);
>>> +        switch (type) {
>>> +        case EFI_TIMER_STOP:
>>> +            efi_events[i].trigger_next = -1ULL;
>>> +            break;
>>> +        case EFI_TIMER_PERIODIC:
>>> +        case EFI_TIMER_RELATIVE:
>>> +            efi_events[i].trigger_next =
>>> +                timer_get_us() + (trigger32 / 10);
>>> +            break;
>>> +        default:
>>> +            return EFI_EXIT(EFI_INVALID_PARAMETER);
>>> +        }
>>> +        efi_events[i].trigger_type = type;
>>> +        efi_events[i].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,
>>>                              void *event, unsigned long *index)
>>>    {
>>> -    u64 now;
>>> +    int i;
>>>          EFI_ENTRY("%ld, %p, %p", num_events, event, index);
>>>    -    now = timer_get_us();
>>> -    while (now < efi_event.trigger_next) { }
>>> -    efi_timer_check();
>>> -
>>> +    for (i = 0; i < ARRAY_SIZE(efi_events); ++i) {
>>> +        if (event != &efi_events[i])
>>> +            continue;
>>
>> We also need to check if EVT_NOTIFY_SIGNAL is set and error out in that
>> case, no?
> 
> Yes according to the UEFI spec.
> 
>>
>>> +        while (!efi_events[i].signaled)
>>> +            efi_timer_check();
>>
>> Ah, nice, so your new code also resets the watchdog. Very good :).
>>
>>> +        efi_events[i].signaled = 0;
>>
>> Please document that line with reference to the spec.
> 
> ok
> 
>>
>>> +        break;
>>> +    }
>>>        return EFI_EXIT(EFI_SUCCESS);
>>>    }
>>>      static efi_status_t EFIAPI efi_signal_event(void *event)
>>>    {
>>> +    int i;
>>> +
>>>        EFI_ENTRY("%p", event);
>>> +    for (i = 0; i < ARRAY_SIZE(efi_events); ++i) {
>>> +        if (event != &efi_events[i])
>>> +            continue;
>>> +        if (efi_events[i].signaled)
>>> +            break;
>>> +        efi_events[i].signaled = 1;
>>> +        if (efi_events[i].type & EVT_NOTIFY_SIGNAL) {
>>> +            EFI_EXIT(EFI_SUCCESS);
>>> +            efi_events[i].notify_function(&efi_events[i],
>>> +                    efi_events[i].notify_context);
>>> +            efi_restore_gd();
>>
>> This looks like code repetition. Can you fold it in with the same code
>> above?
> 
> ok
>>
>>> +        }
>>> +        break;
>>> +    }
>>>        return EFI_EXIT(EFI_SUCCESS);
>>>    }
>>>      static efi_status_t EFIAPI efi_close_event(void *event)
>>>    {
>>> +    int i;
>>> +
>>>        EFI_ENTRY("%p", event);
>>> -    efi_event.trigger_next = -1ULL;
>>> +    for (i = 0; i < ARRAY_SIZE(efi_events); ++i) {
>>> +        if (event != &efi_events[i])
>>> +            continue;
>>> +        efi_events[i].type = 0;
>>
>> Use STOPPED here.
> 
> This is not the trigger_type field (where we could set EFI_TIMER_STOP).

Ok, I'm confused. In efi_create_event() you set efi_events[i] to the 
parameter "type" which is enum efi_event_type, no?

So what are the semantics of the "type" field vs "trigger_type"?

To clarify that, please make the "type" field its own enum and double 
check that it only gets assigned with values that match that enum.

> 
>>
>>> +        efi_events[i].trigger_next = -1ULL;
>>> +        efi_events[i].signaled = 0;
>>> +        break;
>>> +    }
>>>        return EFI_EXIT(EFI_SUCCESS);
>>>    }
>>>      static efi_status_t EFIAPI efi_check_event(void *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) {
>>
>> Here to we need to check for EVT_NOTIFY_SIGNAL and error out, no?
> 
> No. You may want to check if the event available even if you could wait
> for it.
> 
> E.g. the application sets up an event for console input.
> If there is some other work to do, just poll the event with CheckEvent.
> If all work is done, use WaitForEvent.

Well, I agree, but the spec explicitly states that CheckEvent() should 
error out if it sees EVT_NOTIFY_SIGNAL ;).


Alex
Heinrich Schuchardt July 17, 2017, 10:43 a.m. UTC | #4
On 07/16/2017 09:25 AM, Alexander Graf wrote:
> 
> 
> On 15.07.17 13:43, Heinrich Schuchardt wrote:
>> On 07/12/2017 12:55 PM, Alexander Graf wrote:
>>>
>>>
>>> On 05.07.17 19:47, 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>
>>>> ---
>>>>    include/efi_api.h             |   8 ++-
>>>>    include/efi_loader.h          |  23 ++++++
>>>>    lib/efi_loader/efi_boottime.c | 161
>>>> ++++++++++++++++++++++++++----------------
>>>>    3 files changed, 131 insertions(+), 61 deletions(-)
>>>>
>>>> diff --git a/include/efi_api.h b/include/efi_api.h
>>>> index 42cd47ff08..18bef722c6 100644
>>>> --- a/include/efi_api.h
>>>> +++ b/include/efi_api.h
>>>> @@ -28,8 +28,12 @@ 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
>>>>      /* EFI Boot Services table */
>>>>    struct efi_boot_services {
>>>> diff --git a/include/efi_loader.h b/include/efi_loader.h
>>>> index c620652307..a35b971f7e 100644
>>>> --- a/include/efi_loader.h
>>>> +++ b/include/efi_loader.h
>>>> @@ -71,6 +71,29 @@ struct efi_object {
>>>>        void *handle;
>>>>    };
>>>>    +/**
>>>> + * struct efi_event
>>>> + *
>>>> + * @type:        Type of event
>>>> + * @trigger_type:    Type of timer
>>>> + * @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
>>>> + * @signaled:        The notify function was already called
>>>> + */
>>>> +struct efi_event {
>>>> +    u32 type;
>>>> +    unsigned long notify_tpl;
>>>> +    void (EFIAPI *notify_function)(void *event, void *context);
>>>> +    void *notify_context;
>>>> +    u64 trigger_next;
>>>> +    u32 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 3060c25a2a..ef3e7d9d52 100644
>>>> --- a/lib/efi_loader/efi_boottime.c
>>>> +++ b/lib/efi_loader/efi_boottime.c
>>>> @@ -162,21 +162,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 {
>>>> -    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 *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,
>>>> @@ -184,13 +173,10 @@ static efi_status_t EFIAPI efi_create_event(
>>>>                                void *context),
>>>>                void *notify_context, void **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);
>>>>    @@ -201,13 +187,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)
>>>
>>> Please explicitly check for EFI_TIMER_STOP here.
>>
>> TimerCancel (alias EFI_TIMER_STOP) is set in TriggerTimer.
>> If the status of a timer is set to TimerCancel this does not imply that
>> the event can be deleted (i.e. the slot can be reused).
>>
>> The owner of the event might decide to call TriggerTimer for the same
>> event at a later time with TimerPeriodic or TimerRelative.
>>
>> So I do not understand why I should check for EFI_TIMER_STOP here.
> 
> Simply because you're checking for it already, but implicitly:
> 
> enum efi_event_type {
>         EFI_TIMER_STOP = 0,
>         EFI_TIMER_PERIODIC = 1,
>         EFI_TIMER_RELATIVE = 2
> };
> 
> and to avoid confusion, I prefer to call out enums when we match them.
> Otherwise someone will think that this check will include EFI_TIMER_STOP
> one day ;).

We are talking about this structure after applying upconfing v2 of the
patch:

/**
 * struct efi_event
 *
 * @type:               Type of event provided by CreateEvent
 * @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 provided by SetTimer
 * @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;
        u32 trigger_time;
        enum efi_event_type trigger_type;
        int signaled;
};

Field type is a bitmask containing:
EVT_TIMER, EVT_RUNTIME, EVT_NOTIFY_WAIT, EVT_NOTIFY_SIGNAL,
EVT_SIGNAL_EXIT_BOOT_SERVICES, EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE

Type is set in efi_create_event().
Type is zero if the event slot is empty.
Type is non-zero if the event slot is used.

Field trigger_type contains either of
EFI_TIMER_STOP, EFI_TIMER_PERIODIC, or EFI_TIMER_RELATIVE.

Trigger_type is set in efi_set_timer().

> 
>>
>>>
>>>> +            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);
>>>>    }
>>>>      /*
>>>> @@ -216,17 +209,25 @@ 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 & EVT_TIMER) ||
>>>> +            efi_events[i].trigger_type == EFI_TIMER_STOP ||
>>>> +            now < efi_events[i].trigger_next)
>>>> +            continue;
>>>
>>> This if clause is quite hard to read. I don't think it would hurt to
>>> just unfold it into individual cases? You can then also document for
>>> each why they should get ignored.
>>>
>>>> +        if (efi_events[i].trigger_type == EFI_TIMER_PERIODIC)
>>>> +            efi_events[i].trigger_next +=
>>>> +                efi_events[i].trigger_time / 10;
>>>
>>> I stumbled over my own code here - awesome :). Can you please put the
>>> division into a static function that tells the reader from which number
>>> space into which number space we're converting (100ns)?
>>>
>>>> +        efi_events[i].signaled = 1;
>>>> +        if (efi_events[i].type & EVT_NOTIFY_SIGNAL) {
>>>> +            EFI_EXIT(EFI_SUCCESS);
>>>
>>> This is quite dangerous...
>>
>> What do you mean by "quite dangerous"?
>>
>> We have to call the EFI application here.
>> Thus we have to switch the gd context.
> 
> Yes, but switching context is always tricky :).
> 
>>
>>>
>>>> +            efi_events[i].notify_function(&efi_events[i],
>>>> +                    efi_events[i].notify_context);
>>>> +            efi_restore_gd();
>>>
>>> ... I see that you do restore gd but please document this heavily with a
>>> big, obvious comment and ideally even use EFI_ENTRY() again here instead
>>> of hard coding efi_restore_gd().
>>
>> I used efi_restore_gd() to see less messages in debug mode.
>>
>> In the reply to another patch you suggested to provide a configurable
>> verbosity level for EFI_ENTRY. So we can use EFI_ENTRY here.
> 
> That works too, but for clarity it's usually much nicer to have a full
> function start with ENTRY and end with EXIT. That way things become more
> obvious. I've wasted way too many hours on debugging ENTRY/EXIT
> misplacement.
> 
> One thing that would already help readability would be to just move the
> whole exit/entry dance into a separate function. The compiler will
> inline it, so the compiled code should be the same. But at least for the
> reader it's obvious what happens:
> 
> static void efi_call_notify(...)
> {
>     /*
>      * Description about the necessity of this function
>      */
> 
>     EFI_EXIT();
> 
>     notifier(...);
> 
>     /* Go back to U-Boot space */
>     EFI_ENTRY();
> }
>

Will be done in v2 of the patch.

>>
>>>
>>>> +            }
>>>
>>> wrong indentation
>>
>> ok
>>
>>>
>>>>        }
>>>> -
>>>>        WATCHDOG_RESET();
>>>>    }
>>>>    @@ -236,67 +237,109 @@ static efi_status_t EFIAPI efi_set_timer(void
>>>> *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);
>>>>          if (trigger32 < trigger_time) {
>>>> +        trigger32 = 0xffffffff;
>>>
>>> This change should be a separate patch I suppose. Makes bisecting things
>>> easier.
>>
>> ok
>>
>>>
>>>>            printf("WARNING: Truncating timer from %"PRIx64" to %x\n",
>>>>                   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);
>>>> +        switch (type) {
>>>> +        case EFI_TIMER_STOP:
>>>> +            efi_events[i].trigger_next = -1ULL;
>>>> +            break;
>>>> +        case EFI_TIMER_PERIODIC:
>>>> +        case EFI_TIMER_RELATIVE:
>>>> +            efi_events[i].trigger_next =
>>>> +                timer_get_us() + (trigger32 / 10);
>>>> +            break;
>>>> +        default:
>>>> +            return EFI_EXIT(EFI_INVALID_PARAMETER);
>>>> +        }
>>>> +        efi_events[i].trigger_type = type;
>>>> +        efi_events[i].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,
>>>>                              void *event, unsigned long *index)
>>>>    {
>>>> -    u64 now;
>>>> +    int i;
>>>>          EFI_ENTRY("%ld, %p, %p", num_events, event, index);
>>>>    -    now = timer_get_us();
>>>> -    while (now < efi_event.trigger_next) { }
>>>> -    efi_timer_check();
>>>> -
>>>> +    for (i = 0; i < ARRAY_SIZE(efi_events); ++i) {
>>>> +        if (event != &efi_events[i])
>>>> +            continue;
>>>
>>> We also need to check if EVT_NOTIFY_SIGNAL is set and error out in that
>>> case, no?
>>
>> Yes according to the UEFI spec.
>>
>>>
>>>> +        while (!efi_events[i].signaled)
>>>> +            efi_timer_check();
>>>
>>> Ah, nice, so your new code also resets the watchdog. Very good :).
>>>
>>>> +        efi_events[i].signaled = 0;
>>>
>>> Please document that line with reference to the spec.
>>
>> ok
>>
>>>
>>>> +        break;
>>>> +    }
>>>>        return EFI_EXIT(EFI_SUCCESS);
>>>>    }
>>>>      static efi_status_t EFIAPI efi_signal_event(void *event)
>>>>    {
>>>> +    int i;
>>>> +
>>>>        EFI_ENTRY("%p", event);
>>>> +    for (i = 0; i < ARRAY_SIZE(efi_events); ++i) {
>>>> +        if (event != &efi_events[i])
>>>> +            continue;
>>>> +        if (efi_events[i].signaled)
>>>> +            break;
>>>> +        efi_events[i].signaled = 1;
>>>> +        if (efi_events[i].type & EVT_NOTIFY_SIGNAL) {
>>>> +            EFI_EXIT(EFI_SUCCESS);
>>>> +            efi_events[i].notify_function(&efi_events[i],
>>>> +                    efi_events[i].notify_context);
>>>> +            efi_restore_gd();
>>>
>>> This looks like code repetition. Can you fold it in with the same code
>>> above?
>>
>> ok
>>>
>>>> +        }
>>>> +        break;
>>>> +    }
>>>>        return EFI_EXIT(EFI_SUCCESS);
>>>>    }
>>>>      static efi_status_t EFIAPI efi_close_event(void *event)
>>>>    {
>>>> +    int i;
>>>> +
>>>>        EFI_ENTRY("%p", event);
>>>> -    efi_event.trigger_next = -1ULL;
>>>> +    for (i = 0; i < ARRAY_SIZE(efi_events); ++i) {
>>>> +        if (event != &efi_events[i])
>>>> +            continue;
>>>> +        efi_events[i].type = 0;
>>>
>>> Use STOPPED here.
>>
>> This is not the trigger_type field (where we could set EFI_TIMER_STOP).
> 
> Ok, I'm confused. In efi_create_event() you set efi_events[i] to the
> parameter "type" which is enum efi_event_type, no?
> 
> So what are the semantics of the "type" field vs "trigger_type"?
> 
> To clarify that, please make the "type" field its own enum and double
> check that it only gets assigned with values that match that enum.
> 

No type is not an enum but a bitmask. See above.

Regards

Heinrich

>>
>>>
>>>> +        efi_events[i].trigger_next = -1ULL;
>>>> +        efi_events[i].signaled = 0;
>>>> +        break;
>>>> +    }
>>>>        return EFI_EXIT(EFI_SUCCESS);
>>>>    }
>>>>      static efi_status_t EFIAPI efi_check_event(void *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) {
>>>
>>> Here to we need to check for EVT_NOTIFY_SIGNAL and error out, no?
>>
>> No. You may want to check if the event available even if you could wait
>> for it.
>>
>> E.g. the application sets up an event for console input.
>> If there is some other work to do, just poll the event with CheckEvent.
>> If all work is done, use WaitForEvent.
> 
> Well, I agree, but the spec explicitly states that CheckEvent() should
> error out if it sees EVT_NOTIFY_SIGNAL ;).
> 
> 
> Alex
>
diff mbox

Patch

diff --git a/include/efi_api.h b/include/efi_api.h
index 42cd47ff08..18bef722c6 100644
--- a/include/efi_api.h
+++ b/include/efi_api.h
@@ -28,8 +28,12 @@  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
 
 /* EFI Boot Services table */
 struct efi_boot_services {
diff --git a/include/efi_loader.h b/include/efi_loader.h
index c620652307..a35b971f7e 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -71,6 +71,29 @@  struct efi_object {
 	void *handle;
 };
 
+/**
+ * struct efi_event
+ *
+ * @type:		Type of event
+ * @trigger_type:	Type of timer
+ * @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
+ * @signaled:		The notify function was already called
+ */
+struct efi_event {
+	u32 type;
+	unsigned long notify_tpl;
+	void (EFIAPI *notify_function)(void *event, void *context);
+	void *notify_context;
+	u64 trigger_next;
+	u32 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 3060c25a2a..ef3e7d9d52 100644
--- a/lib/efi_loader/efi_boottime.c
+++ b/lib/efi_loader/efi_boottime.c
@@ -162,21 +162,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 {
-	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 *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,
@@ -184,13 +173,10 @@  static efi_status_t EFIAPI efi_create_event(
 							void *context),
 			void *notify_context, void **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);
 
@@ -201,13 +187,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);
 }
 
 /*
@@ -216,17 +209,25 @@  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 & 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 = 1;
+		if (efi_events[i].type & EVT_NOTIFY_SIGNAL) {
+			EFI_EXIT(EFI_SUCCESS);
+			efi_events[i].notify_function(&efi_events[i],
+					efi_events[i].notify_context);
+			efi_restore_gd();
+			}
 	}
-
 	WATCHDOG_RESET();
 }
 
@@ -236,67 +237,109 @@  static efi_status_t EFIAPI efi_set_timer(void *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);
 
 	if (trigger32 < trigger_time) {
+		trigger32 = 0xffffffff;
 		printf("WARNING: Truncating timer from %"PRIx64" to %x\n",
 		       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);
+		switch (type) {
+		case EFI_TIMER_STOP:
+			efi_events[i].trigger_next = -1ULL;
+			break;
+		case EFI_TIMER_PERIODIC:
+		case EFI_TIMER_RELATIVE:
+			efi_events[i].trigger_next =
+				timer_get_us() + (trigger32 / 10);
+			break;
+		default:
+			return EFI_EXIT(EFI_INVALID_PARAMETER);
+		}
+		efi_events[i].trigger_type = type;
+		efi_events[i].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,
 					      void *event, unsigned long *index)
 {
-	u64 now;
+	int i;
 
 	EFI_ENTRY("%ld, %p, %p", num_events, event, index);
 
-	now = timer_get_us();
-	while (now < efi_event.trigger_next) { }
-	efi_timer_check();
-
+	for (i = 0; i < ARRAY_SIZE(efi_events); ++i) {
+		if (event != &efi_events[i])
+			continue;
+		while (!efi_events[i].signaled)
+			efi_timer_check();
+		efi_events[i].signaled = 0;
+		break;
+	}
 	return EFI_EXIT(EFI_SUCCESS);
 }
 
 static efi_status_t EFIAPI efi_signal_event(void *event)
 {
+	int i;
+
 	EFI_ENTRY("%p", event);
+	for (i = 0; i < ARRAY_SIZE(efi_events); ++i) {
+		if (event != &efi_events[i])
+			continue;
+		if (efi_events[i].signaled)
+			break;
+		efi_events[i].signaled = 1;
+		if (efi_events[i].type & EVT_NOTIFY_SIGNAL) {
+			EFI_EXIT(EFI_SUCCESS);
+			efi_events[i].notify_function(&efi_events[i],
+					efi_events[i].notify_context);
+			efi_restore_gd();
+		}
+		break;
+	}
 	return EFI_EXIT(EFI_SUCCESS);
 }
 
 static efi_status_t EFIAPI efi_close_event(void *event)
 {
+	int i;
+
 	EFI_ENTRY("%p", event);
-	efi_event.trigger_next = -1ULL;
+	for (i = 0; i < ARRAY_SIZE(efi_events); ++i) {
+		if (event != &efi_events[i])
+			continue;
+		efi_events[i].type = 0;
+		efi_events[i].trigger_next = -1ULL;
+		efi_events[i].signaled = 0;
+		break;
+	}
 	return EFI_EXIT(EFI_SUCCESS);
 }
 
 static efi_status_t EFIAPI efi_check_event(void *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 (efi_events[i].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,