"Das U-Boot" Source Tree
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

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 <james.hilliard1@gmail.com>
Cc: Jamie Gibbons <Jamie.Gibbons@microchip.com>
Reviewed-by: Marek Vasut <marek.vasut@mailbox.org>

authored by

James Hilliard and committed by
Tom Rini
5ebf0c55 a79edd52

+126 -39
+126 -39
boot/image-fit.c
··· 2368 2368 } 2369 2369 2370 2370 #ifndef USE_HOSTCC 2371 + #ifdef CONFIG_OF_LIBFDT_OVERLAY 2372 + static int boot_get_fdt_fit_into_buffer(const void *src, ulong srclen, 2373 + ulong extra, ulong min_dstlen, 2374 + void **fdtdstbuf, ulong *fdtdstlenp) 2375 + { 2376 + const void *fdtsrcbuf; 2377 + void *tmp = NULL; 2378 + void *dstbuf, *newdstbuf = NULL; 2379 + ulong dstlen, newdstlen; 2380 + int err = 0; 2381 + 2382 + /* Make sure the source FDT/DTO is 8-byte aligned for libfdt. */ 2383 + fdtsrcbuf = src; 2384 + if (!IS_ALIGNED((uintptr_t)src, 8)) { 2385 + tmp = memalign(8, srclen); 2386 + if (!tmp) 2387 + return -ENOMEM; 2388 + 2389 + memcpy(tmp, src, srclen); 2390 + fdtsrcbuf = tmp; 2391 + } 2392 + 2393 + newdstlen = ALIGN(fdt_totalsize(fdtsrcbuf) + extra, SZ_4K); 2394 + min_dstlen = ALIGN(min_dstlen, SZ_4K); 2395 + if (newdstlen < min_dstlen) 2396 + newdstlen = min_dstlen; 2397 + 2398 + dstbuf = *fdtdstbuf; 2399 + dstlen = dstbuf ? *fdtdstlenp : 0; 2400 + 2401 + /* 2402 + * If the caller already provided a large enough writable buffer, 2403 + * and we're not moving the FDT, nothing to do. 2404 + */ 2405 + if (dstlen >= newdstlen && dstbuf == fdtsrcbuf) 2406 + goto out; 2407 + 2408 + /* Try to reuse existing destination buffer if it is large enough. */ 2409 + if (dstbuf && dstlen >= newdstlen) { 2410 + err = fdt_open_into(fdtsrcbuf, dstbuf, dstlen); 2411 + goto out; 2412 + } 2413 + 2414 + newdstbuf = memalign(8, newdstlen); 2415 + if (!newdstbuf) { 2416 + err = -ENOMEM; 2417 + goto out; 2418 + } 2419 + 2420 + err = fdt_open_into(fdtsrcbuf, newdstbuf, newdstlen); 2421 + if (err < 0) 2422 + goto out; 2423 + 2424 + free(dstbuf); 2425 + *fdtdstbuf = newdstbuf; 2426 + *fdtdstlenp = newdstlen; 2427 + newdstbuf = NULL; 2428 + 2429 + out: 2430 + free(newdstbuf); 2431 + free(tmp); 2432 + return err; 2433 + } 2434 + #endif 2435 + 2371 2436 int boot_get_fdt_fit(struct bootm_headers *images, ulong addr, 2372 2437 const char **fit_unamep, const char **fit_uname_configp, 2373 2438 int arch, ulong *datap, ulong *lenp) ··· 2380 2445 char *next_config = NULL; 2381 2446 ulong load, len; 2382 2447 #ifdef CONFIG_OF_LIBFDT_OVERLAY 2383 - ulong ovload, ovlen, ovcopylen; 2448 + ulong ovload, ovlen, ovcopylen, need; 2384 2449 const char *uconfig; 2385 2450 const char *uname; 2386 - /* 2387 - * of_flat_tree is storing the void * returned by map_sysmem, then its 2388 - * address is passed to boot_relocate_fdt which expects a char ** and it 2389 - * is then cast into a ulong. Setting its type to void * would require 2390 - * to cast its address to char ** when passing it to boot_relocate_fdt. 2391 - * Instead, let's be lazy and use void *. 2392 - */ 2393 - char *of_flat_tree; 2394 - void *base, *ov, *ovcopy = NULL; 2451 + void *ovcopy = NULL; 2452 + void *base_buf = NULL; 2453 + ulong base_buf_size = 0; 2395 2454 int i, err, noffset, ov_noffset; 2396 2455 #endif 2397 2456 ··· 2434 2493 /* we need to apply overlays */ 2435 2494 2436 2495 #ifdef CONFIG_OF_LIBFDT_OVERLAY 2437 - /* Relocate FDT so resizing does not overwrite other data in FIT. */ 2438 - of_flat_tree = map_sysmem(load, len); 2439 - len = ALIGN(fdt_totalsize(load), SZ_4K); 2440 - err = boot_relocate_fdt(&of_flat_tree, &len); 2441 - if (err) { 2442 - printf("Required FDT relocation for applying DTOs failed: %d\n", 2443 - err); 2444 - fdt_noffset = -EBADF; 2496 + /* 2497 + * Make a writable copy of the base FDT for applying overlays. 2498 + * 2499 + * Do not use boot_relocate_fdt() here: it allocates from the bootm map and 2500 + * may overlap with the FIT buffer (still needed to load the kernel / 2501 + * ramdisk) when the FIT is loaded into RAM. 2502 + */ 2503 + err = boot_get_fdt_fit_into_buffer(map_sysmem(load, len), len, 2504 + CONFIG_SYS_FDT_PAD, 0, &base_buf, 2505 + &base_buf_size); 2506 + if (err < 0) { 2507 + if (err != -ENOMEM) 2508 + printf("Required FDT copy for applying DTOs failed: %s\n", 2509 + fdt_strerror(err)); 2510 + fdt_noffset = err; 2445 2511 goto out; 2446 2512 } 2447 2513 2448 - load = (ulong)of_flat_tree; 2514 + /* 2515 + * Track packed DTB data size (same as libfdt internal fdt_data_size_()). 2516 + * fdt_off_dt_strings() is an offset from the blob start, so this includes 2517 + * headers/reserve map/struct blocks. Do not use fdt_totalsize() here since 2518 + * it includes free space and would overestimate growth requirements. 2519 + */ 2520 + len = fdt_off_dt_strings(base_buf) + fdt_size_dt_strings(base_buf); 2449 2521 2450 2522 /* apply extra configs in FIT first, followed by args */ 2451 2523 for (i = 1; ; i++) { ··· 2489 2561 } 2490 2562 debug("%s loaded at 0x%08lx len=0x%08lx\n", 2491 2563 uname, ovload, ovlen); 2492 - ov = map_sysmem(ovload, ovlen); 2493 - 2494 - ovcopylen = ALIGN(fdt_totalsize(ov), SZ_4K); 2495 - ovcopy = malloc(ovcopylen); 2496 - if (!ovcopy) { 2497 - printf("failed to duplicate DTO before application\n"); 2498 - fdt_noffset = -ENOMEM; 2499 - goto out; 2500 - } 2501 - 2502 - err = fdt_open_into(ov, ovcopy, ovcopylen); 2564 + err = boot_get_fdt_fit_into_buffer(map_sysmem(ovload, ovlen), 2565 + ovlen, 0, 0, &ovcopy, 2566 + &ovcopylen); 2503 2567 if (err < 0) { 2504 - printf("failed on fdt_open_into for DTO: %s\n", 2505 - fdt_strerror(err)); 2568 + if (err != -ENOMEM) 2569 + printf("failed on fdt_open_into for DTO: %s\n", 2570 + fdt_strerror(err)); 2506 2571 fdt_noffset = err; 2507 2572 goto out; 2508 2573 } 2509 2574 2510 - base = map_sysmem(load, len + ovlen); 2511 - err = fdt_open_into(base, base, len + ovlen); 2575 + /* 2576 + * Ensure the base FDT buffer is open and has enough room for 2577 + * the overlay. Grow it on demand. 2578 + */ 2579 + need = len + ovcopylen + CONFIG_SYS_FDT_PAD; 2580 + err = boot_get_fdt_fit_into_buffer(base_buf, base_buf_size, 0, 2581 + need, &base_buf, 2582 + &base_buf_size); 2512 2583 if (err < 0) { 2513 - printf("failed on fdt_open_into: %s\n", 2514 - fdt_strerror(err)); 2584 + if (err != -ENOMEM) 2585 + printf("failed to expand FDT for DTO application: %s\n", 2586 + fdt_strerror(err)); 2515 2587 fdt_noffset = err; 2516 2588 goto out; 2517 2589 } 2518 2590 2519 2591 /* the verbose method prints out messages on error */ 2520 - err = fdt_overlay_apply_verbose(base, ovcopy); 2592 + err = fdt_overlay_apply_verbose(base_buf, ovcopy); 2521 2593 if (err < 0) { 2522 2594 fdt_noffset = err; 2523 2595 goto out; 2524 2596 } 2525 - fdt_pack(base); 2526 - len = fdt_totalsize(base); 2597 + len = fdt_off_dt_strings(base_buf) + fdt_size_dt_strings(base_buf); 2598 + 2599 + free(ovcopy); 2600 + ovcopy = NULL; 2527 2601 } 2602 + 2603 + err = fdt_pack(base_buf); 2604 + if (err < 0) { 2605 + fdt_noffset = err; 2606 + goto out; 2607 + } 2608 + len = fdt_totalsize(base_buf); 2528 2609 #else 2529 2610 printf("config with overlays but CONFIG_OF_LIBFDT_OVERLAY not set\n"); 2530 2611 fdt_noffset = -EBADF; 2531 2612 #endif 2532 2613 2533 2614 out: 2615 + #ifdef CONFIG_OF_LIBFDT_OVERLAY 2616 + if (fdt_noffset >= 0 && base_buf) 2617 + load = map_to_sysmem(base_buf); 2618 + #endif 2534 2619 if (datap) 2535 2620 *datap = load; 2536 2621 if (lenp) ··· 2541 2626 *fit_uname_configp = fit_uname_config; 2542 2627 2543 2628 #ifdef CONFIG_OF_LIBFDT_OVERLAY 2629 + if (fdt_noffset < 0) 2630 + free(base_buf); 2544 2631 free(ovcopy); 2545 2632 #endif 2546 2633 free(fit_uname_config_copy);