summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorTom Rini <trini@konsulko.com>2018-04-09 11:05:44 -0400
committerTom Rini <trini@konsulko.com>2018-04-09 11:05:44 -0400
commit844fb498cc978608ec88bdf29913c0d46c85bfff (patch)
tree2baa468bf3b9148a0a6c76b9bc7e1b8333b02f72 /lib
parent3404a0b38291a11a70064c4218066e9d7eb56ca9 (diff)
parentf4cf153a487486428a061b5d866fe2f68653b2f8 (diff)
Merge tag 'signed-efi-next' of git://github.com/agraf/u-boot
Patch queue for efi - 2018-04-09 Highlights this time around: - Lots of minor spec compliance fixes - Support full range of GOP BLT commands - More fine grained error checking - Network fixes (init, DP) - Lots of other bug fixes...
Diffstat (limited to 'lib')
-rw-r--r--lib/efi_loader/Makefile3
-rw-r--r--lib/efi_loader/efi_boottime.c388
-rw-r--r--lib/efi_loader/efi_console.c156
-rw-r--r--lib/efi_loader/efi_device_path.c8
-rw-r--r--lib/efi_loader/efi_device_path_utilities.c89
-rw-r--r--lib/efi_loader/efi_file.c85
-rw-r--r--lib/efi_loader/efi_gop.c362
-rw-r--r--lib/efi_loader/efi_image_loader.c101
-rw-r--r--lib/efi_loader/efi_memory.c11
-rw-r--r--lib/efi_loader/efi_net.c68
-rw-r--r--lib/efi_loader/efi_runtime.c32
-rw-r--r--lib/efi_loader/efi_smbios.c23
-rw-r--r--lib/efi_loader/efi_watchdog.c6
-rw-r--r--lib/efi_loader/helloworld.c18
-rw-r--r--lib/efi_selftest/Makefile4
-rw-r--r--lib/efi_selftest/efi_selftest_bitblt.c311
-rw-r--r--lib/efi_selftest/efi_selftest_block_device.c24
-rw-r--r--lib/efi_selftest/efi_selftest_disk_image.h58
-rw-r--r--lib/efi_selftest/efi_selftest_event_groups.c140
-rw-r--r--lib/efi_selftest/efi_selftest_fdt.c188
-rw-r--r--lib/efi_selftest/efi_selftest_textinput.c182
21 files changed, 1899 insertions, 358 deletions
diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile
index 2a87d9ed77..d2ce89713e 100644
--- a/lib/efi_loader/Makefile
+++ b/lib/efi_loader/Makefile
@@ -17,7 +17,8 @@ endif
obj-$(CONFIG_CMD_BOOTEFI_HELLO) += helloworld_efi.o
obj-y += efi_image_loader.o efi_boottime.o efi_runtime.o efi_console.o
obj-y += efi_memory.o efi_device_path_to_text.o efi_device_path.o
-obj-y += efi_file.o efi_variable.o efi_bootmgr.o efi_watchdog.o
+obj-y += efi_device_path_utilities.o efi_file.o efi_variable.o efi_bootmgr.o
+obj-y += efi_watchdog.o
obj-$(CONFIG_LCD) += efi_gop.o
obj-$(CONFIG_DM_VIDEO) += efi_gop.o
obj-$(CONFIG_PARTITIONS) += efi_disk.o
diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
index 66e26fd63a..7a9449f59c 100644
--- a/lib/efi_loader/efi_boottime.c
+++ b/lib/efi_loader/efi_boottime.c
@@ -26,6 +26,9 @@ static efi_uintn_t efi_tpl = TPL_APPLICATION;
/* This list contains all the EFI objects our payload has access to */
LIST_HEAD(efi_obj_list);
+/* List of all events */
+LIST_HEAD(efi_events);
+
/*
* 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
@@ -56,10 +59,28 @@ static volatile void *efi_gd, *app_gd;
static int entry_count;
static int nesting_level;
+/* GUID of the device tree table */
+const efi_guid_t efi_guid_fdt = EFI_FDT_GUID;
/* GUID of the EFI_DRIVER_BINDING_PROTOCOL */
const efi_guid_t efi_guid_driver_binding_protocol =
EFI_DRIVER_BINDING_PROTOCOL_GUID;
+/* event group ExitBootServices() invoked */
+const efi_guid_t efi_guid_event_group_exit_boot_services =
+ EFI_EVENT_GROUP_EXIT_BOOT_SERVICES;
+/* event group SetVirtualAddressMap() invoked */
+const efi_guid_t efi_guid_event_group_virtual_address_change =
+ EFI_EVENT_GROUP_VIRTUAL_ADDRESS_CHANGE;
+/* event group memory map changed */
+const efi_guid_t efi_guid_event_group_memory_map_change =
+ EFI_EVENT_GROUP_MEMORY_MAP_CHANGE;
+/* event group boot manager about to boot */
+const efi_guid_t efi_guid_event_group_ready_to_boot =
+ EFI_EVENT_GROUP_READY_TO_BOOT;
+/* event group ResetSystem() invoked (before ExitBootServices) */
+const efi_guid_t efi_guid_event_group_reset_system =
+ EFI_EVENT_GROUP_RESET_SYSTEM;
+
static efi_status_t EFIAPI efi_disconnect_controller(
efi_handle_t controller_handle,
efi_handle_t driver_image_handle,
@@ -121,6 +142,7 @@ static const char *indent_string(int level)
{
const char *indent = " ";
const int max = strlen(indent);
+
level = min(max, level * 2);
return &indent[max - level];
}
@@ -154,7 +176,7 @@ const char *__efi_nesting_dec(void)
* @event event to signal
* @check_tpl check the TPL level
*/
-void efi_signal_event(struct efi_event *event, bool check_tpl)
+static void efi_queue_event(struct efi_event *event, bool check_tpl)
{
if (event->notify_function) {
event->is_queued = true;
@@ -168,6 +190,50 @@ void efi_signal_event(struct efi_event *event, bool check_tpl)
}
/*
+ * Signal an EFI event.
+ *
+ * This function signals an event. If the event belongs to an event group
+ * all events of the group are signaled. If they are of type EVT_NOTIFY_SIGNAL
+ * their notification function is queued.
+ *
+ * For the SignalEvent service see efi_signal_event_ext.
+ *
+ * @event event to signal
+ * @check_tpl check the TPL level
+ */
+void efi_signal_event(struct efi_event *event, bool check_tpl)
+{
+ if (event->group) {
+ struct efi_event *evt;
+
+ /*
+ * The signaled state has to set before executing any
+ * notification function
+ */
+ list_for_each_entry(evt, &efi_events, link) {
+ if (!evt->group || guidcmp(evt->group, event->group))
+ continue;
+ if (evt->is_signaled)
+ continue;
+ evt->is_signaled = true;
+ if (evt->type & EVT_NOTIFY_SIGNAL &&
+ evt->notify_function)
+ evt->is_queued = true;
+ }
+ list_for_each_entry(evt, &efi_events, link) {
+ if (!evt->group || guidcmp(evt->group, event->group))
+ continue;
+ if (evt->is_queued)
+ efi_queue_event(evt, check_tpl);
+ }
+ } else if (!event->is_signaled) {
+ event->is_signaled = true;
+ if (event->type & EVT_NOTIFY_SIGNAL)
+ efi_queue_event(event, check_tpl);
+ }
+}
+
+/*
* Raise the task priority level.
*
* This function implements the RaiseTpl service.
@@ -212,6 +278,11 @@ static void EFIAPI efi_restore_tpl(efi_uintn_t old_tpl)
if (efi_tpl > TPL_HIGH_LEVEL)
efi_tpl = TPL_HIGH_LEVEL;
+ /*
+ * Lowering the TPL may have made queued events eligible for execution.
+ */
+ efi_timer_check();
+
EFI_EXIT(EFI_SUCCESS);
}
@@ -255,7 +326,7 @@ static efi_status_t EFIAPI efi_free_pages_ext(uint64_t memory,
{
efi_status_t r;
- EFI_ENTRY("%"PRIx64", 0x%zx", memory, pages);
+ EFI_ENTRY("%" PRIx64 ", 0x%zx", memory, pages);
r = efi_free_pages(memory, pages);
return EFI_EXIT(r);
}
@@ -470,10 +541,23 @@ void efi_delete_handle(struct efi_object *obj)
}
/*
- * 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.
+ *
+ * @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.
@@ -494,9 +578,10 @@ efi_status_t efi_create_event(uint32_t type, efi_uintn_t notify_tpl,
void (EFIAPI *notify_function) (
struct efi_event *event,
void *context),
- void *notify_context, struct efi_event **event)
+ void *notify_context, efi_guid_t *group,
+ struct efi_event **event)
{
- int i;
+ struct efi_event *evt;
if (event == NULL)
return EFI_INVALID_PARAMETER;
@@ -504,25 +589,25 @@ efi_status_t efi_create_event(uint32_t type, efi_uintn_t notify_tpl,
if ((type & EVT_NOTIFY_SIGNAL) && (type & EVT_NOTIFY_WAIT))
return EFI_INVALID_PARAMETER;
- if ((type & (EVT_NOTIFY_SIGNAL|EVT_NOTIFY_WAIT)) &&
+ if ((type & (EVT_NOTIFY_SIGNAL | EVT_NOTIFY_WAIT)) &&
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(struct efi_event));
+ 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;
+ evt->group = group;
+ /* 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;
}
/*
@@ -551,10 +636,8 @@ efi_status_t EFIAPI efi_create_event_ex(uint32_t type, efi_uintn_t notify_tpl,
{
EFI_ENTRY("%d, 0x%zx, %p, %p, %pUl", type, notify_tpl, notify_function,
notify_context, event_group);
- if (event_group)
- return EFI_EXIT(EFI_UNSUPPORTED);
return EFI_EXIT(efi_create_event(type, notify_tpl, notify_function,
- notify_context, event));
+ notify_context, event_group, event));
}
/*
@@ -581,10 +664,9 @@ static efi_status_t EFIAPI efi_create_event_ext(
EFI_ENTRY("%d, 0x%zx, %p, %p", type, notify_tpl, notify_function,
notify_context);
return EFI_EXIT(efi_create_event(type, notify_tpl, notify_function,
- notify_context, event));
+ notify_context, NULL, event));
}
-
/*
* Check if a timer event has occurred or a queued notification function should
* be called.
@@ -594,30 +676,26 @@ 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], true);
- if (!(efi_events[i].type & EVT_TIMER) ||
- now < efi_events[i].trigger_next)
+ list_for_each_entry(evt, &efi_events, link) {
+ if (evt->is_queued)
+ efi_queue_event(evt, true);
+ 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], true);
+ evt->is_signaled = false;
+ efi_signal_event(evt, true);
}
WATCHDOG_RESET();
}
@@ -636,7 +714,9 @@ 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;
+ /* 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.
@@ -644,30 +724,21 @@ 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))
- 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;
}
/*
@@ -686,7 +757,7 @@ static efi_status_t EFIAPI efi_set_timer_ext(struct efi_event *event,
enum efi_timer_delay type,
uint64_t trigger_time)
{
- EFI_ENTRY("%p, %d, %"PRIx64, event, type, trigger_time);
+ EFI_ENTRY("%p, %d, %" PRIx64, event, type, trigger_time);
return EFI_EXIT(efi_set_timer(event, type, trigger_time));
}
@@ -706,7 +777,7 @@ static efi_status_t EFIAPI efi_wait_for_event(efi_uintn_t num_events,
struct efi_event **event,
efi_uintn_t *index)
{
- int i, j;
+ int i;
EFI_ENTRY("%zd, %p, %p", num_events, event, index);
@@ -717,16 +788,12 @@ static efi_status_t EFIAPI efi_wait_for_event(efi_uintn_t 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]) != EFI_SUCCESS)
+ 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)
- efi_signal_event(event[i], true);
+ efi_queue_event(event[i], true);
}
/* Wait for signal */
@@ -766,19 +833,10 @@ 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, true);
- break;
- }
+ if (efi_is_event(event) != EFI_SUCCESS)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+ efi_signal_event(event, true);
return EFI_EXIT(EFI_SUCCESS);
}
@@ -794,19 +852,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)
{
- 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);
+ if (efi_is_event(event) != EFI_SUCCESS)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+ list_del(&event->link);
+ free(event);
+ return EFI_EXIT(EFI_SUCCESS);
}
/*
@@ -816,29 +867,26 @@ 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 an event is not signaled yet, the notification function is queued.
+ * The signaled state is cleared.
*
* @event event to check
* @return status code
*/
static efi_status_t EFIAPI efi_check_event(struct efi_event *event)
{
- int i;
-
EFI_ENTRY("%p", event);
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, true);
- if (event->is_signaled)
- return EFI_EXIT(EFI_SUCCESS);
- return EFI_EXIT(EFI_NOT_READY);
+ if (efi_is_event(event) != EFI_SUCCESS ||
+ event->type & EVT_NOTIFY_SIGNAL)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+ if (!event->is_signaled)
+ efi_queue_event(event, true);
+ if (event->is_signaled) {
+ event->is_signaled = false;
+ return EFI_EXIT(EFI_SUCCESS);
}
- return EFI_EXIT(EFI_INVALID_PARAMETER);
+ return EFI_EXIT(EFI_NOT_READY);
}
/*
@@ -1259,7 +1307,7 @@ static efi_status_t efi_locate_handle(
/* Count how much space we need */
list_for_each_entry(efiobj, &efi_obj_list, link) {
if (!efi_search(search_type, protocol, search_key, efiobj))
- size += sizeof(void*);
+ size += sizeof(void *);
}
if (*buffer_size < size) {
@@ -1310,7 +1358,7 @@ static efi_status_t EFIAPI efi_locate_handle_ext(
static void efi_remove_configuration_table(int i)
{
struct efi_configuration_table *this = &efi_conf_table[i];
- struct efi_configuration_table *next = &efi_conf_table[i+1];
+ struct efi_configuration_table *next = &efi_conf_table[i + 1];
struct efi_configuration_table *end = &efi_conf_table[systab.nr_tables];
memmove(this, next, (ulong)end - (ulong)next);
@@ -1327,10 +1375,15 @@ static void efi_remove_configuration_table(int i)
* @table table to be installed
* @return status code
*/
-efi_status_t efi_install_configuration_table(const efi_guid_t *guid, void *table)
+efi_status_t efi_install_configuration_table(const efi_guid_t *guid,
+ void *table)
{
+ struct efi_event *evt;
int i;
+ if (!guid)
+ return EFI_INVALID_PARAMETER;
+
/* Check for guid override */
for (i = 0; i < systab.nr_tables; i++) {
if (!guidcmp(guid, &efi_conf_table[i].guid)) {
@@ -1338,7 +1391,7 @@ efi_status_t efi_install_configuration_table(const efi_guid_t *guid, void *table
efi_conf_table[i].table = table;
else
efi_remove_configuration_table(i);
- return EFI_SUCCESS;
+ goto out;
}
}
@@ -1354,6 +1407,15 @@ efi_status_t efi_install_configuration_table(const efi_guid_t *guid, void *table
efi_conf_table[i].table = table;
systab.nr_tables = i + 1;
+out:
+ /* Notify that the configuration table was changed */
+ list_for_each_entry(evt, &efi_events, link) {
+ if (evt->group && !guidcmp(evt->group, guid)) {
+ efi_signal_event(evt, false);
+ break;
+ }
+ }
+
return EFI_SUCCESS;
}
@@ -1420,14 +1482,15 @@ efi_status_t efi_setup_loaded_image(
if (ret != EFI_SUCCESS)
goto failure;
- ret = efi_add_protocol(obj->handle, &efi_guid_console_control,
- (void *)&efi_console_control);
+ ret = efi_add_protocol(obj->handle,
+ &efi_guid_device_path_to_text_protocol,
+ (void *)&efi_device_path_to_text);
if (ret != EFI_SUCCESS)
goto failure;
ret = efi_add_protocol(obj->handle,
- &efi_guid_device_path_to_text_protocol,
- (void *)&efi_device_path_to_text);
+ &efi_guid_device_path_utilities_protocol,
+ (void *)&efi_device_path_utilities);
if (ret != EFI_SUCCESS)
goto failure;
@@ -1450,7 +1513,7 @@ efi_status_t efi_load_image_from_path(struct efi_device_path *file_path,
struct efi_file_info *info = NULL;
struct efi_file_handle *f;
static efi_status_t ret;
- uint64_t bs;
+ efi_uintn_t bs;
f = efi_file_from_path(file_path);
if (!f)
@@ -1471,7 +1534,8 @@ efi_status_t efi_load_image_from_path(struct efi_device_path *file_path,
if (ret)
goto error;
- EFI_CALL(ret = f->read(f, &info->file_size, *buffer));
+ bs = info->file_size;
+ EFI_CALL(ret = f->read(f, &bs, *buffer));
error:
free(info);
@@ -1505,18 +1569,37 @@ static efi_status_t EFIAPI efi_load_image(bool boot_policy,
efi_handle_t parent_image,
struct efi_device_path *file_path,
void *source_buffer,
- unsigned long source_size,
+ efi_uintn_t source_size,
efi_handle_t *image_handle)
{
struct efi_loaded_image *info;
struct efi_object *obj;
efi_status_t ret;
- EFI_ENTRY("%d, %p, %pD, %p, %ld, %p", boot_policy, parent_image,
+ EFI_ENTRY("%d, %p, %pD, %p, %zd, %p", boot_policy, parent_image,
file_path, source_buffer, source_size, image_handle);
+ if (!image_handle || !parent_image) {
+ ret = EFI_INVALID_PARAMETER;
+ goto error;
+ }
+
+ if (!source_buffer && !file_path) {
+ ret = EFI_NOT_FOUND;
+ goto error;
+ }
+
info = calloc(1, sizeof(*info));
+ if (!info) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto error;
+ }
obj = calloc(1, sizeof(*obj));
+ if (!obj) {
+ free(info);
+ ret = EFI_OUT_OF_RESOURCES;
+ goto error;
+ }
if (!source_buffer) {
struct efi_device_path *dp, *fp;
@@ -1552,6 +1635,7 @@ static efi_status_t EFIAPI efi_load_image(bool boot_policy,
failure:
free(info);
efi_delete_handle(obj);
+error:
return EFI_EXIT(ret);
}
@@ -1635,8 +1719,9 @@ static efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle,
* @return status code
*/
static efi_status_t EFIAPI efi_exit(efi_handle_t image_handle,
- efi_status_t exit_status, unsigned long exit_data_size,
- int16_t *exit_data)
+ efi_status_t exit_status,
+ unsigned long exit_data_size,
+ int16_t *exit_data)
{
/*
* We require that the handle points to the original loaded
@@ -1649,7 +1734,7 @@ static efi_status_t EFIAPI efi_exit(efi_handle_t image_handle,
* TODO: We should call the unload procedure of the loaded
* image protocol.
*/
- struct efi_loaded_image *loaded_image_info = (void*)image_handle;
+ struct efi_loaded_image *loaded_image_info = (void *)image_handle;
EFI_ENTRY("%p, %ld, %ld, %p", image_handle, exit_status,
exit_data_size, exit_data);
@@ -1724,7 +1809,7 @@ static void efi_exit_caches(void)
static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle,
unsigned long map_key)
{
- int i;
+ struct efi_event *evt;
EFI_ENTRY("%p, %ld", image_handle, map_key);
@@ -1735,12 +1820,19 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle,
if (!systab.boottime)
return EFI_EXIT(EFI_SUCCESS);
+ /* Add related events to the event group */
+ list_for_each_entry(evt, &efi_events, link) {
+ if (evt->type == EVT_SIGNAL_EXIT_BOOT_SERVICES)
+ evt->group = &efi_guid_event_group_exit_boot_services;
+ }
/* Notify that ExitBootServices is invoked. */
- for (i = 0; i < ARRAY_SIZE(efi_events); ++i) {
- if (efi_events[i].type != EVT_SIGNAL_EXIT_BOOT_SERVICES)
- continue;
- efi_events[i].is_signaled = true;
- efi_signal_event(&efi_events[i], false);
+ list_for_each_entry(evt, &efi_events, link) {
+ if (evt->group &&
+ !guidcmp(evt->group,
+ &efi_guid_event_group_exit_boot_services)) {
+ efi_signal_event(evt, false);
+ break;
+ }
}
/* TODO Should persist EFI variables here */
@@ -1786,7 +1878,8 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle,
*/
static efi_status_t EFIAPI efi_get_next_monotonic_count(uint64_t *count)
{
- static uint64_t mono = 0;
+ static uint64_t mono;
+
EFI_ENTRY("%p", count);
*count = mono++;
return EFI_EXIT(EFI_SUCCESS);
@@ -1827,7 +1920,7 @@ static efi_status_t EFIAPI efi_set_watchdog_timer(unsigned long timeout,
unsigned long data_size,
uint16_t *watchdog_data)
{
- EFI_ENTRY("%ld, 0x%"PRIx64", %ld, %p", timeout, watchdog_code,
+ EFI_ENTRY("%ld, 0x%" PRIx64 ", %ld, %p", timeout, watchdog_code,
data_size, watchdog_data);
return EFI_EXIT(efi_set_watchdog(timeout));
}
@@ -1892,8 +1985,8 @@ out:
* @entry_count number of entries available in the buffer
* @return status code
*/
-static efi_status_t EFIAPI efi_open_protocol_information(efi_handle_t handle,
- const efi_guid_t *protocol,
+static efi_status_t EFIAPI efi_open_protocol_information(
+ efi_handle_t handle, const efi_guid_t *protocol,
struct efi_open_protocol_info_entry **entry_buffer,
efi_uintn_t *entry_count)
{
@@ -2878,15 +2971,16 @@ static const struct efi_boot_services efi_boot_services = {
.protocols_per_handle = efi_protocols_per_handle,
.locate_handle_buffer = efi_locate_handle_buffer,
.locate_protocol = efi_locate_protocol,
- .install_multiple_protocol_interfaces = efi_install_multiple_protocol_interfaces,
- .uninstall_multiple_protocol_interfaces = efi_uninstall_multiple_protocol_interfaces,
+ .install_multiple_protocol_interfaces =
+ efi_install_multiple_protocol_interfaces,
+ .uninstall_multiple_protocol_interfaces =
+ efi_uninstall_multiple_protocol_interfaces,
.calculate_crc32 = efi_calculate_crc32,
.copy_mem = efi_copy_mem,
.set_mem = efi_set_mem,
.create_event_ex = efi_create_event_ex,
};
-
static uint16_t __efi_runtime_data firmware_vendor[] = L"Das U-Boot";
struct efi_system_table __efi_runtime_data systab = {
@@ -2896,11 +2990,11 @@ struct efi_system_table __efi_runtime_data systab = {
.headersize = sizeof(struct efi_table_hdr),
},
.fw_vendor = (long)firmware_vendor,
- .con_in = (void*)&efi_con_in,
- .con_out = (void*)&efi_con_out,
- .std_err = (void*)&efi_con_out,
- .runtime = (void*)&efi_runtime_services,
- .boottime = (void*)&efi_boot_services,
+ .con_in = (void *)&efi_con_in,
+ .con_out = (void *)&efi_con_out,
+ .std_err = (void *)&efi_con_out,
+ .runtime = (void *)&efi_runtime_services,
+ .boottime = (void *)&efi_boot_services,
.nr_tables = 0,
- .tables = (void*)efi_conf_table,
+ .tables = (void *)efi_conf_table,
};
diff --git a/lib/efi_loader/efi_console.c b/lib/efi_loader/efi_console.c
index 28d63635ec..5d1a9a8081 100644
--- a/lib/efi_loader/efi_console.c
+++ b/lib/efi_loader/efi_console.c
@@ -45,7 +45,6 @@ static struct cout_mode efi_cout_modes[] = {
},
};
-const efi_guid_t efi_guid_console_control = CONSOLE_CONTROL_GUID;
const efi_guid_t efi_guid_text_output_protocol =
EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_GUID;
const efi_guid_t efi_guid_text_input_protocol =
@@ -54,43 +53,6 @@ const efi_guid_t efi_guid_text_input_protocol =
#define cESC '\x1b'
#define ESC "\x1b"
-static efi_status_t EFIAPI efi_cin_get_mode(
- struct efi_console_control_protocol *this,
- int *mode, char *uga_exists, char *std_in_locked)
-{
- EFI_ENTRY("%p, %p, %p, %p", this, mode, uga_exists, std_in_locked);
-
- if (mode)
- *mode = EFI_CONSOLE_MODE_TEXT;
- if (uga_exists)
- *uga_exists = 0;
- if (std_in_locked)
- *std_in_locked = 0;
-
- return EFI_EXIT(EFI_SUCCESS);
-}
-
-static efi_status_t EFIAPI efi_cin_set_mode(
- struct efi_console_control_protocol *this, int mode)
-{
- EFI_ENTRY("%p, %d", this, mode);
- return EFI_EXIT(EFI_UNSUPPORTED);
-}
-
-static efi_status_t EFIAPI efi_cin_lock_std_in(
- struct efi_console_control_protocol *this,
- uint16_t *password)
-{
- EFI_ENTRY("%p, %p", this, password);
- return EFI_EXIT(EFI_UNSUPPORTED);
-}
-
-struct efi_console_control_protocol efi_console_control = {
- .get_mode = efi_cin_get_mode,
- .set_mode = efi_cin_set_mode,
- .lock_std_in = efi_cin_lock_std_in,
-};
-
/* Default to mode 0 */
static struct simple_text_output_mode efi_con_mode = {
.max_mode = 1,
@@ -399,6 +361,48 @@ static efi_status_t EFIAPI efi_cin_reset(
return EFI_EXIT(EFI_UNSUPPORTED);
}
+/*
+ * Analyze modifiers (shift, alt, ctrl) for function keys.
+ * This gets called when we have already parsed CSI.
+ *
+ * @modifiers: bitmask (shift, alt, ctrl)
+ * @return: the unmodified code
+ */
+static char skip_modifiers(int *modifiers)
+{
+ char c, mod = 0, ret = 0;
+
+ c = getc();
+
+ if (c != ';') {
+ ret = c;
+ if (c == '~')
+ goto out;
+ c = getc();
+ }
+ for (;;) {
+ switch (c) {
+ case '0'...'9':
+ mod *= 10;
+ mod += c - '0';
+ /* fall through */
+ case ';':
+ c = getc();
+ break;
+ default:
+ goto out;
+ }
+ }
+out:
+ if (mod)
+ --mod;
+ if (modifiers)
+ *modifiers = mod;
+ if (!ret)
+ ret = c;
+ return ret;
+}
+
static efi_status_t EFIAPI efi_cin_read_key_stroke(
struct efi_simple_input_interface *this,
struct efi_input_key *key)
@@ -421,14 +425,21 @@ static efi_status_t EFIAPI efi_cin_read_key_stroke(
ch = getc();
if (ch == cESC) {
- /* Escape Sequence */
+ /*
+ * Xterm Control Sequences
+ * https://www.xfree86.org/4.8.0/ctlseqs.html
+ */
ch = getc();
switch (ch) {
case cESC: /* ESC */
pressed_key.scan_code = 23;
break;
case 'O': /* F1 - F4 */
- pressed_key.scan_code = getc() - 'P' + 11;
+ ch = getc();
+ /* skip modifiers */
+ if (ch <= '9')
+ ch = getc();
+ pressed_key.scan_code = ch - 'P' + 11;
break;
case 'a'...'z':
ch = ch - 'a';
@@ -445,17 +456,51 @@ static efi_status_t EFIAPI efi_cin_read_key_stroke(
case 'H': /* Home */
pressed_key.scan_code = 5;
break;
- case '1': /* F5 - F8 */
- pressed_key.scan_code = getc() - '0' + 11;
- getc();
+ case '1':
+ ch = skip_modifiers(NULL);
+ switch (ch) {
+ case '1'...'5': /* F1 - F5 */
+ pressed_key.scan_code = ch - '1' + 11;
+ break;
+ case '7'...'9': /* F6 - F8 */
+ pressed_key.scan_code = ch - '7' + 16;
+ break;
+ case 'A'...'D': /* up, down right, left */
+ pressed_key.scan_code = ch - 'A' + 1;
+ break;
+ case 'F':
+ pressed_key.scan_code = 6; /* End */
+ break;
+ case 'H':
+ pressed_key.scan_code = 5; /* Home */
+ break;
+ }
break;
- case '2': /* F9 - F12 */
- pressed_key.scan_code = getc() - '0' + 19;
- getc();
+ case '2':
+ ch = skip_modifiers(NULL);
+ switch (ch) {
+ case '0'...'1': /* F9 - F10 */
+ pressed_key.scan_code = ch - '0' + 19;
+ break;
+ case '3'...'4': /* F11 - F12 */
+ pressed_key.scan_code = ch - '3' + 21;
+ break;
+ case '~': /* INS */
+ pressed_key.scan_code = 7;
+ break;
+ }
break;
case '3': /* DEL */
pressed_key.scan_code = 8;
- getc();
+ skip_modifiers(NULL);
+ break;
+ case '5': /* PG UP */
+ pressed_key.scan_code = 9;
+ skip_modifiers(NULL);
+ break;
+ case '6': /* PG DOWN */
+ pressed_key.scan_code = 10;
+ skip_modifiers(NULL);
break;
}
break;
@@ -464,7 +509,8 @@ static efi_status_t EFIAPI efi_cin_read_key_stroke(
/* Backspace */
ch = 0x08;
}
- pressed_key.unicode_char = ch;
+ if (!pressed_key.scan_code)
+ pressed_key.unicode_char = ch;
*key = pressed_key;
return EFI_EXIT(EFI_SUCCESS);
@@ -506,18 +552,10 @@ static void EFIAPI efi_console_timer_notify(struct efi_event *event,
int efi_console_register(void)
{
efi_status_t r;
- struct efi_object *efi_console_control_obj;
struct efi_object *efi_console_output_obj;
struct efi_object *efi_console_input_obj;
/* Create handles */
- r = efi_create_handle((efi_handle_t *)&efi_console_control_obj);
- if (r != EFI_SUCCESS)
- goto out_of_memory;
- r = efi_add_protocol(efi_console_control_obj->handle,
- &efi_guid_console_control, &efi_console_control);
- if (r != EFI_SUCCESS)
- goto out_of_memory;
r = efi_create_handle((efi_handle_t *)&efi_console_output_obj);
if (r != EFI_SUCCESS)
goto out_of_memory;
@@ -534,14 +572,14 @@ int efi_console_register(void)
goto out_of_memory;
/* Create console events */
- r = efi_create_event(EVT_NOTIFY_WAIT, TPL_CALLBACK,
- efi_key_notify, NULL, &efi_con_in.wait_for_key);
+ r = efi_create_event(EVT_NOTIFY_WAIT, TPL_CALLBACK, efi_key_notify,
+ NULL, NULL, &efi_con_in.wait_for_key);
if (r != EFI_SUCCESS) {
printf("ERROR: Failed to register WaitForKey event\n");
return r;
}
r = efi_create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK,
- efi_console_timer_notify, NULL,
+ efi_console_timer_notify, NULL, NULL,
&console_timer_event);
if (r != EFI_SUCCESS) {
printf("ERROR: Failed to register console event\n");
diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c
index 3c735e60d3..ab28b2fd25 100644
--- a/lib/efi_loader/efi_device_path.c
+++ b/lib/efi_loader/efi_device_path.c
@@ -66,6 +66,7 @@ static void *dp_alloc(size_t sz)
return NULL;
}
+ memset(buf, 0, sz);
return buf;
}
@@ -749,7 +750,9 @@ struct efi_device_path *efi_dp_from_file(struct blk_desc *desc, int part,
#ifdef CONFIG_CMD_NET
struct efi_device_path *efi_dp_from_eth(void)
{
+#ifndef CONFIG_DM_ETH
struct efi_device_path_mac_addr *ndp;
+#endif
void *buf, *start;
unsigned dpsize = 0;
@@ -759,8 +762,8 @@ struct efi_device_path *efi_dp_from_eth(void)
dpsize += dp_size(eth_get_dev());
#else
dpsize += sizeof(ROOT);
-#endif
dpsize += sizeof(*ndp);
+#endif
start = buf = dp_alloc(dpsize + sizeof(END));
if (!buf)
@@ -771,14 +774,15 @@ struct efi_device_path *efi_dp_from_eth(void)
#else
memcpy(buf, &ROOT, sizeof(ROOT));
buf += sizeof(ROOT);
-#endif
ndp = buf;
ndp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE;
ndp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_MAC_ADDR;
ndp->dp.length = sizeof(*ndp);
+ ndp->if_type = 1; /* Ethernet */
memcpy(ndp->mac.addr, eth_get_ethaddr(), ARP_HLEN);
buf = &ndp[1];
+#endif
*((struct efi_device_path *)buf) = END;
diff --git a/lib/efi_loader/efi_device_path_utilities.c b/lib/efi_loader/efi_device_path_utilities.c
new file mode 100644
index 0000000000..bc97eeee31
--- /dev/null
+++ b/lib/efi_loader/efi_device_path_utilities.c
@@ -0,0 +1,89 @@
+/*
+ * EFI device path interface
+ *
+ * Copyright (c) 2017 Leif Lindholm
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <efi_loader.h>
+
+const efi_guid_t efi_guid_device_path_utilities_protocol =
+ EFI_DEVICE_PATH_UTILITIES_PROTOCOL_GUID;
+
+static efi_uintn_t EFIAPI get_device_path_size(
+ const struct efi_device_path *device_path)
+{
+ efi_uintn_t sz = 0;
+
+ EFI_ENTRY("%p", device_path);
+ /* size includes the END node: */
+ if (device_path)
+ sz = efi_dp_size(device_path) + sizeof(struct efi_device_path);
+ return EFI_EXIT(sz);
+}
+
+static struct efi_device_path * EFIAPI duplicate_device_path(
+ const struct efi_device_path *device_path)
+{
+ EFI_ENTRY("%p", device_path);
+ return EFI_EXIT(efi_dp_dup(device_path));
+}
+
+static struct efi_device_path * EFIAPI append_device_path(
+ const struct efi_device_path *src1,
+ const struct efi_device_path *src2)
+{
+ EFI_ENTRY("%p, %p", src1, src2);
+ return EFI_EXIT(efi_dp_append(src1, src2));
+}
+
+static struct efi_device_path * EFIAPI append_device_node(
+ const struct efi_device_path *device_path,
+ const struct efi_device_path *device_node)
+{
+ EFI_ENTRY("%p, %p", device_path, device_node);
+ return EFI_EXIT(efi_dp_append_node(device_path, device_node));
+}
+
+static struct efi_device_path * EFIAPI append_device_path_instance(
+ const struct efi_device_path *device_path,
+ const struct efi_device_path *device_path_instance)
+{
+ EFI_ENTRY("%p, %p", device_path, device_path_instance);
+ return EFI_EXIT(NULL);
+}
+
+static struct efi_device_path * EFIAPI get_next_device_path_instance(
+ struct efi_device_path **device_path_instance,
+ efi_uintn_t *device_path_instance_size)
+{
+ EFI_ENTRY("%p, %p", device_path_instance, device_path_instance_size);
+ return EFI_EXIT(NULL);
+}
+
+static bool EFIAPI is_device_path_multi_instance(
+ const struct efi_device_path *device_path)
+{
+ EFI_ENTRY("%p", device_path);
+ return EFI_EXIT(false);
+}
+
+static struct efi_device_path * EFIAPI create_device_node(
+ uint8_t node_type, uint8_t node_sub_type, uint16_t node_length)
+{
+ EFI_ENTRY("%u, %u, %u", node_type, node_sub_type, node_length);
+ return EFI_EXIT(NULL);
+}
+
+const struct efi_device_path_utilities_protocol efi_device_path_utilities = {
+ .get_device_path_size = get_device_path_size,
+ .duplicate_device_path = duplicate_device_path,
+ .append_device_path = append_device_path,
+ .append_device_node = append_device_node,
+ .append_device_path_instance = append_device_path_instance,
+ .get_next_device_path_instance = get_next_device_path_instance,
+ .is_device_path_multi_instance = is_device_path_multi_instance,
+ .create_device_node = create_device_node,
+};
diff --git a/lib/efi_loader/efi_file.c b/lib/efi_loader/efi_file.c
index 52a4e7438e..cec8347f55 100644
--- a/lib/efi_loader/efi_file.c
+++ b/lib/efi_loader/efi_file.c
@@ -12,6 +12,9 @@
#include <malloc.h>
#include <fs.h>
+/* GUID for file system information */
+const efi_guid_t efi_file_system_info_guid = EFI_FILE_SYSTEM_INFO_GUID;
+
struct file_system {
struct efi_simple_file_system_protocol base;
struct efi_device_path *dp;
@@ -314,29 +317,41 @@ static efi_status_t dir_read(struct file_handle *fh, u64 *buffer_size,
}
static efi_status_t EFIAPI efi_file_read(struct efi_file_handle *file,
- u64 *buffer_size, void *buffer)
+ efi_uintn_t *buffer_size, void *buffer)
{
struct file_handle *fh = to_fh(file);
efi_status_t ret = EFI_SUCCESS;
+ u64 bs;
EFI_ENTRY("%p, %p, %p", file, buffer_size, buffer);
+ if (!buffer_size || !buffer) {
+ ret = EFI_INVALID_PARAMETER;
+ goto error;
+ }
+
if (set_blk_dev(fh)) {
ret = EFI_DEVICE_ERROR;
goto error;
}
+ bs = *buffer_size;
if (fh->isdir)
- ret = dir_read(fh, buffer_size, buffer);
+ ret = dir_read(fh, &bs, buffer);
else
- ret = file_read(fh, buffer_size, buffer);
+ ret = file_read(fh, &bs, buffer);
+ if (bs <= SIZE_MAX)
+ *buffer_size = bs;
+ else
+ *buffer_size = SIZE_MAX;
error:
return EFI_EXIT(ret);
}
static efi_status_t EFIAPI efi_file_write(struct efi_file_handle *file,
- u64 *buffer_size, void *buffer)
+ efi_uintn_t *buffer_size,
+ void *buffer)
{
struct file_handle *fh = to_fh(file);
efi_status_t ret = EFI_SUCCESS;
@@ -363,21 +378,27 @@ error:
}
static efi_status_t EFIAPI efi_file_getpos(struct efi_file_handle *file,
- u64 *pos)
+ efi_uintn_t *pos)
{
struct file_handle *fh = to_fh(file);
+
EFI_ENTRY("%p, %p", file, pos);
- *pos = fh->offset;
- return EFI_EXIT(EFI_SUCCESS);
+
+ if (fh->offset <= SIZE_MAX) {
+ *pos = fh->offset;
+ return EFI_EXIT(EFI_SUCCESS);
+ } else {
+ return EFI_EXIT(EFI_DEVICE_ERROR);
+ }
}
static efi_status_t EFIAPI efi_file_setpos(struct efi_file_handle *file,
- u64 pos)
+ efi_uintn_t pos)
{
struct file_handle *fh = to_fh(file);
efi_status_t ret = EFI_SUCCESS;
- EFI_ENTRY("%p, %llu", file, pos);
+ EFI_ENTRY("%p, %zu", file, pos);
if (fh->isdir) {
if (pos != 0) {
@@ -411,7 +432,9 @@ error:
}
static efi_status_t EFIAPI efi_file_getinfo(struct efi_file_handle *file,
- efi_guid_t *info_type, u64 *buffer_size, void *buffer)
+ const efi_guid_t *info_type,
+ efi_uintn_t *buffer_size,
+ void *buffer)
{
struct file_handle *fh = to_fh(file);
efi_status_t ret = EFI_SUCCESS;
@@ -452,6 +475,41 @@ static efi_status_t EFIAPI efi_file_getinfo(struct efi_file_handle *file,
info->attribute |= EFI_FILE_DIRECTORY;
ascii2unicode((u16 *)info->file_name, filename);
+ } else if (!guidcmp(info_type, &efi_file_system_info_guid)) {
+ struct efi_file_system_info *info = buffer;
+ disk_partition_t part;
+ efi_uintn_t required_size;
+ int r;
+
+ if (fh->fs->part >= 1)
+ r = part_get_info(fh->fs->desc, fh->fs->part, &part);
+ else
+ r = part_get_info_whole_disk(fh->fs->desc, &part);
+ if (r < 0) {
+ ret = EFI_DEVICE_ERROR;
+ goto error;
+ }
+ required_size = sizeof(info) + 2 *
+ (strlen((const char *)part.name) + 1);
+ if (*buffer_size < required_size) {
+ *buffer_size = required_size;
+ ret = EFI_BUFFER_TOO_SMALL;
+ goto error;
+ }
+
+ memset(info, 0, required_size);
+
+ info->size = required_size;
+ info->read_only = true;
+ info->volume_size = part.size * part.blksz;
+ info->free_space = 0;
+ info->block_size = part.blksz;
+ /*
+ * TODO: The volume label is not available in U-Boot.
+ * Use the partition name as substitute.
+ */
+ ascii2unicode((u16 *)info->volume_label,
+ (const char *)part.name);
} else {
ret = EFI_UNSUPPORTED;
}
@@ -461,9 +519,12 @@ error:
}
static efi_status_t EFIAPI efi_file_setinfo(struct efi_file_handle *file,
- efi_guid_t *info_type, u64 buffer_size, void *buffer)
+ const efi_guid_t *info_type,
+ efi_uintn_t buffer_size,
+ void *buffer)
{
- EFI_ENTRY("%p, %p, %llu, %p", file, info_type, buffer_size, buffer);
+ EFI_ENTRY("%p, %p, %zu, %p", file, info_type, buffer_size, buffer);
+
return EFI_EXIT(EFI_UNSUPPORTED);
}
diff --git a/lib/efi_loader/efi_gop.c b/lib/efi_loader/efi_gop.c
index 3caddd5f84..363ccbb789 100644
--- a/lib/efi_loader/efi_gop.c
+++ b/lib/efi_loader/efi_gop.c
@@ -56,27 +56,166 @@ static efi_status_t EFIAPI gop_set_mode(struct efi_gop *this, u32 mode_number)
return EFI_EXIT(EFI_SUCCESS);
}
-efi_status_t EFIAPI gop_blt(struct efi_gop *this, void *buffer,
- u32 operation, efi_uintn_t sx,
- efi_uintn_t sy, efi_uintn_t dx,
- efi_uintn_t dy, efi_uintn_t width,
- efi_uintn_t height, efi_uintn_t delta)
+static __always_inline struct efi_gop_pixel efi_vid16_to_blt_col(u16 vid)
+{
+ struct efi_gop_pixel blt = {
+ .reserved = 0,
+ };
+
+ blt.blue = (vid & 0x1f) << 3;
+ vid >>= 5;
+ blt.green = (vid & 0x3f) << 2;
+ vid >>= 6;
+ blt.red = (vid & 0x1f) << 3;
+ return blt;
+}
+
+static __always_inline u16 efi_blt_col_to_vid16(struct efi_gop_pixel *blt)
+{
+ return (u16)(blt->red >> 3) << 11 |
+ (u16)(blt->green >> 2) << 5 |
+ (u16)(blt->blue >> 3);
+}
+
+static __always_inline efi_status_t gop_blt_int(struct efi_gop *this,
+ struct efi_gop_pixel *bufferp,
+ u32 operation, efi_uintn_t sx,
+ efi_uintn_t sy, efi_uintn_t dx,
+ efi_uintn_t dy,
+ efi_uintn_t width,
+ efi_uintn_t height,
+ efi_uintn_t delta,
+ efi_uintn_t vid_bpp)
{
struct efi_gop_obj *gopobj = container_of(this, struct efi_gop_obj, ops);
- int i, j, line_len16, line_len32;
- void *fb;
+ efi_uintn_t i, j, linelen, slineoff = 0, dlineoff, swidth, dwidth;
+ u32 *fb32 = gopobj->fb;
+ u16 *fb16 = gopobj->fb;
+ struct efi_gop_pixel *buffer = __builtin_assume_aligned(bufferp, 4);
+
+ if (delta) {
+ /* Check for 4 byte alignment */
+ if (delta & 3)
+ return EFI_INVALID_PARAMETER;
+ linelen = delta >> 2;
+ } else {
+ linelen = width;
+ }
- EFI_ENTRY("%p, %p, %u, %zu, %zu, %zu, %zu, %zu, %zu, %zu", this,
- buffer, operation, sx, sy, dx, dy, width, height, delta);
+ /* Check source rectangle */
+ switch (operation) {
+ case EFI_BLT_VIDEO_FILL:
+ break;
+ case EFI_BLT_BUFFER_TO_VIDEO:
+ if (sx + width > linelen)
+ return EFI_INVALID_PARAMETER;
+ break;
+ case EFI_BLT_VIDEO_TO_BLT_BUFFER:
+ case EFI_BLT_VIDEO_TO_VIDEO:
+ if (sx + width > gopobj->info.width ||
+ sy + height > gopobj->info.height)
+ return EFI_INVALID_PARAMETER;
+ break;
+ default:
+ return EFI_INVALID_PARAMETER;
+ }
- if (operation != EFI_BLT_BUFFER_TO_VIDEO)
- return EFI_EXIT(EFI_INVALID_PARAMETER);
+ /* Check destination rectangle */
+ switch (operation) {
+ case EFI_BLT_VIDEO_FILL:
+ case EFI_BLT_BUFFER_TO_VIDEO:
+ case EFI_BLT_VIDEO_TO_VIDEO:
+ if (dx + width > gopobj->info.width ||
+ dy + height > gopobj->info.height)
+ return EFI_INVALID_PARAMETER;
+ break;
+ case EFI_BLT_VIDEO_TO_BLT_BUFFER:
+ if (dx + width > linelen)
+ return EFI_INVALID_PARAMETER;
+ break;
+ }
- fb = gopobj->fb;
- line_len16 = gopobj->info.width * sizeof(u16);
- line_len32 = gopobj->info.width * sizeof(u32);
+ /* Calculate line width */
+ switch (operation) {
+ case EFI_BLT_BUFFER_TO_VIDEO:
+ swidth = linelen;
+ break;
+ case EFI_BLT_VIDEO_TO_BLT_BUFFER:
+ case EFI_BLT_VIDEO_TO_VIDEO:
+ swidth = gopobj->info.width;
+ if (!vid_bpp)
+ return EFI_UNSUPPORTED;
+ break;
+ case EFI_BLT_VIDEO_FILL:
+ swidth = 0;
+ break;
+ }
+
+ switch (operation) {
+ case EFI_BLT_BUFFER_TO_VIDEO:
+ case EFI_BLT_VIDEO_FILL:
+ case EFI_BLT_VIDEO_TO_VIDEO:
+ dwidth = gopobj->info.width;
+ if (!vid_bpp)
+ return EFI_UNSUPPORTED;
+ break;
+ case EFI_BLT_VIDEO_TO_BLT_BUFFER:
+ dwidth = linelen;
+ break;
+ }
- /* Copy the contents line by line */
+ slineoff = swidth * sy;
+ dlineoff = dwidth * dy;
+ for (i = 0; i < height; i++) {
+ for (j = 0; j < width; j++) {
+ struct efi_gop_pixel pix;
+
+ /* Read source pixel */
+ switch (operation) {
+ case EFI_BLT_VIDEO_FILL:
+ pix = *buffer;
+ break;
+ case EFI_BLT_BUFFER_TO_VIDEO:
+ pix = buffer[slineoff + j + sx];
+ break;
+ case EFI_BLT_VIDEO_TO_BLT_BUFFER:
+ case EFI_BLT_VIDEO_TO_VIDEO:
+ if (vid_bpp == 32)
+ pix = *(struct efi_gop_pixel *)&fb32[
+ slineoff + j + sx];
+ else
+ pix = efi_vid16_to_blt_col(fb16[
+ slineoff + j + sx]);
+ break;
+ }
+
+ /* Write destination pixel */
+ switch (operation) {
+ case EFI_BLT_VIDEO_TO_BLT_BUFFER:
+ buffer[dlineoff + j + dx] = pix;
+ break;
+ case EFI_BLT_BUFFER_TO_VIDEO:
+ case EFI_BLT_VIDEO_FILL:
+ case EFI_BLT_VIDEO_TO_VIDEO:
+ if (vid_bpp == 32)
+ fb32[dlineoff + j + dx] = *(u32 *)&pix;
+ else
+ fb16[dlineoff + j + dx] =
+ efi_blt_col_to_vid16(&pix);
+ break;
+ }
+ }
+ slineoff += swidth;
+ dlineoff += dwidth;
+ }
+
+ return EFI_SUCCESS;
+}
+
+static efi_uintn_t gop_get_bpp(struct efi_gop *this)
+{
+ struct efi_gop_obj *gopobj = container_of(this, struct efi_gop_obj, ops);
+ efi_uintn_t vid_bpp = 0;
switch (gopobj->bpix) {
#ifdef CONFIG_DM_VIDEO
@@ -84,38 +223,151 @@ efi_status_t EFIAPI gop_blt(struct efi_gop *this, void *buffer,
#else
case LCD_COLOR32:
#endif
- for (i = 0; i < height; i++) {
- u32 *dest = fb + ((i + dy) * line_len32) +
- (dx * sizeof(u32));
- u32 *src = buffer + ((i + sy) * line_len32) +
- (sx * sizeof(u32));
-
- /* Same color format, just memcpy */
- memcpy(dest, src, width * sizeof(u32));
- }
+ vid_bpp = 32;
break;
#ifdef CONFIG_DM_VIDEO
case VIDEO_BPP16:
#else
case LCD_COLOR16:
#endif
- for (i = 0; i < height; i++) {
- u16 *dest = fb + ((i + dy) * line_len16) +
- (dx * sizeof(u16));
- u32 *src = buffer + ((i + sy) * line_len32) +
- (sx * sizeof(u32));
-
- /* Convert from rgb888 to rgb565 */
- for (j = 0; j < width; j++) {
- u32 rgb888 = src[j];
- dest[j] = ((((rgb888 >> (16 + 3)) & 0x1f) << 11) |
- (((rgb888 >> (8 + 2)) & 0x3f) << 5) |
- (((rgb888 >> (0 + 3)) & 0x1f) << 0));
- }
- }
+ vid_bpp = 16;
break;
}
+ return vid_bpp;
+}
+
+/*
+ * Gcc can't optimize our BLT function well, but we need to make sure that
+ * our 2-dimensional loop gets executed very quickly, otherwise the system
+ * will feel slow.
+ *
+ * By manually putting all obvious branch targets into functions which call
+ * our generic blt function with constants, the compiler can successfully
+ * optimize for speed.
+ */
+static efi_status_t gop_blt_video_fill(struct efi_gop *this,
+ struct efi_gop_pixel *buffer,
+ u32 foo, efi_uintn_t sx,
+ efi_uintn_t sy, efi_uintn_t dx,
+ efi_uintn_t dy, efi_uintn_t width,
+ efi_uintn_t height, efi_uintn_t delta,
+ efi_uintn_t vid_bpp)
+{
+ return gop_blt_int(this, buffer, EFI_BLT_VIDEO_FILL, sx, sy, dx,
+ dy, width, height, delta, vid_bpp);
+}
+
+static efi_status_t gop_blt_buf_to_vid16(struct efi_gop *this,
+ struct efi_gop_pixel *buffer,
+ u32 foo, efi_uintn_t sx,
+ efi_uintn_t sy, efi_uintn_t dx,
+ efi_uintn_t dy, efi_uintn_t width,
+ efi_uintn_t height, efi_uintn_t delta)
+{
+ return gop_blt_int(this, buffer, EFI_BLT_BUFFER_TO_VIDEO, sx, sy, dx,
+ dy, width, height, delta, 16);
+}
+
+static efi_status_t gop_blt_buf_to_vid32(struct efi_gop *this,
+ struct efi_gop_pixel *buffer,
+ u32 foo, efi_uintn_t sx,
+ efi_uintn_t sy, efi_uintn_t dx,
+ efi_uintn_t dy, efi_uintn_t width,
+ efi_uintn_t height, efi_uintn_t delta)
+{
+ return gop_blt_int(this, buffer, EFI_BLT_BUFFER_TO_VIDEO, sx, sy, dx,
+ dy, width, height, delta, 32);
+}
+
+static efi_status_t gop_blt_vid_to_vid(struct efi_gop *this,
+ struct efi_gop_pixel *buffer,
+ u32 foo, efi_uintn_t sx,
+ efi_uintn_t sy, efi_uintn_t dx,
+ efi_uintn_t dy, efi_uintn_t width,
+ efi_uintn_t height, efi_uintn_t delta,
+ efi_uintn_t vid_bpp)
+{
+ return gop_blt_int(this, buffer, EFI_BLT_VIDEO_TO_VIDEO, sx, sy, dx,
+ dy, width, height, delta, vid_bpp);
+}
+
+static efi_status_t gop_blt_vid_to_buf(struct efi_gop *this,
+ struct efi_gop_pixel *buffer,
+ u32 foo, efi_uintn_t sx,
+ efi_uintn_t sy, efi_uintn_t dx,
+ efi_uintn_t dy, efi_uintn_t width,
+ efi_uintn_t height, efi_uintn_t delta,
+ efi_uintn_t vid_bpp)
+{
+ return gop_blt_int(this, buffer, EFI_BLT_VIDEO_TO_BLT_BUFFER, sx, sy,
+ dx, dy, width, height, delta, vid_bpp);
+}
+
+/*
+ * Copy rectangle.
+ *
+ * This function implements the Blt service of the EFI_GRAPHICS_OUTPUT_PROTOCOL.
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @this: EFI_GRAPHICS_OUTPUT_PROTOCOL
+ * @buffer: pixel buffer
+ * @sx: source x-coordinate
+ * @sy: source y-coordinate
+ * @dx: destination x-coordinate
+ * @dy: destination y-coordinate
+ * @width: width of rectangle
+ * @height: height of rectangle
+ * @delta: length in bytes of a line in the pixel buffer (optional)
+ * @return: status code
+ */
+efi_status_t EFIAPI gop_blt(struct efi_gop *this, struct efi_gop_pixel *buffer,
+ u32 operation, efi_uintn_t sx,
+ efi_uintn_t sy, efi_uintn_t dx,
+ efi_uintn_t dy, efi_uintn_t width,
+ efi_uintn_t height, efi_uintn_t delta)
+{
+ efi_status_t ret = EFI_INVALID_PARAMETER;
+ efi_uintn_t vid_bpp;
+
+ EFI_ENTRY("%p, %p, %u, %zu, %zu, %zu, %zu, %zu, %zu, %zu", this,
+ buffer, operation, sx, sy, dx, dy, width, height, delta);
+
+ vid_bpp = gop_get_bpp(this);
+
+ /* Allow for compiler optimization */
+ switch (operation) {
+ case EFI_BLT_VIDEO_FILL:
+ ret = gop_blt_video_fill(this, buffer, operation, sx, sy, dx,
+ dy, width, height, delta, vid_bpp);
+ break;
+ case EFI_BLT_BUFFER_TO_VIDEO:
+ /* This needs to be super-fast, so duplicate for 16/32bpp */
+ if (vid_bpp == 32)
+ ret = gop_blt_buf_to_vid32(this, buffer, operation, sx,
+ sy, dx, dy, width, height,
+ delta);
+ else
+ ret = gop_blt_buf_to_vid16(this, buffer, operation, sx,
+ sy, dx, dy, width, height,
+ delta);
+ break;
+ case EFI_BLT_VIDEO_TO_VIDEO:
+ ret = gop_blt_vid_to_vid(this, buffer, operation, sx, sy, dx,
+ dy, width, height, delta, vid_bpp);
+ break;
+ case EFI_BLT_VIDEO_TO_BLT_BUFFER:
+ ret = gop_blt_vid_to_buf(this, buffer, operation, sx, sy, dx,
+ dy, width, height, delta, vid_bpp);
+ break;
+ default:
+ ret = EFI_UNSUPPORTED;
+ }
+
+ if (ret != EFI_SUCCESS)
+ return EFI_EXIT(ret);
+
#ifdef CONFIG_DM_VIDEO
video_sync_all();
#else
@@ -125,8 +377,13 @@ efi_status_t EFIAPI gop_blt(struct efi_gop *this, void *buffer,
return EFI_EXIT(EFI_SUCCESS);
}
-/* This gets called from do_bootefi_exec(). */
-int efi_gop_register(void)
+/*
+ * Install graphical output protocol.
+ *
+ * If no supported video device exists this is not considered as an
+ * error.
+ */
+efi_status_t efi_gop_register(void)
{
struct efi_gop_obj *gopobj;
u32 bpix, col, row;
@@ -136,12 +393,15 @@ int efi_gop_register(void)
#ifdef CONFIG_DM_VIDEO
struct udevice *vdev;
+ struct video_priv *priv;
/* We only support a single video output device for now */
- if (uclass_first_device(UCLASS_VIDEO, &vdev) || !vdev)
- return -1;
+ if (uclass_first_device(UCLASS_VIDEO, &vdev) || !vdev) {
+ debug("WARNING: No video device\n");
+ return EFI_SUCCESS;
+ }
- struct video_priv *priv = dev_get_uclass_priv(vdev);
+ priv = dev_get_uclass_priv(vdev);
bpix = priv->bpix;
col = video_get_xsize(vdev);
row = video_get_ysize(vdev);
@@ -170,13 +430,14 @@ int efi_gop_register(void)
break;
default:
/* So far, we only work in 16 or 32 bit mode */
- return -1;
+ debug("WARNING: Unsupported video mode\n");
+ return EFI_SUCCESS;
}
gopobj = calloc(1, sizeof(*gopobj));
if (!gopobj) {
printf("ERROR: Out of memory\n");
- return 1;
+ return EFI_OUT_OF_RESOURCES;
}
/* Hook up to the device list */
@@ -186,8 +447,8 @@ int efi_gop_register(void)
ret = efi_add_protocol(gopobj->parent.handle, &efi_gop_guid,
&gopobj->ops);
if (ret != EFI_SUCCESS) {
- printf("ERROR: Out of memory\n");
- return 1;
+ printf("ERROR: Failure adding gop protocol\n");
+ return ret;
}
gopobj->ops.query_mode = gop_query_mode;
gopobj->ops.set_mode = gop_set_mode;
@@ -199,10 +460,11 @@ int efi_gop_register(void)
gopobj->mode.info_size = sizeof(gopobj->info);
#ifdef CONFIG_DM_VIDEO
- if (bpix == VIDEO_BPP32) {
+ if (bpix == VIDEO_BPP32)
#else
- if (bpix == LCD_COLOR32) {
+ if (bpix == LCD_COLOR32)
#endif
+ {
/* With 32bit color space we can directly expose the fb */
gopobj->mode.fb_base = fb_base;
gopobj->mode.fb_size = fb_size;
@@ -217,5 +479,5 @@ int efi_gop_register(void)
gopobj->bpix = bpix;
gopobj->fb = fb;
- return 0;
+ return EFI_SUCCESS;
}
diff --git a/lib/efi_loader/efi_image_loader.c b/lib/efi_loader/efi_image_loader.c
index cac64ba9fe..d5fbba3138 100644
--- a/lib/efi_loader/efi_image_loader.c
+++ b/lib/efi_loader/efi_image_loader.c
@@ -22,6 +22,76 @@ const efi_guid_t efi_simple_file_system_protocol_guid =
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
const efi_guid_t efi_file_info_guid = EFI_FILE_INFO_GUID;
+static int machines[] = {
+#if defined(CONFIG_ARM64)
+ IMAGE_FILE_MACHINE_ARM64,
+#elif defined(CONFIG_ARM)
+ IMAGE_FILE_MACHINE_ARM,
+ IMAGE_FILE_MACHINE_THUMB,
+ IMAGE_FILE_MACHINE_ARMNT,
+#endif
+
+#if defined(CONFIG_X86_64)
+ IMAGE_FILE_MACHINE_AMD64,
+#elif defined(CONFIG_X86)
+ IMAGE_FILE_MACHINE_I386,
+#endif
+
+#if defined(CONFIG_CPU_RISCV_32)
+ IMAGE_FILE_MACHINE_RISCV32,
+#endif
+
+#if defined(CONFIG_CPU_RISCV_64)
+ IMAGE_FILE_MACHINE_RISCV64,
+#endif
+ 0 };
+
+/*
+ * Print information about a loaded image.
+ *
+ * If the program counter is located within the image the offset to the base
+ * address is shown.
+ *
+ * @image: loaded image
+ * @pc: program counter (use NULL to suppress offset output)
+ * @return: status code
+ */
+efi_status_t efi_print_image_info(struct efi_loaded_image *image, void *pc)
+{
+ if (!image)
+ return EFI_INVALID_PARAMETER;
+ printf("UEFI image");
+ printf(" [0x%p:0x%p]",
+ image->reloc_base, image->reloc_base + image->reloc_size - 1);
+ if (pc && pc >= image->reloc_base &&
+ pc < image->reloc_base + image->reloc_size)
+ printf(" pc=0x%zx", pc - image->reloc_base);
+ if (image->file_path)
+ printf(" '%pD'", image->file_path);
+ printf("\n");
+ return EFI_SUCCESS;
+}
+
+/*
+ * Print information about all loaded images.
+ *
+ * @pc: program counter (use NULL to suppress offset output)
+ */
+void efi_print_image_infos(void *pc)
+{
+ struct efi_object *efiobj;
+ struct efi_handler *handler;
+
+ list_for_each_entry(efiobj, &efi_obj_list, link) {
+ list_for_each_entry(handler, &efiobj->protocols, link) {
+ if (!guidcmp(handler->guid, &efi_guid_loaded_image)) {
+ efi_print_image_info(
+ handler->protocol_interface, pc);
+ }
+ }
+ }
+}
+
static efi_status_t efi_loader_relocate(const IMAGE_BASE_RELOCATION *rel,
unsigned long rel_size, void *efi_reloc)
{
@@ -126,14 +196,7 @@ void *efi_load_pe(void *efi, struct efi_loaded_image *loaded_image_info)
void *entry;
uint64_t image_size;
unsigned long virt_size = 0;
- bool can_run_nt64 = true;
- bool can_run_nt32 = true;
-
-#if defined(CONFIG_ARM64)
- can_run_nt32 = false;
-#elif defined(CONFIG_ARM)
- can_run_nt64 = false;
-#endif
+ int supported = 0;
dos = efi;
if (dos->e_magic != IMAGE_DOS_SIGNATURE) {
@@ -147,6 +210,18 @@ void *efi_load_pe(void *efi, struct efi_loaded_image *loaded_image_info)
return NULL;
}
+ for (i = 0; machines[i]; i++)
+ if (machines[i] == nt->FileHeader.Machine) {
+ supported = 1;
+ break;
+ }
+
+ if (!supported) {
+ printf("%s: Machine type 0x%04x is not supported\n",
+ __func__, nt->FileHeader.Machine);
+ return NULL;
+ }
+
/* Calculate upper virtual address boundary */
num_sections = nt->FileHeader.NumberOfSections;
sections = (void *)&nt->OptionalHeader +
@@ -159,8 +234,7 @@ void *efi_load_pe(void *efi, struct efi_loaded_image *loaded_image_info)
}
/* Read 32/64bit specific header bits */
- if (can_run_nt64 &&
- (nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC)) {
+ if (nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
IMAGE_NT_HEADERS64 *nt64 = (void *)nt;
IMAGE_OPTIONAL_HEADER64 *opt = &nt64->OptionalHeader;
image_size = opt->SizeOfImage;
@@ -175,8 +249,8 @@ void *efi_load_pe(void *efi, struct efi_loaded_image *loaded_image_info)
entry = efi_reloc + opt->AddressOfEntryPoint;
rel_size = opt->DataDirectory[rel_idx].Size;
rel = efi_reloc + opt->DataDirectory[rel_idx].VirtualAddress;
- } else if (can_run_nt32 &&
- (nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC)) {
+ virt_size = ALIGN(virt_size, opt->SectionAlignment);
+ } else if (nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
IMAGE_OPTIONAL_HEADER32 *opt = &nt->OptionalHeader;
image_size = opt->SizeOfImage;
efi_set_code_and_data_type(loaded_image_info, opt->Subsystem);
@@ -190,6 +264,7 @@ void *efi_load_pe(void *efi, struct efi_loaded_image *loaded_image_info)
entry = efi_reloc + opt->AddressOfEntryPoint;
rel_size = opt->DataDirectory[rel_idx].Size;
rel = efi_reloc + opt->DataDirectory[rel_idx].VirtualAddress;
+ virt_size = ALIGN(virt_size, opt->SectionAlignment);
} else {
printf("%s: Invalid optional header magic %x\n", __func__,
nt->OptionalHeader.Magic);
@@ -221,6 +296,8 @@ void *efi_load_pe(void *efi, struct efi_loaded_image *loaded_image_info)
/* Populate the loaded image interface bits */
loaded_image_info->image_base = efi;
loaded_image_info->image_size = image_size;
+ loaded_image_info->reloc_base = efi_reloc;
+ loaded_image_info->reloc_size = virt_size;
return entry;
}
diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c
index ff0edf30ff..95f9ff0a14 100644
--- a/lib/efi_loader/efi_memory.c
+++ b/lib/efi_loader/efi_memory.c
@@ -8,12 +8,11 @@
#include <common.h>
#include <efi_loader.h>
+#include <inttypes.h>
#include <malloc.h>
+#include <watchdog.h>
#include <asm/global_data.h>
-#include <linux/libfdt_env.h>
#include <linux/list_sort.h>
-#include <inttypes.h>
-#include <watchdog.h>
DECLARE_GLOBAL_DATA_PTR;
@@ -292,7 +291,7 @@ efi_status_t efi_allocate_pages(int type, int memory_type,
uint64_t addr;
switch (type) {
- case 0:
+ case EFI_ALLOCATE_ANY_PAGES:
/* Any page */
addr = efi_find_free_memory(len, gd->start_addr_sp);
if (!addr) {
@@ -300,7 +299,7 @@ efi_status_t efi_allocate_pages(int type, int memory_type,
break;
}
break;
- case 1:
+ case EFI_ALLOCATE_MAX_ADDRESS:
/* Max address */
addr = efi_find_free_memory(len, *memory);
if (!addr) {
@@ -308,7 +307,7 @@ efi_status_t efi_allocate_pages(int type, int memory_type,
break;
}
break;
- case 2:
+ case EFI_ALLOCATE_ADDRESS:
/* Exact address, reserve it. The addr is already in *memory. */
addr = *memory;
break;
diff --git a/lib/efi_loader/efi_net.c b/lib/efi_loader/efi_net.c
index 8c5d5b492c..9afe76cdb3 100644
--- a/lib/efi_loader/efi_net.c
+++ b/lib/efi_loader/efi_net.c
@@ -54,14 +54,46 @@ static efi_status_t EFIAPI efi_net_stop(struct efi_simple_network *this)
return EFI_EXIT(EFI_SUCCESS);
}
+/*
+ * Initialize network adapter and allocate transmit and receive buffers.
+ *
+ * This function implements the Initialize service of the
+ * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface
+ * (UEFI) specification for details.
+ *
+ * @this: pointer to the protocol instance
+ * @extra_rx: extra receive buffer to be allocated
+ * @extra_tx: extra transmit buffer to be allocated
+ * @return: status code
+ */
static efi_status_t EFIAPI efi_net_initialize(struct efi_simple_network *this,
ulong extra_rx, ulong extra_tx)
{
+ int ret;
+ efi_status_t r = EFI_SUCCESS;
+
EFI_ENTRY("%p, %lx, %lx", this, extra_rx, extra_tx);
- eth_init();
+ if (!this) {
+ r = EFI_INVALID_PARAMETER;
+ goto error;
+ }
- return EFI_EXIT(EFI_SUCCESS);
+ /* Setup packet buffers */
+ net_init();
+ /* Disable hardware and put it into the reset state */
+ eth_halt();
+ /* Set current device according to environment variables */
+ eth_set_current();
+ /* Get hardware ready for send and receive operations */
+ ret = eth_init();
+ if (ret < 0) {
+ eth_halt();
+ r = EFI_DEVICE_ERROR;
+ }
+
+error:
+ return EFI_EXIT(r);
}
static efi_status_t EFIAPI efi_net_reset(struct efi_simple_network *this,
@@ -280,20 +312,22 @@ static void EFIAPI efi_network_timer_notify(struct efi_event *event,
}
/* This gets called from do_bootefi_exec(). */
-int efi_net_register(void)
+efi_status_t efi_net_register(void)
{
struct efi_net_obj *netobj;
efi_status_t r;
if (!eth_get_dev()) {
/* No eth device active, don't expose any */
- return 0;
+ return EFI_SUCCESS;
}
/* We only expose the "active" eth device, so one is enough */
netobj = calloc(1, sizeof(*netobj));
- if (!netobj)
- goto out_of_memory;
+ if (!netobj) {
+ printf("ERROR: Out of memory\n");
+ return EFI_OUT_OF_RESOURCES;
+ }
/* Hook net up to the device list */
efi_add_handle(&netobj->parent);
@@ -302,15 +336,15 @@ int efi_net_register(void)
r = efi_add_protocol(netobj->parent.handle, &efi_net_guid,
&netobj->net);
if (r != EFI_SUCCESS)
- goto out_of_memory;
+ goto failure_to_add_protocol;
r = efi_add_protocol(netobj->parent.handle, &efi_guid_device_path,
efi_dp_from_eth());
if (r != EFI_SUCCESS)
- goto out_of_memory;
+ goto failure_to_add_protocol;
r = efi_add_protocol(netobj->parent.handle, &efi_pxe_guid,
&netobj->pxe);
if (r != EFI_SUCCESS)
- goto out_of_memory;
+ goto failure_to_add_protocol;
netobj->net.revision = EFI_SIMPLE_NETWORK_PROTOCOL_REVISION;
netobj->net.start = efi_net_start;
netobj->net.stop = efi_net_stop;
@@ -339,7 +373,7 @@ int efi_net_register(void)
* Create WaitForPacket event.
*/
r = efi_create_event(EVT_NOTIFY_WAIT, TPL_CALLBACK,
- efi_network_timer_notify, NULL,
+ efi_network_timer_notify, NULL, NULL,
&wait_for_packet);
if (r != EFI_SUCCESS) {
printf("ERROR: Failed to register network event\n");
@@ -351,9 +385,11 @@ int efi_net_register(void)
*
* The notification function is used to check if a new network packet
* has been received.
+ *
+ * iPXE is running at TPL_CALLBACK most of the time. Use a higher TPL.
*/
- r = efi_create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK,
- efi_network_timer_notify, NULL,
+ r = efi_create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_NOTIFY,
+ efi_network_timer_notify, NULL, NULL,
&network_timer_event);
if (r != EFI_SUCCESS) {
printf("ERROR: Failed to register network event\n");
@@ -366,8 +402,8 @@ int efi_net_register(void)
return r;
}
- return 0;
-out_of_memory:
- printf("ERROR: Out of memory\n");
- return 1;
+ return EFI_SUCCESS;
+failure_to_add_protocol:
+ printf("ERROR: Failure to add protocol\n");
+ return r;
}
diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c
index ccb4fc6141..8558124c0a 100644
--- a/lib/efi_loader/efi_runtime.c
+++ b/lib/efi_loader/efi_runtime.c
@@ -74,12 +74,24 @@ static void EFIAPI efi_reset_system_boottime(
efi_status_t reset_status,
unsigned long data_size, void *reset_data)
{
+ struct efi_event *evt;
+
EFI_ENTRY("%d %lx %lx %p", reset_type, reset_status, data_size,
reset_data);
+ /* Notify reset */
+ list_for_each_entry(evt, &efi_events, link) {
+ if (evt->group &&
+ !guidcmp(evt->group,
+ &efi_guid_event_group_reset_system)) {
+ efi_signal_event(evt, false);
+ break;
+ }
+ }
switch (reset_type) {
case EFI_RESET_COLD:
case EFI_RESET_WARM:
+ case EFI_RESET_PLATFORM_SPECIFIC:
do_reset(NULL, 0, 0, NULL);
break;
case EFI_RESET_SHUTDOWN:
@@ -134,8 +146,9 @@ void __weak __efi_runtime EFIAPI efi_reset_system(
while (1) { }
}
-void __weak efi_reset_system_init(void)
+efi_status_t __weak efi_reset_system_init(void)
{
+ return EFI_SUCCESS;
}
efi_status_t __weak __efi_runtime EFIAPI efi_get_time(
@@ -146,8 +159,9 @@ efi_status_t __weak __efi_runtime EFIAPI efi_get_time(
return EFI_DEVICE_ERROR;
}
-void __weak efi_get_time_init(void)
+efi_status_t __weak efi_get_time_init(void)
{
+ return EFI_SUCCESS;
}
struct efi_runtime_detach_list_struct {
@@ -332,18 +346,26 @@ static efi_status_t EFIAPI efi_set_virtual_address_map(
return EFI_EXIT(EFI_INVALID_PARAMETER);
}
-void efi_add_runtime_mmio(void *mmio_ptr, u64 len)
+efi_status_t efi_add_runtime_mmio(void *mmio_ptr, u64 len)
{
struct efi_runtime_mmio_list *newmmio;
-
u64 pages = (len + EFI_PAGE_MASK) >> EFI_PAGE_SHIFT;
- efi_add_memory_map(*(uintptr_t *)mmio_ptr, pages, EFI_MMAP_IO, false);
+ uint64_t addr = *(uintptr_t *)mmio_ptr;
+ uint64_t retaddr;
+
+ retaddr = efi_add_memory_map(addr, pages, EFI_MMAP_IO, false);
+ if (retaddr != addr)
+ return EFI_OUT_OF_RESOURCES;
newmmio = calloc(1, sizeof(*newmmio));
+ if (!newmmio)
+ return EFI_OUT_OF_RESOURCES;
newmmio->ptr = mmio_ptr;
newmmio->paddr = *(uintptr_t *)mmio_ptr;
newmmio->len = len;
list_add_tail(&newmmio->link, &efi_runtime_mmio);
+
+ return EFI_SUCCESS;
}
/*
diff --git a/lib/efi_loader/efi_smbios.c b/lib/efi_loader/efi_smbios.c
index ac412e7362..62e9697902 100644
--- a/lib/efi_loader/efi_smbios.c
+++ b/lib/efi_loader/efi_smbios.c
@@ -13,20 +13,27 @@
static const efi_guid_t smbios_guid = SMBIOS_TABLE_GUID;
-void efi_smbios_register(void)
+/*
+ * Install the SMBIOS table as a configuration table.
+ *
+ * @return status code
+ */
+efi_status_t efi_smbios_register(void)
{
/* Map within the low 32 bits, to allow for 32bit SMBIOS tables */
- uint64_t dmi = 0xffffffff;
- /* Reserve 4kb for SMBIOS */
- uint64_t pages = 1;
- int memtype = EFI_RUNTIME_SERVICES_DATA;
+ u64 dmi = U32_MAX;
+ efi_status_t ret;
- if (efi_allocate_pages(1, memtype, pages, &dmi) != EFI_SUCCESS)
- return;
+ /* Reserve 4kiB page for SMBIOS */
+ ret = efi_allocate_pages(EFI_ALLOCATE_MAX_ADDRESS,
+ EFI_RUNTIME_SERVICES_DATA, 1, &dmi);
+ if (ret != EFI_SUCCESS)
+ return ret;
/* Generate SMBIOS tables */
write_smbios_table(dmi);
/* And expose them to our EFI payload */
- efi_install_configuration_table(&smbios_guid, (void*)(uintptr_t)dmi);
+ return efi_install_configuration_table(&smbios_guid,
+ (void *)(uintptr_t)dmi);
}
diff --git a/lib/efi_loader/efi_watchdog.c b/lib/efi_loader/efi_watchdog.c
index 35a45dedf8..d12e51da0a 100644
--- a/lib/efi_loader/efi_watchdog.c
+++ b/lib/efi_loader/efi_watchdog.c
@@ -59,7 +59,7 @@ efi_status_t efi_set_watchdog(unsigned long timeout)
*
* This function is called by efi_init_obj_list()
*/
-int efi_watchdog_register(void)
+efi_status_t efi_watchdog_register(void)
{
efi_status_t r;
@@ -67,7 +67,7 @@ int efi_watchdog_register(void)
* Create a timer event.
*/
r = efi_create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK,
- efi_watchdog_timer_notify, NULL,
+ efi_watchdog_timer_notify, NULL, NULL,
&watchdog_timer_event);
if (r != EFI_SUCCESS) {
printf("ERROR: Failed to register watchdog event\n");
@@ -85,5 +85,5 @@ int efi_watchdog_register(void)
printf("ERROR: Failed to set watchdog timer\n");
return r;
}
- return 0;
+ return EFI_SUCCESS;
}
diff --git a/lib/efi_loader/helloworld.c b/lib/efi_loader/helloworld.c
index 1ec0179226..6c539ba204 100644
--- a/lib/efi_loader/helloworld.c
+++ b/lib/efi_loader/helloworld.c
@@ -46,9 +46,27 @@ efi_status_t EFIAPI efi_main(efi_handle_t handle,
struct efi_loaded_image *loaded_image;
efi_status_t ret;
efi_uintn_t i;
+ u16 rev[] = L"0.0.0";
con_out->output_string(con_out, L"Hello, world!\n");
+ /* Print the revision number */
+ rev[0] = (systable->hdr.revision >> 16) + '0';
+ rev[4] = systable->hdr.revision & 0xffff;
+ for (; rev[4] >= 10;) {
+ rev[4] -= 10;
+ ++rev[2];
+ }
+ /* Third digit is only to be shown if non-zero */
+ if (rev[4])
+ rev[4] += '0';
+ else
+ rev[3] = 0;
+
+ con_out->output_string(con_out, L"Running on UEFI ");
+ con_out->output_string(con_out, rev);
+ con_out->output_string(con_out, L"\n");
+
/* Get the loaded image protocol */
ret = boottime->handle_protocol(handle, &loaded_image_guid,
(void **)&loaded_image);
diff --git a/lib/efi_selftest/Makefile b/lib/efi_selftest/Makefile
index c4bdbdf6c0..31b444fc8b 100644
--- a/lib/efi_selftest/Makefile
+++ b/lib/efi_selftest/Makefile
@@ -14,14 +14,18 @@ CFLAGS_REMOVE_efi_selftest_miniapp_return.o := $(CFLAGS_NON_EFI) -Os
obj-$(CONFIG_CMD_BOOTEFI_SELFTEST) += \
efi_selftest.o \
+efi_selftest_bitblt.o \
efi_selftest_controllers.o \
efi_selftest_console.o \
efi_selftest_devicepath.o \
efi_selftest_events.o \
+efi_selftest_event_groups.o \
efi_selftest_exitbootservices.o \
+efi_selftest_fdt.o \
efi_selftest_gop.o \
efi_selftest_manageprotocols.o \
efi_selftest_snp.o \
+efi_selftest_textinput.o \
efi_selftest_textoutput.o \
efi_selftest_tpl.o \
efi_selftest_util.o \
diff --git a/lib/efi_selftest/efi_selftest_bitblt.c b/lib/efi_selftest/efi_selftest_bitblt.c
new file mode 100644
index 0000000000..0fb76cc727
--- /dev/null
+++ b/lib/efi_selftest/efi_selftest_bitblt.c
@@ -0,0 +1,311 @@
+/*
+ * efi_selftest_bitblt
+ *
+ * Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * Test the block image transfer in the graphical output protocol.
+ * An animated submarine is shown.
+ */
+
+#include <efi_selftest.h>
+
+#define WIDTH 200
+#define HEIGHT 120
+#define DEPTH 60
+
+static const struct efi_gop_pixel BLACK = { 0, 0, 0, 0};
+static const struct efi_gop_pixel RED = { 0, 0, 255, 0};
+static const struct efi_gop_pixel ORANGE = { 0, 128, 255, 0};
+static const struct efi_gop_pixel YELLOW = { 0, 255, 255, 0};
+static const struct efi_gop_pixel GREEN = { 0, 255, 0, 0};
+static const struct efi_gop_pixel DARK_BLUE = {128, 0, 0, 0};
+static const struct efi_gop_pixel LIGHT_BLUE = {255, 192, 192, 0};
+
+static struct efi_boot_services *boottime;
+static efi_guid_t efi_gop_guid = EFI_GOP_GUID;
+static struct efi_gop *gop;
+static struct efi_gop_pixel *bitmap;
+static struct efi_event *event;
+static efi_uintn_t xpos;
+
+static void ellipse(efi_uintn_t x, efi_uintn_t y,
+ efi_uintn_t x0, efi_uintn_t y0,
+ efi_uintn_t x1, efi_uintn_t y1,
+ const struct efi_gop_pixel col, struct efi_gop_pixel *pix)
+{
+ efi_uintn_t xm = x0 + x1;
+ efi_uintn_t ym = y0 + y1;
+ efi_uintn_t dx = x1 - x0 + 1;
+ efi_uintn_t dy = y1 - y0 + 1;
+
+ if (dy * dy * (2 * x - xm) * (2 * x - xm) +
+ dx * dx * (2 * y - ym) * (2 * y - ym) <= dx * dx * dy * dy)
+ *pix = col;
+}
+
+static void rectangle(efi_uintn_t x, efi_uintn_t y,
+ efi_uintn_t x0, efi_uintn_t y0,
+ efi_uintn_t x1, efi_uintn_t y1,
+ const struct efi_gop_pixel col, struct efi_gop_pixel *pix)
+{
+ if (x >= x0 && y >= y0 && x <= x1 && y <= y1)
+ *pix = col;
+}
+
+/*
+ * Notification function, copies image to video.
+ * The position is incremented in each call.
+ *
+ * @event notified event
+ * @context pointer to the notification count
+ */
+static void EFIAPI notify(struct efi_event *event, void *context)
+{
+ efi_uintn_t *pos = context;
+ efi_uintn_t dx, sx, width;
+
+ if (!pos)
+ return;
+
+ /* Increment position */
+ *pos += 5;
+ if (*pos >= WIDTH + gop->mode->info->width)
+ *pos = 0;
+
+ width = WIDTH;
+ dx = *pos - WIDTH;
+ sx = 0;
+ if (*pos >= gop->mode->info->width) {
+ width = WIDTH + gop->mode->info->width - *pos;
+ } else if (*pos < WIDTH) {
+ dx = 0;
+ sx = WIDTH - *pos;
+ width = *pos;
+ }
+
+ /* Copy image to video */
+ gop->blt(gop, bitmap, EFI_BLT_BUFFER_TO_VIDEO, sx, 0, dx, DEPTH,
+ width, HEIGHT, WIDTH * sizeof(struct efi_gop_pixel));
+}
+
+/*
+ * Setup unit test.
+ *
+ * @handle: handle of the loaded image
+ * @systable: system table
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int setup(const efi_handle_t handle,
+ const struct efi_system_table *systable)
+{
+ efi_status_t ret;
+ struct efi_gop_pixel pix;
+ efi_uintn_t x, y;
+
+ boottime = systable->boottime;
+
+ /* Create event */
+ ret = boottime->create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL,
+ TPL_CALLBACK, notify, (void *)&xpos,
+ &event);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("could not create event\n");
+ return EFI_ST_FAILURE;
+ }
+
+ /* Get graphical output protocol */
+ ret = boottime->locate_protocol(&efi_gop_guid, NULL, (void **)&gop);
+ if (ret != EFI_SUCCESS) {
+ gop = NULL;
+ efi_st_printf("Graphical output protocol is not available.\n");
+ return EFI_ST_SUCCESS;
+ }
+
+ /* Prepare image of submarine */
+ ret = boottime->allocate_pool(EFI_LOADER_DATA,
+ sizeof(struct efi_gop_pixel) *
+ WIDTH * HEIGHT, (void **)&bitmap);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Out of memory\n");
+ return EFI_ST_FAILURE;
+ }
+ for (y = 0; y < HEIGHT; ++y) {
+ for (x = 0; x < WIDTH; ++x) {
+ pix = DARK_BLUE;
+
+ /* Propeller */
+ ellipse(x, y, 35, 55, 43, 75, BLACK, &pix);
+ ellipse(x, y, 36, 56, 42, 74, LIGHT_BLUE, &pix);
+
+ ellipse(x, y, 35, 75, 43, 95, BLACK, &pix);
+ ellipse(x, y, 36, 76, 42, 94, LIGHT_BLUE, &pix);
+
+ /* Shaft */
+ rectangle(x, y, 35, 73, 100, 77, BLACK, &pix);
+
+ /* Periscope */
+ ellipse(x, y, 120, 10, 160, 50, BLACK, &pix);
+ ellipse(x, y, 121, 11, 159, 59, YELLOW, &pix);
+ ellipse(x, y, 130, 20, 150, 40, BLACK, &pix);
+ ellipse(x, y, 131, 21, 149, 49, DARK_BLUE, &pix);
+ rectangle(x, y, 135, 10, 160, 50, DARK_BLUE, &pix);
+ ellipse(x, y, 132, 10, 138, 20, BLACK, &pix);
+ ellipse(x, y, 133, 11, 139, 19, RED, &pix);
+
+ /* Rudder */
+ ellipse(x, y, 45, 40, 75, 70, BLACK, &pix);
+ ellipse(x, y, 46, 41, 74, 69, ORANGE, &pix);
+ ellipse(x, y, 45, 80, 75, 109, BLACK, &pix);
+ ellipse(x, y, 46, 81, 74, 108, RED, &pix);
+
+ /* Bridge */
+ ellipse(x, y, 100, 30, 120, 50, BLACK, &pix);
+ ellipse(x, y, 101, 31, 119, 49, GREEN, &pix);
+ ellipse(x, y, 140, 30, 160, 50, BLACK, &pix);
+ ellipse(x, y, 141, 31, 159, 49, GREEN, &pix);
+ rectangle(x, y, 110, 30, 150, 50, BLACK, &pix);
+ rectangle(x, y, 110, 31, 150, 50, GREEN, &pix);
+
+ /* Hull */
+ ellipse(x, y, 50, 40, 199, 109, BLACK, &pix);
+ ellipse(x, y, 51, 41, 198, 108, LIGHT_BLUE, &pix);
+
+ /* Port holes */
+ ellipse(x, y, 79, 57, 109, 82, BLACK, &pix);
+ ellipse(x, y, 80, 58, 108, 81, LIGHT_BLUE, &pix);
+ ellipse(x, y, 83, 61, 105, 78, BLACK, &pix);
+ ellipse(x, y, 84, 62, 104, 77, YELLOW, &pix);
+ /*
+ * This port hole is created by copying
+ * ellipse(x, y, 119, 57, 149, 82, BLACK, &pix);
+ * ellipse(x, y, 120, 58, 148, 81, LIGHT_BLUE, &pix);
+ * ellipse(x, y, 123, 61, 145, 78, BLACK, &pix);
+ * ellipse(x, y, 124, 62, 144, 77, YELLOW, &pix);
+ */
+ ellipse(x, y, 159, 57, 189, 82, BLACK, &pix);
+ ellipse(x, y, 160, 58, 188, 81, LIGHT_BLUE, &pix);
+ ellipse(x, y, 163, 61, 185, 78, BLACK, &pix);
+ ellipse(x, y, 164, 62, 184, 77, YELLOW, &pix);
+
+ bitmap[WIDTH * y + x] = pix;
+ }
+ }
+
+ return EFI_ST_SUCCESS;
+}
+
+/*
+ * Tear down unit test.
+ *
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int teardown(void)
+{
+ efi_status_t ret;
+
+ if (bitmap) {
+ ret = boottime->free_pool(bitmap);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("FreePool failed\n");
+ return EFI_ST_FAILURE;
+ }
+ }
+ if (event) {
+ ret = boottime->close_event(event);
+ event = NULL;
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("could not close event\n");
+ return EFI_ST_FAILURE;
+ }
+ }
+ return EFI_ST_SUCCESS;
+}
+
+/*
+ * Execute unit test.
+ *
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int execute(void)
+{
+ u32 max_mode;
+ efi_status_t ret;
+ struct efi_gop_mode_info *info;
+
+ if (!gop)
+ return EFI_ST_SUCCESS;
+
+ if (!gop->mode) {
+ efi_st_error("EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE missing\n");
+ return EFI_ST_FAILURE;
+ }
+ info = gop->mode->info;
+ max_mode = gop->mode->max_mode;
+ if (!max_mode) {
+ efi_st_error("No graphical mode available\n");
+ return EFI_ST_FAILURE;
+ }
+
+ /* Fill background */
+ ret = gop->blt(gop, bitmap, EFI_BLT_VIDEO_FILL, 0, 0, 0, 0,
+ info->width, info->height, 0);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("EFI_BLT_VIDEO_FILL failed\n");
+ return EFI_ST_FAILURE;
+ }
+
+ /* Copy image to video */
+ ret = gop->blt(gop, bitmap, EFI_BLT_BUFFER_TO_VIDEO, 0, 0, 0, DEPTH,
+ WIDTH, HEIGHT, 0);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("EFI_BLT_BUFFER_TO_VIDEO failed\n");
+ return EFI_ST_FAILURE;
+ }
+
+ /* Copy left port hole */
+ ret = gop->blt(gop, bitmap, EFI_BLT_VIDEO_TO_VIDEO,
+ 79, 57 + DEPTH, 119, 57 + DEPTH,
+ 31, 26, 0);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("EFI_BLT_VIDEO_TO_VIDEO failed\n");
+ return EFI_ST_FAILURE;
+ }
+
+ /* Copy port holes back to buffer */
+ ret = gop->blt(gop, bitmap, EFI_BLT_VIDEO_TO_BLT_BUFFER,
+ 94, 57 + DEPTH, 94, 57,
+ 90, 26, WIDTH * sizeof(struct efi_gop_pixel));
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("EFI_BLT_VIDEO_TO_BLT_BUFFER failed\n");
+ return EFI_ST_FAILURE;
+ }
+
+ /* Set 250ms timer */
+ xpos = WIDTH;
+ ret = boottime->set_timer(event, EFI_TIMER_PERIODIC, 250000);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Could not set timer\n");
+ return EFI_ST_FAILURE;
+ }
+
+ con_out->set_cursor_position(con_out, 0, 0);
+ con_out->set_attribute(con_out, EFI_WHITE | EFI_BACKGROUND_BLUE);
+ efi_st_printf("The submarine should have three yellow port holes.\n");
+ efi_st_printf("Press any key to continue");
+ efi_st_get_key();
+ con_out->set_attribute(con_out, EFI_LIGHTGRAY);
+ efi_st_printf("\n");
+
+ return EFI_ST_SUCCESS;
+}
+
+EFI_UNIT_TEST(bitblt) = {
+ .name = "block image transfer",
+ .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
+ .setup = setup,
+ .execute = execute,
+ .teardown = teardown,
+ .on_request = true,
+};
diff --git a/lib/efi_selftest/efi_selftest_block_device.c b/lib/efi_selftest/efi_selftest_block_device.c
index 9e4b93d9a6..a8979ed56b 100644
--- a/lib/efi_selftest/efi_selftest_block_device.c
+++ b/lib/efi_selftest/efi_selftest_block_device.c
@@ -29,6 +29,7 @@ static const efi_guid_t block_io_protocol_guid = BLOCK_IO_GUID;
static const efi_guid_t guid_device_path = DEVICE_PATH_GUID;
static const efi_guid_t guid_simple_file_system_protocol =
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
+static const efi_guid_t guid_file_system_info = EFI_FILE_SYSTEM_INFO_GUID;
static efi_guid_t guid_vendor =
EFI_GUID(0xdbca4c98, 0x6cb0, 0x694d,
0x08, 0x72, 0x81, 0x9c, 0x65, 0x0c, 0xb7, 0xb8);
@@ -302,7 +303,11 @@ static int execute(void)
struct efi_device_path *dp_partition;
struct efi_simple_file_system_protocol *file_system;
struct efi_file_handle *root, *file;
- u64 buf_size;
+ struct {
+ struct efi_file_system_info info;
+ u16 label[12];
+ } system_info;
+ efi_uintn_t buf_size;
char buf[16] __aligned(ARCH_DMA_MINALIGN);
ret = boottime->connect_controller(disk_handle, NULL, NULL, 1);
@@ -356,6 +361,23 @@ static int execute(void)
efi_st_error("Failed to open volume\n");
return EFI_ST_FAILURE;
}
+ buf_size = sizeof(system_info);
+ ret = root->getinfo(root, &guid_file_system_info, &buf_size,
+ &system_info);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Failed to get file system info\n");
+ return EFI_ST_FAILURE;
+ }
+ if (system_info.info.block_size != 512) {
+ efi_st_error("Wrong block size %u, expected 512\n",
+ system_info.info.block_size);
+ return EFI_ST_FAILURE;
+ }
+ if (efi_st_strcmp_16_8(system_info.info.volume_label, "U-BOOT TEST")) {
+ efi_st_todo(
+ "Wrong volume label '%ps', expected 'U-BOOT TEST'\n",
+ system_info.info.volume_label);
+ }
ret = root->open(root, &file, (s16 *)L"hello.txt", EFI_FILE_MODE_READ,
0);
if (ret != EFI_SUCCESS) {
diff --git a/lib/efi_selftest/efi_selftest_disk_image.h b/lib/efi_selftest/efi_selftest_disk_image.h
index 4775dace70..9c741ce136 100644
--- a/lib/efi_selftest/efi_selftest_disk_image.h
+++ b/lib/efi_selftest/efi_selftest_disk_image.h
@@ -3,21 +3,21 @@
*
* Generated with tools/file2include
*
- * SPDX-License-Identifier: GPL-2.0+
+ * SPDX-License-Identifier: GPL-2.0+
*/
#define EFI_ST_DISK_IMG { 0x00010000, { \
- {0x000001b8, "\x94\x37\x69\xfc\x00\x00\x00\x00"}, /* .7i..... */ \
- {0x000001c0, "\x02\x00\x83\x02\x02\x00\x01\x00"}, /* ........ */ \
+ {0x000001b8, "\x21\x5d\x53\xd1\x00\x00\x00\x00"}, /* !]S..... */ \
+ {0x000001c0, "\x02\x00\x01\x02\x02\x00\x01\x00"}, /* ........ */ \
{0x000001c8, "\x00\x00\x7f\x00\x00\x00\x00\x00"}, /* ........ */ \
{0x000001f8, "\x00\x00\x00\x00\x00\x00\x55\xaa"}, /* ......U. */ \
{0x00000200, "\xeb\x3c\x90\x6d\x6b\x66\x73\x2e"}, /* .<.mkfs. */ \
{0x00000208, "\x66\x61\x74\x00\x02\x04\x01\x00"}, /* fat..... */ \
{0x00000210, "\x02\x00\x02\x7f\x00\xf8\x01\x00"}, /* ........ */ \
{0x00000218, "\x20\x00\x40\x00\x00\x00\x00\x00"}, /* .@..... */ \
- {0x00000220, "\x00\x00\x00\x00\x80\x00\x29\x86"}, /* ......). */ \
- {0x00000228, "\xe8\x82\x80\x4e\x4f\x20\x4e\x41"}, /* ...NO NA */ \
- {0x00000230, "\x4d\x45\x20\x20\x20\x20\x46\x41"}, /* ME FA */ \
+ {0x00000220, "\x00\x00\x00\x00\x80\x00\x29\xc4"}, /* ......). */ \
+ {0x00000228, "\xc4\x88\x11\x55\x2d\x42\x4f\x4f"}, /* ...U-BOO */ \
+ {0x00000230, "\x54\x20\x54\x45\x53\x54\x46\x41"}, /* T TESTFA */ \
{0x00000238, "\x54\x31\x32\x20\x20\x20\x0e\x1f"}, /* T12 .. */ \
{0x00000240, "\xbe\x5b\x7c\xac\x22\xc0\x74\x0b"}, /* .[|.".t. */ \
{0x00000248, "\x56\xb4\x0e\xbb\x07\x00\xcd\x10"}, /* V....... */ \
@@ -36,34 +36,20 @@
{0x000002b0, "\x72\x79\x20\x61\x67\x61\x69\x6e"}, /* ry again */ \
{0x000002b8, "\x20\x2e\x2e\x2e\x20\x0d\x0a\x00"}, /* ... ... */ \
{0x000003f8, "\x00\x00\x00\x00\x00\x00\x55\xaa"}, /* ......U. */ \
- {0x00000400, "\xf8\xff\xff\x00\x00\x00\x00\xf0"}, /* ........ */ \
- {0x00000408, "\xff\x00\x00\x00\x00\x00\x00\x00"}, /* ........ */ \
- {0x00000600, "\xf8\xff\xff\x00\x00\x00\x00\xf0"}, /* ........ */ \
- {0x00000608, "\xff\x00\x00\x00\x00\x00\x00\x00"}, /* ........ */ \
- {0x00000800, "\xe5\x70\x00\x00\x00\xff\xff\xff"}, /* .p...... */ \
- {0x00000808, "\xff\xff\xff\x0f\x00\x0e\xff\xff"}, /* ........ */ \
- {0x00000810, "\xff\xff\xff\xff\xff\xff\xff\xff"}, /* ........ */ \
- {0x00000818, "\xff\xff\x00\x00\xff\xff\xff\xff"}, /* ........ */ \
- {0x00000820, "\xe5\x2e\x00\x68\x00\x65\x00\x6c"}, /* ...h.e.l */ \
- {0x00000828, "\x00\x6c\x00\x0f\x00\x0e\x6f\x00"}, /* .l....o. */ \
- {0x00000830, "\x2e\x00\x74\x00\x78\x00\x74\x00"}, /* ..t.x.t. */ \
- {0x00000838, "\x2e\x00\x00\x00\x73\x00\x77\x00"}, /* ....s.w. */ \
- {0x00000840, "\xe5\x45\x4c\x4c\x4f\x54\x7e\x31"}, /* .ELLOT~1 */ \
- {0x00000848, "\x53\x57\x50\x20\x00\x64\xd0\x8a"}, /* SWP .d.. */ \
- {0x00000850, "\x92\x4b\x92\x4b\x00\x00\xd0\x8a"}, /* .K.K.... */ \
- {0x00000858, "\x92\x4b\x00\x00\x00\x00\x00\x00"}, /* .K...... */ \
- {0x00000860, "\x41\x68\x00\x65\x00\x6c\x00\x6c"}, /* Ah.e.l.l */ \
- {0x00000868, "\x00\x6f\x00\x0f\x00\xf1\x2e\x00"}, /* .o...... */ \
- {0x00000870, "\x74\x00\x78\x00\x74\x00\x00\x00"}, /* t.x.t... */ \
- {0x00000878, "\xff\xff\x00\x00\xff\xff\xff\xff"}, /* ........ */ \
- {0x00000880, "\x48\x45\x4c\x4c\x4f\x20\x20\x20"}, /* HELLO */ \
- {0x00000888, "\x54\x58\x54\x20\x00\x64\xd4\x8a"}, /* TXT .d.. */ \
- {0x00000890, "\x92\x4b\x92\x4b\x00\x00\xd4\x8a"}, /* .K.K.... */ \
- {0x00000898, "\x92\x4b\x05\x00\x0d\x00\x00\x00"}, /* .K...... */ \
- {0x000008a0, "\xe5\x45\x4c\x4c\x4f\x54\x7e\x31"}, /* .ELLOT~1 */ \
- {0x000008a8, "\x53\x57\x58\x20\x00\x64\xd0\x8a"}, /* SWX .d.. */ \
- {0x000008b0, "\x92\x4b\x92\x4b\x00\x00\xd0\x8a"}, /* .K.K.... */ \
- {0x000008b8, "\x92\x4b\x00\x00\x00\x00\x00\x00"}, /* .K...... */ \
- {0x00006000, "\x48\x65\x6c\x6c\x6f\x20\x77\x6f"}, /* Hello wo */ \
- {0x00006008, "\x72\x6c\x64\x21\x0a\x00\x00\x00"}, /* rld!.... */ \
+ {0x00000400, "\xf8\xff\xff\x00\xf0\xff\x00\x00"}, /* ........ */ \
+ {0x00000600, "\xf8\xff\xff\x00\xf0\xff\x00\x00"}, /* ........ */ \
+ {0x00000800, "\x55\x2d\x42\x4f\x4f\x54\x20\x54"}, /* U-BOOT T */ \
+ {0x00000808, "\x45\x53\x54\x08\x00\x00\xaa\x56"}, /* EST....V */ \
+ {0x00000810, "\x84\x4c\x84\x4c\x00\x00\xaa\x56"}, /* .L.L...V */ \
+ {0x00000818, "\x84\x4c\x00\x00\x00\x00\x00\x00"}, /* .L...... */ \
+ {0x00000820, "\x41\x68\x00\x65\x00\x6c\x00\x6c"}, /* Ah.e.l.l */ \
+ {0x00000828, "\x00\x6f\x00\x0f\x00\xf1\x2e\x00"}, /* .o...... */ \
+ {0x00000830, "\x74\x00\x78\x00\x74\x00\x00\x00"}, /* t.x.t... */ \
+ {0x00000838, "\xff\xff\x00\x00\xff\xff\xff\xff"}, /* ........ */ \
+ {0x00000840, "\x48\x45\x4c\x4c\x4f\x20\x20\x20"}, /* HELLO */ \
+ {0x00000848, "\x54\x58\x54\x20\x00\x64\xd7\x46"}, /* TXT .d.F */ \
+ {0x00000850, "\x84\x4c\x84\x4c\x00\x00\xd7\x46"}, /* .L.L...F */ \
+ {0x00000858, "\x84\x4c\x03\x00\x0d\x00\x00\x00"}, /* .L...... */ \
+ {0x00005000, "\x48\x65\x6c\x6c\x6f\x20\x77\x6f"}, /* Hello wo */ \
+ {0x00005008, "\x72\x6c\x64\x21\x0a\x00\x00\x00"}, /* rld!.... */ \
{0, NULL} } }
diff --git a/lib/efi_selftest/efi_selftest_event_groups.c b/lib/efi_selftest/efi_selftest_event_groups.c
new file mode 100644
index 0000000000..79e4ea1ce2
--- /dev/null
+++ b/lib/efi_selftest/efi_selftest_event_groups.c
@@ -0,0 +1,140 @@
+/*
+ * efi_selftest_event_groups
+ *
+ * Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This test checks the notification of group events and the
+ * following services:
+ * CreateEventEx, CloseEvent, SignalEvent, CheckEvent.
+ */
+
+#include <efi_selftest.h>
+
+#define GROUP_SIZE 16
+
+static struct efi_boot_services *boottime;
+static efi_guid_t event_group =
+ EFI_GUID(0x2335905b, 0xc3b9, 0x4221, 0xa3, 0x71,
+ 0x0e, 0x5b, 0x45, 0xc0, 0x56, 0x91);
+
+/*
+ * Notification function, increments the notfication count if parameter
+ * context is provided.
+ *
+ * @event notified event
+ * @context pointer to the notification count
+ */
+static void EFIAPI notify(struct efi_event *event, void *context)
+{
+ unsigned int *count = context;
+
+ if (count)
+ ++*count;
+}
+
+/*
+ * Setup unit test.
+ *
+ * @handle: handle of the loaded image
+ * @systable: system table
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int setup(const efi_handle_t handle,
+ const struct efi_system_table *systable)
+{
+ boottime = systable->boottime;
+
+ return EFI_ST_SUCCESS;
+}
+
+/*
+ * Execute unit test.
+ *
+ * Create multiple events in an event group. Signal each event once and check
+ * that all events are notified once in each round.
+ *
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int execute(void)
+{
+ unsigned int counter[GROUP_SIZE] = {0};
+ struct efi_event *events[GROUP_SIZE];
+ size_t i, j;
+ efi_status_t ret;
+
+ for (i = 0; i < GROUP_SIZE; ++i) {
+ ret = boottime->create_event_ex(0, TPL_NOTIFY,
+ notify, (void *)&counter[i],
+ &event_group, &events[i]);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Failed to create event\n");
+ return EFI_ST_FAILURE;
+ }
+ }
+
+ for (i = 0; i < GROUP_SIZE; ++i) {
+ ret = boottime->signal_event(events[i]);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Failed to signal event\n");
+ return EFI_ST_FAILURE;
+ }
+ for (j = 0; j < GROUP_SIZE; ++j) {
+ if (counter[j] != i) {
+ efi_st_printf("i %u, j %u, count %u\n",
+ (unsigned int)i, (unsigned int)j,
+ (unsigned int)counter[j]);
+ efi_st_error(
+ "Notification function was called\n");
+ return EFI_ST_FAILURE;
+ }
+ /* Clear signaled state */
+ ret = boottime->check_event(events[j]);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Event was not signaled\n");
+ return EFI_ST_FAILURE;
+ }
+ if (counter[j] != i) {
+ efi_st_printf("i %u, j %u, count %u\n",
+ (unsigned int)i, (unsigned int)j,
+ (unsigned int)counter[j]);
+ efi_st_error(
+ "Notification function was called\n");
+ return EFI_ST_FAILURE;
+ }
+ /* Call notification function */
+ ret = boottime->check_event(events[j]);
+ if (ret != EFI_NOT_READY) {
+ efi_st_error(
+ "Signaled state not cleared\n");
+ return EFI_ST_FAILURE;
+ }
+ if (counter[j] != i + 1) {
+ efi_st_printf("i %u, j %u, count %u\n",
+ (unsigned int)i, (unsigned int)j,
+ (unsigned int)counter[j]);
+ efi_st_error(
+ "Nofification function not called\n");
+ return EFI_ST_FAILURE;
+ }
+ }
+ }
+
+ for (i = 0; i < GROUP_SIZE; ++i) {
+ ret = boottime->close_event(events[i]);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Failed to close event\n");
+ return EFI_ST_FAILURE;
+ }
+ }
+
+ return EFI_ST_SUCCESS;
+}
+
+EFI_UNIT_TEST(eventgoups) = {
+ .name = "event groups",
+ .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
+ .setup = setup,
+ .execute = execute,
+};
diff --git a/lib/efi_selftest/efi_selftest_fdt.c b/lib/efi_selftest/efi_selftest_fdt.c
new file mode 100644
index 0000000000..e5a8d6a6ae
--- /dev/null
+++ b/lib/efi_selftest/efi_selftest_fdt.c
@@ -0,0 +1,188 @@
+/*
+ * efi_selftest_pos
+ *
+ * Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * Test the EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.
+ *
+ * The following services are tested:
+ * OutputString, TestString, SetAttribute.
+ */
+
+#include <efi_selftest.h>
+#include <linux/libfdt.h>
+
+static struct efi_boot_services *boottime;
+static const char *fdt;
+
+/* This should be sufficent for */
+#define BUFFERSIZE 0x100000
+
+static efi_guid_t fdt_guid = EFI_FDT_GUID;
+
+/*
+ * Convert FDT value to host endianness.
+ *
+ * @val FDT value
+ * @return converted value
+ */
+static uint32_t f2h(fdt32_t val)
+{
+ char *buf = (char *)&val;
+ char i;
+
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+ /* Swap the bytes */
+ i = buf[0]; buf[0] = buf[3]; buf[3] = i;
+ i = buf[1]; buf[1] = buf[2]; buf[2] = i;
+#endif
+ return *(uint32_t *)buf;
+}
+
+/*
+ * Return the value of a property of the FDT root node.
+ *
+ * @name name of the property
+ * @return value of the property
+ */
+static char *get_property(const u16 *property)
+{
+ struct fdt_header *header = (struct fdt_header *)fdt;
+ const fdt32_t *pos;
+ const char *strings;
+
+ if (!header)
+ return NULL;
+
+ if (f2h(header->magic) != FDT_MAGIC) {
+ printf("Wrong magic\n");
+ return NULL;
+ }
+
+ pos = (fdt32_t *)(fdt + f2h(header->off_dt_struct));
+ strings = fdt + f2h(header->off_dt_strings);
+
+ for (;;) {
+ switch (f2h(pos[0])) {
+ case FDT_BEGIN_NODE: {
+ char *c = (char *)&pos[1];
+ size_t i;
+
+ for (i = 0; c[i]; ++i)
+ ;
+ pos = &pos[2 + (i >> 2)];
+ break;
+ }
+ case FDT_PROP: {
+ struct fdt_property *prop = (struct fdt_property *)pos;
+ const char *label = &strings[f2h(prop->nameoff)];
+ efi_status_t ret;
+
+ /* Check if this is the property to be returned */
+ if (!efi_st_strcmp_16_8(property, label)) {
+ char *str;
+ efi_uintn_t len = f2h(prop->len);
+
+ if (!len)
+ return NULL;
+ /*
+ * The string might not be 0 terminated.
+ * It is safer to make a copy.
+ */
+ ret = boottime->allocate_pool(
+ EFI_LOADER_DATA, len + 1,
+ (void **)&str);
+ if (ret != EFI_SUCCESS) {
+ efi_st_printf("AllocatePool failed\n");
+ return NULL;
+ }
+ boottime->copy_mem(str, &pos[3], len);
+ str[len] = 0;
+
+ return str;
+ }
+
+ pos = &pos[3 + ((f2h(prop->len) + 3) >> 2)];
+ break;
+ }
+ case FDT_NOP:
+ pos = &pos[1];
+ break;
+ default:
+ return NULL;
+ }
+ }
+}
+
+/*
+ * Setup unit test.
+ *
+ * @handle: handle of the loaded image
+ * @systable: system table
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int setup(const efi_handle_t img_handle,
+ const struct efi_system_table *systable)
+{
+ efi_uintn_t i;
+
+ boottime = systable->boottime;
+
+ /* Find configuration tables */
+ for (i = 0; i < systable->nr_tables; ++i) {
+ if (!efi_st_memcmp(&systable->tables[i].guid, &fdt_guid,
+ sizeof(efi_guid_t)))
+ fdt = systable->tables[i].table;
+ }
+ if (!fdt) {
+ efi_st_error("Missing device tree\n");
+ return EFI_ST_FAILURE;
+ }
+
+ return EFI_ST_SUCCESS;
+}
+
+/*
+ * Execute unit test.
+ *
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int execute(void)
+{
+ char *str;
+ efi_status_t ret;
+
+ str = get_property(L"compatible");
+ if (str) {
+ efi_st_printf("compatible: %s\n", str);
+ ret = boottime->free_pool(str);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("FreePool failed\n");
+ return EFI_ST_FAILURE;
+ }
+ } else {
+ efi_st_printf("Missing property 'compatible'\n");
+ return EFI_ST_FAILURE;
+ }
+ str = get_property(L"serial-number");
+ if (str) {
+ efi_st_printf("serial-number: %s\n", str);
+ ret = boottime->free_pool(str);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("FreePool failed\n");
+ return EFI_ST_FAILURE;
+ }
+ }
+
+ return EFI_ST_SUCCESS;
+}
+
+EFI_UNIT_TEST(fdt) = {
+ .name = "device tree",
+ .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
+ .setup = setup,
+ .execute = execute,
+ .on_request = true,
+};
diff --git a/lib/efi_selftest/efi_selftest_textinput.c b/lib/efi_selftest/efi_selftest_textinput.c
new file mode 100644
index 0000000000..c890ff88b7
--- /dev/null
+++ b/lib/efi_selftest/efi_selftest_textinput.c
@@ -0,0 +1,182 @@
+/*
+ * efi_selftest_textinput
+ *
+ * Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * Provides a unit test for the EFI_SIMPLE_TEXT_INPUT_PROTOCOL.
+ * The unicode character and the scan code are printed for text
+ * input. To run the test:
+ *
+ * setenv efi_selftest text input
+ * bootefi selftest
+ */
+
+#include <efi_selftest.h>
+
+struct translate {
+ u16 code;
+ u16 *text;
+};
+
+static struct efi_boot_services *boottime;
+
+static struct translate control_characters[] = {
+ {0, L"Null"},
+ {8, L"BS"},
+ {9, L"TAB"},
+ {10, L"LF"},
+ {13, L"CR"},
+ {0, NULL},
+};
+
+static u16 ch[] = L"' '";
+static u16 unknown[] = L"unknown";
+
+static struct translate scan_codes[] = {
+ {0x00, L"Null"},
+ {0x01, L"Up"},
+ {0x02, L"Down"},
+ {0x03, L"Right"},
+ {0x04, L"Left"},
+ {0x05, L"Home"},
+ {0x06, L"End"},
+ {0x07, L"Insert"},
+ {0x08, L"Delete"},
+ {0x09, L"Page Up"},
+ {0x0a, L"Page Down"},
+ {0x0b, L"FN 1"},
+ {0x0c, L"FN 2"},
+ {0x0d, L"FN 3"},
+ {0x0e, L"FN 4"},
+ {0x0f, L"FN 5"},
+ {0x10, L"FN 6"},
+ {0x11, L"FN 7"},
+ {0x12, L"FN 8"},
+ {0x13, L"FN 9"},
+ {0x14, L"FN 10"},
+ {0x15, L"FN 11"},
+ {0x16, L"FN 12"},
+ {0x17, L"Escape"},
+ {0x68, L"FN 13"},
+ {0x69, L"FN 14"},
+ {0x6a, L"FN 15"},
+ {0x6b, L"FN 16"},
+ {0x6c, L"FN 17"},
+ {0x6d, L"FN 18"},
+ {0x6e, L"FN 19"},
+ {0x6f, L"FN 20"},
+ {0x70, L"FN 21"},
+ {0x71, L"FN 22"},
+ {0x72, L"FN 23"},
+ {0x73, L"FN 24"},
+ {0x7f, L"Mute"},
+ {0x80, L"Volume Up"},
+ {0x81, L"Volume Down"},
+ {0x100, L"Brightness Up"},
+ {0x101, L"Brightness Down"},
+ {0x102, L"Suspend"},
+ {0x103, L"Hibernate"},
+ {0x104, L"Toggle Display"},
+ {0x105, L"Recovery"},
+ {0x106, L"Reject"},
+ {0x0, NULL},
+};
+
+/*
+ * Translate a unicode character to a string.
+ *
+ * @code unicode character
+ * @return string
+ */
+static u16 *translate_char(u16 code)
+{
+ struct translate *tr;
+
+ if (code >= ' ') {
+ ch[1] = code;
+ return ch;
+ }
+ for (tr = control_characters; tr->text; ++tr) {
+ if (tr->code == code)
+ return tr->text;
+ }
+ return unknown;
+}
+
+/*
+ * Translate a scan code to a human readable string.
+ *
+ * @code unicode character
+ * @return string
+ */
+static u16 *translate_code(u16 code)
+{
+ struct translate *tr;
+
+ for (tr = scan_codes; tr->text; ++tr) {
+ if (tr->code == code)
+ return tr->text;
+ }
+ return unknown;
+}
+
+/*
+ * Setup unit test.
+ *
+ * @handle: handle of the loaded image
+ * @systable: system table
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int setup(const efi_handle_t handle,
+ const struct efi_system_table *systable)
+{
+ boottime = systable->boottime;
+
+ return EFI_ST_SUCCESS;
+}
+
+/*
+ * Execute unit test.
+ *
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int execute(void)
+{
+ struct efi_input_key input_key = {0};
+ efi_status_t ret;
+
+ efi_st_printf("Waiting for your input\n");
+ efi_st_printf("To terminate type 'x'\n");
+
+ for (;;) {
+ /* Wait for next key */
+ do {
+ ret = con_in->read_key_stroke(con_in, &input_key);
+ } while (ret == EFI_NOT_READY);
+
+ /* Allow 5 minutes until time out */
+ boottime->set_watchdog_timer(300, 0, 0, NULL);
+
+ efi_st_printf("Unicode char %u (%ps), scan code %u (%ps)\n",
+ (unsigned int)input_key.unicode_char,
+ translate_char(input_key.unicode_char),
+ (unsigned int)input_key.scan_code,
+ translate_code(input_key.scan_code));
+
+ switch (input_key.unicode_char) {
+ case 'x':
+ case 'X':
+ return EFI_ST_SUCCESS;
+ }
+ }
+}
+
+EFI_UNIT_TEST(textinput) = {
+ .name = "text input",
+ .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
+ .setup = setup,
+ .execute = execute,
+ .on_request = true,
+};