From: Simon Glass <simon.glass@canonical.com> The code for printing FITs is quite messy, with lots of separate printf() calls, an indentation string, etc. It also has no tests. In preparation for refactoring this code, add a test. Use Python code to create the test image and C code to test it. The test covers FIT description, image details (type, architecture, OS, addresses), and configuration details. Co-developed-by: Claude <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- test/boot/Makefile | 1 + test/boot/fit_print.c | 93 +++++++++++++++++++++++++ test/py/tests/test_fit_print.py | 120 ++++++++++++++++++++++++++++++++ 3 files changed, 214 insertions(+) create mode 100644 test/boot/fit_print.c create mode 100644 test/py/tests/test_fit_print.py diff --git a/test/boot/Makefile b/test/boot/Makefile index 71c482f8d24..70e15bf63fa 100644 --- a/test/boot/Makefile +++ b/test/boot/Makefile @@ -5,6 +5,7 @@ ifdef CONFIG_UT_BOOTSTD obj-$(CONFIG_BOOTSTD) += bootdev.o bootstd_common.o bootflow.o bootmeth.o obj-$(CONFIG_FIT) += image.o +obj-$(CONFIG_$(PHASE_)FIT_PRINT) += fit_print.o obj-$(CONFIG_BLK_LUKS) += luks.o obj-$(CONFIG_EXPO) += expo.o expo_common.o diff --git a/test/boot/fit_print.c b/test/boot/fit_print.c new file mode 100644 index 00000000000..ef1e86800ee --- /dev/null +++ b/test/boot/fit_print.c @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test for FIT image printing + * + * Copyright 2025 Canonical Ltd + * Written by Simon Glass <simon.glass@canonical.com> + */ + +#include <image.h> +#include <mapmem.h> +#include <os.h> +#include <test/ut.h> +#include <linux/libfdt.h> +#include "bootstd_common.h" + +/* Test fit_print_contents() output */ +static int test_fit_print_norun(struct unit_test_state *uts) +{ + char fname[256]; + void *fit; + void *buf; + ulong addr; + int size; + + /* Load the FIT created by the Python test */ + ut_assertok(os_persistent_file(fname, sizeof(fname), "test-fit.fit")); + ut_assertok(os_read_file(fname, &buf, &size)); + + /* Copy to address 0x10000 and print from there */ + addr = 0x10000; + fit = map_sysmem(addr, size); + memcpy(fit, buf, size); + + /* Print it and check output line by line */ + console_record_reset_enable(); + fit_print_contents(fit); + + /* Check every line of output */ + ut_assert_nextline(" FIT description: Test FIT image for printing"); + ut_assert_nextline(" Created: 2009-02-13 23:31:30 UTC"); + ut_assert_nextline(" Image 0 (kernel)"); + ut_assert_nextline(" Description: Test kernel"); + ut_assert_nextline(" Created: 2009-02-13 23:31:30 UTC"); + ut_assert_nextline(" Type: Kernel Image"); + ut_assert_nextline(" Compression: gzip compressed"); + ut_assert_nextline(" Data Start: 0x000100c4"); + ut_assert_nextline(" Data Size: 327 Bytes = 327 Bytes"); + ut_assert_nextline(" Architecture: Sandbox"); + ut_assert_nextline(" OS: Linux"); + ut_assert_nextline(" Load Address: 0x01000000"); + ut_assert_nextline(" Entry Point: 0x01000000"); + ut_assert_nextline(" Hash algo: sha256"); + ut_assert_nextline(" Hash value: fad998b94ef12fdac0c347915d8b9b6069a4011399e1a2097638a2cb33244cee"); + ut_assert_nextline(" Image 1 (ramdisk)"); + ut_assert_nextline(" Description: Test ramdisk"); + ut_assert_nextline(" Created: 2009-02-13 23:31:30 UTC"); + ut_assert_nextline(" Type: RAMDisk Image"); + ut_assert_nextline(" Compression: uncompressed"); + ut_assert_nextline(" Data Start: 0x00010304"); + ut_assert_nextline(" Data Size: 301 Bytes = 301 Bytes"); + ut_assert_nextline(" Architecture: Sandbox"); + ut_assert_nextline(" OS: Linux"); + ut_assert_nextline(" Load Address: 0x02000000"); + ut_assert_nextline(" Entry Point: unavailable"); + ut_assert_nextline(" Hash algo: sha256"); + ut_assert_nextline(" Hash value: 53e2a65d92ad890dcd89d83a1f95ad6b8206e0e4889548b035062fc494e7f655"); + ut_assert_nextline(" Image 2 (fdt)"); + ut_assert_nextline(" Description: Test FDT"); + ut_assert_nextline(" Created: 2009-02-13 23:31:30 UTC"); + ut_assert_nextline(" Type: Flat Device Tree"); + ut_assert_nextline(" Compression: uncompressed"); + ut_assert_nextline(" Data Start: 0x00010514"); + ut_assert_nextline(" Data Size: 157 Bytes = 157 Bytes"); + ut_assert_nextline(" Architecture: Sandbox"); + ut_assert_nextline(" Hash algo: sha256"); + ut_assert_nextline(" Hash value: 51918524b06745cae06331047c7e566909431bf71338e5f703dffba1823274f4"); + ut_assert_nextline(" Default Configuration: 'conf-1'"); + ut_assert_nextline(" Configuration 0 (conf-1)"); + ut_assert_nextline(" Description: Test configuration"); + ut_assert_nextline(" Kernel: kernel"); + ut_assert_nextline(" Init Ramdisk: ramdisk"); + ut_assert_nextline(" FDT: fdt"); + ut_assert_nextline(" Configuration 1 (conf-2)"); + ut_assert_nextline(" Description: Alternate configuration"); + ut_assert_nextline(" Kernel: kernel"); + ut_assert_nextline(" FDT: fdt"); + ut_assert_console_end(); + + os_free(buf); + + return 0; +} +BOOTSTD_TEST(test_fit_print_norun, UTF_CONSOLE | UTF_MANUAL); diff --git a/test/py/tests/test_fit_print.py b/test/py/tests/test_fit_print.py new file mode 100644 index 00000000000..4960ce503b4 --- /dev/null +++ b/test/py/tests/test_fit_print.py @@ -0,0 +1,120 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright 2025 Canonical Ltd +# Written by Simon Glass <simon.glass@canonical.com> + +"""Test for FIT image printing""" + +import os + +import pytest + +import fit_util +import utils + +# ITS for testing FIT printing with hashes, ramdisk, and multiple configs +PRINT_ITS = ''' +/dts-v1/; + +/ { + description = "Test FIT image for printing"; + #address-cells = <1>; + + images { + kernel { + description = "Test kernel"; + data = /incbin/("%(kernel)s"); + type = "kernel"; + arch = "sandbox"; + os = "linux"; + compression = "gzip"; + load = <0x1000000>; + entry = <0x1000000>; + hash-1 { + algo = "sha256"; + }; + }; + ramdisk { + description = "Test ramdisk"; + data = /incbin/("%(ramdisk)s"); + type = "ramdisk"; + arch = "sandbox"; + os = "linux"; + compression = "none"; + load = <0x2000000>; + hash-1 { + algo = "sha256"; + }; + }; + fdt { + description = "Test FDT"; + data = /incbin/("%(fdt)s"); + type = "flat_dt"; + arch = "sandbox"; + compression = "none"; + hash-1 { + algo = "sha256"; + }; + }; + }; + configurations { + default = "conf-1"; + conf-1 { + description = "Test configuration"; + kernel = "kernel"; + fdt = "fdt"; + ramdisk = "ramdisk"; + }; + conf-2 { + description = "Alternate configuration"; + kernel = "kernel"; + fdt = "fdt"; + }; + }; +}; +''' + +@pytest.mark.boardspec('sandbox') +@pytest.mark.buildconfigspec('fit_print') +@pytest.mark.requiredtool('dtc') +def test_fit_print(ubman): + """Test fit_print_contents() via C unit test""" + mkimage = os.path.join(ubman.config.build_dir, 'tools/mkimage') + + # Create test files (make kernel ~6.3K) + kernel = fit_util.make_kernel(ubman, 'test-kernel.bin', + 'kernel with some extra test data') + + # Compress the kernel (with -n to avoid timestamps for reproducibility) + kernel_gz = kernel + '.gz' + utils.run_and_log(ubman, ['gzip', '-f', '-n', '-k', kernel]) + + fdt = fit_util.make_dtb(ubman, ''' +/dts-v1/; +/ { + #address-cells = <1>; + #size-cells = <0>; + model = "Test"; +}; +''', 'test-fdt') + ramdisk = fit_util.make_kernel(ubman, 'test-ramdisk.bin', 'ramdisk') + + # Compress the ramdisk (with -n to avoid timestamps for reproducibility) + ramdisk_gz = ramdisk + '.gz' + utils.run_and_log(ubman, ['gzip', '-f', '-n', '-k', ramdisk]) + + # Create FIT image with fixed timestamp for reproducible output + params = { + 'kernel': kernel_gz, + 'fdt': fdt, + 'ramdisk': ramdisk_gz, + } + env = os.environ.copy() + env['SOURCE_DATE_EPOCH'] = '1234567890' # 2009-02-13 23:31:30 UTC + fit = os.path.join(ubman.config.persistent_data_dir, 'test-fit.fit') + its = fit_util.make_its(ubman, PRINT_ITS, params) + utils.run_and_log(ubman, [mkimage, '-f', its, fit], env=env) + + # Run the C test which will load and verify this FIT + ubman.run_command('ut -f bootstd test_fit_print_norun') + result = ubman.run_command('echo $?') + assert '0' == result -- 2.43.0