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);