Add SPL_BLOBLIST_FDT to copy the devicetree that SPL loads for the next phase into the bloblist, unless the bloblist already has one.
The next phase then takes its devicetree from the bloblist and applies the devicetree overlays in the bloblist to it with the existing code. An earlier boot stage, e.g. the one that initializes the DRAM, can then pass an overlay with only what it knows, without having to provide the whole devicetree of the next phase. If the bloblist has no room for the devicetree, move it to a bigger buffer from the SPL heap, so that the earlier boot stage does not have to know how big the devicetree is. The next phase only finds the moved bloblist through the registers, so depend on SPL_BLOBLIST_PASS_REGS. If the devicetree cannot be added, report it and carry on; the next phase then decides whether it can boot without it. Signed-off-by: David Lechner <[email protected]> --- common/Kconfig | 18 ++++++++++++ common/spl/spl.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+) diff --git a/common/Kconfig b/common/Kconfig index 5b728cfe324..c0251c95740 100644 --- a/common/Kconfig +++ b/common/Kconfig @@ -1251,6 +1251,24 @@ config SPL_BLOBLIST_PASS_REGS default jump_to_image() uses them, other implementations need to do the same. +config SPL_BLOBLIST_FDT + bool "Add the devicetree of the next phase to the bloblist" + depends on SPL_LOAD_FIT || SPL_LOAD_FIT_FULL + depends on SPL_BLOBLIST_PASS_REGS + help + Copy the devicetree that SPL loads for the next phase, normally + U-Boot proper, into the bloblist, unless the bloblist already has + one. The next phase then takes its devicetree from the bloblist and + applies any devicetree overlays in the bloblist to it, so an earlier + boot stage only has to pass an overlay with what it knows, e.g. the + DRAM layout. + + If the bloblist does not have room for the devicetree, plus + SYS_FDT_PAD for applying the overlays, SPL moves it to a bigger + buffer from its malloc() heap and passes the new address to the + next phase in registers. That memory must stay intact until the + next phase has relocated the bloblist. + endif # SPL_BLOBLIST if TPL_BLOBLIST diff --git a/common/spl/spl.c b/common/spl/spl.c index 6d81984428c..7cc6a031b66 100644 --- a/common/spl/spl.c +++ b/common/spl/spl.c @@ -39,6 +39,7 @@ #include <dm/device-internal.h> #include <dm/uclass-internal.h> #include <linux/compiler.h> +#include <linux/libfdt.h> #include <fdt_support.h> #include <bootcount.h> #include <wdt.h> @@ -465,6 +466,82 @@ static inline int write_spl_handoff(void) { return 0; } #endif /* HANDOFF */ +#if CONFIG_IS_ENABLED(BLOBLIST_FDT) +/** + * spl_bloblist_grow() - Make sure the bloblist has room for a new blob + * + * The previous phase may have passed a bloblist with only little free space. + * If a blob of the given size does not fit, move the bloblist to a bigger + * buffer. + * + * @size: Size of the blob, without its record header + * Return: 0 if OK, -ENOMEM if there is no memory for a bigger bloblist + */ +static int spl_bloblist_grow(ulong size) +{ + struct bloblist_hdr *hdr = gd->bloblist; + ulong align, need; + void *buf; + + need = ALIGN(bloblist_get_size(), BLOBLIST_BLOB_ALIGN) + + sizeof(struct bloblist_rec) + ALIGN(size, BLOBLIST_BLOB_ALIGN); + if (need <= bloblist_get_total_size()) + return 0; + + align = max_t(ulong, BLOBLIST_ALIGN, 1UL << hdr->align_log2); + buf = memalign(align, need); + if (!buf) + return -ENOMEM; + + log_debug("Moving bloblist to %p, size %lx\n", buf, need); + + return bloblist_reloc(buf, need); +} + +/** + * spl_bloblist_add_fdt() - Add the devicetree of the next phase to the bloblist + * + * Copy the devicetree that SPL loaded for the next phase into the bloblist, + * unless the bloblist already has one. If the bloblist is too small, it is + * moved to a bigger buffer first. + * + * @spl_image: Image that SPL is about to start + * Return: 0 if OK, -ENOENT if no devicetree was loaded, -ENOMEM if there is + * no memory for a bigger bloblist, -ENOSPC if the bloblist has no room for it + */ +static int spl_bloblist_add_fdt(struct spl_image_info *spl_image) +{ + void *fdt = spl_image_fdt_addr(spl_image); + void *blob; + int size, ret; + + if (bloblist_find(BLOBLISTT_CONTROL_FDT, 0)) + return 0; + + if (!fdt) + return -ENOENT; + + size = fdt_totalsize(fdt); + /* Leave room for the next phase to apply overlays to it */ + ret = spl_bloblist_grow(size + CONFIG_SYS_FDT_PAD); + if (ret) + return ret; + + blob = bloblist_add(BLOBLISTT_CONTROL_FDT, size, 0); + if (!blob) + return -ENOSPC; + + memcpy(blob, fdt, size); + + return 0; +} +#else +static int spl_bloblist_add_fdt(struct spl_image_info *spl_image) +{ + return 0; +} +#endif /* BLOBLIST_FDT */ + /** * get_bootstage_id() - Get the bootstage ID to emit * @@ -851,6 +928,12 @@ void board_init_r(gd_t *dummy1, ulong dummy2) hang(); } } + if (CONFIG_IS_ENABLED(BLOBLIST_FDT)) { + ret = spl_bloblist_add_fdt(&spl_image); + if (ret) + printf(PHASE_PROMPT + "Failed to add FDT to bloblist (ret=%d)\n", ret); + } if (CONFIG_IS_ENABLED(BLOBLIST)) { ret = bloblist_finish(); if (ret) -- 2.53.0
