diff --git a/doc/README.multi-dtb-fit b/doc/README.multi-dtb-fit index 0d4f068..6cc4965 100644 --- a/doc/README.multi-dtb-fit +++ b/doc/README.multi-dtb-fit @@ -1,8 +1,11 @@ -MULTI DTB FIT +MULTI DTB FIT and SPL_MULTI_DTB_FIT -The purpose of this feature is to enable u-boot to select its DTB from a FIT -appended at the end of the binary. +The purpose of this feature is to enable U-Boot or the SPL to select its DTB +from a FIT appended at the end of the binary. +It comes in two flavors: U-Boot (CONFIG_MULTI_DTB_FIT) and SPL +(CONFIG_SPL_MULTI_DTB_FIT). +U-Boot flavor: Usually the DTB is selected by the SPL and passed down to U-Boot. But some platforms don't use the SPL. In this case MULTI_DTB_FIT can used to provide U-Boot with a choice of DTBs. @@ -13,3 +16,50 @@ the FIT. The selection is done using board_fit_config_name_match() (same as what the SPL uses to select the DTB for U-Boot). The selection happens during fdtdec_setup() which is called during before relocation by board_init_f(). + +SPL flavor: +the SPL uses only a small subset of the DTB and it usually depends more +on the SOC than on the board. So it's usually fine to include a DTB in the +SPL that doesn't exactly match the board. There are howerver some cases +where it's not possible. In the later case, in order to support multiple +boards (or board revisions) with the same SPL binary, SPL_MULTI_DTB_FIT +can be used. +The relevant DTBs are packed into a FIT. This FIT is automatically generated +at the end of the compilation, compressed and appended to u-boot-spl.bin, so +that SPL can locate it and select the correct DTB from inside the FIT. +CONFIG_SPL__OF_LIST is used to list the relevant DTBs. +The compression stage is optional but reduces the impact on the size of the +SPL. LZO and GZIP compressions are supported. By default, the area where the +FIT is uncompressed is dynamicaly allocated but this behaviour can be changed +for platforms that don't provide a HEAP big enough to contain the uncompressed +FIT. +The SPL uses board_fit_config_name_match() to find the correct DTB within the +FIT (same as what the SPL uses to select the DTB for U-Boot). +Uncompression and selection stages happen in fdtdec_setup() which is called +during the early initialization stage of the SPL (spl_early_init() or +spl_init()) + +Impacts and performances (SPL flavor): +The impact of this option is relatively small. Here are some numbers measured +for a TI DRA72 platform: + + +----------+------------+-----------+------------+ + | size | size delta | SPL boot | boot time | + | (bytes) | (bytes) | time (s) | delta (s) | ++---------------------------+----------+------------+-----------+------------+ +| 1 DTB | | | | | ++---------------------------+----------+------------+-----------+------------+ +| reference | 125305 | 0 | 1.389 | 0 | +| LZO (dynamic allocation) | 125391 | 86 | 1.381 | -0.008 | ++---------------------------+----------+------------+-----------+------------+ +| 4 DTBs (DRA7, DRA71, | | | | | +| DRA72, DRA72 revC) | | | | | ++---------------------------+----------+------------+-----------+------------+ +| LZO (dynamic allocation) | 125991 | 686 | 1.39 | 0.001 | +| LZO (user defined area) | 125927 | 622 | 1.403 | 0.014 | +| GZIP (user defined area) | 133880 | 8575 | 1.421 | 0.032 | +| No compression (in place) | 137472 | 12167 | 1.412 | 0.023 | ++---------------------------+----------+------------+-----------+------------+ + +Note: SPL boot time is the time elapsed between the 'reset' command is entered +and the time when the first U-Boot (not SPL) version string is displayed. diff --git a/dts/Kconfig b/dts/Kconfig index 454acaa..daa757d 100644 --- a/dts/Kconfig +++ b/dts/Kconfig @@ -130,6 +130,89 @@ config MULTI_DTB_FIT the correct DTB to be used. Use this if you need to support multiple DTBs but don't use the SPL. + +config SPL_MULTI_DTB_FIT + depends on SPL_LOAD_FIT && SPL_OF_CONTROL && !SPL_OF_PLATDATA + bool "Support embedding several DTBs in a FIT image for the SPL" + help + This option provides the SPL with the ability to select its own + DTB at runtime from an appended FIT image containing several DTBs. + This allows using the same SPL binary on multiple platforms. + The primary purpose is to handle different versions of + the same platform without tweaking the platform code if the + differences can be expressed in the DTBs (common examples are: bus + capabilities, pad configurations). + +config SPL_OF_LIST + string "List of device tree files to include for DT control in SPL" + depends on SPL_MULTI_DTB_FIT + default OF_LIST + help + This option specifies a list of device tree files to use for DT + control in the SPL. These will be packaged into a FIT. At run-time, + the SPL will select the correct DT to use by examining the + hardware (e.g. reading a board ID value). This is a list of + device tree files (without the directory or .dtb suffix) + separated by . + +choice + prompt "SPL OF LIST compression" + depends on SPL_MULTI_DTB_FIT + default SPL_MULTI_DTB_FIT_LZO + +config SPL_MULTI_DTB_FIT_LZO + bool "LZO" + depends on SYS_MALLOC_F + select SPL_LZO + help + Compress the FIT image containing the DTBs available for the SPL + using LZO compression. (requires lzop on host). + +config SPL_MULTI_DTB_FIT_GZIP + bool "GZIP" + depends on SYS_MALLOC_F + select SPL_GZIP + help + Compress the FIT image containing the DTBs available for the SPL + using GZIP compression. (requires gzip on host) + +config SPL_MULTI_DTB_FIT_NO_COMPRESSION + bool "No compression" + help + Do not compress the FIT image containing the DTBs available for the SPL. + Use this options only if LZO is not available and the DTBs are very small. +endchoice + +choice + prompt "Location of uncompressed DTBs " + depends on (SPL_MULTI_DTB_FIT_GZIP || SPL_MULTI_DTB_FIT_LZO) + default SPL_MULTI_DTB_FIT_DYN_ALLOC if SYS_MALLOC_F + +config SPL_MULTI_DTB_FIT_DYN_ALLOC + bool "Dynamically allocate the memory" + depends on SYS_MALLOC_F + +config SPL_MULTI_DTB_FIT_USER_DEFINED_AREA + bool "User-defined location" +endchoice + +config SPL_MULTI_DTB_FIT_UNCOMPRESS_SZ + hex "Size of memory reserved to uncompress the DTBs" + depends on (SPL_MULTI_DTB_FIT_GZIP || SPL_MULTI_DTB_FIT_LZO) + default 0x8000 + help + This is the size of this area where the DTBs are uncompressed. + If this area is dynamically allocated, make sure that + SPL_SYS_MALLOC_F_LEN is big enough to contain it. + +config SPL_MULTI_DTB_FIT_USER_DEF_ADDR + hex "Address of memory where dtbs are uncompressed" + depends on SPL_MULTI_DTB_FIT_USER_DEFINED_AREA + help + the FIT image containing the DTBs is uncompressed in an area defined + at compilation time. This is the address of this area. It must be + aligned on 2-byte boundary. + config OF_SPL_REMOVE_PROPS string "List of device tree properties to drop for SPL" depends on SPL_OF_CONTROL diff --git a/lib/fdtdec.c b/lib/fdtdec.c index f26d2eb..45f3fe7 100644 --- a/lib/fdtdec.c +++ b/lib/fdtdec.c @@ -15,6 +15,7 @@ #include #include #include +#include DECLARE_GLOBAL_DATA_PTR; @@ -1203,9 +1204,66 @@ int fdtdec_setup_memory_banksize(void) } #endif +#if CONFIG_IS_ENABLED(MULTI_DTB_FIT) +# if CONFIG_IS_ENABLED(MULTI_DTB_FIT_GZIP) ||\ + CONFIG_IS_ENABLED(MULTI_DTB_FIT_LZO) +static int uncompress_blob(const void *src, ulong sz_src, void **dstp) +{ + size_t sz_out = CONFIG_SPL_MULTI_DTB_FIT_UNCOMPRESS_SZ; + ulong sz_in = sz_src; + void *dst; + int rc; + + if (CONFIG_IS_ENABLED(GZIP)) + if (gzip_parse_header(src, sz_in) < 0) + return -1; + if (CONFIG_IS_ENABLED(LZO)) + if (!lzop_is_valid_header(src)) + return -EBADMSG; + + if (CONFIG_IS_ENABLED(MULTI_DTB_FIT_DYN_ALLOC)) { + dst = malloc(sz_out); + if (!dst) { + puts("uncompress_blob: Unable to allocate memory\n"); + return -ENOMEM; + } + } else { +# if CONFIG_IS_ENABLED(MULTI_DTB_FIT_USER_DEFINED_AREA) + dst = (void *)CONFIG_VAL(MULTI_DTB_FIT_USER_DEF_ADDR); +# else + return -ENOTSUPP; +# endif + } + + if (CONFIG_IS_ENABLED(GZIP)) + rc = gunzip(dst, sz_out, (u8 *)src, &sz_in); + else if (CONFIG_IS_ENABLED(LZO)) + rc = lzop_decompress(src, sz_in, dst, &sz_out); + + if (rc < 0) { + /* not a valid compressed blob */ + puts("uncompress_blob: Unable to uncompress\n"); + if (CONFIG_IS_ENABLED(MULTI_DTB_FIT_DYN_ALLOC)) + free(dst); + return -EBADMSG; + } + *dstp = dst; + return 0; +} +# else +static int uncompress_blob(const void *src, ulong sz_src, void **dstp) +{ + return -ENOTSUPP; +} +# endif +#endif + int fdtdec_setup(void) { #if CONFIG_IS_ENABLED(OF_CONTROL) +# if CONFIG_IS_ENABLED(MULTI_DTB_FIT) + void *fdt_blob; +# endif # ifdef CONFIG_OF_EMBED /* Get a pointer to the FDT */ gd->fdt_blob = __dtb_dt_begin; @@ -1216,15 +1274,6 @@ int fdtdec_setup(void) gd->fdt_blob = (ulong *)&_image_binary_end; else gd->fdt_blob = (ulong *)&__bss_end; - -# elif defined CONFIG_MULTI_DTB_FIT - gd->fdt_blob = locate_dtb_in_fit(&_end); - - if (gd->fdt_blob == NULL || gd->fdt_blob <= ((void *)&_end)) { - puts("Failed to find proper dtb in embedded FIT Image\n"); - return -1; - } - # else /* FDT is at end of image */ gd->fdt_blob = (ulong *)&_end; @@ -1243,7 +1292,27 @@ int fdtdec_setup(void) gd->fdt_blob = (void *)env_get_ulong("fdtcontroladdr", 16, (uintptr_t)gd->fdt_blob); # endif + +# if CONFIG_IS_ENABLED(MULTI_DTB_FIT) + /* + * Try and uncompress the blob. + * Unfortunately there is no way to know how big the input blob really + * is. So let us set the maximum input size arbitrarily high. 16MB + * ought to be more than enough for packed DTBs. + */ + if (uncompress_blob(gd->fdt_blob, 0x1000000, &fdt_blob) == 0) + gd->fdt_blob = fdt_blob; + + /* + * Check if blob is a FIT images containings DTBs. + * If so, pick the most relevant + */ + fdt_blob = locate_dtb_in_fit(gd->fdt_blob); + if (fdt_blob) + gd->fdt_blob = fdt_blob; +# endif #endif + return fdtdec_prepare_fdt(); } diff --git a/scripts/Makefile.spl b/scripts/Makefile.spl index b86ea76..49b27ac 100644 --- a/scripts/Makefile.spl +++ b/scripts/Makefile.spl @@ -209,10 +209,21 @@ cmd_cat = cat $(filter-out $(PHONY), $^) > $@ quiet_cmd_copy = COPY $@ cmd_copy = cp $< $@ +ifneq ($(CONFIG_SPL_MULTI_DTB_FIT),y) +FINAL_DTB_CONTAINER = $(obj)/$(SPL_BIN).dtb +else ifeq ($(CONFIG_SPL_MULTI_DTB_FIT_LZO),y) +FINAL_DTB_CONTAINER = $(obj)/$(SPL_BIN).multidtb.fit.lzo +else ifeq ($(CONFIG_SPL_MULTI_DTB_FIT_GZIP),y) +FINAL_DTB_CONTAINER = $(obj)/$(SPL_BIN).multidtb.fit.gz +else +FINAL_DTB_CONTAINER = $(obj)/$(SPL_BIN).multidtb.fit +endif + + ifeq ($(CONFIG_$(SPL_TPL_)OF_CONTROL)$(CONFIG_OF_SEPARATE)$(CONFIG_$(SPL_TPL_)OF_PLATDATA),yy) $(obj)/$(SPL_BIN)-dtb.bin: $(obj)/$(SPL_BIN)-nodtb.bin \ $(if $(CONFIG_SPL_SEPARATE_BSS),,$(obj)/$(SPL_BIN)-pad.bin) \ - $(obj)/$(SPL_BIN).dtb FORCE + $(FINAL_DTB_CONTAINER) FORCE $(call if_changed,cat) $(obj)/$(SPL_BIN).bin: $(obj)/$(SPL_BIN)-dtb.bin FORCE @@ -383,6 +394,28 @@ checkdtoc: tools PHONY += FORCE FORCE: +PHONY += dtbs +dtbs: + $(Q)$(MAKE) $(build)=dts dtbs + # Declare the contents of the .PHONY variable as phony. We keep that # information in a variable so we can use it in if_changed and friends. .PHONY: $(PHONY) + +SHRUNK_ARCH_DTB = $(patsubst %,$(obj)/dts/%.dtb,$(subst ",,$(CONFIG_SPL_OF_LIST))) +.SECONDEXPANSION: +$(SHRUNK_ARCH_DTB): $$(patsubst $(obj)/dts/%, arch/$(ARCH)/dts/%, $$@) + $(call if_changed,fdtgrep) + +MKIMAGEFLAGS_$(SPL_BIN).multidtb.fit = -f auto -A $(ARCH) -T firmware -C none -O u-boot \ + -n "Multi DTB fit image for $(SPL_BIN)" -E \ + $(patsubst %,-b %,$(SHRUNK_ARCH_DTB)) + +$(obj)/$(SPL_BIN).multidtb.fit: /dev/null $(SHRUNK_ARCH_DTB) FORCE + $(call if_changed,mkimage) + +$(obj)/$(SPL_BIN).multidtb.fit.gz: $(obj)/$(SPL_BIN).multidtb.fit + @gzip -kf9 $< > $@ + +$(obj)/$(SPL_BIN).multidtb.fit.lzo: $(obj)/$(SPL_BIN).multidtb.fit + @lzop -f9 $< > $@