}
#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)
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
/* 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++) {
}
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)
*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);