[Concept,09/15] boot: Support rescanning the global bootmeths

Message ID 20250930005137.3650600-10-sjg@u-boot.org
State New
Headers
Series boot: Support priority for global bootmeths |

Commit Message

Simon Glass Sept. 30, 2025, 12:51 a.m. UTC
  From: Simon Glass <sjg@chromium.org>

Add the logic to scan through the global bootmeths for every new
bootdev, in preparation for allowing global bootmeths to select where in
the hunter ordering they go.

Use a new bootmeth_glob_allowed() function to check if a bootmeth is
allowed, ensuring that each can run at most once.

For now this has no actual effect, since the global bootmeths are
unconditionally processed at the start, with iter->methods_done being
updated to include all of them. Therefore when scanning again, no
unprocessed global bootmeths will be found.

Signed-off-by: Simon Glass <sjg@chromium.org>
---

 boot/bootflow.c    | 90 ++++++++++++++++++++++++++++++++++++++++++++--
 include/bootflow.h |  5 +++
 2 files changed, 93 insertions(+), 2 deletions(-)
  

Patch

diff --git a/boot/bootflow.c b/boot/bootflow.c
index a91d76c6baf..51e6ad6dd86 100644
--- a/boot/bootflow.c
+++ b/boot/bootflow.c
@@ -245,19 +245,81 @@  static void scan_next_in_uclass(struct udevice **devp)
 	*devp = dev;
 }
 
+/**
+ * bootmeth_glob_allowed() - Check if a global bootmeth is usable at this point
+ *
+ * @iter: Bootflow iterator being used
+ * Return: true if the global bootmeth has not already been used
+ */
+static bool bootmeth_glob_allowed(struct bootflow_iter *iter, int meth_seq)
+{
+	struct udevice *meth = iter->method_order[meth_seq];
+	bool done = iter->methods_done & BIT(meth_seq);
+
+	log_debug("considering glob '%s': done %d\n", meth->name, done);
+
+	/* if this one has already been used, try the next */
+	if (done)
+		return false;
+
+	return true;
+}
+
+/**
+ * next_glob_bootmeth() - Find the next global bootmeth to use
+ *
+ * Scans the global bootmeths to find the first unused one whose priority has
+ * been reached. If found, iter->cur_method and iter->method are set up and
+ * doing_global is set to true
+ *
+ * @iter: Bootflow iterator being used
+ * Return 0 if found, -ENOENT if no more global bootmeths are available
+ */
+static int next_glob_bootmeth(struct bootflow_iter *iter)
+{
+	log_debug("rescan global bootmeths have_global %d\n",
+		  iter->have_global);
+	if (IS_ENABLED(CONFIG_BOOTMETH_GLOBAL) && iter->have_global) {
+		int i;
+
+		/* rescan the global bootmeths */
+		log_debug("first_glob_method %d num_methods %d methods_done %x\n",
+			  iter->first_glob_method, iter->num_methods,
+			  iter->methods_done);
+		for (i = iter->first_glob_method; i < iter->num_methods; i++) {
+			if (bootmeth_glob_allowed(iter, i)) {
+				iter->cur_method = i;
+				iter->method = iter->method_order[i];
+				iter->doing_global = true;
+				iter->dev = NULL;
+				return 0;
+			}
+		}
+	}
+
+	return -ENOENT;
+}
+
 /**
  * prepare_bootdev() - Get ready to use a bootdev
  *
  * @iter: Bootflow iterator being used
  * @dev: UCLASS_BOOTDEV device to use
  * @method_flags: Method flag for the bootdev
+ * @check_global: true to check global bootmeths before processing @dev
  * Return 0 if OK, -ve if the bootdev failed to probe
  */
 static int prepare_bootdev(struct bootflow_iter *iter, struct udevice *dev,
-			   int method_flags)
+			   int method_flags, bool check_global)
 {
 	int ret;
 
+	if (check_global && !next_glob_bootmeth(iter)) {
+		iter->pending_bootdev = dev;
+		iter->pending_method_flags = method_flags;
+		return 0;
+	}
+
 	/*
 	 * Probe the bootdev. This does not probe any attached block device,
 	 * since they are siblings
@@ -308,6 +370,30 @@  static int iter_incr(struct bootflow_iter *iter)
 		iter->num_methods = iter->first_glob_method;
 		iter->doing_global = false;
 
+		/*
+		 * we've come to the end, so see if we should use a pending
+		 * bootdev from when we decided to rescan the global bootmeths
+		 */
+		if (iter->pending_bootdev) {
+			int meth_flags = iter->pending_method_flags;
+
+			dev = iter->pending_bootdev;
+			iter->pending_bootdev = NULL;
+			iter->pending_method_flags = 0;
+
+			ret = prepare_bootdev(iter, dev, meth_flags, false);
+			if (ret)
+				return log_msg_ret("ipb", ret);
+
+			iter->cur_method = 0;
+			iter->method = iter->method_order[iter->cur_method];
+
+			log_debug("-> using pending bootdev '%s' method '%s'\n",
+				  dev->name, iter->method->name);
+
+			return 0;
+		}
+
 		/*
 		 * Don't move to the next dev as we haven't tried this
 		 * one yet!
@@ -418,7 +504,7 @@  static int iter_incr(struct bootflow_iter *iter)
 		if (ret)
 			bootflow_iter_set_dev(iter, NULL, 0);
 		else
-			ret = prepare_bootdev(iter, dev, method_flags);
+			ret = prepare_bootdev(iter, dev, method_flags, true);
 	}
 
 	/* if there are no more bootdevs, give up */
diff --git a/include/bootflow.h b/include/bootflow.h
index 051158780e6..5ef0f4b61d3 100644
--- a/include/bootflow.h
+++ b/include/bootflow.h
@@ -280,6 +280,9 @@  enum {
  * (enum bootflow_meth_flags_t)
  * @methods_done: indicates which methods have been processed, one bit for
  * each method in @method_order[]
+ * @pending_bootdev: if non-NULL, bootdev which will be used when the global
+ * bootmeths are done
+ * @pending_method_flags: method flags which will be used with @pending_bootdev
  */
 struct bootflow_iter {
 	int flags;
@@ -303,6 +306,8 @@  struct bootflow_iter {
 	bool doing_global;
 	int method_flags;
 	uint methods_done;
+	struct udevice *pending_bootdev;
+	int pending_method_flags;
 };
 
 /**