[Concept,07/15] boot: Keep track of which bootmeths have been used

Message ID 20250930005137.3650600-8-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 a bitfield which tracks when bootmeths have been used. This will be
needed when global bootmeths can be used later in the iteration.

Fix a missing bootflow_free() while here.

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

 boot/bootflow.c        | 13 +++++++++++++
 boot/bootmeth-uclass.c | 10 ++++++++++
 include/bootflow.h     |  8 ++++++++
 test/boot/bootflow.c   |  8 ++++++++
 4 files changed, 39 insertions(+)
  

Patch

diff --git a/boot/bootflow.c b/boot/bootflow.c
index efe8aea765d..6f5876548b2 100644
--- a/boot/bootflow.c
+++ b/boot/bootflow.c
@@ -18,6 +18,10 @@ 
 #include <dm/device-internal.h>
 #include <dm/uclass-internal.h>
 
+/* ensure BOOTMETH_MAX_COUNT fits in method_flags field */
+static_assert(BOOTMETH_MAX_COUNT <=
+	      (sizeof(((struct bootflow_iter *)NULL)->method_flags) * 8));
+
 /* error codes used to signal running out of things */
 enum {
 	BF_NO_MORE_PARTS	= -ESHUTDOWN,
@@ -491,6 +495,10 @@  int bootflow_scan_first(struct udevice *dev, const char *label,
 		bootflow_iter_set_dev(iter, dev, method_flags);
 	}
 
+	if (IS_ENABLED(CONFIG_BOOTMETH_GLOBAL)) {
+		iter->methods_done |= BIT(iter->cur_method);
+		log_debug("methods_done now %x\n", iter->cur_method);
+	}
 	ret = bootflow_check(iter, bflow);
 	if (ret) {
 		log_debug("check - ret=%d\n", ret);
@@ -518,6 +526,11 @@  int bootflow_scan_next(struct bootflow_iter *iter, struct bootflow *bflow)
 			return log_msg_ret("done", ret);
 
 		if (!ret) {
+			if (IS_ENABLED(CONFIG_BOOTMETH_GLOBAL)) {
+				iter->methods_done |= BIT(iter->cur_method);
+				log_debug("methods_done now %x\n",
+					  iter->cur_method);
+			}
 			ret = bootflow_check(iter, bflow);
 			log_debug("check - ret=%d\n", ret);
 			if (!ret)
diff --git a/boot/bootmeth-uclass.c b/boot/bootmeth-uclass.c
index 0147b97fcb0..d71e4f001f1 100644
--- a/boot/bootmeth-uclass.c
+++ b/boot/bootmeth-uclass.c
@@ -199,6 +199,16 @@  int bootmeth_setup_iter_order(struct bootflow_iter *iter, bool include_global)
 		iter->doing_global = true;
 		iter->have_global = true;
 	}
+
+	/*
+	 * check we don't exceed the maximum bits in methods_done when tracking
+	 * which global bootmeths have run
+	 */
+	if (IS_ENABLED(CONFIG_BOOTMETH_GLOBAL) && count > BOOTMETH_MAX_COUNT) {
+		free(order);
+		return log_msg_ret("tmb", -ENOSPC);
+	}
+
 	iter->method_order = order;
 	iter->num_methods = count;
 
diff --git a/include/bootflow.h b/include/bootflow.h
index 0805d10e197..051158780e6 100644
--- a/include/bootflow.h
+++ b/include/bootflow.h
@@ -12,6 +12,7 @@ 
 #include <image.h>
 #include <dm/ofnode_decl.h>
 #include <linux/list.h>
+#include <linux/build_bug.h>
 
 struct bootstd_priv;
 struct expo;
@@ -226,6 +227,10 @@  enum bootflow_meth_flags_t {
 	BOOTFLOW_METHF_SINGLE_UCLASS	= 1 << 3,
 };
 
+enum {
+	BOOTMETH_MAX_COUNT	= 32,
+};
+
 /**
  * struct bootflow_iter - state for iterating through bootflows
  *
@@ -273,6 +278,8 @@  enum bootflow_meth_flags_t {
  *	happens before the normal ones)
  * @method_flags: flags controlling which methods should be used for this @dev
  * (enum bootflow_meth_flags_t)
+ * @methods_done: indicates which methods have been processed, one bit for
+ * each method in @method_order[]
  */
 struct bootflow_iter {
 	int flags;
@@ -295,6 +302,7 @@  struct bootflow_iter {
 	bool have_global;
 	bool doing_global;
 	int method_flags;
+	uint methods_done;
 };
 
 /**
diff --git a/test/boot/bootflow.c b/test/boot/bootflow.c
index a18afad0f72..af7f7391160 100644
--- a/test/boot/bootflow.c
+++ b/test/boot/bootflow.c
@@ -316,6 +316,7 @@  static int bootflow_iter(struct unit_test_state *uts)
 	ut_assert(!iter.doing_global);
 	ut_assert(!iter.have_global);
 	ut_asserteq(-1, iter.first_glob_method);
+	ut_asserteq(BIT(0), iter.methods_done);
 
 	/*
 	 * This shows MEDIA even though there is none, since in
@@ -324,6 +325,7 @@  static int bootflow_iter(struct unit_test_state *uts)
 	 * know.
 	 */
 	ut_asserteq(BOOTFLOWST_MEDIA, bflow.state);
+	bootflow_free(&bflow);
 
 	ut_asserteq(-EPROTONOSUPPORT, bootflow_scan_next(&iter, &bflow));
 	ut_asserteq(3, iter.num_methods);
@@ -333,6 +335,7 @@  static int bootflow_iter(struct unit_test_state *uts)
 	ut_asserteq_str("efi", iter.method->name);
 	ut_asserteq(0, bflow.err);
 	ut_asserteq(BOOTFLOWST_MEDIA, bflow.state);
+	ut_asserteq(BIT(0) | BIT(1), iter.methods_done);
 	bootflow_free(&bflow);
 
 	/* now the VBE boothmeth */
@@ -344,6 +347,7 @@  static int bootflow_iter(struct unit_test_state *uts)
 	ut_asserteq_str("vbe", iter.method->name);
 	ut_asserteq(0, bflow.err);
 	ut_asserteq(BOOTFLOWST_MEDIA, bflow.state);
+	ut_asserteq(BIT(0) | BIT(1) | BIT(2), iter.methods_done);
 	bootflow_free(&bflow);
 
 	/* The next device is mmc1.bootdev - at first we use the whole device */
@@ -355,6 +359,7 @@  static int bootflow_iter(struct unit_test_state *uts)
 	ut_asserteq_str("extlinux", iter.method->name);
 	ut_asserteq(0, bflow.err);
 	ut_asserteq(BOOTFLOWST_MEDIA, bflow.state);
+	ut_asserteq(BIT(0) | BIT(1) | BIT(2), iter.methods_done);
 	bootflow_free(&bflow);
 
 	ut_asserteq(-ENOENT, bootflow_scan_next(&iter, &bflow));
@@ -365,6 +370,7 @@  static int bootflow_iter(struct unit_test_state *uts)
 	ut_asserteq_str("efi", iter.method->name);
 	ut_asserteq(0, bflow.err);
 	ut_asserteq(BOOTFLOWST_MEDIA, bflow.state);
+	ut_asserteq(BIT(0) | BIT(1) | BIT(2), iter.methods_done);
 	bootflow_free(&bflow);
 
 	/* now the VBE boothmeth */
@@ -376,6 +382,7 @@  static int bootflow_iter(struct unit_test_state *uts)
 	ut_asserteq_str("vbe", iter.method->name);
 	ut_asserteq(0, bflow.err);
 	ut_asserteq(BOOTFLOWST_MEDIA, bflow.state);
+	ut_asserteq(BIT(0) | BIT(1) | BIT(2), iter.methods_done);
 	bootflow_free(&bflow);
 
 	/* Then move to partition 1 where we find something */
@@ -418,6 +425,7 @@  static int bootflow_iter(struct unit_test_state *uts)
 	ut_asserteq_str("extlinux", iter.method->name);
 	ut_asserteq(0, bflow.err);
 	ut_asserteq(BOOTFLOWST_MEDIA, bflow.state);
+	ut_asserteq(BIT(0) | BIT(1) | BIT(2), iter.methods_done);
 	bootflow_free(&bflow);
 
 	bootflow_iter_uninit(&iter);