fdt: Add option to default to most compatible conf in a fit image
authorGabe Black <gabeblack@chromium.org>
Thu, 25 Oct 2012 16:31:10 +0000 (16:31 +0000)
committerGerald Van Baren <gvb@unssw.com>
Tue, 13 Nov 2012 04:14:57 +0000 (23:14 -0500)
When booting a fit image with multiple configurations, the user either has to
specify which configuration to use explicitly, or there has to be a default
defined which is chosen automatically. This change adds an option to change
that behavior so that a configuration can be selected explicitly, or the
configuration which has the device tree that claims to be compatible with the
earliest item in U-Boot's device tree.

In other words, if U-Boot claimed to be compatible with A, B, and then C, and
the configurations claimed to be compatible with A, D and B, D and D, E, the
first configuration, A, D, would be chosen. Both the first and second
configurations match, but the first one matches a more specific entry in
U-Boot's device tree. The order in the kernel's device tree is ignored.

Signed-off-by: Gabe Black <gabeblack@google.com>
Commit-Ready: Gabe Black <gabeblack@chromium.org>
Signed-off-by: Simon Glass <sjg@chromium.org>
README
common/cmd_bootm.c
common/image.c
include/image.h

diff --git a/README b/README
index afdf591c623f6cfa5c507d6918e738dea12b2c79..6c7784263e7ad6ce2ec913e8f8b477b2d845617a 100644 (file)
--- a/README
+++ b/README
@@ -2597,6 +2597,17 @@ FIT uImage format:
  -150  common/cmd_nand.c       Incorrect FIT image format
   151  common/cmd_nand.c       FIT image format OK
 
+- FIT image support:
+               CONFIG_FIT
+               Enable support for the FIT uImage format.
+
+               CONFIG_FIT_BEST_MATCH
+               When no configuration is explicitly selected, default to the
+               one whose fdt's compatibility field best matches that of
+               U-Boot itself. A match is considered "best" if it matches the
+               most specific compatibility entry of U-Boot's fdt's root node.
+               The order of entries in the configuration's fdt is ignored.
+
 - Standalone program support:
                CONFIG_STANDALONE_LOAD_ADDR
 
index d256ddfaa6f017a944aef86ff42c5cc86de915aa..4dbe952bb0f56b92f40b0a67dd594f83d987e804 100644 (file)
@@ -949,8 +949,19 @@ static void *boot_get_kernel(cmd_tbl_t *cmdtp, int flag, int argc,
                         * node
                         */
                        bootstage_mark(BOOTSTAGE_ID_FIT_NO_UNIT_NAME);
+#ifdef CONFIG_FIT_BEST_MATCH
+                       if (fit_uname_config)
+                               cfg_noffset =
+                                       fit_conf_get_node(fit_hdr,
+                                                         fit_uname_config);
+                       else
+                               cfg_noffset =
+                                       fit_conf_find_compat(fit_hdr,
+                                                            gd->fdt_blob);
+#else
                        cfg_noffset = fit_conf_get_node(fit_hdr,
                                                        fit_uname_config);
+#endif
                        if (cfg_noffset < 0) {
                                bootstage_error(BOOTSTAGE_ID_FIT_NO_UNIT_NAME);
                                return NULL;
index df642e656ca23271ff1fa05b0afd3b8c1e50090a..e93b6e89cf9da48a130c265a83b575eba4a0c737 100644 (file)
@@ -3049,6 +3049,133 @@ int fit_check_format(const void *fit)
        return 1;
 }
 
+
+/**
+ * fit_conf_find_compat
+ * @fit: pointer to the FIT format image header
+ * @fdt: pointer to the device tree to compare against
+ *
+ * fit_conf_find_compat() attempts to find the configuration whose fdt is the
+ * most compatible with the passed in device tree.
+ *
+ * Example:
+ *
+ * / o image-tree
+ *   |-o images
+ *   | |-o fdt@1
+ *   | |-o fdt@2
+ *   |
+ *   |-o configurations
+ *     |-o config@1
+ *     | |-fdt = fdt@1
+ *     |
+ *     |-o config@2
+ *       |-fdt = fdt@2
+ *
+ * / o U-Boot fdt
+ *   |-compatible = "foo,bar", "bim,bam"
+ *
+ * / o kernel fdt1
+ *   |-compatible = "foo,bar",
+ *
+ * / o kernel fdt2
+ *   |-compatible = "bim,bam", "baz,biz"
+ *
+ * Configuration 1 would be picked because the first string in U-Boot's
+ * compatible list, "foo,bar", matches a compatible string in the root of fdt1.
+ * "bim,bam" in fdt2 matches the second string which isn't as good as fdt1.
+ *
+ * returns:
+ *     offset to the configuration to use if one was found
+ *     -1 otherwise
+ */
+int fit_conf_find_compat(const void *fit, const void *fdt)
+{
+       int ndepth = 0;
+       int noffset, confs_noffset, images_noffset;
+       const void *fdt_compat;
+       int fdt_compat_len;
+       int best_match_offset = 0;
+       int best_match_pos = 0;
+
+       confs_noffset = fdt_path_offset(fit, FIT_CONFS_PATH);
+       images_noffset = fdt_path_offset(fit, FIT_IMAGES_PATH);
+       if (confs_noffset < 0 || images_noffset < 0) {
+               debug("Can't find configurations or images nodes.\n");
+               return -1;
+       }
+
+       fdt_compat = fdt_getprop(fdt, 0, "compatible", &fdt_compat_len);
+       if (!fdt_compat) {
+               debug("Fdt for comparison has no \"compatible\" property.\n");
+               return -1;
+       }
+
+       /*
+        * Loop over the configurations in the FIT image.
+        */
+       for (noffset = fdt_next_node(fit, confs_noffset, &ndepth);
+                       (noffset >= 0) && (ndepth > 0);
+                       noffset = fdt_next_node(fit, noffset, &ndepth)) {
+               const void *kfdt;
+               const char *kfdt_name;
+               int kfdt_noffset;
+               const char *cur_fdt_compat;
+               int len;
+               size_t size;
+               int i;
+
+               if (ndepth > 1)
+                       continue;
+
+               kfdt_name = fdt_getprop(fit, noffset, "fdt", &len);
+               if (!kfdt_name) {
+                       debug("No fdt property found.\n");
+                       continue;
+               }
+               kfdt_noffset = fdt_subnode_offset(fit, images_noffset,
+                                                 kfdt_name);
+               if (kfdt_noffset < 0) {
+                       debug("No image node named \"%s\" found.\n",
+                             kfdt_name);
+                       continue;
+               }
+               /*
+                * Get a pointer to this configuration's fdt.
+                */
+               if (fit_image_get_data(fit, kfdt_noffset, &kfdt, &size)) {
+                       debug("Failed to get fdt \"%s\".\n", kfdt_name);
+                       continue;
+               }
+
+               len = fdt_compat_len;
+               cur_fdt_compat = fdt_compat;
+               /*
+                * Look for a match for each U-Boot compatibility string in
+                * turn in this configuration's fdt.
+                */
+               for (i = 0; len > 0 &&
+                    (!best_match_offset || best_match_pos > i); i++) {
+                       int cur_len = strlen(cur_fdt_compat) + 1;
+
+                       if (!fdt_node_check_compatible(kfdt, 0,
+                                                      cur_fdt_compat)) {
+                               best_match_offset = noffset;
+                               best_match_pos = i;
+                               break;
+                       }
+                       len -= cur_len;
+                       cur_fdt_compat += cur_len;
+               }
+       }
+       if (!best_match_offset) {
+               debug("No match found.\n");
+               return -1;
+       }
+
+       return best_match_offset;
+}
+
 /**
  * fit_conf_get_node - get node offset for configuration of a given unit name
  * @fit: pointer to the FIT format image header
index 0a895f2044c21a4b81245caae79ef95af6a6b999..f54d98330634acb99c56c791d674c3cdb6816c70 100644 (file)
@@ -615,6 +615,7 @@ int fit_image_check_type(const void *fit, int noffset, uint8_t type);
 int fit_image_check_comp(const void *fit, int noffset, uint8_t comp);
 int fit_check_format(const void *fit);
 
+int fit_conf_find_compat(const void *fit, const void *fdt);
 int fit_conf_get_node(const void *fit, const char *conf_uname);
 int fit_conf_get_kernel_node(const void *fit, int noffset);
 int fit_conf_get_ramdisk_node(const void *fit, int noffset);