[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