From 28a4fd46e7c6507d788677406a8bd385e0cb35eb Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Wed, 7 Mar 2018 02:40:51 +0100 Subject: [PATCH 01/60] 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(+) diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 66e26fd63a..8892c86f41 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); } From 8396e3fd63e8c8e17d60a6d1992ca3df10bc8d66 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 3 Mar 2018 15:41:37 +0100 Subject: [PATCH 02/60] 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 --- include/efi_api.h | 18 -------------- lib/efi_loader/efi_boottime.c | 5 ---- lib/efi_loader/efi_console.c | 46 ----------------------------------- 3 files changed, 69 deletions(-) diff --git a/include/efi_api.h b/include/efi_api.h index 3ba650e57e..8559656bad 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -571,24 +571,6 @@ struct efi_simple_input_interface { struct efi_event *wait_for_key; }; -#define CONSOLE_CONTROL_GUID \ - EFI_GUID(0xf42f7782, 0x12e, 0x4c12, \ - 0x99, 0x56, 0x49, 0xf9, 0x43, 0x4, 0xf7, 0x21) -#define EFI_CONSOLE_MODE_TEXT 0 -#define EFI_CONSOLE_MODE_GFX 1 - -struct efi_console_control_protocol -{ - efi_status_t (EFIAPI *get_mode)( - struct efi_console_control_protocol *this, int *mode, - char *uga_exists, char *std_in_locked); - efi_status_t (EFIAPI *set_mode)( - struct efi_console_control_protocol *this, int mode); - efi_status_t (EFIAPI *lock_std_in)( - struct efi_console_control_protocol *this, - uint16_t *password); -}; - #define EFI_DEVICE_PATH_TO_TEXT_PROTOCOL_GUID \ EFI_GUID(0x8b843e20, 0x8132, 0x4852, \ 0x90, 0xcc, 0x55, 0x1a, 0x4e, 0x4a, 0x7f, 0x1c) diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 8892c86f41..71c244ea80 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 28d63635ec..3cb580e3ed 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; From e70f8dfa2ce2f47a6eed891016a4c6f289da1cb6 Mon Sep 17 00:00:00 2001 From: Leif Lindholm Date: Fri, 9 Mar 2018 17:43:21 +0100 Subject: [PATCH 03/60] 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 --- include/efi_api.h | 29 +++++++ include/efi_loader.h | 4 + lib/efi_loader/Makefile | 3 +- lib/efi_loader/efi_boottime.c | 6 ++ lib/efi_loader/efi_device_path_utilities.c | 89 ++++++++++++++++++++++ 5 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 lib/efi_loader/efi_device_path_utilities.c diff --git a/include/efi_api.h b/include/efi_api.h index 8559656bad..827a192620 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -587,6 +587,35 @@ struct efi_device_path_to_text_protocol bool allow_shortcuts); }; +#define EFI_DEVICE_PATH_UTILITIES_PROTOCOL_GUID \ + EFI_GUID(0x0379be4e, 0xd706, 0x437d, \ + 0xb0, 0x37, 0xed, 0xb8, 0x2f, 0xb7, 0x72, 0xa4) + +struct efi_device_path_utilities_protocol { + efi_uintn_t (EFIAPI *get_device_path_size)( + const struct efi_device_path *device_path); + struct efi_device_path *(EFIAPI *duplicate_device_path)( + const struct efi_device_path *device_path); + struct efi_device_path *(EFIAPI *append_device_path)( + const struct efi_device_path *src1, + const struct efi_device_path *src2); + struct efi_device_path *(EFIAPI *append_device_node)( + const struct efi_device_path *device_path, + const struct efi_device_path *device_node); + struct efi_device_path *(EFIAPI *append_device_path_instance)( + const struct efi_device_path *device_path, + const struct efi_device_path *device_path_instance); + struct efi_device_path *(EFIAPI *get_next_device_path_instance)( + struct efi_device_path **device_path_instance, + efi_uintn_t *device_path_instance_size); + bool (EFIAPI *is_device_path_multi_instance)( + const struct efi_device_path *device_path); + struct efi_device_path *(EFIAPI *create_device_node)( + uint8_t node_type, + uint8_t node_sub_type, + uint16_t node_length); +}; + #define EFI_GOP_GUID \ EFI_GUID(0x9042a9de, 0x23dc, 0x4a38, \ 0x96, 0xfb, 0x7a, 0xde, 0xd0, 0x80, 0x51, 0x6a) diff --git a/include/efi_loader.h b/include/efi_loader.h index 07730c3f39..39d0266952 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -83,6 +83,9 @@ extern struct efi_simple_text_output_protocol efi_con_out; extern struct efi_simple_input_interface efi_con_in; extern struct efi_console_control_protocol efi_console_control; extern const struct efi_device_path_to_text_protocol efi_device_path_to_text; +/* implementation of the EFI_DEVICE_PATH_UTILITIES_PROTOCOL */ +extern const struct efi_device_path_utilities_protocol + efi_device_path_utilities; uint16_t *efi_dp_str(struct efi_device_path *dp); @@ -97,6 +100,7 @@ extern const efi_guid_t efi_guid_loaded_image; extern const efi_guid_t efi_guid_device_path_to_text_protocol; extern const efi_guid_t efi_simple_file_system_protocol_guid; extern const efi_guid_t efi_file_info_guid; +extern const efi_guid_t efi_guid_device_path_utilities_protocol; extern unsigned int __efi_runtime_start, __efi_runtime_stop; extern unsigned int __efi_runtime_rel_start, __efi_runtime_rel_stop; diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile index 2a87d9ed77..d2ce89713e 100644 --- a/lib/efi_loader/Makefile +++ b/lib/efi_loader/Makefile @@ -17,7 +17,8 @@ endif obj-$(CONFIG_CMD_BOOTEFI_HELLO) += helloworld_efi.o obj-y += efi_image_loader.o efi_boottime.o efi_runtime.o efi_console.o obj-y += efi_memory.o efi_device_path_to_text.o efi_device_path.o -obj-y += efi_file.o efi_variable.o efi_bootmgr.o efi_watchdog.o +obj-y += efi_device_path_utilities.o efi_file.o efi_variable.o efi_bootmgr.o +obj-y += efi_watchdog.o obj-$(CONFIG_LCD) += efi_gop.o obj-$(CONFIG_DM_VIDEO) += efi_gop.o obj-$(CONFIG_PARTITIONS) += efi_disk.o diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 71c244ea80..4e133e9b47 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 0000000000..bc97eeee31 --- /dev/null +++ b/lib/efi_loader/efi_device_path_utilities.c @@ -0,0 +1,89 @@ +/* + * EFI device path interface + * + * Copyright (c) 2017 Leif Lindholm + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#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, +}; From e1214151e8df577940f656dce3b5db2d504207d1 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Fri, 2 Mar 2018 19:58:49 +0100 Subject: [PATCH 04/60] efi_loader: delete doc/README.efi Delete README.efi. It is replaced by a further patch. Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- doc/README.efi | 86 -------------------------------------------------- 1 file changed, 86 deletions(-) diff --git a/doc/README.efi b/doc/README.efi index 956f5bfa0c..e69de29bb2 100644 --- a/doc/README.efi +++ b/doc/README.efi @@ -1,86 +0,0 @@ -# -# Copyright (C) 2015 Google, Inc -# -# SPDX-License-Identifier: GPL-2.0+ -# - -EFI on U-Boot -============= -This document provides information about the implementation of the UEFI API [1] -in U-Boot. - - -=========== Table of Contents =========== - -Motivation -How do I get it? -Status -Future work - - -Motivation ----------- - -With this API support in place, you can run any UEFI payload (such as the Linux -kernel, grub2 or gummiboot) on U-Boot. This dramatically simplifies boot loader -configuration, as U-Boot based systems now look and feel (almost) the same way -as TianoCore based systems. - -How do I get it? ----------------- - -EFI support for 32bit ARM and AArch64 is already included in U-Boot. All you -need to do is enable - - CONFIG_CMD_BOOTEFI=y - CONFIG_EFI_LOADER=y - -in your .config file and you will automatically get a bootefi command to run -an efi application as well as snippet in the default distro boot script that -scans for removable media efi binaries as fallback. - -Status ------- - -I am successfully able to run grub2 and Linux EFI binaries with this code on -ARMv7 as well as AArch64 systems. - -When enabled, the resulting U-Boot binary only grows by ~10KB, so it's very -light weight. - -All storage devices are directly accessible from the uEFI payload - -Removable media booting (search for /efi/boot/boota{a64,arm}.efi) is supported. - -Simple use cases like "Plug this SD card into my ARM device and it just -boots into grub which boots into Linux", work very well. - - -Running HelloWord.efi ---------------------- - -You can run a simple 'hello world' EFI program in U-Boot. -Enable the option CONFIG_CMD_BOOTEFI_HELLO. - -Then you can boot into U-Boot and type: - - > bootefi hello - -The 'hello world EFI' program will then run, print a message and exit. - - -Future work ------------ - -Of course, there are still a few things one could do on top: - - - Improve disk media detection (don't scan, use what information we -have) - - Add EFI variable support using NVRAM - - Add GFX support - - Make EFI Shell work - - Network device support - - Support for payload exit - - Payload Watchdog support - -[1] http://uefi.org/ From 1914e5b5d86a31fdf14d69a036e5dac957f5e3af Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Fri, 2 Mar 2018 19:58:50 +0100 Subject: [PATCH 05/60] efi_loader: provide new doc/README.uefi Provides information about - usage of the bootefi command - overview of UEFI - interaction between U-Boot and EFI drivers Signed-off-by: Heinrich Schuchardt Reviewed-by: Leif Lindholm Acked-by: Leif Lindholm Signed-off-by: Alexander Graf --- MAINTAINERS | 2 +- doc/README.efi | 0 doc/README.uefi | 332 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 333 insertions(+), 1 deletion(-) delete mode 100644 doc/README.efi create mode 100644 doc/README.uefi diff --git a/MAINTAINERS b/MAINTAINERS index fde77b2b61..bf2a84c7e1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -330,7 +330,7 @@ EFI PAYLOAD M: Alexander Graf S: Maintained T: git git://github.com/agraf/u-boot.git -F: doc/README.efi +F: doc/README.uefi F: doc/README.iscsi F: include/efi* F: include/pe.h diff --git a/doc/README.efi b/doc/README.efi deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/doc/README.uefi b/doc/README.uefi new file mode 100644 index 0000000000..7403be3614 --- /dev/null +++ b/doc/README.uefi @@ -0,0 +1,332 @@ + + +# UEFI on U-Boot + +The Unified Extensible Firmware Interface Specification (UEFI) [1] has become +the default for booting on AArch64 and x86 systems. It provides a stable API for +the interaction of drivers and applications with the firmware. The API comprises +access to block storage, network, and console to name a few. The Linux kernel +and boot loaders like GRUB or the FreeBSD loader can be executed. + +## Building for UEFI + +The UEFI standard supports only little endian systems. The UEFI support can be +activated for ARM and x86 by specifying + + CONFIG_CMD_BOOTEFI=y + CONFIG_EFI_LOADER=y + +in the .config file. + +Support for attaching virtual block devices, e.g. iSCSI drives connected by the +loaded UEFI application [3], requires + + CONFIG_BLK=y + CONFIG_PARTITIONS=y + +### Executing a UEFI binary + +The bootefi command is used to start UEFI applications or to install UEFI +drivers. It takes two parameters + + bootefi [fdt address] + +* image address - the memory address of the UEFI binary +* fdt address - the memory address of the flattened device tree + +Below you find the output of an example session starting GRUB. + + => load mmc 0:2 ${fdt_addr_r} boot/dtb + 29830 bytes read in 14 ms (2 MiB/s) + => load mmc 0:1 ${kernel_addr_r} efi/debian/grubaa64.efi + reading efi/debian/grubaa64.efi + 120832 bytes read in 7 ms (16.5 MiB/s) + => bootefi ${kernel_addr_r} ${fdt_addr_r} + +The environment variable 'bootargs' is passed as load options in the UEFI system +table. The Linux kernel EFI stub uses the load options as command line +arguments. + +### Executing the boot manager + +The UEFI specfication foresees to define boot entries and boot sequence via UEFI +variables. Booting according to these variables is possible via + + bootefi bootmgr [fdt address] + +As of U-Boot v2018.03 UEFI variables are not persisted and cannot be set at +runtime. + +### Executing the built in hello world application + +A hello world UEFI application can be built with + + CONFIG_CMD_BOOTEFI_HELLO_COMPILE=y + +It can be embedded into the U-Boot binary with + + CONFIG_CMD_BOOTEFI_HELLO=y + +The bootefi command is used to start the embedded hello world application. + + bootefi hello [fdt address] + +Below you find the output of an example session. + + => bootefi hello ${fdtcontroladdr} + ## Starting EFI application at 01000000 ... + WARNING: using memory device/image path, this may confuse some payloads! + Hello, world! + Running on UEFI 2.7 + Have SMBIOS table + Have device tree + Load options: root=/dev/sdb3 init=/sbin/init rootwait ro + ## Application terminated, r = 0 + +The environment variable fdtcontroladdr points to U-Boot's internal device tree +(if available). + +### Executing the built-in selftest + +An UEFI selftest suite can be embedded in U-Boot by building with + + CONFIG_CMD_BOOTEFI_SELFTEST=y + +For testing the UEFI implementation the bootefi command can be used to start the +selftest. + + bootefi selftest [fdt address] + +The environment variable 'efi_selftest' can be used to select a single test. If +it is not provided all tests are executed except those marked as 'on request'. +If the environment variable is set to 'list' a list of all tests is shown. + +Below you can find the output of an example session. + + => setenv efi_selftest simple network protocol + => bootefi selftest + Testing EFI API implementation + Selected test: 'simple network protocol' + Setting up 'simple network protocol' + Setting up 'simple network protocol' succeeded + Executing 'simple network protocol' + DHCP Discover + DHCP reply received from 192.168.76.2 (52:55:c0:a8:4c:02) + as broadcast message. + Executing 'simple network protocol' succeeded + Tearing down 'simple network protocol' + Tearing down 'simple network protocol' succeeded + Boot services terminated + Summary: 0 failures + Preparing for reset. Press any key. + +## The UEFI life cycle + +After the U-Boot platform has been initialized the UEFI API provides two kinds +of services + +* boot services and +* runtime services. + +The API can be extended by loading UEFI drivers which come in two variants + +* boot drivers and +* runtime drivers. + +UEFI drivers are installed with U-Boot's bootefi command. With the same command +UEFI applications can be executed. + +Loaded images of UEFI drivers stay in memory after returning to U-Boot while +loaded images of applications are removed from memory. + +An UEFI application (e.g. an operating system) that wants to take full control +of the system calls ExitBootServices. After a UEFI application calls +ExitBootServices + +* boot services are not available anymore +* timer events are stopped +* the memory used by U-Boot except for runtime services is released +* the memory used by boot time drivers is released + +So this is a point of no return. Afterwards the UEFI application can only return +to U-Boot by rebooting. + +## The UEFI object model + +UEFI offers a flexible and expandable object model. The objects in the UEFI API +are devices, drivers, and loaded images. These objects are referenced by +handles. + +The interfaces implemented by the objects are referred to as protocols. These +are identified by GUIDs. They can be installed and uninstalled by calling the +appropriate boot services. + +Handles are created by the InstallProtocolInterface or the +InstallMultipleProtocolinterfaces service if NULL is passed as handle. + +Handles are deleted when the last protocol has been removed with the +UninstallProtocolInterface or the UninstallMultipleProtocolInterfaces service. + +Devices offer the EFI_DEVICE_PATH_PROTOCOL. A device path is the concatenation +of device nodes. By their device paths all devices of a system are arranged in a +tree. + +Drivers offer the EFI_DRIVER_BINDING_PROTOCOL. This protocol is used to connect +a driver to devices (which are referenced as controllers in this context). + +Loaded images offer the EFI_LOADED_IMAGE_PROTOCOL. This protocol provides meta +information about the image and a pointer to the unload callback function. + +## The UEFI events + +In the UEFI terminology an event is a data object referencing a notification +function which is queued for calling when the event is signaled. The following +types of events exist: + +* periodic and single shot timer events +* exit boot services events, triggered by calling the ExitBootServices() service +* virtual address change events +* memory map change events +* read to boot events +* reset system events +* system table events +* events that are only triggered programmatically + +Events can be created with the CreateEvent service and deleted with CloseEvent +service. + +Events can be assigned to an event group. If any of the events in a group is +signaled, all other events in the group are also set to the signaled state. + +## The UEFI driver model + +A driver is specific for a single protocol installed on a device. To install a +driver on a device the ConnectController service is called. In this context +controller refers to the device for which the driver is installed. + +The relevant drivers are identified using the EFI_DRIVER_BINDING_PROTOCOL. This +protocol has has three functions: + +* supported - determines if the driver is compatible with the device +* start - installs the driver by opening the relevant protocol with + attribute EFI_OPEN_PROTOCOL_BY_DRIVER +* stop - uninstalls the driver + +The driver may create child controllers (child devices). E.g. a driver for block +IO devices will create the device handles for the partitions. The child +controllers will open the supported protocol with the attribute +EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER. + +A driver can be detached from a device using the DisconnectController service. + +## U-Boot devices mapped as UEFI devices + +Some of the U-Boot devices are mapped as UEFI devices + +* block IO devices +* console +* graphical output +* network adapter + +As of U-Boot 2018.03 the logic for doing this is hard coded. + +The development target is to integrate the setup of these UEFI devices with the +U-Boot driver model. So when a U-Boot device is discovered a handle should be +created and the device path protocol and the relevant IO protocol should be +installed. The UEFI driver then would be attached by calling ConnectController. +When a U-Boot device is removed DisconnectController should be called. + +## UEFI devices mapped as U-Boot devices + +UEFI drivers binaries and applications may create new (virtual) devices, install +a protocol and call the ConnectController service. Now the matching UEFI driver +is determined by iterating over the implementations of the +EFI_DRIVER_BINDING_PROTOCOL. + +It is the task of the UEFI driver to create a corresponding U-Boot device and to +proxy calls for this U-Boot device to the controller. + +In U-Boot 2018.03 this has only been implemented for block IO devices. + +### UEFI uclass + +An UEFI uclass driver (lib/efi_driver/efi_uclass.c) has been created that +takes care of initializing the UEFI drivers and providing the +EFI_DRIVER_BINDING_PROTOCOL implementation for the UEFI drivers. + +A linker created list is used to keep track of the UEFI drivers. To create an +entry in the list the UEFI driver uses the U_BOOT_DRIVER macro specifying +UCLASS_EFI as the ID of its uclass, e.g. + + /* Identify as UEFI driver */ + U_BOOT_DRIVER(efi_block) = { + .name = "EFI block driver", + .id = UCLASS_EFI, + .ops = &driver_ops, + }; + +The available operations are defined via the structure struct efi_driver_ops. + + struct efi_driver_ops { + const efi_guid_t *protocol; + const efi_guid_t *child_protocol; + int (*bind)(efi_handle_t handle, void *interface); + }; + +When the supported() function of the EFI_DRIVER_BINDING_PROTOCOL is called the +uclass checks if the protocol GUID matches the protocol GUID of the UEFI driver. +In the start() function the bind() function of the UEFI driver is called after +checking the GUID. +The stop() function of the EFI_DRIVER_BINDING_PROTOCOL disconnects the child +controllers created by the UEFI driver and the UEFI driver. (In U-Boot v2013.03 +this is not yet completely implemented.) + +### UEFI block IO driver + +The UEFI block IO driver supports devices exposing the EFI_BLOCK_IO_PROTOCOL. + +When connected it creates a new U-Boot block IO device with interface type +IF_TYPE_EFI, adds child controllers mapping the partitions, and installs the +EFI_SIMPLE_FILE_SYSTEM_PROTOCOL on these. This can be used together with the +software iPXE to boot from iSCSI network drives [3]. + +This driver is only available if U-Boot is configured with + + CONFIG_BLK=y + CONFIG_PARTITIONS=y + +## TODOs as of U-Boot 2018.03 + +* unimplemented or incompletely implemented boot services + * Exit - call unload function, unload applications only + * ReinstallProtocolInterface + * UnloadImage + +* unimplemented events + * EVT_RUNTIME + * EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE + * event groups + +* data model + * manage events in a linked list + * manage configuration tables in a linked list + +* UEFI drivers + * support DisconnectController for UEFI block devices. + +* support for CONFIG_EFI_LOADER in the sandbox (CONFIG_SANDBOX=y) + +* UEFI variables + * persistence + * runtime support + +## Links + +* [1](http://uefi.org/specifications) + http://uefi.org/specifications - UEFI specifications +* [2](./driver-model/README.txt) doc/driver-model/README.txt - Driver model +* [3](./README.iscsi) doc/README.iscsi - iSCSI booting with U-Boot and iPXE From 7657152bddd131e47390c2b2d6f63d58149d79f1 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 3 Mar 2018 15:28:54 +0100 Subject: [PATCH 06/60] 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 --- include/efi_loader.h | 2 +- lib/efi_loader/efi_smbios.c | 23 +++++++++++++++-------- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/include/efi_loader.h b/include/efi_loader.h index 39d0266952..c686cd3b02 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -189,7 +189,7 @@ int efi_net_register(void); /* Called by bootefi to make the watchdog available */ int efi_watchdog_register(void); /* Called by bootefi to make SMBIOS tables available */ -void efi_smbios_register(void); +efi_status_t efi_smbios_register(void); struct efi_simple_file_system_protocol * efi_fs_from_path(struct efi_device_path *fp); diff --git a/lib/efi_loader/efi_smbios.c b/lib/efi_loader/efi_smbios.c index ac412e7362..62e9697902 100644 --- a/lib/efi_loader/efi_smbios.c +++ b/lib/efi_loader/efi_smbios.c @@ -13,20 +13,27 @@ static const efi_guid_t smbios_guid = SMBIOS_TABLE_GUID; -void efi_smbios_register(void) +/* + * Install the SMBIOS table as a configuration table. + * + * @return status code + */ +efi_status_t efi_smbios_register(void) { /* Map within the low 32 bits, to allow for 32bit SMBIOS tables */ - uint64_t dmi = 0xffffffff; - /* Reserve 4kb for SMBIOS */ - uint64_t pages = 1; - int memtype = EFI_RUNTIME_SERVICES_DATA; + u64 dmi = U32_MAX; + efi_status_t ret; - if (efi_allocate_pages(1, memtype, pages, &dmi) != EFI_SUCCESS) - return; + /* Reserve 4kiB page for SMBIOS */ + ret = efi_allocate_pages(EFI_ALLOCATE_MAX_ADDRESS, + EFI_RUNTIME_SERVICES_DATA, 1, &dmi); + if (ret != EFI_SUCCESS) + return ret; /* Generate SMBIOS tables */ write_smbios_table(dmi); /* And expose them to our EFI payload */ - efi_install_configuration_table(&smbios_guid, (void*)(uintptr_t)dmi); + return efi_install_configuration_table(&smbios_guid, + (void *)(uintptr_t)dmi); } From 80ea9b09900bc35a21468f5422bd36a345ae7eda Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 3 Mar 2018 15:28:55 +0100 Subject: [PATCH 07/60] 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 --- include/efi_loader.h | 2 +- lib/efi_loader/efi_gop.c | 34 ++++++++++++++++++++++------------ 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/include/efi_loader.h b/include/efi_loader.h index c686cd3b02..a28a7a1764 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -183,7 +183,7 @@ int efi_disk_create_partitions(efi_handle_t parent, struct blk_desc *desc, const char *if_typename, int diskid, const char *pdevname); /* Called by bootefi to make GOP (graphical) interface available */ -int efi_gop_register(void); +efi_status_t efi_gop_register(void); /* Called by bootefi to make the network interface available */ int efi_net_register(void); /* Called by bootefi to make the watchdog available */ diff --git a/lib/efi_loader/efi_gop.c b/lib/efi_loader/efi_gop.c index 3caddd5f84..28818eb781 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; } From 075d425d652038e67136db61ace4606fcec89475 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 3 Mar 2018 15:28:56 +0100 Subject: [PATCH 08/60] 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 --- include/efi_loader.h | 2 +- lib/efi_loader/efi_net.c | 24 +++++++++++++----------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/include/efi_loader.h b/include/efi_loader.h index a28a7a1764..d2fadc5c73 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -185,7 +185,7 @@ int efi_disk_create_partitions(efi_handle_t parent, struct blk_desc *desc, /* Called by bootefi to make GOP (graphical) interface available */ efi_status_t efi_gop_register(void); /* Called by bootefi to make the network interface available */ -int efi_net_register(void); +efi_status_t efi_net_register(void); /* Called by bootefi to make the watchdog available */ int efi_watchdog_register(void); /* Called by bootefi to make SMBIOS tables available */ diff --git a/lib/efi_loader/efi_net.c b/lib/efi_loader/efi_net.c index 8c5d5b492c..e7fed79450 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; } From d7b181d57d0c7080facce755c4b9ff7a22436a48 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 3 Mar 2018 15:28:57 +0100 Subject: [PATCH 09/60] 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 --- include/efi_loader.h | 2 +- lib/efi_loader/efi_watchdog.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/efi_loader.h b/include/efi_loader.h index d2fadc5c73..48fbe7a214 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -187,7 +187,7 @@ efi_status_t efi_gop_register(void); /* Called by bootefi to make the network interface available */ efi_status_t efi_net_register(void); /* Called by bootefi to make the watchdog available */ -int efi_watchdog_register(void); +efi_status_t efi_watchdog_register(void); /* Called by bootefi to make SMBIOS tables available */ efi_status_t efi_smbios_register(void); diff --git a/lib/efi_loader/efi_watchdog.c b/lib/efi_loader/efi_watchdog.c index 35a45dedf8..b1c35a8e29 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; } From 098a6cdd1cc519f6c75b5e5de91c6655500a188a Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 3 Mar 2018 15:28:58 +0100 Subject: [PATCH 10/60] efi_loader: simplify calling efi_init_obj_list efi_init_obj_list() should be executed only once. Rather than having the caller check this variable and the callee set it, move all access to the variable inside the function. This reduces the logic needed to call efi_init_obj_list(). Suggested-by: Simon Glass Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- cmd/bootefi.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/cmd/bootefi.c b/cmd/bootefi.c index 6546272348..3f4f214a81 100644 --- a/cmd/bootefi.c +++ b/cmd/bootefi.c @@ -22,7 +22,7 @@ DECLARE_GLOBAL_DATA_PTR; -static uint8_t efi_obj_list_initalized; +static u8 efi_obj_list_initialized; static struct efi_device_path *bootefi_image_path; static struct efi_device_path *bootefi_device_path; @@ -30,7 +30,10 @@ static struct efi_device_path *bootefi_device_path; /* Initialize and populate EFI object list */ static void efi_init_obj_list(void) { - efi_obj_list_initalized = 1; + /* Initialize once only */ + if (efi_obj_list_initialized) + return; + efi_obj_list_initialized = 1; /* Initialize EFI driver uclass */ efi_driver_init(); @@ -184,8 +187,7 @@ static efi_status_t do_bootefi_exec(void *efi, void *fdt, } /* Initialize and populate EFI object list */ - if (!efi_obj_list_initalized) - efi_init_obj_list(); + efi_init_obj_list(); efi_setup_loaded_image(&loaded_image_info, &loaded_image_info_obj, device_path, image_path); @@ -284,8 +286,7 @@ static int do_bootefi_bootmgr_exec(unsigned long fdt_addr) efi_status_t r; /* Initialize and populate EFI object list */ - if (!efi_obj_list_initalized) - efi_init_obj_list(); + efi_init_obj_list(); /* * gd lives in a fixed register which may get clobbered while we execute @@ -350,8 +351,7 @@ static int do_bootefi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) */ efi_save_gd(); /* Initialize and populate EFI object list */ - if (!efi_obj_list_initalized) - efi_init_obj_list(); + efi_init_obj_list(); /* Transfer environment variable efi_selftest as load options */ set_load_options(&loaded_image_info, "efi_selftest"); /* Execute the test */ From 22c793e6a26505fdf80cb5b099142dd6f8f0fff9 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 3 Mar 2018 15:28:59 +0100 Subject: [PATCH 11/60] 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 --- arch/arm/cpu/armv8/fsl-layerscape/cpu.c | 4 ++-- arch/arm/mach-bcm283x/reset.c | 4 ++-- include/efi_loader.h | 11 ++++++++--- lib/efi_loader/efi_runtime.c | 15 ++++++++++++--- 4 files changed, 24 insertions(+), 10 deletions(-) diff --git a/arch/arm/cpu/armv8/fsl-layerscape/cpu.c b/arch/arm/cpu/armv8/fsl-layerscape/cpu.c index 70a6070935..0e90302d3f 100644 --- a/arch/arm/cpu/armv8/fsl-layerscape/cpu.c +++ b/arch/arm/cpu/armv8/fsl-layerscape/cpu.c @@ -654,9 +654,9 @@ void __efi_runtime EFIAPI efi_reset_system( while (1) { } } -void efi_reset_system_init(void) +efi_status_t efi_reset_system_init(void) { - efi_add_runtime_mmio(&rstcr, sizeof(*rstcr)); + return efi_add_runtime_mmio(&rstcr, sizeof(*rstcr)); } #endif diff --git a/arch/arm/mach-bcm283x/reset.c b/arch/arm/mach-bcm283x/reset.c index b62cb8a51e..5b83fdf43d 100644 --- a/arch/arm/mach-bcm283x/reset.c +++ b/arch/arm/mach-bcm283x/reset.c @@ -82,9 +82,9 @@ void __efi_runtime EFIAPI efi_reset_system( while (1) { } } -void efi_reset_system_init(void) +efi_status_t efi_reset_system_init(void) { - efi_add_runtime_mmio(&wdog_regs, sizeof(*wdog_regs)); + return efi_add_runtime_mmio(&wdog_regs, sizeof(*wdog_regs)); } #endif diff --git a/include/efi_loader.h b/include/efi_loader.h index 48fbe7a214..3d369436a1 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -350,7 +350,7 @@ static inline int guidcmp(const efi_guid_t *g1, const efi_guid_t *g2) /* Call this with mmio_ptr as the _pointer_ to a pointer to an MMIO region * to make it available at runtime */ -void efi_add_runtime_mmio(void *mmio_ptr, u64 len); +efi_status_t efi_add_runtime_mmio(void *mmio_ptr, u64 len); /* Boards may provide the functions below to implement RTS functionality */ @@ -358,7 +358,9 @@ void __efi_runtime EFIAPI efi_reset_system( enum efi_reset_type reset_type, efi_status_t reset_status, unsigned long data_size, void *reset_data); -void efi_reset_system_init(void); + +/* Architecture specific initialization of the EFI subsystem */ +efi_status_t efi_reset_system_init(void); efi_status_t __efi_runtime EFIAPI efi_get_time( struct efi_time *time, @@ -392,7 +394,10 @@ void *efi_bootmgr_load(struct efi_device_path **device_path, /* Without CONFIG_EFI_LOADER we don't have a runtime section, stub it out */ #define __efi_runtime_data #define __efi_runtime -static inline void efi_add_runtime_mmio(void *mmio_ptr, u64 len) { } +static inline efi_status_t efi_add_runtime_mmio(void *mmio_ptr, u64 len) +{ + return EFI_SUCCESS; +} /* No loader configured, stub out EFI_ENTRY */ static inline void efi_restore_gd(void) { } diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c index ccb4fc6141..85f85711dd 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; } /* From 14ad49d100a0ba4abafedf8ca6459b699c9d0fa1 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 3 Mar 2018 15:29:00 +0100 Subject: [PATCH 12/60] 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 --- include/efi_loader.h | 2 +- lib/efi_loader/efi_runtime.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/include/efi_loader.h b/include/efi_loader.h index 3d369436a1..b6335182ed 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -365,7 +365,7 @@ efi_status_t efi_reset_system_init(void); efi_status_t __efi_runtime EFIAPI efi_get_time( struct efi_time *time, struct efi_time_cap *capabilities); -void efi_get_time_init(void); +efi_status_t efi_get_time_init(void); #ifdef CONFIG_CMD_BOOTEFI_SELFTEST /* diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c index 85f85711dd..c59d161c8f 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 { From 45204b1025e4e2a5cb984caab3adfdf53e658765 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 3 Mar 2018 15:29:01 +0100 Subject: [PATCH 13/60] efi_loader: do_bootefi_exec should always return an EFI status code The return type of do_bootefi_exec() is efi_status_t. So in case of an error we should always return an EFI status code. Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- cmd/bootefi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/bootefi.c b/cmd/bootefi.c index 3f4f214a81..9f02c5a664 100644 --- a/cmd/bootefi.c +++ b/cmd/bootefi.c @@ -164,7 +164,7 @@ static efi_status_t do_bootefi_exec(void *efi, void *fdt, struct efi_loaded_image loaded_image_info = {}; struct efi_object loaded_image_info_obj = {}; struct efi_device_path *memdp = NULL; - ulong ret; + efi_status_t ret; EFIAPI efi_status_t (*entry)(efi_handle_t image_handle, struct efi_system_table *st); @@ -229,7 +229,7 @@ static efi_status_t do_bootefi_exec(void *efi, void *fdt, /* Load the EFI payload */ entry = efi_load_pe(efi, &loaded_image_info); if (!entry) { - ret = -ENOENT; + ret = EFI_LOAD_ERROR; goto exit; } From fc225e60822e3fd18a56b546eb683d6818d3f9c4 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 3 Mar 2018 15:29:02 +0100 Subject: [PATCH 14/60] efi_loader: check initialization of EFI subsystem is successful Up to now errors in the initialization of the EFI subsystems was not checked. If any initialization fails, leave the bootefi command. We do not retry initialization because this would require to undo all prior initalization steps. Suggested-by: Simon Glass Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- cmd/bootefi.c | 67 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 47 insertions(+), 20 deletions(-) diff --git a/cmd/bootefi.c b/cmd/bootefi.c index 9f02c5a664..025a3dd171 100644 --- a/cmd/bootefi.c +++ b/cmd/bootefi.c @@ -22,40 +22,65 @@ DECLARE_GLOBAL_DATA_PTR; -static u8 efi_obj_list_initialized; +#define OBJ_LIST_NOT_INITIALIZED 1 + +static efi_status_t efi_obj_list_initialized = OBJ_LIST_NOT_INITIALIZED; static struct efi_device_path *bootefi_image_path; static struct efi_device_path *bootefi_device_path; /* Initialize and populate EFI object list */ -static void efi_init_obj_list(void) +efi_status_t efi_init_obj_list(void) { + efi_status_t ret = EFI_SUCCESS; + /* Initialize once only */ - if (efi_obj_list_initialized) - return; - efi_obj_list_initialized = 1; + if (efi_obj_list_initialized != OBJ_LIST_NOT_INITIALIZED) + return efi_obj_list_initialized; /* Initialize EFI driver uclass */ - efi_driver_init(); + ret = efi_driver_init(); + if (ret != EFI_SUCCESS) + goto out; - efi_console_register(); + ret = efi_console_register(); + if (ret != EFI_SUCCESS) + goto out; #ifdef CONFIG_PARTITIONS - efi_disk_register(); + ret = efi_disk_register(); + if (ret != EFI_SUCCESS) + goto out; #endif #if defined(CONFIG_LCD) || defined(CONFIG_DM_VIDEO) - efi_gop_register(); + ret = efi_gop_register(); + if (ret != EFI_SUCCESS) + goto out; #endif #ifdef CONFIG_CMD_NET - efi_net_register(); + ret = efi_net_register(); + if (ret != EFI_SUCCESS) + goto out; #endif #ifdef CONFIG_GENERATE_SMBIOS_TABLE - efi_smbios_register(); + ret = efi_smbios_register(); + if (ret != EFI_SUCCESS) + goto out; #endif - efi_watchdog_register(); + ret = efi_watchdog_register(); + if (ret != EFI_SUCCESS) + goto out; /* Initialize EFI runtime services */ - efi_reset_system_init(); - efi_get_time_init(); + ret = efi_reset_system_init(); + if (ret != EFI_SUCCESS) + goto out; + ret = efi_get_time_init(); + if (ret != EFI_SUCCESS) + goto out; + +out: + efi_obj_list_initialized = ret; + return ret; } /* @@ -186,9 +211,6 @@ static efi_status_t do_bootefi_exec(void *efi, void *fdt, assert(device_path && image_path); } - /* Initialize and populate EFI object list */ - efi_init_obj_list(); - efi_setup_loaded_image(&loaded_image_info, &loaded_image_info_obj, device_path, image_path); @@ -285,9 +307,6 @@ static int do_bootefi_bootmgr_exec(unsigned long fdt_addr) void *addr; efi_status_t r; - /* Initialize and populate EFI object list */ - efi_init_obj_list(); - /* * gd lives in a fixed register which may get clobbered while we execute * the payload. So save it here and restore it on every callback entry @@ -316,6 +335,14 @@ static int do_bootefi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) unsigned long addr, fdt_addr = 0; efi_status_t r; + /* Initialize EFI drivers */ + r = efi_init_obj_list(); + if (r != EFI_SUCCESS) { + printf("Error: Cannot set up EFI drivers, r = %lu\n", + r & ~EFI_ERROR_MASK); + return CMD_RET_FAILURE; + } + if (argc < 2) return CMD_RET_USAGE; #ifdef CONFIG_CMD_BOOTEFI_HELLO From bc4f9133edb4baf5f68689a03137bb2c336e2026 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 3 Mar 2018 15:29:03 +0100 Subject: [PATCH 15/60] 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 --- cmd/bootefi.c | 111 ++++++++++++++++++++-------------- include/efi_loader.h | 2 + lib/efi_loader/efi_boottime.c | 2 + 3 files changed, 68 insertions(+), 47 deletions(-) diff --git a/cmd/bootefi.c b/cmd/bootefi.c index 025a3dd171..6afece10bc 100644 --- a/cmd/bootefi.c +++ b/cmd/bootefi.c @@ -178,11 +178,49 @@ static efi_status_t efi_run_in_el2(EFIAPI efi_status_t (*entry)( } #endif +static efi_status_t efi_install_fdt(void *fdt) +{ + bootm_headers_t img = { 0 }; + ulong fdt_pages, fdt_size, fdt_start, fdt_end; + efi_status_t ret; + + if (fdt_check_header(fdt)) { + printf("ERROR: invalid device tree\n"); + return EFI_INVALID_PARAMETER; + } + + /* Prepare fdt for payload */ + fdt = copy_fdt(fdt); + if (!fdt) + return EFI_OUT_OF_RESOURCES; + + if (image_setup_libfdt(&img, fdt, 0, NULL)) { + printf("ERROR: failed to process device tree\n"); + return EFI_LOAD_ERROR; + } + + /* Link to it in the efi tables */ + ret = efi_install_configuration_table(&efi_guid_fdt, fdt); + if (ret != EFI_SUCCESS) + return EFI_OUT_OF_RESOURCES; + + /* And reserve the space in the memory map */ + fdt_start = ((ulong)fdt) & ~EFI_PAGE_MASK; + fdt_end = ((ulong)fdt) + fdt_totalsize(fdt); + fdt_size = (fdt_end - fdt_start) + EFI_PAGE_MASK; + fdt_pages = fdt_size >> EFI_PAGE_SHIFT; + /* Give a bootloader the chance to modify the device tree */ + fdt_pages += 2; + ret = efi_add_memory_map(fdt_start, fdt_pages, + EFI_BOOT_SERVICES_DATA, true); + return ret; +} + /* * Load an EFI payload into a newly allocated piece of memory, register all * EFI objects it would want to access and jump to it. */ -static efi_status_t do_bootefi_exec(void *efi, void *fdt, +static efi_status_t do_bootefi_exec(void *efi, struct efi_device_path *device_path, struct efi_device_path *image_path) { @@ -193,9 +231,6 @@ static efi_status_t do_bootefi_exec(void *efi, void *fdt, EFIAPI efi_status_t (*entry)(efi_handle_t image_handle, struct efi_system_table *st); - ulong fdt_pages, fdt_size, fdt_start, fdt_end; - const efi_guid_t fdt_guid = EFI_FDT_GUID; - bootm_headers_t img = { 0 }; /* * Special case for efi payload not loaded from disk, such as @@ -220,32 +255,6 @@ static efi_status_t do_bootefi_exec(void *efi, void *fdt, */ efi_save_gd(); - if (fdt && !fdt_check_header(fdt)) { - /* Prepare fdt for payload */ - fdt = copy_fdt(fdt); - - if (image_setup_libfdt(&img, fdt, 0, NULL)) { - printf("ERROR: Failed to process device tree\n"); - return -EINVAL; - } - - /* Link to it in the efi tables */ - efi_install_configuration_table(&fdt_guid, fdt); - - /* And reserve the space in the memory map */ - fdt_start = ((ulong)fdt) & ~EFI_PAGE_MASK; - fdt_end = ((ulong)fdt) + fdt_totalsize(fdt); - fdt_size = (fdt_end - fdt_start) + EFI_PAGE_MASK; - fdt_pages = fdt_size >> EFI_PAGE_SHIFT; - /* Give a bootloader the chance to modify the device tree */ - fdt_pages += 2; - efi_add_memory_map(fdt_start, fdt_pages, - EFI_BOOT_SERVICES_DATA, true); - } else { - printf("WARNING: Invalid device tree, expect boot to fail\n"); - efi_install_configuration_table(&fdt_guid, NULL); - } - /* Transfer environment variable bootargs as load options */ set_load_options(&loaded_image_info, "bootargs"); /* Load the EFI payload */ @@ -301,7 +310,7 @@ exit: return ret; } -static int do_bootefi_bootmgr_exec(unsigned long fdt_addr) +static int do_bootefi_bootmgr_exec(void) { struct efi_device_path *device_path, *file_path; void *addr; @@ -318,7 +327,7 @@ static int do_bootefi_bootmgr_exec(unsigned long fdt_addr) return 1; printf("## Starting EFI application at %p ...\n", addr); - r = do_bootefi_exec(addr, (void *)fdt_addr, device_path, file_path); + r = do_bootefi_exec(addr, device_path, file_path); printf("## Application terminated, r = %lu\n", r & ~EFI_ERROR_MASK); @@ -331,9 +340,10 @@ static int do_bootefi_bootmgr_exec(unsigned long fdt_addr) /* Interpreter command to boot an arbitrary EFI image from memory */ static int do_bootefi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { - char *saddr, *sfdt; - unsigned long addr, fdt_addr = 0; + unsigned long addr; + char *saddr; efi_status_t r; + void *fdt_addr; /* Initialize EFI drivers */ r = efi_init_obj_list(); @@ -345,6 +355,22 @@ static int do_bootefi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) if (argc < 2) return CMD_RET_USAGE; + + if (argc > 2) { + fdt_addr = (void *)simple_strtoul(argv[2], NULL, 16); + if (!fdt_addr && *argv[2] != '0') + return CMD_RET_USAGE; + /* Install device tree */ + r = efi_install_fdt(fdt_addr); + if (r != EFI_SUCCESS) { + printf("ERROR: failed to install device tree\n"); + return CMD_RET_FAILURE; + } + } else { + /* Remove device tree. EFI_NOT_FOUND can be ignored here */ + efi_install_configuration_table(&efi_guid_fdt, NULL); + printf("WARNING: booting without device tree\n"); + } #ifdef CONFIG_CMD_BOOTEFI_HELLO if (!strcmp(argv[1], "hello")) { ulong size = __efi_helloworld_end - __efi_helloworld_begin; @@ -390,12 +416,7 @@ static int do_bootefi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) } else #endif if (!strcmp(argv[1], "bootmgr")) { - unsigned long fdt_addr = 0; - - if (argc > 2) - fdt_addr = simple_strtoul(argv[2], NULL, 16); - - return do_bootefi_bootmgr_exec(fdt_addr); + return do_bootefi_bootmgr_exec(); } else { saddr = argv[1]; @@ -404,15 +425,11 @@ static int do_bootefi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) if (!addr && *saddr != '0') return CMD_RET_USAGE; - if (argc > 2) { - sfdt = argv[2]; - fdt_addr = simple_strtoul(sfdt, NULL, 16); - } } printf("## Starting EFI application at %08lx ...\n", addr); - r = do_bootefi_exec((void *)addr, (void *)fdt_addr, - bootefi_device_path, bootefi_image_path); + r = do_bootefi_exec((void *)addr, bootefi_device_path, + bootefi_image_path); printf("## Application terminated, r = %lu\n", r & ~EFI_ERROR_MASK); @@ -433,7 +450,7 @@ static char bootefi_help_text[] = " - boot a sample Hello World application stored within U-Boot\n" #endif #ifdef CONFIG_CMD_BOOTEFI_SELFTEST - "bootefi selftest\n" + "bootefi selftest [fdt address]\n" " - boot an EFI selftest application stored within U-Boot\n" " Use environment variable efi_selftest to select a single test.\n" " Use 'setenv efi_selftest list' to enumerate all tests.\n" diff --git a/include/efi_loader.h b/include/efi_loader.h index b6335182ed..60f84dcf24 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -96,6 +96,8 @@ extern const efi_guid_t efi_guid_console_control; extern const efi_guid_t efi_guid_device_path; /* GUID of the EFI_DRIVER_BINDING_PROTOCOL */ extern const efi_guid_t efi_guid_driver_binding_protocol; +/* GUID of the device tree table */ +extern const efi_guid_t efi_guid_fdt; extern const efi_guid_t efi_guid_loaded_image; extern const efi_guid_t efi_guid_device_path_to_text_protocol; extern const efi_guid_t efi_simple_file_system_protocol_guid; diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 4e133e9b47..3cee8d607c 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; From 06c3d5b989d15a86e2b0a5d49e0bcda365d35baa Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 3 Mar 2018 15:29:04 +0100 Subject: [PATCH 16/60] 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 ++++++++++++++++++++++++++++ test/py/tests/test_efi_selftest.py | 14 +++ 3 files changed, 203 insertions(+) create mode 100644 lib/efi_selftest/efi_selftest_fdt.c diff --git a/lib/efi_selftest/Makefile b/lib/efi_selftest/Makefile index c4bdbdf6c0..88c44840d5 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 0000000000..24db0dcf7d --- /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, +}; diff --git a/test/py/tests/test_efi_selftest.py b/test/py/tests/test_efi_selftest.py index 66b799bed6..b1ef6bd581 100644 --- a/test/py/tests/test_efi_selftest.py +++ b/test/py/tests/test_efi_selftest.py @@ -24,6 +24,20 @@ def test_efi_selftest(u_boot_console): raise Exception('Reset failed during the EFI selftest') u_boot_console.restart_uboot(); +@pytest.mark.buildconfigspec('cmd_bootefi_selftest') +@pytest.mark.buildconfigspec('of_control') +def test_efi_selftest_device_tree(u_boot_console): + u_boot_console.run_command(cmd='setenv efi_selftest list') + output = u_boot_console.run_command('bootefi selftest') + assert '\'device tree\'' in output + u_boot_console.run_command(cmd='setenv efi_selftest device tree') + u_boot_console.run_command(cmd='setenv -f serial# Testing DT') + u_boot_console.run_command(cmd='bootefi selftest ${fdtcontroladdr}', wait_for_prompt=False) + m = u_boot_console.p.expect(['serial-number: Testing DT', 'U-Boot']) + if m != 0: + raise Exception('Reset failed in \'device tree\' test') + u_boot_console.restart_uboot(); + @pytest.mark.buildconfigspec('cmd_bootefi_selftest') def test_efi_selftest_watchdog_reboot(u_boot_console): u_boot_console.run_command(cmd='setenv efi_selftest list') From 0fb4169e2819d6950708154789287e4ad9b40a91 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Mon, 19 Feb 2018 18:53:29 +0100 Subject: [PATCH 17/60] 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(-) diff --git a/lib/efi_loader/efi_console.c b/lib/efi_loader/efi_console.c index 3cb580e3ed..d0e8617d0d 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); From eb68b4ef31cd7d0fe08becda7ac1e56b45f9054a Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sun, 18 Feb 2018 00:08:00 +0100 Subject: [PATCH 18/60] 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(+) diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 3cee8d607c..9ca2e8161e 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)) { From 7069515e7f82fea281160de9c8af1bb857918a96 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sun, 18 Feb 2018 11:32:02 +0100 Subject: [PATCH 19/60] 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(-) diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 9ca2e8161e..8137ef521e 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); From ab9efa979cc989d7c193e7169101e29ff7822e85 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sun, 18 Feb 2018 15:17:49 +0100 Subject: [PATCH 20/60] 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(-) diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 8137ef521e..1a2154591c 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, }; From 43bce44262ca7cbb04bb236c50571ca84eb6fdd9 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sun, 18 Feb 2018 15:17:50 +0100 Subject: [PATCH 21/60] 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 --- include/efi_loader.h | 11 +- lib/efi_loader/efi_boottime.c | 192 +++++++++++++++------------------- 2 files changed, 93 insertions(+), 110 deletions(-) diff --git a/include/efi_loader.h b/include/efi_loader.h index 60f84dcf24..1a670586b9 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -150,17 +150,19 @@ struct efi_object { /** * struct efi_event * + * @link: Link to list of all events * @type: Type of event, see efi_create_event * @notify_tpl: Task priority level of notifications - * @trigger_time: Period of the timer - * @trigger_next: Next time to trigger the timer * @nofify_function: Function to call when the event is triggered * @notify_context: Data to be passed to the notify function + * @trigger_time: Period of the timer + * @trigger_next: Next time to trigger the timer * @trigger_type: Type of timer, see efi_set_timer - * @queued: The notification function is queued - * @signaled: The event occurred. The event is in the signaled state. + * @is_queued: The notification function is queued + * @is_signaled: The event occurred. The event is in the signaled state. */ struct efi_event { + struct list_head link; uint32_t type; efi_uintn_t notify_tpl; void (EFIAPI *notify_function)(struct efi_event *event, void *context); @@ -172,7 +174,6 @@ struct efi_event { bool is_signaled; }; - /* This list contains all UEFI objects we know of */ extern struct list_head efi_obj_list; diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 1a2154591c..ca43925be4 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 */ From a3a28f5f0c39d94733d23e2bbbb53e6e4ddb46c6 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sun, 18 Feb 2018 15:17:51 +0100 Subject: [PATCH 22/60] 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 --- include/efi_api.h | 21 +++++++++++++++++++++ include/efi_loader.h | 10 ++++++++++ lib/efi_loader/efi_boottime.c | 16 ++++++++++++++++ 3 files changed, 47 insertions(+) diff --git a/include/efi_api.h b/include/efi_api.h index 827a192620..433594f339 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -243,6 +243,27 @@ struct efi_runtime_services { u64 maximum_variable_size); }; +/* EFI event group GUID definitions */ +#define EFI_EVENT_GROUP_EXIT_BOOT_SERVICES \ + EFI_GUID(0x27abf055, 0xb1b8, 0x4c26, 0x80, 0x48, \ + 0x74, 0x8f, 0x37, 0xba, 0xa2, 0xdf) + +#define EFI_EVENT_GROUP_VIRTUAL_ADDRESS_CHANGE \ + EFI_GUID(0x13fa7698, 0xc831, 0x49c7, 0x87, 0xea, \ + 0x8f, 0x43, 0xfc, 0xc2, 0x51, 0x96) + +#define EFI_EVENT_GROUP_MEMORY_MAP_CHANGE \ + EFI_GUID(0x78bee926, 0x692f, 0x48fd, 0x9e, 0xdb, \ + 0x01, 0x42, 0x2e, 0xf0, 0xd7, 0xab) + +#define EFI_EVENT_GROUP_READY_TO_BOOT \ + EFI_GUID(0x7ce88fb3, 0x4bd7, 0x4679, 0x87, 0xa8, \ + 0xa8, 0xd8, 0xde, 0xe5, 0x0d, 0x2b) + +#define EFI_EVENT_GROUP_RESET_SYSTEM \ + EFI_GUID(0x62da6a56, 0x13fb, 0x485a, 0xa8, 0xda, \ + 0xa3, 0xdd, 0x79, 0x12, 0xcb, 0x6b) + /* EFI Configuration Table and GUID definitions */ #define NULL_GUID \ EFI_GUID(0x00000000, 0x0000, 0x0000, 0x00, 0x00, \ diff --git a/include/efi_loader.h b/include/efi_loader.h index 1a670586b9..8596c0e729 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -96,6 +96,16 @@ extern const efi_guid_t efi_guid_console_control; extern const efi_guid_t efi_guid_device_path; /* GUID of the EFI_DRIVER_BINDING_PROTOCOL */ extern const efi_guid_t efi_guid_driver_binding_protocol; +/* event group ExitBootServices() invoked */ +extern const efi_guid_t efi_guid_event_group_exit_boot_services; +/* event group SetVirtualAddressMap() invoked */ +extern const efi_guid_t efi_guid_event_group_virtual_address_change; +/* event group memory map changed */ +extern const efi_guid_t efi_guid_event_group_memory_map_change; +/* event group boot manager about to boot */ +extern const efi_guid_t efi_guid_event_group_ready_to_boot; +/* event group ResetSystem() invoked (before ExitBootServices) */ +extern const efi_guid_t efi_guid_event_group_reset_system; /* GUID of the device tree table */ extern const efi_guid_t efi_guid_fdt; extern const efi_guid_t efi_guid_loaded_image; diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index ca43925be4..d9abb3cced 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, From b095f3c85fe08744d6081b98db65768f3421295e Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sun, 18 Feb 2018 15:17:52 +0100 Subject: [PATCH 23/60] 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 --- include/efi_loader.h | 7 ++- 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 +- 6 files changed, 101 insertions(+), 28 deletions(-) diff --git a/include/efi_loader.h b/include/efi_loader.h index 8596c0e729..0df482ee21 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -165,6 +165,7 @@ struct efi_object { * @notify_tpl: Task priority level of notifications * @nofify_function: Function to call when the event is triggered * @notify_context: Data to be passed to the notify function + * @group: Event group * @trigger_time: Period of the timer * @trigger_next: Next time to trigger the timer * @trigger_type: Type of timer, see efi_set_timer @@ -177,6 +178,7 @@ struct efi_event { efi_uintn_t notify_tpl; void (EFIAPI *notify_function)(struct efi_event *event, void *context); void *notify_context; + const efi_guid_t *group; u64 trigger_next; u64 trigger_time; enum efi_timer_delay trigger_type; @@ -186,6 +188,8 @@ struct efi_event { /* This list contains all UEFI objects we know of */ extern struct list_head efi_obj_list; +/* List of all events */ +extern struct list_head efi_events; /* Called by bootefi to make console interface available */ int efi_console_register(void); @@ -252,7 +256,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); /* Call this to set a timer */ efi_status_t efi_set_timer(struct efi_event *event, enum efi_timer_delay type, uint64_t trigger_time); diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index d9abb3cced..1ff0568d47 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 d0e8617d0d..5d1a9a8081 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 e7fed79450..b88dc91f58 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 c59d161c8f..9e281728f9 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 b1c35a8e29..d12e51da0a 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"); From 0e0a3ceb509b19af55c5064607d9057ec7d8c1c4 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Wed, 7 Feb 2018 22:14:22 +0100 Subject: [PATCH 24/60] 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 --- include/efi_api.h | 10 ++- lib/efi_loader/efi_gop.c | 174 +++++++++++++++++++++++++++++---------- 2 files changed, 139 insertions(+), 45 deletions(-) diff --git a/include/efi_api.h b/include/efi_api.h index 433594f339..167c44e3a4 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -665,6 +665,13 @@ struct efi_gop_mode unsigned long fb_size; }; +struct efi_gop_pixel { + u8 blue; + u8 green; + u8 red; + u8 reserved; +}; + #define EFI_BLT_VIDEO_FILL 0 #define EFI_BLT_VIDEO_TO_BLT_BUFFER 1 #define EFI_BLT_BUFFER_TO_VIDEO 2 @@ -676,7 +683,8 @@ struct efi_gop efi_uintn_t *size_of_info, struct efi_gop_mode_info **info); efi_status_t (EFIAPI *set_mode)(struct efi_gop *this, u32 mode_number); - efi_status_t (EFIAPI *blt)(struct efi_gop *this, void *buffer, + efi_status_t (EFIAPI *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, diff --git a/lib/efi_loader/efi_gop.c b/lib/efi_loader/efi_gop.c index 28818eb781..154f306540 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) - return EFI_EXIT(EFI_INVALID_PARAMETER); - - fb = gopobj->fb; - line_len16 = gopobj->info.width * sizeof(u16); - line_len32 = gopobj->info.width * sizeof(u32); - - /* Copy the contents line by line */ - - switch (gopobj->bpix) { -#ifdef CONFIG_DM_VIDEO - case VIDEO_BPP32: -#else - case LCD_COLOR32: -#endif - for (i = 0; i < height; i++) { - u32 *dest = fb + ((i + dy) * line_len32) + - (dx * sizeof(u32)); - u32 *src = buffer + ((i + sy) * line_len32) + - (sx * sizeof(u32)); - - /* Same color format, just memcpy */ - memcpy(dest, src, width * sizeof(u32)); - } + /* Check source rectangle */ + switch (operation) { + case EFI_BLT_VIDEO_FILL: break; -#ifdef CONFIG_DM_VIDEO - case VIDEO_BPP16: -#else - case LCD_COLOR16: -#endif - for (i = 0; i < height; i++) { - u16 *dest = fb + ((i + dy) * line_len16) + - (dx * sizeof(u16)); - u32 *src = buffer + ((i + sy) * line_len32) + - (sx * sizeof(u32)); + 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); + } - /* 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)); + /* 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; + } + + 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; + } + + /* 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: +#else + case LCD_COLOR32: +#endif + fb32[gopobj->info.width * + (i + dy) + j + dx] = *(u32 *)&pix; + break; +#ifdef CONFIG_DM_VIDEO + case VIDEO_BPP16: +#else + case LCD_COLOR16: +#endif + 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 From b944e4711137a60064ef353a7d5972e59fe66c97 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Wed, 7 Feb 2018 22:14:23 +0100 Subject: [PATCH 25/60] 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 diff --git a/lib/efi_selftest/Makefile b/lib/efi_selftest/Makefile index 88c44840d5..b975da2955 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 0000000000..53cc633acc --- /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, +}; From 482fc90c0a93bdab8473bbd4c8d63732ce888bba Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Tue, 6 Feb 2018 22:00:22 +0100 Subject: [PATCH 26/60] 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 --- arch/arm/cpu/armv8/fsl-layerscape/cpu.c | 1 + arch/arm/cpu/armv8/fwcall.c | 1 + arch/arm/mach-bcm283x/reset.c | 1 + include/efi_api.h | 3 ++- lib/efi_loader/efi_runtime.c | 1 + 5 files changed, 6 insertions(+), 1 deletion(-) diff --git a/arch/arm/cpu/armv8/fsl-layerscape/cpu.c b/arch/arm/cpu/armv8/fsl-layerscape/cpu.c index 0e90302d3f..45cbd91d97 100644 --- a/arch/arm/cpu/armv8/fsl-layerscape/cpu.c +++ b/arch/arm/cpu/armv8/fsl-layerscape/cpu.c @@ -644,6 +644,7 @@ void __efi_runtime EFIAPI efi_reset_system( switch (reset_type) { case EFI_RESET_COLD: case EFI_RESET_WARM: + case EFI_RESET_PLATFORM_SPECIFIC: reset_cpu(0); break; case EFI_RESET_SHUTDOWN: diff --git a/arch/arm/cpu/armv8/fwcall.c b/arch/arm/cpu/armv8/fwcall.c index c220267536..ff0712bf65 100644 --- a/arch/arm/cpu/armv8/fwcall.c +++ b/arch/arm/cpu/armv8/fwcall.c @@ -146,6 +146,7 @@ void __efi_runtime EFIAPI efi_reset_system( switch (reset_type) { case EFI_RESET_COLD: case EFI_RESET_WARM: + case EFI_RESET_PLATFORM_SPECIFIC: psci_system_reset(); break; case EFI_RESET_SHUTDOWN: diff --git a/arch/arm/mach-bcm283x/reset.c b/arch/arm/mach-bcm283x/reset.c index 5b83fdf43d..aa02d3f9f6 100644 --- a/arch/arm/mach-bcm283x/reset.c +++ b/arch/arm/mach-bcm283x/reset.c @@ -63,6 +63,7 @@ void __efi_runtime EFIAPI efi_reset_system( switch (reset_type) { case EFI_RESET_COLD: case EFI_RESET_WARM: + case EFI_RESET_PLATFORM_SPECIFIC: reset_cpu(0); break; case EFI_RESET_SHUTDOWN: diff --git a/include/efi_api.h b/include/efi_api.h index 167c44e3a4..28de93a132 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -180,7 +180,8 @@ struct efi_boot_services { enum efi_reset_type { EFI_RESET_COLD = 0, EFI_RESET_WARM = 1, - EFI_RESET_SHUTDOWN = 2 + EFI_RESET_SHUTDOWN = 2, + EFI_RESET_PLATFORM_SPECIFIC = 3, }; /* EFI Runtime Services table */ diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c index 9e281728f9..0888316140 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: From 0aa2da788bb5c6dd249e33a8d3416f9d2ae9ee09 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 3 Mar 2018 15:39:53 +0100 Subject: [PATCH 27/60] 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 diff --git a/lib/efi_selftest/Makefile b/lib/efi_selftest/Makefile index b975da2955..2c44251704 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 0000000000..c890ff88b7 --- /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, +}; From f7c342f413a97c8fb097c3a0649aa5bbc2bcfd26 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Mon, 5 Feb 2018 18:24:26 +0100 Subject: [PATCH 28/60] 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(+) diff --git a/lib/efi_loader/helloworld.c b/lib/efi_loader/helloworld.c index 1ec0179226..6c539ba204 100644 --- a/lib/efi_loader/helloworld.c +++ b/lib/efi_loader/helloworld.c @@ -46,9 +46,27 @@ efi_status_t EFIAPI efi_main(efi_handle_t handle, struct efi_loaded_image *loaded_image; efi_status_t ret; efi_uintn_t i; + u16 rev[] = L"0.0.0"; con_out->output_string(con_out, L"Hello, world!\n"); + /* Print the revision number */ + rev[0] = (systable->hdr.revision >> 16) + '0'; + rev[4] = systable->hdr.revision & 0xffff; + for (; rev[4] >= 10;) { + rev[4] -= 10; + ++rev[2]; + } + /* Third digit is only to be shown if non-zero */ + if (rev[4]) + rev[4] += '0'; + else + rev[3] = 0; + + con_out->output_string(con_out, L"Running on UEFI "); + con_out->output_string(con_out, rev); + con_out->output_string(con_out, L"\n"); + /* Get the loaded image protocol */ ret = boottime->handle_protocol(handle, &loaded_image_guid, (void **)&loaded_image); From 7c92fd69b19bf4bfdc7c3fa74c504b66019004f0 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Tue, 30 Jan 2018 21:08:00 +0100 Subject: [PATCH 29/60] 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(-) diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c index ff0edf30ff..1e413d01cb 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; From bdecaebd5d5e266301b4bc4064b5fbc0922874d2 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 8 Mar 2018 21:53:27 +0100 Subject: [PATCH 30/60] 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 | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c index 1e413d01cb..95f9ff0a14 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 +#include DECLARE_GLOBAL_DATA_PTR; From 9967adb71db685c0b45312f966e954d9fe5293c7 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Mon, 12 Mar 2018 19:52:25 +0100 Subject: [PATCH 31/60] 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(-) diff --git a/lib/efi_selftest/efi_selftest_fdt.c b/lib/efi_selftest/efi_selftest_fdt.c index 24db0dcf7d..e5a8d6a6ae 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; From 6fc901c538907f48d4253a6e23987df2268f75ae Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sun, 18 Feb 2018 15:17:53 +0100 Subject: [PATCH 32/60] 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 diff --git a/lib/efi_selftest/Makefile b/lib/efi_selftest/Makefile index 2c44251704..31b444fc8b 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 0000000000..79e4ea1ce2 --- /dev/null +++ b/lib/efi_selftest/efi_selftest_event_groups.c @@ -0,0 +1,140 @@ +/* + * efi_selftest_event_groups + * + * Copyright (c) 2018 Heinrich Schuchardt + * + * 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, +}; From 51a0f4512295429a8511d8fbce2e4d26d6bc1fcb Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Wed, 14 Mar 2018 19:57:02 +0100 Subject: [PATCH 33/60] 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(-) diff --git a/lib/efi_loader/efi_gop.c b/lib/efi_loader/efi_gop.c index 154f306540..ac92109f16 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 53cc633acc..0fb76cc727 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; From 0176330ee5b6a316b688787d10f0265215e53d33 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Wed, 4 Apr 2018 10:56:00 +0200 Subject: [PATCH 34/60] git: mailrc: Add myself and efi to list I wasn't listed in the mailrc before, let's fix that. Signed-off-by: Alexander Graf --- doc/git-mailrc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/git-mailrc b/doc/git-mailrc index 5a365cddd9..d5e309708c 100644 --- a/doc/git-mailrc +++ b/doc/git-mailrc @@ -15,6 +15,7 @@ alias abiessmann Andreas Bießmann alias abrodkin Alexey Brodkin alias afleming Andy Fleming alias ag Anatolij Gustschin +alias agraf Alexander Graf alias alisonwang Alison Wang alias angelo_ts Angelo Dureghello alias bmeng Bin Meng @@ -120,6 +121,7 @@ alias x86 uboot, sjg, bmeng alias dm uboot, sjg alias cfi uboot, stroese alias dfu uboot, lukma +alias efi uboot, agraf alias eth uboot, jhersh alias kerneldoc uboot, marex alias fdt uboot, sjg From ee3db4fc04714c80196e49f8f3a5f157f20d2862 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 24 Mar 2018 18:40:22 +0100 Subject: [PATCH 35/60] 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(-) diff --git a/lib/efi_loader/efi_net.c b/lib/efi_loader/efi_net.c index b88dc91f58..3d860e658e 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) { From 0f7fcc72565ce411941a12de0fb0ea5538a17cd0 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 24 Mar 2018 18:40:21 +0100 Subject: [PATCH 36/60] 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(+) diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 1ff0568d47..fd35ffa359 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); } From 90b658b4cc9f6b9d50a80fac1fbc62bd6420a483 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Fri, 16 Mar 2018 19:59:06 +0100 Subject: [PATCH 37/60] 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(-) diff --git a/lib/efi_loader/efi_gop.c b/lib/efi_loader/efi_gop.c index ac92109f16..b0c3d59b85 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 | From ba718e67a27f036093d352af46b8ebcb1bbb8316 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Thu, 15 Mar 2018 15:02:28 +0100 Subject: [PATCH 38/60] 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(-) diff --git a/lib/efi_loader/efi_gop.c b/lib/efi_loader/efi_gop.c index b0c3d59b85..26d6f02353 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 From 8e475064097d182191129e31846c585421f0f85a Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Thu, 15 Mar 2018 15:02:29 +0100 Subject: [PATCH 39/60] 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 | 178 +++++++++++++++++++++++++-------------- 1 file changed, 115 insertions(+), 63 deletions(-) diff --git a/lib/efi_loader/efi_gop.c b/lib/efi_loader/efi_gop.c index 26d6f02353..363ccbb789 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,72 +176,67 @@ 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) { -#ifdef CONFIG_DM_VIDEO - case VIDEO_BPP32: -#else - case LCD_COLOR32: -#endif - fb32[gopobj->info.width * - (i + dy) + j + dx] = *(u32 *)&pix; - break; -#ifdef CONFIG_DM_VIDEO - case VIDEO_BPP16: -#else - case LCD_COLOR16: -#endif - fb16[gopobj->info.width * - (i + dy) + j + dx] = + if (vid_bpp == 32) + fb32[dlineoff + j + dx] = *(u32 *)&pix; + else + fb16[dlineoff + j + dx] = efi_blt_col_to_vid16(&pix); - break; - default: - return EFI_UNSUPPORTED; - } 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: +#else + case LCD_COLOR32: +#endif + vid_bpp = 32; + break; +#ifdef CONFIG_DM_VIDEO + case VIDEO_BPP16: +#else + case LCD_COLOR16: +#endif + vid_bpp = 16; + break; + } + + return vid_bpp; +} + /* * Gcc can't optimize our BLT function well, but we need to make sure that * our 2-dimensional loop gets executed very quickly, otherwise the system @@ -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; From 813468cdbd7287f0b2e38f9702aa3eee37b1c5b5 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Thu, 15 Mar 2018 15:08:16 +0100 Subject: [PATCH 40/60] 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(-) diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c index 0888316140..8558124c0a 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; } /* From f9cfad1a61e3461dcf256ecfb83f4eaf68142d1b Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Thu, 15 Mar 2018 17:33:38 +0100 Subject: [PATCH 41/60] 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(-) diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c index 3c735e60d3..22627824f0 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; From e2742358661137b60eccd2e80b6835b58d8c5763 Mon Sep 17 00:00:00 2001 From: Patrick Wildt Date: Tue, 27 Mar 2018 14:23:20 +0200 Subject: [PATCH 42/60] efi_loader: complete efi_pxe_mode struct definition The efi_pxe_mode struct which represents the PXE_BASE_CODE_PROTOCOL Replace the placeholder paddings in the efi_pxe_mode struct with the actual fields as defined in UEFI's PXE Base Code Protocol. Since our efi_ip_address is a simple char array set a specific alignment to the efi_ip_address fields, as expected by the UEFI spec. Signed-off-by: Patrick Wildt [agraf: s/pxe_discovervalid/pxe_discover_valid] Signed-off-by: Alexander Graf --- include/efi_api.h | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/include/efi_api.h b/include/efi_api.h index 28de93a132..d956aee560 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -703,7 +703,7 @@ struct efi_mac_address { struct efi_ip_address { u8 ip_addr[16]; -}; +} __attribute__((aligned(4))); enum efi_simple_network_state { EFI_NETWORK_STOPPED, @@ -797,7 +797,28 @@ struct efi_pxe_packet { struct efi_pxe_mode { - u8 unused[52]; + u8 started; + u8 ipv6_available; + u8 ipv6_supported; + u8 using_ipv6; + u8 bis_supported; + u8 bis_detected; + u8 auto_arp; + u8 send_guid; + u8 dhcp_discover_valid; + u8 dhcp_ack_received; + u8 proxy_offer_received; + u8 pxe_discover_valid; + u8 pxe_reply_received; + u8 pxe_bis_reply_received; + u8 icmp_error_received; + u8 tftp_error_received; + u8 make_callbacks; + u8 ttl; + u8 tos; + u8 pad; + struct efi_ip_address station_ip; + struct efi_ip_address subnet_mask; struct efi_pxe_packet dhcp_discover; struct efi_pxe_packet dhcp_ack; struct efi_pxe_packet proxy_offer; From eab2dc37ee457191583c5d9ff26ce9d7ccda3637 Mon Sep 17 00:00:00 2001 From: Patrick Wildt Date: Sun, 25 Mar 2018 19:54:03 +0200 Subject: [PATCH 43/60] 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(+) diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c index 22627824f0..ab28b2fd25 100644 --- a/lib/efi_loader/efi_device_path.c +++ b/lib/efi_loader/efi_device_path.c @@ -66,6 +66,7 @@ static void *dp_alloc(size_t sz) return NULL; } + memset(buf, 0, sz); return buf; } From 7fb96a10b31953cde698326a61e963ba9df1dc1f Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Tue, 3 Apr 2018 22:29:30 +0200 Subject: [PATCH 44/60] 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 --- include/efi_api.h | 2 +- lib/efi_loader/efi_boottime.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/efi_api.h b/include/efi_api.h index d956aee560..d9a69dbc00 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -107,7 +107,7 @@ struct efi_boot_services { efi_status_t (EFIAPI *load_image)(bool boot_policiy, efi_handle_t parent_image, struct efi_device_path *file_path, void *source_buffer, - unsigned long source_size, efi_handle_t *image); + efi_uintn_t source_size, efi_handle_t *image); efi_status_t (EFIAPI *start_image)(efi_handle_t handle, unsigned long *exitdata_size, s16 **exitdata); diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index fd35ffa359..d15a131e74 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) { From 84b40b40ad9b95b4680766597c866b604147144c Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Tue, 3 Apr 2018 22:29:31 +0200 Subject: [PATCH 45/60] 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 --- include/efi_api.h | 2 ++ lib/efi_loader/efi_image_loader.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/include/efi_api.h b/include/efi_api.h index d9a69dbc00..2be470b237 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -331,6 +331,8 @@ struct efi_loaded_image { /* Below are efi loader private fields */ #ifdef CONFIG_EFI_LOADER + void *reloc_base; + aligned_u64 reloc_size; efi_status_t exit_status; struct jmp_buf_data exit_jmp; #endif diff --git a/lib/efi_loader/efi_image_loader.c b/lib/efi_loader/efi_image_loader.c index cac64ba9fe..701387b95f 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; } From 82786754b9d21a05a70c45ff7e9e48c91745f837 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Tue, 3 Apr 2018 22:29:32 +0200 Subject: [PATCH 46/60] 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(+) diff --git a/lib/efi_loader/efi_image_loader.c b/lib/efi_loader/efi_image_loader.c index 701387b95f..74c6a9f921 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); From 43dace5d897ef1ac5eadaf906d77f671e018116f Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Tue, 3 Apr 2018 22:29:33 +0200 Subject: [PATCH 47/60] efi_loader: correct types for EFI_LOADED_IMAGE_PROTOCOL We should not use void * but specific types for * device_handle * file_path Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- include/efi_api.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/efi_api.h b/include/efi_api.h index 2be470b237..f5bb6ec171 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -318,8 +318,8 @@ struct efi_loaded_image { u32 revision; void *parent_handle; struct efi_system_table *system_table; - void *device_handle; - void *file_path; + efi_handle_t device_handle; + struct efi_device_path *file_path; void *reserved; u32 load_options_size; void *load_options; From b6dd57773719bfcea6a295973c349b7842870337 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Tue, 3 Apr 2018 22:37:11 +0200 Subject: [PATCH 48/60] 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 --- include/efi_api.h | 14 +++--- lib/efi_loader/efi_boottime.c | 5 ++- lib/efi_loader/efi_file.c | 47 +++++++++++++++----- lib/efi_selftest/efi_selftest_block_device.c | 2 +- 4 files changed, 47 insertions(+), 21 deletions(-) diff --git a/include/efi_api.h b/include/efi_api.h index f5bb6ec171..396f45eaa8 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -858,17 +858,19 @@ struct efi_file_handle { efi_status_t (EFIAPI *close)(struct efi_file_handle *file); efi_status_t (EFIAPI *delete)(struct efi_file_handle *file); efi_status_t (EFIAPI *read)(struct efi_file_handle *file, - u64 *buffer_size, void *buffer); + efi_uintn_t *buffer_size, void *buffer); efi_status_t (EFIAPI *write)(struct efi_file_handle *file, - u64 *buffer_size, void *buffer); + efi_uintn_t *buffer_size, void *buffer); efi_status_t (EFIAPI *getpos)(struct efi_file_handle *file, - u64 *pos); + efi_uintn_t *pos); efi_status_t (EFIAPI *setpos)(struct efi_file_handle *file, - u64 pos); + efi_uintn_t pos); efi_status_t (EFIAPI *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); efi_status_t (EFIAPI *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_status_t (EFIAPI *flush)(struct efi_file_handle *file); }; diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index d15a131e74..7a9449f59c 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 52a4e7438e..0be0f8b807 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 9e4b93d9a6..b07b22465f 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); From 2db1eba1c3717856ebcf01727379ad2d3f19b3cc Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Tue, 3 Apr 2018 22:40:55 +0200 Subject: [PATCH 49/60] efi_loader: correctly determine the boot partition The device path of the loaded image should be set to the partition from which the image was loaded. This requires using the same logic as the load command. Without the patch the device path pointed to the whole disk after executing load mmc 0: 0x43000000 FILE and not to the boot partition from which the file was actually loaded. Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- cmd/bootefi.c | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/cmd/bootefi.c b/cmd/bootefi.c index 6afece10bc..2a31a914cd 100644 --- a/cmd/bootefi.c +++ b/cmd/bootefi.c @@ -468,16 +468,6 @@ U_BOOT_CMD( bootefi_help_text ); -static int parse_partnum(const char *devnr) -{ - const char *str = strchr(devnr, ':'); - if (str) { - str++; - return simple_strtoul(str, NULL, 16); - } - return 0; -} - void efi_set_bootdev(const char *dev, const char *devnr, const char *path) { char filename[32] = { 0 }; /* dp->str is u16[32] long */ @@ -485,12 +475,13 @@ void efi_set_bootdev(const char *dev, const char *devnr, const char *path) if (strcmp(dev, "Net")) { struct blk_desc *desc; + disk_partition_t fs_partition; int part; - desc = blk_get_dev(dev, simple_strtol(devnr, NULL, 10)); - if (!desc) + part = blk_get_device_part_str(dev, devnr, &desc, &fs_partition, + 1); + if (part < 0) return; - part = parse_partnum(devnr); bootefi_device_path = efi_dp_from_part(desc, part); } else { From 9c9021e24571505d76969d8fd37b3b5e06ad2590 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Wed, 4 Apr 2018 15:42:09 +0200 Subject: [PATCH 50/60] 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 --- include/efi_api.h | 4 ++-- lib/efi_loader/efi_file.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/efi_api.h b/include/efi_api.h index 396f45eaa8..8af466a6dc 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -866,10 +866,10 @@ struct efi_file_handle { efi_status_t (EFIAPI *setpos)(struct efi_file_handle *file, efi_uintn_t pos); efi_status_t (EFIAPI *getinfo)(struct efi_file_handle *file, - efi_guid_t *info_type, efi_uintn_t *buffer_size, + const efi_guid_t *info_type, efi_uintn_t *buffer_size, void *buffer); efi_status_t (EFIAPI *setinfo)(struct efi_file_handle *file, - efi_guid_t *info_type, efi_uintn_t buffer_size, + const efi_guid_t *info_type, efi_uintn_t buffer_size, void *buffer); efi_status_t (EFIAPI *flush)(struct efi_file_handle *file); }; diff --git a/lib/efi_loader/efi_file.c b/lib/efi_loader/efi_file.c index 0be0f8b807..2fc77cfb87 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) { From c412166de28e7f7722b42a4c4c5e637da2d570ee Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Wed, 4 Apr 2018 15:42:10 +0200 Subject: [PATCH 51/60] efi_loader: ascii2unicode(): add trailing \0 When converting an ASCII string to UTF-16 don't forget to copy the trailing \0. Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- include/efi_loader.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/efi_loader.h b/include/efi_loader.h index 0df482ee21..21e6692e92 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -352,6 +352,7 @@ static inline void ascii2unicode(u16 *unicode, const char *ascii) { while (*ascii) *(unicode++) = *(ascii++); + *unicode = 0; } static inline int guidcmp(const efi_guid_t *g1, const efi_guid_t *g2) From 9e6835e6baab3d56e1bb2d4b96ee80f98e2196e7 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Wed, 4 Apr 2018 15:42:11 +0200 Subject: [PATCH 52/60] 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 --- include/efi_api.h | 13 +++++++++++++ include/efi_loader.h | 2 ++ lib/efi_loader/efi_file.c | 38 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 53 insertions(+) diff --git a/include/efi_api.h b/include/efi_api.h index 8af466a6dc..ae93061160 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -889,6 +889,10 @@ struct efi_simple_file_system_protocol { EFI_GUID(0x9576e92, 0x6d3f, 0x11d2, \ 0x8e, 0x39, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b) +#define EFI_FILE_SYSTEM_INFO_GUID \ + EFI_GUID(0x09576e93, 0x6d3f, 0x11d2, \ + 0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b) + #define EFI_FILE_MODE_READ 0x0000000000000001 #define EFI_FILE_MODE_WRITE 0x0000000000000002 #define EFI_FILE_MODE_CREATE 0x8000000000000000 @@ -912,6 +916,15 @@ struct efi_file_info { s16 file_name[0]; }; +struct efi_file_system_info { + u64 size; + u8 read_only; + u64 volume_size; + u64 free_space; + u32 block_size; + u16 volume_label[0]; +}; + #define EFI_DRIVER_BINDING_PROTOCOL_GUID \ EFI_GUID(0x18a031ab, 0xb443, 0x4d1a,\ 0xa5, 0xc0, 0x0c, 0x09, 0x26, 0x1e, 0x9f, 0x71) diff --git a/include/efi_loader.h b/include/efi_loader.h index 21e6692e92..f2942fbb2b 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -112,6 +112,8 @@ extern const efi_guid_t efi_guid_loaded_image; extern const efi_guid_t efi_guid_device_path_to_text_protocol; extern const efi_guid_t efi_simple_file_system_protocol_guid; extern const efi_guid_t efi_file_info_guid; +/* GUID for file system information */ +extern const efi_guid_t efi_file_system_info_guid; extern const efi_guid_t efi_guid_device_path_utilities_protocol; extern unsigned int __efi_runtime_start, __efi_runtime_stop; diff --git a/lib/efi_loader/efi_file.c b/lib/efi_loader/efi_file.c index 2fc77cfb87..cec8347f55 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; } From db851c84c92ba1281082622d38231755356ce644 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Wed, 4 Apr 2018 15:42:12 +0200 Subject: [PATCH 53/60] 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(-) diff --git a/lib/efi_selftest/efi_selftest_disk_image.h b/lib/efi_selftest/efi_selftest_disk_image.h index 4775dace70..9c741ce136 100644 --- a/lib/efi_selftest/efi_selftest_disk_image.h +++ b/lib/efi_selftest/efi_selftest_disk_image.h @@ -3,21 +3,21 @@ * * Generated with tools/file2include * - * SPDX-License-Identifier: GPL-2.0+ + * SPDX-License-Identifier: GPL-2.0+ */ #define EFI_ST_DISK_IMG { 0x00010000, { \ - {0x000001b8, "\x94\x37\x69\xfc\x00\x00\x00\x00"}, /* .7i..... */ \ - {0x000001c0, "\x02\x00\x83\x02\x02\x00\x01\x00"}, /* ........ */ \ + {0x000001b8, "\x21\x5d\x53\xd1\x00\x00\x00\x00"}, /* !]S..... */ \ + {0x000001c0, "\x02\x00\x01\x02\x02\x00\x01\x00"}, /* ........ */ \ {0x000001c8, "\x00\x00\x7f\x00\x00\x00\x00\x00"}, /* ........ */ \ {0x000001f8, "\x00\x00\x00\x00\x00\x00\x55\xaa"}, /* ......U. */ \ {0x00000200, "\xeb\x3c\x90\x6d\x6b\x66\x73\x2e"}, /* .<.mkfs. */ \ {0x00000208, "\x66\x61\x74\x00\x02\x04\x01\x00"}, /* fat..... */ \ {0x00000210, "\x02\x00\x02\x7f\x00\xf8\x01\x00"}, /* ........ */ \ {0x00000218, "\x20\x00\x40\x00\x00\x00\x00\x00"}, /* .@..... */ \ - {0x00000220, "\x00\x00\x00\x00\x80\x00\x29\x86"}, /* ......). */ \ - {0x00000228, "\xe8\x82\x80\x4e\x4f\x20\x4e\x41"}, /* ...NO NA */ \ - {0x00000230, "\x4d\x45\x20\x20\x20\x20\x46\x41"}, /* ME FA */ \ + {0x00000220, "\x00\x00\x00\x00\x80\x00\x29\xc4"}, /* ......). */ \ + {0x00000228, "\xc4\x88\x11\x55\x2d\x42\x4f\x4f"}, /* ...U-BOO */ \ + {0x00000230, "\x54\x20\x54\x45\x53\x54\x46\x41"}, /* T TESTFA */ \ {0x00000238, "\x54\x31\x32\x20\x20\x20\x0e\x1f"}, /* T12 .. */ \ {0x00000240, "\xbe\x5b\x7c\xac\x22\xc0\x74\x0b"}, /* .[|.".t. */ \ {0x00000248, "\x56\xb4\x0e\xbb\x07\x00\xcd\x10"}, /* V....... */ \ @@ -36,34 +36,20 @@ {0x000002b0, "\x72\x79\x20\x61\x67\x61\x69\x6e"}, /* ry again */ \ {0x000002b8, "\x20\x2e\x2e\x2e\x20\x0d\x0a\x00"}, /* ... ... */ \ {0x000003f8, "\x00\x00\x00\x00\x00\x00\x55\xaa"}, /* ......U. */ \ - {0x00000400, "\xf8\xff\xff\x00\x00\x00\x00\xf0"}, /* ........ */ \ - {0x00000408, "\xff\x00\x00\x00\x00\x00\x00\x00"}, /* ........ */ \ - {0x00000600, "\xf8\xff\xff\x00\x00\x00\x00\xf0"}, /* ........ */ \ - {0x00000608, "\xff\x00\x00\x00\x00\x00\x00\x00"}, /* ........ */ \ - {0x00000800, "\xe5\x70\x00\x00\x00\xff\xff\xff"}, /* .p...... */ \ - {0x00000808, "\xff\xff\xff\x0f\x00\x0e\xff\xff"}, /* ........ */ \ - {0x00000810, "\xff\xff\xff\xff\xff\xff\xff\xff"}, /* ........ */ \ - {0x00000818, "\xff\xff\x00\x00\xff\xff\xff\xff"}, /* ........ */ \ - {0x00000820, "\xe5\x2e\x00\x68\x00\x65\x00\x6c"}, /* ...h.e.l */ \ - {0x00000828, "\x00\x6c\x00\x0f\x00\x0e\x6f\x00"}, /* .l....o. */ \ - {0x00000830, "\x2e\x00\x74\x00\x78\x00\x74\x00"}, /* ..t.x.t. */ \ - {0x00000838, "\x2e\x00\x00\x00\x73\x00\x77\x00"}, /* ....s.w. */ \ - {0x00000840, "\xe5\x45\x4c\x4c\x4f\x54\x7e\x31"}, /* .ELLOT~1 */ \ - {0x00000848, "\x53\x57\x50\x20\x00\x64\xd0\x8a"}, /* SWP .d.. */ \ - {0x00000850, "\x92\x4b\x92\x4b\x00\x00\xd0\x8a"}, /* .K.K.... */ \ - {0x00000858, "\x92\x4b\x00\x00\x00\x00\x00\x00"}, /* .K...... */ \ - {0x00000860, "\x41\x68\x00\x65\x00\x6c\x00\x6c"}, /* Ah.e.l.l */ \ - {0x00000868, "\x00\x6f\x00\x0f\x00\xf1\x2e\x00"}, /* .o...... */ \ - {0x00000870, "\x74\x00\x78\x00\x74\x00\x00\x00"}, /* t.x.t... */ \ - {0x00000878, "\xff\xff\x00\x00\xff\xff\xff\xff"}, /* ........ */ \ - {0x00000880, "\x48\x45\x4c\x4c\x4f\x20\x20\x20"}, /* HELLO */ \ - {0x00000888, "\x54\x58\x54\x20\x00\x64\xd4\x8a"}, /* TXT .d.. */ \ - {0x00000890, "\x92\x4b\x92\x4b\x00\x00\xd4\x8a"}, /* .K.K.... */ \ - {0x00000898, "\x92\x4b\x05\x00\x0d\x00\x00\x00"}, /* .K...... */ \ - {0x000008a0, "\xe5\x45\x4c\x4c\x4f\x54\x7e\x31"}, /* .ELLOT~1 */ \ - {0x000008a8, "\x53\x57\x58\x20\x00\x64\xd0\x8a"}, /* SWX .d.. */ \ - {0x000008b0, "\x92\x4b\x92\x4b\x00\x00\xd0\x8a"}, /* .K.K.... */ \ - {0x000008b8, "\x92\x4b\x00\x00\x00\x00\x00\x00"}, /* .K...... */ \ - {0x00006000, "\x48\x65\x6c\x6c\x6f\x20\x77\x6f"}, /* Hello wo */ \ - {0x00006008, "\x72\x6c\x64\x21\x0a\x00\x00\x00"}, /* rld!.... */ \ + {0x00000400, "\xf8\xff\xff\x00\xf0\xff\x00\x00"}, /* ........ */ \ + {0x00000600, "\xf8\xff\xff\x00\xf0\xff\x00\x00"}, /* ........ */ \ + {0x00000800, "\x55\x2d\x42\x4f\x4f\x54\x20\x54"}, /* U-BOOT T */ \ + {0x00000808, "\x45\x53\x54\x08\x00\x00\xaa\x56"}, /* EST....V */ \ + {0x00000810, "\x84\x4c\x84\x4c\x00\x00\xaa\x56"}, /* .L.L...V */ \ + {0x00000818, "\x84\x4c\x00\x00\x00\x00\x00\x00"}, /* .L...... */ \ + {0x00000820, "\x41\x68\x00\x65\x00\x6c\x00\x6c"}, /* Ah.e.l.l */ \ + {0x00000828, "\x00\x6f\x00\x0f\x00\xf1\x2e\x00"}, /* .o...... */ \ + {0x00000830, "\x74\x00\x78\x00\x74\x00\x00\x00"}, /* t.x.t... */ \ + {0x00000838, "\xff\xff\x00\x00\xff\xff\xff\xff"}, /* ........ */ \ + {0x00000840, "\x48\x45\x4c\x4c\x4f\x20\x20\x20"}, /* HELLO */ \ + {0x00000848, "\x54\x58\x54\x20\x00\x64\xd7\x46"}, /* TXT .d.F */ \ + {0x00000850, "\x84\x4c\x84\x4c\x00\x00\xd7\x46"}, /* .L.L...F */ \ + {0x00000858, "\x84\x4c\x03\x00\x0d\x00\x00\x00"}, /* .L...... */ \ + {0x00005000, "\x48\x65\x6c\x6c\x6f\x20\x77\x6f"}, /* Hello wo */ \ + {0x00005008, "\x72\x6c\x64\x21\x0a\x00\x00\x00"}, /* rld!.... */ \ {0, NULL} } } From 1348c17ab2584151d5c0c7a6ac1b17b929521bad Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Wed, 4 Apr 2018 15:42:13 +0200 Subject: [PATCH 54/60] 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(+) diff --git a/lib/efi_selftest/efi_selftest_block_device.c b/lib/efi_selftest/efi_selftest_block_device.c index b07b22465f..a8979ed56b 100644 --- a/lib/efi_selftest/efi_selftest_block_device.c +++ b/lib/efi_selftest/efi_selftest_block_device.c @@ -29,6 +29,7 @@ static const efi_guid_t block_io_protocol_guid = BLOCK_IO_GUID; static const efi_guid_t guid_device_path = DEVICE_PATH_GUID; static const efi_guid_t guid_simple_file_system_protocol = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID; +static const efi_guid_t guid_file_system_info = EFI_FILE_SYSTEM_INFO_GUID; static efi_guid_t guid_vendor = EFI_GUID(0xdbca4c98, 0x6cb0, 0x694d, 0x08, 0x72, 0x81, 0x9c, 0x65, 0x0c, 0xb7, 0xb8); @@ -302,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) { From c9a63f44b526696a60e275087d79fe709f65f48b Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Thu, 5 Apr 2018 11:56:21 +0200 Subject: [PATCH 55/60] 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 --- include/efi_loader.h | 5 ++++ lib/efi_loader/efi_image_loader.c | 46 +++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/include/efi_loader.h b/include/efi_loader.h index f2942fbb2b..17f9d3d1ef 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -308,6 +308,10 @@ efi_status_t efi_setup_loaded_image( struct efi_device_path *file_path); efi_status_t efi_load_image_from_path(struct efi_device_path *file_path, void **buffer); +/* Print information about a loaded image */ +efi_status_t efi_print_image_info(struct efi_loaded_image *image, void *pc); +/* Print information about all loaded images */ +void efi_print_image_infos(void *pc); #ifdef CONFIG_EFI_LOADER_BOUNCE_BUFFER extern void *efi_bounce_buffer; @@ -425,6 +429,7 @@ static inline void efi_restore_gd(void) { } static inline void efi_set_bootdev(const char *dev, const char *devnr, const char *path) { } static inline void efi_net_set_dhcp_ack(void *pkt, int len) { } +static inline void efi_print_image_infos(void *pc) { } #endif /* CONFIG_EFI_LOADER && !CONFIG_SPL_BUILD */ diff --git a/lib/efi_loader/efi_image_loader.c b/lib/efi_loader/efi_image_loader.c index 74c6a9f921..f5885760d4 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) { From 99b8db7291ce58c83a4d3ad748c4a59ec7063d65 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Thu, 5 Apr 2018 11:56:22 +0200 Subject: [PATCH 56/60] arm: print information about loaded UEFI images If an exception occurs in a UEFI loaded image we need the start address of the image to determine the relocation offset. This patch adds the necessary lines after the registers in the crash dump. A possible output would be: UEFI image [0xbffe6000:0xbffe631f] pc=0x138 '/\snp.efi' With the offset 0x138 we can now find the relevant instruction in the disassembled 'snp.efi' binary. Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- arch/arm/lib/interrupts.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/arch/arm/lib/interrupts.c b/arch/arm/lib/interrupts.c index 80869adb61..cda4d48460 100644 --- a/arch/arm/lib/interrupts.c +++ b/arch/arm/lib/interrupts.c @@ -20,6 +20,7 @@ */ #include +#include #include #include #include @@ -51,6 +52,11 @@ void bad_mode (void) reset_cpu (0); } +static void show_efi_loaded_images(struct pt_regs *regs) +{ + efi_print_image_infos((void *)instruction_pointer(regs)); +} + void show_regs (struct pt_regs *regs) { unsigned long __maybe_unused flags; @@ -106,6 +112,7 @@ void do_undefined_instruction (struct pt_regs *pt_regs) printf ("undefined instruction\n"); fixup_pc(pt_regs, -4); show_regs (pt_regs); + show_efi_loaded_images(pt_regs); bad_mode (); } @@ -115,6 +122,7 @@ void do_software_interrupt (struct pt_regs *pt_regs) printf ("software interrupt\n"); fixup_pc(pt_regs, -4); show_regs (pt_regs); + show_efi_loaded_images(pt_regs); bad_mode (); } @@ -124,6 +132,7 @@ void do_prefetch_abort (struct pt_regs *pt_regs) printf ("prefetch abort\n"); fixup_pc(pt_regs, -8); show_regs (pt_regs); + show_efi_loaded_images(pt_regs); bad_mode (); } @@ -133,6 +142,7 @@ void do_data_abort (struct pt_regs *pt_regs) printf ("data abort\n"); fixup_pc(pt_regs, -8); show_regs (pt_regs); + show_efi_loaded_images(pt_regs); bad_mode (); } @@ -142,6 +152,7 @@ void do_not_used (struct pt_regs *pt_regs) printf ("not used\n"); fixup_pc(pt_regs, -8); show_regs (pt_regs); + show_efi_loaded_images(pt_regs); bad_mode (); } @@ -151,6 +162,7 @@ void do_fiq (struct pt_regs *pt_regs) printf ("fast interrupt request\n"); fixup_pc(pt_regs, -8); show_regs (pt_regs); + show_efi_loaded_images(pt_regs); bad_mode (); } @@ -160,5 +172,6 @@ void do_irq (struct pt_regs *pt_regs) printf ("interrupt request\n"); fixup_pc(pt_regs, -8); show_regs (pt_regs); + show_efi_loaded_images(pt_regs); bad_mode (); } From 0c5d2a3dac01a8d436639ab5b7e44f4218d62b84 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Tue, 3 Apr 2018 22:06:52 +0200 Subject: [PATCH 57/60] 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(-) diff --git a/lib/efi_loader/efi_net.c b/lib/efi_loader/efi_net.c index 3d860e658e..9afe76cdb3 100644 --- a/lib/efi_loader/efi_net.c +++ b/lib/efi_loader/efi_net.c @@ -54,14 +54,46 @@ static efi_status_t EFIAPI efi_net_stop(struct efi_simple_network *this) return EFI_EXIT(EFI_SUCCESS); } +/* + * Initialize network adapter and allocate transmit and receive buffers. + * + * This function implements the Initialize service of the + * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface + * (UEFI) specification for details. + * + * @this: pointer to the protocol instance + * @extra_rx: extra receive buffer to be allocated + * @extra_tx: extra transmit buffer to be allocated + * @return: status code + */ static efi_status_t EFIAPI efi_net_initialize(struct efi_simple_network *this, ulong extra_rx, ulong extra_tx) { + int ret; + efi_status_t r = EFI_SUCCESS; + EFI_ENTRY("%p, %lx, %lx", this, extra_rx, extra_tx); - eth_init(); + if (!this) { + r = EFI_INVALID_PARAMETER; + goto error; + } - return EFI_EXIT(EFI_SUCCESS); + /* Setup packet buffers */ + net_init(); + /* Disable hardware and put it into the reset state */ + eth_halt(); + /* Set current device according to environment variables */ + eth_set_current(); + /* Get hardware ready for send and receive operations */ + ret = eth_init(); + if (ret < 0) { + eth_halt(); + r = EFI_DEVICE_ERROR; + } + +error: + return EFI_EXIT(r); } static efi_status_t EFIAPI efi_net_reset(struct efi_simple_network *this, From 61a5ced6ad9376a0755ea2a920667e3a9072990c Mon Sep 17 00:00:00 2001 From: Ivan Gorinov Date: Thu, 5 Apr 2018 18:32:06 -0700 Subject: [PATCH 58/60] 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 --- include/pe.h | 4 +++ lib/efi_loader/efi_image_loader.c | 51 +++++++++++++++++++++++-------- 2 files changed, 43 insertions(+), 12 deletions(-) diff --git a/include/pe.h b/include/pe.h index c3a19cef76..e7845bb7d2 100644 --- a/include/pe.h +++ b/include/pe.h @@ -38,11 +38,15 @@ typedef struct _IMAGE_DOS_HEADER { #define IMAGE_DOS_SIGNATURE 0x5A4D /* MZ */ #define IMAGE_NT_SIGNATURE 0x00004550 /* PE00 */ +#define IMAGE_FILE_MACHINE_I386 0x014c #define IMAGE_FILE_MACHINE_ARM 0x01c0 #define IMAGE_FILE_MACHINE_THUMB 0x01c2 #define IMAGE_FILE_MACHINE_ARMNT 0x01c4 #define IMAGE_FILE_MACHINE_AMD64 0x8664 #define IMAGE_FILE_MACHINE_ARM64 0xaa64 +#define IMAGE_FILE_MACHINE_RISCV32 0x5032 +#define IMAGE_FILE_MACHINE_RISCV64 0x5064 + #define IMAGE_NT_OPTIONAL_HDR32_MAGIC 0x10b #define IMAGE_NT_OPTIONAL_HDR64_MAGIC 0x20b #define IMAGE_SUBSYSTEM_EFI_APPLICATION 10 diff --git a/lib/efi_loader/efi_image_loader.c b/lib/efi_loader/efi_image_loader.c index f5885760d4..d5fbba3138 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); From 806d2fa8e3c4ebaa1a2b1854ee4569ccc056d238 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Fri, 6 Apr 2018 09:40:51 +0200 Subject: [PATCH 59/60] efi_loader: Respect DT reserved regions With legacy boot (booti, bootz), people can declare memory regions as reserved using device tree memory reservations. This feature is some times used to indicate memory regions that should not be touched. Since in a UEFI world, the DT memory reservations do not get honored, let's copy them into the UEFI memory map so everyone has a coherent view of the world and we give people the chance to add reservations on demand. Signed-off-by: Alexander Graf --- cmd/bootefi.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/cmd/bootefi.c b/cmd/bootefi.c index 2a31a914cd..5a2a81005f 100644 --- a/cmd/bootefi.c +++ b/cmd/bootefi.c @@ -178,6 +178,27 @@ static efi_status_t efi_run_in_el2(EFIAPI efi_status_t (*entry)( } #endif +/* Carve out DT reserved memory ranges */ +static efi_status_t efi_carve_out_dt_rsv(void *fdt) +{ + int nr_rsv, i; + uint64_t addr, size, pages; + + nr_rsv = fdt_num_mem_rsv(fdt); + + /* Look for an existing entry and add it to the efi mem map. */ + for (i = 0; i < nr_rsv; i++) { + if (fdt_get_mem_rsv(fdt, i, &addr, &size) != 0) + continue; + + pages = ALIGN(size, EFI_PAGE_SIZE) >> EFI_PAGE_SHIFT; + efi_add_memory_map(addr, pages, EFI_RESERVED_MEMORY_TYPE, + false); + } + + return EFI_SUCCESS; +} + static efi_status_t efi_install_fdt(void *fdt) { bootm_headers_t img = { 0 }; @@ -199,6 +220,11 @@ static efi_status_t efi_install_fdt(void *fdt) return EFI_LOAD_ERROR; } + if (efi_carve_out_dt_rsv(fdt) != EFI_SUCCESS) { + printf("ERROR: failed to carve out memory\n"); + return EFI_LOAD_ERROR; + } + /* Link to it in the efi tables */ ret = efi_install_configuration_table(&efi_guid_fdt, fdt); if (ret != EFI_SUCCESS) From f4cf153a487486428a061b5d866fe2f68653b2f8 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Fri, 6 Apr 2018 15:36:31 +0200 Subject: [PATCH 60/60] efi_loader: correctly set the machine type in the PE header The portable executable header has a field describing the machine type. The machine type should match the binary. So on i386 we should use IMAGE_FILE_MACHINE_I386 and on x86_64 we should use IMAGE_FILE_MACHINE_AMD64. The actual value is issued by the objcopy command invoked in scripts/Makefile.lib in depdendence of the value of EFI_TARGET. The value is used both for EFI_STUB and for EFI_LOADER. Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- arch/x86/config.mk | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/x86/config.mk b/arch/x86/config.mk index 472ada5490..69074f4711 100644 --- a/arch/x86/config.mk +++ b/arch/x86/config.mk @@ -94,12 +94,16 @@ ifneq ($(CONFIG_EFI_STUB_64BIT),) EFI_LDS := elf_x86_64_efi.lds EFI_CRT0 := crt0_x86_64_efi.o EFI_RELOC := reloc_x86_64_efi.o -EFI_TARGET := --target=efi-app-ia32 else EFI_LDS := elf_ia32_efi.lds EFI_CRT0 := crt0_ia32_efi.o EFI_RELOC := reloc_ia32_efi.o +endif + +ifdef CONFIG_X86_64 EFI_TARGET := --target=efi-app-x86_64 +else +EFI_TARGET := --target=efi-app-ia32 endif endif