diff options
Diffstat (limited to 'lib')
28 files changed, 2016 insertions, 364 deletions
diff --git a/lib/Kconfig b/lib/Kconfig index 4fd41c4282..436b90fa85 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -66,7 +66,6 @@ config PANIC_HANG config REGEX bool "Enable regular expression support" - default n if ARCH_SUNXI default y if NET help If this variable is defined, U-Boot is linked against the @@ -310,5 +309,6 @@ endmenu source lib/efi/Kconfig source lib/efi_loader/Kconfig +source lib/optee/Kconfig endmenu diff --git a/lib/Makefile b/lib/Makefile index 0db41c19f3..35da5705a4 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -18,6 +18,7 @@ obj-$(CONFIG_FIT) += libfdt/ obj-$(CONFIG_OF_LIVE) += of_live.o obj-$(CONFIG_CMD_DHRYSTONE) += dhry/ obj-$(CONFIG_ARCH_AT91) += at91/ +obj-$(CONFIG_OPTEE) += optee/ obj-$(CONFIG_AES) += aes.o obj-y += charset.o diff --git a/lib/efi/efi_stub.c b/lib/efi/efi_stub.c index 2e8d409d31..205aa19947 100644 --- a/lib/efi/efi_stub.c +++ b/lib/efi/efi_stub.c @@ -182,7 +182,7 @@ static int get_codeseg32(void) << 16; base <<= 12; /* 4KB granularity */ limit <<= 12; - if ((desc & GDT_PRESENT) && (desc && GDT_NOTSYS) && + if ((desc & GDT_PRESENT) && (desc & GDT_NOTSYS) && !(desc & GDT_LONG) && (desc & GDT_4KB) && (desc & GDT_32BIT) && (desc & GDT_CODE) && CONFIG_SYS_TEXT_BASE > base && diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile index 2a87d9ed77..55c97c0476 100644 --- a/lib/efi_loader/Makefile +++ b/lib/efi_loader/Makefile @@ -17,9 +17,10 @@ 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 -obj-$(CONFIG_CMD_NET) += efi_net.o +obj-$(CONFIG_NET) += efi_net.o obj-$(CONFIG_GENERATE_SMBIOS_TABLE) += efi_smbios.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..e965f1d88e 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; } @@ -746,10 +747,12 @@ struct efi_device_path *efi_dp_from_file(struct blk_desc *desc, int part, return start; } -#ifdef CONFIG_CMD_NET +#ifdef CONFIG_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, +}; diff --git a/lib/libfdt/fdt_region.c b/lib/libfdt/fdt_region.c index 70914a4e9b..054c4b302f 100644 --- a/lib/libfdt/fdt_region.c +++ b/lib/libfdt/fdt_region.c @@ -14,8 +14,6 @@ #include "fdt_host.h" #endif -#include "libfdt_internal.h" - #define FDT_MAX_DEPTH 32 static int str_in_list(const char *str, char * const list[], int count) diff --git a/lib/optee/Kconfig b/lib/optee/Kconfig new file mode 100644 index 0000000000..1e5ab45c3d --- /dev/null +++ b/lib/optee/Kconfig @@ -0,0 +1,39 @@ +config OPTEE + bool "Support OPTEE images" + help + U-Boot can be configured to boot OPTEE images. + Selecting this option will enable shared OPTEE library code and + enable an OPTEE specific bootm command that will perform additional + OPTEE specific checks before booting an OPTEE image created with + mkimage. + +config OPTEE_LOAD_ADDR + hex "OPTEE load address" + default 0x00000000 + help + The load address of the bootable OPTEE binary. + +config OPTEE_TZDRAM_SIZE + hex "Amount of Trust-Zone RAM for the OPTEE image" + depends on OPTEE + default 0x3000000 + help + The size of pre-allocated Trust Zone DRAM to allocate for the OPTEE + runtime. + +config OPTEE_TZDRAM_BASE + hex "Base address of Trust-Zone RAM for the OPTEE image" + depends on OPTEE + default 0x9d000000 + help + The base address of pre-allocated Trust Zone DRAM for + the OPTEE runtime. + +config BOOTM_OPTEE + bool "Support OPTEE bootm command" + select BOOTM_LINUX + default n + help + Select this command to enable chain-loading of a Linux kernel + via an OPTEE firmware. + The bootflow is BootROM -> u-boot -> OPTEE -> Linux in this case. diff --git a/lib/optee/Makefile b/lib/optee/Makefile new file mode 100644 index 0000000000..03e832f349 --- /dev/null +++ b/lib/optee/Makefile @@ -0,0 +1,7 @@ +# +# (C) Copyright 2017 Linaro +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_OPTEE) += optee.o diff --git a/lib/optee/optee.c b/lib/optee/optee.c new file mode 100644 index 0000000000..78a15e8a32 --- /dev/null +++ b/lib/optee/optee.c @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2017 Linaro + * Bryan O'Donoghue <bryan.odonoghue@linaro.org> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <tee/optee.h> + +#define optee_hdr_err_msg \ + "OPTEE verification error:" \ + "\n\thdr=%p image=0x%08lx magic=0x%08x tzdram 0x%08lx-0x%08lx " \ + "\n\theader lo=0x%08x hi=0x%08x size=0x%08lx arch=0x%08x" \ + "\n\tuimage params 0x%08lx-0x%08lx\n" + +int optee_verify_image(struct optee_header *hdr, unsigned long tzdram_start, + unsigned long tzdram_len, unsigned long image_len) +{ + unsigned long tzdram_end = tzdram_start + tzdram_len; + uint32_t tee_file_size; + + tee_file_size = hdr->init_size + hdr->paged_size + + sizeof(struct optee_header); + + if (hdr->magic != OPTEE_MAGIC || + hdr->version != OPTEE_VERSION || + hdr->init_load_addr_hi > tzdram_end || + hdr->init_load_addr_lo < tzdram_start || + tee_file_size > tzdram_len || + tee_file_size != image_len || + (hdr->init_load_addr_lo + tee_file_size) > tzdram_end) { + return -EINVAL; + } + + return 0; +} + +int optee_verify_bootm_image(unsigned long image_addr, + unsigned long image_load_addr, + unsigned long image_len) +{ + struct optee_header *hdr = (struct optee_header *)image_addr; + unsigned long tzdram_start = CONFIG_OPTEE_TZDRAM_BASE; + unsigned long tzdram_len = CONFIG_OPTEE_TZDRAM_SIZE; + + int ret; + + ret = optee_verify_image(hdr, tzdram_start, tzdram_len, image_len); + if (ret) + goto error; + + if (image_load_addr + sizeof(*hdr) != hdr->init_load_addr_lo) { + ret = -EINVAL; + goto error; + } + + return ret; +error: + printf(optee_hdr_err_msg, hdr, image_addr, hdr->magic, tzdram_start, + tzdram_start + tzdram_len, hdr->init_load_addr_lo, + hdr->init_load_addr_hi, image_len, hdr->arch, image_load_addr, + image_load_addr + image_len); + + return ret; +} |