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 a writable copy of the base FDT with memalign() and
fdt_open_into(), grow it as needed for each overlay, and apply the
overlays to this buffer. Also check fdt_pack() errors and free the
temporary overlay copy after each application.
Fixes: 881f0b77dc8c ("image: apply FDTOs on FDT node")
Signed-off-by: James Hilliard <[email protected]>
---
boot/image-fit.c | 91 +++++++++++++++++++++++++++++++++++-------------
1 file changed, 67 insertions(+), 24 deletions(-)
diff --git a/boot/image-fit.c b/boot/image-fit.c
index 41ab1f552b0..922a3606f47 100644
--- a/boot/image-fit.c
+++ b/boot/image-fit.c
@@ -2356,18 +2356,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 *base, *ov, *ovcopy = NULL, *new_base;
+ void *base_buf = NULL;
+ ulong base_buf_size = 0;
int i, err, noffset, ov_noffset;
#endif
@@ -2410,18 +2404,32 @@ 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.
+ */
+ base = map_sysmem(load, len);
+ base_buf_size = ALIGN(fdt_totalsize(base), SZ_4K) + CONFIG_SYS_FDT_PAD;
+ base_buf = memalign(SZ_4K, base_buf_size);
+ if (!base_buf) {
+ printf("Required FDT copy for applying DTOs failed: out of
memory\n");
+ fdt_noffset = -ENOMEM;
goto out;
}
- load = (ulong)of_flat_tree;
+ err = fdt_open_into(base, base_buf, base_buf_size);
+ if (err < 0) {
+ printf("Required FDT copy for applying DTOs failed: %s\n",
+ fdt_strerror(err));
+ fdt_noffset = err;
+ goto out;
+ }
+
+ load = map_to_sysmem(base_buf);
+ len = fdt_totalsize(base);
/* apply extra configs in FIT first, followed by args */
for (i = 1; ; i++) {
@@ -2482,8 +2490,34 @@ int boot_get_fdt_fit(struct bootm_headers *images, ulong
addr,
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 = ALIGN(len + ovcopylen + CONFIG_SYS_FDT_PAD, SZ_4K);
+ if (need > base_buf_size) {
+ new_base = memalign(SZ_4K, need);
+ if (!new_base) {
+ printf("failed to expand FDT for DTO
application\n");
+ fdt_noffset = -ENOMEM;
+ goto out;
+ }
+
+ err = fdt_open_into(base_buf, new_base, need);
+ if (err < 0) {
+ printf("failed on fdt_open_into while expanding
FDT\n");
+ free(new_base);
+ fdt_noffset = err;
+ goto out;
+ }
+
+ free(base_buf);
+ base_buf = new_base;
+ base_buf_size = need;
+ load = map_to_sysmem(base_buf);
+ }
+
+ err = fdt_open_into(base_buf, base_buf, base_buf_size);
if (err < 0) {
printf("failed on fdt_open_into\n");
fdt_noffset = err;
@@ -2491,13 +2525,20 @@ int boot_get_fdt_fit(struct bootm_headers *images,
ulong addr,
}
/* 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;
+ }
+ err = fdt_pack(base_buf);
if (err < 0) {
fdt_noffset = err;
goto out;
}
- fdt_pack(base);
- len = fdt_totalsize(base);
+ len = fdt_totalsize(base_buf);
+
+ free(ovcopy);
+ ovcopy = NULL;
}
#else
printf("config with overlays but CONFIG_OF_LIBFDT_OVERLAY not set\n");
@@ -2515,6 +2556,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