]> git.ipfire.org Git - people/ms/u-boot.git/blobdiff - common/spl/spl_fit.c
SPL: Add FIT data-position property support
[people/ms/u-boot.git] / common / spl / spl_fit.c
index 32d9ee590122585273af4f31cd6476a21ea26aa1..cc07fbc8a02adc332fd4a7b45b574aa0f74b17ad 100644 (file)
@@ -2,7 +2,7 @@
  * Copyright (C) 2016 Google, Inc
  * Written by Simon Glass <sjg@chromium.org>
  *
- * SPDX-License-Identifier:     GPL-2.0+
+ * SPDX-License-Identifier:    GPL-2.0+
  */
 
 #include <common.h>
 #endif
 
 /**
- * spl_fit_get_image_node(): By using the matching configuration subnode,
+ * 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
  * into that.
  * @fit:       Pointer to the FDT blob.
  * @images:    Offset of the /images subnode.
  * @type:      Name of the property within the configuration subnode.
  * @index:     Index into the list of strings in this property.
+ * @outname:   Name of the image
  *
- * Return:     the node offset of the respective image node or a negative
- *             error number.
+ * Return:     0 on success, or a negative error number
  */
-static int spl_fit_get_image_node(const void *fit, int images,
-                                 const char *type, int index)
+static int spl_fit_get_image_name(const void *fit, int images,
+                                 const char *type, int index,
+                                 char **outname)
 {
        const char *name, *str;
-       int node, conf_node;
+       __maybe_unused int node;
+       int conf_node;
        int len, i;
 
        conf_node = fit_find_config_node(fit);
@@ -63,7 +65,35 @@ static int spl_fit_get_image_node(const void *fit, int images,
                }
        }
 
+       *outname = (char *)str;
+       return 0;
+}
+
+/**
+ * spl_fit_get_image_node(): By using the matching configuration subnode,
+ * retrieve the name of an image, specified by a property name and an index
+ * into that.
+ * @fit:       Pointer to the FDT blob.
+ * @images:    Offset of the /images subnode.
+ * @type:      Name of the property within the configuration subnode.
+ * @index:     Index into the list of strings in this property.
+ *
+ * Return:     the node offset of the respective image node or a negative
+ *             error number.
+ */
+static int spl_fit_get_image_node(const void *fit, int images,
+                                 const char *type, int index)
+{
+       char *str;
+       int err;
+       int node;
+
+       err = spl_fit_get_image_name(fit, images, type, index, &str);
+       if (err)
+               return err;
+
        debug("%s: '%s'\n", type, str);
+
        node = fdt_subnode_offset(fit, images, str);
        if (node < 0) {
                debug("cannot find image node '%s': %d\n", str, node);
@@ -116,15 +146,15 @@ static int get_aligned_image_size(struct spl_load_info *info, int data_size,
  * @info:      points to information about the device to load data from
  * @sector:    the start sector of the FIT image on the device
  * @fit:       points to the flattened device tree blob describing the FIT
- *             image
+ *             image
  * @base_offset: the beginning of the data area containing the actual
  *             image data, relative to the beginning of the FIT
  * @node:      offset of the DT node describing the image to load (relative
- *             to @fit)
+ *             to @fit)
  * @image_info:        will be filled with information about the loaded image
- *             If the FIT node does not contain a "load" (address) property,
- *             the image gets loaded to the address pointed to by the
- *             load_addr member in this struct.
+ *             If the FIT node does not contain a "load" (address) property,
+ *             the image gets loaded to the address pointed to by the
+ *             load_addr member in this struct.
  *
  * Return:     0 on success or a negative error number.
  */
@@ -143,6 +173,7 @@ static int spl_load_fit_image(struct spl_load_info *info, ulong sector,
        int align_len = ARCH_DMA_MINALIGN - 1;
        uint8_t image_comp = -1, type = -1;
        const void *data;
+       bool external_data = false;
 
        if (IS_ENABLED(CONFIG_SPL_OS_BOOT) && IS_ENABLED(CONFIG_SPL_GZIP)) {
                if (fit_image_get_comp(fit, node, &image_comp))
@@ -159,9 +190,15 @@ static int spl_load_fit_image(struct spl_load_info *info, ulong sector,
        if (fit_image_get_load(fit, node, &load_addr))
                load_addr = image_info->load_addr;
 
-       if (!fit_image_get_data_offset(fit, node, &offset)) {
-               /* External data */
+       if (!fit_image_get_data_position(fit, node, &offset)) {
+               external_data = true;
+       } else if (!fit_image_get_data_offset(fit, node, &offset)) {
                offset += base_offset;
+               external_data = true;
+       }
+
+       if (external_data) {
+               /* External data */
                if (fit_image_get_data_size(fit, node, &len))
                        return -ENOENT;
 
@@ -218,6 +255,73 @@ static int spl_load_fit_image(struct spl_load_info *info, ulong sector,
        return 0;
 }
 
+static int spl_fit_append_fdt(struct spl_image_info *spl_image,
+                             struct spl_load_info *info, ulong sector,
+                             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;
+       }
+
+       /*
+        * Read the device tree and place it after the image.
+        * Align the destination address to ARCH_DMA_MINALIGN.
+        */
+       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;
+
+       /* 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)
+       /* Try to make space, so we can inject details on the loadables */
+       ret = fdt_shrink_to_minimum(spl_image->fdt_addr, 8192);
+#endif
+
+       return ret;
+}
+
+static int spl_fit_record_loadable(const void *fit, int images, int index,
+                                  void *blob, struct spl_image_info *image)
+{
+       int ret = 0;
+#if !CONFIG_IS_ENABLED(FIT_IMAGE_TINY)
+       char *name;
+       int node;
+
+       ret = spl_fit_get_image_name(fit, images, "loadables",
+                                    index, &name);
+       if (ret < 0)
+               return ret;
+
+       node = spl_fit_get_image_node(fit, images, "loadables", index);
+
+       ret = fdt_record_loadable(blob, index, name, image->load_addr,
+                                 image->size, image->entry_point,
+                                 fdt_getprop(fit, node, "type", NULL),
+                                 fdt_getprop(fit, node, "os", NULL));
+#endif
+       return ret;
+}
+
+static int spl_fit_image_get_os(const void *fit, int noffset, uint8_t *os)
+{
+#if CONFIG_IS_ENABLED(FIT_IMAGE_TINY)
+       return -ENOTSUPP;
+#else
+       return fit_image_get_os(fit, noffset, os);
+#endif
+}
+
 int spl_load_simple_fit(struct spl_image_info *spl_image,
                        struct spl_load_info *info, ulong sector, void *fit)
 {
@@ -225,7 +329,6 @@ int spl_load_simple_fit(struct spl_image_info *spl_image,
        ulong size;
        unsigned long count;
        struct spl_image_info image_info;
-       bool boot_os = false;
        int node = -1;
        int images, ret;
        int base_offset, align_len = ARCH_DMA_MINALIGN - 1;
@@ -273,17 +376,18 @@ int spl_load_simple_fit(struct spl_image_info *spl_image,
                return -1;
        }
 
+       /*
+        * Find the U-Boot image using the following search order:
+        *   - start at 'firmware' (e.g. an ARM Trusted Firmware)
+        *   - fall back 'kernel' (e.g. a Falcon-mode OS boot
+        *   - fall back to using the first 'loadables' entry
+        */
+       if (node < 0)
+               node = spl_fit_get_image_node(fit, images, "firmware", 0);
 #ifdef CONFIG_SPL_OS_BOOT
-       /* Find OS image first */
-       node = spl_fit_get_image_node(fit, images, FIT_KERNEL_PROP, 0);
        if (node < 0)
-               debug("No kernel image.\n");
-       else
-               boot_os = true;
+               node = spl_fit_get_image_node(fit, images, FIT_KERNEL_PROP, 0);
 #endif
-       /* find the U-Boot image */
-       if (node < 0)
-               node = spl_fit_get_image_node(fit, images, "firmware", 0);
        if (node < 0) {
                debug("could not find firmware image, trying loadables...\n");
                node = spl_fit_get_image_node(fit, images, "loadables", 0);
@@ -305,34 +409,29 @@ int spl_load_simple_fit(struct spl_image_info *spl_image,
        if (ret)
                return ret;
 
-#ifdef CONFIG_SPL_OS_BOOT
-       if (!fit_image_get_os(fit, node, &spl_image->os))
+       /*
+        * For backward compatibility, we treat the first node that is
+        * as a U-Boot image, if no OS-type has been declared.
+        */
+       if (!spl_fit_image_get_os(fit, node, &spl_image->os))
                debug("Image OS is %s\n", genimg_get_os_name(spl_image->os));
-#else
-       spl_image->os = IH_OS_U_BOOT;
+#if !defined(CONFIG_SPL_OS_BOOT)
+       else
+               spl_image->os = IH_OS_U_BOOT;
 #endif
 
-       if (!boot_os) {
-               /* 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;
-               }
-
-               /*
-                * Read the device tree and place it after the image.
-                * Align the destination address to ARCH_DMA_MINALIGN.
-                */
-               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;
-       }
+       /*
+        * Booting a next-stage U-Boot may require us to append the FDT.
+        * We allow this to fail, as the U-Boot image might embed its FDT.
+        */
+       if (spl_image->os == IH_OS_U_BOOT)
+               spl_fit_append_fdt(spl_image, info, sector, fit,
+                                  images, base_offset);
 
        /* Now check if there are more images for us to load */
        for (; ; index++) {
+               uint8_t os_type = IH_OS_INVALID;
+
                node = spl_fit_get_image_node(fit, images, "loadables", index);
                if (node < 0)
                        break;
@@ -342,6 +441,15 @@ int spl_load_simple_fit(struct spl_image_info *spl_image,
                if (ret < 0)
                        continue;
 
+               if (!spl_fit_image_get_os(fit, node, &os_type))
+                       debug("Loadable is %s\n", genimg_get_os_name(os_type));
+
+               if (os_type == IH_OS_U_BOOT) {
+                       spl_fit_append_fdt(&image_info, info, sector,
+                                          fit, images, base_offset);
+                       spl_image->fdt_addr = image_info.fdt_addr;
+               }
+
                /*
                 * If the "firmware" image did not provide an entry point,
                 * use the first valid entry point from the loadables.
@@ -349,6 +457,12 @@ int spl_load_simple_fit(struct spl_image_info *spl_image,
                if (spl_image->entry_point == FDT_ERROR &&
                    image_info.entry_point != FDT_ERROR)
                        spl_image->entry_point = image_info.entry_point;
+
+               /* Record our loadables into the FDT */
+               if (spl_image->fdt_addr)
+                       spl_fit_record_loadable(fit, images, index,
+                                               spl_image->fdt_addr,
+                                               &image_info);
        }
 
        /*