[PATCH 00/15] boot: Support priority for global bootmeths

From: Simon Glass <sjg@chromium.org> At present global bootmeths always run first, before all other bootmeths. Optimisations in the code take advantage of this, putting them at the end, so they can be used once and then forgotten. In some cases it is useful to run global bootmeths later in the boot. For example, the EFI-bootmgr bootmeth may itself scan devices and the network, so running it first can hold up the boot significantly for boards not actually relying on EFI-bootmgr to boot. This series introduces a new field in global bootmeths which indicates the priority, using the same scheme as is used with bootdev hunters. Thus it is possible to insert the EFI-bootmgr bootmeth just before the hunter for network bootdevs is invoked. Despite the simplicity of the concept and the relatively small series, this is a fairly significant enhancement. It is also quite tricky to implement, largely due to the way the original code was written, with global bootmeths being a small, size-optimised add-on to the original bootstd implementation. For now we only allow each global bootmeth to run at most once, but this implementation is written in a way that we could relax that if needed. Then the bootmeth itself could decide whether to run at any particular point in the bootflow iteration. Size growth is about 390 bytes on Thumb2 (e.g. firefly-rk3288) if CONFIG_BOOTMETH_GLOBAL is enabled, which it normally is. With that disabled (which saves about 4K on the same platform), there is no growth. Simon Glass (15): boot: Improve comments related to global bootmeths boot: Add more debugging to iter_incr() boot: Move showing of bootflows out of the command boot: Add a new test for global bootmeths boot: Update first_glob_method when dropping a bootmeth boot: Add a flag for whether there are global bootmeths boot: Keep track of which bootmeths have been used boot: Move preparing bootdev into a function boot: Support rescanning the global bootmeths boot: Only run global bootmeths once each boot: Implement a priority for global bootmeths boot: Don't change the method count after global bootmeths boot: Run global bootmeths after all bootdevs are exhausted boot: Run the EFI bootmgr just before network devices boot: doc: Update for new global-bootmeth features boot/bootflow.c | 275 +++++++++++++++++++++++++++++-- boot/bootmeth-uclass.c | 15 +- boot/bootmeth_efi_mgr.c | 1 + cmd/bootflow.c | 68 +------- doc/develop/bootstd/overview.rst | 26 ++- include/bootflow.h | 29 +++- include/bootmeth.h | 4 + test/boot/bootflow.c | 73 +++++++- 8 files changed, 397 insertions(+), 94 deletions(-) -- 2.43.0 base-commit: 2617d1179c27ba51e9eb7996b7062a0205c587a9 branch: glob

From: Simon Glass <sjg@chromium.org> Add a few comments about global bootmeths and first_glob_method Fix a broken line in bootmeth_setup_iter_order() while we are here. Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/bootflow.c | 3 ++- boot/bootmeth-uclass.c | 4 ++-- include/bootflow.h | 3 ++- test/boot/bootflow.c | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/boot/bootflow.c b/boot/bootflow.c index c8391641001..de1f0c99916 100644 --- a/boot/bootflow.c +++ b/boot/bootflow.c @@ -150,7 +150,7 @@ static void bootflow_iter_set_dev(struct bootflow_iter *iter, if (dev) printf("Scanning bootdev '%s':\n", dev->name); else if (IS_ENABLED(CONFIG_BOOTMETH_GLOBAL) && - ucp->flags & BOOTMETHF_GLOBAL) + ucp->flags & BOOTMETHF_GLOBAL) printf("Scanning global bootmeth '%s':\n", iter->method->name); else @@ -347,6 +347,7 @@ static int bootflow_check(struct bootflow_iter *iter, struct bootflow *bflow) struct udevice *dev; int ret; + /* handle global bootmeths if needed */ if (IS_ENABLED(CONFIG_BOOTMETH_GLOBAL) && iter->doing_global) { bootflow_iter_set_dev(iter, NULL, 0); ret = bootmeth_get_bootflow(iter->method, bflow); diff --git a/boot/bootmeth-uclass.c b/boot/bootmeth-uclass.c index 8c191082cf3..152c334f205 100644 --- a/boot/bootmeth-uclass.c +++ b/boot/bootmeth-uclass.c @@ -147,8 +147,7 @@ int bootmeth_setup_iter_order(struct bootflow_iter *iter, bool include_global) bool is_global; ucp = dev_get_uclass_plat(dev); - is_global = ucp->flags & - BOOTMETHF_GLOBAL; + is_global = ucp->flags & BOOTMETHF_GLOBAL; if (is_global) { iter->first_glob_method = i; break; @@ -193,6 +192,7 @@ int bootmeth_setup_iter_order(struct bootflow_iter *iter, bool include_global) if (!count) return log_msg_ret("count2", -ENOENT); + /* start with the global bootmeths */ if (IS_ENABLED(CONFIG_BOOTMETH_GLOBAL) && include_global && iter->first_glob_method != -1 && iter->first_glob_method != count) { iter->cur_method = iter->first_glob_method; diff --git a/include/bootflow.h b/include/bootflow.h index 284c23c59dd..4d1f888a5a8 100644 --- a/include/bootflow.h +++ b/include/bootflow.h @@ -263,7 +263,8 @@ enum bootflow_meth_flags_t { * @cur_label: Current label being processed * @num_methods: Number of bootmeth devices in @method_order * @cur_method: Current method number, an index into @method_order - * @first_glob_method: First global method, if any, else -1 + * @first_glob_method: Index of first global method within @method_order[], if + * any, else -1 * @cur_prio: Current priority being scanned * @method_order: List of bootmeth devices to use, in order. The normal methods * appear first, then the global ones, if any diff --git a/test/boot/bootflow.c b/test/boot/bootflow.c index 14e3326a08f..be85032985d 100644 --- a/test/boot/bootflow.c +++ b/test/boot/bootflow.c @@ -439,7 +439,7 @@ static int bootflow_system(struct unit_test_state *uts) ut_assertok(device_probe(dev)); sandbox_set_fake_efi_mgr_dev(dev, true); - /* We should get a single 'bootmgr' method right at the end */ + /* We should get a single 'bootmgr' method at the start */ bootstd_clear_glob(); ut_assertok(run_command("bootflow scan -lH", 0)); ut_assert_skip_to_line( -- 2.43.0

Am 30. September 2025 02:51:17 MESZ schrieb Simon Glass <sjg@u-boot.org>:
From: Simon Glass <sjg@chromium.org>
Add a few comments about global bootmeths and first_glob_method
Fix a broken line in bootmeth_setup_iter_order() while we are here.
Signed-off-by: Simon Glass <sjg@chromium.org>
LGTM Reviewed-by: Heinrich Schuchardt <xypron.glpk@gmx.de>
---
boot/bootflow.c | 3 ++- boot/bootmeth-uclass.c | 4 ++-- include/bootflow.h | 3 ++- test/boot/bootflow.c | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-)
diff --git a/boot/bootflow.c b/boot/bootflow.c index c8391641001..de1f0c99916 100644 --- a/boot/bootflow.c +++ b/boot/bootflow.c @@ -150,7 +150,7 @@ static void bootflow_iter_set_dev(struct bootflow_iter *iter, if (dev) printf("Scanning bootdev '%s':\n", dev->name); else if (IS_ENABLED(CONFIG_BOOTMETH_GLOBAL) && - ucp->flags & BOOTMETHF_GLOBAL) + ucp->flags & BOOTMETHF_GLOBAL) printf("Scanning global bootmeth '%s':\n", iter->method->name); else @@ -347,6 +347,7 @@ static int bootflow_check(struct bootflow_iter *iter, struct bootflow *bflow) struct udevice *dev; int ret;
+ /* handle global bootmeths if needed */ if (IS_ENABLED(CONFIG_BOOTMETH_GLOBAL) && iter->doing_global) { bootflow_iter_set_dev(iter, NULL, 0); ret = bootmeth_get_bootflow(iter->method, bflow); diff --git a/boot/bootmeth-uclass.c b/boot/bootmeth-uclass.c index 8c191082cf3..152c334f205 100644 --- a/boot/bootmeth-uclass.c +++ b/boot/bootmeth-uclass.c @@ -147,8 +147,7 @@ int bootmeth_setup_iter_order(struct bootflow_iter *iter, bool include_global) bool is_global;
ucp = dev_get_uclass_plat(dev); - is_global = ucp->flags & - BOOTMETHF_GLOBAL; + is_global = ucp->flags & BOOTMETHF_GLOBAL; if (is_global) { iter->first_glob_method = i; break; @@ -193,6 +192,7 @@ int bootmeth_setup_iter_order(struct bootflow_iter *iter, bool include_global) if (!count) return log_msg_ret("count2", -ENOENT);
+ /* start with the global bootmeths */ if (IS_ENABLED(CONFIG_BOOTMETH_GLOBAL) && include_global && iter->first_glob_method != -1 && iter->first_glob_method != count) { iter->cur_method = iter->first_glob_method; diff --git a/include/bootflow.h b/include/bootflow.h index 284c23c59dd..4d1f888a5a8 100644 --- a/include/bootflow.h +++ b/include/bootflow.h @@ -263,7 +263,8 @@ enum bootflow_meth_flags_t { * @cur_label: Current label being processed * @num_methods: Number of bootmeth devices in @method_order * @cur_method: Current method number, an index into @method_order - * @first_glob_method: First global method, if any, else -1 + * @first_glob_method: Index of first global method within @method_order[], if + * any, else -1 * @cur_prio: Current priority being scanned * @method_order: List of bootmeth devices to use, in order. The normal methods * appear first, then the global ones, if any diff --git a/test/boot/bootflow.c b/test/boot/bootflow.c index 14e3326a08f..be85032985d 100644 --- a/test/boot/bootflow.c +++ b/test/boot/bootflow.c @@ -439,7 +439,7 @@ static int bootflow_system(struct unit_test_state *uts) ut_assertok(device_probe(dev)); sandbox_set_fake_efi_mgr_dev(dev, true);
- /* We should get a single 'bootmgr' method right at the end */ + /* We should get a single 'bootmgr' method at the start */ bootstd_clear_glob(); ut_assertok(run_command("bootflow scan -lH", 0)); ut_assert_skip_to_line(

From: Simon Glass <sjg@chromium.org> This function is the core of the bootstd iteration. Add some debugging for the decisions it makes along the way, to make it easier to track what is going on. Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/bootflow.c | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/boot/bootflow.c b/boot/bootflow.c index de1f0c99916..79be94960e7 100644 --- a/boot/bootflow.c +++ b/boot/bootflow.c @@ -195,14 +195,19 @@ static int iter_incr(struct bootflow_iter *iter) log_debug("entry: err=%d\n", iter->err); global = iter->doing_global; - if (iter->err == BF_NO_MORE_DEVICES) + if (iter->err == BF_NO_MORE_DEVICES) { + log_debug("-> err: no more devices1\n"); return BF_NO_MORE_DEVICES; + } /* Get the next boothmethod */ if (++iter->cur_method < iter->num_methods) { iter->method = iter->method_order[iter->cur_method]; + log_debug("-> next method '%s'\n", iter->method->name); return 0; } + log_debug("! no more methods: cur_method %d num_methods %d\n", + iter->cur_method, iter->num_methods); /* * If we have finished scanning the global bootmeths, start the @@ -219,8 +224,10 @@ static int iter_incr(struct bootflow_iter *iter) inc_dev = false; } - if (iter->flags & BOOTFLOWIF_SINGLE_PARTITION) + if (iter->flags & BOOTFLOWIF_SINGLE_PARTITION) { + log_debug("-> single partition: no more devices\n"); return BF_NO_MORE_DEVICES; + } /* No more bootmeths; start at the first one, and... */ iter->cur_method = 0; @@ -228,11 +235,15 @@ static int iter_incr(struct bootflow_iter *iter) if (iter->err != BF_NO_MORE_PARTS) { /* ...select next partition */ - if (++iter->part <= iter->max_part) + if (++iter->part <= iter->max_part) { + log_debug("-> next partition %d max %d\n", iter->part, + iter->max_part); return 0; + } } /* No more partitions; start at the first one and... */ + log_debug("! no more partitions\n"); iter->part = 0; /* @@ -328,8 +339,13 @@ static int iter_incr(struct bootflow_iter *iter) } /* if there are no more bootdevs, give up */ - if (ret) + if (ret) { + log_debug("-> no more bootdevs\n"); return log_msg_ret("incr", BF_NO_MORE_DEVICES); + } + + log_debug("-> bootdev '%s' method '%s'\n", dev->name, + iter->method->name); return 0; } -- 2.43.0

From: Simon Glass <sjg@chromium.org> It is helpful in tests to be able to show the bootflow that is being examined. Move show_bootflow() into boot/ and rename it. Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/bootflow.c | 55 +++++++++++++++++++++++++++++++++++++ cmd/bootflow.c | 68 ++-------------------------------------------- include/bootflow.h | 9 ++++++ 3 files changed, 67 insertions(+), 65 deletions(-) diff --git a/boot/bootflow.c b/boot/bootflow.c index 79be94960e7..4e646202f3d 100644 --- a/boot/bootflow.c +++ b/boot/bootflow.c @@ -56,6 +56,61 @@ const char *bootflow_state_get_name(enum bootflow_state_t state) return bootflow_state[state]; } +/** + * report_bootflow_err() - Report where a bootflow failed + * + * When a bootflow does not make it to the 'loaded' state, something went wrong. + * Print a helpful message if there is an error + * + * @bflow: Bootflow to process + * @err: Error code (0 if none) + */ +static void report_bootflow_err(struct bootflow *bflow, int err) +{ + if (!err) + return; + + /* Indent out to 'Method' */ + printf(" ** "); + + switch (bflow->state) { + case BOOTFLOWST_BASE: + printf("No media/partition found"); + break; + case BOOTFLOWST_MEDIA: + printf("No partition found"); + break; + case BOOTFLOWST_PART: + printf("No filesystem found"); + break; + case BOOTFLOWST_FS: + printf("File not found"); + break; + case BOOTFLOWST_FILE: + printf("File cannot be loaded"); + break; + case BOOTFLOWST_READY: + printf("Ready"); + break; + case BOOTFLOWST_COUNT: + break; + } + + printf(", err=%dE\n", err); +} + +void bootflow_show(int index, struct bootflow *bflow, bool errors) +{ + const char *name = bootflow_guess_label(bflow); + + printf("%3x %-11s %-6s %-9.9s %4x %-25.25s %s\n", index, + bflow->method ? bflow->method->name : "(none)", + bootflow_state_get_name(bflow->state), name, bflow->part, + bflow->name, bflow->fname ?: ""); + if (errors) + report_bootflow_err(bflow, bflow->err); +} + int bootflow_first_glob(struct bootflow **bflowp) { struct bootstd_priv *std; diff --git a/cmd/bootflow.c b/cmd/bootflow.c index 33ed9a1cd73..c9f36a364dd 100644 --- a/cmd/bootflow.c +++ b/cmd/bootflow.c @@ -18,68 +18,6 @@ #include <log.h> #include <mapmem.h> -/** - * report_bootflow_err() - Report where a bootflow failed - * - * When a bootflow does not make it to the 'loaded' state, something went wrong. - * Print a helpful message if there is an error - * - * @bflow: Bootflow to process - * @err: Error code (0 if none) - */ -static void report_bootflow_err(struct bootflow *bflow, int err) -{ - if (!err) - return; - - /* Indent out to 'Method' */ - printf(" ** "); - - switch (bflow->state) { - case BOOTFLOWST_BASE: - printf("No media/partition found"); - break; - case BOOTFLOWST_MEDIA: - printf("No partition found"); - break; - case BOOTFLOWST_PART: - printf("No filesystem found"); - break; - case BOOTFLOWST_FS: - printf("File not found"); - break; - case BOOTFLOWST_FILE: - printf("File cannot be loaded"); - break; - case BOOTFLOWST_READY: - printf("Ready"); - break; - case BOOTFLOWST_COUNT: - break; - } - - printf(", err=%dE\n", err); -} - -/** - * show_bootflow() - Show the status of a bootflow - * - * @seq: Bootflow index - * @bflow: Bootflow to show - * @errors: True to show the error received, if any - */ -static void show_bootflow(int index, struct bootflow *bflow, bool errors) -{ - const char *name = bootflow_guess_label(bflow); - - printf("%3x %-11s %-6s %-9.9s %4x %-25.25s %s\n", index, - bflow->method ? bflow->method->name : "(none)", - bootflow_state_get_name(bflow->state), name, bflow->part, - bflow->name, bflow->fname ?: ""); - if (errors) - report_bootflow_err(bflow, bflow->err); -} - static void show_header(void) { printf("Seq Method State Uclass Part Name Filename\n"); @@ -240,7 +178,7 @@ static int do_bootflow_scan(struct cmd_tbl *cmdtp, int flag, int argc, return CMD_RET_FAILURE; } if (list) - show_bootflow(i, &bflow, errors); + bootflow_show(i, &bflow, errors); if (!menu && boot && !bflow.err) bootflow_run_boot(&iter, &bflow); } @@ -298,7 +236,7 @@ static int do_bootflow_list(struct cmd_tbl *cmdtp, int flag, int argc, !ret; ret = bootdev_next_bootflow(&bflow), i++) { num_valid += bflow->state == BOOTFLOWST_READY; - show_bootflow(i, bflow, errors); + bootflow_show(i, bflow, errors); } } else { printf("Showing all bootflows\n"); @@ -307,7 +245,7 @@ static int do_bootflow_list(struct cmd_tbl *cmdtp, int flag, int argc, !ret; ret = bootflow_next_glob(&bflow), i++) { num_valid += bflow->state == BOOTFLOWST_READY; - show_bootflow(i, bflow, errors); + bootflow_show(i, bflow, errors); } } show_footer(i, num_valid); diff --git a/include/bootflow.h b/include/bootflow.h index 4d1f888a5a8..6f53a7d4991 100644 --- a/include/bootflow.h +++ b/include/bootflow.h @@ -740,4 +740,13 @@ int bootflow_menu_poll(struct expo *exp, int *seqp); */ const char *bootflow_guess_label(const struct bootflow *bflow); +/** + * bootflow_show() - Show the status of a bootflow + * + * @seq: Bootflow index + * @bflow: Bootflow to show + * @errors: True to show the error received, if any + */ +void bootflow_show(int index, struct bootflow *bflow, bool errors); + #endif -- 2.43.0

Am 30. September 2025 02:51:19 MESZ schrieb Simon Glass <sjg@u-boot.org>:
From: Simon Glass <sjg@chromium.org>
It is helpful in tests to be able to show the bootflow that is being examined. Move show_bootflow() into boot/ and rename it.
Signed-off-by: Simon Glass <sjg@chromium.org> ---
boot/bootflow.c | 55 +++++++++++++++++++++++++++++++++++++ cmd/bootflow.c | 68 ++-------------------------------------------- include/bootflow.h | 9 ++++++ 3 files changed, 67 insertions(+), 65 deletions(-)
diff --git a/boot/bootflow.c b/boot/bootflow.c index 79be94960e7..4e646202f3d 100644 --- a/boot/bootflow.c +++ b/boot/bootflow.c @@ -56,6 +56,61 @@ const char *bootflow_state_get_name(enum bootflow_state_t state) return bootflow_state[state]; }
+/** + * report_bootflow_err() - Report where a bootflow failed + * + * When a bootflow does not make it to the 'loaded' state, something went wrong. + * Print a helpful message if there is an error + * + * @bflow: Bootflow to process + * @err: Error code (0 if none) + */ +static void report_bootflow_err(struct bootflow *bflow, int err) +{ + if (!err) + return; + + /* Indent out to 'Method' */ + printf(" ** "); + + switch (bflow->state) { + case BOOTFLOWST_BASE: + printf("No media/partition found");
Shoud these printf() be log_err()?
+ break; + case BOOTFLOWST_MEDIA: + printf("No partition found"); + break; + case BOOTFLOWST_PART: + printf("No filesystem found"); + break; + case BOOTFLOWST_FS: + printf("File not found"); + break; + case BOOTFLOWST_FILE: + printf("File cannot be loaded"); + break; + case BOOTFLOWST_READY: + printf("Ready"); + break; + case BOOTFLOWST_COUNT:
Replace this by default: log_err("Unexpected boot value of bootflow error %d",bflow->state);
+ break; + } + + printf(", err=%dE\n", err);
log_cont()?
+} + +void bootflow_show(int index, struct bootflow *bflow, bool errors) +{ + const char *name = bootflow_guess_label(bflow); + + printf("%3x %-11s %-6s %-9.9s %4x %-25.25s %s\n", index, + bflow->method ? bflow->method->name : "(none)", + bootflow_state_get_name(bflow->state), name, bflow->part, + bflow->name, bflow->fname ?: ""); + if (errors) + report_bootflow_err(bflow, bflow->err); +} + int bootflow_first_glob(struct bootflow **bflowp) { struct bootstd_priv *std; diff --git a/cmd/bootflow.c b/cmd/bootflow.c index 33ed9a1cd73..c9f36a364dd 100644 --- a/cmd/bootflow.c +++ b/cmd/bootflow.c @@ -18,68 +18,6 @@ #include <log.h> #include <mapmem.h>
-/** - * report_bootflow_err() - Report where a bootflow failed - * - * When a bootflow does not make it to the 'loaded' state, something went wrong. - * Print a helpful message if there is an error - * - * @bflow: Bootflow to process - * @err: Error code (0 if none) - */ -static void report_bootflow_err(struct bootflow *bflow, int err) -{ - if (!err) - return; - - /* Indent out to 'Method' */ - printf(" ** "); - - switch (bflow->state) { - case BOOTFLOWST_BASE: - printf("No media/partition found"); - break; - case BOOTFLOWST_MEDIA: - printf("No partition found"); - break; - case BOOTFLOWST_PART: - printf("No filesystem found"); - break; - case BOOTFLOWST_FS: - printf("File not found"); - break; - case BOOTFLOWST_FILE: - printf("File cannot be loaded"); - break; - case BOOTFLOWST_READY: - printf("Ready"); - break; - case BOOTFLOWST_COUNT: - break; - } - - printf(", err=%dE\n", err); -} - -/** - * show_bootflow() - Show the status of a bootflow - * - * @seq: Bootflow index - * @bflow: Bootflow to show - * @errors: True to show the error received, if any - */ -static void show_bootflow(int index, struct bootflow *bflow, bool errors) -{ - const char *name = bootflow_guess_label(bflow); - - printf("%3x %-11s %-6s %-9.9s %4x %-25.25s %s\n", index, - bflow->method ? bflow->method->name : "(none)", - bootflow_state_get_name(bflow->state), name, bflow->part, - bflow->name, bflow->fname ?: ""); - if (errors) - report_bootflow_err(bflow, bflow->err); -} - static void show_header(void) { printf("Seq Method State Uclass Part Name Filename\n"); @@ -240,7 +178,7 @@ static int do_bootflow_scan(struct cmd_tbl *cmdtp, int flag, int argc, return CMD_RET_FAILURE; } if (list) - show_bootflow(i, &bflow, errors); + bootflow_show(i, &bflow, errors); if (!menu && boot && !bflow.err) bootflow_run_boot(&iter, &bflow); } @@ -298,7 +236,7 @@ static int do_bootflow_list(struct cmd_tbl *cmdtp, int flag, int argc, !ret; ret = bootdev_next_bootflow(&bflow), i++) { num_valid += bflow->state == BOOTFLOWST_READY; - show_bootflow(i, bflow, errors); + bootflow_show(i, bflow, errors); } } else { printf("Showing all bootflows\n"); @@ -307,7 +245,7 @@ static int do_bootflow_list(struct cmd_tbl *cmdtp, int flag, int argc, !ret; ret = bootflow_next_glob(&bflow), i++) { num_valid += bflow->state == BOOTFLOWST_READY; - show_bootflow(i, bflow, errors); + bootflow_show(i, bflow, errors); } } show_footer(i, num_valid); diff --git a/include/bootflow.h b/include/bootflow.h index 4d1f888a5a8..6f53a7d4991 100644 --- a/include/bootflow.h +++ b/include/bootflow.h @@ -740,4 +740,13 @@ int bootflow_menu_poll(struct expo *exp, int *seqp); */ const char *bootflow_guess_label(const struct bootflow *bflow);
+/** + * bootflow_show() - Show the status of a bootflow + * + * @seq: Bootflow index + * @bflow: Bootflow to show + * @errors: True to show the error received, if any + */ +void bootflow_show(int index, struct bootflow *bflow, bool errors); + #endif

Hi Heinrich, On Mon, 29 Sept 2025 at 19:38, Heinrich Schuchardt <xypron.glpk@gmx.de> wrote:
Am 30. September 2025 02:51:19 MESZ schrieb Simon Glass <sjg@u-boot.org>:
From: Simon Glass <sjg@chromium.org>
It is helpful in tests to be able to show the bootflow that is being examined. Move show_bootflow() into boot/ and rename it.
Signed-off-by: Simon Glass <sjg@chromium.org> ---
boot/bootflow.c | 55 +++++++++++++++++++++++++++++++++++++ cmd/bootflow.c | 68 ++-------------------------------------------- include/bootflow.h | 9 ++++++ 3 files changed, 67 insertions(+), 65 deletions(-)
diff --git a/boot/bootflow.c b/boot/bootflow.c index 79be94960e7..4e646202f3d 100644 --- a/boot/bootflow.c +++ b/boot/bootflow.c @@ -56,6 +56,61 @@ const char *bootflow_state_get_name(enum bootflow_state_t state) return bootflow_state[state]; }
+/** + * report_bootflow_err() - Report where a bootflow failed + * + * When a bootflow does not make it to the 'loaded' state, something went wrong. + * Print a helpful message if there is an error + * + * @bflow: Bootflow to process + * @err: Error code (0 if none) + */ +static void report_bootflow_err(struct bootflow *bflow, int err) +{ + if (!err) + return; + + /* Indent out to 'Method' */ + printf(" ** "); + + switch (bflow->state) { + case BOOTFLOWST_BASE: + printf("No media/partition found");
Shoud these printf() be log_err()?
I don't tend to use that for messages that the user sees. For example we might have LOGF_FUNC enabled and then the function name is shown.
+ break; + case BOOTFLOWST_MEDIA: + printf("No partition found"); + break; + case BOOTFLOWST_PART: + printf("No filesystem found"); + break; + case BOOTFLOWST_FS: + printf("File not found"); + break; + case BOOTFLOWST_FILE: + printf("File cannot be loaded"); + break; + case BOOTFLOWST_READY: + printf("Ready"); + break; + case BOOTFLOWST_COUNT:
Replace this by
default: log_err("Unexpected boot value of bootflow error %d",bflow->state);
OK. Note that this 'shouldn't happen' (TM);
+ break; + } + + printf(", err=%dE\n", err);
log_cont()?
See above. Regards, Simon
+} + +void bootflow_show(int index, struct bootflow *bflow, bool errors) +{ + const char *name = bootflow_guess_label(bflow); + + printf("%3x %-11s %-6s %-9.9s %4x %-25.25s %s\n", index, + bflow->method ? bflow->method->name : "(none)", + bootflow_state_get_name(bflow->state), name, bflow->part, + bflow->name, bflow->fname ?: ""); + if (errors) + report_bootflow_err(bflow, bflow->err); +} + int bootflow_first_glob(struct bootflow **bflowp) { struct bootstd_priv *std; diff --git a/cmd/bootflow.c b/cmd/bootflow.c index 33ed9a1cd73..c9f36a364dd 100644 --- a/cmd/bootflow.c +++ b/cmd/bootflow.c @@ -18,68 +18,6 @@ #include <log.h> #include <mapmem.h>
-/** - * report_bootflow_err() - Report where a bootflow failed - * - * When a bootflow does not make it to the 'loaded' state, something went wrong. - * Print a helpful message if there is an error - * - * @bflow: Bootflow to process - * @err: Error code (0 if none) - */ -static void report_bootflow_err(struct bootflow *bflow, int err) -{ - if (!err) - return; - - /* Indent out to 'Method' */ - printf(" ** "); - - switch (bflow->state) { - case BOOTFLOWST_BASE: - printf("No media/partition found"); - break; - case BOOTFLOWST_MEDIA: - printf("No partition found"); - break; - case BOOTFLOWST_PART: - printf("No filesystem found"); - break; - case BOOTFLOWST_FS: - printf("File not found"); - break; - case BOOTFLOWST_FILE: - printf("File cannot be loaded"); - break; - case BOOTFLOWST_READY: - printf("Ready"); - break; - case BOOTFLOWST_COUNT: - break; - } - - printf(", err=%dE\n", err); -} - -/** - * show_bootflow() - Show the status of a bootflow - * - * @seq: Bootflow index - * @bflow: Bootflow to show - * @errors: True to show the error received, if any - */ -static void show_bootflow(int index, struct bootflow *bflow, bool errors) -{ - const char *name = bootflow_guess_label(bflow); - - printf("%3x %-11s %-6s %-9.9s %4x %-25.25s %s\n", index, - bflow->method ? bflow->method->name : "(none)", - bootflow_state_get_name(bflow->state), name, bflow->part, - bflow->name, bflow->fname ?: ""); - if (errors) - report_bootflow_err(bflow, bflow->err); -} - static void show_header(void) { printf("Seq Method State Uclass Part Name Filename\n"); @@ -240,7 +178,7 @@ static int do_bootflow_scan(struct cmd_tbl *cmdtp, int flag, int argc, return CMD_RET_FAILURE; } if (list) - show_bootflow(i, &bflow, errors); + bootflow_show(i, &bflow, errors); if (!menu && boot && !bflow.err) bootflow_run_boot(&iter, &bflow); } @@ -298,7 +236,7 @@ static int do_bootflow_list(struct cmd_tbl *cmdtp, int flag, int argc, !ret; ret = bootdev_next_bootflow(&bflow), i++) { num_valid += bflow->state == BOOTFLOWST_READY; - show_bootflow(i, bflow, errors); + bootflow_show(i, bflow, errors); } } else { printf("Showing all bootflows\n"); @@ -307,7 +245,7 @@ static int do_bootflow_list(struct cmd_tbl *cmdtp, int flag, int argc, !ret; ret = bootflow_next_glob(&bflow), i++) { num_valid += bflow->state == BOOTFLOWST_READY; - show_bootflow(i, bflow, errors); + bootflow_show(i, bflow, errors); } } show_footer(i, num_valid); diff --git a/include/bootflow.h b/include/bootflow.h index 4d1f888a5a8..6f53a7d4991 100644 --- a/include/bootflow.h +++ b/include/bootflow.h @@ -740,4 +740,13 @@ int bootflow_menu_poll(struct expo *exp, int *seqp); */ const char *bootflow_guess_label(const struct bootflow *bflow);
+/** + * bootflow_show() - Show the status of a bootflow + * + * @seq: Bootflow index + * @bflow: Bootflow to show + * @errors: True to show the error received, if any + */ +void bootflow_show(int index, struct bootflow *bflow, bool errors); + #endif

From: Simon Glass <sjg@chromium.org> These have different behaviour from normal bootmeths and we are about to enhance it. So add a test and also an extra check in bootflow_iter() Signed-off-by: Simon Glass <sjg@chromium.org> --- test/boot/bootflow.c | 45 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/test/boot/bootflow.c b/test/boot/bootflow.c index be85032985d..69a965dfa4b 100644 --- a/test/boot/bootflow.c +++ b/test/boot/bootflow.c @@ -313,6 +313,8 @@ static int bootflow_iter(struct unit_test_state *uts) ut_asserteq(0, iter.max_part); ut_asserteq_str("extlinux", iter.method->name); ut_asserteq(0, bflow.err); + ut_assert(!iter.doing_global); + ut_asserteq(-1, iter.first_glob_method); /* * This shows MEDIA even though there is none, since in @@ -426,6 +428,49 @@ static int bootflow_iter(struct unit_test_state *uts) BOOTSTD_TEST(bootflow_iter, UTF_DM | UTF_SCAN_FDT | UTF_CONSOLE); #if defined(CONFIG_SANDBOX) && defined(CONFIG_BOOTMETH_GLOBAL) + +/* Check iterating through available bootflows to test global bootmeths */ +static int bootflow_iter_glob(struct unit_test_state *uts) +{ + struct bootflow_iter iter; + struct bootflow bflow; + + bootstd_clear_glob(); + + /* we should get the global bootmeth initially */ + ut_asserteq(-EINVAL, + bootflow_scan_first(NULL, NULL, &iter, BOOTFLOWIF_ALL | + BOOTFLOWIF_SHOW, &bflow)); + bootflow_show(0, &bflow, true); + ut_asserteq(4, iter.num_methods); + ut_assert(iter.doing_global); + ut_asserteq(3, iter.first_glob_method); + + ut_asserteq(3, iter.cur_method); + ut_asserteq(0, iter.part); + ut_asserteq(0, iter.max_part); + ut_asserteq_str("firmware0", iter.method->name); + ut_asserteq(0, bflow.err); + bootflow_free(&bflow); + + /* next we should get the first non-global bootmeth */ + ut_asserteq(-EPROTONOSUPPORT, bootflow_scan_next(&iter, &bflow)); + + /* at this point the global bootmeths are stranded above num_methods */ + ut_asserteq(3, iter.num_methods); + ut_asserteq(3, iter.first_glob_method); + ut_assert(!iter.doing_global); + + ut_asserteq(0, iter.cur_method); + ut_asserteq(0, iter.part); + ut_asserteq(0, iter.max_part); + ut_asserteq_str("extlinux", iter.method->name); + ut_asserteq(0, bflow.err); + + return 0; +} +BOOTSTD_TEST(bootflow_iter_glob, UTF_DM | UTF_SCAN_FDT); + /* Check using the system bootdev */ static int bootflow_system(struct unit_test_state *uts) { -- 2.43.0

From: Simon Glass <sjg@chromium.org> For now we only support dropping non-global bootmeths from the iteration. Update first_glob_method in that case and add a few checks that things are correct. Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/bootflow.c | 6 ++++++ test/boot/bootflow.c | 8 ++++++++ 2 files changed, 14 insertions(+) diff --git a/boot/bootflow.c b/boot/bootflow.c index 4e646202f3d..efe8aea765d 100644 --- a/boot/bootflow.c +++ b/boot/bootflow.c @@ -167,11 +167,17 @@ int bootflow_iter_drop_bootmeth(struct bootflow_iter *iter, iter->method_order[iter->cur_method] != bmeth) return -EINVAL; + log_debug("Dropping bootmeth '%s'\n", bmeth->name); + memmove(&iter->method_order[iter->cur_method], &iter->method_order[iter->cur_method + 1], (iter->num_methods - iter->cur_method - 1) * sizeof(void *)); iter->num_methods--; + if (iter->first_glob_method > 0) { + iter->first_glob_method--; + log_debug("first_glob_method %d\n", iter->first_glob_method); + } return 0; } diff --git a/test/boot/bootflow.c b/test/boot/bootflow.c index 69a965dfa4b..7748a5ad534 100644 --- a/test/boot/bootflow.c +++ b/test/boot/bootflow.c @@ -521,7 +521,11 @@ static int bootflow_iter_disable(struct unit_test_state *uts) /* Try to boot the bootmgr flow, which will fail */ console_record_reset_enable(); ut_assertok(bootflow_scan_first(NULL, NULL, &iter, 0, &bflow)); + + /* at this point the global bootmeths are stranded above num_methods */ ut_asserteq(4, iter.num_methods); + ut_assert(!iter.doing_global); + ut_asserteq(4, iter.first_glob_method); ut_asserteq_str("sandbox", iter.method->name); ut_assertok(inject_response(uts)); ut_asserteq(-ENOTSUPP, bootflow_run_boot(&iter, &bflow)); @@ -531,9 +535,13 @@ static int bootflow_iter_disable(struct unit_test_state *uts) /* Check that the sandbox bootmeth has been removed */ ut_asserteq(3, iter.num_methods); + for (i = 0; i < iter.num_methods; i++) ut_assert(strcmp("sandbox", iter.method_order[i]->name)); + /* the first global bootmeth is now down one place in the list */ + ut_asserteq(3, iter.first_glob_method); + return 0; } BOOTSTD_TEST(bootflow_iter_disable, UTF_DM | UTF_SCAN_FDT | UTF_CONSOLE); -- 2.43.0

From: Simon Glass <sjg@chromium.org> The current 'doing_global' refers to being in the state of processing global bootmeths. Since global bootmeths are currently used once at the start, it becomes false once the last global bootmeth has been used. In preparation for allowing bootmeths to run at other points in the bootstd interation, add a new 'have_global' flag which tracks whether there are any global bootmeths in the method_order[] list. It is set up when iteration starts. Unlike doing_global which resets back to false after the global bootmeths have been handled, once have_global is set to true, it remains true for the entire iteration process. This provides a quick check as to whether global-bootmeth processing is needed. Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/bootmeth-uclass.c | 1 + include/bootflow.h | 2 ++ test/boot/bootflow.c | 4 ++++ 3 files changed, 7 insertions(+) diff --git a/boot/bootmeth-uclass.c b/boot/bootmeth-uclass.c index 152c334f205..0147b97fcb0 100644 --- a/boot/bootmeth-uclass.c +++ b/boot/bootmeth-uclass.c @@ -197,6 +197,7 @@ int bootmeth_setup_iter_order(struct bootflow_iter *iter, bool include_global) iter->first_glob_method != -1 && iter->first_glob_method != count) { iter->cur_method = iter->first_glob_method; iter->doing_global = true; + iter->have_global = true; } iter->method_order = order; iter->num_methods = count; diff --git a/include/bootflow.h b/include/bootflow.h index 6f53a7d4991..0805d10e197 100644 --- a/include/bootflow.h +++ b/include/bootflow.h @@ -268,6 +268,7 @@ enum bootflow_meth_flags_t { * @cur_prio: Current priority being scanned * @method_order: List of bootmeth devices to use, in order. The normal methods * appear first, then the global ones, if any + * @have_global: true if we have global bootmeths in @method_order[] * @doing_global: true if we are iterating through the global bootmeths (which * happens before the normal ones) * @method_flags: flags controlling which methods should be used for this @dev @@ -291,6 +292,7 @@ struct bootflow_iter { int first_glob_method; enum bootdev_prio_t cur_prio; struct udevice **method_order; + bool have_global; bool doing_global; int method_flags; }; diff --git a/test/boot/bootflow.c b/test/boot/bootflow.c index 7748a5ad534..a18afad0f72 100644 --- a/test/boot/bootflow.c +++ b/test/boot/bootflow.c @@ -314,6 +314,7 @@ static int bootflow_iter(struct unit_test_state *uts) ut_asserteq_str("extlinux", iter.method->name); ut_asserteq(0, bflow.err); ut_assert(!iter.doing_global); + ut_assert(!iter.have_global); ut_asserteq(-1, iter.first_glob_method); /* @@ -444,6 +445,7 @@ static int bootflow_iter_glob(struct unit_test_state *uts) bootflow_show(0, &bflow, true); ut_asserteq(4, iter.num_methods); ut_assert(iter.doing_global); + ut_assert(iter.have_global); ut_asserteq(3, iter.first_glob_method); ut_asserteq(3, iter.cur_method); @@ -460,6 +462,7 @@ static int bootflow_iter_glob(struct unit_test_state *uts) ut_asserteq(3, iter.num_methods); ut_asserteq(3, iter.first_glob_method); ut_assert(!iter.doing_global); + ut_assert(iter.have_global); ut_asserteq(0, iter.cur_method); ut_asserteq(0, iter.part); @@ -525,6 +528,7 @@ static int bootflow_iter_disable(struct unit_test_state *uts) /* at this point the global bootmeths are stranded above num_methods */ ut_asserteq(4, iter.num_methods); ut_assert(!iter.doing_global); + ut_assert(iter.have_global); ut_asserteq(4, iter.first_glob_method); ut_asserteq_str("sandbox", iter.method->name); ut_assertok(inject_response(uts)); -- 2.43.0

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(+) 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); -- 2.43.0

From: Simon Glass <sjg@chromium.org> The code at the end of iter_inc() is already somewhat tortuous. Before making it worse, move it into a function. Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/bootflow.c | 40 +++++++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/boot/bootflow.c b/boot/bootflow.c index 6f5876548b2..a91d76c6baf 100644 --- a/boot/bootflow.c +++ b/boot/bootflow.c @@ -245,6 +245,32 @@ static void scan_next_in_uclass(struct udevice **devp) *devp = dev; } +/** + * 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 + * 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 ret; + + /* + * Probe the bootdev. This does not probe any attached block device, + * since they are siblings + */ + ret = device_probe(dev); + log_debug("probe %s %d\n", dev->name, ret); + if (ret) + return log_msg_ret("probe", ret); + bootflow_iter_set_dev(iter, dev, method_flags); + + return 0; +} + /** * iter_incr() - Move to the next item (method, part, bootdev) * @@ -389,18 +415,10 @@ static int iter_incr(struct bootflow_iter *iter) } log_debug("ret=%d, dev=%p %s\n", ret, dev, dev ? dev->name : "none"); - if (ret) { + if (ret) bootflow_iter_set_dev(iter, NULL, 0); - } else { - /* - * Probe the bootdev. This does not probe any attached - * block device, since they are siblings - */ - ret = device_probe(dev); - log_debug("probe %s %d\n", dev->name, ret); - if (!log_msg_ret("probe", ret)) - bootflow_iter_set_dev(iter, dev, method_flags); - } + else + ret = prepare_bootdev(iter, dev, method_flags); } /* if there are no more bootdevs, give up */ -- 2.43.0

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(-) 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; }; /** -- 2.43.0

From: Simon Glass <sjg@chromium.org> Use the methods_done flags to make sure that each global bootmeth is only used once. For now this has no effect, since they are all processed at the start. Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/bootflow.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/boot/bootflow.c b/boot/bootflow.c index 51e6ad6dd86..d6c396e3087 100644 --- a/boot/bootflow.c +++ b/boot/bootflow.c @@ -354,7 +354,13 @@ static int iter_incr(struct bootflow_iter *iter) } /* Get the next boothmethod */ - if (++iter->cur_method < iter->num_methods) { + for (iter->cur_method++; iter->cur_method < iter->num_methods; + iter->cur_method++) { + /* loop until we find a global bootmeth we haven't used */ + if (IS_ENABLED(CONFIG_BOOTMETH_GLOBAL) && iter->doing_global && + !bootmeth_glob_allowed(iter, iter->cur_method)) + continue; + iter->method = iter->method_order[iter->cur_method]; log_debug("-> next method '%s'\n", iter->method->name); return 0; -- 2.43.0

From: Simon Glass <sjg@chromium.org> Allow bootmeths to select when they want to run, using the bootdev priority. Provide a new bootmeth_glob_allowed() function which checks if a bootmeth is ready to use. Fix a comment in bootflow_system() which is a test for global bootmeths. Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/bootflow.c | 15 +++++++++++---- include/bootflow.h | 2 +- include/bootmeth.h | 4 ++++ 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/boot/bootflow.c b/boot/bootflow.c index d6c396e3087..becb2ef109f 100644 --- a/boot/bootflow.c +++ b/boot/bootflow.c @@ -249,17 +249,24 @@ static void scan_next_in_uclass(struct udevice **devp) * 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 + * Return: true if the global bootmeth has a suitable priority and 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); + struct bootmeth_uc_plat *ucp; - log_debug("considering glob '%s': done %d\n", meth->name, done); + ucp = dev_get_uclass_plat(meth); + log_debug("considering glob '%s': done %d glob_prio %d\n", meth->name, + done, ucp->glob_prio); - /* if this one has already been used, try the next */ - if (done) + /* + * if this one has already been used, or its priority is too low, try + * the next + */ + if (done || ucp->glob_prio > iter->cur_prio) return false; return true; diff --git a/include/bootflow.h b/include/bootflow.h index 5ef0f4b61d3..351a6539978 100644 --- a/include/bootflow.h +++ b/include/bootflow.h @@ -275,7 +275,7 @@ enum { * appear first, then the global ones, if any * @have_global: true if we have global bootmeths in @method_order[] * @doing_global: true if we are iterating through the global bootmeths (which - * happens before the normal ones) + * generally 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 diff --git a/include/bootmeth.h b/include/bootmeth.h index a34bfc91dd5..b5288843d03 100644 --- a/include/bootmeth.h +++ b/include/bootmeth.h @@ -30,10 +30,14 @@ enum bootmeth_flags { * * @desc: A long description of the bootmeth * @flags: Flags for this bootmeth (enum bootmeth_flags) + * @glob_prio: Priority for this bootmeth. If unset (0) the bootmeth is started + * before all other bootmeths. Otherwise it is started before the iteration + * reaches the given priority. */ struct bootmeth_uc_plat { const char *desc; int flags; + enum bootdev_prio_t glob_prio; }; /** struct bootmeth_ops - Operations for boot methods */ -- 2.43.0

From: Simon Glass <sjg@chromium.org> At present before scanning global bootmeths, the iterator sets the method count to the index of the first global bootmeth. Now that we support scanning the global bootmeths multiple times, we must leave this count alone. Check against have_global and first_glob_method instead. Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/bootflow.c | 18 ++++++++++++++---- test/boot/bootflow.c | 6 +++--- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/boot/bootflow.c b/boot/bootflow.c index becb2ef109f..e58c55ebf19 100644 --- a/boot/bootflow.c +++ b/boot/bootflow.c @@ -364,9 +364,20 @@ static int iter_incr(struct bootflow_iter *iter) for (iter->cur_method++; iter->cur_method < iter->num_methods; iter->cur_method++) { /* loop until we find a global bootmeth we haven't used */ - if (IS_ENABLED(CONFIG_BOOTMETH_GLOBAL) && iter->doing_global && - !bootmeth_glob_allowed(iter, iter->cur_method)) - continue; + if (IS_ENABLED(CONFIG_BOOTMETH_GLOBAL) && iter->doing_global) { + if (!bootmeth_glob_allowed(iter, iter->cur_method)) + continue; + + iter->method = iter->method_order[iter->cur_method]; + log_debug("-> next global method '%s'\n", + iter->method->name); + return 0; + } + + /* at this point we are only considering non-global bootmeths */ + if (IS_ENABLED(CONFIG_BOOTMETH_GLOBAL) && iter->have_global && + iter->cur_method >= iter->first_glob_method) + break; iter->method = iter->method_order[iter->cur_method]; log_debug("-> next method '%s'\n", iter->method->name); @@ -380,7 +391,6 @@ static int iter_incr(struct bootflow_iter *iter) * normal bootdev scan */ if (IS_ENABLED(CONFIG_BOOTMETH_GLOBAL) && global) { - iter->num_methods = iter->first_glob_method; iter->doing_global = false; /* diff --git a/test/boot/bootflow.c b/test/boot/bootflow.c index af7f7391160..ceab3c453c4 100644 --- a/test/boot/bootflow.c +++ b/test/boot/bootflow.c @@ -467,7 +467,7 @@ static int bootflow_iter_glob(struct unit_test_state *uts) ut_asserteq(-EPROTONOSUPPORT, bootflow_scan_next(&iter, &bflow)); /* at this point the global bootmeths are stranded above num_methods */ - ut_asserteq(3, iter.num_methods); + ut_asserteq(4, iter.num_methods); ut_asserteq(3, iter.first_glob_method); ut_assert(!iter.doing_global); ut_assert(iter.have_global); @@ -534,7 +534,7 @@ static int bootflow_iter_disable(struct unit_test_state *uts) ut_assertok(bootflow_scan_first(NULL, NULL, &iter, 0, &bflow)); /* at this point the global bootmeths are stranded above num_methods */ - ut_asserteq(4, iter.num_methods); + ut_asserteq(5, iter.num_methods); ut_assert(!iter.doing_global); ut_assert(iter.have_global); ut_asserteq(4, iter.first_glob_method); @@ -546,7 +546,7 @@ static int bootflow_iter_disable(struct unit_test_state *uts) ut_assert_console_end(); /* Check that the sandbox bootmeth has been removed */ - ut_asserteq(3, iter.num_methods); + ut_asserteq(4, iter.num_methods); for (i = 0; i < iter.num_methods; i++) ut_assert(strcmp("sandbox", iter.method_order[i]->name)); -- 2.43.0

From: Simon Glass <sjg@chromium.org> When there are no more bootdevs we should still go through the global bootmeths, since some may not have yet been used, if their priority has not yet come up. Add a final check for this at the end of the iterator. Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/bootflow.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/boot/bootflow.c b/boot/bootflow.c index e58c55ebf19..9769a8a5f74 100644 --- a/boot/bootflow.c +++ b/boot/bootflow.c @@ -417,6 +417,18 @@ static int iter_incr(struct bootflow_iter *iter) return 0; } + /* if this was the final global bootmeth check, we are done */ + if (iter->cur_prio == BOOTDEVP_COUNT) { + log_debug("-> done global bootmeths\n"); + + /* print the same message as bootflow_iter_set_dev() */ + if ((iter->flags & (BOOTFLOWIF_SHOW | + BOOTFLOWIF_SINGLE_DEV)) == + BOOTFLOWIF_SHOW) + printf("No more bootdevs\n"); + return BF_NO_MORE_DEVICES; + } + /* * Don't move to the next dev as we haven't tried this * one yet! @@ -530,6 +542,17 @@ static int iter_incr(struct bootflow_iter *iter) ret = prepare_bootdev(iter, dev, method_flags, true); } + if (IS_ENABLED(CONFIG_BOOTMETH_GLOBAL) && ret) { + log_debug("no more bootdevs, trying global\n"); + + /* allow global bootmeths with any priority */ + iter->cur_prio = BOOTDEVP_COUNT; + if (!next_glob_bootmeth(iter)) { + log_debug("-> next method '%s'\n", iter->method->name); + return 0; + } + } + /* if there are no more bootdevs, give up */ if (ret) { log_debug("-> no more bootdevs\n"); -- 2.43.0

From: Simon Glass <sjg@chromium.org> At present the EFI bootmgr scans all devices in the system before deciding which one to boot. Ideally it would use the bootstd iterator for this, but in the meantime, give it a lower priority, so it runs just before the network devices. Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/bootmeth_efi_mgr.c | 1 + test/boot/bootflow.c | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/boot/bootmeth_efi_mgr.c b/boot/bootmeth_efi_mgr.c index 5e83d1da103..917cec2fe64 100644 --- a/boot/bootmeth_efi_mgr.c +++ b/boot/bootmeth_efi_mgr.c @@ -98,6 +98,7 @@ static int bootmeth_efi_mgr_bind(struct udevice *dev) plat->desc = "EFI bootmgr flow"; plat->flags = BOOTMETHF_GLOBAL; + plat->glob_prio = BOOTDEVP_6_NET_BASE; return 0; } diff --git a/test/boot/bootflow.c b/test/boot/bootflow.c index ceab3c453c4..65e2b5b5c9d 100644 --- a/test/boot/bootflow.c +++ b/test/boot/bootflow.c @@ -495,11 +495,11 @@ static int bootflow_system(struct unit_test_state *uts) ut_assertok(device_probe(dev)); sandbox_set_fake_efi_mgr_dev(dev, true); - /* We should get a single 'bootmgr' method at the start */ + /* We should get a single 'bootmgr' method at the end */ bootstd_clear_glob(); ut_assertok(run_command("bootflow scan -lH", 0)); ut_assert_skip_to_line( - " 0 efi_mgr ready (none) 0 <NULL> "); + " 1 efi_mgr ready (none) 0 <NULL> "); ut_assert_skip_to_line("No more bootdevs"); ut_assert_skip_to_line("(2 bootflows, 2 valid)"); ut_assert_console_end(); -- 2.43.0

Am 30. September 2025 02:51:30 MESZ schrieb Simon Glass <sjg@u-boot.org>:
From: Simon Glass <sjg@chromium.org>
At present the EFI bootmgr scans all devices in the system before deciding which one to boot. Ideally it would use the bootstd iterator for this, but in the meantime, give it a lower priority, so it runs just before the network devices.
Signed-off-by: Simon Glass <sjg@chromium.org> ---
boot/bootmeth_efi_mgr.c | 1 + test/boot/bootflow.c | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/boot/bootmeth_efi_mgr.c b/boot/bootmeth_efi_mgr.c index 5e83d1da103..917cec2fe64 100644 --- a/boot/bootmeth_efi_mgr.c +++ b/boot/bootmeth_efi_mgr.c @@ -98,6 +98,7 @@ static int bootmeth_efi_mgr_bind(struct udevice *dev)
plat->desc = "EFI bootmgr flow"; plat->flags = BOOTMETHF_GLOBAL; + plat->glob_prio = BOOTDEVP_6_NET_BASE;
Is this meant to work because at each priority level, global methods will be treated first? Maybe mention this in the commit message. Adding a code comment here why this priority was chosen would be helpful.
return 0; } diff --git a/test/boot/bootflow.c b/test/boot/bootflow.c index ceab3c453c4..65e2b5b5c9d 100644 --- a/test/boot/bootflow.c +++ b/test/boot/bootflow.c @@ -495,11 +495,11 @@ static int bootflow_system(struct unit_test_state *uts) ut_assertok(device_probe(dev)); sandbox_set_fake_efi_mgr_dev(dev, true);
- /* We should get a single 'bootmgr' method at the start */ + /* We should get a single 'bootmgr' method at the end */ bootstd_clear_glob(); ut_assertok(run_command("bootflow scan -lH", 0)); ut_assert_skip_to_line( - " 0 efi_mgr ready (none) 0 <NULL> "); + " 1 efi_mgr ready (none) 0 <NULL> "); ut_assert_skip_to_line("No more bootdevs"); ut_assert_skip_to_line("(2 bootflows, 2 valid)"); ut_assert_console_end();

From: Simon Glass <sjg@chromium.org> Provide some developer documentation on the priority feature for global bootmeths. Signed-off-by: Simon Glass <sjg@chromium.org> --- doc/develop/bootstd/overview.rst | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/doc/develop/bootstd/overview.rst b/doc/develop/bootstd/overview.rst index ca76bad7bca..4c924e2eb21 100644 --- a/doc/develop/bootstd/overview.rst +++ b/doc/develop/bootstd/overview.rst @@ -133,7 +133,8 @@ which scans for available bootflows, optionally listing each find it finds (-l) and trying to boot it (-b). When global bootmeths are available, these are typically checked before the -above bootdev scanning. +above bootdev scanning, but it is possible provide a priority to make them +run later, by setting the glob_prio field in the driver's bind() method. Controlling ordering @@ -614,9 +615,9 @@ simply copied into the iterator. Either way, the `method_order` array it set up, along with `num_methods`. Note that global bootmeths are always put at the end of the ordering. If any are -present, `cur_method` is set to the first one, so that global bootmeths are done -first. Once all have been used, these bootmeths are dropped from the iteration. -When there are no global bootmeths, `cur_method` is set to 0. +present, `cur_method` is set to the first one, so that global bootmeths are +processed first, so long as their priority allows it. Bootstd keeps track of +which global bootmeths have been used, to make sure they are only used once. At this point the iterator is ready to use, with the first bootmeth selected. Most of the other fields are 0. This means that the current partition @@ -719,6 +720,23 @@ When it sees `BF_NO_MORE_DEVICES` it knows that there is nothing more it can do so it should immediately return. The caller of `iter_incr()` is responsible for updating the `err` field, based on the return value it sees. +Global bootmeths can have a non-zero priority, which indicates where in the +iteration sequence they should run. Each time a new bootdev is produced by a +hunter, all of the global bootmeths are first checked to see if they should run +before this new bootdev. For example, if the bootdev was produced by a hunter +with priority BOOTDEVP_6_NET_BASE, then a quick check is made for global +bootmeths with that priority or less. If there are any, they run before the new +bootdev is processed. + +Assuming they are enabled and the iteration sequence runs right to the end, all +global bootmeths will be used. This is handled by a special case at the end of +iter_incr(), where it processes amy so-far-unused global bootmeths. + +Of course if a specific bootmeth ordering is provided, then this overrides the +default ordering. Global bootmeths must be listed at the end, reflecting their +hybrid nature (they are bootmeths but operate on the system as a whole, not on +a particular bootdev). + The above describes the iteration process at a high level. It is basically a very simple increment function with a checker called `bootflow_check()` that checks the result of each iteration generated, to determine whether it can -- 2.43.0

Am 30. September 2025 02:51:31 MESZ schrieb Simon Glass <sjg@u-boot.org>:
From: Simon Glass <sjg@chromium.org>
Provide some developer documentation on the priority feature for global bootmeths.
Signed-off-by: Simon Glass <sjg@chromium.org> ---
doc/develop/bootstd/overview.rst | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-)
diff --git a/doc/develop/bootstd/overview.rst b/doc/develop/bootstd/overview.rst index ca76bad7bca..4c924e2eb21 100644 --- a/doc/develop/bootstd/overview.rst +++ b/doc/develop/bootstd/overview.rst @@ -133,7 +133,8 @@ which scans for available bootflows, optionally listing each find it finds (-l) and trying to boot it (-b).
When global bootmeths are available, these are typically checked before the -above bootdev scanning. +above bootdev scanning, but it is possible provide a priority to make them +run later, by setting the glob_prio field in the driver's bind() method.
Controlling ordering @@ -614,9 +615,9 @@ simply copied into the iterator. Either way, the `method_order` array it set up, along with `num_methods`.
Note that global bootmeths are always put at the end of the ordering. If any are -present, `cur_method` is set to the first one, so that global bootmeths are done -first. Once all have been used, these bootmeths are dropped from the iteration. -When there are no global bootmeths, `cur_method` is set to 0. +present, `cur_method` is set to the first one, so that global bootmeths are +processed first, so long as their priority allows it. Bootstd keeps track of +which global bootmeths have been used, to make sure they are only used once.
At this point the iterator is ready to use, with the first bootmeth selected. Most of the other fields are 0. This means that the current partition @@ -719,6 +720,23 @@ When it sees `BF_NO_MORE_DEVICES` it knows that there is nothing more it can do so it should immediately return. The caller of `iter_incr()` is responsible for updating the `err` field, based on the return value it sees.
+Global bootmeths can have a non-zero priority, which indicates where in the +iteration sequence they should run. Each time a new bootdev is produced by a +hunter, all of the global bootmeths are first checked to see if they should run +before this new bootdev. For example, if the bootdev was produced by a hunter +with priority BOOTDEVP_6_NET_BASE,
This would imply that the EFI bootmgr never runs if there is no huntable network device or network devices are not hunted. I hope this is not true. then a quick check is made for global
+bootmeths with that priority or less. If there are any, they run before the new +bootdev is processed. + +Assuming they are enabled and the iteration sequence runs right to the end, all +global bootmeths will be used. This is handled by a special case at the end of +iter_incr(), where it processes amy so-far-unused global bootmeths. + +Of course if a specific bootmeth ordering is provided, then this overrides the +default ordering. Global bootmeths must be listed at the end, reflecting their +hybrid nature (they are bootmeths but operate on the system as a whole, not on +a particular bootdev). + The above describes the iteration process at a high level. It is basically a very simple increment function with a checker called `bootflow_check()` that checks the result of each iteration generated, to determine whether it can

Hi Heinrich, On Mon, 29 Sept 2025 at 20:02, Heinrich Schuchardt <xypron.glpk@gmx.de> wrote:
Am 30. September 2025 02:51:31 MESZ schrieb Simon Glass <sjg@u-boot.org>:
From: Simon Glass <sjg@chromium.org>
Provide some developer documentation on the priority feature for global bootmeths.
Signed-off-by: Simon Glass <sjg@chromium.org> ---
doc/develop/bootstd/overview.rst | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-)
diff --git a/doc/develop/bootstd/overview.rst b/doc/develop/bootstd/overview.rst index ca76bad7bca..4c924e2eb21 100644 --- a/doc/develop/bootstd/overview.rst +++ b/doc/develop/bootstd/overview.rst @@ -133,7 +133,8 @@ which scans for available bootflows, optionally listing each find it finds (-l) and trying to boot it (-b).
When global bootmeths are available, these are typically checked before the -above bootdev scanning. +above bootdev scanning, but it is possible provide a priority to make them +run later, by setting the glob_prio field in the driver's bind() method.
Controlling ordering @@ -614,9 +615,9 @@ simply copied into the iterator. Either way, the `method_order` array it set up, along with `num_methods`.
Note that global bootmeths are always put at the end of the ordering. If any are -present, `cur_method` is set to the first one, so that global bootmeths are done -first. Once all have been used, these bootmeths are dropped from the iteration. -When there are no global bootmeths, `cur_method` is set to 0. +present, `cur_method` is set to the first one, so that global bootmeths are +processed first, so long as their priority allows it. Bootstd keeps track of +which global bootmeths have been used, to make sure they are only used once.
At this point the iterator is ready to use, with the first bootmeth selected. Most of the other fields are 0. This means that the current partition @@ -719,6 +720,23 @@ When it sees `BF_NO_MORE_DEVICES` it knows that there is nothing more it can do so it should immediately return. The caller of `iter_incr()` is responsible for updating the `err` field, based on the return value it sees.
+Global bootmeths can have a non-zero priority, which indicates where in the +iteration sequence they should run. Each time a new bootdev is produced by a +hunter, all of the global bootmeths are first checked to see if they should run +before this new bootdev. For example, if the bootdev was produced by a hunter +with priority BOOTDEVP_6_NET_BASE,
This would imply that the EFI bootmgr never runs if there is no huntable network device or network devices are not hunted.
I hope this is not true.
Any unused global boot bootmeths are run at the end, after all bootdevs are exhausted.
then a quick check is made for global
+bootmeths with that priority or less. If there are any, they run before the new +bootdev is processed. + +Assuming they are enabled and the iteration sequence runs right to the end, all +global bootmeths will be used. This is handled by a special case at the end of +iter_incr(), where it processes amy so-far-unused global bootmeths. + +Of course if a specific bootmeth ordering is provided, then this overrides the +default ordering. Global bootmeths must be listed at the end, reflecting their +hybrid nature (they are bootmeths but operate on the system as a whole, not on +a particular bootdev). + The above describes the iteration process at a high level. It is basically a very simple increment function with a checker called `bootflow_check()` that checks the result of each iteration generated, to determine whether it can
Regards, Simon

On 9/30/25 13:24, Simon Glass wrote:
Hi Heinrich,
On Mon, 29 Sept 2025 at 20:02, Heinrich Schuchardt <xypron.glpk@gmx.de> wrote:
Am 30. September 2025 02:51:31 MESZ schrieb Simon Glass <sjg@u-boot.org>:
From: Simon Glass <sjg@chromium.org>
Provide some developer documentation on the priority feature for global bootmeths.
Signed-off-by: Simon Glass <sjg@chromium.org> ---
doc/develop/bootstd/overview.rst | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-)
diff --git a/doc/develop/bootstd/overview.rst b/doc/develop/bootstd/overview.rst index ca76bad7bca..4c924e2eb21 100644 --- a/doc/develop/bootstd/overview.rst +++ b/doc/develop/bootstd/overview.rst @@ -133,7 +133,8 @@ which scans for available bootflows, optionally listing each find it finds (-l) and trying to boot it (-b).
When global bootmeths are available, these are typically checked before the -above bootdev scanning. +above bootdev scanning, but it is possible provide a priority to make them +run later, by setting the glob_prio field in the driver's bind() method.
Controlling ordering @@ -614,9 +615,9 @@ simply copied into the iterator. Either way, the `method_order` array it set up, along with `num_methods`.
Note that global bootmeths are always put at the end of the ordering. If any are -present, `cur_method` is set to the first one, so that global bootmeths are done -first. Once all have been used, these bootmeths are dropped from the iteration. -When there are no global bootmeths, `cur_method` is set to 0. +present, `cur_method` is set to the first one, so that global bootmeths are +processed first, so long as their priority allows it. Bootstd keeps track of +which global bootmeths have been used, to make sure they are only used once.
At this point the iterator is ready to use, with the first bootmeth selected. Most of the other fields are 0. This means that the current partition @@ -719,6 +720,23 @@ When it sees `BF_NO_MORE_DEVICES` it knows that there is nothing more it can do so it should immediately return. The caller of `iter_incr()` is responsible for updating the `err` field, based on the return value it sees.
+Global bootmeths can have a non-zero priority, which indicates where in the +iteration sequence they should run. Each time a new bootdev is produced by a +hunter, all of the global bootmeths are first checked to see if they should run +before this new bootdev. For example, if the bootdev was produced by a hunter +with priority BOOTDEVP_6_NET_BASE,
This would imply that the EFI bootmgr never runs if there is no huntable network device or network devices are not hunted.
I hope this is not true.
Any unused global boot bootmeths are run at the end, after all bootdevs are exhausted.
What happens if I have 3 bootmeths: local-1, prio 3 global-2, prio 4 local-3, prio 5 global should still be the 2nd bootmeth to be executed. Best regards Heinrich
then a quick check is made for global
+bootmeths with that priority or less. If there are any, they run before the new +bootdev is processed. + +Assuming they are enabled and the iteration sequence runs right to the end, all +global bootmeths will be used. This is handled by a special case at the end of +iter_incr(), where it processes amy so-far-unused global bootmeths. + +Of course if a specific bootmeth ordering is provided, then this overrides the +default ordering. Global bootmeths must be listed at the end, reflecting their +hybrid nature (they are bootmeths but operate on the system as a whole, not on +a particular bootdev). + The above describes the iteration process at a high level. It is basically a very simple increment function with a checker called `bootflow_check()` that checks the result of each iteration generated, to determine whether it can
Regards, Simon

Hi Heinrich, On Tue, 30 Sept 2025 at 05:35, Heinrich Schuchardt <xypron.glpk@gmx.de> wrote:
On 9/30/25 13:24, Simon Glass wrote:
Hi Heinrich,
On Mon, 29 Sept 2025 at 20:02, Heinrich Schuchardt <xypron.glpk@gmx.de> wrote:
Am 30. September 2025 02:51:31 MESZ schrieb Simon Glass <sjg@u-boot.org>:
From: Simon Glass <sjg@chromium.org>
Provide some developer documentation on the priority feature for global bootmeths.
Signed-off-by: Simon Glass <sjg@chromium.org> ---
doc/develop/bootstd/overview.rst | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-)
diff --git a/doc/develop/bootstd/overview.rst b/doc/develop/bootstd/overview.rst index ca76bad7bca..4c924e2eb21 100644 --- a/doc/develop/bootstd/overview.rst +++ b/doc/develop/bootstd/overview.rst @@ -133,7 +133,8 @@ which scans for available bootflows, optionally listing each find it finds (-l) and trying to boot it (-b).
When global bootmeths are available, these are typically checked before the -above bootdev scanning. +above bootdev scanning, but it is possible provide a priority to make them +run later, by setting the glob_prio field in the driver's bind() method.
Controlling ordering @@ -614,9 +615,9 @@ simply copied into the iterator. Either way, the `method_order` array it set up, along with `num_methods`.
Note that global bootmeths are always put at the end of the ordering. If any are -present, `cur_method` is set to the first one, so that global bootmeths are done -first. Once all have been used, these bootmeths are dropped from the iteration. -When there are no global bootmeths, `cur_method` is set to 0. +present, `cur_method` is set to the first one, so that global bootmeths are +processed first, so long as their priority allows it. Bootstd keeps track of +which global bootmeths have been used, to make sure they are only used once.
At this point the iterator is ready to use, with the first bootmeth selected. Most of the other fields are 0. This means that the current partition @@ -719,6 +720,23 @@ When it sees `BF_NO_MORE_DEVICES` it knows that there is nothing more it can do so it should immediately return. The caller of `iter_incr()` is responsible for updating the `err` field, based on the return value it sees.
+Global bootmeths can have a non-zero priority, which indicates where in the +iteration sequence they should run. Each time a new bootdev is produced by a +hunter, all of the global bootmeths are first checked to see if they should run +before this new bootdev. For example, if the bootdev was produced by a hunter +with priority BOOTDEVP_6_NET_BASE,
This would imply that the EFI bootmgr never runs if there is no huntable network device or network devices are not hunted.
I hope this is not true.
Any unused global boot bootmeths are run at the end, after all bootdevs are exhausted.
What happens if I have 3 bootmeths:
local-1, prio 3 global-2, prio 4 local-3, prio 5
global should still be the 2nd bootmeth to be executed.
Only global bootmeths have a priority. It is meaningless for normal bootmeths since they are used repeatedly on each bootdev. So the picture might be: bootdev priority 3: local-1, local-3 global-2, prio 4 bootdev priority 5: local-1, local-3 This will work as expected, since when the priority-5 hunter / label is about to be used, the global-2 bootmeth will be used first. Regards, SImon
then a quick check is made for global
+bootmeths with that priority or less. If there are any, they run before the new +bootdev is processed. + +Assuming they are enabled and the iteration sequence runs right to the end, all +global bootmeths will be used. This is handled by a special case at the end of +iter_incr(), where it processes amy so-far-unused global bootmeths. + +Of course if a specific bootmeth ordering is provided, then this overrides the +default ordering. Global bootmeths must be listed at the end, reflecting their +hybrid nature (they are bootmeths but operate on the system as a whole, not on +a particular bootdev). + The above describes the iteration process at a high level. It is basically a very simple increment function with a checker called `bootflow_check()` that checks the result of each iteration generated, to determine whether it can
participants (3)
-
Heinrich Schuchardt
-
Simon Glass
-
Simon Glass