spl: dm: Make it possible for the SPL to pick its own DTB from a FIT
authorJean-Jacques Hiblot <jjhiblot@ti.com>
Fri, 15 Sep 2017 10:57:32 +0000 (12:57 +0200)
committerTom Rini <trini@konsulko.com>
Fri, 6 Oct 2017 15:26:41 +0000 (11:26 -0400)
u-boot can be embedded within a FIT image with multiple DTBs. It then
selects at run-time  which one is best suited for the platform.
Use the same principle here for the SPL: put the DTBs in a FIT image,
compress it (LZO, GZIP, or no compression) and append it at the end of the
SPL.

Signed-off-by: Jean-Jacques Hiblot <jjhiblot@ti.com>
[trini: Move default y of SPL_MULTI_DTB_FIT_DYN_ALLOC to it being the
default choice if SYS_MALLOC_F, drop spl.h include from lib/fdtdec.c
it's unused.]
Signed-off-by Tom Rini <trini@konsulko.com>

doc/README.multi-dtb-fit
dts/Kconfig
lib/fdtdec.c
scripts/Makefile.spl

index 0d4f068..6cc4965 100644 (file)
@@ -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.
index 454acaa..daa757d 100644 (file)
@@ -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 <space>.
+
+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
index f26d2eb..45f3fe7 100644 (file)
@@ -15,6 +15,7 @@
 #include <serial.h>
 #include <asm/sections.h>
 #include <linux/ctype.h>
+#include <linux/lzo.h>
 
 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();
 }
 
index b86ea76..49b27ac 100644 (file)
@@ -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 $< > $@