Merge tag 'ti-v2020.07-rc3' of https://gitlab.denx.de/u-boot/custodians/u-boot-ti
[oweals/u-boot.git] / common / spl / spl_fit.c
index cb0cc5299bc5e941825cceae0636be527c164dea..f581a2242132e29903df4b018a8bc4af5980075e 100644 (file)
@@ -6,14 +6,58 @@
 
 #include <common.h>
 #include <errno.h>
+#include <board.h>
+#include <fpga.h>
+#include <gzip.h>
 #include <image.h>
-#include <linux/libfdt.h>
+#include <log.h>
+#include <malloc.h>
 #include <spl.h>
+#include <asm/cache.h>
+#include <linux/libfdt.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#ifndef CONFIG_SPL_LOAD_FIT_APPLY_OVERLAY_BUF_SZ
+#define CONFIG_SPL_LOAD_FIT_APPLY_OVERLAY_BUF_SZ (64 * 1024)
+#endif
 
 #ifndef CONFIG_SYS_BOOTM_LEN
 #define CONFIG_SYS_BOOTM_LEN   (64 << 20)
 #endif
 
+__weak void board_spl_fit_post_load(ulong load_addr, size_t length)
+{
+}
+
+__weak ulong board_spl_fit_size_align(ulong size)
+{
+       return size;
+}
+
+static int find_node_from_desc(const void *fit, int node, const char *str)
+{
+       int child;
+
+       if (node < 0)
+               return -EINVAL;
+
+       /* iterate the FIT nodes and find a matching description */
+       for (child = fdt_first_subnode(fit, node); child >= 0;
+            child = fdt_next_subnode(fit, child)) {
+               int len;
+               const char *desc = fdt_getprop(fit, child, "description", &len);
+
+               if (!desc)
+                       continue;
+
+               if (!strcmp(desc, str))
+                       return child;
+       }
+
+       return -ENOENT;
+}
+
 /**
  * spl_fit_get_image_name(): By using the matching configuration subnode,
  * retrieve the name of an image, specified by a property name and an index
  */
 static int spl_fit_get_image_name(const void *fit, int images,
                                  const char *type, int index,
-                                 char **outname)
+                                 const char **outname)
 {
+       struct udevice *board;
        const char *name, *str;
        __maybe_unused int node;
        int conf_node;
        int len, i;
+       bool found = true;
 
        conf_node = fit_find_config_node(fit);
        if (conf_node < 0) {
@@ -59,12 +105,45 @@ static int spl_fit_get_image_name(const void *fit, int images,
        for (i = 0; i < index; i++) {
                str = strchr(str, '\0') + 1;
                if (!str || (str - name >= len)) {
-                       debug("no string for index %d\n", index);
-                       return -E2BIG;
+                       found = false;
+                       break;
                }
        }
 
-       *outname = (char *)str;
+       if (!found && !board_get(&board)) {
+               int rc;
+               /*
+                * no string in the property for this index. Check if the board
+                * level code can supply one.
+                */
+               rc = board_get_fit_loadable(board, index - i - 1, type, &str);
+               if (rc && rc != -ENOENT)
+                       return rc;
+
+               if (!rc) {
+                       /*
+                        * The board provided a name for a loadable.
+                        * Try to match it against the description properties
+                        * first. If no matching node is found, use it as a
+                        * node name.
+                        */
+                       int node;
+                       int images = fdt_path_offset(fit, FIT_IMAGES_PATH);
+
+                       node = find_node_from_desc(fit, images, str);
+                       if (node > 0)
+                               str = fdt_get_name(fit, node, NULL);
+
+                       found = true;
+               }
+       }
+
+       if (!found) {
+               debug("no string for index %d\n", index);
+               return -E2BIG;
+       }
+
+       *outname = str;
        return 0;
 }
 
@@ -83,7 +162,7 @@ static int spl_fit_get_image_name(const void *fit, int images,
 static int spl_fit_get_image_node(const void *fit, int images,
                                  const char *type, int index)
 {
-       char *str;
+       const char *str;
        int err;
        int node;
 
@@ -95,7 +174,7 @@ static int spl_fit_get_image_node(const void *fit, int images,
 
        node = fdt_subnode_offset(fit, images, str);
        if (node < 0) {
-               debug("cannot find image node '%s': %d\n", str, node);
+               pr_err("cannot find image node '%s': %d\n", str, node);
                return -EINVAL;
        }
 
@@ -140,14 +219,6 @@ static int get_aligned_image_size(struct spl_load_info *info, int data_size,
        return (data_size + info->bl_len - 1) / info->bl_len;
 }
 
-#ifdef CONFIG_SPL_FPGA_SUPPORT
-__weak int spl_load_fpga_image(struct spl_load_info *info, size_t length,
-                              int nr_sectors, int sector_offset)
-{
-       return 0;
-}
-#endif
-
 /**
  * spl_load_fit_image(): load the image described in a certain FIT node
  * @info:      points to information about the device to load data from
@@ -169,7 +240,7 @@ static int spl_load_fit_image(struct spl_load_info *info, ulong sector,
                              void *fit, ulong base_offset, int node,
                              struct spl_image_info *image_info)
 {
-       int offset, sector_offset;
+       int offset;
        size_t length;
        int len;
        ulong size;
@@ -190,11 +261,9 @@ static int spl_load_fit_image(struct spl_load_info *info, ulong sector,
                        debug("%s ", genimg_get_type_name(type));
        }
 
-       if (IS_ENABLED(CONFIG_SPL_OS_BOOT) && IS_ENABLED(CONFIG_SPL_GZIP)) {
-               if (fit_image_get_comp(fit, node, &image_comp))
-                       puts("Cannot get image compression format.\n");
-               else
-                       debug("%s ", genimg_get_comp_name(image_comp));
+       if (IS_ENABLED(CONFIG_SPL_GZIP)) {
+               fit_image_get_comp(fit, node, &image_comp);
+               debug("%s ", genimg_get_comp_name(image_comp));
        }
 
        if (fit_image_get_load(fit, node, &load_addr))
@@ -217,16 +286,9 @@ static int spl_load_fit_image(struct spl_load_info *info, ulong sector,
 
                overhead = get_aligned_image_overhead(info, offset);
                nr_sectors = get_aligned_image_size(info, length, offset);
-               sector_offset = sector + get_aligned_image_offset(info, offset);
 
-#ifdef CONFIG_SPL_FPGA_SUPPORT
-               if (type == IH_TYPE_FPGA) {
-                       return spl_load_fpga_image(info, length, nr_sectors,
-                                                  sector_offset);
-               }
-#endif
-
-               if (info->read(info, sector_offset,
+               if (info->read(info,
+                              sector + get_aligned_image_offset(info, offset),
                               nr_sectors, (void *)load_ptr) != nr_sectors)
                        return -EIO;
 
@@ -283,31 +345,96 @@ static int spl_fit_append_fdt(struct spl_image_info *spl_image,
                              void *fit, int images, ulong base_offset)
 {
        struct spl_image_info image_info;
-       int node, ret;
-
-       /* Figure out which device tree the board wants to use */
-       node = spl_fit_get_image_node(fit, images, FIT_FDT_PROP, 0);
-       if (node < 0) {
-               debug("%s: cannot find FDT node\n", __func__);
-               return node;
-       }
+       int node, ret = 0, index = 0;
 
        /*
-        * Read the device tree and place it after the image.
-        * Align the destination address to ARCH_DMA_MINALIGN.
+        * Use the address following the image as target address for the
+        * device tree.
         */
        image_info.load_addr = spl_image->load_addr + spl_image->size;
-       ret = spl_load_fit_image(info, sector, fit, base_offset, node,
-                                &image_info);
 
-       if (ret < 0)
-               return ret;
+       /* Figure out which device tree the board wants to use */
+       node = spl_fit_get_image_node(fit, images, FIT_FDT_PROP, index++);
+       if (node < 0) {
+               debug("%s: cannot find FDT node\n", __func__);
+
+               /*
+                * U-Boot did not find a device tree inside the FIT image. Use
+                * the U-Boot device tree instead.
+                */
+               if (gd->fdt_blob)
+                       memcpy((void *)image_info.load_addr, gd->fdt_blob,
+                              fdt_totalsize(gd->fdt_blob));
+               else
+                       return node;
+       } else {
+               ret = spl_load_fit_image(info, sector, fit, base_offset, node,
+                                        &image_info);
+               if (ret < 0)
+                       return ret;
+       }
 
        /* Make the load-address of the FDT available for the SPL framework */
        spl_image->fdt_addr = (void *)image_info.load_addr;
 #if !CONFIG_IS_ENABLED(FIT_IMAGE_TINY)
+       if (CONFIG_IS_ENABLED(LOAD_FIT_APPLY_OVERLAY)) {
+               void *tmpbuffer = NULL;
+
+               for (; ; index++) {
+                       node = spl_fit_get_image_node(fit, images, FIT_FDT_PROP,
+                                                     index);
+                       if (node == -E2BIG) {
+                               debug("%s: No additional FDT node\n", __func__);
+                               break;
+                       } else if (node < 0) {
+                               debug("%s: unable to find FDT node %d\n",
+                                     __func__, index);
+                               continue;
+                       }
+
+                       if (!tmpbuffer) {
+                               /*
+                                * allocate memory to store the DT overlay
+                                * before it is applied. It may not be used
+                                * depending on how the overlay is stored, so
+                                * don't fail yet if the allocation failed.
+                                */
+                               tmpbuffer = malloc(CONFIG_SPL_LOAD_FIT_APPLY_OVERLAY_BUF_SZ);
+                               if (!tmpbuffer)
+                                       debug("%s: unable to allocate space for overlays\n",
+                                             __func__);
+                       }
+                       image_info.load_addr = (ulong)tmpbuffer;
+                       ret = spl_load_fit_image(info, sector, fit, base_offset,
+                                                node, &image_info);
+                       if (ret < 0)
+                               break;
+
+                       /* Make room in FDT for changes from the overlay */
+                       ret = fdt_increase_size(spl_image->fdt_addr,
+                                               image_info.size);
+                       if (ret < 0)
+                               break;
+
+                       ret = fdt_overlay_apply_verbose(spl_image->fdt_addr,
+                                                       (void *)image_info.load_addr);
+                       if (ret) {
+                               pr_err("failed to apply DT overlay %s\n",
+                                      fit_get_name(fit, node, NULL));
+                               break;
+                       }
+
+                       debug("%s: DT overlay %s applied\n", __func__,
+                             fit_get_name(fit, node, NULL));
+               }
+               free(tmpbuffer);
+               if (ret)
+                       return ret;
+       }
        /* Try to make space, so we can inject details on the loadables */
        ret = fdt_shrink_to_minimum(spl_image->fdt_addr, 8192);
+       if (ret < 0)
+               return ret;
 #endif
 
        return ret;
@@ -318,7 +445,7 @@ static int spl_fit_record_loadable(const void *fit, int images, int index,
 {
        int ret = 0;
 #if !CONFIG_IS_ENABLED(FIT_IMAGE_TINY)
-       char *name;
+       const char *name;
        int node;
 
        ret = spl_fit_get_image_name(fit, images, "loadables",
@@ -338,13 +465,23 @@ static int spl_fit_record_loadable(const void *fit, int images, int index,
 
 static int spl_fit_image_get_os(const void *fit, int noffset, uint8_t *os)
 {
-#if CONFIG_IS_ENABLED(FIT_IMAGE_TINY)
+#if CONFIG_IS_ENABLED(FIT_IMAGE_TINY) && !defined(CONFIG_SPL_OS_BOOT)
        return -ENOTSUPP;
 #else
        return fit_image_get_os(fit, noffset, os);
 #endif
 }
 
+/*
+ * Weak default function to allow customizing SPL fit loading for load-only
+ * use cases by allowing to skip the parsing/processing of the FIT contents
+ * (so that this can be done separately in a more customized fashion)
+ */
+__weak bool spl_load_simple_fit_skip_processing(void)
+{
+       return false;
+}
+
 int spl_load_simple_fit(struct spl_image_info *spl_image,
                        struct spl_load_info *info, ulong sector, void *fit)
 {
@@ -356,6 +493,7 @@ int spl_load_simple_fit(struct spl_image_info *spl_image,
        int images, ret;
        int base_offset, hsize, align_len = ARCH_DMA_MINALIGN - 1;
        int index = 0;
+       int firmware_node;
 
        /*
         * For FIT with external data, figure out where the external images
@@ -364,6 +502,7 @@ int spl_load_simple_fit(struct spl_image_info *spl_image,
         */
        size = fdt_totalsize(fit);
        size = (size + 3) & ~3;
+       size = board_spl_fit_size_align(size);
        base_offset = (size + 3) & ~3;
 
        /*
@@ -387,11 +526,16 @@ int spl_load_simple_fit(struct spl_image_info *spl_image,
        fit = spl_get_load_buffer(-hsize, hsize);
        sectors = get_aligned_image_size(info, size, 0);
        count = info->read(info, sector, sectors, fit);
-       debug("fit read sector %lx, sectors=%d, dst=%p, count=%lu\n",
-             sector, sectors, fit, count);
+       debug("fit read sector %lx, sectors=%d, dst=%p, count=%lu, size=0x%lx\n",
+             sector, sectors, fit, count, size);
+
        if (count == 0)
                return -EIO;
 
+       /* skip further processing if requested to enable load-only use cases */
+       if (spl_load_simple_fit_skip_processing())
+               return 0;
+
        /* find the node holding the images information */
        images = fdt_path_offset(fit, FIT_IMAGES_PATH);
        if (images < 0) {
@@ -409,6 +553,18 @@ int spl_load_simple_fit(struct spl_image_info *spl_image,
                        printf("%s: Cannot load the FPGA: %i\n", __func__, ret);
                        return ret;
                }
+
+               debug("FPGA bitstream at: %x, size: %x\n",
+                     (u32)spl_image->load_addr, spl_image->size);
+
+               ret = fpga_load(0, (const void *)spl_image->load_addr,
+                               spl_image->size, BIT_FULL);
+               if (ret) {
+                       printf("%s: Cannot load the image to the FPGA\n",
+                              __func__);
+                       return ret;
+               }
+
                puts("FPGA image loaded from FIT\n");
                node = -1;
        }
@@ -467,6 +623,7 @@ int spl_load_simple_fit(struct spl_image_info *spl_image,
                spl_fit_append_fdt(spl_image, info, sector, fit,
                                   images, base_offset);
 
+       firmware_node = node;
        /* Now check if there are more images for us to load */
        for (; ; index++) {
                uint8_t os_type = IH_OS_INVALID;
@@ -475,6 +632,14 @@ int spl_load_simple_fit(struct spl_image_info *spl_image,
                if (node < 0)
                        break;
 
+               /*
+                * if the firmware is also a loadable, skip it because
+                * it already has been loaded. This is typically the case with
+                * u-boot.img generated by mkimage.
+                */
+               if (firmware_node == node)
+                       continue;
+
                ret = spl_load_fit_image(info, sector, fit, base_offset, node,
                                         &image_info);
                if (ret < 0)
@@ -512,5 +677,11 @@ int spl_load_simple_fit(struct spl_image_info *spl_image,
        if (spl_image->entry_point == FDT_ERROR || spl_image->entry_point == 0)
                spl_image->entry_point = spl_image->load_addr;
 
+       spl_image->flags |= SPL_FIT_FOUND;
+
+#ifdef CONFIG_IMX_HAB
+       board_spl_fit_post_load((ulong)fit, size);
+#endif
+
        return 0;
 }