From 28a4fd46e7c6507d788677406a8bd385e0cb35eb Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Wed, 7 Mar 2018 02:40:51 +0100 Subject: efi_loader: parameter checks for LoadImage Add parameter checks in efi_load_image(). Check memory allocation is successful in efi_load_image(). Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- lib/efi_loader/efi_boottime.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'lib') diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 66e26fd63a1..8892c86f419 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -1515,8 +1515,27 @@ static efi_status_t EFIAPI efi_load_image(bool boot_policy, EFI_ENTRY("%d, %p, %pD, %p, %ld, %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 +1571,7 @@ static efi_status_t EFIAPI efi_load_image(bool boot_policy, failure: free(info); efi_delete_handle(obj); +error: return EFI_EXIT(ret); } -- cgit From 8396e3fd63e8c8e17d60a6d1992ca3df10bc8d66 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 3 Mar 2018 15:41:37 +0100 Subject: efi_loader: remove deprecated ConsoleControlProtocol The console control protocol is not defined in the UEFI standard. It exists in EDK2's EdkCompatiblityPkg package. But this package is deprecated according to https://github.com/tianocore/tianocore.github.io/wiki/Differences-between-EDK-and-EDK-II Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- lib/efi_loader/efi_boottime.c | 5 ----- lib/efi_loader/efi_console.c | 46 ------------------------------------------- 2 files changed, 51 deletions(-) (limited to 'lib') diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 8892c86f419..71c244ea805 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -1420,11 +1420,6 @@ 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); - 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); diff --git a/lib/efi_loader/efi_console.c b/lib/efi_loader/efi_console.c index 28d63635ec7..3cb580e3eda 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, @@ -506,18 +468,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; -- cgit From e70f8dfa2ce2f47a6eed891016a4c6f289da1cb6 Mon Sep 17 00:00:00 2001 From: Leif Lindholm Date: Fri, 9 Mar 2018 17:43:21 +0100 Subject: efi_loader: Initial EFI_DEVICE_PATH_UTILITIES_PROTOCOL Not complete, but enough for Shell.efi and SCT.efi. We'll implement the rest as needed or once we have SCT running properly so there is a way to validate the interface against the conformance test suite. Initial skeleton written by Leif, and then implementation by Rob. Signed-off-by: Leif Lindholm [Fill initial skeleton] Signed-off-by: Rob Clark [Rebase on v2018.03-rc1] Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- lib/efi_loader/Makefile | 3 +- lib/efi_loader/efi_boottime.c | 6 ++ lib/efi_loader/efi_device_path_utilities.c | 89 ++++++++++++++++++++++++++++++ 3 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 lib/efi_loader/efi_device_path_utilities.c (limited to 'lib') diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile index 2a87d9ed776..d2ce89713ec 100644 --- a/lib/efi_loader/Makefile +++ b/lib/efi_loader/Makefile @@ -17,7 +17,8 @@ endif obj-$(CONFIG_CMD_BOOTEFI_HELLO) += helloworld_efi.o obj-y += efi_image_loader.o efi_boottime.o efi_runtime.o efi_console.o obj-y += efi_memory.o efi_device_path_to_text.o efi_device_path.o -obj-y += efi_file.o efi_variable.o efi_bootmgr.o efi_watchdog.o +obj-y += efi_device_path_utilities.o efi_file.o efi_variable.o efi_bootmgr.o +obj-y += efi_watchdog.o obj-$(CONFIG_LCD) += efi_gop.o obj-$(CONFIG_DM_VIDEO) += efi_gop.o obj-$(CONFIG_PARTITIONS) += efi_disk.o diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 71c244ea805..4e133e9b47d 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -1426,6 +1426,12 @@ efi_status_t efi_setup_loaded_image( if (ret != EFI_SUCCESS) goto failure; + ret = efi_add_protocol(obj->handle, + &efi_guid_device_path_utilities_protocol, + (void *)&efi_device_path_utilities); + if (ret != EFI_SUCCESS) + goto failure; + return ret; failure: printf("ERROR: Failure to install protocols for loaded image\n"); 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 00000000000..bc97eeee31b --- /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 +#include + +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, +}; -- cgit From 7657152bddd131e47390c2b2d6f63d58149d79f1 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 3 Mar 2018 15:28:54 +0100 Subject: efi_loader: efi_smbios_register should have a return value Errors may occur inside efi_smbios_register(). - Return a status code. - Remove unused variables. - Use constants where applicable. Suggested-by: Simon Glass Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- lib/efi_loader/efi_smbios.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) (limited to 'lib') diff --git a/lib/efi_loader/efi_smbios.c b/lib/efi_loader/efi_smbios.c index ac412e7362a..62e96979021 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); } -- cgit From 80ea9b09900bc35a21468f5422bd36a345ae7eda Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 3 Mar 2018 15:28:55 +0100 Subject: efi_loader: return efi_status_t from efi_gop_register All initialization routines should return a status code instead of a boolean. Signed-off-by: Heinrich Schuchardt [agraf: Convert warnings to debug() prints] Signed-off-by: Alexander Graf --- lib/efi_loader/efi_gop.c | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) (limited to 'lib') diff --git a/lib/efi_loader/efi_gop.c b/lib/efi_loader/efi_gop.c index 3caddd5f844..28818eb781a 100644 --- a/lib/efi_loader/efi_gop.c +++ b/lib/efi_loader/efi_gop.c @@ -125,8 +125,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 +141,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 +178,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 +195,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 +208,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 +227,5 @@ int efi_gop_register(void) gopobj->bpix = bpix; gopobj->fb = fb; - return 0; + return EFI_SUCCESS; } -- cgit From 075d425d652038e67136db61ace4606fcec89475 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 3 Mar 2018 15:28:56 +0100 Subject: efi_loader: return efi_status_t from efi_net_register Consistently return status codes form efi_net_register(). Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- lib/efi_loader/efi_net.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) (limited to 'lib') diff --git a/lib/efi_loader/efi_net.c b/lib/efi_loader/efi_net.c index 8c5d5b492ca..e7fed794502 100644 --- a/lib/efi_loader/efi_net.c +++ b/lib/efi_loader/efi_net.c @@ -280,20 +280,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 +304,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; @@ -366,8 +368,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; } -- cgit From d7b181d57d0c7080facce755c4b9ff7a22436a48 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 3 Mar 2018 15:28:57 +0100 Subject: efi_loader: consistently return efi_status_t efi_watchdog_register efi_watchdog_register() should always return a status code and not a boolean value. Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- lib/efi_loader/efi_watchdog.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/efi_loader/efi_watchdog.c b/lib/efi_loader/efi_watchdog.c index 35a45dedf84..b1c35a8e29d 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; @@ -85,5 +85,5 @@ int efi_watchdog_register(void) printf("ERROR: Failed to set watchdog timer\n"); return r; } - return 0; + return EFI_SUCCESS; } -- cgit From 22c793e6a26505fdf80cb5b099142dd6f8f0fff9 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 3 Mar 2018 15:28:59 +0100 Subject: efi_loader: exit status for efi_reset_system_init efi_reset_system_init provides the architecture or board specific initialization of the EFI subsystem. Errors should be caught and signalled by a return code. Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- lib/efi_loader/efi_runtime.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c index ccb4fc6141b..85f85711ddc 100644 --- a/lib/efi_loader/efi_runtime.c +++ b/lib/efi_loader/efi_runtime.c @@ -134,8 +134,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( @@ -332,18 +333,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; + efi_status_t ret; u64 pages = (len + EFI_PAGE_MASK) >> EFI_PAGE_SHIFT; - efi_add_memory_map(*(uintptr_t *)mmio_ptr, pages, EFI_MMAP_IO, false); + ret = efi_add_memory_map(*(uintptr_t *)mmio_ptr, pages, EFI_MMAP_IO, + false); + if (ret != EFI_SUCCESS) + return ret; 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 ret; } /* -- cgit From 14ad49d100a0ba4abafedf8ca6459b699c9d0fa1 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 3 Mar 2018 15:29:00 +0100 Subject: efi_loader: efi_get_time_init should return status code All EFI initialization functions should return a status code. Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- lib/efi_loader/efi_runtime.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c index 85f85711ddc..c59d161c8fe 100644 --- a/lib/efi_loader/efi_runtime.c +++ b/lib/efi_loader/efi_runtime.c @@ -147,8 +147,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 { -- cgit From bc4f9133edb4baf5f68689a03137bb2c336e2026 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 3 Mar 2018 15:29:03 +0100 Subject: efi_loader: support device tree for bootefi selftest The second argument of the bootefi command should always be usable to specify a device tree. This was missing for bootefi selftest and bootefi hello. Proper error handling is added. Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- lib/efi_loader/efi_boottime.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'lib') diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 4e133e9b47d..3cee8d607c8 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -56,6 +56,8 @@ 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; -- cgit From 06c3d5b989d15a86e2b0a5d49e0bcda365d35baa Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 3 Mar 2018 15:29:04 +0100 Subject: efi_selftest: check installation of the device tree The unit test checks if a device tree is installed. It requires that the 'compatible' property of the root node exists. If available it prints the 'serial-number' property. The serial-number property is derived from the environment variable 'serial#'. This can be used to check if the image_setup_libfdt() function is executed. A Python test is supplied. It sets a value for serial# and checks that the selftest shows this as serial-number. Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- lib/efi_selftest/Makefile | 1 + lib/efi_selftest/efi_selftest_fdt.c | 188 ++++++++++++++++++++++++++++++++++++ 2 files changed, 189 insertions(+) create mode 100644 lib/efi_selftest/efi_selftest_fdt.c (limited to 'lib') diff --git a/lib/efi_selftest/Makefile b/lib/efi_selftest/Makefile index c4bdbdf6c05..88c44840d52 100644 --- a/lib/efi_selftest/Makefile +++ b/lib/efi_selftest/Makefile @@ -19,6 +19,7 @@ efi_selftest_console.o \ efi_selftest_devicepath.o \ efi_selftest_events.o \ efi_selftest_exitbootservices.o \ +efi_selftest_fdt.o \ efi_selftest_gop.o \ efi_selftest_manageprotocols.o \ efi_selftest_snp.o \ diff --git a/lib/efi_selftest/efi_selftest_fdt.c b/lib/efi_selftest/efi_selftest_fdt.c new file mode 100644 index 00000000000..24db0dcf7d5 --- /dev/null +++ b/lib/efi_selftest/efi_selftest_fdt.c @@ -0,0 +1,188 @@ +/* + * efi_selftest_pos + * + * Copyright (c) 2018 Heinrich Schuchardt + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Test the EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL. + * + * The following services are tested: + * OutputString, TestString, SetAttribute. + */ + +#include +#include + +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, +}; -- cgit From 0fb4169e2819d6950708154789287e4ad9b40a91 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Mon, 19 Feb 2018 18:53:29 +0100 Subject: efi_loader: correct input of special keys Don't set unicode_char if scan_code is set. Add support for page up, page down, and insert. Correct input of function keys. Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- lib/efi_loader/efi_console.c | 104 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 94 insertions(+), 10 deletions(-) (limited to 'lib') diff --git a/lib/efi_loader/efi_console.c b/lib/efi_loader/efi_console.c index 3cb580e3eda..d0e8617d0d3 100644 --- a/lib/efi_loader/efi_console.c +++ b/lib/efi_loader/efi_console.c @@ -361,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) @@ -383,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'; @@ -407,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; @@ -426,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); -- cgit From eb68b4ef31cd7d0fe08becda7ac1e56b45f9054a Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sun, 18 Feb 2018 00:08:00 +0100 Subject: efi_loader: check parameter in InstallConfigurationTable Check that parameter guid is not NULL. This avoids a possible NULL pointer exception. Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- lib/efi_loader/efi_boottime.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'lib') diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 3cee8d607c8..9ca2e8161ec 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -1333,6 +1333,9 @@ efi_status_t efi_install_configuration_table(const efi_guid_t *guid, void *table { 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)) { -- cgit From 7069515e7f82fea281160de9c8af1bb857918a96 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sun, 18 Feb 2018 11:32:02 +0100 Subject: efi_loader: clear signaled state in CheckEvent CheckEvent must clear the signaled state. Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- lib/efi_loader/efi_boottime.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 9ca2e8161ec..8137ef521e4 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -818,7 +818,8 @@ 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 @@ -836,8 +837,10 @@ static efi_status_t EFIAPI efi_check_event(struct efi_event *event) break; if (!event->is_signaled) efi_signal_event(event, true); - if (event->is_signaled) + if (event->is_signaled) { + event->is_signaled = false; return EFI_EXIT(EFI_SUCCESS); + } return EFI_EXIT(EFI_NOT_READY); } return EFI_EXIT(EFI_INVALID_PARAMETER); -- cgit From ab9efa979cc989d7c193e7169101e29ff7822e85 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sun, 18 Feb 2018 15:17:49 +0100 Subject: efi_loader: fix formatting errors Fix formatting errors in efi_boottime.c indicated by scripts/checkpatch.py. Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- lib/efi_loader/efi_boottime.c | 48 +++++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 22 deletions(-) (limited to 'lib') diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 8137ef521e4..1a2154591cb 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -123,6 +123,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]; } @@ -257,7 +258,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); } @@ -586,7 +587,6 @@ static efi_status_t EFIAPI efi_create_event_ext( notify_context, event)); } - /* * Check if a timer event has occurred or a queued notification function should * be called. @@ -688,7 +688,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)); } @@ -1264,7 +1264,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) { @@ -1315,7 +1315,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); @@ -1332,7 +1332,8 @@ 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) { int i; @@ -1664,8 +1665,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 @@ -1678,7 +1680,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); @@ -1815,7 +1817,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); @@ -1856,7 +1859,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)); } @@ -1921,8 +1924,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) { @@ -2907,15 +2910,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 = { @@ -2925,11 +2929,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, }; -- cgit From 43bce44262ca7cbb04bb236c50571ca84eb6fdd9 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sun, 18 Feb 2018 15:17:50 +0100 Subject: efi_loader: manage events in a linked list Lift the limit on the number of events by using a linked list. This also allows to have events with type == 0. This patch is based on Rob's patch efi_loader: fix events https://lists.denx.de/pipermail/u-boot/2017-October/309348.html Suggested-by: Rob Clark Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- lib/efi_loader/efi_boottime.c | 192 +++++++++++++++++++----------------------- 1 file changed, 87 insertions(+), 105 deletions(-) (limited to 'lib') diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 1a2154591cb..ca43925be44 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 */ +static 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 @@ -473,10 +476,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. @@ -499,7 +515,7 @@ efi_status_t efi_create_event(uint32_t type, efi_uintn_t notify_tpl, void *context), void *notify_context, struct efi_event **event) { - int i; + struct efi_event *evt; if (event == NULL) return EFI_INVALID_PARAMETER; @@ -507,25 +523,24 @@ efi_status_t efi_create_event(uint32_t type, efi_uintn_t notify_tpl, if ((type & EVT_NOTIFY_SIGNAL) && (type & EVT_NOTIFY_WAIT)) 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; + /* 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; } /* @@ -596,30 +611,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) + list_for_each_entry(evt, &efi_events, link) { + if (evt->is_queued) + efi_signal_event(evt, true); + if (!(evt->type & EVT_TIMER) || now < evt->trigger_next) 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) - 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 = true; + efi_signal_event(evt, true); } WATCHDOG_RESET(); } @@ -638,7 +649,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. @@ -646,30 +659,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; } /* @@ -708,7 +712,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); @@ -719,12 +723,8 @@ 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) @@ -768,18 +768,13 @@ 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; + if (efi_is_event(event) != EFI_SUCCESS) + return EFI_EXIT(EFI_INVALID_PARAMETER); + if (!event->is_signaled) { event->is_signaled = true; if (event->type & EVT_NOTIFY_SIGNAL) efi_signal_event(event, true); - break; } return EFI_EXIT(EFI_SUCCESS); } @@ -796,19 +791,12 @@ static efi_status_t EFIAPI efi_signal_event_ext(struct efi_event *event) */ static efi_status_t EFIAPI efi_close_event(struct efi_event *event) { - 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); } /* @@ -826,24 +814,18 @@ static efi_status_t EFIAPI efi_close_event(struct efi_event *event) */ static efi_status_t EFIAPI efi_check_event(struct efi_event *event) { - 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) { - event->is_signaled = false; - 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_signal_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); } /* @@ -1755,7 +1737,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); @@ -1767,11 +1749,11 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, return EFI_EXIT(EFI_SUCCESS); /* Notify that ExitBootServices is invoked. */ - for (i = 0; i < ARRAY_SIZE(efi_events); ++i) { - if (efi_events[i].type != EVT_SIGNAL_EXIT_BOOT_SERVICES) + list_for_each_entry(evt, &efi_events, link) { + if (evt->type != EVT_SIGNAL_EXIT_BOOT_SERVICES) continue; - efi_events[i].is_signaled = true; - efi_signal_event(&efi_events[i], false); + evt->is_signaled = true; + efi_signal_event(evt, false); } /* TODO Should persist EFI variables here */ -- cgit From a3a28f5f0c39d94733d23e2bbbb53e6e4ddb46c6 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sun, 18 Feb 2018 15:17:51 +0100 Subject: efi_loader: define GUIDS for event groups Event groups are used to signal multiple events at the same time. They are identified by GUIDs. This patch provided the predefined GUIDs of UEFI specification 2.7. Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- lib/efi_loader/efi_boottime.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'lib') diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index ca43925be44..d9abb3ccedd 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -65,6 +65,22 @@ const efi_guid_t efi_guid_fdt = EFI_FDT_GUID; 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, -- cgit From b095f3c85fe08744d6081b98db65768f3421295e Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sun, 18 Feb 2018 15:17:52 +0100 Subject: efi_loader: implement event groups If an event of a group event is signaled all other events of the same group are signaled too. Function efi_signal_event is renamed to efi_queue_event. A new function efi_signal_event is introduced that checks if an event belongs to a group and than signals all events of the group. Event group notifciation is implemented for ExitBootServices, InstallConfigurationTable, and ResetSystem. Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- lib/efi_loader/efi_boottime.c | 99 ++++++++++++++++++++++++++++++++++--------- lib/efi_loader/efi_console.c | 6 +-- lib/efi_loader/efi_net.c | 4 +- lib/efi_loader/efi_runtime.c | 11 +++++ lib/efi_loader/efi_watchdog.c | 2 +- 5 files changed, 95 insertions(+), 27 deletions(-) (limited to 'lib') diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index d9abb3ccedd..1ff0568d474 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -27,7 +27,7 @@ static efi_uintn_t efi_tpl = TPL_APPLICATION; LIST_HEAD(efi_obj_list); /* List of all events */ -static LIST_HEAD(efi_events); +LIST_HEAD(efi_events); /* * If we're running on nasty systems (32bit ARM booting into non-EFI Linux) @@ -176,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; @@ -189,6 +189,50 @@ void efi_signal_event(struct efi_event *event, bool check_tpl) event->is_queued = false; } +/* + * 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. * @@ -529,7 +573,8 @@ 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) { struct efi_event *evt; @@ -550,6 +595,7 @@ efi_status_t efi_create_event(uint32_t type, efi_uintn_t notify_tpl, 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; @@ -585,10 +631,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)); } /* @@ -615,7 +659,7 @@ 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)); } /* @@ -632,7 +676,7 @@ void efi_timer_check(void) list_for_each_entry(evt, &efi_events, link) { if (evt->is_queued) - efi_signal_event(evt, true); + efi_queue_event(evt, true); if (!(evt->type & EVT_TIMER) || now < evt->trigger_next) continue; switch (evt->trigger_type) { @@ -645,7 +689,7 @@ void efi_timer_check(void) default: continue; } - evt->is_signaled = true; + evt->is_signaled = false; efi_signal_event(evt, true); } WATCHDOG_RESET(); @@ -744,7 +788,7 @@ static efi_status_t EFIAPI efi_wait_for_event(efi_uintn_t num_events, 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 */ @@ -787,11 +831,7 @@ static efi_status_t EFIAPI efi_signal_event_ext(struct efi_event *event) EFI_ENTRY("%p", event); if (efi_is_event(event) != EFI_SUCCESS) return EFI_EXIT(EFI_INVALID_PARAMETER); - if (!event->is_signaled) { - event->is_signaled = true; - if (event->type & EVT_NOTIFY_SIGNAL) - efi_signal_event(event, true); - } + efi_signal_event(event, true); return EFI_EXIT(EFI_SUCCESS); } @@ -836,7 +876,7 @@ static efi_status_t EFIAPI efi_check_event(struct efi_event *event) event->type & EVT_NOTIFY_SIGNAL) return EFI_EXIT(EFI_INVALID_PARAMETER); if (!event->is_signaled) - efi_signal_event(event, true); + efi_queue_event(event, true); if (event->is_signaled) { event->is_signaled = false; return EFI_EXIT(EFI_SUCCESS); @@ -1333,6 +1373,7 @@ static void efi_remove_configuration_table(int i) efi_status_t efi_install_configuration_table(const efi_guid_t *guid, void *table) { + struct efi_event *evt; int i; if (!guid) @@ -1345,7 +1386,7 @@ efi_status_t efi_install_configuration_table(const efi_guid_t *guid, efi_conf_table[i].table = table; else efi_remove_configuration_table(i); - return EFI_SUCCESS; + goto out; } } @@ -1361,6 +1402,15 @@ efi_status_t efi_install_configuration_table(const efi_guid_t *guid, 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; } @@ -1764,12 +1814,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. */ list_for_each_entry(evt, &efi_events, link) { - if (evt->type != EVT_SIGNAL_EXIT_BOOT_SERVICES) - continue; - evt->is_signaled = true; - efi_signal_event(evt, false); + 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 */ diff --git a/lib/efi_loader/efi_console.c b/lib/efi_loader/efi_console.c index d0e8617d0d3..5d1a9a8081e 100644 --- a/lib/efi_loader/efi_console.c +++ b/lib/efi_loader/efi_console.c @@ -572,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_net.c b/lib/efi_loader/efi_net.c index e7fed794502..b88dc91f58c 100644 --- a/lib/efi_loader/efi_net.c +++ b/lib/efi_loader/efi_net.c @@ -341,7 +341,7 @@ efi_status_t 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"); @@ -355,7 +355,7 @@ efi_status_t efi_net_register(void) * has been received. */ r = efi_create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK, - efi_network_timer_notify, NULL, + efi_network_timer_notify, NULL, NULL, &network_timer_event); if (r != EFI_SUCCESS) { printf("ERROR: Failed to register network event\n"); diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c index c59d161c8fe..9e281728f9f 100644 --- a/lib/efi_loader/efi_runtime.c +++ b/lib/efi_loader/efi_runtime.c @@ -74,9 +74,20 @@ 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: diff --git a/lib/efi_loader/efi_watchdog.c b/lib/efi_loader/efi_watchdog.c index b1c35a8e29d..d12e51da0a2 100644 --- a/lib/efi_loader/efi_watchdog.c +++ b/lib/efi_loader/efi_watchdog.c @@ -67,7 +67,7 @@ efi_status_t 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"); -- cgit From 0e0a3ceb509b19af55c5064607d9057ec7d8c1c4 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Wed, 7 Feb 2018 22:14:22 +0100 Subject: efi_loader: implement missing bit blit operations in gop With the patch all block image transfer operations of the EFI_GRAPHICS_OUTPUT_PROTOCOL are supported. Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- lib/efi_loader/efi_gop.c | 158 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 122 insertions(+), 36 deletions(-) (limited to 'lib') diff --git a/lib/efi_loader/efi_gop.c b/lib/efi_loader/efi_gop.c index 28818eb781a..154f3065406 100644 --- a/lib/efi_loader/efi_gop.c +++ b/lib/efi_loader/efi_gop.c @@ -56,64 +56,150 @@ 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, +static 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 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); +} + +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) { 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; + u32 *fb32 = gopobj->fb; + u16 *fb16 = gopobj->fb; + + if (delta) + linelen = delta; + 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); - if (operation != EFI_BLT_BUFFER_TO_VIDEO) + /* Check source rectangle */ + switch (operation) { + case EFI_BLT_VIDEO_FILL: + break; + case EFI_BLT_BUFFER_TO_VIDEO: + if (sx + width > linelen) + return EFI_EXIT(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_EXIT(EFI_INVALID_PARAMETER); + break; + default: return EFI_EXIT(EFI_INVALID_PARAMETER); + } - fb = gopobj->fb; - line_len16 = gopobj->info.width * sizeof(u16); - line_len32 = gopobj->info.width * sizeof(u32); + /* 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_EXIT(EFI_INVALID_PARAMETER); + break; + case EFI_BLT_VIDEO_TO_BLT_BUFFER: + if (dx + width > linelen) + return EFI_EXIT(EFI_INVALID_PARAMETER); + break; + } - /* Copy the contents line by line */ + 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[linelen * (i + sy) + j + sx]; + break; + case EFI_BLT_VIDEO_TO_BLT_BUFFER: + case EFI_BLT_VIDEO_TO_VIDEO: + switch (gopobj->bpix) { +#ifdef CONFIG_DM_VIDEO + case VIDEO_BPP32: +#else + case LCD_COLOR32: +#endif + pix = *(struct efi_gop_pixel *)&fb32[ + gopobj->info.width * + (i + sy) + j + sx]; + break; +#ifdef CONFIG_DM_VIDEO + case VIDEO_BPP16: +#else + case LCD_COLOR16: +#endif + pix = efi_vid16_to_blt_col(fb16[ + gopobj->info.width * + (i + sy) + j + sx]); + break; + default: + return EFI_EXIT(EFI_UNSUPPORTED); + } + break; + } - switch (gopobj->bpix) { + /* Write destination pixel */ + switch (operation) { + case EFI_BLT_VIDEO_TO_BLT_BUFFER: + buffer[linelen * (i + dy) + j + dx] = pix; + break; + case EFI_BLT_BUFFER_TO_VIDEO: + case EFI_BLT_VIDEO_FILL: + case EFI_BLT_VIDEO_TO_VIDEO: + switch (gopobj->bpix) { #ifdef CONFIG_DM_VIDEO - case VIDEO_BPP32: + case VIDEO_BPP32: #else - case LCD_COLOR32: + 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)); - } - break; + fb32[gopobj->info.width * + (i + dy) + j + dx] = *(u32 *)&pix; + break; #ifdef CONFIG_DM_VIDEO - case VIDEO_BPP16: + case VIDEO_BPP16: #else - case LCD_COLOR16: + 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)); + fb16[gopobj->info.width * + (i + dy) + j + dx] = + efi_blt_col_to_vid16(&pix); + break; + default: + return EFI_EXIT(EFI_UNSUPPORTED); + } + break; } } - break; } #ifdef CONFIG_DM_VIDEO -- cgit From b944e4711137a60064ef353a7d5972e59fe66c97 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Wed, 7 Feb 2018 22:14:23 +0100 Subject: efi_selftest: test gop bitblt The test checks all block image transfer operations of the graphical output protocol. An animated submarine is shown. To run the test: setenv efi_selftest bock image transfer bootefi selftest Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- lib/efi_selftest/Makefile | 1 + lib/efi_selftest/efi_selftest_bitblt.c | 311 +++++++++++++++++++++++++++++++++ 2 files changed, 312 insertions(+) create mode 100644 lib/efi_selftest/efi_selftest_bitblt.c (limited to 'lib') diff --git a/lib/efi_selftest/Makefile b/lib/efi_selftest/Makefile index 88c44840d52..b975da2955c 100644 --- a/lib/efi_selftest/Makefile +++ b/lib/efi_selftest/Makefile @@ -14,6 +14,7 @@ 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 \ diff --git a/lib/efi_selftest/efi_selftest_bitblt.c b/lib/efi_selftest/efi_selftest_bitblt.c new file mode 100644 index 00000000000..53cc633acc6 --- /dev/null +++ b/lib/efi_selftest/efi_selftest_bitblt.c @@ -0,0 +1,311 @@ +/* + * efi_selftest_bitblt + * + * Copyright (c) 2017 Heinrich Schuchardt + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Test the block image transfer in the graphical output protocol. + * An animated submarine is shown. + */ + +#include + +#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); +} + +/* + * 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); + 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, +}; -- cgit From 482fc90c0a93bdab8473bbd4c8d63732ce888bba Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Tue, 6 Feb 2018 22:00:22 +0100 Subject: efi_loader: add missing EFI_RESET_PLATFORM_SPECIFIC EFI_RESET_PLATFORM_SPECIFIC is one of the values that can be used for the EFI service ResetSystem. The missing definition is added. The value has to handled in efi_reset_system(). Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- lib/efi_loader/efi_runtime.c | 1 + 1 file changed, 1 insertion(+) (limited to 'lib') diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c index 9e281728f9f..08883161407 100644 --- a/lib/efi_loader/efi_runtime.c +++ b/lib/efi_loader/efi_runtime.c @@ -91,6 +91,7 @@ static void EFIAPI efi_reset_system_boottime( 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: -- cgit From 0aa2da788bb5c6dd249e33a8d3416f9d2ae9ee09 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 3 Mar 2018 15:39:53 +0100 Subject: efi_selftest: unit test for EFI_SIMPLE_TEXT_INPUT_PROTOCOL Provide 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 Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- lib/efi_selftest/Makefile | 1 + lib/efi_selftest/efi_selftest_textinput.c | 182 ++++++++++++++++++++++++++++++ 2 files changed, 183 insertions(+) create mode 100644 lib/efi_selftest/efi_selftest_textinput.c (limited to 'lib') diff --git a/lib/efi_selftest/Makefile b/lib/efi_selftest/Makefile index b975da2955c..2c442517040 100644 --- a/lib/efi_selftest/Makefile +++ b/lib/efi_selftest/Makefile @@ -24,6 +24,7 @@ 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_textinput.c b/lib/efi_selftest/efi_selftest_textinput.c new file mode 100644 index 00000000000..c890ff88b7e --- /dev/null +++ b/lib/efi_selftest/efi_selftest_textinput.c @@ -0,0 +1,182 @@ +/* + * efi_selftest_textinput + * + * Copyright (c) 2018 Heinrich Schuchardt + * + * 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 + +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, +}; -- cgit From f7c342f413a97c8fb097c3a0649aa5bbc2bcfd26 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Mon, 5 Feb 2018 18:24:26 +0100 Subject: efi_loader: show UEFI revision in helloworld Output the UEFI revision number in helloworld. Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- lib/efi_loader/helloworld.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'lib') diff --git a/lib/efi_loader/helloworld.c b/lib/efi_loader/helloworld.c index 1ec01792263..6c539ba2049 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); -- cgit From 7c92fd69b19bf4bfdc7c3fa74c504b66019004f0 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Tue, 30 Jan 2018 21:08:00 +0100 Subject: efi_loader: use constants in efi_allocate_pages() Using the existing predefined constants is less error prone and makes the code easier to read. Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- lib/efi_loader/efi_memory.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c index ff0edf30ffb..1e413d01cb9 100644 --- a/lib/efi_loader/efi_memory.c +++ b/lib/efi_loader/efi_memory.c @@ -292,7 +292,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 +300,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 +308,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; -- cgit From bdecaebd5d5e266301b4bc4064b5fbc0922874d2 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 8 Mar 2018 21:53:27 +0100 Subject: efi: Correct header order in efi_memory The headers are not in the correct order. Fix this. Also drop libfdt_env.h since it is not needed. Signed-off-by: Simon Glass Rebased Reviewed-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- lib/efi_loader/efi_memory.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c index 1e413d01cb9..95f9ff0a140 100644 --- a/lib/efi_loader/efi_memory.c +++ b/lib/efi_loader/efi_memory.c @@ -8,12 +8,11 @@ #include #include +#include #include +#include #include -#include #include -#include -#include DECLARE_GLOBAL_DATA_PTR; -- cgit From 9967adb71db685c0b45312f966e954d9fe5293c7 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Mon, 12 Mar 2018 19:52:25 +0100 Subject: efi_selftest: fix device tree unit test Include libfdt.h was moved by commit b08c8c487083 ("libfdt: move headers to and ") Fixes: e236200c7fa6 ("efi_selftest: check installation of the device tree") Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- lib/efi_selftest/efi_selftest_fdt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/efi_selftest/efi_selftest_fdt.c b/lib/efi_selftest/efi_selftest_fdt.c index 24db0dcf7d5..e5a8d6a6ae5 100644 --- a/lib/efi_selftest/efi_selftest_fdt.c +++ b/lib/efi_selftest/efi_selftest_fdt.c @@ -12,7 +12,7 @@ */ #include -#include +#include static struct efi_boot_services *boottime; static const char *fdt; -- cgit From 6fc901c538907f48d4253a6e23987df2268f75ae Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sun, 18 Feb 2018 15:17:53 +0100 Subject: efi_selftest: unit test for event groups Supply a unit test for event groups. Create multiple events in an event group. Signal each event once and check that all events are notified once in each round. Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- lib/efi_selftest/Makefile | 1 + lib/efi_selftest/efi_selftest_event_groups.c | 140 +++++++++++++++++++++++++++ 2 files changed, 141 insertions(+) create mode 100644 lib/efi_selftest/efi_selftest_event_groups.c (limited to 'lib') diff --git a/lib/efi_selftest/Makefile b/lib/efi_selftest/Makefile index 2c442517040..31b444fc8b1 100644 --- a/lib/efi_selftest/Makefile +++ b/lib/efi_selftest/Makefile @@ -19,6 +19,7 @@ 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 \ 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 00000000000..79e4ea1ce27 --- /dev/null +++ b/lib/efi_selftest/efi_selftest_event_groups.c @@ -0,0 +1,140 @@ +/* + * efi_selftest_event_groups + * + * Copyright (c) 2018 Heinrich Schuchardt + * + * SPDX-License-Identifier: GPL-2.0+ + * + * This test checks the notification of group events and the + * following services: + * CreateEventEx, CloseEvent, SignalEvent, CheckEvent. + */ + +#include + +#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, +}; -- cgit From 51a0f4512295429a8511d8fbce2e4d26d6bc1fcb Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Wed, 14 Mar 2018 19:57:02 +0100 Subject: efi_loader: correctly support parameter delta in Blt In the Blt service of the EFI_GRAPHICS_OUTPUT_PROTOCOL the parameter delta is measured in bytes and not in pixels. The coding only supports delta being a multiple of four. The UEFI specification does not explicitly require this but as pixels have a size of four bytes we should be able to assume four byte alignment. The corresponding unit test is corrected, too. It can be launched with setenv efi_selftest block image transfer bootefi selftest Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- lib/efi_loader/efi_gop.c | 32 +++++++++++++++++++++++++++----- lib/efi_selftest/efi_selftest_bitblt.c | 4 ++-- 2 files changed, 29 insertions(+), 7 deletions(-) (limited to 'lib') diff --git a/lib/efi_loader/efi_gop.c b/lib/efi_loader/efi_gop.c index 154f3065406..ac92109f16d 100644 --- a/lib/efi_loader/efi_gop.c +++ b/lib/efi_loader/efi_gop.c @@ -77,6 +77,24 @@ static inline u16 efi_blt_col_to_vid16(struct efi_gop_pixel *blt) (u16)(blt->blue >> 3); } +/* + * 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, @@ -88,14 +106,18 @@ efi_status_t EFIAPI gop_blt(struct efi_gop *this, struct efi_gop_pixel *buffer, u32 *fb32 = gopobj->fb; u16 *fb16 = gopobj->fb; - if (delta) - linelen = delta; - 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); + if (delta) { + /* Check for 4 byte alignment */ + if (delta & 3) + return EFI_EXIT(EFI_INVALID_PARAMETER); + linelen = delta >> 2; + } else { + linelen = width; + } + /* Check source rectangle */ switch (operation) { case EFI_BLT_VIDEO_FILL: diff --git a/lib/efi_selftest/efi_selftest_bitblt.c b/lib/efi_selftest/efi_selftest_bitblt.c index 53cc633acc6..0fb76cc727b 100644 --- a/lib/efi_selftest/efi_selftest_bitblt.c +++ b/lib/efi_selftest/efi_selftest_bitblt.c @@ -87,7 +87,7 @@ static void EFIAPI notify(struct efi_event *event, void *context) /* Copy image to video */ gop->blt(gop, bitmap, EFI_BLT_BUFFER_TO_VIDEO, sx, 0, dx, DEPTH, - width, HEIGHT, WIDTH); + width, HEIGHT, WIDTH * sizeof(struct efi_gop_pixel)); } /* @@ -276,7 +276,7 @@ static int execute(void) /* 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); + 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; -- cgit From ee3db4fc04714c80196e49f8f3a5f157f20d2862 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 24 Mar 2018 18:40:22 +0100 Subject: efi_loader: use TPL_NOTIFY for network timer event We use a timer to poll the network. iPXE is used for booting from iSCSI drives. It has been changed to run at TPL_CALLBACK most of the time (which is not what the UEFI spec recommends). By changing our timer to TPL_NOTIFY we can ensure that it is nevertheless executed. Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- lib/efi_loader/efi_net.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/efi_loader/efi_net.c b/lib/efi_loader/efi_net.c index b88dc91f58c..3d860e658e6 100644 --- a/lib/efi_loader/efi_net.c +++ b/lib/efi_loader/efi_net.c @@ -353,8 +353,10 @@ efi_status_t 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, + r = efi_create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_NOTIFY, efi_network_timer_notify, NULL, NULL, &network_timer_event); if (r != EFI_SUCCESS) { -- cgit From 0f7fcc72565ce411941a12de0fb0ea5538a17cd0 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 24 Mar 2018 18:40:21 +0100 Subject: efi_loader: RestoreTPL should execute queued events When the TPL is lowered queued events may become eligible for execution. iPXE uses the following pattern to request event execution: bs->RestoreTPL ( TPL_APPLICATION ); bs->RaiseTPL ( TPL_CALLBACK ); Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- lib/efi_loader/efi_boottime.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'lib') diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 1ff0568d474..fd35ffa359b 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -278,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); } -- cgit From 90b658b4cc9f6b9d50a80fac1fbc62bd6420a483 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Fri, 16 Mar 2018 19:59:06 +0100 Subject: efi_loader: use __always_inline for pixel conversion We optimize for size using -Os so gcc might ignore 'inline'. Pixel conversions are called so often that we always want to inline them. Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- lib/efi_loader/efi_gop.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/efi_loader/efi_gop.c b/lib/efi_loader/efi_gop.c index ac92109f16d..b0c3d59b85e 100644 --- a/lib/efi_loader/efi_gop.c +++ b/lib/efi_loader/efi_gop.c @@ -56,7 +56,7 @@ static efi_status_t EFIAPI gop_set_mode(struct efi_gop *this, u32 mode_number) return EFI_EXIT(EFI_SUCCESS); } -static inline struct efi_gop_pixel efi_vid16_to_blt_col(u16 vid) +static __always_inline struct efi_gop_pixel efi_vid16_to_blt_col(u16 vid) { struct efi_gop_pixel blt = { .reserved = 0, @@ -70,7 +70,7 @@ static inline struct efi_gop_pixel efi_vid16_to_blt_col(u16 vid) return blt; } -static inline u16 efi_blt_col_to_vid16(struct efi_gop_pixel *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 | -- cgit From ba718e67a27f036093d352af46b8ebcb1bbb8316 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Thu, 15 Mar 2018 15:02:28 +0100 Subject: efi_loader: Optimize GOP switch We usually try to compile for size, not for speed. Unfortunately with the more powerful GOP infrastructure to handle all sorts of GOP operations, we end up slowing down our copying hot path quite a lot. So this patch moves the 4 possible GOP operation modes into separate functions which call a common function again. The end result of that is more optimized code that can properly do constant propagation throughout its switch() statements and thus removes compares in the hot path. Signed-off-by: Alexander Graf --- lib/efi_loader/efi_gop.c | 160 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 126 insertions(+), 34 deletions(-) (limited to 'lib') diff --git a/lib/efi_loader/efi_gop.c b/lib/efi_loader/efi_gop.c index b0c3d59b85e..26d6f02353d 100644 --- a/lib/efi_loader/efi_gop.c +++ b/lib/efi_loader/efi_gop.c @@ -77,42 +77,24 @@ static __always_inline u16 efi_blt_col_to_vid16(struct efi_gop_pixel *blt) (u16)(blt->blue >> 3); } -/* - * 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) +static __always_inline efi_status_t gop_blt_int(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) { struct efi_gop_obj *gopobj = container_of(this, struct efi_gop_obj, ops); efi_uintn_t i, j, linelen; u32 *fb32 = gopobj->fb; u16 *fb16 = gopobj->fb; - EFI_ENTRY("%p, %p, %u, %zu, %zu, %zu, %zu, %zu, %zu, %zu", this, - buffer, operation, sx, sy, dx, dy, width, height, delta); - if (delta) { /* Check for 4 byte alignment */ if (delta & 3) - return EFI_EXIT(EFI_INVALID_PARAMETER); + return EFI_INVALID_PARAMETER; linelen = delta >> 2; } else { linelen = width; @@ -124,16 +106,16 @@ efi_status_t EFIAPI gop_blt(struct efi_gop *this, struct efi_gop_pixel *buffer, break; case EFI_BLT_BUFFER_TO_VIDEO: if (sx + width > linelen) - return EFI_EXIT(EFI_INVALID_PARAMETER); + 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_EXIT(EFI_INVALID_PARAMETER); + return EFI_INVALID_PARAMETER; break; default: - return EFI_EXIT(EFI_INVALID_PARAMETER); + return EFI_INVALID_PARAMETER; } /* Check destination rectangle */ @@ -143,11 +125,11 @@ efi_status_t EFIAPI gop_blt(struct efi_gop *this, struct efi_gop_pixel *buffer, case EFI_BLT_VIDEO_TO_VIDEO: if (dx + width > gopobj->info.width || dy + height > gopobj->info.height) - return EFI_EXIT(EFI_INVALID_PARAMETER); + return EFI_INVALID_PARAMETER; break; case EFI_BLT_VIDEO_TO_BLT_BUFFER: if (dx + width > linelen) - return EFI_EXIT(EFI_INVALID_PARAMETER); + return EFI_INVALID_PARAMETER; break; } @@ -185,7 +167,7 @@ efi_status_t EFIAPI gop_blt(struct efi_gop *this, struct efi_gop_pixel *buffer, (i + sy) + j + sx]); break; default: - return EFI_EXIT(EFI_UNSUPPORTED); + return EFI_UNSUPPORTED; } break; } @@ -217,13 +199,123 @@ efi_status_t EFIAPI gop_blt(struct efi_gop *this, struct efi_gop_pixel *buffer, efi_blt_col_to_vid16(&pix); break; default: - return EFI_EXIT(EFI_UNSUPPORTED); + return EFI_UNSUPPORTED; } break; } } } + return EFI_SUCCESS; +} + +/* + * 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) +{ + return gop_blt_int(this, buffer, EFI_BLT_VIDEO_FILL, sx, sy, dx, + dy, width, height, delta); +} + +static efi_status_t gop_blt_buf_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) +{ + return gop_blt_int(this, buffer, EFI_BLT_BUFFER_TO_VIDEO, sx, sy, dx, + dy, width, height, delta); +} + +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) +{ + return gop_blt_int(this, buffer, EFI_BLT_VIDEO_TO_VIDEO, sx, sy, dx, + dy, width, height, delta); +} + +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) +{ + return gop_blt_int(this, buffer, EFI_BLT_VIDEO_TO_BLT_BUFFER, sx, sy, + dx, dy, width, height, delta); +} + +/* + * 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_ENTRY("%p, %p, %u, %zu, %zu, %zu, %zu, %zu, %zu, %zu", this, + buffer, operation, sx, sy, dx, dy, width, height, delta); + + /* 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); + break; + case EFI_BLT_BUFFER_TO_VIDEO: + ret = gop_blt_buf_to_vid(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); + break; + case EFI_BLT_VIDEO_TO_BLT_BUFFER: + ret = gop_blt_vid_to_buf(this, buffer, operation, sx, sy, dx, + dy, width, height, delta); + break; + default: + ret = EFI_UNSUPPORTED; + } + + if (ret != EFI_SUCCESS) + return EFI_EXIT(ret); + #ifdef CONFIG_DM_VIDEO video_sync_all(); #else -- cgit From 8e475064097d182191129e31846c585421f0f85a Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Thu, 15 Mar 2018 15:02:29 +0100 Subject: efi_loader: Optimize GOP more The GOP path was optimized, but still not as fast as it should be. Let's push it even further by trimming the hot path into simple 32bit load/store operations for buf->vid 32bpp operations. Signed-off-by: Alexander Graf --- lib/efi_loader/efi_gop.c | 176 ++++++++++++++++++++++++++++++----------------- 1 file changed, 114 insertions(+), 62 deletions(-) (limited to 'lib') diff --git a/lib/efi_loader/efi_gop.c b/lib/efi_loader/efi_gop.c index 26d6f02353d..363ccbb7890 100644 --- a/lib/efi_loader/efi_gop.c +++ b/lib/efi_loader/efi_gop.c @@ -78,18 +78,20 @@ static __always_inline u16 efi_blt_col_to_vid16(struct efi_gop_pixel *blt) } static __always_inline efi_status_t gop_blt_int(struct efi_gop *this, - struct efi_gop_pixel *buffer, + 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 delta, + efi_uintn_t vid_bpp) { struct efi_gop_obj *gopobj = container_of(this, struct efi_gop_obj, ops); - efi_uintn_t i, j, linelen; + 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 */ @@ -133,6 +135,37 @@ static __always_inline efi_status_t gop_blt_int(struct efi_gop *this, break; } + /* 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; + } + + slineoff = swidth * sy; + dlineoff = dwidth * dy; for (i = 0; i < height; i++) { for (j = 0; j < width; j++) { struct efi_gop_pixel pix; @@ -143,70 +176,65 @@ static __always_inline efi_status_t gop_blt_int(struct efi_gop *this, pix = *buffer; break; case EFI_BLT_BUFFER_TO_VIDEO: - pix = buffer[linelen * (i + sy) + j + sx]; + pix = buffer[slineoff + j + sx]; break; case EFI_BLT_VIDEO_TO_BLT_BUFFER: case EFI_BLT_VIDEO_TO_VIDEO: - switch (gopobj->bpix) { -#ifdef CONFIG_DM_VIDEO - case VIDEO_BPP32: -#else - case LCD_COLOR32: -#endif + if (vid_bpp == 32) pix = *(struct efi_gop_pixel *)&fb32[ - gopobj->info.width * - (i + sy) + j + sx]; - break; -#ifdef CONFIG_DM_VIDEO - case VIDEO_BPP16: -#else - case LCD_COLOR16: -#endif + slineoff + j + sx]; + else pix = efi_vid16_to_blt_col(fb16[ - gopobj->info.width * - (i + sy) + j + sx]); - break; - default: - return EFI_UNSUPPORTED; - } + slineoff + j + sx]); break; } /* Write destination pixel */ switch (operation) { case EFI_BLT_VIDEO_TO_BLT_BUFFER: - buffer[linelen * (i + dy) + j + dx] = pix; + buffer[dlineoff + j + dx] = pix; break; case EFI_BLT_BUFFER_TO_VIDEO: case EFI_BLT_VIDEO_FILL: case EFI_BLT_VIDEO_TO_VIDEO: - switch (gopobj->bpix) { + 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 - case VIDEO_BPP32: + case VIDEO_BPP32: #else - case LCD_COLOR32: + case LCD_COLOR32: #endif - fb32[gopobj->info.width * - (i + dy) + j + dx] = *(u32 *)&pix; - break; + vid_bpp = 32; + break; #ifdef CONFIG_DM_VIDEO - case VIDEO_BPP16: + case VIDEO_BPP16: #else - case LCD_COLOR16: + case LCD_COLOR16: #endif - fb16[gopobj->info.width * - (i + dy) + j + dx] = - efi_blt_col_to_vid16(&pix); - break; - default: - return EFI_UNSUPPORTED; - } - break; - } - } + vid_bpp = 16; + break; } - return EFI_SUCCESS; + return vid_bpp; } /* @@ -223,21 +251,33 @@ static efi_status_t gop_blt_video_fill(struct efi_gop *this, 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 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); + dy, width, height, delta, vid_bpp); } -static efi_status_t gop_blt_buf_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) +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); + 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, @@ -245,10 +285,11 @@ static efi_status_t gop_blt_vid_to_vid(struct efi_gop *this, 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 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); + dy, width, height, delta, vid_bpp); } static efi_status_t gop_blt_vid_to_buf(struct efi_gop *this, @@ -256,10 +297,11 @@ static efi_status_t gop_blt_vid_to_buf(struct efi_gop *this, 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 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); + dx, dy, width, height, delta, vid_bpp); } /* @@ -287,27 +329,37 @@ efi_status_t EFIAPI gop_blt(struct efi_gop *this, struct efi_gop_pixel *buffer, 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); + dy, width, height, delta, vid_bpp); break; case EFI_BLT_BUFFER_TO_VIDEO: - ret = gop_blt_buf_to_vid(this, buffer, operation, sx, sy, dx, - dy, width, height, delta); + /* 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); + 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); + dy, width, height, delta, vid_bpp); break; default: ret = EFI_UNSUPPORTED; -- cgit From 813468cdbd7287f0b2e38f9702aa3eee37b1c5b5 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Thu, 15 Mar 2018 15:08:16 +0100 Subject: efi_loader: Fix return value for efi_add_runtime_mmio The efi_add_runtime_mmio function incorrectly returned the added address as return value rather than EFI_SUCCESS. Fix it by checking the return value of efi_add_memory_map properly. Fixes: f057cfef5dc ("efi_loader: exit status for efi_reset_system_init") Signed-off-by: Alexander Graf --- lib/efi_loader/efi_runtime.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'lib') diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c index 08883161407..8558124c0a1 100644 --- a/lib/efi_loader/efi_runtime.c +++ b/lib/efi_loader/efi_runtime.c @@ -349,13 +349,13 @@ static efi_status_t EFIAPI efi_set_virtual_address_map( efi_status_t efi_add_runtime_mmio(void *mmio_ptr, u64 len) { struct efi_runtime_mmio_list *newmmio; - efi_status_t ret; - u64 pages = (len + EFI_PAGE_MASK) >> EFI_PAGE_SHIFT; - ret = efi_add_memory_map(*(uintptr_t *)mmio_ptr, pages, EFI_MMAP_IO, - false); - if (ret != EFI_SUCCESS) - return ret; + 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) @@ -365,7 +365,7 @@ efi_status_t efi_add_runtime_mmio(void *mmio_ptr, u64 len) newmmio->len = len; list_add_tail(&newmmio->link, &efi_runtime_mmio); - return ret; + return EFI_SUCCESS; } /* -- cgit From f9cfad1a61e3461dcf256ecfb83f4eaf68142d1b Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Thu, 15 Mar 2018 17:33:38 +0100 Subject: efi_loader: Fix network DP with DM_ETH When CONFIG_DM_ETH is set, we assemble the device path properly with a full device hierarchy. Our helper function dp_fill() even put the MAC node itself in it for us. However, for non-DM compatibility we also have code in that added the MAC node manually. That code now runs on top of the existing MAC node: Handle 0x3db2f6b0 /HardwareVendor(e61d73b9-a384-4acc-aeab-82e828f3628b)[0: ] /USBClass(0,0,9,0,0)/USBClass(424,9514,9,0,2)/MacAddr(b8:27:eb:e1:81:47,1) /MacAddr(b8:27:eb:e1:81:47,57)/EndEntire We obviously don't need the additional node and in fact, grub chokes on it and fails to match the DP against the ethernet device node. So this patch moves the additional MAC node into the non-DM code path: Handle 0x3db3fde0 /HardwareVendor(e61d73b9-a384-4acc-aeab-82e828f3628b)[0: ] /USBClass(0,0,9,0,0)/USBClass(424,9514,9,0,2)/MacAddr(b8:27:eb:e1:81:47,1) /EndEntire While at it, we also mark the non-DM MAC node as ethernet. Fixes: b66c60dde9d ("efi_loader: add device-path utils") Signed-off-by: Alexander Graf --- lib/efi_loader/efi_device_path.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c index 3c735e60d3b..22627824f05 100644 --- a/lib/efi_loader/efi_device_path.c +++ b/lib/efi_loader/efi_device_path.c @@ -749,7 +749,9 @@ struct efi_device_path *efi_dp_from_file(struct blk_desc *desc, int part, #ifdef CONFIG_CMD_NET struct efi_device_path *efi_dp_from_eth(void) { +#ifndef CONFIG_DM_ETH struct efi_device_path_mac_addr *ndp; +#endif void *buf, *start; unsigned dpsize = 0; @@ -759,8 +761,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 +773,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; -- cgit From eab2dc37ee457191583c5d9ff26ce9d7ccda3637 Mon Sep 17 00:00:00 2001 From: Patrick Wildt Date: Sun, 25 Mar 2018 19:54:03 +0200 Subject: efi_loader: initialize device path on alloc Since the backing memory for a new device path can contain stale data we have to make sure that we zero the buffer. Otherwise some code paths that don't set all fields in a structure backed by this device path might contain unwanted stale data. Signed-off-by: Patrick Wildt Reviewed-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- lib/efi_loader/efi_device_path.c | 1 + 1 file changed, 1 insertion(+) (limited to 'lib') diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c index 22627824f05..ab28b2fd257 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; } -- cgit From 7fb96a10b31953cde698326a61e963ba9df1dc1f Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Tue, 3 Apr 2018 22:29:30 +0200 Subject: efi_loader: use efi_uintn_t for LoadImage We generally use efi_uintn_t where the UEFI spec uses UINTN. Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- lib/efi_loader/efi_boottime.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index fd35ffa359b..d15a131e743 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -1568,14 +1568,14 @@ 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) { -- cgit From 84b40b40ad9b95b4680766597c866b604147144c Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Tue, 3 Apr 2018 22:29:31 +0200 Subject: efi_loader: save image relocation address and size For analyzing crash output the relocation address and size are needed. Save them in the loaded image info. Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- lib/efi_loader/efi_image_loader.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'lib') diff --git a/lib/efi_loader/efi_image_loader.c b/lib/efi_loader/efi_image_loader.c index cac64ba9fec..701387b95f2 100644 --- a/lib/efi_loader/efi_image_loader.c +++ b/lib/efi_loader/efi_image_loader.c @@ -221,6 +221,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; } -- cgit From 82786754b9d21a05a70c45ff7e9e48c91745f837 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Tue, 3 Apr 2018 22:29:32 +0200 Subject: efi_loader: ImageSize must be multiple of SectionAlignment According to the Portable Executable and Common Object File Format Specification the image size must be a multiple of the alignment of sections. Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- lib/efi_loader/efi_image_loader.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'lib') diff --git a/lib/efi_loader/efi_image_loader.c b/lib/efi_loader/efi_image_loader.c index 701387b95f2..74c6a9f9213 100644 --- a/lib/efi_loader/efi_image_loader.c +++ b/lib/efi_loader/efi_image_loader.c @@ -175,6 +175,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 if (can_run_nt32 && (nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC)) { IMAGE_OPTIONAL_HEADER32 *opt = &nt->OptionalHeader; @@ -190,6 +191,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); -- cgit From b6dd57773719bfcea6a295973c349b7842870337 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Tue, 3 Apr 2018 22:37:11 +0200 Subject: efi_loader: use correct types in EFI_FILE_PROTOCOL In the EFI_FILE_PROTOCOL buffer sizes and positions are passed as UINTN and not as u64. Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- lib/efi_loader/efi_boottime.c | 5 +-- lib/efi_loader/efi_file.c | 47 +++++++++++++++++++++------- lib/efi_selftest/efi_selftest_block_device.c | 2 +- 3 files changed, 39 insertions(+), 15 deletions(-) (limited to 'lib') diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index d15a131e743..7a9449f59c3 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -1513,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) @@ -1534,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); diff --git a/lib/efi_loader/efi_file.c b/lib/efi_loader/efi_file.c index 52a4e7438e3..0be0f8b807e 100644 --- a/lib/efi_loader/efi_file.c +++ b/lib/efi_loader/efi_file.c @@ -314,29 +314,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 +375,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 +429,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) + 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; @@ -461,9 +481,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) + 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_selftest/efi_selftest_block_device.c b/lib/efi_selftest/efi_selftest_block_device.c index 9e4b93d9a61..b07b22465f4 100644 --- a/lib/efi_selftest/efi_selftest_block_device.c +++ b/lib/efi_selftest/efi_selftest_block_device.c @@ -302,7 +302,7 @@ 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; + efi_uintn_t buf_size; char buf[16] __aligned(ARCH_DMA_MINALIGN); ret = boottime->connect_controller(disk_handle, NULL, NULL, 1); -- cgit From 9c9021e24571505d76969d8fd37b3b5e06ad2590 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Wed, 4 Apr 2018 15:42:09 +0200 Subject: efi_loader: use const for GUIDs in the EFI_FILE_PROTOCOL Use const efi_guid_t* when passing GUIDs. Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- lib/efi_loader/efi_file.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/efi_loader/efi_file.c b/lib/efi_loader/efi_file.c index 0be0f8b807e..2fc77cfb874 100644 --- a/lib/efi_loader/efi_file.c +++ b/lib/efi_loader/efi_file.c @@ -429,7 +429,7 @@ error: } static efi_status_t EFIAPI efi_file_getinfo(struct efi_file_handle *file, - efi_guid_t *info_type, + const efi_guid_t *info_type, efi_uintn_t *buffer_size, void *buffer) { @@ -481,7 +481,7 @@ error: } static efi_status_t EFIAPI efi_file_setinfo(struct efi_file_handle *file, - efi_guid_t *info_type, + const efi_guid_t *info_type, efi_uintn_t buffer_size, void *buffer) { -- cgit From 9e6835e6baab3d56e1bb2d4b96ee80f98e2196e7 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Wed, 4 Apr 2018 15:42:11 +0200 Subject: efi_loader: implement EFI_FILE_SYSTEM_INFO Implement the information type EFI_FILE_SYSTEM_INFO in the service GetInfo() of the EFI_FILE_PROTOCOL. The volume label is not available in U-Boot. As a work-around use the partition name instead. Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- lib/efi_loader/efi_file.c | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) (limited to 'lib') diff --git a/lib/efi_loader/efi_file.c b/lib/efi_loader/efi_file.c index 2fc77cfb874..cec8347f558 100644 --- a/lib/efi_loader/efi_file.c +++ b/lib/efi_loader/efi_file.c @@ -12,6 +12,9 @@ #include #include +/* 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; @@ -472,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; } -- cgit From db851c84c92ba1281082622d38231755356ce644 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Wed, 4 Apr 2018 15:42:12 +0200 Subject: efi_selftest: partition label for test image efi_selftest_disk_image.h contains a disk image. We use it to test the EFI_FILE_PROTOCOL. The patch sets the partition label to 'U-BOOT TEST'. Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- lib/efi_selftest/efi_selftest_disk_image.h | 58 ++++++++++++------------------ 1 file changed, 22 insertions(+), 36 deletions(-) (limited to 'lib') diff --git a/lib/efi_selftest/efi_selftest_disk_image.h b/lib/efi_selftest/efi_selftest_disk_image.h index 4775dace706..9c741ce136c 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} } } -- cgit From 1348c17ab2584151d5c0c7a6ac1b17b929521bad Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Wed, 4 Apr 2018 15:42:13 +0200 Subject: efi_selftest: test getinfo(EFI_FILE_SYSTEM_INFO) Check that the getinfo() service of the file protocol correctly returns the partion label. Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- lib/efi_selftest/efi_selftest_block_device.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'lib') diff --git a/lib/efi_selftest/efi_selftest_block_device.c b/lib/efi_selftest/efi_selftest_block_device.c index b07b22465f4..a8979ed56bd 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,6 +303,10 @@ static int execute(void) struct efi_device_path *dp_partition; struct efi_simple_file_system_protocol *file_system; struct efi_file_handle *root, *file; + struct { + struct efi_file_system_info info; + u16 label[12]; + } system_info; efi_uintn_t buf_size; char buf[16] __aligned(ARCH_DMA_MINALIGN); @@ -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) { -- cgit From c9a63f44b526696a60e275087d79fe709f65f48b Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Thu, 5 Apr 2018 11:56:21 +0200 Subject: efi_loader: new functions to print loaded image information Introduce functions to print information about loaded images. If we want to analyze an exception in an EFI image we need the offset between the PC and the start of the loaded image. With efi_print_image_info() we can print the necessary information for a single image, e.g. UEFI image [0xbffe6000:0xbffe631f] pc=0x138 '/\snp.efi' efi_print_image_infos() provides output for all loaded images. Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- lib/efi_loader/efi_image_loader.c | 46 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) (limited to 'lib') diff --git a/lib/efi_loader/efi_image_loader.c b/lib/efi_loader/efi_image_loader.c index 74c6a9f9213..f5885760d41 100644 --- a/lib/efi_loader/efi_image_loader.c +++ b/lib/efi_loader/efi_image_loader.c @@ -22,6 +22,52 @@ 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; +/* + * 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) { -- cgit From 0c5d2a3dac01a8d436639ab5b7e44f4218d62b84 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Tue, 3 Apr 2018 22:06:52 +0200 Subject: efi_loader: completely initialize network Add missing network initialization code. Before the patch the network was only usable if a network command like dhcp or tftp had beed executed. This was visible when interrupting the console countdown and executing bootefi selftest for vexpress_ca15_tc2_defconfig. Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- lib/efi_loader/efi_net.c | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/efi_loader/efi_net.c b/lib/efi_loader/efi_net.c index 3d860e658e6..9afe76cdb31 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, -- cgit From 61a5ced6ad9376a0755ea2a920667e3a9072990c Mon Sep 17 00:00:00 2001 From: Ivan Gorinov Date: Thu, 5 Apr 2018 18:32:06 -0700 Subject: efi_loader: Check machine type in the image header Check FileHeader.Machine to make sure the EFI executable image is built for the same architecture. For example, 32-bit U-Boot on x86 will print an error message instead of loading an x86_64 image and crashing. Signed-off-by: Ivan Gorinov Signed-off-by: Alexander Graf --- lib/efi_loader/efi_image_loader.c | 51 ++++++++++++++++++++++++++++++--------- 1 file changed, 39 insertions(+), 12 deletions(-) (limited to 'lib') diff --git a/lib/efi_loader/efi_image_loader.c b/lib/efi_loader/efi_image_loader.c index f5885760d41..d5fbba31383 100644 --- a/lib/efi_loader/efi_image_loader.c +++ b/lib/efi_loader/efi_image_loader.c @@ -22,6 +22,30 @@ 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. * @@ -172,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) { @@ -193,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 + @@ -205,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; @@ -222,8 +250,7 @@ void *efi_load_pe(void *efi, struct efi_loaded_image *loaded_image_info) rel_size = opt->DataDirectory[rel_idx].Size; rel = efi_reloc + opt->DataDirectory[rel_idx].VirtualAddress; virt_size = ALIGN(virt_size, opt->SectionAlignment); - } else if (can_run_nt32 && - (nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC)) { + } 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); -- cgit