[PATCH v3 0/5] Add support for booting EFI FIT images

Currently the only way to run an EFI binary like GRUB2 is via the 'bootefi' command, which cannot be used in a verified boot scenario.
The obvious solution to this limitation is to add support for booting FIT images containing those EFI binaries.
The implementation relies on a new image type - IH_OS_EFI - which can be created by using 'os = "efi"' inside an ITS file:
/ { #address-cells = <1>;
images { efi-grub { description = "GRUB EFI"; data = /incbin/("bootarm.efi"); type = "kernel_noload"; arch = "arm"; os = "efi"; compression = "none"; load = <0x0>; entry = <0x0>; hash-1 { algo = "sha256"; }; }; };
configurations { default = "config-grub"; config-grub { kernel = "efi-grub"; signature-1 { algo = "sha256,rsa2048"; sign-images = "kernel"; }; }; }; };
The bootm command has been extended to handle the IH_OS_EFI images. To enable this feature, a new configuration option has been added: BOOTM_EFI
I tested the solution using the 'qemu_arm' board:
=> load scsi 0:1 ${kernel_addr_r} efi-image.fit => bootm ${kernel_addr_r}#config-grub
Changes in v3: * Rebase patches on Heinrich Schuchardt's patch series v3: efi_loader: prepare for FIT images https://lists.denx.de/pipermail/u-boot/2019-December/393677.html This fixes implicitly the sandbox issue 'phys_to_virt: Cannot map sandbox address' since efi_install_fdt() is now expecting a pointer to addressable memory instead of a physical address. * Get rid of 'EFI/BOOT/' prefix used in ITS samples * Add a python test to verify the implementation in sandbox environment
Changes in v2: * Rebase patches on Heinrich Schuchardt's patch series: efi_loader: prepare for FIT images https://lists.denx.de/pipermail/u-boot/2019-December/393192.html * Add sample configuration: doc/uImage.FIT/uefi.its * Update uefi documentation: doc/uefi/uefi.rst
Cristian Ciocaltea (5): image: Add IH_OS_EFI for EFI chain-load boot bootm: Add a bootm command for type IH_OS_EFI doc: Add sample uefi.its image description file doc: uefi.rst: Document launching UEFI binaries from FIT images test/py: Create a test for launching UEFI binaries from FIT images
cmd/Kconfig | 7 + common/bootm_os.c | 56 ++++++++ common/image-fit.c | 3 +- common/image.c | 1 + doc/uImage.FIT/uefi.its | 67 ++++++++++ doc/uefi/uefi.rst | 34 +++++ include/image.h | 1 + test/py/tests/test_efi_fit.py | 233 ++++++++++++++++++++++++++++++++++ 8 files changed, 401 insertions(+), 1 deletion(-) create mode 100644 doc/uImage.FIT/uefi.its create mode 100644 test/py/tests/test_efi_fit.py

Add a new OS type to be used for chain-loading an EFI compatible firmware or boot loader like GRUB2, possibly in a verified boot scenario.
Bellow is sample ITS file that generates a FIT image supporting secure boot. Please note the presence of 'os = "efi";' line, which identifies the currently introduced OS type:
/ { #address-cells = <1>;
images { efi-grub { description = "GRUB EFI"; data = /incbin/("bootarm.efi"); type = "kernel_noload"; arch = "arm"; os = "efi"; compression = "none"; load = <0x0>; entry = <0x0>; hash-1 { algo = "sha256"; }; }; };
configurations { default = "config-grub"; config-grub { kernel = "efi-grub"; signature-1 { algo = "sha256,rsa2048"; sign-images = "kernel"; }; }; }; };
Signed-off-by: Cristian Ciocaltea cristian.ciocaltea@gmail.com --- common/image-fit.c | 3 ++- common/image.c | 1 + include/image.h | 1 + 3 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/common/image-fit.c b/common/image-fit.c index 5c63c769de..19e313bf41 100644 --- a/common/image-fit.c +++ b/common/image-fit.c @@ -1925,7 +1925,8 @@ int fit_image_load(bootm_headers_t *images, ulong addr, image_type == IH_TYPE_FPGA || fit_image_check_os(fit, noffset, IH_OS_LINUX) || fit_image_check_os(fit, noffset, IH_OS_U_BOOT) || - fit_image_check_os(fit, noffset, IH_OS_OPENRTOS); + fit_image_check_os(fit, noffset, IH_OS_OPENRTOS) || + fit_image_check_os(fit, noffset, IH_OS_EFI);
/* * If either of the checks fail, we should report an error, but diff --git a/common/image.c b/common/image.c index f17fa40c49..2e0e2b0e7f 100644 --- a/common/image.c +++ b/common/image.c @@ -134,6 +134,7 @@ static const table_entry_t uimage_os[] = { { IH_OS_OPENRTOS, "openrtos", "OpenRTOS", }, #endif { IH_OS_OPENSBI, "opensbi", "RISC-V OpenSBI", }, + { IH_OS_EFI, "efi", "EFI Firmware" },
{ -1, "", "", }, }; diff --git a/include/image.h b/include/image.h index f4d2aaf53e..4a280b78e7 100644 --- a/include/image.h +++ b/include/image.h @@ -157,6 +157,7 @@ enum { IH_OS_ARM_TRUSTED_FIRMWARE, /* ARM Trusted Firmware */ IH_OS_TEE, /* Trusted Execution Environment */ IH_OS_OPENSBI, /* RISC-V OpenSBI */ + IH_OS_EFI, /* EFI Firmware (e.g. GRUB2) */
IH_OS_COUNT, };

On 12/17/19 8:46 AM, Cristian Ciocaltea wrote:
Add a new OS type to be used for chain-loading an EFI compatible firmware or boot loader like GRUB2, possibly in a verified boot scenario.
Bellow is sample ITS file that generates a FIT image supporting secure boot. Please note the presence of 'os = "efi";' line, which identifies the currently introduced OS type:
/ { #address-cells = <1>;
images { efi-grub { description = "GRUB EFI"; data = /incbin/("bootarm.efi"); type = "kernel_noload"; arch = "arm"; os = "efi"; compression = "none"; load = <0x0>; entry = <0x0>; hash-1 { algo = "sha256"; }; }; }; configurations { default = "config-grub"; config-grub { kernel = "efi-grub"; signature-1 { algo = "sha256,rsa2048"; sign-images = "kernel"; }; }; };
};
Signed-off-by: Cristian Ciocaltea cristian.ciocaltea@gmail.com
Reviewed-by: Heinrich Schuchardt xypron.glpk@gmx.de

Add support for booting EFI binaries contained in FIT images. A typical usage scenario is chain-loading GRUB2 in a verified boot environment.
Signed-off-by: Cristian Ciocaltea cristian.ciocaltea@gmail.com --- cmd/Kconfig | 7 ++++++ common/bootm_os.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+)
diff --git a/cmd/Kconfig b/cmd/Kconfig index cf982ff65e..39fa87082d 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -263,6 +263,13 @@ config CMD_BOOTI help Boot an AArch64 Linux Kernel image from memory.
+config BOOTM_EFI + bool "Support booting EFI OS images" + depends on CMD_BOOTEFI + default y + help + Support booting EFI images via the bootm command. + config BOOTM_LINUX bool "Support booting Linux OS images" depends on CMD_BOOTM || CMD_BOOTZ || CMD_BOOTI diff --git a/common/bootm_os.c b/common/bootm_os.c index 6fb7d658da..96792430da 100644 --- a/common/bootm_os.c +++ b/common/bootm_os.c @@ -6,10 +6,12 @@
#include <common.h> #include <bootm.h> +#include <efi_loader.h> #include <env.h> #include <fdt_support.h> #include <linux/libfdt.h> #include <malloc.h> +#include <mapmem.h> #include <vxworks.h> #include <tee/optee.h>
@@ -462,6 +464,57 @@ static int do_bootm_tee(int flag, int argc, char * const argv[], } #endif
+#ifdef CONFIG_BOOTM_EFI +static int do_bootm_efi(int flag, int argc, char * const argv[], + bootm_headers_t *images) +{ + int ret; + efi_status_t efi_ret; + void *image_buf; + + if (flag != BOOTM_STATE_OS_GO) + return 0; + + /* Locate FDT, if provided */ + ret = bootm_find_images(flag, argc, argv); + if (ret) + return ret; + + /* Initialize EFI drivers */ + efi_ret = efi_init_obj_list(); + if (efi_ret != EFI_SUCCESS) { + printf("## Failed to initialize UEFI sub-system: r = %lu\n", + efi_ret & ~EFI_ERROR_MASK); + return 1; + } + + /* Install device tree */ + efi_ret = efi_install_fdt(images->ft_len + ? images->ft_addr : EFI_FDT_USE_INTERNAL); + if (efi_ret != EFI_SUCCESS) { + printf("## Failed to install device tree: r = %lu\n", + efi_ret & ~EFI_ERROR_MASK); + return 1; + } + + /* Run EFI image */ + printf("## Transferring control to EFI (at address %08lx) ...\n", + images->ep); + bootstage_mark(BOOTSTAGE_ID_RUN_OS); + + image_buf = map_sysmem(images->ep, images->os.image_len); + + efi_ret = efi_run_image(image_buf, images->os.image_len); + if (efi_ret != EFI_SUCCESS) { + printf("## Failed to run EFI image: r = %lu\n", + efi_ret & ~EFI_ERROR_MASK); + return 1; + } + + return 0; +} +#endif + static boot_os_fn *boot_os[] = { [IH_OS_U_BOOT] = do_bootm_standalone, #ifdef CONFIG_BOOTM_LINUX @@ -498,6 +551,9 @@ static boot_os_fn *boot_os[] = { #ifdef CONFIG_BOOTM_OPTEE [IH_OS_TEE] = do_bootm_tee, #endif +#ifdef CONFIG_BOOTM_EFI + [IH_OS_EFI] = do_bootm_efi, +#endif };
/* Allow for arch specific config before we boot */

On 12/17/19 8:46 AM, Cristian Ciocaltea wrote:
Add support for booting EFI binaries contained in FIT images. A typical usage scenario is chain-loading GRUB2 in a verified boot environment.
Signed-off-by: Cristian Ciocaltea cristian.ciocaltea@gmail.com
You missed the current changes in common/bootm_os.c. I will rebase the patch.
Reviewed-by: Heinrich Schuchardt xypron.glpk@gmx.de

On Tue, Dec 17, 2019 at 09:34:44PM +0100, Heinrich Schuchardt wrote:
On 12/17/19 8:46 AM, Cristian Ciocaltea wrote:
Add support for booting EFI binaries contained in FIT images. A typical usage scenario is chain-loading GRUB2 in a verified boot environment.
Signed-off-by: Cristian Ciocaltea cristian.ciocaltea@gmail.com
You missed the current changes in common/bootm_os.c. I will rebase the patch.
Right, sorry. I have already rebased the patch and I will resubmit it as soon as I finalize the changes on pytest.
Thanks!
Reviewed-by: Heinrich Schuchardt xypron.glpk@gmx.de

This patch adds an example FIT image description file demonstrating the usage of bootm command to securely launch UEFI binaries.
Signed-off-by: Cristian Ciocaltea cristian.ciocaltea@gmail.com Reviewed-by: Heinrich Schuchardt xypron.glpk@gmx.de --- doc/uImage.FIT/uefi.its | 67 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 doc/uImage.FIT/uefi.its
diff --git a/doc/uImage.FIT/uefi.its b/doc/uImage.FIT/uefi.its new file mode 100644 index 0000000000..378ca4ed8d --- /dev/null +++ b/doc/uImage.FIT/uefi.its @@ -0,0 +1,67 @@ +/* + * Example FIT image description file demonstrating the usage of the + * bootm command to launch UEFI binaries. + * + * Two boot configurations are available to enable booting GRUB2 on QEMU, + * the former uses a FDT blob contained in the FIT image, while the later + * relies on the FDT provided by the board emulator. + */ + +/dts-v1/; + +/ { + description = "GRUB2 EFI and QEMU FDT blob"; + #address-cells = <1>; + + images { + efi-grub { + description = "GRUB EFI Firmware"; + data = /incbin/("bootarm.efi"); + type = "kernel_noload"; + arch = "arm"; + os = "efi"; + compression = "none"; + load = <0x0>; + entry = <0x0>; + hash-1 { + algo = "sha256"; + }; + }; + + fdt-qemu { + description = "QEMU DTB"; + data = /incbin/("qemu-arm.dtb"); + type = "flat_dt"; + arch = "arm"; + compression = "none"; + hash-1 { + algo = "sha256"; + }; + }; + }; + + configurations { + default = "config-grub-fdt"; + + config-grub-fdt { + description = "GRUB EFI Boot w/ FDT"; + kernel = "efi-grub"; + fdt = "fdt-qemu"; + signature-1 { + algo = "sha256,rsa2048"; + key-name-hint = "dev"; + sign-images = "kernel", "fdt"; + }; + }; + + config-grub-nofdt { + description = "GRUB EFI Boot w/o FDT"; + kernel = "efi-grub"; + signature-1 { + algo = "sha256,rsa2048"; + key-name-hint = "dev"; + sign-images = "kernel"; + }; + }; + }; +};

This patch adds a new section "Launching a UEFI binary from a FIT image" documenting the usage of the CONFIG_BOOTM_EFI extension to bootm command that offers a verified boot alternative for UEFI binaries such as GRUB2.
Signed-off-by: Cristian Ciocaltea cristian.ciocaltea@gmail.com Reviewed-by: Heinrich Schuchardt xypron.glpk@gmx.de --- doc/uefi/uefi.rst | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+)
diff --git a/doc/uefi/uefi.rst b/doc/uefi/uefi.rst index db942df694..a8fd886d6b 100644 --- a/doc/uefi/uefi.rst +++ b/doc/uefi/uefi.rst @@ -63,6 +63,40 @@ 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.
+Launching a UEFI binary from a FIT image +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A signed FIT image can be used to securely boot a UEFI image via the +bootm command. This feature is available if U-Boot is configured with:: + + CONFIG_BOOTM_EFI=y + +A sample configuration is provided as file doc/uImage.FIT/uefi.its. + +Below you find the output of an example session starting GRUB:: + + => load mmc 0:1 ${kernel_addr_r} image.fit + 4620426 bytes read in 83 ms (53.1 MiB/s) + => bootm ${kernel_addr_r}#config-grub-nofdt + ## Loading kernel from FIT Image at 40400000 ... + Using 'config-grub-nofdt' configuration + Verifying Hash Integrity ... sha256,rsa2048:dev+ OK + Trying 'efi-grub' kernel subimage + Description: GRUB EFI Firmware + Created: 2019-11-20 8:18:16 UTC + Type: Kernel Image (no loading done) + Compression: uncompressed + Data Start: 0x404000d0 + Data Size: 450560 Bytes = 440 KiB + Hash algo: sha256 + Hash value: 4dbee00021112df618f58b3f7cf5e1595533d543094064b9ce991e8b054a9eec + Verifying Hash Integrity ... sha256+ OK + XIP Kernel Image (no loading done) + ## Transferring control to EFI (at address 404000d0) ... + Welcome to GRUB! + +See doc/uImage.FIT/howto.txt for an introduction to FIT images. + Executing the boot manager ~~~~~~~~~~~~~~~~~~~~~~~~~~

This test verifies the implementation of the 'bootm' extension that handles UEFI binaries inside FIT images (enabled via CONFIG_BOOTM_EFI).
Signed-off-by: Cristian Ciocaltea cristian.ciocaltea@gmail.com --- test/py/tests/test_efi_fit.py | 233 ++++++++++++++++++++++++++++++++++ 1 file changed, 233 insertions(+) create mode 100644 test/py/tests/test_efi_fit.py
diff --git a/test/py/tests/test_efi_fit.py b/test/py/tests/test_efi_fit.py new file mode 100644 index 0000000000..52b415b198 --- /dev/null +++ b/test/py/tests/test_efi_fit.py @@ -0,0 +1,233 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2019, Cristian Ciocaltea cristian.ciocaltea@gmail.com + +# Test launching UEFI binaries from FIT images. + +import os +import pytest +import u_boot_utils as util + +# Define the parametrized ITS data to be used for FIT image generation. +its_data = ''' +/dts-v1/; + +/ { + description = "EFI image with FDT blob"; + #address-cells = <1>; + + images { + efi { + description = "Sandbox EFI"; + data = /incbin/("%(efi-bin)s"); + type = "%(kernel-type)s"; + arch = "sandbox"; + os = "efi"; + compression = "%(efi-comp)s"; + load = <0x0>; + entry = <0x0>; + }; + fdt { + description = "Sandbox FDT"; + data = /incbin/("%(fdt-bin)s"); + type = "flat_dt"; + arch = "sandbox"; + compression = "%(fdt-comp)s"; + }; + }; + + configurations { + default = "config-efi-fdt"; + config-efi-fdt { + description = "EFI FIT w/ FDT"; + kernel = "efi"; + fdt = "fdt"; + }; + config-efi-nofdt { + description = "EFI FIT w/o FDT"; + kernel = "efi"; + }; + }; +}; +''' + +# Define the parametrized FDT data. +fdt_data = ''' +/dts-v1/; + +/ { + model = "Sandbox %(fdt_type) EFI FIT Boot Test "; + compatible = "sandbox"; + + reset@0 { + compatible = "sandbox,reset"; + }; +}; +''' + +@pytest.mark.boardspec('sandbox') +@pytest.mark.buildconfigspec('bootm_efi') +@pytest.mark.buildconfigspec('cmd_bootefi_hello_compile') +@pytest.mark.requiredtool('dtc') +def test_efi_fit(u_boot_console): + """Test handling of UEFI binaries inside FIT images. + + The tests are trying to launch U-Boot's helloworld.efi embedded into + FIT images, in uncompressed or gzip compressed format. + + Additionally, a sample FDT blob is created and embedded into the above + mentioned FIT images, in uncompressed or gzip compressed format. + + The following test cases are currently defined and enabled: + - Launch uncompressed FIT EFI & FIT FDT + - Launch compressed FIT EFI & FIT FDT + - Launch uncompressed FIT EFI & internal FDT + - Launch compressed FIT EFI & internal FDT + """ + + def make_fpath(fname): + """Compute the path of a given (temporary) file. + + Args: + fname: The name of a file within U-Boot build dir. + Return: + The computed file path. + """ + return os.path.join(u_boot_console.config.build_dir, fname) + + def make_efi(fname, comp): + """Create an UEFI binary. + + This simply copies lib/efi_loader/helloworld.efi into U-Boot + build dir and, optionally, compresses the file using gzip. + + Args: + fname: The target file name within U-Boot build dir. + comp: Flag to enable gzip compression. + Return: + The path of the created file. + """ + bin_path = make_fpath(fname) + os.system('cp %s %s' % (make_fpath('lib/efi_loader/helloworld.efi'), bin_path)) + if comp: + util.run_and_log(u_boot_console, ['gzip', '-f', bin_path]) + bin_path += '.gz' + return bin_path + + def make_dtb(fdt_type, comp): + """Create a sample DTB file. + + Creates a DTS file and compiles it to a DTB. + + Args: + fdt_type: The type of the FDT, i.e. internal, user. + comp: Flag to enable gzip compression. + Returns: + The path of the created file. + """ + dts = make_fpath('test-efi-fit-sandbox-%s.dts' % fdt_type) + dtb = make_fpath('test-efi-fit-sandbox-%s.dtb' % fdt_type) + with open(dts, 'w') as fd: + fd.write(fdt_data) + util.run_and_log(u_boot_console, ['dtc', dts, '-O', 'dtb', '-o', dtb]) + if comp: + util.run_and_log(u_boot_console, ['gzip', '-f', dtb]) + dtb += '.gz' + return dtb + + def make_fit(efi_comp, fdt_comp): + """Create a sample FIT image. + + Runs 'mkimage' to create a FIT image within U-Boot build dir. + Args: + efi_comp: Enable gzip compression for the EFI binary. + fdt_comp: Enable gzip compression for the FDT blob. + Return: + The path of the created file. + """ + # Generate resources referenced by ITS. + its_params = { + 'efi-bin' : os.path.basename( + make_efi('test-efi-fit-sandbox.efi', efi_comp)), + 'kernel-type' : 'kernel' if efi_comp else 'kernel_noload', + 'efi-comp' : 'gzip' if efi_comp else 'none', + 'fdt-bin' : os.path.basename(make_dtb('user', fdt_comp)), + 'fdt-comp' : 'gzip' if fdt_comp else 'none', + } + + # Generate a test ITS file. + its_path = make_fpath('test-efi-fit.its') + with open(its_path, 'w') as fd: + fd.write(its_data % its_params) + + # Build the test ITS. + fit_path = make_fpath('test-efi-fit.fit') + util.run_and_log(u_boot_console, + [make_fpath('tools/mkimage'), '-f', its_path, fit_path]) + return fit_path + + def file_size(fpath): + """Get the size of a file. + + Args: + fname: Path of the file to check. + Return: + The size of file in bytes. + """ + return os.stat(fpath).st_size + + def launch_efi(enable_fdt, enable_comp): + """Launch a UEFI binary from a FIT image. + + Creates a test FIT image containing a fake UEFI binary and tries + to run it via the 'bootm' command in the U-Boot console. + + Args: + enable_fdt: Flag to enable using the FDT blob inside FIT image. + enable_comp: Flag to enable gzip compression on EFI and FDT content. + """ + # Create test FIT image. + fit_img_path = make_fit(enable_comp, enable_comp) + + # Select boot configuration. + fit_config = 'config-efi-fdt' if enable_fdt else 'config-efi-nofdt' + + # Run U-Boot commands. + with u_boot_console.log.section('Boot EFI FIT %s' % fit_config): + output = u_boot_console.run_command( + 'host load hostfs - $kernel_addr_r %s' % fit_img_path) + + expected_text = '%d bytes read' % file_size(fit_img_path) + assert(expected_text in output) + + output = u_boot_console.run_command( + 'bootm ${kernel_addr_r}#%s' % fit_config) + + if enable_fdt: + expected_text = 'Booting using the fdt blob' + assert(expected_text in output) + + expected_text = 'Hello, world' + assert expected_text in output + expected_text = '## Application terminated, r = 0' + assert expected_text in output + + try: + # Use our own device tree file, will be restored afterwards. + control_dtb = make_dtb('internal', False) + old_dtb = u_boot_console.config.dtb + u_boot_console.config.dtb = control_dtb + + # Run tests + # - fdt ON, gzip OFF + launch_efi(True, False) + # - fdt ON, gzip ON + launch_efi(True, True) + # - fdt OFF, gzip OFF + launch_efi(False, False) + # - fdt OFF, gzip ON + launch_efi(False, True) + + finally: + # Go back to the original U-Boot with the correct dtb. + u_boot_console.config.dtb = old_dtb + u_boot_console.restart_uboot()

On 12/17/19 8:47 AM, Cristian Ciocaltea wrote:
This test verifies the implementation of the 'bootm' extension that handles UEFI binaries inside FIT images (enabled via CONFIG_BOOTM_EFI).
Signed-off-by: Cristian Ciocaltea cristian.ciocaltea@gmail.com
test/py/tests/test_efi_fit.py | 233 ++++++++++++++++++++++++++++++++++ 1 file changed, 233 insertions(+) create mode 100644 test/py/tests/test_efi_fit.py
diff --git a/test/py/tests/test_efi_fit.py b/test/py/tests/test_efi_fit.py new file mode 100644 index 0000000000..52b415b198 --- /dev/null +++ b/test/py/tests/test_efi_fit.py @@ -0,0 +1,233 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2019, Cristian Ciocaltea cristian.ciocaltea@gmail.com
+# Test launching UEFI binaries from FIT images.
+import os +import pytest +import u_boot_utils as util
+# Define the parametrized ITS data to be used for FIT image generation. +its_data = ''' +/dts-v1/;
+/ {
- description = "EFI image with FDT blob";
- #address-cells = <1>;
- images {
efi {
description = "Sandbox EFI";
data = /incbin/("%(efi-bin)s");
type = "%(kernel-type)s";
arch = "sandbox";
os = "efi";
compression = "%(efi-comp)s";
load = <0x0>;
entry = <0x0>;
};
fdt {
description = "Sandbox FDT";
data = /incbin/("%(fdt-bin)s");
type = "flat_dt";
arch = "sandbox";
compression = "%(fdt-comp)s";
};
- };
- configurations {
default = "config-efi-fdt";
config-efi-fdt {
description = "EFI FIT w/ FDT";
kernel = "efi";
fdt = "fdt";
};
config-efi-nofdt {
description = "EFI FIT w/o FDT";
kernel = "efi";
};
- };
+}; +'''
+# Define the parametrized FDT data. +fdt_data = ''' +/dts-v1/;
+/ {
- model = "Sandbox %(fdt_type) EFI FIT Boot Test ";
- compatible = "sandbox";
- reset@0 {
compatible = "sandbox,reset";
This produces a warning:
+dtc ./test-efi-fit-sandbox-internal.dts -O dtb -o ./test-efi-fit-sandbox-internal.dtb ./test-efi-fit-sandbox-internal.dts:8.13-10.7: Warning (unit_address_vs_reg): /reset@0: node has a unit name, but no reg property
- };
+}; +'''
+@pytest.mark.boardspec('sandbox')
This test looks ok in principal. But why should we restrict it to the sandbox?
Best regards
Heinrich
+@pytest.mark.buildconfigspec('bootm_efi') +@pytest.mark.buildconfigspec('cmd_bootefi_hello_compile') +@pytest.mark.requiredtool('dtc') +def test_efi_fit(u_boot_console):
- """Test handling of UEFI binaries inside FIT images.
- The tests are trying to launch U-Boot's helloworld.efi embedded into
- FIT images, in uncompressed or gzip compressed format.
- Additionally, a sample FDT blob is created and embedded into the above
- mentioned FIT images, in uncompressed or gzip compressed format.
- The following test cases are currently defined and enabled:
- Launch uncompressed FIT EFI & FIT FDT
- Launch compressed FIT EFI & FIT FDT
- Launch uncompressed FIT EFI & internal FDT
- Launch compressed FIT EFI & internal FDT
- """
- def make_fpath(fname):
"""Compute the path of a given (temporary) file.
Args:
fname: The name of a file within U-Boot build dir.
Return:
The computed file path.
"""
return os.path.join(u_boot_console.config.build_dir, fname)
- def make_efi(fname, comp):
"""Create an UEFI binary.
This simply copies lib/efi_loader/helloworld.efi into U-Boot
build dir and, optionally, compresses the file using gzip.
Args:
fname: The target file name within U-Boot build dir.
comp: Flag to enable gzip compression.
Return:
The path of the created file.
"""
bin_path = make_fpath(fname)
os.system('cp %s %s' % (make_fpath('lib/efi_loader/helloworld.efi'), bin_path))
if comp:
util.run_and_log(u_boot_console, ['gzip', '-f', bin_path])
bin_path += '.gz'
return bin_path
- def make_dtb(fdt_type, comp):
"""Create a sample DTB file.
Creates a DTS file and compiles it to a DTB.
Args:
fdt_type: The type of the FDT, i.e. internal, user.
comp: Flag to enable gzip compression.
Returns:
The path of the created file.
"""
dts = make_fpath('test-efi-fit-sandbox-%s.dts' % fdt_type)
dtb = make_fpath('test-efi-fit-sandbox-%s.dtb' % fdt_type)
with open(dts, 'w') as fd:
fd.write(fdt_data)
util.run_and_log(u_boot_console, ['dtc', dts, '-O', 'dtb', '-o', dtb])
if comp:
util.run_and_log(u_boot_console, ['gzip', '-f', dtb])
dtb += '.gz'
return dtb
- def make_fit(efi_comp, fdt_comp):
"""Create a sample FIT image.
Runs 'mkimage' to create a FIT image within U-Boot build dir.
Args:
efi_comp: Enable gzip compression for the EFI binary.
fdt_comp: Enable gzip compression for the FDT blob.
Return:
The path of the created file.
"""
# Generate resources referenced by ITS.
its_params = {
'efi-bin' : os.path.basename(
make_efi('test-efi-fit-sandbox.efi', efi_comp)),
'kernel-type' : 'kernel' if efi_comp else 'kernel_noload',
'efi-comp' : 'gzip' if efi_comp else 'none',
'fdt-bin' : os.path.basename(make_dtb('user', fdt_comp)),
'fdt-comp' : 'gzip' if fdt_comp else 'none',
}
# Generate a test ITS file.
its_path = make_fpath('test-efi-fit.its')
with open(its_path, 'w') as fd:
fd.write(its_data % its_params)
# Build the test ITS.
fit_path = make_fpath('test-efi-fit.fit')
util.run_and_log(u_boot_console,
[make_fpath('tools/mkimage'), '-f', its_path, fit_path])
return fit_path
- def file_size(fpath):
"""Get the size of a file.
Args:
fname: Path of the file to check.
Return:
The size of file in bytes.
"""
return os.stat(fpath).st_size
- def launch_efi(enable_fdt, enable_comp):
"""Launch a UEFI binary from a FIT image.
Creates a test FIT image containing a fake UEFI binary and tries
to run it via the 'bootm' command in the U-Boot console.
Args:
enable_fdt: Flag to enable using the FDT blob inside FIT image.
enable_comp: Flag to enable gzip compression on EFI and FDT content.
"""
# Create test FIT image.
fit_img_path = make_fit(enable_comp, enable_comp)
# Select boot configuration.
fit_config = 'config-efi-fdt' if enable_fdt else 'config-efi-nofdt'
# Run U-Boot commands.
with u_boot_console.log.section('Boot EFI FIT %s' % fit_config):
output = u_boot_console.run_command(
'host load hostfs - $kernel_addr_r %s' % fit_img_path)
expected_text = '%d bytes read' % file_size(fit_img_path)
assert(expected_text in output)
output = u_boot_console.run_command(
'bootm ${kernel_addr_r}#%s' % fit_config)
if enable_fdt:
expected_text = 'Booting using the fdt blob'
assert(expected_text in output)
expected_text = 'Hello, world'
assert expected_text in output
expected_text = '## Application terminated, r = 0'
assert expected_text in output
- try:
# Use our own device tree file, will be restored afterwards.
control_dtb = make_dtb('internal', False)
old_dtb = u_boot_console.config.dtb
u_boot_console.config.dtb = control_dtb
# Run tests
# - fdt ON, gzip OFF
launch_efi(True, False)
# - fdt ON, gzip ON
launch_efi(True, True)
# - fdt OFF, gzip OFF
launch_efi(False, False)
# - fdt OFF, gzip ON
launch_efi(False, True)
- finally:
# Go back to the original U-Boot with the correct dtb.
u_boot_console.config.dtb = old_dtb
u_boot_console.restart_uboot()

On Tue, Dec 17, 2019 at 10:08:31PM +0100, Heinrich Schuchardt wrote:
On 12/17/19 8:47 AM, Cristian Ciocaltea wrote:
This test verifies the implementation of the 'bootm' extension that handles UEFI binaries inside FIT images (enabled via CONFIG_BOOTM_EFI).
Signed-off-by: Cristian Ciocaltea cristian.ciocaltea@gmail.com
test/py/tests/test_efi_fit.py | 233 ++++++++++++++++++++++++++++++++++ 1 file changed, 233 insertions(+) create mode 100644 test/py/tests/test_efi_fit.py
diff --git a/test/py/tests/test_efi_fit.py b/test/py/tests/test_efi_fit.py new file mode 100644 index 0000000000..52b415b198 --- /dev/null +++ b/test/py/tests/test_efi_fit.py @@ -0,0 +1,233 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2019, Cristian Ciocaltea cristian.ciocaltea@gmail.com
+# Test launching UEFI binaries from FIT images.
+import os +import pytest +import u_boot_utils as util
+# Define the parametrized ITS data to be used for FIT image generation. +its_data = ''' +/dts-v1/;
+/ {
- description = "EFI image with FDT blob";
- #address-cells = <1>;
- images {
efi {
description = "Sandbox EFI";
data = /incbin/("%(efi-bin)s");
type = "%(kernel-type)s";
arch = "sandbox";
os = "efi";
compression = "%(efi-comp)s";
load = <0x0>;
entry = <0x0>;
};
fdt {
description = "Sandbox FDT";
data = /incbin/("%(fdt-bin)s");
type = "flat_dt";
arch = "sandbox";
compression = "%(fdt-comp)s";
};
- };
- configurations {
default = "config-efi-fdt";
config-efi-fdt {
description = "EFI FIT w/ FDT";
kernel = "efi";
fdt = "fdt";
};
config-efi-nofdt {
description = "EFI FIT w/o FDT";
kernel = "efi";
};
- };
+}; +'''
+# Define the parametrized FDT data. +fdt_data = ''' +/dts-v1/;
+/ {
- model = "Sandbox %(fdt_type) EFI FIT Boot Test ";
- compatible = "sandbox";
- reset@0 {
compatible = "sandbox,reset";
This produces a warning:
+dtc ./test-efi-fit-sandbox-internal.dts -O dtb -o ./test-efi-fit-sandbox-internal.dtb ./test-efi-fit-sandbox-internal.dts:8.13-10.7: Warning (unit_address_vs_reg): /reset@0: node has a unit name, but no reg property
A similar sample is also used by test_fit.py and test_vboot.py, which expose the same warning, that's why I initially ignored it. If acceptable, I can suppress it via '-W no-unit_address_vs_reg'.
- };
+}; +'''
+@pytest.mark.boardspec('sandbox')
This test looks ok in principal. But why should we restrict it to the sandbox?
Let me see how this should work on real hardware, I'm going to test on qemu for the moment.
Best regards
Heinrich
+@pytest.mark.buildconfigspec('bootm_efi') +@pytest.mark.buildconfigspec('cmd_bootefi_hello_compile') +@pytest.mark.requiredtool('dtc') +def test_efi_fit(u_boot_console):
- """Test handling of UEFI binaries inside FIT images.
- The tests are trying to launch U-Boot's helloworld.efi embedded into
- FIT images, in uncompressed or gzip compressed format.
- Additionally, a sample FDT blob is created and embedded into the above
- mentioned FIT images, in uncompressed or gzip compressed format.
- The following test cases are currently defined and enabled:
- Launch uncompressed FIT EFI & FIT FDT
- Launch compressed FIT EFI & FIT FDT
- Launch uncompressed FIT EFI & internal FDT
- Launch compressed FIT EFI & internal FDT
- """
- def make_fpath(fname):
"""Compute the path of a given (temporary) file.
Args:
fname: The name of a file within U-Boot build dir.
Return:
The computed file path.
"""
return os.path.join(u_boot_console.config.build_dir, fname)
- def make_efi(fname, comp):
"""Create an UEFI binary.
This simply copies lib/efi_loader/helloworld.efi into U-Boot
build dir and, optionally, compresses the file using gzip.
Args:
fname: The target file name within U-Boot build dir.
comp: Flag to enable gzip compression.
Return:
The path of the created file.
"""
bin_path = make_fpath(fname)
os.system('cp %s %s' % (make_fpath('lib/efi_loader/helloworld.efi'), bin_path))
if comp:
util.run_and_log(u_boot_console, ['gzip', '-f', bin_path])
bin_path += '.gz'
return bin_path
- def make_dtb(fdt_type, comp):
"""Create a sample DTB file.
Creates a DTS file and compiles it to a DTB.
Args:
fdt_type: The type of the FDT, i.e. internal, user.
comp: Flag to enable gzip compression.
Returns:
The path of the created file.
"""
dts = make_fpath('test-efi-fit-sandbox-%s.dts' % fdt_type)
dtb = make_fpath('test-efi-fit-sandbox-%s.dtb' % fdt_type)
with open(dts, 'w') as fd:
fd.write(fdt_data)
util.run_and_log(u_boot_console, ['dtc', dts, '-O', 'dtb', '-o', dtb])
if comp:
util.run_and_log(u_boot_console, ['gzip', '-f', dtb])
dtb += '.gz'
return dtb
- def make_fit(efi_comp, fdt_comp):
"""Create a sample FIT image.
Runs 'mkimage' to create a FIT image within U-Boot build dir.
Args:
efi_comp: Enable gzip compression for the EFI binary.
fdt_comp: Enable gzip compression for the FDT blob.
Return:
The path of the created file.
"""
# Generate resources referenced by ITS.
its_params = {
'efi-bin' : os.path.basename(
make_efi('test-efi-fit-sandbox.efi', efi_comp)),
'kernel-type' : 'kernel' if efi_comp else 'kernel_noload',
'efi-comp' : 'gzip' if efi_comp else 'none',
'fdt-bin' : os.path.basename(make_dtb('user', fdt_comp)),
'fdt-comp' : 'gzip' if fdt_comp else 'none',
}
# Generate a test ITS file.
its_path = make_fpath('test-efi-fit.its')
with open(its_path, 'w') as fd:
fd.write(its_data % its_params)
# Build the test ITS.
fit_path = make_fpath('test-efi-fit.fit')
util.run_and_log(u_boot_console,
[make_fpath('tools/mkimage'), '-f', its_path, fit_path])
return fit_path
- def file_size(fpath):
"""Get the size of a file.
Args:
fname: Path of the file to check.
Return:
The size of file in bytes.
"""
return os.stat(fpath).st_size
- def launch_efi(enable_fdt, enable_comp):
"""Launch a UEFI binary from a FIT image.
Creates a test FIT image containing a fake UEFI binary and tries
to run it via the 'bootm' command in the U-Boot console.
Args:
enable_fdt: Flag to enable using the FDT blob inside FIT image.
enable_comp: Flag to enable gzip compression on EFI and FDT content.
"""
# Create test FIT image.
fit_img_path = make_fit(enable_comp, enable_comp)
# Select boot configuration.
fit_config = 'config-efi-fdt' if enable_fdt else 'config-efi-nofdt'
# Run U-Boot commands.
with u_boot_console.log.section('Boot EFI FIT %s' % fit_config):
output = u_boot_console.run_command(
'host load hostfs - $kernel_addr_r %s' % fit_img_path)
expected_text = '%d bytes read' % file_size(fit_img_path)
assert(expected_text in output)
output = u_boot_console.run_command(
'bootm ${kernel_addr_r}#%s' % fit_config)
if enable_fdt:
expected_text = 'Booting using the fdt blob'
assert(expected_text in output)
expected_text = 'Hello, world'
assert expected_text in output
expected_text = '## Application terminated, r = 0'
assert expected_text in output
- try:
# Use our own device tree file, will be restored afterwards.
control_dtb = make_dtb('internal', False)
old_dtb = u_boot_console.config.dtb
u_boot_console.config.dtb = control_dtb
# Run tests
# - fdt ON, gzip OFF
launch_efi(True, False)
# - fdt ON, gzip ON
launch_efi(True, True)
# - fdt OFF, gzip OFF
launch_efi(False, False)
# - fdt OFF, gzip ON
launch_efi(False, True)
- finally:
# Go back to the original U-Boot with the correct dtb.
u_boot_console.config.dtb = old_dtb
u_boot_console.restart_uboot()

On 12/18/19 9:22 AM, Cristian Ciocaltea wrote:
On Tue, Dec 17, 2019 at 10:08:31PM +0100, Heinrich Schuchardt wrote:
On 12/17/19 8:47 AM, Cristian Ciocaltea wrote:
This test verifies the implementation of the 'bootm' extension that handles UEFI binaries inside FIT images (enabled via CONFIG_BOOTM_EFI).
Signed-off-by: Cristian Ciocaltea cristian.ciocaltea@gmail.com
test/py/tests/test_efi_fit.py | 233 ++++++++++++++++++++++++++++++++++ 1 file changed, 233 insertions(+) create mode 100644 test/py/tests/test_efi_fit.py
diff --git a/test/py/tests/test_efi_fit.py b/test/py/tests/test_efi_fit.py new file mode 100644 index 0000000000..52b415b198 --- /dev/null +++ b/test/py/tests/test_efi_fit.py @@ -0,0 +1,233 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2019, Cristian Ciocaltea cristian.ciocaltea@gmail.com
+# Test launching UEFI binaries from FIT images.
+import os +import pytest +import u_boot_utils as util
+# Define the parametrized ITS data to be used for FIT image generation. +its_data = ''' +/dts-v1/;
+/ {
- description = "EFI image with FDT blob";
- #address-cells = <1>;
- images {
efi {
description = "Sandbox EFI";
data = /incbin/("%(efi-bin)s");
type = "%(kernel-type)s";
arch = "sandbox";
os = "efi";
compression = "%(efi-comp)s";
load = <0x0>;
entry = <0x0>;
};
fdt {
description = "Sandbox FDT";
data = /incbin/("%(fdt-bin)s");
type = "flat_dt";
arch = "sandbox";
compression = "%(fdt-comp)s";
};
- };
- configurations {
default = "config-efi-fdt";
config-efi-fdt {
description = "EFI FIT w/ FDT";
kernel = "efi";
fdt = "fdt";
};
config-efi-nofdt {
description = "EFI FIT w/o FDT";
kernel = "efi";
};
- };
+}; +'''
+# Define the parametrized FDT data. +fdt_data = ''' +/dts-v1/;
+/ {
- model = "Sandbox %(fdt_type) EFI FIT Boot Test ";
- compatible = "sandbox";
- reset@0 {
compatible = "sandbox,reset";
This produces a warning:
+dtc ./test-efi-fit-sandbox-internal.dts -O dtb -o ./test-efi-fit-sandbox-internal.dtb ./test-efi-fit-sandbox-internal.dts:8.13-10.7: Warning (unit_address_vs_reg): /reset@0: node has a unit name, but no reg property
A similar sample is also used by test_fit.py and test_vboot.py, which expose the same warning, that's why I initially ignored it. If acceptable, I can suppress it via '-W no-unit_address_vs_reg'.
reset@0 includes a unit-address. So a reg property is expected. How about:
/dts-v1/;
/ { #address-cells = <1>; #size-cells = <0>;
model = "Sandbox %(fdt_type) EFI FIT Boot Test "; compatible = "sandbox";
reset@0 { compatible = "sandbox,reset"; reg = <0>; }; };
Best regards
Heinrich
- };
+}; +'''
+@pytest.mark.boardspec('sandbox')
This test looks ok in principal. But why should we restrict it to the sandbox?
Let me see how this should work on real hardware, I'm going to test on qemu for the moment.
Best regards
Heinrich
+@pytest.mark.buildconfigspec('bootm_efi') +@pytest.mark.buildconfigspec('cmd_bootefi_hello_compile') +@pytest.mark.requiredtool('dtc') +def test_efi_fit(u_boot_console):
- """Test handling of UEFI binaries inside FIT images.
- The tests are trying to launch U-Boot's helloworld.efi embedded into
- FIT images, in uncompressed or gzip compressed format.
- Additionally, a sample FDT blob is created and embedded into the above
- mentioned FIT images, in uncompressed or gzip compressed format.
- The following test cases are currently defined and enabled:
- Launch uncompressed FIT EFI & FIT FDT
- Launch compressed FIT EFI & FIT FDT
- Launch uncompressed FIT EFI & internal FDT
- Launch compressed FIT EFI & internal FDT
- """
- def make_fpath(fname):
"""Compute the path of a given (temporary) file.
Args:
fname: The name of a file within U-Boot build dir.
Return:
The computed file path.
"""
return os.path.join(u_boot_console.config.build_dir, fname)
- def make_efi(fname, comp):
"""Create an UEFI binary.
This simply copies lib/efi_loader/helloworld.efi into U-Boot
build dir and, optionally, compresses the file using gzip.
Args:
fname: The target file name within U-Boot build dir.
comp: Flag to enable gzip compression.
Return:
The path of the created file.
"""
bin_path = make_fpath(fname)
os.system('cp %s %s' % (make_fpath('lib/efi_loader/helloworld.efi'), bin_path))
if comp:
util.run_and_log(u_boot_console, ['gzip', '-f', bin_path])
bin_path += '.gz'
return bin_path
- def make_dtb(fdt_type, comp):
"""Create a sample DTB file.
Creates a DTS file and compiles it to a DTB.
Args:
fdt_type: The type of the FDT, i.e. internal, user.
comp: Flag to enable gzip compression.
Returns:
The path of the created file.
"""
dts = make_fpath('test-efi-fit-sandbox-%s.dts' % fdt_type)
dtb = make_fpath('test-efi-fit-sandbox-%s.dtb' % fdt_type)
with open(dts, 'w') as fd:
fd.write(fdt_data)
util.run_and_log(u_boot_console, ['dtc', dts, '-O', 'dtb', '-o', dtb])
if comp:
util.run_and_log(u_boot_console, ['gzip', '-f', dtb])
dtb += '.gz'
return dtb
- def make_fit(efi_comp, fdt_comp):
"""Create a sample FIT image.
Runs 'mkimage' to create a FIT image within U-Boot build dir.
Args:
efi_comp: Enable gzip compression for the EFI binary.
fdt_comp: Enable gzip compression for the FDT blob.
Return:
The path of the created file.
"""
# Generate resources referenced by ITS.
its_params = {
'efi-bin' : os.path.basename(
make_efi('test-efi-fit-sandbox.efi', efi_comp)),
'kernel-type' : 'kernel' if efi_comp else 'kernel_noload',
'efi-comp' : 'gzip' if efi_comp else 'none',
'fdt-bin' : os.path.basename(make_dtb('user', fdt_comp)),
'fdt-comp' : 'gzip' if fdt_comp else 'none',
}
# Generate a test ITS file.
its_path = make_fpath('test-efi-fit.its')
with open(its_path, 'w') as fd:
fd.write(its_data % its_params)
# Build the test ITS.
fit_path = make_fpath('test-efi-fit.fit')
util.run_and_log(u_boot_console,
[make_fpath('tools/mkimage'), '-f', its_path, fit_path])
return fit_path
- def file_size(fpath):
"""Get the size of a file.
Args:
fname: Path of the file to check.
Return:
The size of file in bytes.
"""
return os.stat(fpath).st_size
- def launch_efi(enable_fdt, enable_comp):
"""Launch a UEFI binary from a FIT image.
Creates a test FIT image containing a fake UEFI binary and tries
to run it via the 'bootm' command in the U-Boot console.
Args:
enable_fdt: Flag to enable using the FDT blob inside FIT image.
enable_comp: Flag to enable gzip compression on EFI and FDT content.
"""
# Create test FIT image.
fit_img_path = make_fit(enable_comp, enable_comp)
# Select boot configuration.
fit_config = 'config-efi-fdt' if enable_fdt else 'config-efi-nofdt'
# Run U-Boot commands.
with u_boot_console.log.section('Boot EFI FIT %s' % fit_config):
output = u_boot_console.run_command(
'host load hostfs - $kernel_addr_r %s' % fit_img_path)
expected_text = '%d bytes read' % file_size(fit_img_path)
assert(expected_text in output)
output = u_boot_console.run_command(
'bootm ${kernel_addr_r}#%s' % fit_config)
if enable_fdt:
expected_text = 'Booting using the fdt blob'
assert(expected_text in output)
expected_text = 'Hello, world'
assert expected_text in output
expected_text = '## Application terminated, r = 0'
assert expected_text in output
- try:
# Use our own device tree file, will be restored afterwards.
control_dtb = make_dtb('internal', False)
old_dtb = u_boot_console.config.dtb
u_boot_console.config.dtb = control_dtb
# Run tests
# - fdt ON, gzip OFF
launch_efi(True, False)
# - fdt ON, gzip ON
launch_efi(True, True)
# - fdt OFF, gzip OFF
launch_efi(False, False)
# - fdt OFF, gzip ON
launch_efi(False, True)
- finally:
# Go back to the original U-Boot with the correct dtb.
u_boot_console.config.dtb = old_dtb
u_boot_console.restart_uboot()

On Wed, Dec 18, 2019 at 11:06:48AM +0100, Heinrich Schuchardt wrote:
On 12/18/19 9:22 AM, Cristian Ciocaltea wrote:
On Tue, Dec 17, 2019 at 10:08:31PM +0100, Heinrich Schuchardt wrote:
On 12/17/19 8:47 AM, Cristian Ciocaltea wrote:
This test verifies the implementation of the 'bootm' extension that handles UEFI binaries inside FIT images (enabled via CONFIG_BOOTM_EFI).
Signed-off-by: Cristian Ciocaltea cristian.ciocaltea@gmail.com
test/py/tests/test_efi_fit.py | 233 ++++++++++++++++++++++++++++++++++ 1 file changed, 233 insertions(+) create mode 100644 test/py/tests/test_efi_fit.py
diff --git a/test/py/tests/test_efi_fit.py b/test/py/tests/test_efi_fit.py new file mode 100644 index 0000000000..52b415b198 --- /dev/null +++ b/test/py/tests/test_efi_fit.py @@ -0,0 +1,233 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2019, Cristian Ciocaltea cristian.ciocaltea@gmail.com
+# Test launching UEFI binaries from FIT images.
+import os +import pytest +import u_boot_utils as util
+# Define the parametrized ITS data to be used for FIT image generation. +its_data = ''' +/dts-v1/;
+/ {
- description = "EFI image with FDT blob";
- #address-cells = <1>;
- images {
efi {
description = "Sandbox EFI";
data = /incbin/("%(efi-bin)s");
type = "%(kernel-type)s";
arch = "sandbox";
os = "efi";
compression = "%(efi-comp)s";
load = <0x0>;
entry = <0x0>;
};
fdt {
description = "Sandbox FDT";
data = /incbin/("%(fdt-bin)s");
type = "flat_dt";
arch = "sandbox";
compression = "%(fdt-comp)s";
};
- };
- configurations {
default = "config-efi-fdt";
config-efi-fdt {
description = "EFI FIT w/ FDT";
kernel = "efi";
fdt = "fdt";
};
config-efi-nofdt {
description = "EFI FIT w/o FDT";
kernel = "efi";
};
- };
+}; +'''
+# Define the parametrized FDT data. +fdt_data = ''' +/dts-v1/;
+/ {
- model = "Sandbox %(fdt_type) EFI FIT Boot Test ";
- compatible = "sandbox";
- reset@0 {
compatible = "sandbox,reset";
This produces a warning:
+dtc ./test-efi-fit-sandbox-internal.dts -O dtb -o ./test-efi-fit-sandbox-internal.dtb ./test-efi-fit-sandbox-internal.dts:8.13-10.7: Warning (unit_address_vs_reg): /reset@0: node has a unit name, but no reg property
A similar sample is also used by test_fit.py and test_vboot.py, which expose the same warning, that's why I initially ignored it. If acceptable, I can suppress it via '-W no-unit_address_vs_reg'.
reset@0 includes a unit-address. So a reg property is expected. How about:
/dts-v1/;
/ { #address-cells = <1>; #size-cells = <0>;
model = "Sandbox %(fdt_type) EFI FIT Boot Test "; compatible = "sandbox";
reset@0 { compatible = "sandbox,reset"; reg = <0>; }; };
Yes, that's a much better approach. It works fine, thanks!
Best regards
Heinrich
- };
+}; +'''
+@pytest.mark.boardspec('sandbox')
This test looks ok in principal. But why should we restrict it to the sandbox?
Let me see how this should work on real hardware, I'm going to test on qemu for the moment.
Best regards
Heinrich
+@pytest.mark.buildconfigspec('bootm_efi') +@pytest.mark.buildconfigspec('cmd_bootefi_hello_compile') +@pytest.mark.requiredtool('dtc') +def test_efi_fit(u_boot_console):
- """Test handling of UEFI binaries inside FIT images.
- The tests are trying to launch U-Boot's helloworld.efi embedded into
- FIT images, in uncompressed or gzip compressed format.
- Additionally, a sample FDT blob is created and embedded into the above
- mentioned FIT images, in uncompressed or gzip compressed format.
- The following test cases are currently defined and enabled:
- Launch uncompressed FIT EFI & FIT FDT
- Launch compressed FIT EFI & FIT FDT
- Launch uncompressed FIT EFI & internal FDT
- Launch compressed FIT EFI & internal FDT
- """
- def make_fpath(fname):
"""Compute the path of a given (temporary) file.
Args:
fname: The name of a file within U-Boot build dir.
Return:
The computed file path.
"""
return os.path.join(u_boot_console.config.build_dir, fname)
- def make_efi(fname, comp):
"""Create an UEFI binary.
This simply copies lib/efi_loader/helloworld.efi into U-Boot
build dir and, optionally, compresses the file using gzip.
Args:
fname: The target file name within U-Boot build dir.
comp: Flag to enable gzip compression.
Return:
The path of the created file.
"""
bin_path = make_fpath(fname)
os.system('cp %s %s' % (make_fpath('lib/efi_loader/helloworld.efi'), bin_path))
if comp:
util.run_and_log(u_boot_console, ['gzip', '-f', bin_path])
bin_path += '.gz'
return bin_path
- def make_dtb(fdt_type, comp):
"""Create a sample DTB file.
Creates a DTS file and compiles it to a DTB.
Args:
fdt_type: The type of the FDT, i.e. internal, user.
comp: Flag to enable gzip compression.
Returns:
The path of the created file.
"""
dts = make_fpath('test-efi-fit-sandbox-%s.dts' % fdt_type)
dtb = make_fpath('test-efi-fit-sandbox-%s.dtb' % fdt_type)
with open(dts, 'w') as fd:
fd.write(fdt_data)
util.run_and_log(u_boot_console, ['dtc', dts, '-O', 'dtb', '-o', dtb])
if comp:
util.run_and_log(u_boot_console, ['gzip', '-f', dtb])
dtb += '.gz'
return dtb
- def make_fit(efi_comp, fdt_comp):
"""Create a sample FIT image.
Runs 'mkimage' to create a FIT image within U-Boot build dir.
Args:
efi_comp: Enable gzip compression for the EFI binary.
fdt_comp: Enable gzip compression for the FDT blob.
Return:
The path of the created file.
"""
# Generate resources referenced by ITS.
its_params = {
'efi-bin' : os.path.basename(
make_efi('test-efi-fit-sandbox.efi', efi_comp)),
'kernel-type' : 'kernel' if efi_comp else 'kernel_noload',
'efi-comp' : 'gzip' if efi_comp else 'none',
'fdt-bin' : os.path.basename(make_dtb('user', fdt_comp)),
'fdt-comp' : 'gzip' if fdt_comp else 'none',
}
# Generate a test ITS file.
its_path = make_fpath('test-efi-fit.its')
with open(its_path, 'w') as fd:
fd.write(its_data % its_params)
# Build the test ITS.
fit_path = make_fpath('test-efi-fit.fit')
util.run_and_log(u_boot_console,
[make_fpath('tools/mkimage'), '-f', its_path, fit_path])
return fit_path
- def file_size(fpath):
"""Get the size of a file.
Args:
fname: Path of the file to check.
Return:
The size of file in bytes.
"""
return os.stat(fpath).st_size
- def launch_efi(enable_fdt, enable_comp):
"""Launch a UEFI binary from a FIT image.
Creates a test FIT image containing a fake UEFI binary and tries
to run it via the 'bootm' command in the U-Boot console.
Args:
enable_fdt: Flag to enable using the FDT blob inside FIT image.
enable_comp: Flag to enable gzip compression on EFI and FDT content.
"""
# Create test FIT image.
fit_img_path = make_fit(enable_comp, enable_comp)
# Select boot configuration.
fit_config = 'config-efi-fdt' if enable_fdt else 'config-efi-nofdt'
# Run U-Boot commands.
with u_boot_console.log.section('Boot EFI FIT %s' % fit_config):
output = u_boot_console.run_command(
'host load hostfs - $kernel_addr_r %s' % fit_img_path)
expected_text = '%d bytes read' % file_size(fit_img_path)
assert(expected_text in output)
output = u_boot_console.run_command(
'bootm ${kernel_addr_r}#%s' % fit_config)
if enable_fdt:
expected_text = 'Booting using the fdt blob'
assert(expected_text in output)
expected_text = 'Hello, world'
assert expected_text in output
expected_text = '## Application terminated, r = 0'
assert expected_text in output
- try:
# Use our own device tree file, will be restored afterwards.
control_dtb = make_dtb('internal', False)
old_dtb = u_boot_console.config.dtb
u_boot_console.config.dtb = control_dtb
# Run tests
# - fdt ON, gzip OFF
launch_efi(True, False)
# - fdt ON, gzip ON
launch_efi(True, True)
# - fdt OFF, gzip OFF
launch_efi(False, False)
# - fdt OFF, gzip ON
launch_efi(False, True)
- finally:
# Go back to the original U-Boot with the correct dtb.
u_boot_console.config.dtb = old_dtb
u_boot_console.restart_uboot()

On 12/18/19 9:22 AM, Cristian Ciocaltea wrote:
- };
+}; +'''
+@pytest.mark.boardspec('sandbox')
This test looks ok in principal. But why should we restrict it to the sandbox?
Let me see how this should work on real hardware, I'm going to test on qemu for the moment.
Device trees cannot be used in conjunction with ACPI tables when booting via UEFI. Currently this concerns only x86 and x86_64 but Phytec is working on an arm64 board which shall provide an ACPI table. So you probably want to check CONFIG_GENERATE_ACPI_TABLE instead of the board type, e.g.
@pytest.mark.notbuildconfigspec('generate_acpi_table')
Best regards
Heinrich

On Wed, Dec 18, 2019 at 11:56:00AM +0100, Heinrich Schuchardt wrote:
On 12/18/19 9:22 AM, Cristian Ciocaltea wrote:
- };
+}; +'''
+@pytest.mark.boardspec('sandbox')
This test looks ok in principal. But why should we restrict it to the sandbox?
Let me see how this should work on real hardware, I'm going to test on qemu for the moment.
Device trees cannot be used in conjunction with ACPI tables when booting via UEFI. Currently this concerns only x86 and x86_64 but Phytec is working on an arm64 board which shall provide an ACPI table. So you probably want to check CONFIG_GENERATE_ACPI_TABLE instead of the board type, e.g.
@pytest.mark.notbuildconfigspec('generate_acpi_table')
Thanks for the hint!
For some strange reason, whatever I put in the 'notbuildconfigspec' marker causes the test to be skipped:
[-] Section: test_efi_fit TIME: NOW: 2019/12/18 17:33:14.976576 TIME: SINCE-PREV: 0:00:00.192132 TIME: SINCE-START: 0:00:00.192132 SKIPPED: ('[...]/uboot/test/py/conftest.py', 463, 'Skipped: .config feature "bootm_efi" enabled')
I don't really understand the connection to 'bootm_efi'. If I comment out the 'bootm_efi' marker, the reported status becomes:
SKIPPED: ('[].../uboot/test/py/conftest.py', 463, 'Skipped: .config feature "cmd_bootefi_hello_compile" enabled')
So it seems 'notbuildconfigspec' gets its parameter from the first active 'buildconfigspec' statement. This is my current test:
#@pytest.mark.buildconfigspec('bootm_efi') @pytest.mark.buildconfigspec('cmd_bootefi_hello_compile') #@pytest.mark.notbuildconfigspec('generate_acpi_table') @pytest.mark.notbuildconfigspec('fake_item') @pytest.mark.requiredtool('dtc')
Best regards
Heinrich
participants (2)
-
Cristian Ciocaltea
-
Heinrich Schuchardt