public inbox for u-boot@lists.denx.de
 help / color / mirror / Atom feed
* [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