From 33c60a38bb95c9954704857fe5edb29b749b9b18 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 21 Dec 2022 16:08:15 -0700 Subject: [PATCH 01/38] trace: Use notrace for short The attribute syntax is quite verbose. Use the macro provided for this purpose. Signed-off-by: Simon Glass --- arch/arm/cpu/armv7/s5p-common/timer.c | 2 +- arch/arm/mach-exynos/include/mach/cpu.h | 6 +++--- arch/x86/include/asm/global_data.h | 2 +- arch/x86/include/asm/msr.h | 2 +- arch/x86/include/asm/u-boot-x86.h | 2 +- common/spl/spl_fit.c | 1 + doc/develop/trace.rst | 2 +- lib/efi_loader/efi_freestanding.c | 4 ++-- lib/trace.c | 26 +++++++++++-------------- 9 files changed, 22 insertions(+), 25 deletions(-) diff --git a/arch/arm/cpu/armv7/s5p-common/timer.c b/arch/arm/cpu/armv7/s5p-common/timer.c index f4a045e2f0..9d981cce14 100644 --- a/arch/arm/cpu/armv7/s5p-common/timer.c +++ b/arch/arm/cpu/armv7/s5p-common/timer.c @@ -82,7 +82,7 @@ unsigned long get_timer(unsigned long base) return time_ms - base; } -unsigned long __attribute__((no_instrument_function)) timer_get_us(void) +unsigned long notrace timer_get_us(void) { static unsigned long base_time_us; diff --git a/arch/arm/mach-exynos/include/mach/cpu.h b/arch/arm/mach-exynos/include/mach/cpu.h index fb5fdaf3ba..dab148e332 100644 --- a/arch/arm/mach-exynos/include/mach/cpu.h +++ b/arch/arm/mach-exynos/include/mach/cpu.h @@ -248,7 +248,7 @@ static inline char *s5p_get_cpu_name(void) } #define IS_SAMSUNG_TYPE(type, id) \ -static inline int __attribute__((no_instrument_function)) cpu_is_##type(void) \ +static inline int notrace cpu_is_##type(void) \ { \ return (s5p_cpu_id >> 12) == id; \ } @@ -257,7 +257,7 @@ IS_SAMSUNG_TYPE(exynos4, 0x4) IS_SAMSUNG_TYPE(exynos5, 0x5) #define IS_EXYNOS_TYPE(type, id) \ -static inline int __attribute__((no_instrument_function)) \ +static inline int notrace \ proid_is_##type(void) \ { \ return s5p_cpu_id == id; \ @@ -272,7 +272,7 @@ IS_EXYNOS_TYPE(exynos5422, 0x5422) #define proid_is_exynos542x() (proid_is_exynos5420() || proid_is_exynos5422()) #define SAMSUNG_BASE(device, base) \ -static inline unsigned long __attribute__((no_instrument_function)) \ +static inline unsigned long notrace \ samsung_get_base_##device(void) \ { \ if (cpu_is_exynos4()) { \ diff --git a/arch/x86/include/asm/global_data.h b/arch/x86/include/asm/global_data.h index 23693f85a7..22d103df4e 100644 --- a/arch/x86/include/asm/global_data.h +++ b/arch/x86/include/asm/global_data.h @@ -137,7 +137,7 @@ struct arch_global_data { #define DECLARE_GLOBAL_DATA_PTR extern struct global_data *global_data_ptr # else -static inline __attribute__((no_instrument_function)) gd_t *get_fs_gd_ptr(void) +static inline notrace gd_t *get_fs_gd_ptr(void) { gd_t *gd_ptr; diff --git a/arch/x86/include/asm/msr.h b/arch/x86/include/asm/msr.h index 3e613de6ce..27764fc56c 100644 --- a/arch/x86/include/asm/msr.h +++ b/arch/x86/include/asm/msr.h @@ -71,7 +71,7 @@ static inline unsigned long long native_read_tscp(unsigned int *aux) #define EAX_EDX_RET(val, low, high) "=A" (val) #endif -static inline __attribute__((no_instrument_function)) +static inline notrace unsigned long long native_read_msr(unsigned int msr) { DECLARE_ARGS(val, low, high); diff --git a/arch/x86/include/asm/u-boot-x86.h b/arch/x86/include/asm/u-boot-x86.h index 4cf41e9354..8f38c2d1c6 100644 --- a/arch/x86/include/asm/u-boot-x86.h +++ b/arch/x86/include/asm/u-boot-x86.h @@ -108,7 +108,7 @@ void board_init_f_r(void) __attribute__ ((noreturn)); int arch_misc_init(void); /* Read the time stamp counter */ -static inline __attribute__((no_instrument_function)) uint64_t rdtsc(void) +static inline notrace uint64_t rdtsc(void) { uint32_t high, low; __asm__ __volatile__("rdtsc" : "=a" (low), "=d" (high)); diff --git a/common/spl/spl_fit.c b/common/spl/spl_fit.c index 08da7fed88..0e026bb3d8 100644 --- a/common/spl/spl_fit.c +++ b/common/spl/spl_fit.c @@ -599,6 +599,7 @@ static int spl_fit_upload_fpga(struct spl_fit_info *ctx, int node, debug("Ignoring compatible = %s property\n", compatible); } + return 0; ret = fpga_load(devnum, (void *)fpga_image->load_addr, fpga_image->size, BIT_FULL, flags); diff --git a/doc/develop/trace.rst b/doc/develop/trace.rst index b22e068ef9..5c7802da51 100644 --- a/doc/develop/trace.rst +++ b/doc/develop/trace.rst @@ -185,7 +185,7 @@ this produces sensible results for your board. Suitable sources for this timer include high resolution timers, PWMs or profile timers if available. Most modern SOCs have a suitable timer for this. Make sure that you mark this timer (and anything it calls) with -__attribute__((no_instrument_function)) so that the trace library can +notrace so that the trace library can use it without causing an infinite loop. diff --git a/lib/efi_loader/efi_freestanding.c b/lib/efi_loader/efi_freestanding.c index c85df026f0..4b65fc64dd 100644 --- a/lib/efi_loader/efi_freestanding.c +++ b/lib/efi_loader/efi_freestanding.c @@ -100,7 +100,7 @@ void *memset(void *s, int c, size_t n) * func_ptr: Pointer to function being entered * caller: Pointer to function which called this function */ -void __attribute__((no_instrument_function)) +void notrace __cyg_profile_func_enter(void *func_ptr, void *caller) { } @@ -116,7 +116,7 @@ __cyg_profile_func_enter(void *func_ptr, void *caller) * func_ptr: Pointer to function being entered * caller: Pointer to function which called this function */ -void __attribute__((no_instrument_function)) +void notrace __cyg_profile_func_exit(void *func_ptr, void *caller) { } diff --git a/lib/trace.c b/lib/trace.c index 54f0bf2f57..880d90ebd5 100644 --- a/lib/trace.c +++ b/lib/trace.c @@ -68,7 +68,7 @@ static volatile gd_t *trace_gd; /** * trace_save_gd() - save the value of the gd register */ -static void __attribute__((no_instrument_function)) trace_save_gd(void) +static void notrace trace_save_gd(void) { trace_gd = gd; } @@ -81,7 +81,7 @@ static void __attribute__((no_instrument_function)) trace_save_gd(void) * have to set the gd register to the U-Boot value when entering a trace * point and set it back to the application value when exiting the trace point. */ -static void __attribute__((no_instrument_function)) trace_swap_gd(void) +static void notrace trace_swap_gd(void) { volatile gd_t *temp_gd = trace_gd; @@ -91,18 +91,17 @@ static void __attribute__((no_instrument_function)) trace_swap_gd(void) #else -static void __attribute__((no_instrument_function)) trace_save_gd(void) +static void notrace trace_save_gd(void) { } -static void __attribute__((no_instrument_function)) trace_swap_gd(void) +static void notrace trace_swap_gd(void) { } #endif -static void __attribute__((no_instrument_function)) add_ftrace(void *func_ptr, - void *caller, ulong flags) +static void notrace add_ftrace(void *func_ptr, void *caller, ulong flags) { if (hdr->depth > hdr->depth_limit) { hdr->ftrace_too_deep_count++; @@ -118,7 +117,7 @@ static void __attribute__((no_instrument_function)) add_ftrace(void *func_ptr, hdr->ftrace_count++; } -static void __attribute__((no_instrument_function)) add_textbase(void) +static void notrace add_textbase(void) { if (hdr->ftrace_count < hdr->ftrace_size) { struct trace_call *rec = &hdr->ftrace[hdr->ftrace_count]; @@ -139,8 +138,7 @@ static void __attribute__((no_instrument_function)) add_textbase(void) * @func_ptr: pointer to function being entered * @caller: pointer to function which called this function */ -void __attribute__((no_instrument_function)) __cyg_profile_func_enter( - void *func_ptr, void *caller) +void notrace __cyg_profile_func_enter(void *func_ptr, void *caller) { if (trace_enabled) { int func; @@ -167,8 +165,7 @@ void __attribute__((no_instrument_function)) __cyg_profile_func_enter( * @func_ptr: pointer to function being entered * @caller: pointer to function which called this function */ -void __attribute__((no_instrument_function)) __cyg_profile_func_exit( - void *func_ptr, void *caller) +void notrace __cyg_profile_func_exit(void *func_ptr, void *caller) { if (trace_enabled) { trace_swap_gd(); @@ -327,7 +324,7 @@ void trace_print_stats(void) puts(" calls not traced due to depth\n"); } -void __attribute__((no_instrument_function)) trace_set_enabled(int enabled) +void notrace trace_set_enabled(int enabled) { trace_enabled = enabled != 0; } @@ -339,8 +336,7 @@ void __attribute__((no_instrument_function)) trace_set_enabled(int enabled) * @buff_size: Size of trace buffer * Return: 0 if ok */ -int __attribute__((no_instrument_function)) trace_init(void *buff, - size_t buff_size) +int notrace trace_init(void *buff, size_t buff_size) { ulong func_count = gd->mon_len / FUNC_SITE_SIZE; size_t needed; @@ -404,7 +400,7 @@ int __attribute__((no_instrument_function)) trace_init(void *buff, * * Return: 0 if ok, -ENOSPC if not enough memory is available */ -int __attribute__((no_instrument_function)) trace_early_init(void) +int notrace trace_early_init(void) { ulong func_count = gd->mon_len / FUNC_SITE_SIZE; size_t buff_size = CONFIG_TRACE_EARLY_SIZE; From d81d17637a04746341fb6ae2e55597cd61a92c07 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 21 Dec 2022 16:08:16 -0700 Subject: [PATCH 02/38] arm: Support trace on armv8 Use the notrace attribute so that timer functions can be used when tracing. This is required to avoid infinite loops when recording a trace. Signed-off-by: Simon Glass --- arch/arm/cpu/armv8/generic_timer.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/arm/cpu/armv8/generic_timer.c b/arch/arm/cpu/armv8/generic_timer.c index f27a74b9d0..8f83372cbc 100644 --- a/arch/arm/cpu/armv8/generic_timer.c +++ b/arch/arm/cpu/armv8/generic_timer.c @@ -17,7 +17,7 @@ DECLARE_GLOBAL_DATA_PTR; /* * Generic timer implementation of get_tbclk() */ -unsigned long get_tbclk(void) +unsigned long notrace get_tbclk(void) { unsigned long cntfrq; asm volatile("mrs %0, cntfrq_el0" : "=r" (cntfrq)); @@ -78,7 +78,7 @@ unsigned long timer_read_counter(void) /* * timer_read_counter() using the Arm Generic Timer (aka arch timer). */ -unsigned long timer_read_counter(void) +unsigned long notrace timer_read_counter(void) { unsigned long cntpct; @@ -89,7 +89,7 @@ unsigned long timer_read_counter(void) } #endif -uint64_t get_ticks(void) +uint64_t notrace get_ticks(void) { unsigned long ticks = timer_read_counter(); From 0c16fca927751fbb9be6f026ec8534d289156d3c Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 7 Jan 2023 14:57:20 -0700 Subject: [PATCH 03/38] tpm: Add a proper Kconfig option for crc8 in SPL The current approach is a bit of a hack and only works for the tpm subsystem. Add a Kconfig so that crc8 can be enabled in SPL for other purposes. Signed-off-by: Simon Glass --- lib/Kconfig | 18 ++++++++++++++++++ lib/Makefile | 3 ++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/lib/Kconfig b/lib/Kconfig index def36f275c..36d3cf99c3 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -422,6 +422,7 @@ config TPM config SPL_TPM bool "Trusted Platform Module (TPM) Support in SPL" depends on SPL_DM + imply SPL_CRC8 help This enables support for TPMs which can be used to provide security features for your board. The TPM can be connected via LPC or I2C @@ -617,6 +618,23 @@ config SPL_MD5 security applications, but it can be useful for providing a quick checksum of a block of data. +config CRC8 + def_bool y + help + Enables CRC8 support in U-Boot. This is normally required. CRC8 is + a simple and fast checksumming algorithm which does a bytewise + checksum with feedback to produce an 8-bit result. The code is small + and it does not require a lookup table (unlike CRC32). + +config SPL_CRC8 + bool "Support CRC8 in SPL" + depends on SPL + help + Enables CRC8 support in SPL. This is not normally required. CRC8 is + a simple and fast checksumming algorithm which does a bytewise + checksum with feedback to produce an 8-bit result. The code is small + and it does not require a lookup table (unlike CRC32). + config CRC32 def_bool y help diff --git a/lib/Makefile b/lib/Makefile index d77b33e7f4..a282e40258 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -57,12 +57,13 @@ endif obj-$(CONFIG_$(SPL_TPL_)TPM) += tpm-common.o ifeq ($(CONFIG_$(SPL_TPL_)TPM),y) -obj-y += crc8.o obj-$(CONFIG_TPM) += tpm_api.o obj-$(CONFIG_TPM_V1) += tpm-v1.o obj-$(CONFIG_TPM_V2) += tpm-v2.o endif +obj-$(CONFIG_$(SPL_TPL_)CRC8) += crc8.o + obj-y += crypto/ obj-$(CONFIG_$(SPL_TPL_)GENERATE_ACPI_TABLE) += acpi/ From c662d0b72272b183d66039c9337f7b58b56530ff Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 21 Dec 2022 16:08:18 -0700 Subject: [PATCH 04/38] fdt: Avoid exporting fdtdec_prepare_fdt() This function is not used outside this file. Make it static. Signed-off-by: Simon Glass --- include/fdtdec.h | 9 --------- lib/fdtdec.c | 26 +++++++++++++------------- 2 files changed, 13 insertions(+), 22 deletions(-) diff --git a/include/fdtdec.h b/include/fdtdec.h index 12355afd7f..aa61a0fca1 100644 --- a/include/fdtdec.h +++ b/include/fdtdec.h @@ -554,15 +554,6 @@ uint64_t fdtdec_get_uint64(const void *blob, int node, const char *prop_name, */ int fdtdec_get_is_enabled(const void *blob, int node); -/** - * Make sure we have a valid fdt available to control U-Boot. - * - * If not, a message is printed to the console if the console is ready. - * - * Return: 0 if all ok, -1 if not - */ -int fdtdec_prepare_fdt(void); - /** * Checks that we have a valid fdt available to control U-Boot. diff --git a/lib/fdtdec.c b/lib/fdtdec.c index 64c5b3da15..6388bb8b89 100644 --- a/lib/fdtdec.c +++ b/lib/fdtdec.c @@ -586,24 +586,12 @@ int fdtdec_get_chosen_node(const void *blob, const char *name) return fdt_path_offset(blob, prop); } -int fdtdec_check_fdt(void) -{ - /* - * We must have an FDT, but we cannot panic() yet since the console - * is not ready. So for now, just assert(). Boards which need an early - * FDT (prior to console ready) will need to make their own - * arrangements and do their own checks. - */ - assert(!fdtdec_prepare_fdt()); - return 0; -} - /* * This function is a little odd in that it accesses global data. At some * point if the architecture board.c files merge this will make more sense. * Even now, it is common code. */ -int fdtdec_prepare_fdt(void) +static int fdtdec_prepare_fdt(void) { if (!gd->fdt_blob || ((uintptr_t)gd->fdt_blob & 3) || fdt_check_header(gd->fdt_blob)) { @@ -625,6 +613,18 @@ int fdtdec_prepare_fdt(void) return 0; } +int fdtdec_check_fdt(void) +{ + /* + * We must have an FDT, but we cannot panic() yet since the console + * is not ready. So for now, just assert(). Boards which need an early + * FDT (prior to console ready) will need to make their own + * arrangements and do their own checks. + */ + assert(!fdtdec_prepare_fdt()); + return 0; +} + int fdtdec_lookup_phandle(const void *blob, int node, const char *prop_name) { const u32 *phandle; From b62d34937ad9eeb4ee67eb62cf1937f35f0d014c Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 21 Dec 2022 16:08:19 -0700 Subject: [PATCH 05/38] fdt: Drop ifdefs in fdtdec_prepare_fdt() This function is a bit messy with several #ifdefs. Convert them to use C for the conditions. Rewrite the function comment since most of it is stale. Signed-off-by: Simon Glass --- lib/fdtdec.c | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/lib/fdtdec.c b/lib/fdtdec.c index 6388bb8b89..891b274aa3 100644 --- a/lib/fdtdec.c +++ b/lib/fdtdec.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -586,30 +587,31 @@ int fdtdec_get_chosen_node(const void *blob, const char *name) return fdt_path_offset(blob, prop); } -/* - * This function is a little odd in that it accesses global data. At some - * point if the architecture board.c files merge this will make more sense. - * Even now, it is common code. +/** + * fdtdec_prepare_fdt() - Check we have a valid fdt available to control U-Boot + * + * If not, a message is printed to the console if the console is ready. + * + * Return: 0 if all ok, -ENOENT if not */ static int fdtdec_prepare_fdt(void) { if (!gd->fdt_blob || ((uintptr_t)gd->fdt_blob & 3) || fdt_check_header(gd->fdt_blob)) { -#ifdef CONFIG_SPL_BUILD - puts("Missing DTB\n"); -#else - printf("No valid device tree binary found at %p\n", - gd->fdt_blob); -# ifdef DEBUG - if (gd->fdt_blob) { - printf("fdt_blob=%p\n", gd->fdt_blob); + if (spl_phase() <= PHASE_SPL) { + puts("Missing DTB\n"); + } else { + printf("No valid device tree binary found at %p\n", + gd->fdt_blob); + if (_DEBUG && gd->fdt_blob) { + printf("fdt_blob=%p\n", gd->fdt_blob); print_buffer((ulong)gd->fdt_blob, gd->fdt_blob, 4, 32, 0); + } } -# endif -#endif - return -1; + return -ENOENT; } + return 0; } From ec4f327145ead89a5fd6714baa878112818b7147 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 21 Dec 2022 16:08:20 -0700 Subject: [PATCH 06/38] fdt: Pass the device tree to fdtdec_prepare_fdt() This function uses gd->fdt_blob a lot and cannot be used to check any other device tree. Use a parameter instead. Signed-off-by: Simon Glass --- lib/fdtdec.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/fdtdec.c b/lib/fdtdec.c index 891b274aa3..03c9ceab77 100644 --- a/lib/fdtdec.c +++ b/lib/fdtdec.c @@ -590,23 +590,23 @@ int fdtdec_get_chosen_node(const void *blob, const char *name) /** * fdtdec_prepare_fdt() - Check we have a valid fdt available to control U-Boot * + * @blob: Blob to check + * * If not, a message is printed to the console if the console is ready. * * Return: 0 if all ok, -ENOENT if not */ -static int fdtdec_prepare_fdt(void) +static int fdtdec_prepare_fdt(const void *blob) { - if (!gd->fdt_blob || ((uintptr_t)gd->fdt_blob & 3) || - fdt_check_header(gd->fdt_blob)) { + if (!blob || ((uintptr_t)blob & 3) || fdt_check_header(blob)) { if (spl_phase() <= PHASE_SPL) { puts("Missing DTB\n"); } else { printf("No valid device tree binary found at %p\n", - gd->fdt_blob); - if (_DEBUG && gd->fdt_blob) { - printf("fdt_blob=%p\n", gd->fdt_blob); - print_buffer((ulong)gd->fdt_blob, gd->fdt_blob, 4, - 32, 0); + blob); + if (_DEBUG && blob) { + printf("fdt_blob=%p\n", blob); + print_buffer((ulong)blob, blob, 4, 32, 0); } } return -ENOENT; @@ -623,7 +623,7 @@ int fdtdec_check_fdt(void) * FDT (prior to console ready) will need to make their own * arrangements and do their own checks. */ - assert(!fdtdec_prepare_fdt()); + assert(!fdtdec_prepare_fdt(gd->fdt_blob)); return 0; } @@ -1668,7 +1668,7 @@ int fdtdec_setup(void) if (CONFIG_IS_ENABLED(MULTI_DTB_FIT)) setup_multi_dtb_fit(); - ret = fdtdec_prepare_fdt(); + ret = fdtdec_prepare_fdt(gd->fdt_blob); if (!ret) ret = fdtdec_board_setup(gd->fdt_blob); oftree_reset(); @@ -1700,7 +1700,7 @@ int fdtdec_resetup(int *rescan) *rescan = 1; gd->fdt_blob = fdt_blob; - return fdtdec_prepare_fdt(); + return fdtdec_prepare_fdt(fdt_blob); } /* From 9557592edc82cc690db2e81ef249a772f60078cb Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 21 Dec 2022 16:08:21 -0700 Subject: [PATCH 07/38] fdt: Check for overlapping data and FDT If the FDT overlaps with the data region of the image, or with the stack, it can become corrupted before relocation. Add a check for this, behind a debug flag, as it can be very confusing and time-consuming to debug. Signed-off-by: Simon Glass --- lib/fdtdec.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/lib/fdtdec.c b/lib/fdtdec.c index 03c9ceab77..8d5c68860e 100644 --- a/lib/fdtdec.c +++ b/lib/fdtdec.c @@ -1231,6 +1231,29 @@ static void *fdt_find_separate(void) #else /* FDT is at end of image */ fdt_blob = (ulong *)&_end; + + if (_DEBUG && !fdtdec_prepare_fdt(fdt_blob)) { + int stack_ptr; + const void *top = fdt_blob + fdt_totalsize(fdt_blob); + + /* + * Perform a sanity check on the memory layout. If this fails, + * it indicates that the device tree is positioned above the + * global data pointer or the stack pointer. This should not + * happen. + * + * If this fails, check that SYS_INIT_SP_ADDR has enough space + * below it for SYS_MALLOC_F_LEN and global_data, as well as the + * stack, without overwriting the device tree or U-Boot itself. + * Since the device tree is sitting at _end (the start of the + * BSS region), we need the top of the device tree to be below + * any memory allocated by board_init_f_alloc_reserve(). + */ + if (top > (void *)gd || top > (void *)&stack_ptr) { + printf("FDT %p gd %p\n", fdt_blob, gd); + panic("FDT overlap"); + } + } #endif return fdt_blob; From bebc1410ca79f5b8b33ca86b18bd55fd45d13185 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 21 Dec 2022 16:08:22 -0700 Subject: [PATCH 08/38] trace: Move trace pointer to data section This can be written before relocation. Move it to the data section, since accessing BSS before relocation is not permitted. Signed-off-by: Simon Glass --- lib/trace.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/trace.c b/lib/trace.c index 880d90ebd5..b9dc6d2e4b 100644 --- a/lib/trace.c +++ b/lib/trace.c @@ -40,7 +40,8 @@ struct trace_hdr { int max_depth; }; -static struct trace_hdr *hdr; /* Pointer to start of trace buffer */ +/* Pointer to start of trace buffer */ +static struct trace_hdr *hdr __section(".data"); static inline uintptr_t __attribute__((no_instrument_function)) func_ptr_to_num(void *func_ptr) From 90cfae2ade358018bd46f84dba435eabccd4f8f6 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 21 Dec 2022 16:08:23 -0700 Subject: [PATCH 09/38] mkimage: Add a few more messages for FIT failures Add messages to make it clearer which part of the FIT creation is failing. This can happen when an invalid 'algo' property is provided in the .its file. Signed-off-by: Simon Glass --- tools/fit_image.c | 4 +++- tools/image-host.c | 6 +++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/tools/fit_image.c b/tools/fit_image.c index 923a9755b7..8a18b1b0ba 100644 --- a/tools/fit_image.c +++ b/tools/fit_image.c @@ -36,8 +36,10 @@ static int fit_add_file_data(struct image_tool_params *params, size_t size_inc, tfd = mmap_fdt(params->cmdname, tmpfile, size_inc, &ptr, &sbuf, true, false); - if (tfd < 0) + if (tfd < 0) { + fprintf(stderr, "Cannot map FDT file '%s'\n", tmpfile); return -EIO; + } if (params->keydest) { struct stat dest_sbuf; diff --git a/tools/image-host.c b/tools/image-host.c index 4e0512be63..4a24dee815 100644 --- a/tools/image-host.c +++ b/tools/image-host.c @@ -1292,8 +1292,12 @@ int fit_add_verification_data(const char *keydir, const char *keyfile, ret = fit_image_add_verification_data(keydir, keyfile, keydest, fit, noffset, comment, require_keys, engine_id, cmdname, algo_name); - if (ret) + if (ret) { + printf("Can't add verification data for node '%s' (%s)\n", + fdt_get_name(fit, noffset, NULL), + fdt_strerror(ret)); return ret; + } } /* If there are no keys, we can't sign configurations */ From b87f0818b8b16b44c0b75851fee4d0b77ee4974f Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 21 Dec 2022 16:08:24 -0700 Subject: [PATCH 10/38] trace: Adjust flags in proftool The flags in this tool don't match the comments or help. Also the variable names are quite confusing. Update them for consistency. Signed-off-by: Simon Glass --- tools/proftool.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/tools/proftool.c b/tools/proftool.c index ea7d07a277..0ba84d30c1 100644 --- a/tools/proftool.c +++ b/tools/proftool.c @@ -81,14 +81,15 @@ static void outf(int level, const char *fmt, ...) static void usage(void) { fprintf(stderr, - "Usage: proftool -cds -v3 \n" + "Usage: proftool [-cmtv] \n" "\n" "Commands\n" " dump-ftrace\t\tDump out textual data in ftrace format\n" "\n" "Options:\n" + " -c \tSpecific config file\n" " -m \tSpecify Systen.map file\n" - " -t \tSpecific trace data file (from U-Boot)\n" + " -t \tSpecify trace data file (from U-Boot 'trace calls')\n" " -v <0-4>\tSpecify verbosity\n"); exit(EXIT_FAILURE); } @@ -562,23 +563,23 @@ static int prof_tool(int argc, char *const argv[], int main(int argc, char *argv[]) { const char *map_fname = "System.map"; - const char *prof_fname = NULL; - const char *trace_config_fname = NULL; + const char *trace_fname = NULL; + const char *config_fname = NULL; int opt; verbose = 2; - while ((opt = getopt(argc, argv, "m:p:t:v:")) != -1) { + while ((opt = getopt(argc, argv, "c:m:t:v:")) != -1) { switch (opt) { + case 'c': + config_fname = optarg; + break; + case 'm': map_fname = optarg; break; - case 'p': - prof_fname = optarg; - break; - case 't': - trace_config_fname = optarg; + trace_fname = optarg; break; case 'v': @@ -594,6 +595,5 @@ int main(int argc, char *argv[]) usage(); debug("Debug enabled\n"); - return prof_tool(argc, argv, prof_fname, map_fname, - trace_config_fname); + return prof_tool(argc, argv, trace_fname, map_fname, config_fname); } From ef42e270976aff61f9ef6e8315c66edd2cd80edf Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 21 Dec 2022 16:08:25 -0700 Subject: [PATCH 11/38] trace: Update trace-format generator for newer version This now includes flags and the layout has changed slightly in recent versions of Linux. Update the generator accordingly. Signed-off-by: Simon Glass --- tools/proftool.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/tools/proftool.c b/tools/proftool.c index 0ba84d30c1..b66ea55648 100644 --- a/tools/proftool.c +++ b/tools/proftool.c @@ -163,7 +163,7 @@ static int read_data(FILE *fin, void *buff, int size) if (!err) return 1; if (err != size) { - error("Cannot read profile file at pos %ld\n", ftell(fin)); + error("Cannot read profile file at pos %lx\n", ftell(fin)); return -1; } return 0; @@ -496,10 +496,17 @@ static int make_ftrace(void) int missing_count = 0, skip_count = 0; int i; - printf("# tracer: ftrace\n" - "#\n" - "# TASK-PID CPU# TIMESTAMP FUNCTION\n" - "# | | | | |\n"); + printf("# tracer: function\n" + "#\n" + "# entries-in-buffer/entries-written: 140080/250280 #P:4\n" + "#\n" + "# _-----=> irqs-off\n" + "# / _----=> need-resched\n" + "# | / _---=> hardirq/softirq\n" + "# || / _--=> preempt-depth\n" + "# ||| / delay\n" + "# TASK-PID CPU# |||| TIMESTAMP FUNCTION\n" + "# | | | |||| | |\n"); for (i = 0, call = call_list; i < call_count; i++, call++) { struct func_info *func = find_func_by_offset(call->func); ulong time = call->flags & FUNCF_TIMESTAMP_MASK; @@ -521,7 +528,7 @@ static int make_ftrace(void) continue; } - printf("%16s-%-5d [01] %lu.%06lu: ", "uboot", 1, + printf("%16s-%-5d [000] .... %lu.%06lu: ", "uboot", 1, time / 1000000, time % 1000000); out_func(call->func, 0, " <- "); From 12619d4ec8a3177d1197117302762bf9e8d03be9 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 21 Dec 2022 16:08:26 -0700 Subject: [PATCH 12/38] trace: Don't require TIMER_EARLY Some platforms cannot honour this and don't need trace before relocation. Use 'imply' instead, so boards can disable this. Signed-off-by: Simon Glass --- lib/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Kconfig b/lib/Kconfig index 36d3cf99c3..2425296ce6 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -316,7 +316,7 @@ config BITREVERSE config TRACE bool "Support for tracing of function calls and timing" imply CMD_TRACE - select TIMER_EARLY + imply TIMER_EARLY help Enables function tracing within U-Boot. This allows recording of call traces including timing information. The command can write data to From b2412dd5dee36cef233053a66f0e0035bc30d44a Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 7 Jan 2023 14:57:30 -0700 Subject: [PATCH 13/38] rockchip: Enable bootstage on rockpro64 This board is useful for benchmarking overall U-Boot performance. Enable the bootstage feature so we get a report. Since this returns to the boot rom before finishing executing board_init_r() in SPL, add a few bootstage calls so that we can collect timing from TPL. For the stash region, use a portion of SRAM, 64KB below the stack top. This allows the TPL image to be up to nearly 120KB (it is typically about 64KB). SPL normally runs from SDRAM at 0, so can use the same stash region. Signed-off-by: Simon Glass --- arch/arm/mach-rockchip/tpl.c | 16 +++++++++++++--- configs/rockpro64-rk3399_defconfig | 8 ++++++++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/arch/arm/mach-rockchip/tpl.c b/arch/arm/mach-rockchip/tpl.c index ed46a9ad28..fdd0c592b3 100644 --- a/arch/arm/mach-rockchip/tpl.c +++ b/arch/arm/mach-rockchip/tpl.c @@ -4,6 +4,7 @@ */ #include +#include #include #include #include @@ -70,15 +71,15 @@ void board_init_f(ulong dummy) U_BOOT_TIME ")\n"); #endif #endif + /* Init secure timer */ + rockchip_stimer_init(); + ret = spl_early_init(); if (ret) { debug("spl_early_init() failed: %d\n", ret); hang(); } - /* Init secure timer */ - rockchip_stimer_init(); - /* Init ARM arch timer */ if (IS_ENABLED(CONFIG_SYS_ARCH_TIMER)) timer_init(); @@ -93,6 +94,15 @@ void board_init_f(ulong dummy) int board_return_to_bootrom(struct spl_image_info *spl_image, struct spl_boot_device *bootdev) { +#ifdef CONFIG_BOOTSTAGE_STASH + int ret; + + bootstage_mark_name(BOOTSTAGE_ID_END_TPL, "end tpl"); + ret = bootstage_stash((void *)CONFIG_BOOTSTAGE_STASH_ADDR, + CONFIG_BOOTSTAGE_STASH_SIZE); + if (ret) + debug("Failed to stash bootstage: err=%d\n", ret); +#endif back_to_bootrom(BROM_BOOT_NEXTSTAGE); return 0; diff --git a/configs/rockpro64-rk3399_defconfig b/configs/rockpro64-rk3399_defconfig index 6422b9fa7f..3b4820ce55 100644 --- a/configs/rockpro64-rk3399_defconfig +++ b/configs/rockpro64-rk3399_defconfig @@ -9,6 +9,7 @@ CONFIG_ENV_OFFSET=0x3F8000 CONFIG_DEFAULT_DEVICE_TREE="rk3399-rockpro64" CONFIG_ROCKCHIP_RK3399=y CONFIG_TARGET_ROCKPRO64_RK3399=y +CONFIG_BOOTSTAGE_STASH_ADDR=0xff8e0000 CONFIG_DEBUG_UART_BASE=0xFF1A0000 CONFIG_DEBUG_UART_CLOCK=24000000 CONFIG_SPL_SPI_FLASH_SUPPORT=y @@ -17,6 +18,12 @@ CONFIG_SYS_LOAD_ADDR=0x800800 CONFIG_DEBUG_UART=y CONFIG_HAS_CUSTOM_SYS_INIT_SP_ADDR=y CONFIG_CUSTOM_SYS_INIT_SP_ADDR=0x300000 +CONFIG_BOOTSTAGE=y +CONFIG_SPL_BOOTSTAGE=y +CONFIG_TPL_BOOTSTAGE=y +CONFIG_BOOTSTAGE_REPORT=y +CONFIG_SPL_BOOTSTAGE_RECORD_COUNT=10 +CONFIG_BOOTSTAGE_STASH=y CONFIG_USE_PREBOOT=y CONFIG_DEFAULT_FDT_FILE="rockchip/rk3399-rockpro64.dtb" CONFIG_DISPLAY_BOARDINFO_LATE=y @@ -40,6 +47,7 @@ CONFIG_CMD_PCI=y CONFIG_CMD_USB=y # CONFIG_CMD_SETEXPR is not set CONFIG_CMD_TIME=y +CONFIG_CMD_BOOTSTAGE=y CONFIG_SPL_OF_CONTROL=y CONFIG_OF_SPL_REMOVE_PROPS="pinctrl-0 pinctrl-names clock-names interrupt-parent assigned-clocks assigned-clock-rates assigned-clock-parents" CONFIG_ENV_IS_IN_SPI_FLASH=y From ebc1d50ab51bd08f3adc19de37e81d23d655f9f4 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 21 Dec 2022 16:08:28 -0700 Subject: [PATCH 14/38] Revert "fdtdec: drop needlessly convoluted CONFIG_PHANDLE_CHECK_SEQ" The fdt_path_offset() function is slow since it must scan the tree. This substantial overhead now applies to all boards. The original code may not be ideal but it is fit for purpose and is only needed on a few boards. Reverting this reduces time to set up driver model by about 30ms. Before revert: Accumulated time: 47,170 dm_r 53,237 dm_spl 572,986 dm_f Accumulated time: 44,598 dm_r 50,347 dm_spl 549,133 dm_f This reverts commit 26f981f295d00351b6f0c69b5317b254b2361cc0. Signed-off-by: Simon Glass --- configs/am65x_evm_a53_defconfig | 1 + configs/evb-ast2600_defconfig | 1 + configs/sama7g5ek_mmc1_defconfig | 1 + configs/sama7g5ek_mmc_defconfig | 1 + lib/Kconfig | 7 +++++++ lib/fdtdec.c | 7 +++++-- 6 files changed, 16 insertions(+), 2 deletions(-) diff --git a/configs/am65x_evm_a53_defconfig b/configs/am65x_evm_a53_defconfig index fe3346f215..ad46a74d1f 100644 --- a/configs/am65x_evm_a53_defconfig +++ b/configs/am65x_evm_a53_defconfig @@ -179,3 +179,4 @@ CONFIG_USB_GADGET_VENDOR_NUM=0x0451 CONFIG_USB_GADGET_PRODUCT_NUM=0x6162 CONFIG_USB_GADGET_DOWNLOAD=y CONFIG_OF_LIBFDT_OVERLAY=y +CONFIG_PHANDLE_CHECK_SEQ=y diff --git a/configs/evb-ast2600_defconfig b/configs/evb-ast2600_defconfig index 2fac79bbd7..3440062156 100644 --- a/configs/evb-ast2600_defconfig +++ b/configs/evb-ast2600_defconfig @@ -120,3 +120,4 @@ CONFIG_WDT=y CONFIG_SHA384=y CONFIG_HEXDUMP=y # CONFIG_EFI_LOADER is not set +CONFIG_PHANDLE_CHECK_SEQ=y diff --git a/configs/sama7g5ek_mmc1_defconfig b/configs/sama7g5ek_mmc1_defconfig index f004e44803..ecb4dfc785 100644 --- a/configs/sama7g5ek_mmc1_defconfig +++ b/configs/sama7g5ek_mmc1_defconfig @@ -79,3 +79,4 @@ CONFIG_TIMER=y CONFIG_MCHP_PIT64B_TIMER=y CONFIG_OF_LIBFDT_OVERLAY=y # CONFIG_EFI_LOADER_HII is not set +CONFIG_PHANDLE_CHECK_SEQ=y diff --git a/configs/sama7g5ek_mmc_defconfig b/configs/sama7g5ek_mmc_defconfig index 5b42fc63f3..1d5bccd15b 100644 --- a/configs/sama7g5ek_mmc_defconfig +++ b/configs/sama7g5ek_mmc_defconfig @@ -79,3 +79,4 @@ CONFIG_TIMER=y CONFIG_MCHP_PIT64B_TIMER=y CONFIG_OF_LIBFDT_OVERLAY=y # CONFIG_EFI_LOADER_HII is not set +CONFIG_PHANDLE_CHECK_SEQ=y diff --git a/lib/Kconfig b/lib/Kconfig index 2425296ce6..a83f32d82a 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -1045,6 +1045,13 @@ config LMB_RESERVED_REGIONS Define the number of supported reserved regions in the library logical memory blocks. +config PHANDLE_CHECK_SEQ + bool "Enable phandle check while getting sequence number" + help + When there are multiple device tree nodes with same name, + enable this config option to distinguish them using + phandles in fdtdec_get_alias_seq() function. + endmenu menu "FWU Multi Bank Updates" diff --git a/lib/fdtdec.c b/lib/fdtdec.c index 8d5c68860e..0827e16859 100644 --- a/lib/fdtdec.c +++ b/lib/fdtdec.c @@ -519,8 +519,11 @@ int fdtdec_get_alias_seq(const void *blob, const char *base, int offset, * Adding an extra check to distinguish DT nodes with * same name */ - if (offset != fdt_path_offset(blob, prop)) - continue; + if (IS_ENABLED(CONFIG_PHANDLE_CHECK_SEQ)) { + if (fdt_get_phandle(blob, offset) != + fdt_get_phandle(blob, fdt_path_offset(blob, prop))) + continue; + } val = trailing_strtol(name); if (val != -1) { From 85d87112cb0abedf0d6f1a14422e89d335533157 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 21 Dec 2022 07:27:39 -0700 Subject: [PATCH 15/38] dm: pinctrl: Revert "pinctrl: probe pinctrl drivers during post-bind" This breaks chromebook_coral and it is also not how things should work. If a board needs to bind GPIOs as part of a pinctrl driver this can be done during the bind step, if needed. We cannot probe pinctrl devices when binding as a rule, since it cannot be supported on some platforms. The bind and probe steps are separate in U-Boot and they should remain separate. This reverts commit f9ec791b5e24378b71590877499f8683d5f54dac. Signed-off-by: Simon Glass --- drivers/pinctrl/pinctrl-uclass.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/pinctrl/pinctrl-uclass.c b/drivers/pinctrl/pinctrl-uclass.c index ce2d5ddf6d..a1b85ca87e 100644 --- a/drivers/pinctrl/pinctrl-uclass.c +++ b/drivers/pinctrl/pinctrl-uclass.c @@ -403,13 +403,6 @@ static int __maybe_unused pinctrl_post_bind(struct udevice *dev) { const struct pinctrl_ops *ops = pinctrl_get_ops(dev); - /* - * Make sure that the pinctrl driver gets probed after binding - * as some pinctrl drivers also register the GPIO driver during - * probe, and if they are not probed GPIO-s are not registered. - */ - dev_or_flags(dev, DM_FLAG_PROBE_AFTER_BIND); - if (!ops) { dev_dbg(dev, "ops is not set. Do not bind.\n"); return -EINVAL; From efddab6c365439b9084ef1ac4750eacb7ba9e889 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 7 Jan 2023 14:07:08 -0700 Subject: [PATCH 16/38] binman: Allow writing section contents to a file At present only the image (which is a section) has a filename. Move this implementation to the entry_Section class so that any section can have a filename. With this, the section data is written to a file. This allows parts of an image to be written, along with the entire image. Make a note that this can be used to include the contents of a section in one image in another (later) image. Signed-off-by: Simon Glass --- tools/binman/binman.rst | 5 +++++ tools/binman/etype/section.py | 12 +++++++++- tools/binman/ftest.py | 14 ++++++++++++ tools/binman/image.py | 3 --- tools/binman/test/261_section_fname.dts | 29 +++++++++++++++++++++++++ 5 files changed, 59 insertions(+), 4 deletions(-) create mode 100644 tools/binman/test/261_section_fname.dts diff --git a/tools/binman/binman.rst b/tools/binman/binman.rst index 69e4b00239..2899e1c783 100644 --- a/tools/binman/binman.rst +++ b/tools/binman/binman.rst @@ -836,6 +836,11 @@ name-prefix: renamed to 'ro-u-boot' and 'rw-u-boot'. This can be useful to distinguish binaries with otherwise identical names. +filename: + This allows the contents of the section to be written to a file in the + output directory. This can sometimes be useful to use the data in one + section in different image, since there is currently no way to share data + beteen images other than through files. Image Properties ---------------- diff --git a/tools/binman/etype/section.py b/tools/binman/etype/section.py index da561e2bcc..305155c846 100644 --- a/tools/binman/etype/section.py +++ b/tools/binman/etype/section.py @@ -144,6 +144,10 @@ class Entry_section(Entry): be written at offset 4 in the image file, since the first 16 bytes are skipped when writing. + filename + filename to write the unpadded section contents to within the output + directory (None to skip this). + Since a section is also an entry, it inherits all the properies of entries too. @@ -163,6 +167,7 @@ class Entry_section(Entry): self._skip_at_start = None self._end_4gb = False self._ignore_missing = False + self._filename = None def ReadNode(self): """Read properties from the section node""" @@ -183,6 +188,8 @@ class Entry_section(Entry): self._skip_at_start = 0 self._name_prefix = fdt_util.GetString(self._node, 'name-prefix') self.align_default = fdt_util.GetInt(self._node, 'align-default', 0) + self._filename = fdt_util.GetString(self._node, 'filename', + self._filename) self.ReadEntries() @@ -348,7 +355,8 @@ class Entry_section(Entry): """Get the contents of an entry This builds the contents of the section, stores this as the contents of - the section and returns it + the section and returns it. If the section has a filename, the data is + written there also. Args: required: True if the data must be present, False if it is OK to @@ -363,6 +371,8 @@ class Entry_section(Entry): if data is None: return None self.SetContents(data) + if self._filename: + tools.write_file(tools.get_output_filename(self._filename), data) return data def GetOffsets(self): diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index 62ee86b9b7..c3cb32dca2 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -6077,5 +6077,19 @@ fdt fdtmap Extract the devicetree blob from the fdtmap 'Cannot write symbols to an ELF file without Python elftools', str(exc.exception)) + def testSectionFilename(self): + """Check writing of section contents to a file""" + data = self._DoReadFile('261_section_fname.dts') + expected = (b'&&' + U_BOOT_DATA + b'&&&' + + tools.get_bytes(ord('!'), 7) + + U_BOOT_DATA + tools.get_bytes(ord('&'), 12)) + self.assertEqual(expected, data) + + sect_fname = tools.get_output_filename('outfile.bin') + self.assertTrue(os.path.exists(sect_fname)) + sect_data = tools.read_file(sect_fname) + self.assertEqual(U_BOOT_DATA, sect_data) + + if __name__ == "__main__": unittest.main() diff --git a/tools/binman/image.py b/tools/binman/image.py index 6d4bff5843..b84dd21e22 100644 --- a/tools/binman/image.py +++ b/tools/binman/image.py @@ -94,9 +94,6 @@ class Image(section.Entry_section): def ReadNode(self): super().ReadNode() - filename = fdt_util.GetString(self._node, 'filename') - if filename: - self._filename = filename self.allow_repack = fdt_util.GetBool(self._node, 'allow-repack') self._symlink = fdt_util.GetString(self._node, 'symlink') diff --git a/tools/binman/test/261_section_fname.dts b/tools/binman/test/261_section_fname.dts new file mode 100644 index 0000000000..790381e730 --- /dev/null +++ b/tools/binman/test/261_section_fname.dts @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + pad-byte = <0x26>; + size = <0x20>; + section@0 { + size = <0x10>; + pad-byte = <0x21>; + pad-before = <2>; + pad-after = <3>; + + section { + filename = "outfile.bin"; + u-boot { + }; + }; + }; + section@1 { + u-boot { + }; + }; + }; +}; From 226ce1d24dfcb9291e6ffbaba4799981b8c29ab2 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 7 Jan 2023 14:07:09 -0700 Subject: [PATCH 17/38] binman: Tidy up comment in fit _gen_node Expand this comment to cover both cases that are supported. Signed-off-by: Simon Glass --- tools/binman/etype/fit.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/tools/binman/etype/fit.py b/tools/binman/etype/fit.py index 7860e2aeea..8ad4f3a8a8 100644 --- a/tools/binman/etype/fit.py +++ b/tools/binman/etype/fit.py @@ -579,11 +579,17 @@ class Entry_fit(Entry_section): def _gen_node(base_node, node, depth, in_images, entry): """Generate nodes from a template - This creates one node for each member of self._fdts using the - provided template. If a property value contains 'NAME' it is - replaced with the filename of the FDT. If a property value contains - SEQ it is replaced with the node sequence number, where 1 is the - first. + This creates one or more nodes depending on the fit,operation being + used. + + For OP_GEN_FDT_NODES it creates one node for each member of + self._fdts using the provided template. If a property value contains + 'NAME' it is replaced with the filename of the FDT. If a property + value contains SEQ it is replaced with the node sequence number, + where 1 is the first. + + For OP_SPLIT_ELF it emits one node for each section in the ELF file. + If the file is missing, nothing is generated. Args: base_node (Node): Base Node of the FIT (with 'description' From 237ac96a7033d991763c7f3398c93f8ba769707a Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 7 Jan 2023 14:07:10 -0700 Subject: [PATCH 18/38] binman: Update entry docs These have got out of data recently. Regenerate them. Signed-off-by: Simon Glass --- tools/binman/entries.rst | 171 ++++++++++++++++++++++++++++++---- tools/binman/etype/mkimage.py | 28 +++--- 2 files changed, 169 insertions(+), 30 deletions(-) diff --git a/tools/binman/entries.rst b/tools/binman/entries.rst index 3dc32db8a5..b2ce7960d3 100644 --- a/tools/binman/entries.rst +++ b/tools/binman/entries.rst @@ -216,9 +216,9 @@ This is a blob containing a device tree. The contents of the blob are obtained from the list of available device-tree files, managed by the 'state' module. -Additional Properties / Entry arguments: - - prepend: Header type to use: - length: 32-bit length header +Additional attributes: + prepend: Header used (e.g. 'length') + .. _etype_blob_ext: @@ -1178,11 +1178,13 @@ Properties / Entry arguments: - multiple-data-files: boolean to tell binman to pass all files as datafiles to mkimage instead of creating a temporary file the result of datafiles concatenation + - filename: filename of output binary generated by mkimage The data passed to mkimage via the -d flag is collected from subnodes of the mkimage node, e.g.:: mkimage { + filename = "imximage.bin"; args = "-n test -T imximage"; u-boot-spl { @@ -1190,13 +1192,14 @@ mkimage node, e.g.:: }; This calls mkimage to create an imximage with `u-boot-spl.bin` as the data -file, which mkimage being called like this:: +file, with mkimage being called like this:: mkimage -d -n test -T imximage The output from mkimage then becomes part of the image produced by -binman. If you need to put mulitple things in the data file, you can use -a section, or just multiple subnodes like this:: +binman but also is written into `imximage.bin` file. If you need to put +multiple things in the data file, you can use a section, or just multiple +subnodes like this:: mkimage { args = "-n test -T imximage"; @@ -1208,17 +1211,20 @@ a section, or just multiple subnodes like this:: }; }; +Note that binman places the contents (here SPL and TPL) into a single file +and passes that to mkimage using the -d option. + To pass all datafiles untouched to mkimage:: mkimage { - args = "-n rk3399 -T rkspi"; - multiple-data-files; + args = "-n rk3399 -T rkspi"; + multiple-data-files; - u-boot-tpl { - }; + u-boot-tpl { + }; - u-boot-spl { - }; + u-boot-spl { + }; }; This calls mkimage to create a Rockchip RK3399-specific first stage @@ -1242,17 +1248,17 @@ the 'data-to-imagename' property:: mkimage { args = "-T imximage"; - data-to-imagename'; + data-to-imagename; u-boot-spl { }; }; That will pass the data to mkimage both as the data file (with -d) and as -the image name (with -n). +the image name (with -n). In both cases, a filename is passed as the +argument, with the actual data being in that file. - -If need to pass different data in with -n, then use an imagename subnode:: +If need to pass different data in with -n, then use an `imagename` subnode:: mkimage { args = "-T imximage"; @@ -1271,6 +1277,7 @@ This will pass in u-boot-spl as the input data and the .cfgout file as the -n data. + .. _etype_opensbi: Entry: opensbi: RISC-V OpenSBI fw_dynamic blob @@ -1478,6 +1485,10 @@ skip-at-start be written at offset 4 in the image file, since the first 16 bytes are skipped when writing. +filename + filename to write the unpadded section contents to within the output + directory (None to skip this). + Since a section is also an entry, it inherits all the properies of entries too. @@ -2034,6 +2045,134 @@ Entry types that have a part to play in handling microcode: +.. _etype_u_boot_vpl: + +Entry: u-boot-vpl: U-Boot VPL binary +------------------------------------ + +Properties / Entry arguments: + - filename: Filename of u-boot-vpl.bin (default 'vpl/u-boot-vpl.bin') + +This is the U-Boot VPL (Verifying Program Loader) binary. This is a small +binary which loads before SPL, typically into on-chip SRAM. It is +responsible for locating, loading and jumping to SPL, the next-stage +loader. Note that VPL is not relocatable so must be loaded to the correct +address in SRAM, or written to run from the correct address if direct +flash execution is possible (e.g. on x86 devices). + +SPL can access binman symbols at runtime. See: + + 'Access to binman entry offsets at run time (symbols)' + +in the binman README for more information. + +The ELF file 'vpl/u-boot-vpl' must also be available for this to work, since +binman uses that to look up symbols to write into the VPL binary. + + + +.. _etype_u_boot_vpl_bss_pad: + +Entry: u-boot-vpl-bss-pad: U-Boot VPL binary padded with a BSS region +--------------------------------------------------------------------- + +Properties / Entry arguments: + None + +This holds the padding added after the VPL binary to cover the BSS (Block +Started by Symbol) region. This region holds the various variables used by +VPL. It is set to 0 by VPL when it starts up. If you want to append data to +the VPL image (such as a device tree file), you must pad out the BSS region +to avoid the data overlapping with U-Boot variables. This entry is useful in +that case. It automatically pads out the entry size to cover both the code, +data and BSS. + +The contents of this entry will a certain number of zero bytes, determined +by __bss_size + +The ELF file 'vpl/u-boot-vpl' must also be available for this to work, since +binman uses that to look up the BSS address. + + + +.. _etype_u_boot_vpl_dtb: + +Entry: u-boot-vpl-dtb: U-Boot VPL device tree +--------------------------------------------- + +Properties / Entry arguments: + - filename: Filename of u-boot.dtb (default 'vpl/u-boot-vpl.dtb') + +This is the VPL device tree, containing configuration information for +VPL. VPL needs this to know what devices are present and which drivers +to activate. + + + +.. _etype_u_boot_vpl_elf: + +Entry: u-boot-vpl-elf: U-Boot VPL ELF image +------------------------------------------- + +Properties / Entry arguments: + - filename: Filename of VPL u-boot (default 'vpl/u-boot-vpl') + +This is the U-Boot VPL ELF image. It does not include a device tree but can +be relocated to any address for execution. + + + +.. _etype_u_boot_vpl_expanded: + +Entry: u-boot-vpl-expanded: U-Boot VPL flat binary broken out into its component parts +-------------------------------------------------------------------------------------- + +Properties / Entry arguments: + - vpl-dtb: Controls whether this entry is selected (set to 'y' or '1' to + select) + +This is a section containing the U-Boot binary, BSS padding if needed and a +devicetree. Using this entry type automatically creates this section, with +the following entries in it: + + u-boot-vpl-nodtb + u-boot-vpl-bss-pad + u-boot-dtb + +Having the devicetree separate allows binman to update it in the final +image, so that the entries positions are provided to the running U-Boot. + +This entry is selected based on the value of the 'vpl-dtb' entryarg. If +this is non-empty (and not 'n' or '0') then this expanded entry is selected. + + + +.. _etype_u_boot_vpl_nodtb: + +Entry: u-boot-vpl-nodtb: VPL binary without device tree appended +---------------------------------------------------------------- + +Properties / Entry arguments: + - filename: Filename to include (default 'vpl/u-boot-vpl-nodtb.bin') + +This is the U-Boot VPL binary, It does not include a device tree blob at +the end of it so may not be able to work without it, assuming VPL needs +a device tree to operate on your platform. You can add a u_boot_vpl_dtb +entry after this one, or use a u_boot_vpl entry instead, which normally +expands to a section containing u-boot-vpl-dtb, u-boot-vpl-bss-pad and +u-boot-vpl-dtb + +VPL can access binman symbols at runtime. See: + + 'Access to binman entry offsets at run time (symbols)' + +in the binman README for more information. + +The ELF file 'vpl/u-boot-vpl' must also be available for this to work, since +binman uses that to look up symbols to write into the VPL binary. + + + .. _etype_u_boot_with_ucode_ptr: Entry: u-boot-with-ucode-ptr: U-Boot with embedded microcode pointer diff --git a/tools/binman/etype/mkimage.py b/tools/binman/etype/mkimage.py index c2288c48ee..cb264c3cad 100644 --- a/tools/binman/etype/mkimage.py +++ b/tools/binman/etype/mkimage.py @@ -57,24 +57,24 @@ class Entry_mkimage(Entry): Note that binman places the contents (here SPL and TPL) into a single file and passes that to mkimage using the -d option. - To pass all datafiles untouched to mkimage:: + To pass all datafiles untouched to mkimage:: - mkimage { - args = "-n rk3399 -T rkspi"; - multiple-data-files; + mkimage { + args = "-n rk3399 -T rkspi"; + multiple-data-files; - u-boot-tpl { - }; + u-boot-tpl { + }; - u-boot-spl { - }; - }; + u-boot-spl { + }; + }; - This calls mkimage to create a Rockchip RK3399-specific first stage - bootloader, made of TPL+SPL. Since this first stage bootloader requires to - align the TPL and SPL but also some weird hacks that is handled by mkimage - directly, binman is told to not perform the concatenation of datafiles prior - to passing the data to mkimage. + This calls mkimage to create a Rockchip RK3399-specific first stage + bootloader, made of TPL+SPL. Since this first stage bootloader requires to + align the TPL and SPL but also some weird hacks that is handled by mkimage + directly, binman is told to not perform the concatenation of datafiles prior + to passing the data to mkimage. To use CONFIG options in the arguments, use a string list instead, as in this example which also produces four arguments:: From 23ab4e0054783e619fcf39a50faf9c08c2e18fa5 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 7 Jan 2023 14:07:11 -0700 Subject: [PATCH 19/38] binman: Use a reference for binman symbols docs Several etypes have this reference in their documentation. Now that we are using rST, link to the section directly. Signed-off-by: Simon Glass Suggested-by: Quentin Schulz --- tools/binman/binman.rst | 1 + tools/binman/entries.rst | 32 ++++++-------------------- tools/binman/etype/u_boot.py | 6 +---- tools/binman/etype/u_boot_spl.py | 4 +--- tools/binman/etype/u_boot_spl_nodtb.py | 4 +--- tools/binman/etype/u_boot_tpl.py | 4 +--- tools/binman/etype/u_boot_tpl_nodtb.py | 4 +--- tools/binman/etype/u_boot_vpl.py | 4 +--- tools/binman/etype/u_boot_vpl_nodtb.py | 6 +---- 9 files changed, 15 insertions(+), 50 deletions(-) diff --git a/tools/binman/binman.rst b/tools/binman/binman.rst index 2899e1c783..ef3e5a6d19 100644 --- a/tools/binman/binman.rst +++ b/tools/binman/binman.rst @@ -487,6 +487,7 @@ For x86 devices (with the end-at-4gb property) this base address is not added since it is assumed that images are XIP and the offsets already include the address. +.. _binman_fdt: Access to binman entry offsets at run time (fdt) ------------------------------------------------ diff --git a/tools/binman/entries.rst b/tools/binman/entries.rst index b2ce7960d3..5b9eb8b82c 100644 --- a/tools/binman/entries.rst +++ b/tools/binman/entries.rst @@ -1578,11 +1578,7 @@ This is the U-Boot binary, containing relocation information to allow it to relocate itself at runtime. The binary typically includes a device tree blob at the end of it. -U-Boot can access binman symbols at runtime. See: - - 'Access to binman entry offsets at run time (fdt)' - -in the binman README for more information. +U-Boot can access binman symbols at runtime. See :ref:`binman_fdt`. Note that this entry is automatically replaced with u-boot-expanded unless --no-expanded is used or the node has a 'no-expanded' property. @@ -1712,9 +1708,7 @@ not relocatable so must be loaded to the correct address in SRAM, or written to run from the correct address if direct flash execution is possible (e.g. on x86 devices). -SPL can access binman symbols at runtime. See: - - 'Access to binman entry offsets at run time (symbols)' +SPL can access binman symbols at runtime. See :ref:`binman_fdt`. in the binman README for more information. @@ -1817,9 +1811,7 @@ entry after this one, or use a u-boot-spl entry instead' which normally expands to a section containing u-boot-spl-dtb, u-boot-spl-bss-pad and u-boot-spl-dtb -SPL can access binman symbols at runtime. See: - - 'Access to binman entry offsets at run time (symbols)' +SPL can access binman symbols at runtime. See :ref:`binman_fdt`. in the binman README for more information. @@ -1855,9 +1847,7 @@ loader. Note that SPL is not relocatable so must be loaded to the correct address in SRAM, or written to run from the correct address if direct flash execution is possible (e.g. on x86 devices). -SPL can access binman symbols at runtime. See: - - 'Access to binman entry offsets at run time (symbols)' +SPL can access binman symbols at runtime. See :ref:`binman_fdt`. in the binman README for more information. @@ -1972,9 +1962,7 @@ entry after this one, or use a u-boot-tpl entry instead, which normally expands to a section containing u-boot-tpl-dtb, u-boot-tpl-bss-pad and u-boot-tpl-dtb -TPL can access binman symbols at runtime. See: - - 'Access to binman entry offsets at run time (symbols)' +TPL can access binman symbols at runtime. See :ref:`binman_fdt`. in the binman README for more information. @@ -2060,9 +2048,7 @@ loader. Note that VPL is not relocatable so must be loaded to the correct address in SRAM, or written to run from the correct address if direct flash execution is possible (e.g. on x86 devices). -SPL can access binman symbols at runtime. See: - - 'Access to binman entry offsets at run time (symbols)' +SPL can access binman symbols at runtime. See :ref:`binman_fdt`. in the binman README for more information. @@ -2162,11 +2148,7 @@ entry after this one, or use a u_boot_vpl entry instead, which normally expands to a section containing u-boot-vpl-dtb, u-boot-vpl-bss-pad and u-boot-vpl-dtb -VPL can access binman symbols at runtime. See: - - 'Access to binman entry offsets at run time (symbols)' - -in the binman README for more information. +VPL can access binman symbols at runtime. See :ref:`binman_fdt`. The ELF file 'vpl/u-boot-vpl' must also be available for this to work, since binman uses that to look up symbols to write into the VPL binary. diff --git a/tools/binman/etype/u_boot.py b/tools/binman/etype/u_boot.py index e8d180a46d..d5639eef2e 100644 --- a/tools/binman/etype/u_boot.py +++ b/tools/binman/etype/u_boot.py @@ -18,11 +18,7 @@ class Entry_u_boot(Entry_blob): to relocate itself at runtime. The binary typically includes a device tree blob at the end of it. - U-Boot can access binman symbols at runtime. See: - - 'Access to binman entry offsets at run time (fdt)' - - in the binman README for more information. + U-Boot can access binman symbols at runtime. See :ref:`binman_fdt`. Note that this entry is automatically replaced with u-boot-expanded unless --no-expanded is used or the node has a 'no-expanded' property. diff --git a/tools/binman/etype/u_boot_spl.py b/tools/binman/etype/u_boot_spl.py index d1aa3b4fda..be1610569f 100644 --- a/tools/binman/etype/u_boot_spl.py +++ b/tools/binman/etype/u_boot_spl.py @@ -21,9 +21,7 @@ class Entry_u_boot_spl(Entry_blob): to run from the correct address if direct flash execution is possible (e.g. on x86 devices). - SPL can access binman symbols at runtime. See: - - 'Access to binman entry offsets at run time (symbols)' + SPL can access binman symbols at runtime. See :ref:`binman_fdt`. in the binman README for more information. diff --git a/tools/binman/etype/u_boot_spl_nodtb.py b/tools/binman/etype/u_boot_spl_nodtb.py index 50a126dc7e..e7ec329c90 100644 --- a/tools/binman/etype/u_boot_spl_nodtb.py +++ b/tools/binman/etype/u_boot_spl_nodtb.py @@ -21,9 +21,7 @@ class Entry_u_boot_spl_nodtb(Entry_blob): expands to a section containing u-boot-spl-dtb, u-boot-spl-bss-pad and u-boot-spl-dtb - SPL can access binman symbols at runtime. See: - - 'Access to binman entry offsets at run time (symbols)' + SPL can access binman symbols at runtime. See :ref:`binman_fdt`. in the binman README for more information. diff --git a/tools/binman/etype/u_boot_tpl.py b/tools/binman/etype/u_boot_tpl.py index 1883a2bd5f..397b9f8953 100644 --- a/tools/binman/etype/u_boot_tpl.py +++ b/tools/binman/etype/u_boot_tpl.py @@ -21,9 +21,7 @@ class Entry_u_boot_tpl(Entry_blob): address in SRAM, or written to run from the correct address if direct flash execution is possible (e.g. on x86 devices). - SPL can access binman symbols at runtime. See: - - 'Access to binman entry offsets at run time (symbols)' + SPL can access binman symbols at runtime. See :ref:`binman_fdt`. in the binman README for more information. diff --git a/tools/binman/etype/u_boot_tpl_nodtb.py b/tools/binman/etype/u_boot_tpl_nodtb.py index 7e08e58f1e..9bb2b5dda3 100644 --- a/tools/binman/etype/u_boot_tpl_nodtb.py +++ b/tools/binman/etype/u_boot_tpl_nodtb.py @@ -21,9 +21,7 @@ class Entry_u_boot_tpl_nodtb(Entry_blob): expands to a section containing u-boot-tpl-dtb, u-boot-tpl-bss-pad and u-boot-tpl-dtb - TPL can access binman symbols at runtime. See: - - 'Access to binman entry offsets at run time (symbols)' + TPL can access binman symbols at runtime. See :ref:`binman_fdt`. in the binman README for more information. diff --git a/tools/binman/etype/u_boot_vpl.py b/tools/binman/etype/u_boot_vpl.py index 62e5969c6e..31d7e8374e 100644 --- a/tools/binman/etype/u_boot_vpl.py +++ b/tools/binman/etype/u_boot_vpl.py @@ -21,9 +21,7 @@ class Entry_u_boot_vpl(Entry_blob): address in SRAM, or written to run from the correct address if direct flash execution is possible (e.g. on x86 devices). - SPL can access binman symbols at runtime. See: - - 'Access to binman entry offsets at run time (symbols)' + SPL can access binman symbols at runtime. See :ref:`binman_fdt`. in the binman README for more information. diff --git a/tools/binman/etype/u_boot_vpl_nodtb.py b/tools/binman/etype/u_boot_vpl_nodtb.py index db3d8a91c9..64c2767488 100644 --- a/tools/binman/etype/u_boot_vpl_nodtb.py +++ b/tools/binman/etype/u_boot_vpl_nodtb.py @@ -21,11 +21,7 @@ class Entry_u_boot_vpl_nodtb(Entry_blob): expands to a section containing u-boot-vpl-dtb, u-boot-vpl-bss-pad and u-boot-vpl-dtb - VPL can access binman symbols at runtime. See: - - 'Access to binman entry offsets at run time (symbols)' - - in the binman README for more information. + VPL can access binman symbols at runtime. See :ref:`binman_fdt`. The ELF file 'vpl/u-boot-vpl' must also be available for this to work, since binman uses that to look up symbols to write into the VPL binary. From c8c9f3108a7b8c3ff391f60b184fa372ae4f32f2 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 7 Jan 2023 14:07:12 -0700 Subject: [PATCH 20/38] binman: Support optional entries Support entries which can be optional depending on their contents. This allows special entry types which appear in the image only when needed. Signed-off-by: Simon Glass --- tools/binman/binman.rst | 22 ++++++++++++++++++++++ tools/binman/control.py | 1 + tools/binman/entry.py | 9 +++++++++ tools/binman/etype/_testing.py | 3 +++ tools/binman/etype/section.py | 7 +++++++ tools/binman/ftest.py | 5 +++++ tools/binman/test/262_absent.dts | 20 ++++++++++++++++++++ 7 files changed, 67 insertions(+) create mode 100644 tools/binman/test/262_absent.dts diff --git a/tools/binman/binman.rst b/tools/binman/binman.rst index ef3e5a6d19..5e3961f225 100644 --- a/tools/binman/binman.rst +++ b/tools/binman/binman.rst @@ -1013,6 +1013,28 @@ For the BSS case, a 'spl-bss-pad' entry arg controls whether it is present. All entry args are provided by the U-Boot Makefile. +Optional entries +---------------- + +Some entries need to exist only if certain conditions are met. For example, an +entry may want to appear in the image only if a file has a particular format. +Obviously the entry must exist in the image description for it to be processed +at all, so a way needs to be found to have the entry remove itself. + +To handle this, when entry.ObtainContents() is called, the entry can call +entry.mark_absent() to mark itself as absent, passing a suitable message as the +reason. + +Any absent entries are dropped immediately after ObtainContents() has been +called on all entries. + +It is not possible for an entry to mark itself absent at any other point in the +processing. It must happen in the ObtainContents() method. + +The effect is as if the entry had never been present at all, since the image +is packed without it and it disappears from the list of entries. + + Compression ----------- diff --git a/tools/binman/control.py b/tools/binman/control.py index 964c6984f9..0722538114 100644 --- a/tools/binman/control.py +++ b/tools/binman/control.py @@ -552,6 +552,7 @@ def ProcessImage(image, update_fdt, write_map, get_contents=True, image.SetAllowMissing(allow_missing) image.SetAllowFakeBlob(allow_fake_blobs) image.GetEntryContents() + image.drop_absent() image.GetEntryOffsets() # We need to pack the entries to figure out where everything diff --git a/tools/binman/entry.py b/tools/binman/entry.py index 1be31a05e0..637aece370 100644 --- a/tools/binman/entry.py +++ b/tools/binman/entry.py @@ -91,6 +91,10 @@ class Entry(object): file, or is a binary file produced from an ELF file auto_write_symbols (bool): True to write ELF symbols into this entry's contents + absent (bool): True if this entry is absent. This can be controlled by + the entry itself, allowing it to vanish in certain circumstances. + An absent entry is removed during processing so that it does not + appear in the map """ fake_dir = None @@ -133,6 +137,7 @@ class Entry(object): self.comp_bintool = None self.elf_fname = None self.auto_write_symbols = auto_write_symbols + self.absent = False @staticmethod def FindEntryClass(etype, expanded): @@ -1281,3 +1286,7 @@ features to produce new behaviours. not_present.append(prop) if not_present: self.Raise(f"'{self.etype}' entry is missing properties: {' '.join(not_present)}") + + def mark_absent(self, msg): + tout.info("Entry '%s' marked absent: %s" % (self._node.path, msg)) + self.absent = True diff --git a/tools/binman/etype/_testing.py b/tools/binman/etype/_testing.py index 6960048781..1c1efb21a4 100644 --- a/tools/binman/etype/_testing.py +++ b/tools/binman/etype/_testing.py @@ -63,6 +63,7 @@ class Entry__testing(Entry): 'bad-update-contents-twice') self.return_contents_later = fdt_util.GetBool(self._node, 'return-contents-later') + self.set_to_absent = fdt_util.GetBool(self._node, 'set-to-absent') # Set to True when the entry is ready to process the FDT. self.process_fdt_ready = False @@ -119,6 +120,8 @@ class Entry__testing(Entry): if self.require_bintool_for_contents: if self.bintool_for_contents is None: self.Raise("Required bintool unusable in ObtainContents()") + if self.set_to_absent: + self.mark_absent('for testing purposes') return True def GetOffsets(self): diff --git a/tools/binman/etype/section.py b/tools/binman/etype/section.py index 305155c846..dcb7a06204 100644 --- a/tools/binman/etype/section.py +++ b/tools/binman/etype/section.py @@ -672,6 +672,9 @@ class Entry_section(Entry): def GetEntryContents(self, skip_entry=None): """Call ObtainContents() for each entry in the section + + Note that this may set entry.absent to True if the entry is not + actually needed """ def _CheckDone(entry): if entry != skip_entry: @@ -716,6 +719,10 @@ class Entry_section(Entry): todo) return True + def drop_absent(self): + """Drop entries which are absent""" + self._entries = {n: e for n, e in self._entries.items() if not e.absent} + def _SetEntryOffsetSize(self, name, offset, size): """Set the offset and size of an entry diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index c3cb32dca2..f47a745f1e 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -6090,6 +6090,11 @@ fdt fdtmap Extract the devicetree blob from the fdtmap sect_data = tools.read_file(sect_fname) self.assertEqual(U_BOOT_DATA, sect_data) + def testAbsent(self): + """Check handling of absent entries""" + data = self._DoReadFile('262_absent.dts') + self.assertEqual(U_BOOT_DATA + U_BOOT_IMG_DATA, data) + if __name__ == "__main__": unittest.main() diff --git a/tools/binman/test/262_absent.dts b/tools/binman/test/262_absent.dts new file mode 100644 index 0000000000..2ab8766c87 --- /dev/null +++ b/tools/binman/test/262_absent.dts @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u-boot { + }; + + _testing { + set-to-absent; + }; + + u-boot-img { + }; + }; +}; From 39f4a85bb2dd5915ebc86921c34da26faa278fec Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 7 Jan 2023 14:07:13 -0700 Subject: [PATCH 21/38] binman: Add a way to check for a valid ELF file Add a function which checks whether data is in ELF format or not. This will be used by binman to check this for entries. Signed-off-by: Simon Glass --- tools/binman/elf.py | 15 +++++++++++++++ tools/binman/elf_test.py | 10 ++++++++++ 2 files changed, 25 insertions(+) diff --git a/tools/binman/elf.py b/tools/binman/elf.py index fe50bf542c..73f318b81d 100644 --- a/tools/binman/elf.py +++ b/tools/binman/elf.py @@ -518,3 +518,18 @@ def read_loadable_segments(data): rend = start + segment['p_filesz'] segments.append((i, segment['p_paddr'], data[start:rend])) return segments, entry + +def is_valid(data): + """Check if some binary data is a valid ELF file + + Args: + data (bytes): Bytes to check + + Returns: + bool: True if a valid Elf file, False if not + """ + try: + DecodeElf(data, 0) + return True + except ELFError: + return False diff --git a/tools/binman/elf_test.py b/tools/binman/elf_test.py index 75b867c2be..082a3e1d28 100644 --- a/tools/binman/elf_test.py +++ b/tools/binman/elf_test.py @@ -348,6 +348,16 @@ class TestElf(unittest.TestCase): finally: elf.ELF_TOOLS = old_val + def test_is_valid(self): + """Test is_valid()""" + self.assertEqual(False, elf.is_valid(b'')) + self.assertEqual(False, elf.is_valid(b'1234')) + + fname = self.ElfTestFile('elf_sections') + data = tools.read_file(fname) + self.assertEqual(True, elf.is_valid(data)) + self.assertEqual(False, elf.is_valid(data[4:])) + if __name__ == '__main__': unittest.main() From 2f80c5ef134c2c339f6d4ad2f9a21aa0ffd465a8 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 7 Jan 2023 14:07:14 -0700 Subject: [PATCH 22/38] binman: Support new op-tee binary format OP-TEE has a format with a binary header that can be used instead of the ELF file. With newer versions of OP-TEE this may be required on some platforms. Add support for this in binman. First, add a method to obtain the ELF sections from an entry, then use that in the FIT support. We then end up with the ability to support both types of OP-TEE files, depending on which one is passed in with the entry argument (TEE=xxx in the U-Boot build). Signed-off-by: Simon Glass --- tools/binman/entries.rst | 37 ++++++++- tools/binman/entry.py | 13 +++ tools/binman/etype/fit.py | 72 +++++++++-------- tools/binman/etype/section.py | 9 +++ tools/binman/etype/tee_os.py | 76 +++++++++++++++++- tools/binman/ftest.py | 83 ++++++++++++++++++++ tools/binman/test/263_tee_os_opt.dts | 22 ++++++ tools/binman/test/264_tee_os_opt_fit.dts | 33 ++++++++ tools/binman/test/265_tee_os_opt_fit_bad.dts | 40 ++++++++++ 9 files changed, 352 insertions(+), 33 deletions(-) create mode 100644 tools/binman/test/263_tee_os_opt.dts create mode 100644 tools/binman/test/264_tee_os_opt_fit.dts create mode 100644 tools/binman/test/265_tee_os_opt_fit_bad.dts diff --git a/tools/binman/entries.rst b/tools/binman/entries.rst index 5b9eb8b82c..f6cc800385 100644 --- a/tools/binman/entries.rst +++ b/tools/binman/entries.rst @@ -1508,12 +1508,47 @@ Entry: tee-os: Entry containing an OP-TEE Trusted OS (TEE) blob Properties / Entry arguments: - tee-os-path: Filename of file to read into entry. This is typically - called tee-pager.bin + called tee.bin or tee.elf This entry holds the run-time firmware, typically started by U-Boot SPL. See the U-Boot README for your architecture or board for how to use it. See https://github.com/OP-TEE/optee_os for more information about OP-TEE. +Note that if the file is in ELF format, it must go in a FIT. In that case, +this entry will mark itself as absent, providing the data only through the +read_elf_segments() method. + +Marking this entry as absent means that it if is used in the wrong context +it can be automatically dropped. Thus it is possible to add an OP-TEE entry +like this:: + + binman { + tee-os { + }; + }; + +and pass either an ELF or plain binary in with -a tee-os-path +and have binman do the right thing: + + - include the entry if tee.bin is provided and it does NOT have the v1 + header + - drop it otherwise + +When used within a FIT, we can do:: + + binman { + fit { + tee-os { + }; + }; + }; + +which will split the ELF into separate nodes for each segment, if an ELF +file is provided (see :ref:`etype_fit`), or produce a single node if the +OP-TEE binary v1 format is provided (see optee_doc_) . + +.. _optee_doc: https://optee.readthedocs.io/en/latest/architecture/core.html#partitioning-of-the-binary + .. _etype_text: diff --git a/tools/binman/entry.py b/tools/binman/entry.py index 637aece370..de51d29589 100644 --- a/tools/binman/entry.py +++ b/tools/binman/entry.py @@ -1290,3 +1290,16 @@ features to produce new behaviours. def mark_absent(self, msg): tout.info("Entry '%s' marked absent: %s" % (self._node.path, msg)) self.absent = True + + def read_elf_segments(self): + """Read segments from an entry that can generate an ELF file + + Returns: + tuple: + list of segments, each: + int: Segment number (0 = first) + int: Start address of segment in memory + bytes: Contents of segment + int: entry address of ELF file + """ + return None diff --git a/tools/binman/etype/fit.py b/tools/binman/etype/fit.py index 8ad4f3a8a8..fea3adcc68 100644 --- a/tools/binman/etype/fit.py +++ b/tools/binman/etype/fit.py @@ -540,41 +540,35 @@ class Entry_fit(Entry_section): else: self.Raise("Generator node requires 'fit,fdt-list' property") - def _gen_split_elf(base_node, node, elf_data, missing): + def _gen_split_elf(base_node, node, segments, entry_addr): """Add nodes for the ELF file, one per group of contiguous segments Args: base_node (Node): Template node from the binman definition node (Node): Node to replace (in the FIT being built) - data (bytes): ELF-format data to process (may be empty) - missing (bool): True if any of the data is missing - + segments (list): list of segments, each: + int: Segment number (0 = first) + int: Start address of segment in memory + bytes: Contents of segment + entry_addr (int): entry address of ELF file """ - # If any pieces are missing, skip this. The missing entries will - # show an error - if not missing: - try: - segments, entry = elf.read_loadable_segments(elf_data) - except ValueError as exc: - self._raise_subnode(node, - f'Failed to read ELF file: {str(exc)}') - for (seq, start, data) in segments: - node_name = node.name[1:].replace('SEQ', str(seq + 1)) - with fsw.add_node(node_name): - loadables.append(node_name) - for pname, prop in node.props.items(): - if not pname.startswith('fit,'): - fsw.property(pname, prop.bytes) - elif pname == 'fit,load': - fsw.property_u32('load', start) - elif pname == 'fit,entry': - if seq == 0: - fsw.property_u32('entry', entry) - elif pname == 'fit,data': - fsw.property('data', bytes(data)) - elif pname != 'fit,operation': - self._raise_subnode( - node, f"Unknown directive '{pname}'") + for (seq, start, data) in segments: + node_name = node.name[1:].replace('SEQ', str(seq + 1)) + with fsw.add_node(node_name): + loadables.append(node_name) + for pname, prop in node.props.items(): + if not pname.startswith('fit,'): + fsw.property(pname, prop.bytes) + elif pname == 'fit,load': + fsw.property_u32('load', start) + elif pname == 'fit,entry': + if seq == 0: + fsw.property_u32('entry', entry_addr) + elif pname == 'fit,data': + fsw.property('data', bytes(data)) + elif pname != 'fit,operation': + self._raise_subnode( + node, f"Unknown directive '{pname}'") def _gen_node(base_node, node, depth, in_images, entry): """Generate nodes from a template @@ -598,6 +592,8 @@ class Entry_fit(Entry_section): depth (int): Current node depth (0 is the base 'fit' node) in_images (bool): True if this is inside the 'images' node, so that 'data' properties should be generated + entry (entry_Section): Entry for the section containing the + contents of this node """ oper = self._get_operation(base_node, node) if oper == OP_GEN_FDT_NODES: @@ -609,10 +605,24 @@ class Entry_fit(Entry_section): missing_list = [] entry.ObtainContents() entry.Pack(0) - data = entry.GetData() entry.CheckMissing(missing_list) - _gen_split_elf(base_node, node, data, bool(missing_list)) + # If any pieces are missing, skip this. The missing entries will + # show an error + if not missing_list: + segs = entry.read_elf_segments() + if segs: + segments, entry_addr = segs + else: + elf_data = entry.GetData() + try: + segments, entry_addr = ( + elf.read_loadable_segments(elf_data)) + except ValueError as exc: + self._raise_subnode( + node, f'Failed to read ELF file: {str(exc)}') + + _gen_split_elf(base_node, node, segments, entry_addr) def _add_node(base_node, depth, node): """Add nodes to the output FIT diff --git a/tools/binman/etype/section.py b/tools/binman/etype/section.py index dcb7a06204..57bfee0b28 100644 --- a/tools/binman/etype/section.py +++ b/tools/binman/etype/section.py @@ -948,3 +948,12 @@ class Entry_section(Entry): super().AddBintools(btools) for entry in self._entries.values(): entry.AddBintools(btools) + + def read_elf_segments(self): + entries = self.GetEntries() + + # If the section only has one entry, see if it can provide ELF segments + if len(entries) == 1: + for entry in entries.values(): + return entry.read_elf_segments() + return None diff --git a/tools/binman/etype/tee_os.py b/tools/binman/etype/tee_os.py index 6ce4b672de..5529727e83 100644 --- a/tools/binman/etype/tee_os.py +++ b/tools/binman/etype/tee_os.py @@ -4,19 +4,93 @@ # Entry-type module for OP-TEE Trusted OS firmware blob # +import struct + from binman.etype.blob_named_by_arg import Entry_blob_named_by_arg +from binman import elf class Entry_tee_os(Entry_blob_named_by_arg): """Entry containing an OP-TEE Trusted OS (TEE) blob Properties / Entry arguments: - tee-os-path: Filename of file to read into entry. This is typically - called tee-pager.bin + called tee.bin or tee.elf This entry holds the run-time firmware, typically started by U-Boot SPL. See the U-Boot README for your architecture or board for how to use it. See https://github.com/OP-TEE/optee_os for more information about OP-TEE. + + Note that if the file is in ELF format, it must go in a FIT. In that case, + this entry will mark itself as absent, providing the data only through the + read_elf_segments() method. + + Marking this entry as absent means that it if is used in the wrong context + it can be automatically dropped. Thus it is possible to add an OP-TEE entry + like this:: + + binman { + tee-os { + }; + }; + + and pass either an ELF or plain binary in with -a tee-os-path + and have binman do the right thing: + + - include the entry if tee.bin is provided and it does NOT have the v1 + header + - drop it otherwise + + When used within a FIT, we can do:: + + binman { + fit { + tee-os { + }; + }; + }; + + which will split the ELF into separate nodes for each segment, if an ELF + file is provided (see :ref:`etype_fit`), or produce a single node if the + OP-TEE binary v1 format is provided (see optee_doc_) . + + .. _optee_doc: https://optee.readthedocs.io/en/latest/architecture/core.html#partitioning-of-the-binary """ def __init__(self, section, etype, node): super().__init__(section, etype, node, 'tee-os') self.external = True + + @staticmethod + def is_optee_bin_v1(data): + return len(data) >= 8 and data[0:5] == b'OPTE\x01' + + def ObtainContents(self, fake_size=0): + result = super().ObtainContents(fake_size) + if not self.missing: + # If using the flat binary (without the OP-TEE header), then it is + # just included as a blob. But if it is an ELF or usees the v1 + # binary header, then the FIT implementation will call + # read_elf_segments() to get the segment information + if elf.is_valid(self.data): + self.mark_absent('uses Elf format which must be in a FIT') + elif self.is_optee_bin_v1(self.data): + # The FIT implementation will call read_elf_segments() to get + # the segment information + self.mark_absent('uses v1 format which must be in a FIT') + return result + + def read_elf_segments(self): + data = self.GetData() + if self.is_optee_bin_v1(data): + # OP-TEE v1 format (tee.bin) + init_sz, start_hi, start_lo, _, paged_sz = ( + struct.unpack_from('<5I', data, 0x8)) + if paged_sz != 0: + self.Raise("OP-TEE paged mode not supported") + e_entry = (start_hi << 32) + start_lo + p_addr = e_entry + p_data = data[0x1c:] + if len(p_data) != init_sz: + self.Raise("Invalid OP-TEE file: size mismatch (expected %#x, have %#x)" % + (init_sz, len(p_data))) + return [[0, p_addr, p_data]], e_entry + return None diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index f47a745f1e..f893050e70 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -112,6 +112,8 @@ REPACK_DTB_PROPS = ['orig-offset', 'orig-size'] # Supported compression bintools COMP_BINTOOLS = ['bzip2', 'gzip', 'lz4', 'lzma_alone', 'lzop', 'xz', 'zstd'] +TEE_ADDR = 0x5678 + class TestFunctional(unittest.TestCase): """Functional tests for binman @@ -219,6 +221,9 @@ class TestFunctional(unittest.TestCase): TestFunctional._MakeInputFile('tee.elf', tools.read_file(cls.ElfTestFile('elf_sections'))) + # Newer OP_TEE file in v1 binary format + cls.make_tee_bin('tee.bin') + cls.comp_bintools = {} for name in COMP_BINTOOLS: cls.comp_bintools[name] = bintool.Bintool.create(name) @@ -644,6 +649,14 @@ class TestFunctional(unittest.TestCase): def ElfTestFile(cls, fname): return os.path.join(cls._elf_testdir, fname) + @classmethod + def make_tee_bin(cls, fname, paged_sz=0, extra_data=b''): + init_sz, start_hi, start_lo, dummy = (len(U_BOOT_DATA), 0, TEE_ADDR, 0) + data = b'OPTE\x01xxx' + struct.pack('<5I', init_sz, start_hi, start_lo, + dummy, paged_sz) + U_BOOT_DATA + data += extra_data + TestFunctional._MakeInputFile(fname, data) + def AssertInList(self, grep_list, target): """Assert that at least one of a list of things is in a target @@ -6095,6 +6108,76 @@ fdt fdtmap Extract the devicetree blob from the fdtmap data = self._DoReadFile('262_absent.dts') self.assertEqual(U_BOOT_DATA + U_BOOT_IMG_DATA, data) + def testPackTeeOsOptional(self): + """Test that an image with an optional TEE binary can be created""" + entry_args = { + 'tee-os-path': 'tee.elf', + } + data = self._DoReadFileDtb('263_tee_os_opt.dts', + entry_args=entry_args)[0] + self.assertEqual(U_BOOT_DATA + U_BOOT_IMG_DATA, data) + + def checkFitTee(self, dts, tee_fname): + """Check that a tee-os entry works and returns data + + Args: + dts (str): Device tree filename to use + tee_fname (str): filename containing tee-os + + Returns: + bytes: Image contents + """ + if not elf.ELF_TOOLS: + self.skipTest('Python elftools not available') + entry_args = { + 'of-list': 'test-fdt1 test-fdt2', + 'default-dt': 'test-fdt2', + 'tee-os-path': tee_fname, + } + test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR) + data = self._DoReadFileDtb(dts, entry_args=entry_args, + extra_indirs=[test_subdir])[0] + return data + + def testFitTeeOsOptionalFit(self): + """Test an image with a FIT with an optional OP-TEE binary""" + data = self.checkFitTee('264_tee_os_opt_fit.dts', 'tee.bin') + + # There should be only one node, holding the data set up in SetUpClass() + # for tee.bin + dtb = fdt.Fdt.FromData(data) + dtb.Scan() + node = dtb.GetNode('/images/tee-1') + self.assertEqual(TEE_ADDR, + fdt_util.fdt32_to_cpu(node.props['load'].value)) + self.assertEqual(TEE_ADDR, + fdt_util.fdt32_to_cpu(node.props['entry'].value)) + self.assertEqual(U_BOOT_DATA, node.props['data'].bytes) + + def testFitTeeOsOptionalFitBad(self): + """Test an image with a FIT with an optional OP-TEE binary""" + with self.assertRaises(ValueError) as exc: + self.checkFitTee('265_tee_os_opt_fit_bad.dts', 'tee.bin') + self.assertIn( + "Node '/binman/fit': subnode 'images/@tee-SEQ': Failed to read ELF file: Magic number does not match", + str(exc.exception)) + + def testFitTeeOsBad(self): + """Test an OP-TEE binary with wrong formats""" + self.make_tee_bin('tee.bad1', 123) + with self.assertRaises(ValueError) as exc: + self.checkFitTee('264_tee_os_opt_fit.dts', 'tee.bad1') + self.assertIn( + "Node '/binman/fit/images/@tee-SEQ/tee-os': OP-TEE paged mode not supported", + str(exc.exception)) + + self.make_tee_bin('tee.bad2', 0, b'extra data') + with self.assertRaises(ValueError) as exc: + self.checkFitTee('264_tee_os_opt_fit.dts', 'tee.bad2') + self.assertIn( + "Node '/binman/fit/images/@tee-SEQ/tee-os': Invalid OP-TEE file: size mismatch (expected 0x4, have 0xe)", + str(exc.exception)) + if __name__ == "__main__": unittest.main() diff --git a/tools/binman/test/263_tee_os_opt.dts b/tools/binman/test/263_tee_os_opt.dts new file mode 100644 index 0000000000..2e4ec24ac2 --- /dev/null +++ b/tools/binman/test/263_tee_os_opt.dts @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u-boot { + }; + tee-os { + /* + * this results in nothing being added since only the + * .bin format is supported by this etype, unless it is + * part of a FIT + */ + }; + u-boot-img { + }; + }; +}; diff --git a/tools/binman/test/264_tee_os_opt_fit.dts b/tools/binman/test/264_tee_os_opt_fit.dts new file mode 100644 index 0000000000..ae44b433ed --- /dev/null +++ b/tools/binman/test/264_tee_os_opt_fit.dts @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + fit { + description = "test-desc"; + #address-cells = <1>; + fit,fdt-list = "of-list"; + + images { + @tee-SEQ { + fit,operation = "split-elf"; + description = "TEE"; + type = "tee"; + arch = "arm64"; + os = "tee"; + compression = "none"; + fit,load; + fit,entry; + fit,data; + + tee-os { + }; + }; + }; + }; + }; +}; diff --git a/tools/binman/test/265_tee_os_opt_fit_bad.dts b/tools/binman/test/265_tee_os_opt_fit_bad.dts new file mode 100644 index 0000000000..7fa363cc19 --- /dev/null +++ b/tools/binman/test/265_tee_os_opt_fit_bad.dts @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + fit { + description = "test-desc"; + #address-cells = <1>; + fit,fdt-list = "of-list"; + + images { + @tee-SEQ { + fit,operation = "split-elf"; + description = "TEE"; + type = "tee"; + arch = "arm64"; + os = "tee"; + compression = "none"; + fit,load; + fit,entry; + fit,data; + + tee-os { + }; + + /* + * mess up the ELF data by adding + * another bit of data at the end + */ + u-boot { + }; + }; + }; + }; + }; +}; From 67a050170846b6cb751c7162c3a3bdb898261660 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 7 Jan 2023 14:07:15 -0700 Subject: [PATCH 23/38] binman: Support optional external blobs Some blobs are actually not necessary for the board to work correctly. Add a property to allow this to be indicated. Missing optional blobs do not cause a build failure. Signed-off-by: Simon Glass --- tools/binman/binman.rst | 9 +++++++++ tools/binman/control.py | 11 +++++++++++ tools/binman/entry.py | 25 +++++++++++++++++++++---- tools/binman/etype/blob.py | 2 +- tools/binman/etype/fit.py | 11 ++++++----- tools/binman/etype/section.py | 14 +++++++++++++- tools/binman/ftest.py | 10 ++++++++++ tools/binman/test/266_blob_ext_opt.dts | 21 +++++++++++++++++++++ 8 files changed, 92 insertions(+), 11 deletions(-) create mode 100644 tools/binman/test/266_blob_ext_opt.dts diff --git a/tools/binman/binman.rst b/tools/binman/binman.rst index 5e3961f225..bfe300a39c 100644 --- a/tools/binman/binman.rst +++ b/tools/binman/binman.rst @@ -690,6 +690,15 @@ no-expanded: `no-expanded` property disables this just for a single entry. Put the `no-expanded` boolean property in the node to select this behaviour. +optional: + External blobs are normally required to be present for the image to be + built (but see `External blobs`_). This properly allows an entry to be + optional, so that when it is cannot be found, this problem is ignored and + an empty file is used for this blob. This should be used only when the blob + is entirely optional and is not needed for correct operation of the image. + Note that missing, optional blobs do not produce a non-zero exit code from + binman, although it does show a warning about the missing external blob. + The attributes supported for images and sections are described below. Several are similar to those for entries. diff --git a/tools/binman/control.py b/tools/binman/control.py index 0722538114..e64740094f 100644 --- a/tools/binman/control.py +++ b/tools/binman/control.py @@ -594,12 +594,14 @@ def ProcessImage(image, update_fdt, write_map, get_contents=True, image.BuildImage() if write_map: image.WriteMap() + missing_list = [] image.CheckMissing(missing_list) if missing_list: tout.warning("Image '%s' is missing external blobs and is non-functional: %s" % (image.name, ' '.join([e.name for e in missing_list]))) _ShowHelpForMissingBlobs(missing_list) + faked_list = [] image.CheckFakedBlobs(faked_list) if faked_list: @@ -607,6 +609,15 @@ def ProcessImage(image, update_fdt, write_map, get_contents=True, "Image '%s' has faked external blobs and is non-functional: %s" % (image.name, ' '.join([os.path.basename(e.GetDefaultFilename()) for e in faked_list]))) + + optional_list = [] + image.CheckOptional(optional_list) + if optional_list: + tout.warning( + "Image '%s' is missing external blobs but is still functional: %s" % + (image.name, ' '.join([e.name for e in optional_list]))) + _ShowHelpForMissingBlobs(optional_list) + missing_bintool_list = [] image.check_missing_bintools(missing_bintool_list) if missing_bintool_list: diff --git a/tools/binman/entry.py b/tools/binman/entry.py index de51d29589..d73f301340 100644 --- a/tools/binman/entry.py +++ b/tools/binman/entry.py @@ -73,7 +73,9 @@ class Entry(object): compress: Compression algoithm used (e.g. 'lz4'), 'none' if none orig_offset: Original offset value read from node orig_size: Original size value read from node - missing: True if this entry is missing its contents + missing: True if this entry is missing its contents. Note that if it is + optional, this entry will not appear in the list generated by + entry.CheckMissing() since it is considered OK for it to be missing. allow_missing: Allow children of this entry to be missing (used by subclasses such as Entry_section) allow_fake: Allow creating a dummy fake file if the blob file is not @@ -95,6 +97,7 @@ class Entry(object): the entry itself, allowing it to vanish in certain circumstances. An absent entry is removed during processing so that it does not appear in the map + optional (bool): True if this entry contains an optional external blob """ fake_dir = None @@ -138,6 +141,7 @@ class Entry(object): self.elf_fname = None self.auto_write_symbols = auto_write_symbols self.absent = False + self.optional = False @staticmethod def FindEntryClass(etype, expanded): @@ -289,6 +293,7 @@ class Entry(object): self.offset_unset = fdt_util.GetBool(self._node, 'offset-unset') self.extend_size = fdt_util.GetBool(self._node, 'extend-size') self.missing_msg = fdt_util.GetString(self._node, 'missing-msg') + self.optional = fdt_util.GetBool(self._node, 'optional') # This is only supported by blobs and sections at present self.compress = fdt_util.GetString(self._node, 'compress', 'none') @@ -1039,14 +1044,15 @@ features to produce new behaviours. self.allow_fake = allow_fake def CheckMissing(self, missing_list): - """Check if any entries in this section have missing external blobs + """Check if the entry has missing external blobs - If there are missing blobs, the entries are added to the list + If there are missing (non-optional) blobs, the entries are added to the + list Args: missing_list: List of Entry objects to be added to """ - if self.missing: + if self.missing and not self.optional: missing_list.append(self) def check_fake_fname(self, fname, size=0): @@ -1085,6 +1091,17 @@ features to produce new behaviours. # This is meaningless for anything other than blobs pass + def CheckOptional(self, optional_list): + """Check if the entry has missing but optional external blobs + + If there are missing (optional) blobs, the entries are added to the list + + Args: + optional_list (list): List of Entry objects to be added to + """ + if self.missing and self.optional: + optional_list.append(self) + def GetAllowMissing(self): """Get whether a section allows missing external blobs diff --git a/tools/binman/etype/blob.py b/tools/binman/etype/blob.py index a50a806890..70dea7158e 100644 --- a/tools/binman/etype/blob.py +++ b/tools/binman/etype/blob.py @@ -39,7 +39,7 @@ class Entry_blob(Entry): def ObtainContents(self, fake_size=0): self._filename = self.GetDefaultFilename() self._pathname = tools.get_input_filename(self._filename, - self.external and self.section.GetAllowMissing()) + self.external and (self.optional or self.section.GetAllowMissing())) # Allow the file to be missing if not self._pathname: self._pathname, faked = self.check_fake_fname(self._filename, diff --git a/tools/binman/etype/fit.py b/tools/binman/etype/fit.py index fea3adcc68..f0e3fd1a09 100644 --- a/tools/binman/etype/fit.py +++ b/tools/binman/etype/fit.py @@ -392,8 +392,8 @@ class Entry_fit(Entry_section): _add_entries(self._node, 0, self._node) - # Keep a copy of all entries, including generator entries, since these - # removed from self._entries later. + # Keep a copy of all entries, including generator entries, since those + # are removed from self._entries later. self._priv_entries = dict(self._entries) def BuildSectionData(self, required): @@ -602,14 +602,15 @@ class Entry_fit(Entry_section): # Entry_section.ObtainContents() either returns True or # raises an exception. data = None - missing_list = [] + missing_opt_list = [] entry.ObtainContents() entry.Pack(0) - entry.CheckMissing(missing_list) + entry.CheckMissing(missing_opt_list) + entry.CheckOptional(missing_opt_list) # If any pieces are missing, skip this. The missing entries will # show an error - if not missing_list: + if not missing_opt_list: segs = entry.read_elf_segments() if segs: segments, entry_addr = segs diff --git a/tools/binman/etype/section.py b/tools/binman/etype/section.py index 57bfee0b28..44dafaf726 100644 --- a/tools/binman/etype/section.py +++ b/tools/binman/etype/section.py @@ -863,7 +863,8 @@ class Entry_section(Entry): def CheckMissing(self, missing_list): """Check if any entries in this section have missing external blobs - If there are missing blobs, the entries are added to the list + If there are missing (non-optional) blobs, the entries are added to the + list Args: missing_list: List of Entry objects to be added to @@ -882,6 +883,17 @@ class Entry_section(Entry): for entry in self._entries.values(): entry.CheckFakedBlobs(faked_blobs_list) + def CheckOptional(self, optional_list): + """Check the section for missing but optional external blobs + + If there are missing (optional) blobs, the entries are added to the list + + Args: + optional_list (list): List of Entry objects to be added to + """ + for entry in self._entries.values(): + entry.CheckOptional(optional_list) + def check_missing_bintools(self, missing_list): """Check if any entries in this section have missing bintools diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index f893050e70..330e8e1ccb 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -6178,6 +6178,16 @@ fdt fdtmap Extract the devicetree blob from the fdtmap "Node '/binman/fit/images/@tee-SEQ/tee-os': Invalid OP-TEE file: size mismatch (expected 0x4, have 0xe)", str(exc.exception)) + def testExtblobOptional(self): + """Test an image with an external blob that is optional""" + with test_util.capture_sys_output() as (stdout, stderr): + data = self._DoReadFile('266_blob_ext_opt.dts') + self.assertEqual(REFCODE_DATA, data) + err = stderr.getvalue() + self.assertRegex( + err, + "Image '.*' is missing external blobs but is still functional: missing") + if __name__ == "__main__": unittest.main() diff --git a/tools/binman/test/266_blob_ext_opt.dts b/tools/binman/test/266_blob_ext_opt.dts new file mode 100644 index 0000000000..717153152c --- /dev/null +++ b/tools/binman/test/266_blob_ext_opt.dts @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + ok { + type = "blob-ext"; + filename = "refcode.bin"; + }; + + missing { + type = "blob-ext"; + filename = "missing.bin"; + optional; + }; + }; +}; From c4db0720ad104444886ff4696bfbf2891f7777a5 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 7 Jan 2023 14:07:16 -0700 Subject: [PATCH 24/38] rockchip: evb-rk3288: Drop raw-image support This boards uses SPL_FIT so does not need to support loading a raw image. Drop it to avoid binman trying to insert a symbol which has no value. Signed-off-by: Simon Glass --- configs/evb-rk3288_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/configs/evb-rk3288_defconfig b/configs/evb-rk3288_defconfig index fcfb6aac1e..5c6b1d5d55 100644 --- a/configs/evb-rk3288_defconfig +++ b/configs/evb-rk3288_defconfig @@ -29,6 +29,7 @@ CONFIG_SILENT_CONSOLE=y CONFIG_DISPLAY_BOARDINFO_LATE=y CONFIG_SPL_PAD_TO=0x7f8000 CONFIG_SPL_NO_BSS_LIMIT=y +# CONFIG_SPL_RAW_IMAGE_SUPPORT is not set # CONFIG_SPL_SHARES_INIT_SP_ADDR is not set CONFIG_SPL_STACK=0xff718000 CONFIG_SPL_STACK_R=y From 4170dd9ec023b3b4b7375755c4dbb484a4d00443 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 7 Jan 2023 14:07:17 -0700 Subject: [PATCH 25/38] rockchip: Use multiple-images for rk3399 Enable multiple-images so we can generate more than one image. Also add a comment for the end of the #if block. Signed-off-by: Simon Glass --- arch/arm/dts/rk3399-u-boot.dtsi | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm/dts/rk3399-u-boot.dtsi b/arch/arm/dts/rk3399-u-boot.dtsi index 3c1a15fe51..8a0b1803f3 100644 --- a/arch/arm/dts/rk3399-u-boot.dtsi +++ b/arch/arm/dts/rk3399-u-boot.dtsi @@ -62,6 +62,7 @@ #if defined(CONFIG_ROCKCHIP_SPI_IMAGE) && defined(CONFIG_HAS_ROM) &binman { + multiple-images; rom { filename = "u-boot.rom"; size = <0x400000>; @@ -82,7 +83,7 @@ }; }; }; -#endif +#endif /* CONFIG_ROCKCHIP_SPI_IMAGE && CONFIG_HAS_ROM */ &cru { u-boot,dm-pre-reloc; From e0c0efff2a02bf153d6afced3a57107790159ae2 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 7 Jan 2023 14:07:18 -0700 Subject: [PATCH 26/38] rockchip: Support building the all output files in binman Add the required binman images to replace the Makefile rules which are currently used. This includes subsuming: - tpl/u-boot-tpl-rockchip.bin if TPL is enabled - idbloader.img if either or both of SPL and TPL are enabled - u-boot.itb if SPL_FIT is enabled - u-boot-rockchip.bin if SPL is used, either using u-boot.itb when SPL_FIT is enabled or u-boot.img when it isn't Note that the intermediate files are dropped with binman, since it producing everything in one pass. This means that tpl/u-boot-tpl-rockchip.bin is not created, for example. Note that for some 32-bit rk3288 boards, rockchip-optee.dtsi is included. Signed-off-by: Simon Glass --- arch/arm/dts/px30-ringneck-haikou-u-boot.dtsi | 2 +- arch/arm/dts/rk3399-puma-haikou-u-boot.dtsi | 4 +- arch/arm/dts/rockchip-u-boot.dtsi | 78 +++++++++++++++++-- 3 files changed, 75 insertions(+), 9 deletions(-) diff --git a/arch/arm/dts/px30-ringneck-haikou-u-boot.dtsi b/arch/arm/dts/px30-ringneck-haikou-u-boot.dtsi index e8a34c7c1e..1325e0cb05 100644 --- a/arch/arm/dts/px30-ringneck-haikou-u-boot.dtsi +++ b/arch/arm/dts/px30-ringneck-haikou-u-boot.dtsi @@ -17,7 +17,7 @@ &binman { simple-bin { - blob { + fit { offset = <((CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR - 64) * 512)>; }; }; diff --git a/arch/arm/dts/rk3399-puma-haikou-u-boot.dtsi b/arch/arm/dts/rk3399-puma-haikou-u-boot.dtsi index d2349ae90e..088861dbf6 100644 --- a/arch/arm/dts/rk3399-puma-haikou-u-boot.dtsi +++ b/arch/arm/dts/rk3399-puma-haikou-u-boot.dtsi @@ -46,14 +46,14 @@ &binman { simple-bin { - blob { + fit { offset = <((CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR - 64) * 512)>; }; }; #ifdef CONFIG_ROCKCHIP_SPI_IMAGE simple-bin-spi { - blob { + fit { /* same as u-boot,spl-payload-offset */ offset = <0x80000>; }; diff --git a/arch/arm/dts/rockchip-u-boot.dtsi b/arch/arm/dts/rockchip-u-boot.dtsi index fa094b0039..234fc5df43 100644 --- a/arch/arm/dts/rockchip-u-boot.dtsi +++ b/arch/arm/dts/rockchip-u-boot.dtsi @@ -30,14 +30,79 @@ }; }; -#ifdef CONFIG_ARM64 - blob { +#if defined(CONFIG_SPL_FIT) && defined(CONFIG_ARM64) + fit: fit { + description = "FIT image for U-Boot with bl31 (TF-A)"; + #address-cells = <1>; + fit,fdt-list = "of-list"; filename = "u-boot.itb"; + fit,external-offset = ; + offset = ; + images { + u-boot { + description = "U-Boot (64-bit)"; + type = "standalone"; + os = "U-Boot"; + arch = "arm64"; + compression = "none"; + load = ; + entry = ; + u-boot-nodtb { + }; + }; + + @atf-SEQ { + fit,operation = "split-elf"; + description = "ARM Trusted Firmware"; + type = "firmware"; + arch = "arm64"; + os = "arm-trusted-firmware"; + compression = "none"; + fit,load; + fit,entry; + fit,data; + + atf-bl31 { + }; + }; + @tee-SEQ { + fit,operation = "split-elf"; + description = "TEE"; + type = "tee"; + arch = "arm64"; + os = "tee"; + compression = "none"; + fit,load; + fit,entry; + fit,data; + + tee-os { + optional; + }; + }; + + @fdt-SEQ { + description = "fdt-NAME"; + compression = "none"; + type = "flat_dt"; + }; + }; + + configurations { + default = "@config-DEFAULT-SEQ"; + @config-SEQ { + description = "NAME.dtb"; + fdt = "fdt-SEQ"; + firmware = "u-boot"; + fit,loadables; + }; + }; + }; #else u-boot-img { -#endif offset = ; }; +#endif }; #ifdef CONFIG_ROCKCHIP_SPI_IMAGE @@ -59,7 +124,8 @@ }; #ifdef CONFIG_ARM64 - blob { + fit { + type = "blob"; filename = "u-boot.itb"; #else u-boot-img { @@ -68,6 +134,6 @@ offset = ; }; }; -#endif +#endif /* CONFIG_ROCKCHIP_SPI_IMAGE */ }; -#endif +#endif /* CONFIG_SPL */ From 31f35e83b18fe8c6b1bc6f1501b575c7cf764939 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 7 Jan 2023 14:07:19 -0700 Subject: [PATCH 27/38] rockchip: Convert all boards to use binman Instead of the bash script, use binman to generate the FIT for arm64. For 32-bit boards, use binman for all images, dropping the intermediate files. With this change, only Zynq is now using SPL_FIT_GENERATOR so update the Kconfig rule accordingly. Clean up the Makefile to the extent possible. Unfortunately, two boards do not use SPL_FRAMEWORK so don't enable the u-boot.img rule: evb-rk3036 kylin-rk3036 So a small remnant remains. Signed-off-by: Simon Glass --- Makefile | 8 +------- boot/Kconfig | 2 +- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index a4a14d5d35..a841ebe008 100644 --- a/Makefile +++ b/Makefile @@ -1006,14 +1006,9 @@ ifeq ($(CONFIG_INIT_SP_RELATIVE)$(CONFIG_OF_SEPARATE),yy) INPUTS-y += init_sp_bss_offset_check endif -ifeq ($(CONFIG_ARCH_ROCKCHIP)$(CONFIG_SPL),yy) -# Binman image dependencies -ifeq ($(CONFIG_ARM64),y) -INPUTS-y += u-boot.itb -else +ifeq ($(CONFIG_ARCH_ROCKCHIP)_$(CONFIG_SPL_FRAMEWORK),y_) INPUTS-y += u-boot.img endif -endif INPUTS-$(CONFIG_X86) += u-boot-x86-start16.bin u-boot-x86-reset16.bin \ $(if $(CONFIG_SPL_X86_16BIT_INIT),spl/u-boot-spl.bin) \ @@ -1477,7 +1472,6 @@ OBJCOPYFLAGS_u-boot-with-spl.bin = -I binary -O binary \ u-boot-with-spl.bin: $(SPL_IMAGE) $(SPL_PAYLOAD) FORCE $(call if_changed,pad_cat) - ifeq ($(CONFIG_ARCH_LPC32XX)$(CONFIG_SPL),yy) MKIMAGEFLAGS_lpc32xx-spl.img = -T lpc32xximage -a $(CONFIG_SPL_TEXT_BASE) diff --git a/boot/Kconfig b/boot/Kconfig index 36ccbf6b54..219125ebbb 100644 --- a/boot/Kconfig +++ b/boot/Kconfig @@ -282,7 +282,7 @@ config SPL_FIT_SOURCE config USE_SPL_FIT_GENERATOR bool "Use a script to generate the .its script" depends on SPL_FIT - default y if !ARCH_SUNXI && !RISCV + default y if SPL_FIT && ARCH_ZYNQMP config SPL_FIT_GENERATOR string ".its file generator script for U-Boot FIT image" From 12c3e948eeab0531c7eec813969e089f9f252891 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 7 Jan 2023 14:07:20 -0700 Subject: [PATCH 28/38] rockchip: Drop the FIT generator script This is not used anymore. Drop it. Signed-off-by: Simon Glass --- Makefile | 3 - arch/arm/mach-rockchip/make_fit_atf.py | 267 ------------------------- boot/Kconfig | 1 - 3 files changed, 271 deletions(-) delete mode 100755 arch/arm/mach-rockchip/make_fit_atf.py diff --git a/Makefile b/Makefile index a841ebe008..eb354c045c 100644 --- a/Makefile +++ b/Makefile @@ -1365,9 +1365,6 @@ $(U_BOOT_ITS): $(subst ",,$(CONFIG_SPL_FIT_SOURCE)) else ifneq ($(CONFIG_USE_SPL_FIT_GENERATOR),) U_BOOT_ITS := u-boot.its -ifeq ($(CONFIG_SPL_FIT_GENERATOR),"arch/arm/mach-rockchip/make_fit_atf.py") -U_BOOT_ITS_DEPS += u-boot -endif $(U_BOOT_ITS): $(U_BOOT_ITS_DEPS) FORCE $(srctree)/$(CONFIG_SPL_FIT_GENERATOR) \ $(patsubst %,arch/$(ARCH)/dts/%.dtb,$(subst ",,$(CONFIG_OF_LIST))) > $@ diff --git a/arch/arm/mach-rockchip/make_fit_atf.py b/arch/arm/mach-rockchip/make_fit_atf.py deleted file mode 100755 index 08cfe9f51e..0000000000 --- a/arch/arm/mach-rockchip/make_fit_atf.py +++ /dev/null @@ -1,267 +0,0 @@ -#!/usr/bin/env python3 -""" -# SPDX-License-Identifier: GPL-2.0+ -# -# A script to generate FIT image source for rockchip boards -# with ARM Trusted Firmware -# and multiple device trees (given on the command line) -# -# usage: $0 [ [; - - images { -""" - -DT_UBOOT = """ - uboot { - description = "U-Boot (64-bit)"; - data = /incbin/("u-boot-nodtb.bin"); - type = "standalone"; - os = "U-Boot"; - arch = "arm64"; - compression = "none"; - load = <0x%08x>; - }; - -""" - -DT_IMAGES_NODE_END = """ }; - -""" - -DT_END = "};" - -def append_bl31_node(file, atf_index, phy_addr, elf_entry): - # Append BL31 DT node to input FIT dts file. - data = 'bl31_0x%08x.bin' % phy_addr - file.write('\t\tatf_%d {\n' % atf_index) - file.write('\t\t\tdescription = \"ARM Trusted Firmware\";\n') - file.write('\t\t\tdata = /incbin/("%s");\n' % data) - file.write('\t\t\ttype = "firmware";\n') - file.write('\t\t\tarch = "arm64";\n') - file.write('\t\t\tos = "arm-trusted-firmware";\n') - file.write('\t\t\tcompression = "none";\n') - file.write('\t\t\tload = <0x%08x>;\n' % phy_addr) - if atf_index == 1: - file.write('\t\t\tentry = <0x%08x>;\n' % elf_entry) - file.write('\t\t};\n') - file.write('\n') - -def append_tee_node(file, atf_index, phy_addr, elf_entry): - # Append TEE DT node to input FIT dts file. - data = 'tee_0x%08x.bin' % phy_addr - file.write('\t\tatf_%d {\n' % atf_index) - file.write('\t\t\tdescription = \"TEE\";\n') - file.write('\t\t\tdata = /incbin/("%s");\n' % data) - file.write('\t\t\ttype = "tee";\n') - file.write('\t\t\tarch = "arm64";\n') - file.write('\t\t\tos = "tee";\n') - file.write('\t\t\tcompression = "none";\n') - file.write('\t\t\tload = <0x%08x>;\n' % phy_addr) - file.write('\t\t\tentry = <0x%08x>;\n' % elf_entry) - file.write('\t\t};\n') - file.write('\n') - -def append_fdt_node(file, dtbs): - # Append FDT nodes. - cnt = 1 - for dtb in dtbs: - dtname = os.path.basename(dtb) - file.write('\t\tfdt_%d {\n' % cnt) - file.write('\t\t\tdescription = "%s";\n' % dtname) - file.write('\t\t\tdata = /incbin/("%s");\n' % dtb) - file.write('\t\t\ttype = "flat_dt";\n') - file.write('\t\t\tcompression = "none";\n') - file.write('\t\t};\n') - file.write('\n') - cnt = cnt + 1 - -def append_conf_section(file, cnt, dtname, segments): - file.write('\t\tconfig_%d {\n' % cnt) - file.write('\t\t\tdescription = "%s";\n' % dtname) - file.write('\t\t\tfirmware = "atf_1";\n') - file.write('\t\t\tloadables = "uboot"') - if segments > 1: - file.write(',') - for i in range(1, segments): - file.write('"atf_%d"' % (i + 1)) - if i != (segments - 1): - file.write(',') - else: - file.write(';\n') - if segments <= 1: - file.write(';\n') - file.write('\t\t\tfdt = "fdt_%d";\n' % cnt) - file.write('\t\t};\n') - file.write('\n') - -def append_conf_node(file, dtbs, segments): - # Append configeration nodes. - cnt = 1 - file.write('\tconfigurations {\n') - file.write('\t\tdefault = "config_1";\n') - for dtb in dtbs: - dtname = os.path.basename(dtb) - append_conf_section(file, cnt, dtname, segments) - cnt = cnt + 1 - file.write('\t};\n') - file.write('\n') - -def generate_atf_fit_dts_uboot(fit_file, uboot_file_name): - segments = unpack_elf(uboot_file_name) - if len(segments) != 1: - raise ValueError("Invalid u-boot ELF image '%s'" % uboot_file_name) - index, entry, p_paddr, data = segments[0] - fit_file.write(DT_UBOOT % p_paddr) - -def generate_atf_fit_dts_bl31(fit_file, bl31_file_name, tee_file_name, dtbs_file_name): - segments = unpack_elf(bl31_file_name) - for index, entry, paddr, data in segments: - append_bl31_node(fit_file, index + 1, paddr, entry) - num_segments = len(segments) - - if tee_file_name: - tee_segments = unpack_tee_file(tee_file_name) - for index, entry, paddr, data in tee_segments: - append_tee_node(fit_file, num_segments + index + 1, paddr, entry) - num_segments = num_segments + len(tee_segments) - - append_fdt_node(fit_file, dtbs_file_name) - fit_file.write(DT_IMAGES_NODE_END) - append_conf_node(fit_file, dtbs_file_name, num_segments) - -def generate_atf_fit_dts(fit_file_name, bl31_file_name, tee_file_name, uboot_file_name, dtbs_file_name): - # Generate FIT script for ATF image. - if fit_file_name != sys.stdout: - fit_file = open(fit_file_name, "wb") - else: - fit_file = sys.stdout - - fit_file.write(DT_HEADER) - generate_atf_fit_dts_uboot(fit_file, uboot_file_name) - generate_atf_fit_dts_bl31(fit_file, bl31_file_name, tee_file_name, dtbs_file_name) - fit_file.write(DT_END) - - if fit_file_name != sys.stdout: - fit_file.close() - -def generate_atf_binary(bl31_file_name): - for index, entry, paddr, data in unpack_elf(bl31_file_name): - file_name = 'bl31_0x%08x.bin' % paddr - with open(file_name, "wb") as atf: - atf.write(data) - -def generate_tee_binary(tee_file_name): - if tee_file_name: - for index, entry, paddr, data in unpack_tee_file(tee_file_name): - file_name = 'tee_0x%08x.bin' % paddr - with open(file_name, "wb") as atf: - atf.write(data) - -def unpack_elf(filename): - with open(filename, 'rb') as file: - elf = file.read() - if elf[0:7] != b'\x7fELF\x02\x01\x01' or elf[18:20] != b'\xb7\x00': - raise ValueError("Invalid arm64 ELF file '%s'" % filename) - - e_entry, e_phoff = struct.unpack_from('<2Q', elf, 0x18) - e_phentsize, e_phnum = struct.unpack_from('<2H', elf, 0x36) - segments = [] - - for index in range(e_phnum): - offset = e_phoff + e_phentsize * index - p_type, p_flags, p_offset = struct.unpack_from(' 0: - p_data = elf[p_offset:p_offset + p_filesz] - segments.append((index, e_entry, p_paddr, p_data)) - return segments - -def unpack_tee_file(filename): - if filename.endswith('.elf'): - return unpack_elf(filename) - with open(filename, 'rb') as file: - bin = file.read() - segments = [] - if bin[0:5] == b'OPTE\x01': - # OP-TEE v1 format (tee.bin) - init_sz, start_hi, start_lo, _, paged_sz = struct.unpack_from('<5I', - bin, - 0x8) - if paged_sz != 0: - raise ValueError("OP-TEE paged mode not supported") - e_entry = (start_hi << 32) + start_lo - p_addr = e_entry - p_data = bin[0x1c:] - if len(p_data) != init_sz: - raise ValueError("Invalid file '%s': size mismatch " - "(expected %d, have %d)" % (filename, init_sz, - len(p_data))) - segments.append((0, e_entry, p_addr, p_data)) - else: - raise ValueError("Unknown format for TEE file '%s'" % filename) - return segments - -def main(): - uboot_elf = "./u-boot" - fit_its = sys.stdout - if "BL31" in os.environ: - bl31_elf=os.getenv("BL31"); - elif os.path.isfile("./bl31.elf"): - bl31_elf = "./bl31.elf" - else: - os.system("echo 'int main(){}' > bl31.c") - os.system("${CROSS_COMPILE}gcc -c bl31.c -o bl31.elf") - bl31_elf = "./bl31.elf" - logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.DEBUG) - logging.warning(' BL31 file bl31.elf NOT found, resulting binary is non-functional') - logging.warning(' Please read Building section in doc/README.rockchip') - - if "TEE" in os.environ: - tee_file = os.getenv("TEE") - elif os.path.isfile("./tee.bin"): - tee_file = "./tee.bin" - elif os.path.isfile("./tee.elf"): - tee_file = "./tee.elf" - else: - tee_file = "" - - opts, args = getopt.getopt(sys.argv[1:], "o:u:b:t:h") - for opt, val in opts: - if opt == "-o": - fit_its = val - elif opt == "-u": - uboot_elf = val - elif opt == "-b": - bl31_elf = val - elif opt == "-t": - tee_file = val - elif opt == "-h": - print(__doc__) - sys.exit(2) - - dtbs = args - - generate_atf_fit_dts(fit_its, bl31_elf, tee_file, uboot_elf, dtbs) - generate_atf_binary(bl31_elf) - generate_tee_binary(tee_file) - -if __name__ == "__main__": - main() diff --git a/boot/Kconfig b/boot/Kconfig index 219125ebbb..45e567d4ac 100644 --- a/boot/Kconfig +++ b/boot/Kconfig @@ -287,7 +287,6 @@ config USE_SPL_FIT_GENERATOR config SPL_FIT_GENERATOR string ".its file generator script for U-Boot FIT image" depends on USE_SPL_FIT_GENERATOR - default "arch/arm/mach-rockchip/make_fit_atf.py" if SPL_LOAD_FIT && ARCH_ROCKCHIP default "arch/arm/mach-zynqmp/mkimage_fit_atf.sh" if SPL_LOAD_FIT && ARCH_ZYNQMP help Specifies a (platform specific) script file to generate the FIT From 0b079fcb09c5178ea3de62c7983cb03eea62b4e2 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 11 Jan 2023 16:10:12 -0700 Subject: [PATCH 29/38] binman: Add a test for an inner section with a size This is a slightly different scenario from the existing testSections tests. Add a new test for it. Signed-off-by: Simon Glass --- tools/binman/ftest.py | 6 ++++++ tools/binman/test/267_section_inner.dts | 16 ++++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 tools/binman/test/267_section_inner.dts diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index 330e8e1ccb..a4f78ae041 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -6188,6 +6188,12 @@ fdt fdtmap Extract the devicetree blob from the fdtmap err, "Image '.*' is missing external blobs but is still functional: missing") + def testSectionInner(self): + """Test an inner section with a size""" + data = self._DoReadFile('267_section_inner.dts') + expected = U_BOOT_DATA + tools.get_bytes(0, 12) + self.assertEqual(expected, data) + if __name__ == "__main__": unittest.main() diff --git a/tools/binman/test/267_section_inner.dts b/tools/binman/test/267_section_inner.dts new file mode 100644 index 0000000000..f6faab3d2f --- /dev/null +++ b/tools/binman/test/267_section_inner.dts @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + section { + size = <0x10>; + u-boot { + }; + }; + }; +}; From 4331d66661566cd823086204636754f785bd5d45 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 11 Jan 2023 16:10:13 -0700 Subject: [PATCH 30/38] binman: Clarify use of False when obtaining data This means that the data is not yet available. Update some comments to make this clearer. Signed-off-by: Simon Glass --- tools/binman/entry.py | 3 ++- tools/binman/etype/section.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/tools/binman/entry.py b/tools/binman/entry.py index d73f301340..f99618d453 100644 --- a/tools/binman/entry.py +++ b/tools/binman/entry.py @@ -582,7 +582,8 @@ class Entry(object): Returns: bytes content of the entry, excluding any padding. If the entry is - compressed, the compressed data is returned + compressed, the compressed data is returned. If the entry data + is not yet available, False can be returned """ self.Detail('GetData: size %s' % to_hex_size(self.data)) return self.data diff --git a/tools/binman/etype/section.py b/tools/binman/etype/section.py index 44dafaf726..85474f2411 100644 --- a/tools/binman/etype/section.py +++ b/tools/binman/etype/section.py @@ -265,6 +265,7 @@ class Entry_section(Entry): Args: entry: Entry to check + entry_data: Data for the entry, False if is null Returns: Contents of the entry along with any pad bytes before and @@ -678,7 +679,7 @@ class Entry_section(Entry): """ def _CheckDone(entry): if entry != skip_entry: - if not entry.ObtainContents(): + if entry.ObtainContents() is False: next_todo.append(entry) return entry From 62ef2f7bf3c442b6f717d2fbe823d579cf090dd8 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 11 Jan 2023 16:10:14 -0700 Subject: [PATCH 31/38] binman: Add a null entry It is sometimes useful to define an entry which does not have its own contents but does appear in the image. The contents are set by the section which contains it, even though it appears as an entry in the fdtmap. Add support for this. Signed-off-by: Simon Glass --- tools/binman/entries.rst | 13 +++++++++++++ tools/binman/entry.py | 5 +++-- tools/binman/etype/null.py | 25 +++++++++++++++++++++++++ tools/binman/etype/section.py | 6 ++++++ tools/binman/ftest.py | 5 +++++ tools/binman/test/268_null.dts | 19 +++++++++++++++++++ 6 files changed, 71 insertions(+), 2 deletions(-) create mode 100644 tools/binman/etype/null.py create mode 100644 tools/binman/test/268_null.dts diff --git a/tools/binman/entries.rst b/tools/binman/entries.rst index f6cc800385..2b32c131ed 100644 --- a/tools/binman/entries.rst +++ b/tools/binman/entries.rst @@ -1278,6 +1278,19 @@ This will pass in u-boot-spl as the input data and the .cfgout file as the +.. _etype_null: + +Entry: null: An entry which has no contents of its own +------------------------------------------------------ + +Note that the size property must be set since otherwise this entry does not +know how large it should be. + +The contents are set by the containing section, e.g. the section's pad +byte. + + + .. _etype_opensbi: Entry: opensbi: RISC-V OpenSBI fw_dynamic blob diff --git a/tools/binman/entry.py b/tools/binman/entry.py index f99618d453..e6ff026ddb 100644 --- a/tools/binman/entry.py +++ b/tools/binman/entry.py @@ -454,7 +454,7 @@ class Entry(object): Returns: True if the contents were found, False if another call is needed - after the other entries are processed. + after the other entries are processed, None if there is no contents """ # No contents by default: subclasses can implement this return True @@ -583,7 +583,8 @@ class Entry(object): Returns: bytes content of the entry, excluding any padding. If the entry is compressed, the compressed data is returned. If the entry data - is not yet available, False can be returned + is not yet available, False can be returned. If the entry data + is null, then None is returned. """ self.Detail('GetData: size %s' % to_hex_size(self.data)) return self.data diff --git a/tools/binman/etype/null.py b/tools/binman/etype/null.py new file mode 100644 index 0000000000..c10d482447 --- /dev/null +++ b/tools/binman/etype/null.py @@ -0,0 +1,25 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright 2023 Google LLC +# Written by Simon Glass +# + +from binman.entry import Entry +from dtoc import fdt_util +from patman import tools + +class Entry_null(Entry): + """An entry which has no contents of its own + + Note that the size property must be set since otherwise this entry does not + know how large it should be. + + The contents are set by the containing section, e.g. the section's pad + byte. + """ + def __init__(self, section, etype, node): + super().__init__(section, etype, node) + self.required_props = ['size'] + + def ObtainContents(self): + # null contents + return None diff --git a/tools/binman/etype/section.py b/tools/binman/etype/section.py index 85474f2411..28f04cb84a 100644 --- a/tools/binman/etype/section.py +++ b/tools/binman/etype/section.py @@ -320,6 +320,12 @@ class Entry_section(Entry): # earlier in the image description. See testCollectionSection(). if not required and entry_data is None: return None + + if entry_data is None: + pad_byte = (entry._pad_byte if isinstance(entry, Entry_section) + else self._pad_byte) + entry_data = tools.get_bytes(self._pad_byte, entry.size) + data = self.GetPaddedDataForEntry(entry, entry_data) # Handle empty space before the entry pad = (entry.offset or 0) - self._skip_at_start - len(section_data) diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index a4f78ae041..ac9b050fb6 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -6194,6 +6194,11 @@ fdt fdtmap Extract the devicetree blob from the fdtmap expected = U_BOOT_DATA + tools.get_bytes(0, 12) self.assertEqual(expected, data) + def testNull(self): + """Test an image with a null entry""" + data = self._DoReadFile('268_null.dts') + self.assertEqual(U_BOOT_DATA + b'\xff\xff\xff\xff' + U_BOOT_IMG_DATA, data) + if __name__ == "__main__": unittest.main() diff --git a/tools/binman/test/268_null.dts b/tools/binman/test/268_null.dts new file mode 100644 index 0000000000..3824ba8509 --- /dev/null +++ b/tools/binman/test/268_null.dts @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + pad-byte = <0xff>; + u-boot { + }; + null { + size = <4>; + }; + u-boot-img { + }; + }; +}; From 97fb8081ec0c8009ff5e70c054e8ece08e4465ed Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 11 Jan 2023 16:10:15 -0700 Subject: [PATCH 32/38] binman: Add a function to check for special section nodes This appears in two places in the code. Use a shared function instead. Signed-off-by: Simon Glass --- tools/binman/etype/fit.py | 3 +-- tools/binman/etype/section.py | 13 ++++++++++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/tools/binman/etype/fit.py b/tools/binman/etype/fit.py index f0e3fd1a09..0e9d81b9e8 100644 --- a/tools/binman/etype/fit.py +++ b/tools/binman/etype/fit.py @@ -655,8 +655,7 @@ class Entry_fit(Entry_section): for subnode in node.subnodes: subnode_path = f'{rel_path}/{subnode.name}' - if has_images and not (subnode.name.startswith('hash') or - subnode.name.startswith('signature')): + if has_images and not self.IsSpecialSubnode(subnode): # This subnode is a content node not meant to appear in # the FIT (e.g. "/images/kernel/u-boot"), so don't call # fsw.add_node() or _add_node() for it. diff --git a/tools/binman/etype/section.py b/tools/binman/etype/section.py index 28f04cb84a..a22be7ec77 100644 --- a/tools/binman/etype/section.py +++ b/tools/binman/etype/section.py @@ -169,6 +169,17 @@ class Entry_section(Entry): self._ignore_missing = False self._filename = None + def IsSpecialSubnode(self, node): + """Check if a node is a special one used by the section itself + + Some notes are used for hashing / signatures and do not add entries to + the actual section. + + Returns: + bool: True if the node is a special one, else False + """ + return node.name.startswith('hash') or node.name.startswith('signature') + def ReadNode(self): """Read properties from the section node""" super().ReadNode() @@ -195,7 +206,7 @@ class Entry_section(Entry): def ReadEntries(self): for node in self._node.subnodes: - if node.name.startswith('hash') or node.name.startswith('signature'): + if self.IsSpecialSubnode(node): continue entry = Entry.Create(self, node, expanded=self.GetImage().use_expanded, From 9766f69c98c2aa056d0518a9545f9e89484e9172 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 11 Jan 2023 16:10:16 -0700 Subject: [PATCH 33/38] binman: Support overlapping entries In some cases it is useful to have an entry overlap with another in a section, either to update the contents within a blob, or to add an entry to the fdtmap that covers only part of the blob. Add support for this. Signed-off-by: Simon Glass --- tools/binman/binman.rst | 9 +++- tools/binman/entry.py | 5 ++ tools/binman/etype/section.py | 31 +++++++---- tools/binman/ftest.py | 63 +++++++++++++++++++++++ tools/binman/test/269_overlap.dts | 21 ++++++++ tools/binman/test/270_overlap_null.dts | 24 +++++++++ tools/binman/test/271_overlap_bad.dts | 21 ++++++++ tools/binman/test/272_overlap_no_size.dts | 19 +++++++ 8 files changed, 183 insertions(+), 10 deletions(-) create mode 100644 tools/binman/test/269_overlap.dts create mode 100644 tools/binman/test/270_overlap_null.dts create mode 100644 tools/binman/test/271_overlap_bad.dts create mode 100644 tools/binman/test/272_overlap_no_size.dts diff --git a/tools/binman/binman.rst b/tools/binman/binman.rst index bfe300a39c..97e2d4e55d 100644 --- a/tools/binman/binman.rst +++ b/tools/binman/binman.rst @@ -792,6 +792,12 @@ align-default: symlink: Adds a symlink to the image with string given in the symlink property. +overlap: + Indicates that this entry overlaps with others in the same section. These + entries should appear at the end of the section. Overlapping entries are not + packed with other entries, but their contents are written over other entries + in the section. Overlapping entries must have an explicit offset and size. + Examples of the above options can be found in the tests. See the tools/binman/test directory. @@ -1720,7 +1726,8 @@ implementation of Pack() is usually sufficient. Note: for sections, this also checks that the entries do not overlap, nor extend outside the section. If the section does not have a defined size, the size is -set large enough to hold all the entries. +set large enough to hold all the entries. For entries that are explicitly marked +as overlapping, this check is skipped. 6. SetImagePos() - sets the image position of every entry. This is the absolute position 'image-pos', as opposed to 'offset' which is relative to the containing diff --git a/tools/binman/entry.py b/tools/binman/entry.py index e6ff026ddb..0c94665f7a 100644 --- a/tools/binman/entry.py +++ b/tools/binman/entry.py @@ -98,6 +98,7 @@ class Entry(object): An absent entry is removed during processing so that it does not appear in the map optional (bool): True if this entry contains an optional external blob + overlap (bool): True if this entry overlaps with others """ fake_dir = None @@ -142,6 +143,7 @@ class Entry(object): self.auto_write_symbols = auto_write_symbols self.absent = False self.optional = False + self.overlap = False @staticmethod def FindEntryClass(etype, expanded): @@ -294,6 +296,9 @@ class Entry(object): self.extend_size = fdt_util.GetBool(self._node, 'extend-size') self.missing_msg = fdt_util.GetString(self._node, 'missing-msg') self.optional = fdt_util.GetBool(self._node, 'optional') + self.overlap = fdt_util.GetBool(self._node, 'overlap') + if self.overlap: + self.required_props += ['offset', 'size'] # This is only supported by blobs and sections at present self.compress = fdt_util.GetString(self._node, 'compress', 'none') diff --git a/tools/binman/etype/section.py b/tools/binman/etype/section.py index a22be7ec77..57b91ff726 100644 --- a/tools/binman/etype/section.py +++ b/tools/binman/etype/section.py @@ -332,19 +332,31 @@ class Entry_section(Entry): if not required and entry_data is None: return None + entry_data_final = entry_data if entry_data is None: pad_byte = (entry._pad_byte if isinstance(entry, Entry_section) else self._pad_byte) - entry_data = tools.get_bytes(self._pad_byte, entry.size) + entry_data_final = tools.get_bytes(self._pad_byte, entry.size) - data = self.GetPaddedDataForEntry(entry, entry_data) + data = self.GetPaddedDataForEntry(entry, entry_data_final) # Handle empty space before the entry pad = (entry.offset or 0) - self._skip_at_start - len(section_data) if pad > 0: section_data += tools.get_bytes(self._pad_byte, pad) # Add in the actual entry data - section_data += data + if entry.overlap: + end_offset = entry.offset + entry.size + if end_offset > len(section_data): + entry.Raise("Offset %#x (%d) ending at %#x (%d) must overlap with existing entries" % + (entry.offset, entry.offset, end_offset, + end_offset)) + # Don't write anything for null entries' + if entry_data is not None: + section_data = (section_data[:entry.offset] + data + + section_data[entry.offset + entry.size:]) + else: + section_data += data self.Detail('GetData: %d entries, total size %#x' % (len(self._entries), len(section_data))) @@ -467,12 +479,13 @@ class Entry_section(Entry): (entry.offset, entry.offset, entry.size, entry.size, self._node.path, self._skip_at_start, self._skip_at_start, max_size, max_size)) - if entry.offset < offset and entry.size: - entry.Raise("Offset %#x (%d) overlaps with previous entry '%s' " - "ending at %#x (%d)" % - (entry.offset, entry.offset, prev_name, offset, offset)) - offset = entry.offset + entry.size - prev_name = entry.GetPath() + if not entry.overlap: + if entry.offset < offset and entry.size: + entry.Raise("Offset %#x (%d) overlaps with previous entry '%s' ending at %#x (%d)" % + (entry.offset, entry.offset, prev_name, offset, + offset)) + offset = entry.offset + entry.size + prev_name = entry.GetPath() def WriteSymbols(self, section): """Write symbol values into binary files for access at run time""" diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index ac9b050fb6..aea8a5f758 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -6199,6 +6199,69 @@ fdt fdtmap Extract the devicetree blob from the fdtmap data = self._DoReadFile('268_null.dts') self.assertEqual(U_BOOT_DATA + b'\xff\xff\xff\xff' + U_BOOT_IMG_DATA, data) + def testOverlap(self): + """Test an image with a overlapping entry""" + data = self._DoReadFile('269_overlap.dts') + self.assertEqual(U_BOOT_DATA[:1] + b'aa' + U_BOOT_DATA[3:], data) + + image = control.images['image'] + entries = image.GetEntries() + + self.assertIn('inset', entries) + inset = entries['inset'] + self.assertEqual(1, inset.offset); + self.assertEqual(1, inset.image_pos); + self.assertEqual(2, inset.size); + + def testOverlapNull(self): + """Test an image with a null overlap""" + data = self._DoReadFile('270_overlap_null.dts') + self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)]) + + # Check the FMAP + fhdr, fentries = fmap_util.DecodeFmap(data[len(U_BOOT_DATA):]) + self.assertEqual(4, fhdr.nareas) + fiter = iter(fentries) + + fentry = next(fiter) + self.assertEqual(b'SECTION', fentry.name) + self.assertEqual(0, fentry.offset) + self.assertEqual(len(U_BOOT_DATA), fentry.size) + self.assertEqual(0, fentry.flags) + + fentry = next(fiter) + self.assertEqual(b'U_BOOT', fentry.name) + self.assertEqual(0, fentry.offset) + self.assertEqual(len(U_BOOT_DATA), fentry.size) + self.assertEqual(0, fentry.flags) + + # Make sure that the NULL entry appears in the FMAP + fentry = next(fiter) + self.assertEqual(b'NULL', fentry.name) + self.assertEqual(1, fentry.offset) + self.assertEqual(2, fentry.size) + self.assertEqual(0, fentry.flags) + + fentry = next(fiter) + self.assertEqual(b'FMAP', fentry.name) + self.assertEqual(len(U_BOOT_DATA), fentry.offset) + + def testOverlapBad(self): + """Test an image with a bad overlapping entry""" + with self.assertRaises(ValueError) as exc: + self._DoReadFile('271_overlap_bad.dts') + self.assertIn( + "Node '/binman/inset': Offset 0x10 (16) ending at 0x12 (18) must overlap with existing entries", + str(exc.exception)) + + def testOverlapNoOffset(self): + """Test an image with a bad overlapping entry""" + with self.assertRaises(ValueError) as exc: + self._DoReadFile('272_overlap_no_size.dts') + self.assertIn( + "Node '/binman/inset': 'fill' entry is missing properties: size", + str(exc.exception)) + if __name__ == "__main__": unittest.main() diff --git a/tools/binman/test/269_overlap.dts b/tools/binman/test/269_overlap.dts new file mode 100644 index 0000000000..f949b8b359 --- /dev/null +++ b/tools/binman/test/269_overlap.dts @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u-boot { + }; + + inset { + type = "fill"; + fill-byte = [61]; + offset = <1>; + size = <2>; + overlap; + }; + }; +}; diff --git a/tools/binman/test/270_overlap_null.dts b/tools/binman/test/270_overlap_null.dts new file mode 100644 index 0000000000..feed9ec892 --- /dev/null +++ b/tools/binman/test/270_overlap_null.dts @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + section { + u-boot { + }; + + null { + offset = <1>; + size = <2>; + overlap; + }; + }; + + fmap { + }; + }; +}; diff --git a/tools/binman/test/271_overlap_bad.dts b/tools/binman/test/271_overlap_bad.dts new file mode 100644 index 0000000000..f281802114 --- /dev/null +++ b/tools/binman/test/271_overlap_bad.dts @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u-boot { + }; + + inset { + type = "fill"; + fill-byte = [61]; + offset = <0x10>; + size = <2>; + overlap; + }; + }; +}; diff --git a/tools/binman/test/272_overlap_no_size.dts b/tools/binman/test/272_overlap_no_size.dts new file mode 100644 index 0000000000..4517536f2e --- /dev/null +++ b/tools/binman/test/272_overlap_no_size.dts @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u-boot { + }; + + inset { + type = "fill"; + fill-byte = [61]; + overlap; + }; + }; +}; From c1157860c5e9ca45e41859e013ed83919e7397f0 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 11 Jan 2023 16:10:17 -0700 Subject: [PATCH 34/38] binman: Provide general support for updating ELF symbols The current support for updating variables in a binary is hard-coded to work with U-Boot: - It assumes the image starts at __image_copy_start - It uses the existing U-Boot-specific entry types It is useful for other projects to use these feature. Add properties to enable writing symbols for any blob, a way of specifying the base symbol and a way of providing the ELF filename to allow symbol lookup to take place. With this it is possible to update a Zephyr image, such as zephyr.bin after it has been built. Signed-off-by: Simon Glass --- tools/binman/binman.rst | 25 ++++++++++++++++++++++ tools/binman/elf.py | 8 +++++-- tools/binman/entry.py | 3 ++- tools/binman/etype/blob.py | 6 ++++++ tools/binman/etype/u_boot_spl.py | 1 - tools/binman/ftest.py | 19 +++++++++++++++++ tools/binman/test/273_blob_symbol.dts | 24 +++++++++++++++++++++ tools/binman/test/Makefile | 9 +++++++- tools/binman/test/blob_syms.c | 20 ++++++++++++++++++ tools/binman/test/blob_syms.lds | 30 +++++++++++++++++++++++++++ 10 files changed, 140 insertions(+), 5 deletions(-) create mode 100644 tools/binman/test/273_blob_symbol.dts create mode 100644 tools/binman/test/blob_syms.c create mode 100644 tools/binman/test/blob_syms.lds diff --git a/tools/binman/binman.rst b/tools/binman/binman.rst index 97e2d4e55d..980a1ac5bd 100644 --- a/tools/binman/binman.rst +++ b/tools/binman/binman.rst @@ -487,6 +487,13 @@ For x86 devices (with the end-at-4gb property) this base address is not added since it is assumed that images are XIP and the offsets already include the address. +While U-Boot's symbol updating is handled automatically by the u-boot-spl +entry type (and others), it is possible to use this feature with any blob. To +do this, add a `write-symbols` (boolean) property to the node, set the ELF +filename using `elf-filename` and set 'elf-base-sym' to the base symbol for the +start of the binary image (this defaults to `__image_copy_start` which is what +U-Boot uses). See `testBlobSymbol()` for an example. + .. _binman_fdt: Access to binman entry offsets at run time (fdt) @@ -798,6 +805,24 @@ overlap: packed with other entries, but their contents are written over other entries in the section. Overlapping entries must have an explicit offset and size. +write-symbols: + Indicates that the blob should be updated with symbol values calculated by + binman. This is automatic for certain entry types, e.g. `u-boot-spl`. See + binman_syms_ for more information. + +elf-filename: + Sets the file name of a blob's associated ELF file. For example, if the + blob is `zephyr.bin` then the ELF file may be `zephyr.elf`. This allows + binman to locate symbols and understand the structure of the blob. See + binman_syms_ for more information. + +elf-base-sym: + Sets the name of the ELF symbol that points to the start of a blob. For + U-Boot this is `__image_copy_start` and that is the default used by binman + if this property is missing. For other projects, a difference symbol may be + needed. Add this symbol to the properties for the blob so that symbols can + be read correctly. See binman_syms_ for more information. + Examples of the above options can be found in the tests. See the tools/binman/test directory. diff --git a/tools/binman/elf.py b/tools/binman/elf.py index 73f318b81d..9ac00ed9cc 100644 --- a/tools/binman/elf.py +++ b/tools/binman/elf.py @@ -210,7 +210,8 @@ def GetPackString(sym, msg): raise ValueError('%s has size %d: only 4 and 8 are supported' % (msg, sym.size)) -def LookupAndWriteSymbols(elf_fname, entry, section, is_elf=False): +def LookupAndWriteSymbols(elf_fname, entry, section, is_elf=False, + base_sym=None): """Replace all symbols in an entry with their correct values The entry contents is updated so that values for referenced symbols will be @@ -223,7 +224,10 @@ def LookupAndWriteSymbols(elf_fname, entry, section, is_elf=False): entry entry: Entry to process section: Section which can be used to lookup symbol values + base_sym: Base symbol marking the start of the image """ + if not base_sym: + base_sym = '__image_copy_start' fname = tools.get_input_filename(elf_fname) syms = GetSymbols(fname, ['image', 'binman']) if is_elf: @@ -243,7 +247,7 @@ def LookupAndWriteSymbols(elf_fname, entry, section, is_elf=False): if not syms: tout.debug('LookupAndWriteSymbols: no syms') return - base = syms.get('__image_copy_start') + base = syms.get(base_sym) if not base and not is_elf: tout.debug('LookupAndWriteSymbols: no base') return diff --git a/tools/binman/entry.py b/tools/binman/entry.py index 0c94665f7a..aca08e62d3 100644 --- a/tools/binman/entry.py +++ b/tools/binman/entry.py @@ -144,6 +144,7 @@ class Entry(object): self.absent = False self.optional = False self.overlap = False + self.elf_base_sym = None @staticmethod def FindEntryClass(etype, expanded): @@ -676,7 +677,7 @@ class Entry(object): # Check if we are writing symbols into an ELF file is_elf = self.GetDefaultFilename() == self.elf_fname elf.LookupAndWriteSymbols(self.elf_fname, self, section.GetImage(), - is_elf) + is_elf, self.elf_base_sym) def CheckEntries(self): """Check that the entry offsets are correct diff --git a/tools/binman/etype/blob.py b/tools/binman/etype/blob.py index 70dea7158e..c7ddcedffb 100644 --- a/tools/binman/etype/blob.py +++ b/tools/binman/etype/blob.py @@ -35,6 +35,12 @@ class Entry_blob(Entry): super().__init__(section, etype, node, auto_write_symbols=auto_write_symbols) self._filename = fdt_util.GetString(self._node, 'filename', self.etype) + self.elf_fname = fdt_util.GetString(self._node, 'elf-filename', + self.elf_fname) + self.elf_base_sym = fdt_util.GetString(self._node, 'elf-base-sym') + if not self.auto_write_symbols: + if fdt_util.GetBool(self._node, 'write-symbols'): + self.auto_write_symbols = True def ObtainContents(self, fake_size=0): self._filename = self.GetDefaultFilename() diff --git a/tools/binman/etype/u_boot_spl.py b/tools/binman/etype/u_boot_spl.py index be1610569f..7f710c857d 100644 --- a/tools/binman/etype/u_boot_spl.py +++ b/tools/binman/etype/u_boot_spl.py @@ -34,7 +34,6 @@ class Entry_u_boot_spl(Entry_blob): def __init__(self, section, etype, node): super().__init__(section, etype, node, auto_write_symbols=True) self.elf_fname = 'spl/u-boot-spl' - self.auto_write_symbols = True def GetDefaultFilename(self): return 'spl/u-boot-spl.bin' diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index aea8a5f758..17b0431d4f 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -6262,6 +6262,25 @@ fdt fdtmap Extract the devicetree blob from the fdtmap "Node '/binman/inset': 'fill' entry is missing properties: size", str(exc.exception)) + def testBlobSymbol(self): + """Test a blob with symbols read from an ELF file""" + elf_fname = self.ElfTestFile('blob_syms') + TestFunctional._MakeInputFile('blob_syms', tools.read_file(elf_fname)) + TestFunctional._MakeInputFile('blob_syms.bin', + tools.read_file(self.ElfTestFile('blob_syms.bin'))) + + data = self._DoReadFile('273_blob_symbol.dts') + + syms = elf.GetSymbols(elf_fname, ['binman', 'image']) + addr = elf.GetSymbolAddress(elf_fname, '__my_start_sym') + self.assertEqual(syms['_binman_sym_magic'].address, addr) + self.assertEqual(syms['_binman_inset_prop_offset'].address, addr + 4) + self.assertEqual(syms['_binman_inset_prop_size'].address, addr + 8) + + sym_values = struct.pack('; + #size-cells = <1>; + + binman { + blob { + filename = "blob_syms.bin"; + write-symbols; + elf-filename = "blob_syms"; + elf-base-sym = "__my_start_sym"; + }; + + inset { + type = "null"; + offset = <4>; + size = <8>; + overlap; + }; + }; +}; diff --git a/tools/binman/test/Makefile b/tools/binman/test/Makefile index bea8567c9b..cd66a3038b 100644 --- a/tools/binman/test/Makefile +++ b/tools/binman/test/Makefile @@ -30,11 +30,12 @@ LDS_BINMAN_BAD := -T $(SRC)u_boot_binman_syms_bad.lds LDS_BINMAN_X86 := -T $(SRC)u_boot_binman_syms_x86.lds LDS_BINMAN_EMBED := -T $(SRC)u_boot_binman_embed.lds LDS_EFL_SECTIONS := -T $(SRC)elf_sections.lds +LDS_BLOB := -T $(SRC)blob_syms.lds TARGETS = u_boot_ucode_ptr u_boot_no_ucode_ptr bss_data \ u_boot_binman_syms u_boot_binman_syms.bin u_boot_binman_syms_bad \ u_boot_binman_syms_size u_boot_binman_syms_x86 embed_data \ - u_boot_binman_embed u_boot_binman_embed_sm elf_sections + u_boot_binman_embed u_boot_binman_embed_sm elf_sections blob_syms.bin all: $(TARGETS) @@ -71,6 +72,12 @@ u_boot_binman_embed: u_boot_binman_embed.c u_boot_binman_embed_sm: CFLAGS += $(LDS_BINMAN_EMBED) u_boot_binman_embed_sm: u_boot_binman_embed_sm.c +blob_syms.bin: blob_syms + $(OBJCOPY) -O binary $< -R .note.gnu.build-id $@ + +blob_syms: CFLAGS += $(LDS_BLOB) +blob_syms: blob_syms.c + elf_sections: CFLAGS += $(LDS_EFL_SECTIONS) elf_sections: elf_sections.c diff --git a/tools/binman/test/blob_syms.c b/tools/binman/test/blob_syms.c new file mode 100644 index 0000000000..d652c79aa9 --- /dev/null +++ b/tools/binman/test/blob_syms.c @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2017 Google, Inc + * + * Simple program to create some binman symbols. This is used by binman tests. + */ + +typedef unsigned long ulong; + +#include +#include + +DECLARE_BINMAN_MAGIC_SYM; + +unsigned long val1 = 123; +unsigned long val2 = 456; +binman_sym_declare(unsigned long, inset, offset); +unsigned long val3 = 789; +unsigned long val4 = 999; +binman_sym_declare(unsigned long, inset, size); diff --git a/tools/binman/test/blob_syms.lds b/tools/binman/test/blob_syms.lds new file mode 100644 index 0000000000..787e38dd85 --- /dev/null +++ b/tools/binman/test/blob_syms.lds @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2016 Google, Inc + */ + +OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") +OUTPUT_ARCH(i386) +ENTRY(_start) + +SECTIONS +{ + . = 0x00000010; + _start = .; + + . = ALIGN(4); + .text : + { + __my_start_sym = .; + *(.text*) + } + + . = ALIGN(4); + .binman_sym_table : { + __binman_sym_start = .; + KEEP(*(SORT(.binman_sym*))); + __binman_sym_end = .; + } + .interp : { *(.interp*) } + +} From 8f5afe21aed8b8ed4d75678a4e8972e7d8a23a6b Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 11 Jan 2023 16:10:18 -0700 Subject: [PATCH 35/38] dtoc: Add a way to read a phandle with params Add a function to read a phandle and associated name and offset. This is useful for binman. Signed-off-by: Simon Glass --- tools/dtoc/fdt_util.py | 28 +++++++++++++++++++++++++++ tools/dtoc/test/dtoc_test_phandle.dts | 1 + tools/dtoc/test_dtoc.py | 3 +++ tools/dtoc/test_fdt.py | 11 +++++++++++ 4 files changed, 43 insertions(+) diff --git a/tools/dtoc/fdt_util.py b/tools/dtoc/fdt_util.py index d7c38ad1e0..f34316632a 100644 --- a/tools/dtoc/fdt_util.py +++ b/tools/dtoc/fdt_util.py @@ -281,6 +281,34 @@ def GetPhandleList(node, propname): value = [value] return [fdt32_to_cpu(v) for v in value] +def GetPhandleNameOffset(node, propname): + """Get a <&phandle>, "string", value from a property + + Args: + node: Node object to read from + propname: property name to read + + Returns: + tuple: + Node object + str + int + or None if the property does not exist + """ + prop = node.props.get(propname) + if not prop: + return None + value = prop.bytes + phandle = fdt32_to_cpu(value[:4]) + node = node.GetFdt().LookupPhandle(phandle) + name = '' + for byte in value[4:]: + if not byte: + break + name += chr(byte) + val = fdt32_to_cpu(value[4 + len(name) + 1:]) + return node, name, val + def GetDatatype(node, propname, datatype): """Get a value of a given type from a property diff --git a/tools/dtoc/test/dtoc_test_phandle.dts b/tools/dtoc/test/dtoc_test_phandle.dts index a71acffc69..d9aa433503 100644 --- a/tools/dtoc/test/dtoc_test_phandle.dts +++ b/tools/dtoc/test/dtoc_test_phandle.dts @@ -32,6 +32,7 @@ u-boot,dm-pre-reloc; compatible = "source"; clocks = <&phandle &phandle_1 11 &phandle_2 12 13 &phandle>; + phandle-name-offset = <&phandle_2>, "fred", <123>; }; phandle-source2 { diff --git a/tools/dtoc/test_dtoc.py b/tools/dtoc/test_dtoc.py index 879ca2ab2b..c62fcbac83 100755 --- a/tools/dtoc/test_dtoc.py +++ b/tools/dtoc/test_dtoc.py @@ -929,6 +929,7 @@ U_BOOT_DRVINFO(spl_test) = { self._check_strings(HEADER + ''' struct dtd_source { \tstruct phandle_2_arg clocks[4]; +\tunsigned char phandle_name_offset[13]; }; struct dtd_target { \tfdt32_t\t\tintval; @@ -981,6 +982,8 @@ static struct dtd_source dtv_phandle_source = { \t\t\t{0, {11}}, \t\t\t{1, {12, 13}}, \t\t\t{4, {}},}, +\t.phandle_name_offset = {0x0, 0x0, 0x0, 0x3, 0x66, 0x72, 0x65, 0x64, +\t\t0x0, 0x0, 0x0, 0x0, 0x7b}, }; U_BOOT_DRVINFO(phandle_source) = { \t.name\t\t= "source", diff --git a/tools/dtoc/test_fdt.py b/tools/dtoc/test_fdt.py index a3e36ea363..3b8ee00d4e 100755 --- a/tools/dtoc/test_fdt.py +++ b/tools/dtoc/test_fdt.py @@ -795,6 +795,17 @@ class TestFdtUtil(unittest.TestCase): finally: tools.outdir= old_outdir + def test_get_phandle_name_offset(self): + val = fdt_util.GetPhandleNameOffset(self.node, 'missing') + self.assertIsNone(val) + + dtb = fdt.FdtScan(find_dtb_file('dtoc_test_phandle.dts')) + node = dtb.GetNode('/phandle-source') + node, name, offset = fdt_util.GetPhandleNameOffset(node, + 'phandle-name-offset') + self.assertEqual('phandle3-target', node.name) + self.assertEqual('fred', name) + self.assertEqual(123, offset) def run_test_coverage(build_dir): """Run the tests and check that we get 100% coverage From 571bc4e67d39e4c376f8bab0d6518ab5ee832d9e Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 11 Jan 2023 16:10:19 -0700 Subject: [PATCH 36/38] binman: Support positioning an entry by and ELF symbol MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In some cases it is useful to position an entry over the top of a symbol in an ELF file. For example, if the symbol holds a version string then it allows the string to be accessed from the fdtmap. Add support for this. Suggested-by: Pali Rohár Suggested-by: Keith Short Signed-off-by: Simon Glass --- tools/binman/binman.rst | 7 ++++++ tools/binman/elf.py | 23 +++++++++++++++++ tools/binman/entry.py | 19 +++++++++++++- tools/binman/ftest.py | 28 +++++++++++++++++++++ tools/binman/test/274_offset_from_elf.dts | 30 +++++++++++++++++++++++ 5 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 tools/binman/test/274_offset_from_elf.dts diff --git a/tools/binman/binman.rst b/tools/binman/binman.rst index 980a1ac5bd..fa8abdcd86 100644 --- a/tools/binman/binman.rst +++ b/tools/binman/binman.rst @@ -823,6 +823,13 @@ elf-base-sym: needed. Add this symbol to the properties for the blob so that symbols can be read correctly. See binman_syms_ for more information. +offset-from-elf: + Sets the offset of an entry based on a symbol value in an another entry. + The format is <&phandle>, "sym_name", where phandle is the entry + containing the blob (with associated ELF file providing symbols), + is the symbol to lookup (relative to elf-base-sym) and is an offset + to add to that value. + Examples of the above options can be found in the tests. See the tools/binman/test directory. diff --git a/tools/binman/elf.py b/tools/binman/elf.py index 9ac00ed9cc..3cc8a38449 100644 --- a/tools/binman/elf.py +++ b/tools/binman/elf.py @@ -210,6 +210,29 @@ def GetPackString(sym, msg): raise ValueError('%s has size %d: only 4 and 8 are supported' % (msg, sym.size)) +def GetSymbolOffset(elf_fname, sym_name, base_sym=None): + """Read the offset of a symbol compared to base symbol + + This is useful for obtaining the value of a single symbol relative to the + base of a binary blob. + + Args: + elf_fname: Filename of the ELF file to read + sym_name (str): Name of symbol to read + base_sym (str): Base symbol to sue to calculate the offset (or None to + use '__image_copy_start' + + Returns: + int: Offset of the symbol relative to the base symbol + """ + if not base_sym: + base_sym = '__image_copy_start' + fname = tools.get_input_filename(elf_fname) + syms = GetSymbols(fname, [base_sym, sym_name]) + base = syms[base_sym].address + val = syms[sym_name].address + return val - base + def LookupAndWriteSymbols(elf_fname, entry, section, is_elf=False, base_sym=None): """Replace all symbols in an entry with their correct values diff --git a/tools/binman/entry.py b/tools/binman/entry.py index aca08e62d3..5d8696e32a 100644 --- a/tools/binman/entry.py +++ b/tools/binman/entry.py @@ -145,6 +145,7 @@ class Entry(object): self.optional = False self.overlap = False self.elf_base_sym = None + self.offset_from_elf = None @staticmethod def FindEntryClass(etype, expanded): @@ -303,6 +304,8 @@ class Entry(object): # This is only supported by blobs and sections at present self.compress = fdt_util.GetString(self._node, 'compress', 'none') + self.offset_from_elf = fdt_util.GetPhandleNameOffset(self._node, + 'offset-from-elf') def GetDefaultFilename(self): return None @@ -499,7 +502,10 @@ class Entry(object): if self.offset_unset: self.Raise('No offset set with offset-unset: should another ' 'entry provide this correct offset?') - self.offset = tools.align(offset, self.align) + elif self.offset_from_elf: + self.offset = self.lookup_offset() + else: + self.offset = tools.align(offset, self.align) needed = self.pad_before + self.contents_size + self.pad_after needed = tools.align(needed, self.align_size) size = self.size @@ -1328,3 +1334,14 @@ features to produce new behaviours. int: entry address of ELF file """ return None + + def lookup_offset(self): + node, sym_name, offset = self.offset_from_elf + entry = self.section.FindEntryByNode(node) + if not entry: + self.Raise("Cannot find entry for node '%s'" % node.name) + if not entry.elf_fname: + entry.Raise("Need elf-fname property '%s'" % node.name) + val = elf.GetSymbolOffset(entry.elf_fname, sym_name, + entry.elf_base_sym) + return val + offset diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index 17b0431d4f..be0aea49ce 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -6281,6 +6281,34 @@ fdt fdtmap Extract the devicetree blob from the fdtmap expected = sym_values self.assertEqual(expected, data[:len(expected)]) + def testOffsetFromElf(self): + """Test a blob with symbols read from an ELF file""" + elf_fname = self.ElfTestFile('blob_syms') + TestFunctional._MakeInputFile('blob_syms', tools.read_file(elf_fname)) + TestFunctional._MakeInputFile('blob_syms.bin', + tools.read_file(self.ElfTestFile('blob_syms.bin'))) + + data = self._DoReadFile('274_offset_from_elf.dts') + + syms = elf.GetSymbols(elf_fname, ['binman', 'image']) + base = elf.GetSymbolAddress(elf_fname, '__my_start_sym') + + image = control.images['image'] + entries = image.GetEntries() + + self.assertIn('inset', entries) + inset = entries['inset'] + + self.assertEqual(base + 4, inset.offset); + self.assertEqual(base + 4, inset.image_pos); + self.assertEqual(4, inset.size); + + self.assertIn('inset2', entries) + inset = entries['inset2'] + self.assertEqual(base + 8, inset.offset); + self.assertEqual(base + 8, inset.image_pos); + self.assertEqual(4, inset.size); + if __name__ == "__main__": unittest.main() diff --git a/tools/binman/test/274_offset_from_elf.dts b/tools/binman/test/274_offset_from_elf.dts new file mode 100644 index 0000000000..e3372fc7c3 --- /dev/null +++ b/tools/binman/test/274_offset_from_elf.dts @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + blob: blob { + filename = "blob_syms.bin"; + elf-filename = "blob_syms"; + elf-base-sym = "__my_start_sym"; + }; + + inset { + type = "null"; + offset-from-elf = <&blob>, "val3", <0>; + size = <4>; + overlap; + }; + + inset2 { + type = "null"; + offset-from-elf = <&blob>, "val3", <4>; + size = <4>; + overlap; + }; + }; +}; From da413b56356c0c95c10ecb8e73ec31d160c7024a Mon Sep 17 00:00:00 2001 From: Maxim Cournoyer Date: Fri, 13 Jan 2023 08:50:49 -0500 Subject: [PATCH 37/38] Revert "patman: invoke the checkpatch.pl script with '--u-boot' and '--strict'" This reverts commit 648d8186dd7f9c444fb07f355090d275dcdd4de4, because it broke usage of patman on Linux, whose check script doesn't know about '--strict' or '--u-boot'. Reported-by: Sjoerd Simons Signed-off-by: Maxim Cournoyer Reviewed-by: Simon Glass Tested-by: Sjoerd Simons --- tools/patman/checkpatch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/patman/checkpatch.py b/tools/patman/checkpatch.py index 012c0d895c..d1b902dd96 100644 --- a/tools/patman/checkpatch.py +++ b/tools/patman/checkpatch.py @@ -211,7 +211,7 @@ def check_patch(fname, verbose=False, show_types=False, use_tree=False): stdout: Full output of checkpatch """ chk = find_check_patch() - args = [chk, '--u-boot', '--strict'] + args = [chk] if not use_tree: args.append('--no-tree') if show_types: From 4c5907889553696160fabaa7e9f0c96ed1fa6597 Mon Sep 17 00:00:00 2001 From: Maxim Cournoyer Date: Fri, 13 Jan 2023 08:50:50 -0500 Subject: [PATCH 38/38] patman: symlink top level .checkpatch.conf This makes it possible to run the patman test suite simply by invoking 'pytest' from the patman sub-directory: $ cd tools/patman $ pytest Otherwise, the top level .checkpatch.conf would be ignored and multiple test_checkpatch.py tests would fail. Signed-off-by: Maxim Cournoyer Reviewed-by: Simon Glass --- .gitignore | 1 + tools/patman/.checkpatch.conf | 1 + 2 files changed, 2 insertions(+) create mode 120000 tools/patman/.checkpatch.conf diff --git a/.gitignore b/.gitignore index eb769f144c..3adf1faf4e 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ # Normal rules (sorted alphabetically) # .* +!.checkpatch.conf *.a *.asn1.[ch] *.bin diff --git a/tools/patman/.checkpatch.conf b/tools/patman/.checkpatch.conf new file mode 120000 index 0000000000..c0e2020afe --- /dev/null +++ b/tools/patman/.checkpatch.conf @@ -0,0 +1 @@ +../../.checkpatch.conf \ No newline at end of file