From 396c9b59644081a05a8b078eadc2c4aead37160d Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 15 Oct 2025 16:44:04 +0100 Subject: boot: Try all bootmeths on the final partition At present, normally when one bootmeth fails on a partition, we move on and try the next bootmeth. However, this was not the case for the final partition due to a bug. Rework the logic so that all partitions are treated the same. Signed-off-by: Simon Glass --- boot/bootflow.c | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) (limited to 'boot') diff --git a/boot/bootflow.c b/boot/bootflow.c index 7ed076c898f..deb5f42ba65 100644 --- a/boot/bootflow.c +++ b/boot/bootflow.c @@ -221,27 +221,25 @@ static int iter_incr(struct bootflow_iter *iter) if (iter->err == BF_NO_MORE_DEVICES) return BF_NO_MORE_DEVICES; - if (iter->err != BF_NO_MORE_PARTS) { - /* Get the next boothmethod */ - if (++iter->cur_method < iter->num_methods) { - iter->method = iter->method_order[iter->cur_method]; - return 0; - } + /* Get the next boothmethod */ + if (++iter->cur_method < iter->num_methods) { + iter->method = iter->method_order[iter->cur_method]; + return 0; + } + + /* + * If we have finished scanning the global bootmeths, start the + * normal bootdev scan + */ + if (IS_ENABLED(CONFIG_BOOTMETH_GLOBAL) && global) { + iter->num_methods = iter->first_glob_method; + iter->doing_global = false; /* - * If we have finished scanning the global bootmeths, start the - * normal bootdev scan + * Don't move to the next dev as we haven't tried this + * one yet! */ - if (IS_ENABLED(CONFIG_BOOTMETH_GLOBAL) && global) { - iter->num_methods = iter->first_glob_method; - iter->doing_global = false; - - /* - * Don't move to the next dev as we haven't tried this - * one yet! - */ - inc_dev = false; - } + inc_dev = false; } if (iter->flags & BOOTFLOWIF_SINGLE_PARTITION) -- cgit v1.2.3 From eca985905d7956ca69e1abfe9ef1d3eb5c64c0a9 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 15 Oct 2025 16:44:06 +0100 Subject: boot: Update first_glob_method when dropping a bootmeth 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 --- boot/bootflow.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'boot') diff --git a/boot/bootflow.c b/boot/bootflow.c index deb5f42ba65..62634a59a94 100644 --- a/boot/bootflow.c +++ b/boot/bootflow.c @@ -109,11 +109,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; } -- cgit v1.2.3 From 0fe6de0dc5b137a2def3a8cc0baa2fb73a3f8541 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 15 Oct 2025 16:44:07 +0100 Subject: boot: Add a flag for whether there are global bootmeths 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 --- boot/bootmeth-uclass.c | 1 + 1 file changed, 1 insertion(+) (limited to 'boot') diff --git a/boot/bootmeth-uclass.c b/boot/bootmeth-uclass.c index bb2dd8447cf..a0aa6336c9b 100644 --- a/boot/bootmeth-uclass.c +++ b/boot/bootmeth-uclass.c @@ -209,6 +209,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; -- cgit v1.2.3 From bef963cb751049cacc86f2754452efadd03ae2f0 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 15 Oct 2025 16:44:08 +0100 Subject: boot: Keep track of which bootmeths have been used 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 --- boot/bootflow.c | 13 +++++++++++++ boot/bootmeth-uclass.c | 10 ++++++++++ 2 files changed, 23 insertions(+) (limited to 'boot') diff --git a/boot/bootflow.c b/boot/bootflow.c index 62634a59a94..2e4d1a345cd 100644 --- a/boot/bootflow.c +++ b/boot/bootflow.c @@ -17,6 +17,10 @@ #include #include +/* 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, @@ -433,6 +437,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); @@ -460,6 +468,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 a0aa6336c9b..a9709465f6e 100644 --- a/boot/bootmeth-uclass.c +++ b/boot/bootmeth-uclass.c @@ -211,6 +211,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; -- cgit v1.2.3 From 8e31093dbce4fb233c6f14520149293b92981b50 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 15 Oct 2025 16:44:09 +0100 Subject: boot: Support rescanning the global bootmeths 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 --- boot/bootflow.c | 90 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 88 insertions(+), 2 deletions(-) (limited to 'boot') diff --git a/boot/bootflow.c b/boot/bootflow.c index 2e4d1a345cd..73deba24d30 100644 --- a/boot/bootflow.c +++ b/boot/bootflow.c @@ -187,19 +187,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 @@ -245,6 +307,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! @@ -347,7 +433,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 */ -- cgit v1.2.3 From e52053c93c128284ccfae11001d7b211bb081aeb Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 15 Oct 2025 16:44:10 +0100 Subject: boot: Only run global bootmeths once each 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 --- boot/bootflow.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'boot') diff --git a/boot/bootflow.c b/boot/bootflow.c index 73deba24d30..ca1fe741bab 100644 --- a/boot/bootflow.c +++ b/boot/bootflow.c @@ -294,7 +294,13 @@ static int iter_incr(struct bootflow_iter *iter) return BF_NO_MORE_DEVICES; /* 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]; return 0; } -- cgit v1.2.3 From 4ce78089b2a615eab466347e8996fbd54a876234 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 15 Oct 2025 16:44:11 +0100 Subject: boot: Implement a priority for global bootmeths 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 --- boot/bootflow.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'boot') diff --git a/boot/bootflow.c b/boot/bootflow.c index ca1fe741bab..1a4cd0e28e8 100644 --- a/boot/bootflow.c +++ b/boot/bootflow.c @@ -191,17 +191,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; -- cgit v1.2.3 From eff1dca96330269c8281b17d00f5d2d0e62bd26e Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 15 Oct 2025 16:44:12 +0100 Subject: boot: Don't change the method count after global bootmeths 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 --- boot/bootflow.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) (limited to 'boot') diff --git a/boot/bootflow.c b/boot/bootflow.c index 1a4cd0e28e8..38b8af916b2 100644 --- a/boot/bootflow.c +++ b/boot/bootflow.c @@ -304,9 +304,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]; return 0; @@ -317,7 +328,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; /* -- cgit v1.2.3 From 060ce66b83e5ed19180103c26e525d85c2a2aa8b Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 15 Oct 2025 16:44:13 +0100 Subject: boot: Run global bootmeths after all bootdevs are exhausted 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. Update the documentation to match the new behaviour of global bootmeths. Signed-off-by: Simon Glass --- boot/bootflow.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'boot') diff --git a/boot/bootflow.c b/boot/bootflow.c index 38b8af916b2..d8a4a81a838 100644 --- a/boot/bootflow.c +++ b/boot/bootflow.c @@ -354,6 +354,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! @@ -459,6 +471,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) return log_msg_ret("incr", BF_NO_MORE_DEVICES); -- cgit v1.2.3 From 6a56d10fdcf1309d2070b62dc15e80a047da971b Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 15 Oct 2025 16:44:14 +0100 Subject: boot: Run the EFI bootmgr just before network devices 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. Note that if there are no hunted network devices hunted, then it will run at the end, after all bootdevs are exhausted. In other words, it will always run. Signed-off-by: Simon Glass --- boot/bootmeth_efi_mgr.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'boot') diff --git a/boot/bootmeth_efi_mgr.c b/boot/bootmeth_efi_mgr.c index 42b8863815e..05fc35d01a9 100644 --- a/boot/bootmeth_efi_mgr.c +++ b/boot/bootmeth_efi_mgr.c @@ -99,6 +99,15 @@ static int bootmeth_efi_mgr_bind(struct udevice *dev) plat->desc = "EFI bootmgr flow"; plat->flags = BOOTMETHF_GLOBAL; + /* + * bootmgr scans all available devices which can take a while, + * especially for network devices. So choose the priority so that it + * comes just before the 'very slow' devices. This allows systems which + * don't rely on bootmgr to boot quickly, while allowing bootmgr to run + * on systems which need it. + */ + plat->glob_prio = BOOTDEVP_6_NET_BASE; + return 0; } -- cgit v1.2.3