[PATCH v4 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 v4: * Extend the python test to also run on real hardware, currently tested on qemu_arm
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 | 459 ++++++++++++++++++++++++++++++++++ 8 files changed, 627 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 Reviewed-by: Heinrich Schuchardt xypron.glpk@gmx.de --- 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 c52f945120..231612ff5f 100644 --- a/common/image-fit.c +++ b/common/image-fit.c @@ -1926,7 +1926,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 eb626dcac9..75d5dd944f 100644 --- a/common/image.c +++ b/common/image.c @@ -137,6 +137,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, };

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 Reviewed-by: Heinrich Schuchardt xypron.glpk@gmx.de --- cmd/Kconfig | 7 ++++++ common/bootm_os.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+)
diff --git a/cmd/Kconfig b/cmd/Kconfig index 1e4cf146c5..87f2335a3c 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 d89ddc32b0..1d58462509 100644 --- a/common/bootm_os.c +++ b/common/bootm_os.c @@ -7,10 +7,12 @@ #include <common.h> #include <bootm.h> #include <cpu_func.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>
@@ -498,6 +500,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 @@ -534,6 +587,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/24/19 5:05 PM, 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 Reviewed-by: Heinrich Schuchardt xypron.glpk@gmx.de
cmd/Kconfig | 7 ++++++ common/bootm_os.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+)
diff --git a/cmd/Kconfig b/cmd/Kconfig index 1e4cf146c5..87f2335a3c 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"
Shouldn't this be "Support booting UEFI FIT images"?
- depends on CMD_BOOTEFI
depends on BOOTM
is missing here.
- default y
- help
Support booting EFI images via the bootm command.
Should we say:
Support booting UEFI FIT images via the bootm command.
Best regards
Heinrich
- 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 d89ddc32b0..1d58462509 100644 --- a/common/bootm_os.c +++ b/common/bootm_os.c @@ -7,10 +7,12 @@ #include <common.h> #include <bootm.h> #include <cpu_func.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>
@@ -498,6 +500,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
@@ -534,6 +587,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/29/19 11:34 AM, Heinrich Schuchardt wrote:
On 12/24/19 5:05 PM, 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 Reviewed-by: Heinrich Schuchardt xypron.glpk@gmx.de
cmd/Kconfig | 7 ++++++ common/bootm_os.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+)
diff --git a/cmd/Kconfig b/cmd/Kconfig index 1e4cf146c5..87f2335a3c 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"
Shouldn't this be "Support booting UEFI FIT images"?
+ depends on CMD_BOOTEFI
depends on BOOTM
depends on CMD_BOOTM
The patch series compiles without CONFIG_FIT. But shouldn't this also be a dependency?
If we place the definition directly after CMD_BOOTM, it will be indented so that it is evident that this is a sub-feature of CMD_BOOTM.
So how about the following?
config CMD_BOOTM bool "bootm" default y help Boot an application image from the memory.
config BOOTM_EFI bool "Support booting UEFI FIT images" depends on CMD_BOOTEFI && CMD_BOOTM && FIT default y help Support booting UEFI FIT images via the bootm command.
Best regards
Heinrich
is missing here.
+ default y + help + Support booting EFI images via the bootm command.
Should we say:
Support booting UEFI FIT images via the bootm command.
Best regards
Heinrich
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 d89ddc32b0..1d58462509 100644 --- a/common/bootm_os.c +++ b/common/bootm_os.c @@ -7,10 +7,12 @@ #include <common.h> #include <bootm.h> #include <cpu_func.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>
@@ -498,6 +500,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 @@ -534,6 +587,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 Sun, Dec 29, 2019 at 11:56:01AM +0100, Heinrich Schuchardt wrote:
On 12/29/19 11:34 AM, Heinrich Schuchardt wrote:
On 12/24/19 5:05 PM, 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 Reviewed-by: Heinrich Schuchardt xypron.glpk@gmx.de
cmd/Kconfig | 7 ++++++ common/bootm_os.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+)
diff --git a/cmd/Kconfig b/cmd/Kconfig index 1e4cf146c5..87f2335a3c 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"
Shouldn't this be "Support booting UEFI FIT images"?
+ depends on CMD_BOOTEFI
depends on BOOTM
depends on CMD_BOOTM
The patch series compiles without CONFIG_FIT. But shouldn't this also be a dependency?
Indeed, thanks.
If we place the definition directly after CMD_BOOTM, it will be indented so that it is evident that this is a sub-feature of CMD_BOOTM.
So how about the following?
config CMD_BOOTM bool "bootm" default y help Boot an application image from the memory.
config BOOTM_EFI bool "Support booting UEFI FIT images" depends on CMD_BOOTEFI && CMD_BOOTM && FIT default y help Support booting UEFI FIT images via the bootm command.
In this case, we should probably also move CMD_BOOTZ, CMD_BOOTI and BOOTM_LINUX right after CMD_BOOTEFI_HELLO, since those commands do not depend on CMD_BOOTM, while all BOOTM_* features, except BOOTM_LINUX, depend exclusively on CMD_BOOTM.
Best regards
Heinrich
is missing here.
+ default y + help + Support booting EFI images via the bootm command.
Should we say:
Support booting UEFI FIT images via the bootm command.
Best regards
Heinrich
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 d89ddc32b0..1d58462509 100644 --- a/common/bootm_os.c +++ b/common/bootm_os.c @@ -7,10 +7,12 @@ #include <common.h> #include <bootm.h> #include <cpu_func.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>
@@ -498,6 +500,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 @@ -534,6 +587,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 Sun, Dec 29, 2019 at 11:34:09AM +0100, Heinrich Schuchardt wrote:
On 12/24/19 5:05 PM, 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 Reviewed-by: Heinrich Schuchardt xypron.glpk@gmx.de
cmd/Kconfig | 7 ++++++ common/bootm_os.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+)
diff --git a/cmd/Kconfig b/cmd/Kconfig index 1e4cf146c5..87f2335a3c 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"
Shouldn't this be "Support booting UEFI FIT images"?
Done.
- depends on CMD_BOOTEFI
depends on BOOTM
is missing here.
Right, thanks. Added 'CMD_BOOTEFI && CMD_BOOTM'.
- default y
- help
Support booting EFI images via the bootm command.
Should we say:
Support booting UEFI FIT images via the bootm command.
Done.
Best regards
Heinrich
- 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 d89ddc32b0..1d58462509 100644 --- a/common/bootm_os.c +++ b/common/bootm_os.c @@ -7,10 +7,12 @@ #include <common.h> #include <bootm.h> #include <cpu_func.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>
@@ -498,6 +500,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
@@ -534,6 +587,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 */

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 | 459 ++++++++++++++++++++++++++++++++++ 1 file changed, 459 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..e1f0e42694 --- /dev/null +++ b/test/py/tests/test_efi_fit.py @@ -0,0 +1,459 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2019, Cristian Ciocaltea cristian.ciocaltea@gmail.com +# +# Work based on: +# - test_net.py +# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. +# - test_fit.py +# Copyright (c) 2013, Google Inc. +# +# Test launching UEFI binaries from FIT images. + +import os.path +import pytest +import u_boot_utils as util + +""" +Note: This test relies on boardenv_* containing configuration values to define +which network environment is available for testing. Without this, the parts +that rely on network will be automatically skipped. + +For example: + +# Boolean indicating whether the Ethernet device is attached to USB, and hence +# USB enumeration needs to be performed prior to network tests. +# This variable may be omitted if its value is False. +env__net_uses_usb = False + +# Boolean indicating whether the Ethernet device is attached to PCI, and hence +# PCI enumeration needs to be performed prior to network tests. +# This variable may be omitted if its value is False. +env__net_uses_pci = True + +# True if a DHCP server is attached to the network, and should be tested. +# If DHCP testing is not possible or desired, this variable may be omitted or +# set to False. +env__net_dhcp_server = True + +# A list of environment variables that should be set in order to configure a +# static IP. If solely relying on DHCP, this variable may be omitted or set to +# an empty list. +env__net_static_env_vars = [ + ('ipaddr', '10.0.0.100'), + ('netmask', '255.255.255.0'), + ('serverip', '10.0.0.1'), +] + +# Details regarding a file that may be read from a TFTP server. This variable +# may be omitted or set to None if TFTP testing is not possible or desired. +# Additionally, when the 'size' is not available, the file will be generated +# automatically in the TFTP root directory, as specified by the 'dn' field. +env__efi_fit_tftp_file = { + 'fn': 'test-efi-fit.img', # File path relative to TFTP root + 'size': 3831, # File size + 'crc32': '9fa3f79c', # Checksum using CRC-32 algorithm, optional + 'addr': '$kernel_addr_r', # Loading address, optional + 'dn': 'tftp/root/dir', # TFTP root directory path, optional +} +""" + +# Define the parametrized ITS data to be used for FIT images generation. +its_data = ''' +/dts-v1/; + +/ { + description = "EFI image with FDT blob"; + #address-cells = <1>; + + images { + efi { + description = "Test EFI"; + data = /incbin/("%(efi-bin)s"); + type = "%(kernel-type)s"; + arch = "%(sys-arch)s"; + os = "efi"; + compression = "%(efi-comp)s"; + load = <0x0>; + entry = <0x0>; + }; + fdt { + description = "Test FDT"; + data = /incbin/("%(fdt-bin)s"); + type = "flat_dt"; + arch = "%(sys-arch)s"; + 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 to be used for DTB images generation. +fdt_data = ''' +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <0>; + + model = "%(sys-arch)s %(fdt_type)s EFI FIT Boot Test"; + compatible = "%(sys-arch)s"; + + reset@0 { + compatible = "%(sys-arch)s,reset"; + reg = <0>; + }; +}; +''' + +@pytest.mark.buildconfigspec('bootm_efi') +@pytest.mark.buildconfigspec('cmd_bootefi_hello_compile') +@pytest.mark.buildconfigspec('fit') +@pytest.mark.notbuildconfigspec('generate_acpi_table') +@pytest.mark.requiredtool('dtc') +def test_efi_fit_launch(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. + + For more details, see launch_efi(). + + The following test cases are currently defined and enabled: + - Launch uncompressed FIT EFI & internal FDT + - Launch uncompressed FIT EFI & FIT FDT + - Launch compressed FIT EFI & internal FDT + - Launch compressed FIT EFI & FIT FDT + """ + + def net_pre_commands(): + """Execute any commands required to enable network hardware. + + These commands are provided by the boardenv_* file; see the comment + at the beginning of this file. + """ + + init_usb = cons.config.env.get('env__net_uses_usb', False) + if init_usb: + cons.run_command('usb start') + + init_pci = cons.config.env.get('env__net_uses_pci', False) + if init_pci: + cons.run_command('pci enum') + + def net_dhcp(): + """Execute the dhcp command. + + The boardenv_* file may be used to enable/disable DHCP; see the + comment at the beginning of this file. + """ + + has_dhcp = cons.config.buildconfig.get('config_cmd_dhcp', 'n') == 'y' + if not has_dhcp: + cons.log.warning('CONFIG_CMD_DHCP != y: Skipping DHCP network setup') + return False + + test_dhcp = cons.config.env.get('env__net_dhcp_server', False) + if not test_dhcp: + cons.log.info('No DHCP server available') + return False + + cons.run_command('setenv autoload no') + output = cons.run_command('dhcp') + assert 'DHCP client bound to address ' in output + return True + + def net_setup_static(): + """Set up a static IP configuration. + + The configuration is provided by the boardenv_* file; see the comment at + the beginning of this file. + """ + + has_dhcp = cons.config.buildconfig.get('config_cmd_dhcp', 'n') == 'y' + if not has_dhcp: + cons.log.warning('CONFIG_NET != y: Skipping static network setup') + return False + + env_vars = cons.config.env.get('env__net_static_env_vars', None) + if not env_vars: + cons.log.info('No static network configuration is defined') + return False + + for (var, val) in env_vars: + cons.run_command('setenv %s %s' % (var, val)) + return True + + 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(cons.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) + util.run_and_log(cons, + ['cp', make_fpath('lib/efi_loader/helloworld.efi'), bin_path]) + if comp: + util.run_and_log(cons, ['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. + Return: + The path of the created file. + """ + + # Generate resources referenced by FDT. + fdt_params = { + 'sys-arch': sys_arch, + 'fdt_type' : fdt_type, + } + + # Generate a test FDT file. + dts = make_fpath('test-efi-fit-%s.dts' % fdt_type) + with open(dts, 'w') as fd: + fd.write(fdt_data % fdt_params) + + # Build the test FDT. + dtb = make_fpath('test-efi-fit-%s.dtb' % fdt_type) + util.run_and_log(cons, ['dtc', '-I', 'dts', '-O', 'dtb', '-o', dtb, dts]) + if comp: + util.run_and_log(cons, ['gzip', '-f', dtb]) + dtb += '.gz' + return dtb + + def make_fit(comp): + """Create a sample FIT image. + + Runs 'mkimage' to create a FIT image within U-Boot build dir. + Args: + comp: Enable gzip compression for the EFI binary and FDT blob. + Return: + The path of the created file. + """ + + # Generate resources referenced by ITS. + its_params = { + 'sys-arch': sys_arch, + 'efi-bin': os.path.basename(make_efi('test-efi-fit-helloworld.efi', comp)), + 'kernel-type': 'kernel' if comp else 'kernel_noload', + 'efi-comp': 'gzip' if comp else 'none', + 'fdt-bin': os.path.basename(make_dtb('user', comp)), + 'fdt-comp': 'gzip' if comp else 'none', + } + + # Generate a test ITS file. + its_path = make_fpath('test-efi-fit-helloworld.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-helloworld.fit') + util.run_and_log( + cons, [make_fpath('tools/mkimage'), '-f', its_path, fit_path]) + return fit_path + + def load_fit_from_host(f): + """Load the FIT image using the 'host load' command and return its address. + + Args: + f: Dictionary describing the FIT image to load, see env__efi_fit_test_file + in the comment at the beginning of this file. + Return: + The address where the file has been loaded. + """ + + addr = f.get('addr', None) + if not addr: + addr = u_boot_utils.find_ram_base(cons) + + output = cons.run_command( + 'host load hostfs - %s %s/%s' % (addr, f['dn'], f['fn'])) + expected_text = ' bytes read' + sz = f.get('size', None) + if sz: + expected_text = '%d' % sz + expected_text + assert(expected_text in output) + + return addr + + def load_fit_from_tftp(f): + """Load the FIT image using the tftpboot command and return its address. + + The file is downloaded from the TFTP server, its size and optionally its + CRC32 are validated. + + Args: + f: Dictionary describing the FIT image to load, see env__efi_fit_tftp_file + in the comment at the beginning of this file. + Return: + The address where the file has been loaded. + """ + + addr = f.get('addr', None) + if not addr: + addr = u_boot_utils.find_ram_base(cons) + + fn = f['fn'] + output = cons.run_command('tftpboot %s %s' % (addr, fn)) + expected_text = 'Bytes transferred = ' + sz = f.get('size', None) + if sz: + expected_text += '%d' % sz + assert expected_text in output + + expected_crc = f.get('crc32', None) + if not expected_crc: + return addr + + if cons.config.buildconfig.get('config_cmd_crc32', 'n') != 'y': + return addr + + output = cons.run_command('crc32 $fileaddr $filesize') + assert expected_crc in output + + return addr + + def launch_efi(enable_fdt, enable_comp): + """Launch U-Boot's helloworld.efi binary from a FIT image. + + An external image file can be downloaded from TFTP, when related + details are provided by the boardenv_* file; see the comment at the + beginning of this file. + + If the size of the TFTP file is not provided within env__efi_fit_tftp_file, + the test image is generated automatically and placed in the TFTP root + directory specified via the 'dn' field. + + When running the tests on Sandbox, the image file is loaded directly + from the host filesystem. + + Once the load address is available on U-Boot console, the 'bootm' + command is executed for either 'config-efi-fdt' or 'config-efi-nofdt' + FIT configuration, depending on the value of the 'enable_fdt' function + argument. + + Eventually the 'Hello, world' message is expected 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 + generated content. + """ + + with cons.log.section('FDT=%s;COMP=%s' % (enable_fdt, enable_comp)): + if is_sandbox: + fit = { + 'dn': cons.config.build_dir, + 'addr': '${kernel_addr_r}', + } + else: + # Init networking. + net_pre_commands() + net_set_up = net_dhcp() + net_set_up = net_setup_static() or net_set_up + if not net_set_up: + pytest.skip('Network not initialized') + + fit = cons.config.env.get('env__efi_fit_tftp_file', None) + if not fit: + pytest.skip('No env__efi_fit_tftp_file binary specified in environment') + + sz = fit.get('size', None) + if not sz: + if not fit.get('dn', None): + pytest.skip('Neither "size", nor "dn" info provided in env__efi_fit_tftp_file') + + # Create test FIT image. + fit_path = make_fit(enable_comp) + fit['fn'] = os.path.basename(fit_path) + fit['size'] = os.path.getsize(fit_path) + + # Copy image to TFTP root directory. + if fit['dn'] != cons.config.build_dir: + util.run_and_log(cons, ['mv', '-f', fit_path, '%s/' % fit['dn']]) + + # Load FIT image. + addr = load_fit_from_host(fit) if is_sandbox else load_fit_from_tftp(fit) + + # Select boot configuration. + fit_config = 'config-efi-fdt' if enable_fdt else 'config-efi-nofdt' + + # Try booting. + cons.run_command( + 'bootm %s#%s' % (addr, fit_config), wait_for_prompt=False) + if enable_fdt: + cons.wait_for('Booting using the fdt blob') + cons.wait_for('Hello, world') + cons.wait_for('## Application terminated, r = 0') + cons.restart_uboot(); + + cons = u_boot_console + # Array slice removes leading/trailing quotes. + sys_arch = cons.config.buildconfig.get('config_sys_arch', '"sandbox"')[1:-1] + is_sandbox = sys_arch == 'sandbox' + + try: + if is_sandbox: + # Use our own device tree file, will be restored afterwards. + control_dtb = make_dtb('internal', False) + old_dtb = cons.config.dtb + cons.config.dtb = control_dtb + + # Run tests + # - fdt OFF, gzip OFF + launch_efi(False, False) + # - fdt ON, gzip OFF + launch_efi(True, False) + + if is_sandbox: + # - fdt OFF, gzip ON + launch_efi(False, True) + # - fdt ON, gzip ON + launch_efi(True, True) + + finally: + if is_sandbox: + # Go back to the original U-Boot with the correct dtb. + cons.config.dtb = old_dtb + cons.restart_uboot()

On 12/24/19 5:05 PM, 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
Thanks a lot for devising this test.
---
You are using variable env__efi_fit_tftp_file. To run the test on Gitlab and Travis CI a patch will be needed for:
https://github.com/swarren/uboot-test-hooks.git
I hope
https://github.com/xypron/uboot-test-hooks/commit/20dcd721437dd5f7d7d3d235f7...
will do the job.
Once we have this applied we will have to adjust the config files for QEMU.
---
I have been trying to run the test on qemu_arm64_defconfig using the following lines in u_boot_boardenv_qemu_arm64.py:
env__efi_fit_tftp_file = { "fn": "helloworld.efi", "size": 4480, "crc32": "19f9c0ab", }
I got an error:
test/py/tests/test_efi_fit.py:417: in launch_efi addr = load_fit_from_host(fit) if is_sandbox else load_fit_from_tftp(fit) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ addr = f.get('addr', None) if not addr:
addr = u_boot_utils.find_ram_base(cons)
E NameError: name 'u_boot_utils' is not defined
When I provided addr:
env__efi_fit_tftp_file = { "fn": "helloworld.efi", "size": 4480, "crc32": "19f9c0ab", "addr": 0x40400000, }
I got the following error:
=> tftpboot 1073741824 helloworld.efi TFTP error: trying to overwrite reserved memory...
I would have expected a command
tftpboot 40400000 helloworld.efi
to be issued.
Same error with bootm:
=> bootm 1077936128#config-efi-nofdt "Synchronous Abort" handler, esr 0x96000010 elr: 000000000001c36c lr : 00000000000140f4 (reloc)
Please, fix the lines indicated below and verify that you can actually execute this test on the QEMU platform.
https://github.com/xypron/u-boot-build/tree/qemu-arm64/u-boot-test
contains the files I use to run Python tests on qemu_arm64_defconfig.
test/py/tests/test_efi_fit.py | 459 ++++++++++++++++++++++++++++++++++ 1 file changed, 459 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..e1f0e42694 --- /dev/null +++ b/test/py/tests/test_efi_fit.py @@ -0,0 +1,459 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2019, Cristian Ciocaltea cristian.ciocaltea@gmail.com +# +# Work based on: +# - test_net.py +# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. +# - test_fit.py +# Copyright (c) 2013, Google Inc. +# +# Test launching UEFI binaries from FIT images.
+import os.path +import pytest +import u_boot_utils as util
"as util" causes an error if you use u_boot_utils.* below. Below I indicate the places to change.
+""" +Note: This test relies on boardenv_* containing configuration values to define +which network environment is available for testing. Without this, the parts +that rely on network will be automatically skipped.
+For example:
+# Boolean indicating whether the Ethernet device is attached to USB, and hence +# USB enumeration needs to be performed prior to network tests. +# This variable may be omitted if its value is False. +env__net_uses_usb = False
+# Boolean indicating whether the Ethernet device is attached to PCI, and hence +# PCI enumeration needs to be performed prior to network tests. +# This variable may be omitted if its value is False. +env__net_uses_pci = True
+# True if a DHCP server is attached to the network, and should be tested. +# If DHCP testing is not possible or desired, this variable may be omitted or +# set to False. +env__net_dhcp_server = True
+# A list of environment variables that should be set in order to configure a +# static IP. If solely relying on DHCP, this variable may be omitted or set to +# an empty list. +env__net_static_env_vars = [
- ('ipaddr', '10.0.0.100'),
- ('netmask', '255.255.255.0'),
- ('serverip', '10.0.0.1'),
+]
+# Details regarding a file that may be read from a TFTP server. This variable +# may be omitted or set to None if TFTP testing is not possible or desired. +# Additionally, when the 'size' is not available, the file will be generated +# automatically in the TFTP root directory, as specified by the 'dn' field. +env__efi_fit_tftp_file = {
- 'fn': 'test-efi-fit.img', # File path relative to TFTP root
- 'size': 3831, # File size
- 'crc32': '9fa3f79c', # Checksum using CRC-32 algorithm, optional
- 'addr': '$kernel_addr_r', # Loading address, optional
- 'dn': 'tftp/root/dir', # TFTP root directory path, optional
+} +"""
+# Define the parametrized ITS data to be used for FIT images generation. +its_data = ''' +/dts-v1/;
+/ {
- description = "EFI image with FDT blob";
- #address-cells = <1>;
- images {
efi {
description = "Test EFI";
data = /incbin/("%(efi-bin)s");
type = "%(kernel-type)s";
arch = "%(sys-arch)s";
os = "efi";
compression = "%(efi-comp)s";
load = <0x0>;
entry = <0x0>;
};
fdt {
description = "Test FDT";
data = /incbin/("%(fdt-bin)s");
type = "flat_dt";
arch = "%(sys-arch)s";
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 to be used for DTB images generation. +fdt_data = ''' +/dts-v1/;
+/ {
- #address-cells = <1>;
- #size-cells = <0>;
- model = "%(sys-arch)s %(fdt_type)s EFI FIT Boot Test";
- compatible = "%(sys-arch)s";
- reset@0 {
compatible = "%(sys-arch)s,reset";
reg = <0>;
- };
+}; +'''
+@pytest.mark.buildconfigspec('bootm_efi') +@pytest.mark.buildconfigspec('cmd_bootefi_hello_compile') +@pytest.mark.buildconfigspec('fit') +@pytest.mark.notbuildconfigspec('generate_acpi_table') +@pytest.mark.requiredtool('dtc') +def test_efi_fit_launch(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.
- For more details, see launch_efi().
- The following test cases are currently defined and enabled:
- Launch uncompressed FIT EFI & internal FDT
- Launch uncompressed FIT EFI & FIT FDT
- Launch compressed FIT EFI & internal FDT
- Launch compressed FIT EFI & FIT FDT
- """
- def net_pre_commands():
"""Execute any commands required to enable network hardware.
These commands are provided by the boardenv_* file; see the comment
at the beginning of this file.
"""
init_usb = cons.config.env.get('env__net_uses_usb', False)
if init_usb:
cons.run_command('usb start')
init_pci = cons.config.env.get('env__net_uses_pci', False)
if init_pci:
cons.run_command('pci enum')
- def net_dhcp():
"""Execute the dhcp command.
The boardenv_* file may be used to enable/disable DHCP; see the
comment at the beginning of this file.
"""
has_dhcp = cons.config.buildconfig.get('config_cmd_dhcp', 'n') == 'y'
if not has_dhcp:
cons.log.warning('CONFIG_CMD_DHCP != y: Skipping DHCP network setup')
return False
test_dhcp = cons.config.env.get('env__net_dhcp_server', False)
if not test_dhcp:
cons.log.info('No DHCP server available')
return False
cons.run_command('setenv autoload no')
output = cons.run_command('dhcp')
assert 'DHCP client bound to address ' in output
return True
- def net_setup_static():
"""Set up a static IP configuration.
The configuration is provided by the boardenv_* file; see the comment at
the beginning of this file.
"""
has_dhcp = cons.config.buildconfig.get('config_cmd_dhcp', 'n') == 'y'
if not has_dhcp:
cons.log.warning('CONFIG_NET != y: Skipping static network setup')
return False
env_vars = cons.config.env.get('env__net_static_env_vars', None)
if not env_vars:
cons.log.info('No static network configuration is defined')
return False
for (var, val) in env_vars:
cons.run_command('setenv %s %s' % (var, val))
return True
- 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(cons.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)
util.run_and_log(cons,
['cp', make_fpath('lib/efi_loader/helloworld.efi'), bin_path])
if comp:
util.run_and_log(cons, ['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.
Return:
The path of the created file.
"""
# Generate resources referenced by FDT.
fdt_params = {
'sys-arch': sys_arch,
'fdt_type' : fdt_type,
}
# Generate a test FDT file.
dts = make_fpath('test-efi-fit-%s.dts' % fdt_type)
with open(dts, 'w') as fd:
fd.write(fdt_data % fdt_params)
# Build the test FDT.
dtb = make_fpath('test-efi-fit-%s.dtb' % fdt_type)
util.run_and_log(cons, ['dtc', '-I', 'dts', '-O', 'dtb', '-o', dtb, dts])
if comp:
util.run_and_log(cons, ['gzip', '-f', dtb])
dtb += '.gz'
return dtb
- def make_fit(comp):
"""Create a sample FIT image.
Runs 'mkimage' to create a FIT image within U-Boot build dir.
Args:
comp: Enable gzip compression for the EFI binary and FDT blob.
Return:
The path of the created file.
"""
# Generate resources referenced by ITS.
its_params = {
'sys-arch': sys_arch,
'efi-bin': os.path.basename(make_efi('test-efi-fit-helloworld.efi', comp)),
'kernel-type': 'kernel' if comp else 'kernel_noload',
'efi-comp': 'gzip' if comp else 'none',
'fdt-bin': os.path.basename(make_dtb('user', comp)),
'fdt-comp': 'gzip' if comp else 'none',
}
# Generate a test ITS file.
its_path = make_fpath('test-efi-fit-helloworld.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-helloworld.fit')
util.run_and_log(
cons, [make_fpath('tools/mkimage'), '-f', its_path, fit_path])
return fit_path
- def load_fit_from_host(f):
"""Load the FIT image using the 'host load' command and return its address.
Args:
f: Dictionary describing the FIT image to load, see env__efi_fit_test_file
in the comment at the beginning of this file.
Return:
The address where the file has been loaded.
"""
addr = f.get('addr', None)
if not addr:
addr = u_boot_utils.find_ram_base(cons)
%s/u_boot_utils/util/
output = cons.run_command(
'host load hostfs - %s %s/%s' % (addr, f['dn'], f['fn']))
expected_text = ' bytes read'
sz = f.get('size', None)
if sz:
expected_text = '%d' % sz + expected_text
assert(expected_text in output)
return addr
- def load_fit_from_tftp(f):
"""Load the FIT image using the tftpboot command and return its address.
The file is downloaded from the TFTP server, its size and optionally its
CRC32 are validated.
Args:
f: Dictionary describing the FIT image to load, see env__efi_fit_tftp_file
in the comment at the beginning of this file.
Return:
The address where the file has been loaded.
"""
addr = f.get('addr', None)
if not addr:
addr = u_boot_utils.find_ram_base(cons)
%s/u_boot_utils/util/
fn = f['fn']
output = cons.run_command('tftpboot %s %s' % (addr, fn))
You have to pass addr as hexadecimal number.
output = cons.run_command('tftpboot %x %s' % (addr, fn))
expected_text = 'Bytes transferred = '
sz = f.get('size', None)
if sz:
expected_text += '%d' % sz
assert expected_text in output
expected_crc = f.get('crc32', None)
if not expected_crc:
return addr
if cons.config.buildconfig.get('config_cmd_crc32', 'n') != 'y':
return addr
output = cons.run_command('crc32 $fileaddr $filesize')
assert expected_crc in output
return addr
- def launch_efi(enable_fdt, enable_comp):
"""Launch U-Boot's helloworld.efi binary from a FIT image.
An external image file can be downloaded from TFTP, when related
details are provided by the boardenv_* file; see the comment at the
beginning of this file.
If the size of the TFTP file is not provided within env__efi_fit_tftp_file,
the test image is generated automatically and placed in the TFTP root
directory specified via the 'dn' field.
When running the tests on Sandbox, the image file is loaded directly
from the host filesystem.
Once the load address is available on U-Boot console, the 'bootm'
command is executed for either 'config-efi-fdt' or 'config-efi-nofdt'
FIT configuration, depending on the value of the 'enable_fdt' function
argument.
Eventually the 'Hello, world' message is expected 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
generated content.
"""
with cons.log.section('FDT=%s;COMP=%s' % (enable_fdt, enable_comp)):
if is_sandbox:
fit = {
'dn': cons.config.build_dir,
'addr': '${kernel_addr_r}',
}
else:
# Init networking.
net_pre_commands()
net_set_up = net_dhcp()
net_set_up = net_setup_static() or net_set_up
if not net_set_up:
pytest.skip('Network not initialized')
fit = cons.config.env.get('env__efi_fit_tftp_file', None)
if not fit:
pytest.skip('No env__efi_fit_tftp_file binary specified in environment')
sz = fit.get('size', None)
if not sz:
if not fit.get('dn', None):
pytest.skip('Neither "size", nor "dn" info provided in env__efi_fit_tftp_file')
# Create test FIT image.
fit_path = make_fit(enable_comp)
fit['fn'] = os.path.basename(fit_path)
fit['size'] = os.path.getsize(fit_path)
# Copy image to TFTP root directory.
if fit['dn'] != cons.config.build_dir:
util.run_and_log(cons, ['mv', '-f', fit_path, '%s/' % fit['dn']])
# Load FIT image.
addr = load_fit_from_host(fit) if is_sandbox else load_fit_from_tftp(fit)
# Select boot configuration.
fit_config = 'config-efi-fdt' if enable_fdt else 'config-efi-nofdt'
# Try booting.
cons.run_command(
'bootm %s#%s' % (addr, fit_config), wait_for_prompt=False)
You have to pass the address as hexadecimal number.
'bootm %x#%s' % (addr, fit_config), wait_for_prompt=False)
Best regards
Heinrich
if enable_fdt:
cons.wait_for('Booting using the fdt blob')
cons.wait_for('Hello, world')
cons.wait_for('## Application terminated, r = 0')
cons.restart_uboot();
- cons = u_boot_console
- # Array slice removes leading/trailing quotes.
- sys_arch = cons.config.buildconfig.get('config_sys_arch', '"sandbox"')[1:-1]
- is_sandbox = sys_arch == 'sandbox'
- try:
if is_sandbox:
# Use our own device tree file, will be restored afterwards.
control_dtb = make_dtb('internal', False)
old_dtb = cons.config.dtb
cons.config.dtb = control_dtb
# Run tests
# - fdt OFF, gzip OFF
launch_efi(False, False)
# - fdt ON, gzip OFF
launch_efi(True, False)
if is_sandbox:
# - fdt OFF, gzip ON
launch_efi(False, True)
# - fdt ON, gzip ON
launch_efi(True, True)
- finally:
if is_sandbox:
# Go back to the original U-Boot with the correct dtb.
cons.config.dtb = old_dtb
cons.restart_uboot()

On 12/29/19 11:22 AM, Heinrich Schuchardt wrote:
On 12/24/19 5:05 PM, 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
Thanks a lot for devising this test.
You are using variable env__efi_fit_tftp_file. To run the test on Gitlab and Travis CI a patch will be needed for:
https://github.com/swarren/uboot-test-hooks.git
I hope
https://github.com/xypron/uboot-test-hooks/commit/20dcd721437dd5f7d7d3d235f7...
will do the job.
Once we have this applied we will have to adjust the config files for QEMU.
I have been trying to run the test on qemu_arm64_defconfig using the following lines in u_boot_boardenv_qemu_arm64.py:
env__efi_fit_tftp_file = { "fn": "helloworld.efi", "size": 4480, "crc32": "19f9c0ab", }
I got an error:
test/py/tests/test_efi_fit.py:417: in launch_efi addr = load_fit_from_host(fit) if is_sandbox else load_fit_from_tftp(fit) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ addr = f.get('addr', None) if not addr:
addr = u_boot_utils.find_ram_base(cons)
E NameError: name 'u_boot_utils' is not defined
When I provided addr:
env__efi_fit_tftp_file = { "fn": "helloworld.efi", "size": 4480, "crc32": "19f9c0ab", "addr": 0x40400000, }
I got the following error:
=> tftpboot 1073741824 helloworld.efi TFTP error: trying to overwrite reserved memory...
I would have expected a command
tftpboot 40400000 helloworld.efi
to be issued.
Same error with bootm:
=> bootm 1077936128#config-efi-nofdt "Synchronous Abort" handler, esr 0x96000010 elr: 000000000001c36c lr : 00000000000140f4 (reloc)
Please, fix the lines indicated below and verify that you can actually execute this test on the QEMU platform.
https://github.com/xypron/u-boot-build/tree/qemu-arm64/u-boot-test
contains the files I use to run Python tests on qemu_arm64_defconfig.
test/py/tests/test_efi_fit.py | 459 ++++++++++++++++++++++++++++++++++ 1 file changed, 459 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..e1f0e42694 --- /dev/null +++ b/test/py/tests/test_efi_fit.py @@ -0,0 +1,459 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2019, Cristian Ciocaltea cristian.ciocaltea@gmail.com +# +# Work based on: +# - test_net.py +# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. +# - test_fit.py +# Copyright (c) 2013, Google Inc. +# +# Test launching UEFI binaries from FIT images.
+import os.path +import pytest +import u_boot_utils as util
"as util" causes an error if you use u_boot_utils.* below. Below I indicate the places to change.
+""" +Note: This test relies on boardenv_* containing configuration values to define +which network environment is available for testing. Without this, the parts +that rely on network will be automatically skipped.
+For example:
+# Boolean indicating whether the Ethernet device is attached to USB, and hence +# USB enumeration needs to be performed prior to network tests. +# This variable may be omitted if its value is False. +env__net_uses_usb = False
+# Boolean indicating whether the Ethernet device is attached to PCI, and hence +# PCI enumeration needs to be performed prior to network tests. +# This variable may be omitted if its value is False. +env__net_uses_pci = True
+# True if a DHCP server is attached to the network, and should be tested. +# If DHCP testing is not possible or desired, this variable may be omitted or +# set to False. +env__net_dhcp_server = True
+# A list of environment variables that should be set in order to configure a +# static IP. If solely relying on DHCP, this variable may be omitted or set to +# an empty list. +env__net_static_env_vars = [ + ('ipaddr', '10.0.0.100'), + ('netmask', '255.255.255.0'), + ('serverip', '10.0.0.1'), +]
+# Details regarding a file that may be read from a TFTP server. This variable +# may be omitted or set to None if TFTP testing is not possible or desired. +# Additionally, when the 'size' is not available, the file will be generated +# automatically in the TFTP root directory, as specified by the 'dn' field. +env__efi_fit_tftp_file = { + 'fn': 'test-efi-fit.img', # File path relative to TFTP root + 'size': 3831, # File size + 'crc32': '9fa3f79c', # Checksum using CRC-32 algorithm, optional + 'addr': '$kernel_addr_r', # Loading address, optional
addr must be an integer not a string. Otherwise this does not match your function call
addr = util.find_ram_base(cons)
Best regards
Heinrich
+ 'dn': 'tftp/root/dir', # TFTP root directory path, optional +} +"""
+# Define the parametrized ITS data to be used for FIT images generation. +its_data = ''' +/dts-v1/;
+/ { + description = "EFI image with FDT blob"; + #address-cells = <1>;
+ images { + efi { + description = "Test EFI"; + data = /incbin/("%(efi-bin)s"); + type = "%(kernel-type)s"; + arch = "%(sys-arch)s"; + os = "efi"; + compression = "%(efi-comp)s"; + load = <0x0>; + entry = <0x0>; + }; + fdt { + description = "Test FDT"; + data = /incbin/("%(fdt-bin)s"); + type = "flat_dt"; + arch = "%(sys-arch)s"; + 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 to be used for DTB images generation. +fdt_data = ''' +/dts-v1/;
+/ { + #address-cells = <1>; + #size-cells = <0>;
+ model = "%(sys-arch)s %(fdt_type)s EFI FIT Boot Test"; + compatible = "%(sys-arch)s";
+ reset@0 { + compatible = "%(sys-arch)s,reset"; + reg = <0>; + }; +}; +'''
+@pytest.mark.buildconfigspec('bootm_efi') +@pytest.mark.buildconfigspec('cmd_bootefi_hello_compile') +@pytest.mark.buildconfigspec('fit') +@pytest.mark.notbuildconfigspec('generate_acpi_table') +@pytest.mark.requiredtool('dtc') +def test_efi_fit_launch(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.
+ For more details, see launch_efi().
+ The following test cases are currently defined and enabled: + - Launch uncompressed FIT EFI & internal FDT + - Launch uncompressed FIT EFI & FIT FDT + - Launch compressed FIT EFI & internal FDT + - Launch compressed FIT EFI & FIT FDT + """
+ def net_pre_commands(): + """Execute any commands required to enable network hardware.
+ These commands are provided by the boardenv_* file; see the comment + at the beginning of this file. + """
+ init_usb = cons.config.env.get('env__net_uses_usb', False) + if init_usb: + cons.run_command('usb start')
+ init_pci = cons.config.env.get('env__net_uses_pci', False) + if init_pci: + cons.run_command('pci enum')
+ def net_dhcp(): + """Execute the dhcp command.
+ The boardenv_* file may be used to enable/disable DHCP; see the + comment at the beginning of this file. + """
+ has_dhcp = cons.config.buildconfig.get('config_cmd_dhcp', 'n') == 'y' + if not has_dhcp: + cons.log.warning('CONFIG_CMD_DHCP != y: Skipping DHCP network setup') + return False
+ test_dhcp = cons.config.env.get('env__net_dhcp_server', False) + if not test_dhcp: + cons.log.info('No DHCP server available') + return False
+ cons.run_command('setenv autoload no') + output = cons.run_command('dhcp') + assert 'DHCP client bound to address ' in output + return True
+ def net_setup_static(): + """Set up a static IP configuration.
+ The configuration is provided by the boardenv_* file; see the comment at + the beginning of this file. + """
+ has_dhcp = cons.config.buildconfig.get('config_cmd_dhcp', 'n') == 'y' + if not has_dhcp: + cons.log.warning('CONFIG_NET != y: Skipping static network setup') + return False
+ env_vars = cons.config.env.get('env__net_static_env_vars', None) + if not env_vars: + cons.log.info('No static network configuration is defined') + return False
+ for (var, val) in env_vars: + cons.run_command('setenv %s %s' % (var, val)) + return True
+ 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(cons.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) + util.run_and_log(cons, + ['cp', make_fpath('lib/efi_loader/helloworld.efi'), bin_path]) + if comp: + util.run_and_log(cons, ['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. + Return: + The path of the created file. + """
+ # Generate resources referenced by FDT. + fdt_params = { + 'sys-arch': sys_arch, + 'fdt_type' : fdt_type, + }
+ # Generate a test FDT file. + dts = make_fpath('test-efi-fit-%s.dts' % fdt_type) + with open(dts, 'w') as fd: + fd.write(fdt_data % fdt_params)
+ # Build the test FDT. + dtb = make_fpath('test-efi-fit-%s.dtb' % fdt_type) + util.run_and_log(cons, ['dtc', '-I', 'dts', '-O', 'dtb', '-o', dtb, dts]) + if comp: + util.run_and_log(cons, ['gzip', '-f', dtb]) + dtb += '.gz' + return dtb
+ def make_fit(comp): + """Create a sample FIT image.
+ Runs 'mkimage' to create a FIT image within U-Boot build dir. + Args: + comp: Enable gzip compression for the EFI binary and FDT blob. + Return: + The path of the created file. + """
+ # Generate resources referenced by ITS. + its_params = { + 'sys-arch': sys_arch, + 'efi-bin': os.path.basename(make_efi('test-efi-fit-helloworld.efi', comp)), + 'kernel-type': 'kernel' if comp else 'kernel_noload', + 'efi-comp': 'gzip' if comp else 'none', + 'fdt-bin': os.path.basename(make_dtb('user', comp)), + 'fdt-comp': 'gzip' if comp else 'none', + }
+ # Generate a test ITS file. + its_path = make_fpath('test-efi-fit-helloworld.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-helloworld.fit') + util.run_and_log( + cons, [make_fpath('tools/mkimage'), '-f', its_path, fit_path]) + return fit_path
+ def load_fit_from_host(f): + """Load the FIT image using the 'host load' command and return its address.
+ Args: + f: Dictionary describing the FIT image to load, see env__efi_fit_test_file + in the comment at the beginning of this file. + Return: + The address where the file has been loaded. + """
+ addr = f.get('addr', None) + if not addr: + addr = u_boot_utils.find_ram_base(cons)
%s/u_boot_utils/util/
+ output = cons.run_command( + 'host load hostfs - %s %s/%s' % (addr, f['dn'], f['fn'])) + expected_text = ' bytes read' + sz = f.get('size', None) + if sz: + expected_text = '%d' % sz + expected_text + assert(expected_text in output)
+ return addr
+ def load_fit_from_tftp(f): + """Load the FIT image using the tftpboot command and return its address.
+ The file is downloaded from the TFTP server, its size and optionally its + CRC32 are validated.
+ Args: + f: Dictionary describing the FIT image to load, see env__efi_fit_tftp_file + in the comment at the beginning of this file. + Return: + The address where the file has been loaded. + """
+ addr = f.get('addr', None) + if not addr: + addr = u_boot_utils.find_ram_base(cons)
%s/u_boot_utils/util/
+ fn = f['fn'] + output = cons.run_command('tftpboot %s %s' % (addr, fn))
You have to pass addr as hexadecimal number.
output = cons.run_command('tftpboot %x %s' % (addr, fn))
+ expected_text = 'Bytes transferred = ' + sz = f.get('size', None) + if sz: + expected_text += '%d' % sz + assert expected_text in output
+ expected_crc = f.get('crc32', None) + if not expected_crc: + return addr
+ if cons.config.buildconfig.get('config_cmd_crc32', 'n') != 'y': + return addr
+ output = cons.run_command('crc32 $fileaddr $filesize') + assert expected_crc in output
+ return addr
+ def launch_efi(enable_fdt, enable_comp): + """Launch U-Boot's helloworld.efi binary from a FIT image.
+ An external image file can be downloaded from TFTP, when related + details are provided by the boardenv_* file; see the comment at the + beginning of this file.
+ If the size of the TFTP file is not provided within env__efi_fit_tftp_file, + the test image is generated automatically and placed in the TFTP root + directory specified via the 'dn' field.
+ When running the tests on Sandbox, the image file is loaded directly + from the host filesystem.
+ Once the load address is available on U-Boot console, the 'bootm' + command is executed for either 'config-efi-fdt' or 'config-efi-nofdt' + FIT configuration, depending on the value of the 'enable_fdt' function + argument.
+ Eventually the 'Hello, world' message is expected 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 + generated content. + """
+ with cons.log.section('FDT=%s;COMP=%s' % (enable_fdt, enable_comp)): + if is_sandbox: + fit = { + 'dn': cons.config.build_dir, + 'addr': '${kernel_addr_r}', + } + else: + # Init networking. + net_pre_commands() + net_set_up = net_dhcp() + net_set_up = net_setup_static() or net_set_up + if not net_set_up: + pytest.skip('Network not initialized')
+ fit = cons.config.env.get('env__efi_fit_tftp_file', None) + if not fit: + pytest.skip('No env__efi_fit_tftp_file binary specified in environment')
+ sz = fit.get('size', None) + if not sz: + if not fit.get('dn', None): + pytest.skip('Neither "size", nor "dn" info provided in env__efi_fit_tftp_file')
+ # Create test FIT image. + fit_path = make_fit(enable_comp) + fit['fn'] = os.path.basename(fit_path) + fit['size'] = os.path.getsize(fit_path)
+ # Copy image to TFTP root directory. + if fit['dn'] != cons.config.build_dir: + util.run_and_log(cons, ['mv', '-f', fit_path, '%s/' % fit['dn']])
+ # Load FIT image. + addr = load_fit_from_host(fit) if is_sandbox else load_fit_from_tftp(fit)
+ # Select boot configuration. + fit_config = 'config-efi-fdt' if enable_fdt else 'config-efi-nofdt'
+ # Try booting. + cons.run_command( + 'bootm %s#%s' % (addr, fit_config), wait_for_prompt=False)
You have to pass the address as hexadecimal number.
'bootm %x#%s' % (addr, fit_config), wait_for_prompt=False)
Best regards
Heinrich
+ if enable_fdt: + cons.wait_for('Booting using the fdt blob') + cons.wait_for('Hello, world') + cons.wait_for('## Application terminated, r = 0') + cons.restart_uboot();
+ cons = u_boot_console + # Array slice removes leading/trailing quotes. + sys_arch = cons.config.buildconfig.get('config_sys_arch', '"sandbox"')[1:-1] + is_sandbox = sys_arch == 'sandbox'
+ try: + if is_sandbox: + # Use our own device tree file, will be restored afterwards. + control_dtb = make_dtb('internal', False) + old_dtb = cons.config.dtb + cons.config.dtb = control_dtb
+ # Run tests + # - fdt OFF, gzip OFF + launch_efi(False, False) + # - fdt ON, gzip OFF + launch_efi(True, False)
+ if is_sandbox: + # - fdt OFF, gzip ON + launch_efi(False, True) + # - fdt ON, gzip ON + launch_efi(True, True)
+ finally: + if is_sandbox: + # Go back to the original U-Boot with the correct dtb. + cons.config.dtb = old_dtb + cons.restart_uboot()

On Sun, Dec 29, 2019 at 11:22:08AM +0100, Heinrich Schuchardt wrote:
On 12/24/19 5:05 PM, 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
Thanks a lot for devising this test.
You are using variable env__efi_fit_tftp_file. To run the test on Gitlab and Travis CI a patch will be needed for:
https://github.com/swarren/uboot-test-hooks.git
I hope
https://github.com/xypron/uboot-test-hooks/commit/20dcd721437dd5f7d7d3d235f7...
will do the job.
Once we have this applied we will have to adjust the config files for QEMU.
I have been trying to run the test on qemu_arm64_defconfig using the following lines in u_boot_boardenv_qemu_arm64.py:
env__efi_fit_tftp_file = { "fn": "helloworld.efi", "size": 4480, "crc32": "19f9c0ab", }
I got an error:
test/py/tests/test_efi_fit.py:417: in launch_efi addr = load_fit_from_host(fit) if is_sandbox else load_fit_from_tftp(fit) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ addr = f.get('addr', None) if not addr:
addr = u_boot_utils.find_ram_base(cons)
E NameError: name 'u_boot_utils' is not defined
When I provided addr:
env__efi_fit_tftp_file = { "fn": "helloworld.efi", "size": 4480, "crc32": "19f9c0ab", "addr": 0x40400000, }
I got the following error:
=> tftpboot 1073741824 helloworld.efi TFTP error: trying to overwrite reserved memory...
I would have expected a command
tftpboot 40400000 helloworld.efi
to be issued.
Same error with bootm:
=> bootm 1077936128#config-efi-nofdt "Synchronous Abort" handler, esr 0x96000010 elr: 000000000001c36c lr : 00000000000140f4 (reloc)
Please, fix the lines indicated below and verify that you can actually execute this test on the QEMU platform.
Thank you for the detailed report!
Unfortunately I have only tested on qemu_arm and somehow I missed the check of having the address computed by the test suite. It used to work before I changed the address data type to string - the reason was to allow for more flexibility, e.g. providing values like '$kernel_addr_r' instead of just precomputed numbers.
I'm going to extend the tests on qemu_arm64 as well.
https://github.com/xypron/u-boot-build/tree/qemu-arm64/u-boot-test
contains the files I use to run Python tests on qemu_arm64_defconfig.
test/py/tests/test_efi_fit.py | 459 ++++++++++++++++++++++++++++++++++ 1 file changed, 459 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..e1f0e42694 --- /dev/null +++ b/test/py/tests/test_efi_fit.py @@ -0,0 +1,459 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2019, Cristian Ciocaltea cristian.ciocaltea@gmail.com +# +# Work based on: +# - test_net.py +# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. +# - test_fit.py +# Copyright (c) 2013, Google Inc. +# +# Test launching UEFI binaries from FIT images.
+import os.path +import pytest +import u_boot_utils as util
"as util" causes an error if you use u_boot_utils.* below. Below I indicate the places to change.
Fixed, thanks.
+""" +Note: This test relies on boardenv_* containing configuration values to define +which network environment is available for testing. Without this, the parts +that rely on network will be automatically skipped.
+For example:
+# Boolean indicating whether the Ethernet device is attached to USB, and hence +# USB enumeration needs to be performed prior to network tests. +# This variable may be omitted if its value is False. +env__net_uses_usb = False
+# Boolean indicating whether the Ethernet device is attached to PCI, and hence +# PCI enumeration needs to be performed prior to network tests. +# This variable may be omitted if its value is False. +env__net_uses_pci = True
+# True if a DHCP server is attached to the network, and should be tested. +# If DHCP testing is not possible or desired, this variable may be omitted or +# set to False. +env__net_dhcp_server = True
+# A list of environment variables that should be set in order to configure a +# static IP. If solely relying on DHCP, this variable may be omitted or set to +# an empty list. +env__net_static_env_vars = [
- ('ipaddr', '10.0.0.100'),
- ('netmask', '255.255.255.0'),
- ('serverip', '10.0.0.1'),
+]
+# Details regarding a file that may be read from a TFTP server. This variable +# may be omitted or set to None if TFTP testing is not possible or desired. +# Additionally, when the 'size' is not available, the file will be generated +# automatically in the TFTP root directory, as specified by the 'dn' field. +env__efi_fit_tftp_file = {
- 'fn': 'test-efi-fit.img', # File path relative to TFTP root
- 'size': 3831, # File size
- 'crc32': '9fa3f79c', # Checksum using CRC-32 algorithm, optional
- 'addr': '$kernel_addr_r', # Loading address, optional
- 'dn': 'tftp/root/dir', # TFTP root directory path, optional
+} +"""
+# Define the parametrized ITS data to be used for FIT images generation. +its_data = ''' +/dts-v1/;
+/ {
- description = "EFI image with FDT blob";
- #address-cells = <1>;
- images {
efi {
description = "Test EFI";
data = /incbin/("%(efi-bin)s");
type = "%(kernel-type)s";
arch = "%(sys-arch)s";
os = "efi";
compression = "%(efi-comp)s";
load = <0x0>;
entry = <0x0>;
};
fdt {
description = "Test FDT";
data = /incbin/("%(fdt-bin)s");
type = "flat_dt";
arch = "%(sys-arch)s";
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 to be used for DTB images generation. +fdt_data = ''' +/dts-v1/;
+/ {
- #address-cells = <1>;
- #size-cells = <0>;
- model = "%(sys-arch)s %(fdt_type)s EFI FIT Boot Test";
- compatible = "%(sys-arch)s";
- reset@0 {
compatible = "%(sys-arch)s,reset";
reg = <0>;
- };
+}; +'''
+@pytest.mark.buildconfigspec('bootm_efi') +@pytest.mark.buildconfigspec('cmd_bootefi_hello_compile') +@pytest.mark.buildconfigspec('fit') +@pytest.mark.notbuildconfigspec('generate_acpi_table') +@pytest.mark.requiredtool('dtc') +def test_efi_fit_launch(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.
- For more details, see launch_efi().
- The following test cases are currently defined and enabled:
- Launch uncompressed FIT EFI & internal FDT
- Launch uncompressed FIT EFI & FIT FDT
- Launch compressed FIT EFI & internal FDT
- Launch compressed FIT EFI & FIT FDT
- """
- def net_pre_commands():
"""Execute any commands required to enable network hardware.
These commands are provided by the boardenv_* file; see the comment
at the beginning of this file.
"""
init_usb = cons.config.env.get('env__net_uses_usb', False)
if init_usb:
cons.run_command('usb start')
init_pci = cons.config.env.get('env__net_uses_pci', False)
if init_pci:
cons.run_command('pci enum')
- def net_dhcp():
"""Execute the dhcp command.
The boardenv_* file may be used to enable/disable DHCP; see the
comment at the beginning of this file.
"""
has_dhcp = cons.config.buildconfig.get('config_cmd_dhcp', 'n') == 'y'
if not has_dhcp:
cons.log.warning('CONFIG_CMD_DHCP != y: Skipping DHCP network setup')
return False
test_dhcp = cons.config.env.get('env__net_dhcp_server', False)
if not test_dhcp:
cons.log.info('No DHCP server available')
return False
cons.run_command('setenv autoload no')
output = cons.run_command('dhcp')
assert 'DHCP client bound to address ' in output
return True
- def net_setup_static():
"""Set up a static IP configuration.
The configuration is provided by the boardenv_* file; see the comment at
the beginning of this file.
"""
has_dhcp = cons.config.buildconfig.get('config_cmd_dhcp', 'n') == 'y'
if not has_dhcp:
cons.log.warning('CONFIG_NET != y: Skipping static network setup')
return False
env_vars = cons.config.env.get('env__net_static_env_vars', None)
if not env_vars:
cons.log.info('No static network configuration is defined')
return False
for (var, val) in env_vars:
cons.run_command('setenv %s %s' % (var, val))
return True
- 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(cons.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)
util.run_and_log(cons,
['cp', make_fpath('lib/efi_loader/helloworld.efi'), bin_path])
if comp:
util.run_and_log(cons, ['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.
Return:
The path of the created file.
"""
# Generate resources referenced by FDT.
fdt_params = {
'sys-arch': sys_arch,
'fdt_type' : fdt_type,
}
# Generate a test FDT file.
dts = make_fpath('test-efi-fit-%s.dts' % fdt_type)
with open(dts, 'w') as fd:
fd.write(fdt_data % fdt_params)
# Build the test FDT.
dtb = make_fpath('test-efi-fit-%s.dtb' % fdt_type)
util.run_and_log(cons, ['dtc', '-I', 'dts', '-O', 'dtb', '-o', dtb, dts])
if comp:
util.run_and_log(cons, ['gzip', '-f', dtb])
dtb += '.gz'
return dtb
- def make_fit(comp):
"""Create a sample FIT image.
Runs 'mkimage' to create a FIT image within U-Boot build dir.
Args:
comp: Enable gzip compression for the EFI binary and FDT blob.
Return:
The path of the created file.
"""
# Generate resources referenced by ITS.
its_params = {
'sys-arch': sys_arch,
'efi-bin': os.path.basename(make_efi('test-efi-fit-helloworld.efi', comp)),
'kernel-type': 'kernel' if comp else 'kernel_noload',
'efi-comp': 'gzip' if comp else 'none',
'fdt-bin': os.path.basename(make_dtb('user', comp)),
'fdt-comp': 'gzip' if comp else 'none',
}
# Generate a test ITS file.
its_path = make_fpath('test-efi-fit-helloworld.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-helloworld.fit')
util.run_and_log(
cons, [make_fpath('tools/mkimage'), '-f', its_path, fit_path])
return fit_path
- def load_fit_from_host(f):
"""Load the FIT image using the 'host load' command and return its address.
Args:
f: Dictionary describing the FIT image to load, see env__efi_fit_test_file
in the comment at the beginning of this file.
Return:
The address where the file has been loaded.
"""
addr = f.get('addr', None)
if not addr:
addr = u_boot_utils.find_ram_base(cons)
%s/u_boot_utils/util/
output = cons.run_command(
'host load hostfs - %s %s/%s' % (addr, f['dn'], f['fn']))
expected_text = ' bytes read'
sz = f.get('size', None)
if sz:
expected_text = '%d' % sz + expected_text
assert(expected_text in output)
return addr
- def load_fit_from_tftp(f):
"""Load the FIT image using the tftpboot command and return its address.
The file is downloaded from the TFTP server, its size and optionally its
CRC32 are validated.
Args:
f: Dictionary describing the FIT image to load, see env__efi_fit_tftp_file
in the comment at the beginning of this file.
Return:
The address where the file has been loaded.
"""
addr = f.get('addr', None)
if not addr:
addr = u_boot_utils.find_ram_base(cons)
%s/u_boot_utils/util/
fn = f['fn']
output = cons.run_command('tftpboot %s %s' % (addr, fn))
You have to pass addr as hexadecimal number.
output = cons.run_command('tftpboot %x %s' % (addr, fn))
As explained before, I eventually converted 'addr' to string in order to allow values like '$kernel_addr_r' to be passed from the environment (this is what I actually used during my local tests). What I missed was the following:
addr = '%x' % util.find_ram_base(cons)
If this is not desired, I can revert to numbers only.
expected_text = 'Bytes transferred = '
sz = f.get('size', None)
if sz:
expected_text += '%d' % sz
assert expected_text in output
expected_crc = f.get('crc32', None)
if not expected_crc:
return addr
if cons.config.buildconfig.get('config_cmd_crc32', 'n') != 'y':
return addr
output = cons.run_command('crc32 $fileaddr $filesize')
assert expected_crc in output
return addr
- def launch_efi(enable_fdt, enable_comp):
"""Launch U-Boot's helloworld.efi binary from a FIT image.
An external image file can be downloaded from TFTP, when related
details are provided by the boardenv_* file; see the comment at the
beginning of this file.
If the size of the TFTP file is not provided within env__efi_fit_tftp_file,
the test image is generated automatically and placed in the TFTP root
directory specified via the 'dn' field.
When running the tests on Sandbox, the image file is loaded directly
from the host filesystem.
Once the load address is available on U-Boot console, the 'bootm'
command is executed for either 'config-efi-fdt' or 'config-efi-nofdt'
FIT configuration, depending on the value of the 'enable_fdt' function
argument.
Eventually the 'Hello, world' message is expected 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
generated content.
"""
with cons.log.section('FDT=%s;COMP=%s' % (enable_fdt, enable_comp)):
if is_sandbox:
fit = {
'dn': cons.config.build_dir,
'addr': '${kernel_addr_r}',
}
else:
# Init networking.
net_pre_commands()
net_set_up = net_dhcp()
net_set_up = net_setup_static() or net_set_up
if not net_set_up:
pytest.skip('Network not initialized')
fit = cons.config.env.get('env__efi_fit_tftp_file', None)
if not fit:
pytest.skip('No env__efi_fit_tftp_file binary specified in environment')
sz = fit.get('size', None)
if not sz:
if not fit.get('dn', None):
pytest.skip('Neither "size", nor "dn" info provided in env__efi_fit_tftp_file')
# Create test FIT image.
fit_path = make_fit(enable_comp)
fit['fn'] = os.path.basename(fit_path)
fit['size'] = os.path.getsize(fit_path)
# Copy image to TFTP root directory.
if fit['dn'] != cons.config.build_dir:
util.run_and_log(cons, ['mv', '-f', fit_path, '%s/' % fit['dn']])
# Load FIT image.
addr = load_fit_from_host(fit) if is_sandbox else load_fit_from_tftp(fit)
# Select boot configuration.
fit_config = 'config-efi-fdt' if enable_fdt else 'config-efi-nofdt'
# Try booting.
cons.run_command(
'bootm %s#%s' % (addr, fit_config), wait_for_prompt=False)
You have to pass the address as hexadecimal number.
'bootm %x#%s' % (addr, fit_config), wait_for_prompt=False)
Please see comments above.
Best regards
Heinrich
if enable_fdt:
cons.wait_for('Booting using the fdt blob')
cons.wait_for('Hello, world')
cons.wait_for('## Application terminated, r = 0')
cons.restart_uboot();
- cons = u_boot_console
- # Array slice removes leading/trailing quotes.
- sys_arch = cons.config.buildconfig.get('config_sys_arch', '"sandbox"')[1:-1]
- is_sandbox = sys_arch == 'sandbox'
- try:
if is_sandbox:
# Use our own device tree file, will be restored afterwards.
control_dtb = make_dtb('internal', False)
old_dtb = cons.config.dtb
cons.config.dtb = control_dtb
# Run tests
# - fdt OFF, gzip OFF
launch_efi(False, False)
# - fdt ON, gzip OFF
launch_efi(True, False)
if is_sandbox:
# - fdt OFF, gzip ON
launch_efi(False, True)
# - fdt ON, gzip ON
launch_efi(True, True)
- finally:
if is_sandbox:
# Go back to the original U-Boot with the correct dtb.
cons.config.dtb = old_dtb
cons.restart_uboot()

On 12/29/19 7:39 PM, Cristian Ciocaltea wrote:
On Sun, Dec 29, 2019 at 11:22:08AM +0100, Heinrich Schuchardt wrote:
On 12/24/19 5:05 PM, 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
Thanks a lot for devising this test.
You are using variable env__efi_fit_tftp_file. To run the test on Gitlab and Travis CI a patch will be needed for:
https://github.com/swarren/uboot-test-hooks.git
I hope
https://github.com/xypron/uboot-test-hooks/commit/20dcd721437dd5f7d7d3d235f7...
will do the job.
Once we have this applied we will have to adjust the config files for QEMU.
I have been trying to run the test on qemu_arm64_defconfig using the following lines in u_boot_boardenv_qemu_arm64.py:
env__efi_fit_tftp_file = { "fn": "helloworld.efi", "size": 4480, "crc32": "19f9c0ab", }
I got an error:
test/py/tests/test_efi_fit.py:417: in launch_efi addr = load_fit_from_host(fit) if is_sandbox else load_fit_from_tftp(fit) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ addr = f.get('addr', None) if not addr:
addr = u_boot_utils.find_ram_base(cons)
E NameError: name 'u_boot_utils' is not defined
When I provided addr:
env__efi_fit_tftp_file = { "fn": "helloworld.efi", "size": 4480, "crc32": "19f9c0ab", "addr": 0x40400000, }
I got the following error:
=> tftpboot 1073741824 helloworld.efi TFTP error: trying to overwrite reserved memory...
I would have expected a command
tftpboot 40400000 helloworld.efi
to be issued.
Same error with bootm:
=> bootm 1077936128#config-efi-nofdt "Synchronous Abort" handler, esr 0x96000010 elr: 000000000001c36c lr : 00000000000140f4 (reloc)
Please, fix the lines indicated below and verify that you can actually execute this test on the QEMU platform.
Thank you for the detailed report!
Unfortunately I have only tested on qemu_arm and somehow I missed the check of having the address computed by the test suite. It used to work before I changed the address data type to string - the reason was to allow for more flexibility, e.g. providing values like '$kernel_addr_r' instead of just precomputed numbers.
I'm going to extend the tests on qemu_arm64 as well.
https://github.com/xypron/u-boot-build/tree/qemu-arm64/u-boot-test
contains the files I use to run Python tests on qemu_arm64_defconfig.
test/py/tests/test_efi_fit.py | 459 ++++++++++++++++++++++++++++++++++ 1 file changed, 459 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..e1f0e42694 --- /dev/null +++ b/test/py/tests/test_efi_fit.py @@ -0,0 +1,459 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2019, Cristian Ciocaltea cristian.ciocaltea@gmail.com +# +# Work based on: +# - test_net.py +# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. +# - test_fit.py +# Copyright (c) 2013, Google Inc. +# +# Test launching UEFI binaries from FIT images.
+import os.path +import pytest +import u_boot_utils as util
"as util" causes an error if you use u_boot_utils.* below. Below I indicate the places to change.
Fixed, thanks.
+""" +Note: This test relies on boardenv_* containing configuration values to define +which network environment is available for testing. Without this, the parts +that rely on network will be automatically skipped.
+For example:
+# Boolean indicating whether the Ethernet device is attached to USB, and hence +# USB enumeration needs to be performed prior to network tests. +# This variable may be omitted if its value is False. +env__net_uses_usb = False
+# Boolean indicating whether the Ethernet device is attached to PCI, and hence +# PCI enumeration needs to be performed prior to network tests. +# This variable may be omitted if its value is False. +env__net_uses_pci = True
+# True if a DHCP server is attached to the network, and should be tested. +# If DHCP testing is not possible or desired, this variable may be omitted or +# set to False. +env__net_dhcp_server = True
+# A list of environment variables that should be set in order to configure a +# static IP. If solely relying on DHCP, this variable may be omitted or set to +# an empty list. +env__net_static_env_vars = [
- ('ipaddr', '10.0.0.100'),
- ('netmask', '255.255.255.0'),
- ('serverip', '10.0.0.1'),
+]
+# Details regarding a file that may be read from a TFTP server. This variable +# may be omitted or set to None if TFTP testing is not possible or desired. +# Additionally, when the 'size' is not available, the file will be generated +# automatically in the TFTP root directory, as specified by the 'dn' field. +env__efi_fit_tftp_file = {
- 'fn': 'test-efi-fit.img', # File path relative to TFTP root
- 'size': 3831, # File size
- 'crc32': '9fa3f79c', # Checksum using CRC-32 algorithm, optional
- 'addr': '$kernel_addr_r', # Loading address, optional
- 'dn': 'tftp/root/dir', # TFTP root directory path, optional
+} +"""
+# Define the parametrized ITS data to be used for FIT images generation. +its_data = ''' +/dts-v1/;
+/ {
- description = "EFI image with FDT blob";
- #address-cells = <1>;
- images {
efi {
description = "Test EFI";
data = /incbin/("%(efi-bin)s");
type = "%(kernel-type)s";
arch = "%(sys-arch)s";
os = "efi";
compression = "%(efi-comp)s";
load = <0x0>;
entry = <0x0>;
};
fdt {
description = "Test FDT";
data = /incbin/("%(fdt-bin)s");
type = "flat_dt";
arch = "%(sys-arch)s";
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 to be used for DTB images generation. +fdt_data = ''' +/dts-v1/;
+/ {
- #address-cells = <1>;
- #size-cells = <0>;
- model = "%(sys-arch)s %(fdt_type)s EFI FIT Boot Test";
- compatible = "%(sys-arch)s";
- reset@0 {
compatible = "%(sys-arch)s,reset";
reg = <0>;
- };
+}; +'''
+@pytest.mark.buildconfigspec('bootm_efi') +@pytest.mark.buildconfigspec('cmd_bootefi_hello_compile') +@pytest.mark.buildconfigspec('fit') +@pytest.mark.notbuildconfigspec('generate_acpi_table') +@pytest.mark.requiredtool('dtc') +def test_efi_fit_launch(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.
- For more details, see launch_efi().
- The following test cases are currently defined and enabled:
- Launch uncompressed FIT EFI & internal FDT
- Launch uncompressed FIT EFI & FIT FDT
- Launch compressed FIT EFI & internal FDT
- Launch compressed FIT EFI & FIT FDT
- """
- def net_pre_commands():
"""Execute any commands required to enable network hardware.
These commands are provided by the boardenv_* file; see the comment
at the beginning of this file.
"""
init_usb = cons.config.env.get('env__net_uses_usb', False)
if init_usb:
cons.run_command('usb start')
init_pci = cons.config.env.get('env__net_uses_pci', False)
if init_pci:
cons.run_command('pci enum')
- def net_dhcp():
"""Execute the dhcp command.
The boardenv_* file may be used to enable/disable DHCP; see the
comment at the beginning of this file.
"""
has_dhcp = cons.config.buildconfig.get('config_cmd_dhcp', 'n') == 'y'
if not has_dhcp:
cons.log.warning('CONFIG_CMD_DHCP != y: Skipping DHCP network setup')
return False
test_dhcp = cons.config.env.get('env__net_dhcp_server', False)
if not test_dhcp:
cons.log.info('No DHCP server available')
return False
cons.run_command('setenv autoload no')
output = cons.run_command('dhcp')
assert 'DHCP client bound to address ' in output
return True
- def net_setup_static():
"""Set up a static IP configuration.
The configuration is provided by the boardenv_* file; see the comment at
the beginning of this file.
"""
has_dhcp = cons.config.buildconfig.get('config_cmd_dhcp', 'n') == 'y'
if not has_dhcp:
cons.log.warning('CONFIG_NET != y: Skipping static network setup')
return False
env_vars = cons.config.env.get('env__net_static_env_vars', None)
if not env_vars:
cons.log.info('No static network configuration is defined')
return False
for (var, val) in env_vars:
cons.run_command('setenv %s %s' % (var, val))
return True
- 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(cons.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)
util.run_and_log(cons,
['cp', make_fpath('lib/efi_loader/helloworld.efi'), bin_path])
if comp:
util.run_and_log(cons, ['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.
Return:
The path of the created file.
"""
# Generate resources referenced by FDT.
fdt_params = {
'sys-arch': sys_arch,
'fdt_type' : fdt_type,
}
# Generate a test FDT file.
dts = make_fpath('test-efi-fit-%s.dts' % fdt_type)
with open(dts, 'w') as fd:
fd.write(fdt_data % fdt_params)
# Build the test FDT.
dtb = make_fpath('test-efi-fit-%s.dtb' % fdt_type)
util.run_and_log(cons, ['dtc', '-I', 'dts', '-O', 'dtb', '-o', dtb, dts])
if comp:
util.run_and_log(cons, ['gzip', '-f', dtb])
dtb += '.gz'
return dtb
- def make_fit(comp):
"""Create a sample FIT image.
Runs 'mkimage' to create a FIT image within U-Boot build dir.
Args:
comp: Enable gzip compression for the EFI binary and FDT blob.
Return:
The path of the created file.
"""
# Generate resources referenced by ITS.
its_params = {
'sys-arch': sys_arch,
'efi-bin': os.path.basename(make_efi('test-efi-fit-helloworld.efi', comp)),
'kernel-type': 'kernel' if comp else 'kernel_noload',
'efi-comp': 'gzip' if comp else 'none',
'fdt-bin': os.path.basename(make_dtb('user', comp)),
'fdt-comp': 'gzip' if comp else 'none',
}
# Generate a test ITS file.
its_path = make_fpath('test-efi-fit-helloworld.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-helloworld.fit')
util.run_and_log(
cons, [make_fpath('tools/mkimage'), '-f', its_path, fit_path])
return fit_path
- def load_fit_from_host(f):
"""Load the FIT image using the 'host load' command and return its address.
Args:
f: Dictionary describing the FIT image to load, see env__efi_fit_test_file
in the comment at the beginning of this file.
Return:
The address where the file has been loaded.
"""
addr = f.get('addr', None)
if not addr:
addr = u_boot_utils.find_ram_base(cons)
%s/u_boot_utils/util/
output = cons.run_command(
'host load hostfs - %s %s/%s' % (addr, f['dn'], f['fn']))
expected_text = ' bytes read'
sz = f.get('size', None)
if sz:
expected_text = '%d' % sz + expected_text
assert(expected_text in output)
return addr
- def load_fit_from_tftp(f):
"""Load the FIT image using the tftpboot command and return its address.
The file is downloaded from the TFTP server, its size and optionally its
CRC32 are validated.
Args:
f: Dictionary describing the FIT image to load, see env__efi_fit_tftp_file
in the comment at the beginning of this file.
Return:
The address where the file has been loaded.
"""
addr = f.get('addr', None)
if not addr:
addr = u_boot_utils.find_ram_base(cons)
%s/u_boot_utils/util/
fn = f['fn']
output = cons.run_command('tftpboot %s %s' % (addr, fn))
You have to pass addr as hexadecimal number.
output = cons.run_command('tftpboot %x %s' % (addr, fn))
As explained before, I eventually converted 'addr' to string in order to allow values like '$kernel_addr_r' to be passed from the environment (this is what I actually used during my local tests). What I missed was the following:
addr = '%x' % util.find_ram_base(cons)
If this is not desired, I can revert to numbers only.
I suggest to use the following instead of referring to $kernel_addr_r:
with cons.log.section('FDT=%s;COMP=%s' % (enable_fdt, enable_comp)): if is_sandbox: addr = util.find_ram_base(cons) fit = { 'dn': cons.config.build_dir, 'addr': addr, } else:
Best regards
Heinrich
expected_text = 'Bytes transferred = '
sz = f.get('size', None)
if sz:
expected_text += '%d' % sz
assert expected_text in output
expected_crc = f.get('crc32', None)
if not expected_crc:
return addr
if cons.config.buildconfig.get('config_cmd_crc32', 'n') != 'y':
return addr
output = cons.run_command('crc32 $fileaddr $filesize')
assert expected_crc in output
return addr
- def launch_efi(enable_fdt, enable_comp):
"""Launch U-Boot's helloworld.efi binary from a FIT image.
An external image file can be downloaded from TFTP, when related
details are provided by the boardenv_* file; see the comment at the
beginning of this file.
If the size of the TFTP file is not provided within env__efi_fit_tftp_file,
the test image is generated automatically and placed in the TFTP root
directory specified via the 'dn' field.
When running the tests on Sandbox, the image file is loaded directly
from the host filesystem.
Once the load address is available on U-Boot console, the 'bootm'
command is executed for either 'config-efi-fdt' or 'config-efi-nofdt'
FIT configuration, depending on the value of the 'enable_fdt' function
argument.
Eventually the 'Hello, world' message is expected 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
generated content.
"""
with cons.log.section('FDT=%s;COMP=%s' % (enable_fdt, enable_comp)):
if is_sandbox:
fit = {
'dn': cons.config.build_dir,
'addr': '${kernel_addr_r}',
}
else:
# Init networking.
net_pre_commands()
net_set_up = net_dhcp()
net_set_up = net_setup_static() or net_set_up
if not net_set_up:
pytest.skip('Network not initialized')
fit = cons.config.env.get('env__efi_fit_tftp_file', None)
if not fit:
pytest.skip('No env__efi_fit_tftp_file binary specified in environment')
sz = fit.get('size', None)
if not sz:
if not fit.get('dn', None):
pytest.skip('Neither "size", nor "dn" info provided in env__efi_fit_tftp_file')
# Create test FIT image.
fit_path = make_fit(enable_comp)
fit['fn'] = os.path.basename(fit_path)
fit['size'] = os.path.getsize(fit_path)
# Copy image to TFTP root directory.
if fit['dn'] != cons.config.build_dir:
util.run_and_log(cons, ['mv', '-f', fit_path, '%s/' % fit['dn']])
# Load FIT image.
addr = load_fit_from_host(fit) if is_sandbox else load_fit_from_tftp(fit)
# Select boot configuration.
fit_config = 'config-efi-fdt' if enable_fdt else 'config-efi-nofdt'
# Try booting.
cons.run_command(
'bootm %s#%s' % (addr, fit_config), wait_for_prompt=False)
You have to pass the address as hexadecimal number.
'bootm %x#%s' % (addr, fit_config), wait_for_prompt=False)
Please see comments above.
Best regards
Heinrich
if enable_fdt:
cons.wait_for('Booting using the fdt blob')
cons.wait_for('Hello, world')
cons.wait_for('## Application terminated, r = 0')
cons.restart_uboot();
- cons = u_boot_console
- # Array slice removes leading/trailing quotes.
- sys_arch = cons.config.buildconfig.get('config_sys_arch', '"sandbox"')[1:-1]
- is_sandbox = sys_arch == 'sandbox'
- try:
if is_sandbox:
# Use our own device tree file, will be restored afterwards.
control_dtb = make_dtb('internal', False)
old_dtb = cons.config.dtb
cons.config.dtb = control_dtb
# Run tests
# - fdt OFF, gzip OFF
launch_efi(False, False)
# - fdt ON, gzip OFF
launch_efi(True, False)
if is_sandbox:
# - fdt OFF, gzip ON
launch_efi(False, True)
# - fdt ON, gzip ON
launch_efi(True, True)
- finally:
if is_sandbox:
# Go back to the original U-Boot with the correct dtb.
cons.config.dtb = old_dtb
cons.restart_uboot()

On Sun, Dec 29, 2019 at 08:11:10PM +0100, Heinrich Schuchardt wrote:
On 12/29/19 7:39 PM, Cristian Ciocaltea wrote:
On Sun, Dec 29, 2019 at 11:22:08AM +0100, Heinrich Schuchardt wrote:
On 12/24/19 5:05 PM, 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
Thanks a lot for devising this test.
You are using variable env__efi_fit_tftp_file. To run the test on Gitlab and Travis CI a patch will be needed for:
https://github.com/swarren/uboot-test-hooks.git
I hope
https://github.com/xypron/uboot-test-hooks/commit/20dcd721437dd5f7d7d3d235f7...
will do the job.
Once we have this applied we will have to adjust the config files for QEMU.
I have been trying to run the test on qemu_arm64_defconfig using the following lines in u_boot_boardenv_qemu_arm64.py:
env__efi_fit_tftp_file = { "fn": "helloworld.efi", "size": 4480, "crc32": "19f9c0ab", }
I got an error:
test/py/tests/test_efi_fit.py:417: in launch_efi addr = load_fit_from_host(fit) if is_sandbox else load_fit_from_tftp(fit) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ addr = f.get('addr', None) if not addr:
addr = u_boot_utils.find_ram_base(cons)
E NameError: name 'u_boot_utils' is not defined
When I provided addr:
env__efi_fit_tftp_file = { "fn": "helloworld.efi", "size": 4480, "crc32": "19f9c0ab", "addr": 0x40400000, }
I got the following error:
=> tftpboot 1073741824 helloworld.efi TFTP error: trying to overwrite reserved memory...
I would have expected a command
tftpboot 40400000 helloworld.efi
to be issued.
Same error with bootm:
=> bootm 1077936128#config-efi-nofdt "Synchronous Abort" handler, esr 0x96000010 elr: 000000000001c36c lr : 00000000000140f4 (reloc)
Please, fix the lines indicated below and verify that you can actually execute this test on the QEMU platform.
Thank you for the detailed report!
Unfortunately I have only tested on qemu_arm and somehow I missed the check of having the address computed by the test suite. It used to work before I changed the address data type to string - the reason was to allow for more flexibility, e.g. providing values like '$kernel_addr_r' instead of just precomputed numbers.
I'm going to extend the tests on qemu_arm64 as well.
https://github.com/xypron/u-boot-build/tree/qemu-arm64/u-boot-test
contains the files I use to run Python tests on qemu_arm64_defconfig.
test/py/tests/test_efi_fit.py | 459 ++++++++++++++++++++++++++++++++++ 1 file changed, 459 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..e1f0e42694 --- /dev/null +++ b/test/py/tests/test_efi_fit.py @@ -0,0 +1,459 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2019, Cristian Ciocaltea cristian.ciocaltea@gmail.com +# +# Work based on: +# - test_net.py +# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. +# - test_fit.py +# Copyright (c) 2013, Google Inc. +# +# Test launching UEFI binaries from FIT images.
+import os.path +import pytest +import u_boot_utils as util
"as util" causes an error if you use u_boot_utils.* below. Below I indicate the places to change.
Fixed, thanks.
+""" +Note: This test relies on boardenv_* containing configuration values to define +which network environment is available for testing. Without this, the parts +that rely on network will be automatically skipped.
+For example:
+# Boolean indicating whether the Ethernet device is attached to USB, and hence +# USB enumeration needs to be performed prior to network tests. +# This variable may be omitted if its value is False. +env__net_uses_usb = False
+# Boolean indicating whether the Ethernet device is attached to PCI, and hence +# PCI enumeration needs to be performed prior to network tests. +# This variable may be omitted if its value is False. +env__net_uses_pci = True
+# True if a DHCP server is attached to the network, and should be tested. +# If DHCP testing is not possible or desired, this variable may be omitted or +# set to False. +env__net_dhcp_server = True
+# A list of environment variables that should be set in order to configure a +# static IP. If solely relying on DHCP, this variable may be omitted or set to +# an empty list. +env__net_static_env_vars = [
- ('ipaddr', '10.0.0.100'),
- ('netmask', '255.255.255.0'),
- ('serverip', '10.0.0.1'),
+]
+# Details regarding a file that may be read from a TFTP server. This variable +# may be omitted or set to None if TFTP testing is not possible or desired. +# Additionally, when the 'size' is not available, the file will be generated +# automatically in the TFTP root directory, as specified by the 'dn' field. +env__efi_fit_tftp_file = {
- 'fn': 'test-efi-fit.img', # File path relative to TFTP root
- 'size': 3831, # File size
- 'crc32': '9fa3f79c', # Checksum using CRC-32 algorithm, optional
- 'addr': '$kernel_addr_r', # Loading address, optional
- 'dn': 'tftp/root/dir', # TFTP root directory path, optional
+} +"""
+# Define the parametrized ITS data to be used for FIT images generation. +its_data = ''' +/dts-v1/;
+/ {
- description = "EFI image with FDT blob";
- #address-cells = <1>;
- images {
efi {
description = "Test EFI";
data = /incbin/("%(efi-bin)s");
type = "%(kernel-type)s";
arch = "%(sys-arch)s";
os = "efi";
compression = "%(efi-comp)s";
load = <0x0>;
entry = <0x0>;
};
fdt {
description = "Test FDT";
data = /incbin/("%(fdt-bin)s");
type = "flat_dt";
arch = "%(sys-arch)s";
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 to be used for DTB images generation. +fdt_data = ''' +/dts-v1/;
+/ {
- #address-cells = <1>;
- #size-cells = <0>;
- model = "%(sys-arch)s %(fdt_type)s EFI FIT Boot Test";
- compatible = "%(sys-arch)s";
- reset@0 {
compatible = "%(sys-arch)s,reset";
reg = <0>;
- };
+}; +'''
+@pytest.mark.buildconfigspec('bootm_efi') +@pytest.mark.buildconfigspec('cmd_bootefi_hello_compile') +@pytest.mark.buildconfigspec('fit') +@pytest.mark.notbuildconfigspec('generate_acpi_table') +@pytest.mark.requiredtool('dtc') +def test_efi_fit_launch(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.
- For more details, see launch_efi().
- The following test cases are currently defined and enabled:
- Launch uncompressed FIT EFI & internal FDT
- Launch uncompressed FIT EFI & FIT FDT
- Launch compressed FIT EFI & internal FDT
- Launch compressed FIT EFI & FIT FDT
- """
- def net_pre_commands():
"""Execute any commands required to enable network hardware.
These commands are provided by the boardenv_* file; see the comment
at the beginning of this file.
"""
init_usb = cons.config.env.get('env__net_uses_usb', False)
if init_usb:
cons.run_command('usb start')
init_pci = cons.config.env.get('env__net_uses_pci', False)
if init_pci:
cons.run_command('pci enum')
- def net_dhcp():
"""Execute the dhcp command.
The boardenv_* file may be used to enable/disable DHCP; see the
comment at the beginning of this file.
"""
has_dhcp = cons.config.buildconfig.get('config_cmd_dhcp', 'n') == 'y'
if not has_dhcp:
cons.log.warning('CONFIG_CMD_DHCP != y: Skipping DHCP network setup')
return False
test_dhcp = cons.config.env.get('env__net_dhcp_server', False)
if not test_dhcp:
cons.log.info('No DHCP server available')
return False
cons.run_command('setenv autoload no')
output = cons.run_command('dhcp')
assert 'DHCP client bound to address ' in output
return True
- def net_setup_static():
"""Set up a static IP configuration.
The configuration is provided by the boardenv_* file; see the comment at
the beginning of this file.
"""
has_dhcp = cons.config.buildconfig.get('config_cmd_dhcp', 'n') == 'y'
if not has_dhcp:
cons.log.warning('CONFIG_NET != y: Skipping static network setup')
return False
env_vars = cons.config.env.get('env__net_static_env_vars', None)
if not env_vars:
cons.log.info('No static network configuration is defined')
return False
for (var, val) in env_vars:
cons.run_command('setenv %s %s' % (var, val))
return True
- 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(cons.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)
util.run_and_log(cons,
['cp', make_fpath('lib/efi_loader/helloworld.efi'), bin_path])
if comp:
util.run_and_log(cons, ['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.
Return:
The path of the created file.
"""
# Generate resources referenced by FDT.
fdt_params = {
'sys-arch': sys_arch,
'fdt_type' : fdt_type,
}
# Generate a test FDT file.
dts = make_fpath('test-efi-fit-%s.dts' % fdt_type)
with open(dts, 'w') as fd:
fd.write(fdt_data % fdt_params)
# Build the test FDT.
dtb = make_fpath('test-efi-fit-%s.dtb' % fdt_type)
util.run_and_log(cons, ['dtc', '-I', 'dts', '-O', 'dtb', '-o', dtb, dts])
if comp:
util.run_and_log(cons, ['gzip', '-f', dtb])
dtb += '.gz'
return dtb
- def make_fit(comp):
"""Create a sample FIT image.
Runs 'mkimage' to create a FIT image within U-Boot build dir.
Args:
comp: Enable gzip compression for the EFI binary and FDT blob.
Return:
The path of the created file.
"""
# Generate resources referenced by ITS.
its_params = {
'sys-arch': sys_arch,
'efi-bin': os.path.basename(make_efi('test-efi-fit-helloworld.efi', comp)),
'kernel-type': 'kernel' if comp else 'kernel_noload',
'efi-comp': 'gzip' if comp else 'none',
'fdt-bin': os.path.basename(make_dtb('user', comp)),
'fdt-comp': 'gzip' if comp else 'none',
}
# Generate a test ITS file.
its_path = make_fpath('test-efi-fit-helloworld.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-helloworld.fit')
util.run_and_log(
cons, [make_fpath('tools/mkimage'), '-f', its_path, fit_path])
return fit_path
- def load_fit_from_host(f):
"""Load the FIT image using the 'host load' command and return its address.
Args:
f: Dictionary describing the FIT image to load, see env__efi_fit_test_file
in the comment at the beginning of this file.
Return:
The address where the file has been loaded.
"""
addr = f.get('addr', None)
if not addr:
addr = u_boot_utils.find_ram_base(cons)
%s/u_boot_utils/util/
output = cons.run_command(
'host load hostfs - %s %s/%s' % (addr, f['dn'], f['fn']))
expected_text = ' bytes read'
sz = f.get('size', None)
if sz:
expected_text = '%d' % sz + expected_text
assert(expected_text in output)
return addr
- def load_fit_from_tftp(f):
"""Load the FIT image using the tftpboot command and return its address.
The file is downloaded from the TFTP server, its size and optionally its
CRC32 are validated.
Args:
f: Dictionary describing the FIT image to load, see env__efi_fit_tftp_file
in the comment at the beginning of this file.
Return:
The address where the file has been loaded.
"""
addr = f.get('addr', None)
if not addr:
addr = u_boot_utils.find_ram_base(cons)
%s/u_boot_utils/util/
fn = f['fn']
output = cons.run_command('tftpboot %s %s' % (addr, fn))
You have to pass addr as hexadecimal number.
output = cons.run_command('tftpboot %x %s' % (addr, fn))
As explained before, I eventually converted 'addr' to string in order to allow values like '$kernel_addr_r' to be passed from the environment (this is what I actually used during my local tests). What I missed was the following:
addr = '%x' % util.find_ram_base(cons)
If this is not desired, I can revert to numbers only.
I suggest to use the following instead of referring to $kernel_addr_r:
with cons.log.section('FDT=%s;COMP=%s' % (enable_fdt,
enable_comp)): if is_sandbox: addr = util.find_ram_base(cons) fit = { 'dn': cons.config.build_dir, 'addr': addr, } else:
Actually this is already performed by 'load_fit_from_host()', all we need here is to avoid setting 'addr' in the dictionary.
Thanks again for the whole feedback, I'll send a new patch series with all the changes agreed so far.
Best regards
Heinrich
expected_text = 'Bytes transferred = '
sz = f.get('size', None)
if sz:
expected_text += '%d' % sz
assert expected_text in output
expected_crc = f.get('crc32', None)
if not expected_crc:
return addr
if cons.config.buildconfig.get('config_cmd_crc32', 'n') != 'y':
return addr
output = cons.run_command('crc32 $fileaddr $filesize')
assert expected_crc in output
return addr
- def launch_efi(enable_fdt, enable_comp):
"""Launch U-Boot's helloworld.efi binary from a FIT image.
An external image file can be downloaded from TFTP, when related
details are provided by the boardenv_* file; see the comment at the
beginning of this file.
If the size of the TFTP file is not provided within env__efi_fit_tftp_file,
the test image is generated automatically and placed in the TFTP root
directory specified via the 'dn' field.
When running the tests on Sandbox, the image file is loaded directly
from the host filesystem.
Once the load address is available on U-Boot console, the 'bootm'
command is executed for either 'config-efi-fdt' or 'config-efi-nofdt'
FIT configuration, depending on the value of the 'enable_fdt' function
argument.
Eventually the 'Hello, world' message is expected 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
generated content.
"""
with cons.log.section('FDT=%s;COMP=%s' % (enable_fdt, enable_comp)):
if is_sandbox:
fit = {
'dn': cons.config.build_dir,
'addr': '${kernel_addr_r}',
}
else:
# Init networking.
net_pre_commands()
net_set_up = net_dhcp()
net_set_up = net_setup_static() or net_set_up
if not net_set_up:
pytest.skip('Network not initialized')
fit = cons.config.env.get('env__efi_fit_tftp_file', None)
if not fit:
pytest.skip('No env__efi_fit_tftp_file binary specified in environment')
sz = fit.get('size', None)
if not sz:
if not fit.get('dn', None):
pytest.skip('Neither "size", nor "dn" info provided in env__efi_fit_tftp_file')
# Create test FIT image.
fit_path = make_fit(enable_comp)
fit['fn'] = os.path.basename(fit_path)
fit['size'] = os.path.getsize(fit_path)
# Copy image to TFTP root directory.
if fit['dn'] != cons.config.build_dir:
util.run_and_log(cons, ['mv', '-f', fit_path, '%s/' % fit['dn']])
# Load FIT image.
addr = load_fit_from_host(fit) if is_sandbox else load_fit_from_tftp(fit)
# Select boot configuration.
fit_config = 'config-efi-fdt' if enable_fdt else 'config-efi-nofdt'
# Try booting.
cons.run_command(
'bootm %s#%s' % (addr, fit_config), wait_for_prompt=False)
You have to pass the address as hexadecimal number.
'bootm %x#%s' % (addr, fit_config), wait_for_prompt=False)
Please see comments above.
Best regards
Heinrich
if enable_fdt:
cons.wait_for('Booting using the fdt blob')
cons.wait_for('Hello, world')
cons.wait_for('## Application terminated, r = 0')
cons.restart_uboot();
- cons = u_boot_console
- # Array slice removes leading/trailing quotes.
- sys_arch = cons.config.buildconfig.get('config_sys_arch', '"sandbox"')[1:-1]
- is_sandbox = sys_arch == 'sandbox'
- try:
if is_sandbox:
# Use our own device tree file, will be restored afterwards.
control_dtb = make_dtb('internal', False)
old_dtb = cons.config.dtb
cons.config.dtb = control_dtb
# Run tests
# - fdt OFF, gzip OFF
launch_efi(False, False)
# - fdt ON, gzip OFF
launch_efi(True, False)
if is_sandbox:
# - fdt OFF, gzip ON
launch_efi(False, True)
# - fdt ON, gzip ON
launch_efi(True, True)
- finally:
if is_sandbox:
# Go back to the original U-Boot with the correct dtb.
cons.config.dtb = old_dtb
cons.restart_uboot()
participants (3)
-
Cristian Ciocaltea
-
Heinrich Schuchardt
-
Heinrich Schuchardt