* [PATCH v4 0/5] Add support for booting EFI FIT images
@ 2019-12-24 16:05 Cristian Ciocaltea
2019-12-24 16:05 ` [PATCH v4 1/5] image: Add IH_OS_EFI for EFI chain-load boot Cristian Ciocaltea
` (4 more replies)
0 siblings, 5 replies; 15+ messages in thread
From: Cristian Ciocaltea @ 2019-12-24 16:05 UTC (permalink / raw)
To: u-boot
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
--
2.17.1
^ permalink raw reply [flat|nested] 15+ messages in thread* [PATCH v4 1/5] image: Add IH_OS_EFI for EFI chain-load boot 2019-12-24 16:05 [PATCH v4 0/5] Add support for booting EFI FIT images Cristian Ciocaltea @ 2019-12-24 16:05 ` Cristian Ciocaltea 2019-12-24 16:05 ` [PATCH v4 2/5] bootm: Add a bootm command for type IH_OS_EFI Cristian Ciocaltea ` (3 subsequent siblings) 4 siblings, 0 replies; 15+ messages in thread From: Cristian Ciocaltea @ 2019-12-24 16:05 UTC (permalink / raw) To: u-boot 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, }; -- 2.17.1 ^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v4 2/5] bootm: Add a bootm command for type IH_OS_EFI 2019-12-24 16:05 [PATCH v4 0/5] Add support for booting EFI FIT images Cristian Ciocaltea 2019-12-24 16:05 ` [PATCH v4 1/5] image: Add IH_OS_EFI for EFI chain-load boot Cristian Ciocaltea @ 2019-12-24 16:05 ` Cristian Ciocaltea 2019-12-29 10:34 ` Heinrich Schuchardt 2019-12-24 16:05 ` [PATCH v4 3/5] doc: Add sample uefi.its image description file Cristian Ciocaltea ` (2 subsequent siblings) 4 siblings, 1 reply; 15+ messages in thread From: Cristian Ciocaltea @ 2019-12-24 16:05 UTC (permalink / raw) To: u-boot 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 */ -- 2.17.1 ^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v4 2/5] bootm: Add a bootm command for type IH_OS_EFI 2019-12-24 16:05 ` [PATCH v4 2/5] bootm: Add a bootm command for type IH_OS_EFI Cristian Ciocaltea @ 2019-12-29 10:34 ` Heinrich Schuchardt 2019-12-29 10:56 ` Heinrich Schuchardt 2019-12-29 16:53 ` Cristian Ciocaltea 0 siblings, 2 replies; 15+ messages in thread From: Heinrich Schuchardt @ 2019-12-29 10:34 UTC (permalink / raw) To: u-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 */ > ^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v4 2/5] bootm: Add a bootm command for type IH_OS_EFI 2019-12-29 10:34 ` Heinrich Schuchardt @ 2019-12-29 10:56 ` Heinrich Schuchardt 2019-12-29 17:22 ` Cristian Ciocaltea 2019-12-29 16:53 ` Cristian Ciocaltea 1 sibling, 1 reply; 15+ messages in thread From: Heinrich Schuchardt @ 2019-12-29 10:56 UTC (permalink / raw) To: u-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 */ >> > > ^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v4 2/5] bootm: Add a bootm command for type IH_OS_EFI 2019-12-29 10:56 ` Heinrich Schuchardt @ 2019-12-29 17:22 ` Cristian Ciocaltea 0 siblings, 0 replies; 15+ messages in thread From: Cristian Ciocaltea @ 2019-12-29 17:22 UTC (permalink / raw) To: u-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 */ > > > > > > > > ^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v4 2/5] bootm: Add a bootm command for type IH_OS_EFI 2019-12-29 10:34 ` Heinrich Schuchardt 2019-12-29 10:56 ` Heinrich Schuchardt @ 2019-12-29 16:53 ` Cristian Ciocaltea 1 sibling, 0 replies; 15+ messages in thread From: Cristian Ciocaltea @ 2019-12-29 16:53 UTC (permalink / raw) To: u-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 */ > > > ^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v4 3/5] doc: Add sample uefi.its image description file 2019-12-24 16:05 [PATCH v4 0/5] Add support for booting EFI FIT images Cristian Ciocaltea 2019-12-24 16:05 ` [PATCH v4 1/5] image: Add IH_OS_EFI for EFI chain-load boot Cristian Ciocaltea 2019-12-24 16:05 ` [PATCH v4 2/5] bootm: Add a bootm command for type IH_OS_EFI Cristian Ciocaltea @ 2019-12-24 16:05 ` Cristian Ciocaltea 2019-12-24 16:05 ` [PATCH v4 4/5] doc: uefi.rst: Document launching UEFI binaries from FIT images Cristian Ciocaltea 2019-12-24 16:05 ` [PATCH v4 5/5] test/py: Create a test for " Cristian Ciocaltea 4 siblings, 0 replies; 15+ messages in thread From: Cristian Ciocaltea @ 2019-12-24 16:05 UTC (permalink / raw) To: u-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"; + }; + }; + }; +}; -- 2.17.1 ^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v4 4/5] doc: uefi.rst: Document launching UEFI binaries from FIT images 2019-12-24 16:05 [PATCH v4 0/5] Add support for booting EFI FIT images Cristian Ciocaltea ` (2 preceding siblings ...) 2019-12-24 16:05 ` [PATCH v4 3/5] doc: Add sample uefi.its image description file Cristian Ciocaltea @ 2019-12-24 16:05 ` Cristian Ciocaltea 2019-12-24 16:05 ` [PATCH v4 5/5] test/py: Create a test for " Cristian Ciocaltea 4 siblings, 0 replies; 15+ messages in thread From: Cristian Ciocaltea @ 2019-12-24 16:05 UTC (permalink / raw) To: u-boot 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 ~~~~~~~~~~~~~~~~~~~~~~~~~~ -- 2.17.1 ^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v4 5/5] test/py: Create a test for launching UEFI binaries from FIT images 2019-12-24 16:05 [PATCH v4 0/5] Add support for booting EFI FIT images Cristian Ciocaltea ` (3 preceding siblings ...) 2019-12-24 16:05 ` [PATCH v4 4/5] doc: uefi.rst: Document launching UEFI binaries from FIT images Cristian Ciocaltea @ 2019-12-24 16:05 ` Cristian Ciocaltea 2019-12-29 10:22 ` Heinrich Schuchardt 4 siblings, 1 reply; 15+ messages in thread From: Cristian Ciocaltea @ 2019-12-24 16:05 UTC (permalink / raw) To: u-boot 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 at 0 { + compatible = "%(sys-arch)s,reset"; + reg = <0>; + }; +}; +''' + + at pytest.mark.buildconfigspec('bootm_efi') + at pytest.mark.buildconfigspec('cmd_bootefi_hello_compile') + at pytest.mark.buildconfigspec('fit') + at pytest.mark.notbuildconfigspec('generate_acpi_table') + at 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() -- 2.17.1 ^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v4 5/5] test/py: Create a test for launching UEFI binaries from FIT images 2019-12-24 16:05 ` [PATCH v4 5/5] test/py: Create a test for " Cristian Ciocaltea @ 2019-12-29 10:22 ` Heinrich Schuchardt 2019-12-29 16:49 ` Heinrich Schuchardt 2019-12-29 18:39 ` Cristian Ciocaltea 0 siblings, 2 replies; 15+ messages in thread From: Heinrich Schuchardt @ 2019-12-29 10:22 UTC (permalink / raw) To: u-boot 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/20dcd721437dd5f7d7d3d235f7112246f43305d2 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 at 0 { > + compatible = "%(sys-arch)s,reset"; > + reg = <0>; > + }; > +}; > +''' > + > + at pytest.mark.buildconfigspec('bootm_efi') > + at pytest.mark.buildconfigspec('cmd_bootefi_hello_compile') > + at pytest.mark.buildconfigspec('fit') > + at pytest.mark.notbuildconfigspec('generate_acpi_table') > + at 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() > ^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v4 5/5] test/py: Create a test for launching UEFI binaries from FIT images 2019-12-29 10:22 ` Heinrich Schuchardt @ 2019-12-29 16:49 ` Heinrich Schuchardt 2019-12-29 18:39 ` Cristian Ciocaltea 1 sibling, 0 replies; 15+ messages in thread From: Heinrich Schuchardt @ 2019-12-29 16:49 UTC (permalink / raw) To: u-boot 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/20dcd721437dd5f7d7d3d235f7112246f43305d2 > > > 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 at 0 { >> + compatible = "%(sys-arch)s,reset"; >> + reg = <0>; >> + }; >> +}; >> +''' >> + >> + at pytest.mark.buildconfigspec('bootm_efi') >> + at pytest.mark.buildconfigspec('cmd_bootefi_hello_compile') >> + at pytest.mark.buildconfigspec('fit') >> + at pytest.mark.notbuildconfigspec('generate_acpi_table') >> + at 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() >> > > ^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v4 5/5] test/py: Create a test for launching UEFI binaries from FIT images 2019-12-29 10:22 ` Heinrich Schuchardt 2019-12-29 16:49 ` Heinrich Schuchardt @ 2019-12-29 18:39 ` Cristian Ciocaltea 2019-12-29 19:11 ` Heinrich Schuchardt 1 sibling, 1 reply; 15+ messages in thread From: Cristian Ciocaltea @ 2019-12-29 18:39 UTC (permalink / raw) To: u-boot 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/20dcd721437dd5f7d7d3d235f7112246f43305d2 > > 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 at 0 { > > + compatible = "%(sys-arch)s,reset"; > > + reg = <0>; > > + }; > > +}; > > +''' > > + > > + at pytest.mark.buildconfigspec('bootm_efi') > > + at pytest.mark.buildconfigspec('cmd_bootefi_hello_compile') > > + at pytest.mark.buildconfigspec('fit') > > + at pytest.mark.notbuildconfigspec('generate_acpi_table') > > + at 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() > > > ^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v4 5/5] test/py: Create a test for launching UEFI binaries from FIT images 2019-12-29 18:39 ` Cristian Ciocaltea @ 2019-12-29 19:11 ` Heinrich Schuchardt 2019-12-29 21:50 ` Cristian Ciocaltea 0 siblings, 1 reply; 15+ messages in thread From: Heinrich Schuchardt @ 2019-12-29 19:11 UTC (permalink / raw) To: u-boot 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/20dcd721437dd5f7d7d3d235f7112246f43305d2 >> >> 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 at 0 { >>> + compatible = "%(sys-arch)s,reset"; >>> + reg = <0>; >>> + }; >>> +}; >>> +''' >>> + >>> + at pytest.mark.buildconfigspec('bootm_efi') >>> + at pytest.mark.buildconfigspec('cmd_bootefi_hello_compile') >>> + at pytest.mark.buildconfigspec('fit') >>> + at pytest.mark.notbuildconfigspec('generate_acpi_table') >>> + at 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() >>> >> > ^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v4 5/5] test/py: Create a test for launching UEFI binaries from FIT images 2019-12-29 19:11 ` Heinrich Schuchardt @ 2019-12-29 21:50 ` Cristian Ciocaltea 0 siblings, 0 replies; 15+ messages in thread From: Cristian Ciocaltea @ 2019-12-29 21:50 UTC (permalink / raw) To: u-boot 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/20dcd721437dd5f7d7d3d235f7112246f43305d2 > > > > > > 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 at 0 { > > > > + compatible = "%(sys-arch)s,reset"; > > > > + reg = <0>; > > > > + }; > > > > +}; > > > > +''' > > > > + > > > > + at pytest.mark.buildconfigspec('bootm_efi') > > > > + at pytest.mark.buildconfigspec('cmd_bootefi_hello_compile') > > > > + at pytest.mark.buildconfigspec('fit') > > > > + at pytest.mark.notbuildconfigspec('generate_acpi_table') > > > > + at 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() > > > > > > > > > > ^ permalink raw reply [flat|nested] 15+ messages in thread
end of thread, other threads:[~2019-12-29 21:50 UTC | newest] Thread overview: 15+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2019-12-24 16:05 [PATCH v4 0/5] Add support for booting EFI FIT images Cristian Ciocaltea 2019-12-24 16:05 ` [PATCH v4 1/5] image: Add IH_OS_EFI for EFI chain-load boot Cristian Ciocaltea 2019-12-24 16:05 ` [PATCH v4 2/5] bootm: Add a bootm command for type IH_OS_EFI Cristian Ciocaltea 2019-12-29 10:34 ` Heinrich Schuchardt 2019-12-29 10:56 ` Heinrich Schuchardt 2019-12-29 17:22 ` Cristian Ciocaltea 2019-12-29 16:53 ` Cristian Ciocaltea 2019-12-24 16:05 ` [PATCH v4 3/5] doc: Add sample uefi.its image description file Cristian Ciocaltea 2019-12-24 16:05 ` [PATCH v4 4/5] doc: uefi.rst: Document launching UEFI binaries from FIT images Cristian Ciocaltea 2019-12-24 16:05 ` [PATCH v4 5/5] test/py: Create a test for " Cristian Ciocaltea 2019-12-29 10:22 ` Heinrich Schuchardt 2019-12-29 16:49 ` Heinrich Schuchardt 2019-12-29 18:39 ` Cristian Ciocaltea 2019-12-29 19:11 ` Heinrich Schuchardt 2019-12-29 21:50 ` Cristian Ciocaltea
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox