fit_find_compatible_unit() keeps the first configuration it walked into
when several match the barebox root node equally well and only looks at
'default' when nothing matched at all. A FIT carrying one base devicetree
plus a couple of overlay combinations for the same board has all of its
configurations match equally well, so the choice the image author
expressed in 'default' is silently ignored.

The Flat Image Tree specification asks for the opposite since 8f6cc2523be7
("usage: prefer the default configuration on a compatible tie"):

  If several configurations are equally good matches (they match at the
  same position in the bootloader's compatible stringlist), the one named
  by the 'default' property is selected.

Let's resolve 'default' to a node up front and let it win ties. A strictly
better match still wins and a FIT without a 'default' keeps booting the
first best match, so only equal scores change. The shortcut out of the
loop on a perfect match now needs the default to have been walked past.

A 'default' naming no configuration is reported here now instead of by
fit_open_configuration(). The /configurations node is not covered by the
configuration signature either way, so rewriting 'default' and reordering
the configurations stay equivalent.

Assisted-by: Claude:opus-5
Signed-off-by: Ahmad Fatoum <[email protected]>
---
 Documentation/user/booting-linux.rst |  2 ++
 common/image-fit.c                   | 48 +++++++++++++++++++++-------
 2 files changed, 39 insertions(+), 11 deletions(-)

diff --git a/Documentation/user/booting-linux.rst 
b/Documentation/user/booting-linux.rst
index 0f1225681360..7412c9893691 100644
--- a/Documentation/user/booting-linux.rst
+++ b/Documentation/user/booting-linux.rst
@@ -65,6 +65,8 @@ To use an initramfs, use the ``-r`` option or the
 
 FIT image configurations will be matched by comparing the ``compatible`` 
property
 inside the configuration node with the barebox live tree's ``/compatible``.
+If several configurations match equally well, the one named by the
+``/configurations`` node's ``default`` property is preferred.
 It's also possible to select a specific configuration explicitly:
 
 .. code-block:: sh
diff --git a/common/image-fit.c b/common/image-fit.c
index d16d805d53c5..3b75b31f54b6 100644
--- a/common/image-fit.c
+++ b/common/image-fit.c
@@ -952,12 +952,19 @@ static int fit_find_compatible_unit(struct fit_handle 
*handle,
                                    bool (*config_node_valid)(struct fit_handle 
*handle,
                                                              struct 
device_node *config))
 {
-       struct device_node *child = NULL;
+       struct device_node *child = NULL, *dflt = NULL, *best = NULL;
        struct device_node *barebox_root;
        int best_score = 0;
-       const char *machine;
+       const char *machine, *dfltname = NULL;
+       bool dflt_pending = false;
        int ret;
 
+       if (!of_property_read_string(conf_node, "default", &dfltname)) {
+               dflt = fit_get_child_by_name_exact(conf_node, dfltname);
+               if (dflt)
+                       dflt_pending = true;
+       }
+
        barebox_root = of_get_root_node();
        if (!barebox_root)
                goto default_unit;
@@ -969,6 +976,9 @@ static int fit_find_compatible_unit(struct fit_handle 
*handle,
        for_each_child_of_node(conf_node, child) {
                int score;
 
+               if (child == dflt)
+                       dflt_pending = false;
+
                if (config_node_valid && !config_node_valid(handle, child))
                        continue;
 
@@ -977,26 +987,42 @@ static int fit_find_compatible_unit(struct fit_handle 
*handle,
                if (!score)
                        score = fit_fdt_is_compatible(handle, child, machine);
 
-               if (score > best_score) {
-                       best_score = score;
-                       *unit = child->name;
+               if (!score)
+                       continue;
 
-                       if (score == OF_DEVICE_COMPATIBLE_MAX_SCORE)
-                               break;
+               /*
+                * A FIT may carry one base devicetree plus a number of
+                * overlay combinations with multiple configurations matching 
the
+                * board equally well. Allow the image author to influences who
+                * wins ties by means of the default property.
+                */
+               if (score > best_score || (score == best_score && child == 
dflt)) {
+                       best_score = score;
+                       best = child;
                }
+
+               /* Nothing left to walk into that could do better */
+               if (best_score == OF_DEVICE_COMPATIBLE_MAX_SCORE && 
!dflt_pending)
+                       break;
        }
 
-       if (best_score) {
+       if (best) {
+               *unit = best->name;
                pr_info("matching unit '%s' found\n", *unit);
                return 0;
        }
 
 default_unit:
        pr_info("No match found. Trying default.\n");
-       if (of_property_read_string(conf_node, "default", unit) == 0)
-               return 0;
+       if (!dflt) {
+               if (dfltname)
+                       pr_err("default configuration '%s' not found\n", 
dfltname);
+               return -ENOENT;
+       }
 
-       return -ENOENT;
+       *unit = dflt->name;
+
+       return 0;
 }
 
 static int fit_find_last_unit(struct fit_handle *handle,
-- 
2.47.3


Reply via email to