[PATCH v4 1/1] image: fit: Apply overlays using aligned writable FDT copies
Jamie.Gibbons at microchip.com
Jamie.Gibbons at microchip.com
Thu Feb 12 13:42:32 CET 2026
Hi James, Marek, Tom,
On Wed, 2026-02-11 at 11:06 -0700, James Hilliard wrote:
> EXTERNAL EMAIL: Do not click links or open attachments unless you
> know the content is safe
>
> libfdt expects FDT/DTO blobs to be 8-byte aligned. When loading the
> base FDT or overlays from a FIT, the mapped buffer may be unaligned,
> which can break fdt_open_into() on strict-alignment architectures.
>
> boot_get_fdt_fit() relocates the base FDT with boot_relocate_fdt()
> before applying overlays. That uses the bootm memory map and can
> overlap with the FIT buffer when the FIT is loaded into RAM,
> corrupting data needed to load the kernel and ramdisk.
>
> Allocate writable, 8-byte aligned copies of the base FDT and overlays
> with memalign() and fdt_open_into(). Grow the base buffer as needed,
> apply overlays to it and pack the final tree. Free each temporary
> overlay copy after application and check fdt_pack() errors.
>
> Fixes: 8fbcc0e0e839 ("boot: Assure FDT is always 8-byte aligned")
> Fixes: 881f0b77dc8c ("image: apply FDTOs on FDT image node")
> Signed-off-by: James Hilliard <james.hilliard1 at gmail.com>
> Cc: Jamie Gibbons <Jamie.Gibbons at microchip.com>
I can confirm that this patch fixes the remaining overlay application
boot errors I was seeing - specifically fdt_open_into overlay alignment
error. Thank you all.
Tested with Microchip's PolarFire SoC Icicle kit ES.
Tested-by: Jamie Gibbons <jamie.gibbons at microchip.com>
> ---
> Changes v1 -> v2:
> - also fix alignment issues
> Changes v2 -> v3:
> - do not print on -ENOMEM errors
> - let boot_get_fdt_fit_into_buffer() reuse buffers and handle
> rounding
> Changes v3 -> v4:
> - rebase on master
> ---
> boot/image-fit.c | 163 +++++++++++++++++++++++++++++++++++----------
> --
> 1 file changed, 124 insertions(+), 39 deletions(-)
>
> diff --git a/boot/image-fit.c b/boot/image-fit.c
> index 3ed69b5f7bc..c70b7707c00 100644
> --- a/boot/image-fit.c
> +++ b/boot/image-fit.c
> @@ -2368,6 +2368,75 @@ int boot_get_setup_fit(struct bootm_headers
> *images, uint8_t arch,
> }
>
> #ifndef USE_HOSTCC
> +#ifdef CONFIG_OF_LIBFDT_OVERLAY
> +static int boot_get_fdt_fit_into_buffer(const void *src, ulong
> srclen,
> + ulong extra, ulong
> min_dstlen,
> + void **fdtdstbuf, ulong
> *fdtdstlenp)
> +{
> + const void *fdtsrcbuf;
> + void *tmp = NULL;
> + void *dstbuf, *newdstbuf;
> + ulong dstlen, newdstlen;
> + int err;
> +
> + /* Make sure the source FDT/DTO is 8-byte aligned for libfdt.
> */
> + fdtsrcbuf = src;
> + if (!IS_ALIGNED((uintptr_t)src, 8)) {
> + tmp = memalign(8, srclen);
> + if (!tmp)
> + return -ENOMEM;
> +
> + memcpy(tmp, src, srclen);
> + fdtsrcbuf = tmp;
> + }
> +
> + newdstlen = ALIGN(fdt_totalsize(fdtsrcbuf) + extra, SZ_4K);
> + min_dstlen = ALIGN(min_dstlen, SZ_4K);
> + if (newdstlen < min_dstlen)
> + newdstlen = min_dstlen;
> +
> + dstbuf = *fdtdstbuf;
> + dstlen = dstbuf ? *fdtdstlenp : 0;
> +
> + /*
> + * If the caller already provided a large enough writable
> buffer,
> + * and we're not moving the FDT, nothing to do.
> + */
> + if (dstbuf && dstlen >= newdstlen && dstbuf == fdtsrcbuf) {
> + free(tmp);
> + return 0;
> + }
> +
> + /* Try to reuse existing destination buffer if it is large
> enough. */
> + if (dstbuf && dstlen >= newdstlen) {
> + err = fdt_open_into(fdtsrcbuf, dstbuf, dstlen);
> + free(tmp);
> + if (err < 0)
> + return err;
> + return 0;
> + }
> +
> + newdstbuf = memalign(8, newdstlen);
> + if (!newdstbuf) {
> + free(tmp);
> + return -ENOMEM;
> + }
> +
> + err = fdt_open_into(fdtsrcbuf, newdstbuf, newdstlen);
> + free(tmp);
> + if (err < 0) {
> + free(newdstbuf);
> + return err;
> + }
> +
> + free(dstbuf);
> + *fdtdstbuf = newdstbuf;
> + *fdtdstlenp = newdstlen;
> +
> + return 0;
> +}
> +#endif
> +
> int boot_get_fdt_fit(struct bootm_headers *images, ulong addr,
> const char **fit_unamep, const char
> **fit_uname_configp,
> int arch, ulong *datap, ulong *lenp)
> @@ -2380,18 +2449,12 @@ int boot_get_fdt_fit(struct bootm_headers
> *images, ulong addr,
> char *next_config = NULL;
> ulong load, len;
> #ifdef CONFIG_OF_LIBFDT_OVERLAY
> - ulong ovload, ovlen, ovcopylen;
> + ulong ovload, ovlen, ovcopylen, need;
> const char *uconfig;
> const char *uname;
> - /*
> - * of_flat_tree is storing the void * returned by map_sysmem,
> then its
> - * address is passed to boot_relocate_fdt which expects a
> char ** and it
> - * is then cast into a ulong. Setting its type to void *
> would require
> - * to cast its address to char ** when passing it to
> boot_relocate_fdt.
> - * Instead, let's be lazy and use void *.
> - */
> - char *of_flat_tree;
> - void *base, *ov, *ovcopy = NULL;
> + void *ovcopy = NULL;
> + void *base_buf = NULL;
> + ulong base_buf_size = 0;
> int i, err, noffset, ov_noffset;
> #endif
>
> @@ -2434,18 +2497,25 @@ int boot_get_fdt_fit(struct bootm_headers
> *images, ulong addr,
> /* we need to apply overlays */
>
> #ifdef CONFIG_OF_LIBFDT_OVERLAY
> - /* Relocate FDT so resizing does not overwrite other data in
> FIT. */
> - of_flat_tree = map_sysmem(load, len);
> - len = ALIGN(fdt_totalsize(load), SZ_4K);
> - err = boot_relocate_fdt(&of_flat_tree, &len);
> - if (err) {
> - printf("Required FDT relocation for applying DTOs
> failed: %d\n",
> - err);
> - fdt_noffset = -EBADF;
> + /*
> + * Make a writable copy of the base FDT for applying
> overlays.
> + *
> + * Do not use boot_relocate_fdt() here: it allocates from the
> bootm map and
> + * may overlap with the FIT buffer (still needed to load the
> kernel /
> + * ramdisk) when the FIT is loaded into RAM.
> + */
> + err = boot_get_fdt_fit_into_buffer(map_sysmem(load, len),
> len,
> + CONFIG_SYS_FDT_PAD, 0,
> &base_buf,
> + &base_buf_size);
> + if (err < 0) {
> + if (err != -ENOMEM)
> + printf("Required FDT copy for applying DTOs
> failed: %s\n",
> + fdt_strerror(err));
> + fdt_noffset = err;
> goto out;
> }
>
> - load = (ulong)of_flat_tree;
> + len = fdt_off_dt_strings(base_buf) +
> fdt_size_dt_strings(base_buf);
>
> /* apply extra configs in FIT first, followed by args */
> for (i = 1; ; i++) {
> @@ -2489,48 +2559,61 @@ int boot_get_fdt_fit(struct bootm_headers
> *images, ulong addr,
> }
> debug("%s loaded at 0x%08lx len=0x%08lx\n",
> uname, ovload, ovlen);
> - ov = map_sysmem(ovload, ovlen);
> -
> - ovcopylen = ALIGN(fdt_totalsize(ov), SZ_4K);
> - ovcopy = malloc(ovcopylen);
> - if (!ovcopy) {
> - printf("failed to duplicate DTO before
> application\n");
> - fdt_noffset = -ENOMEM;
> - goto out;
> - }
> -
> - err = fdt_open_into(ov, ovcopy, ovcopylen);
> + err = boot_get_fdt_fit_into_buffer(map_sysmem(ovload,
> ovlen),
> + ovlen, 0, 0,
> &ovcopy,
> + &ovcopylen);
> if (err < 0) {
> - printf("failed on fdt_open_into for DTO:
> %s\n",
> - fdt_strerror(err));
> + if (err != -ENOMEM)
> + printf("failed on fdt_open_into for
> DTO: %s\n",
> + fdt_strerror(err));
> fdt_noffset = err;
> goto out;
> }
>
> - base = map_sysmem(load, len + ovlen);
> - err = fdt_open_into(base, base, len + ovlen);
> + /*
> + * Ensure the base FDT buffer is open and has enough
> room for
> + * the overlay. Grow it on demand.
> + */
> + need = len + ovcopylen + CONFIG_SYS_FDT_PAD;
> + err = boot_get_fdt_fit_into_buffer(base_buf,
> base_buf_size, 0,
> + need, &base_buf,
> + &base_buf_size);
> if (err < 0) {
> - printf("failed on fdt_open_into: %s\n",
> - fdt_strerror(err));
> + if (err != -ENOMEM)
> + printf("failed to expand FDT for DTO
> application: %s\n",
> + fdt_strerror(err));
> fdt_noffset = err;
> goto out;
> }
>
> /* the verbose method prints out messages on error */
> - err = fdt_overlay_apply_verbose(base, ovcopy);
> + err = fdt_overlay_apply_verbose(base_buf, ovcopy);
> if (err < 0) {
> fdt_noffset = err;
> goto out;
> }
> - fdt_pack(base);
> - len = fdt_totalsize(base);
> + len = fdt_off_dt_strings(base_buf) +
> fdt_size_dt_strings(base_buf);
> +
> + free(ovcopy);
> + ovcopy = NULL;
> }
> +
> + err = fdt_pack(base_buf);
> + if (err < 0) {
> + fdt_noffset = err;
> + goto out;
> + }
> + len = fdt_totalsize(base_buf);
> #else
> printf("config with overlays but CONFIG_OF_LIBFDT_OVERLAY not
> set\n");
> fdt_noffset = -EBADF;
> #endif
>
> out:
> +#ifdef CONFIG_OF_LIBFDT_OVERLAY
> + if (fdt_noffset >= 0 && base_buf)
> + load = map_to_sysmem(base_buf);
> +#endif
> if (datap)
> *datap = load;
> if (lenp)
> @@ -2541,6 +2624,8 @@ out:
> *fit_uname_configp = fit_uname_config;
>
> #ifdef CONFIG_OF_LIBFDT_OVERLAY
> + if (fdt_noffset < 0)
> + free(base_buf);
> free(ovcopy);
> #endif
> free(fit_uname_config_copy);
> --
> 2.43.0
>
More information about the U-Boot
mailing list