From 4bbf198c46849ef0dab078fb971a153d5af0f461 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 17 Nov 2025 15:56:46 -0700 Subject: [PATCH] test: Add a test for FIT image printing 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 Signed-off-by: Simon Glass --- 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 + */ + +#include +#include +#include +#include +#include +#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 + +"""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