summaryrefslogtreecommitdiff
path: root/boot
diff options
context:
space:
mode:
authorJames Hilliard <[email protected]>2026-02-12 14:34:09 -0700
committerTom Rini <[email protected]>2026-02-16 11:52:01 -0600
commit5ebf0c55a23ad740da1ec191ac157c490f207e0f (patch)
treed29fce038ef5b032ec838776572439597b9e3866 /boot
parenta79edd52a80ac346ac605362f1a61a6062b2aec8 (diff)
image: fit: Apply overlays using aligned writable FDT copies
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 <[email protected]> Cc: Jamie Gibbons <[email protected]> Reviewed-by: Marek Vasut <[email protected]>
Diffstat (limited to 'boot')
-rw-r--r--boot/image-fit.c165
1 files changed, 126 insertions, 39 deletions
diff --git a/boot/image-fit.c b/boot/image-fit.c
index 3ed69b5f7bc..ddc64debb14 100644
--- a/boot/image-fit.c
+++ b/boot/image-fit.c
@@ -2368,6 +2368,71 @@ 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 = NULL;
+ ulong dstlen, newdstlen;
+ int err = 0;
+
+ /* 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 (dstlen >= newdstlen && dstbuf == fdtsrcbuf)
+ goto out;
+
+ /* Try to reuse existing destination buffer if it is large enough. */
+ if (dstbuf && dstlen >= newdstlen) {
+ err = fdt_open_into(fdtsrcbuf, dstbuf, dstlen);
+ goto out;
+ }
+
+ newdstbuf = memalign(8, newdstlen);
+ if (!newdstbuf) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ err = fdt_open_into(fdtsrcbuf, newdstbuf, newdstlen);
+ if (err < 0)
+ goto out;
+
+ free(dstbuf);
+ *fdtdstbuf = newdstbuf;
+ *fdtdstlenp = newdstlen;
+ newdstbuf = NULL;
+
+out:
+ free(newdstbuf);
+ free(tmp);
+ return err;
+}
+#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 +2445,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 +2493,31 @@ 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;
+ /*
+ * Track packed DTB data size (same as libfdt internal fdt_data_size_()).
+ * fdt_off_dt_strings() is an offset from the blob start, so this includes
+ * headers/reserve map/struct blocks. Do not use fdt_totalsize() here since
+ * it includes free space and would overestimate growth requirements.
+ */
+ 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 +2561,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 +2626,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);