提交 43bce442 编写于 作者: H Heinrich Schuchardt 提交者: Alexander Graf

efi_loader: manage events in a linked list

Lift the limit on the number of events by using a linked list.

This also allows to have events with type == 0.

This patch is based on Rob's patch
efi_loader: fix events
https://lists.denx.de/pipermail/u-boot/2017-October/309348.htmlSuggested-by: NRob Clark <robdclark@gmail.com>
Signed-off-by: NHeinrich Schuchardt <xypron.glpk@gmx.de>
Signed-off-by: NAlexander Graf <agraf@suse.de>
上级 ab9efa97
...@@ -150,17 +150,19 @@ struct efi_object { ...@@ -150,17 +150,19 @@ struct efi_object {
/** /**
* struct efi_event * struct efi_event
* *
* @link: Link to list of all events
* @type: Type of event, see efi_create_event * @type: Type of event, see efi_create_event
* @notify_tpl: Task priority level of notifications * @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 * @nofify_function: Function to call when the event is triggered
* @notify_context: Data to be passed to the notify function * @notify_context: Data to be passed to the notify function
* @trigger_time: Period of the timer
* @trigger_next: Next time to trigger the timer
* @trigger_type: Type of timer, see efi_set_timer * @trigger_type: Type of timer, see efi_set_timer
* @queued: The notification function is queued * @is_queued: The notification function is queued
* @signaled: The event occurred. The event is in the signaled state. * @is_signaled: The event occurred. The event is in the signaled state.
*/ */
struct efi_event { struct efi_event {
struct list_head link;
uint32_t type; uint32_t type;
efi_uintn_t notify_tpl; efi_uintn_t notify_tpl;
void (EFIAPI *notify_function)(struct efi_event *event, void *context); void (EFIAPI *notify_function)(struct efi_event *event, void *context);
...@@ -172,7 +174,6 @@ struct efi_event { ...@@ -172,7 +174,6 @@ struct efi_event {
bool is_signaled; bool is_signaled;
}; };
/* This list contains all UEFI objects we know of */ /* This list contains all UEFI objects we know of */
extern struct list_head efi_obj_list; extern struct list_head efi_obj_list;
......
...@@ -26,6 +26,9 @@ static efi_uintn_t efi_tpl = TPL_APPLICATION; ...@@ -26,6 +26,9 @@ static efi_uintn_t efi_tpl = TPL_APPLICATION;
/* This list contains all the EFI objects our payload has access to */ /* This list contains all the EFI objects our payload has access to */
LIST_HEAD(efi_obj_list); LIST_HEAD(efi_obj_list);
/* List of all events */
static LIST_HEAD(efi_events);
/* /*
* If we're running on nasty systems (32bit ARM booting into non-EFI Linux) * If we're running on nasty systems (32bit ARM booting into non-EFI Linux)
* we need to do trickery with caches. Since we don't want to break the EFI * we need to do trickery with caches. Since we don't want to break the EFI
...@@ -473,10 +476,23 @@ void efi_delete_handle(struct efi_object *obj) ...@@ -473,10 +476,23 @@ void efi_delete_handle(struct efi_object *obj)
} }
/* /*
* Our event capabilities are very limited. Only a small limited * Check if a pointer is a valid event.
* number of events is allowed to coexist. *
* @event pointer to check
* @return status code
*/ */
static struct efi_event efi_events[16]; static efi_status_t efi_is_event(const struct efi_event *event)
{
const struct efi_event *evt;
if (!event)
return EFI_INVALID_PARAMETER;
list_for_each_entry(evt, &efi_events, link) {
if (evt == event)
return EFI_SUCCESS;
}
return EFI_INVALID_PARAMETER;
}
/* /*
* Create an event. * Create an event.
...@@ -499,7 +515,7 @@ efi_status_t efi_create_event(uint32_t type, efi_uintn_t notify_tpl, ...@@ -499,7 +515,7 @@ efi_status_t efi_create_event(uint32_t type, efi_uintn_t notify_tpl,
void *context), void *context),
void *notify_context, struct efi_event **event) void *notify_context, struct efi_event **event)
{ {
int i; struct efi_event *evt;
if (event == NULL) if (event == NULL)
return EFI_INVALID_PARAMETER; return EFI_INVALID_PARAMETER;
...@@ -507,25 +523,24 @@ efi_status_t efi_create_event(uint32_t type, efi_uintn_t notify_tpl, ...@@ -507,25 +523,24 @@ efi_status_t efi_create_event(uint32_t type, efi_uintn_t notify_tpl,
if ((type & EVT_NOTIFY_SIGNAL) && (type & EVT_NOTIFY_WAIT)) if ((type & EVT_NOTIFY_SIGNAL) && (type & EVT_NOTIFY_WAIT))
return EFI_INVALID_PARAMETER; return EFI_INVALID_PARAMETER;
if ((type & (EVT_NOTIFY_SIGNAL|EVT_NOTIFY_WAIT)) && if ((type & (EVT_NOTIFY_SIGNAL | EVT_NOTIFY_WAIT)) &&
notify_function == NULL) notify_function == NULL)
return EFI_INVALID_PARAMETER; return EFI_INVALID_PARAMETER;
for (i = 0; i < ARRAY_SIZE(efi_events); ++i) { evt = calloc(1, sizeof(struct efi_event));
if (efi_events[i].type) if (!evt)
continue; return EFI_OUT_OF_RESOURCES;
efi_events[i].type = type; evt->type = type;
efi_events[i].notify_tpl = notify_tpl; evt->notify_tpl = notify_tpl;
efi_events[i].notify_function = notify_function; evt->notify_function = notify_function;
efi_events[i].notify_context = notify_context; evt->notify_context = notify_context;
/* Disable timers on bootup */ /* Disable timers on bootup */
efi_events[i].trigger_next = -1ULL; evt->trigger_next = -1ULL;
efi_events[i].is_queued = false; evt->is_queued = false;
efi_events[i].is_signaled = false; evt->is_signaled = false;
*event = &efi_events[i]; list_add_tail(&evt->link, &efi_events);
return EFI_SUCCESS; *event = evt;
} return EFI_SUCCESS;
return EFI_OUT_OF_RESOURCES;
} }
/* /*
...@@ -596,30 +611,26 @@ static efi_status_t EFIAPI efi_create_event_ext( ...@@ -596,30 +611,26 @@ static efi_status_t EFIAPI efi_create_event_ext(
*/ */
void efi_timer_check(void) void efi_timer_check(void)
{ {
int i; struct efi_event *evt;
u64 now = timer_get_us(); u64 now = timer_get_us();
for (i = 0; i < ARRAY_SIZE(efi_events); ++i) { list_for_each_entry(evt, &efi_events, link) {
if (!efi_events[i].type) if (evt->is_queued)
efi_signal_event(evt, true);
if (!(evt->type & EVT_TIMER) || now < evt->trigger_next)
continue; continue;
if (efi_events[i].is_queued) switch (evt->trigger_type) {
efi_signal_event(&efi_events[i], true);
if (!(efi_events[i].type & EVT_TIMER) ||
now < efi_events[i].trigger_next)
continue;
switch (efi_events[i].trigger_type) {
case EFI_TIMER_RELATIVE: case EFI_TIMER_RELATIVE:
efi_events[i].trigger_type = EFI_TIMER_STOP; evt->trigger_type = EFI_TIMER_STOP;
break; break;
case EFI_TIMER_PERIODIC: case EFI_TIMER_PERIODIC:
efi_events[i].trigger_next += evt->trigger_next += evt->trigger_time;
efi_events[i].trigger_time;
break; break;
default: default:
continue; continue;
} }
efi_events[i].is_signaled = true; evt->is_signaled = true;
efi_signal_event(&efi_events[i], true); efi_signal_event(evt, true);
} }
WATCHDOG_RESET(); WATCHDOG_RESET();
} }
...@@ -638,7 +649,9 @@ void efi_timer_check(void) ...@@ -638,7 +649,9 @@ void efi_timer_check(void)
efi_status_t efi_set_timer(struct efi_event *event, enum efi_timer_delay type, efi_status_t efi_set_timer(struct efi_event *event, enum efi_timer_delay type,
uint64_t trigger_time) uint64_t trigger_time)
{ {
int i; /* Check that the event is valid */
if (efi_is_event(event) != EFI_SUCCESS || !(event->type & EVT_TIMER))
return EFI_INVALID_PARAMETER;
/* /*
* The parameter defines a multiple of 100ns. * The parameter defines a multiple of 100ns.
...@@ -646,30 +659,21 @@ efi_status_t efi_set_timer(struct efi_event *event, enum efi_timer_delay type, ...@@ -646,30 +659,21 @@ efi_status_t efi_set_timer(struct efi_event *event, enum efi_timer_delay type,
*/ */
do_div(trigger_time, 10); do_div(trigger_time, 10);
for (i = 0; i < ARRAY_SIZE(efi_events); ++i) { switch (type) {
if (event != &efi_events[i]) case EFI_TIMER_STOP:
continue; event->trigger_next = -1ULL;
break;
if (!(event->type & EVT_TIMER)) case EFI_TIMER_PERIODIC:
break; case EFI_TIMER_RELATIVE:
switch (type) { event->trigger_next = timer_get_us() + trigger_time;
case EFI_TIMER_STOP: break;
event->trigger_next = -1ULL; default:
break; return EFI_INVALID_PARAMETER;
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;
} }
return EFI_INVALID_PARAMETER; event->trigger_type = type;
event->trigger_time = trigger_time;
event->is_signaled = false;
return EFI_SUCCESS;
} }
/* /*
...@@ -708,7 +712,7 @@ static efi_status_t EFIAPI efi_wait_for_event(efi_uintn_t num_events, ...@@ -708,7 +712,7 @@ static efi_status_t EFIAPI efi_wait_for_event(efi_uintn_t num_events,
struct efi_event **event, struct efi_event **event,
efi_uintn_t *index) efi_uintn_t *index)
{ {
int i, j; int i;
EFI_ENTRY("%zd, %p, %p", num_events, event, index); EFI_ENTRY("%zd, %p, %p", num_events, event, index);
...@@ -719,12 +723,8 @@ static efi_status_t EFIAPI efi_wait_for_event(efi_uintn_t num_events, ...@@ -719,12 +723,8 @@ static efi_status_t EFIAPI efi_wait_for_event(efi_uintn_t num_events,
if (efi_tpl != TPL_APPLICATION) if (efi_tpl != TPL_APPLICATION)
return EFI_EXIT(EFI_UNSUPPORTED); return EFI_EXIT(EFI_UNSUPPORTED);
for (i = 0; i < num_events; ++i) { for (i = 0; i < num_events; ++i) {
for (j = 0; j < ARRAY_SIZE(efi_events); ++j) { if (efi_is_event(event[i]) != EFI_SUCCESS)
if (event[i] == &efi_events[j]) return EFI_EXIT(EFI_INVALID_PARAMETER);
goto known_event;
}
return EFI_EXIT(EFI_INVALID_PARAMETER);
known_event:
if (!event[i]->type || event[i]->type & EVT_NOTIFY_SIGNAL) if (!event[i]->type || event[i]->type & EVT_NOTIFY_SIGNAL)
return EFI_EXIT(EFI_INVALID_PARAMETER); return EFI_EXIT(EFI_INVALID_PARAMETER);
if (!event[i]->is_signaled) if (!event[i]->is_signaled)
...@@ -768,18 +768,13 @@ out: ...@@ -768,18 +768,13 @@ out:
*/ */
static efi_status_t EFIAPI efi_signal_event_ext(struct efi_event *event) static efi_status_t EFIAPI efi_signal_event_ext(struct efi_event *event)
{ {
int i;
EFI_ENTRY("%p", event); EFI_ENTRY("%p", event);
for (i = 0; i < ARRAY_SIZE(efi_events); ++i) { if (efi_is_event(event) != EFI_SUCCESS)
if (event != &efi_events[i]) return EFI_EXIT(EFI_INVALID_PARAMETER);
continue; if (!event->is_signaled) {
if (event->is_signaled)
break;
event->is_signaled = true; event->is_signaled = true;
if (event->type & EVT_NOTIFY_SIGNAL) if (event->type & EVT_NOTIFY_SIGNAL)
efi_signal_event(event, true); efi_signal_event(event, true);
break;
} }
return EFI_EXIT(EFI_SUCCESS); return EFI_EXIT(EFI_SUCCESS);
} }
...@@ -796,19 +791,12 @@ static efi_status_t EFIAPI efi_signal_event_ext(struct efi_event *event) ...@@ -796,19 +791,12 @@ static efi_status_t EFIAPI efi_signal_event_ext(struct efi_event *event)
*/ */
static efi_status_t EFIAPI efi_close_event(struct efi_event *event) static efi_status_t EFIAPI efi_close_event(struct efi_event *event)
{ {
int i;
EFI_ENTRY("%p", event); EFI_ENTRY("%p", event);
for (i = 0; i < ARRAY_SIZE(efi_events); ++i) { if (efi_is_event(event) != EFI_SUCCESS)
if (event == &efi_events[i]) { return EFI_EXIT(EFI_INVALID_PARAMETER);
event->type = 0; list_del(&event->link);
event->trigger_next = -1ULL; free(event);
event->is_queued = false; return EFI_EXIT(EFI_SUCCESS);
event->is_signaled = false;
return EFI_EXIT(EFI_SUCCESS);
}
}
return EFI_EXIT(EFI_INVALID_PARAMETER);
} }
/* /*
...@@ -826,24 +814,18 @@ static efi_status_t EFIAPI efi_close_event(struct efi_event *event) ...@@ -826,24 +814,18 @@ static efi_status_t EFIAPI efi_close_event(struct efi_event *event)
*/ */
static efi_status_t EFIAPI efi_check_event(struct efi_event *event) static efi_status_t EFIAPI efi_check_event(struct efi_event *event)
{ {
int i;
EFI_ENTRY("%p", event); EFI_ENTRY("%p", event);
efi_timer_check(); efi_timer_check();
for (i = 0; i < ARRAY_SIZE(efi_events); ++i) { if (efi_is_event(event) != EFI_SUCCESS ||
if (event != &efi_events[i]) event->type & EVT_NOTIFY_SIGNAL)
continue; return EFI_EXIT(EFI_INVALID_PARAMETER);
if (!event->type || event->type & EVT_NOTIFY_SIGNAL) if (!event->is_signaled)
break; efi_signal_event(event, true);
if (!event->is_signaled) if (event->is_signaled) {
efi_signal_event(event, true); event->is_signaled = false;
if (event->is_signaled) { return EFI_EXIT(EFI_SUCCESS);
event->is_signaled = false;
return EFI_EXIT(EFI_SUCCESS);
}
return EFI_EXIT(EFI_NOT_READY);
} }
return EFI_EXIT(EFI_INVALID_PARAMETER); return EFI_EXIT(EFI_NOT_READY);
} }
/* /*
...@@ -1755,7 +1737,7 @@ static void efi_exit_caches(void) ...@@ -1755,7 +1737,7 @@ static void efi_exit_caches(void)
static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle,
unsigned long map_key) unsigned long map_key)
{ {
int i; struct efi_event *evt;
EFI_ENTRY("%p, %ld", image_handle, map_key); EFI_ENTRY("%p, %ld", image_handle, map_key);
...@@ -1767,11 +1749,11 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, ...@@ -1767,11 +1749,11 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle,
return EFI_EXIT(EFI_SUCCESS); return EFI_EXIT(EFI_SUCCESS);
/* Notify that ExitBootServices is invoked. */ /* Notify that ExitBootServices is invoked. */
for (i = 0; i < ARRAY_SIZE(efi_events); ++i) { list_for_each_entry(evt, &efi_events, link) {
if (efi_events[i].type != EVT_SIGNAL_EXIT_BOOT_SERVICES) if (evt->type != EVT_SIGNAL_EXIT_BOOT_SERVICES)
continue; continue;
efi_events[i].is_signaled = true; evt->is_signaled = true;
efi_signal_event(&efi_events[i], false); efi_signal_event(evt, false);
} }
/* TODO Should persist EFI variables here */ /* TODO Should persist EFI variables here */
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册