* [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 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 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 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 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 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 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