mail archive of the barebox mailing list
 help / color / mirror / Atom feed
From: Sascha Hauer <s.hauer@pengutronix.de>
To: BAREBOX <barebox@lists.infradead.org>
Subject: [PATCH 3/5] boot: hand overrides on to nested boots and the bootm command
Date: Fri, 30 Oct 2026 15:13:30 +0100	[thread overview]
Message-ID: <20261030-devboot-v1-3-ab9dd3a92c66@pengutronix.de> (raw)
In-Reply-To: <20261030-devboot-v1-0-ab9dd3a92c66@pengutronix.de>

Overrides are stored in the boot entry they are given for and only
applied by bootm_entry(). They get lost when a boot entry boots something
else, like a boot script calling boot, or calling the bootm command.

Keep track of the overrides of the boot entry being booted. Hand them on
to boot entries booted from there, with the ones of the outer entry
taking precedence, and apply them in the bootm command. bootchooser no
longer needs to merge the overrides into its targets itself.

This is done in do_bootm() and not in bootm_boot(), which the Android
image handler calls while a bootm is already running, so it would apply
the overrides a second time.

Assisted-by: Claude:claude-opus-5-5
---
 Documentation/user/devboot.rst |  4 ++
 commands/bootm.c               |  3 +-
 common/boot.c                  | 44 ++++++++++++--------
 common/bootchooser.c           | 13 ++----
 common/bootm.c                 | 22 +++++++++-
 include/boot.h                 |  8 ++++
 include/bootchooser.h          |  2 +-
 include/bootm.h                |  3 ++
 test/py/test_boot_overrides.py | 92 ++++++++++++++++++++++++++++++++++++++++++
 9 files changed, 163 insertions(+), 28 deletions(-)

diff --git a/Documentation/user/devboot.rst b/Documentation/user/devboot.rst
index 62cc357c3a..e05541f05b 100644
--- a/Documentation/user/devboot.rst
+++ b/Documentation/user/devboot.rst
@@ -38,6 +38,10 @@ Overrides are applied after the boot entry has been loaded, so the original
 entry still selects the boot handler, kernel arguments, etc. Only the
 specified components are replaced.
 
+Overrides also apply to whatever a boot entry boots itself, like a boot script
+calling ``boot`` or ``bootm``, even if that is passed an image explicitly. If
+a nested ``boot`` has overrides of its own, the outer ones take precedence.
+
 Override rules
 ^^^^^^^^^^^^^^
 
diff --git a/commands/bootm.c b/commands/bootm.c
index 44abb953f6..0d6a95edae 100644
--- a/commands/bootm.c
+++ b/commands/bootm.c
@@ -16,6 +16,7 @@
 #include <fcntl.h>
 #include <fs.h>
 #include <errno.h>
+#include <boot.h>
 #include <bootm.h>
 #include <rtc.h>
 #include <init.h>
@@ -94,7 +95,7 @@ static int do_bootm(int argc, char *argv[])
 		goto err_out;
 	}
 
-	ret = bootm_boot(&data);
+	ret = bootm_boot_overrides(&data, boot_get_active_overrides());
 	if (ret) {
 		printf("handler failed with: %pe\n", ERR_PTR(ret));
 		goto err_out;
diff --git a/common/boot.c b/common/boot.c
index b993cabf65..271cfd3a29 100644
--- a/common/boot.c
+++ b/common/boot.c
@@ -187,8 +187,23 @@ BAREBOX_MAGICVAR(global.boot.watchdog_timeout,
 BAREBOX_MAGICVAR(global.user,
 		"Username used by the default environment to construct network boot paths");
 
+/* The overrides of the boot entry currently being booted */
+static const struct bootm_overrides *boot_active_overrides;
+
+/**
+ * boot_get_active_overrides - get the overrides of the current boot entry
+ *
+ * Return: the overrides of the boot entry currently being booted, NULL if
+ * there is none
+ */
+const struct bootm_overrides *boot_get_active_overrides(void)
+{
+	return boot_active_overrides;
+}
+
 int boot_entry(struct bootentry *be, int verbose, int dryrun)
 {
+	const struct bootm_overrides *outer = boot_active_overrides;
 	struct globalvar_scope *scope;
 	int ret;
 
@@ -206,7 +221,19 @@ int boot_entry(struct bootentry *be, int verbose, int dryrun)
 
 	scope = boot_globalvar_scope_push();
 
+	/*
+	 * A boot entry may boot other entries, for example a boot script
+	 * calling boot. Hand the overrides on to them, the ones of the outer
+	 * entry take precedence.
+	 */
+	if (outer)
+		bootm_merge_overrides(&be->overrides, outer);
+	boot_active_overrides = &be->overrides;
+
 	ret = be->boot(be, verbose, dryrun);
+
+	boot_active_overrides = outer;
+
 	if (ret > 0)
 		pr_err("Booting entry '%s' failed: exit status %d\n", be->title, ret);
 	else if (ret && ret != -ENOMEDIUM)
@@ -642,20 +669,5 @@ BAREBOX_MAGICVAR(global.boot.default, "default boot order");
  */
 int bootm_entry(struct bootentry *be, const struct bootm_data *bootm_data)
 {
-	struct image_data *data;
-	int ret;
-
-	data = bootm_boot_prep(bootm_data);
-	if (IS_ERR(data))
-		return PTR_ERR(data);
-
-	ret = bootm_apply_overrides(data, &be->overrides);
-	if (ret)
-		goto out;
-
-	ret = bootm_boot_handler(data);
-
-out:
-	bootm_boot_cleanup(data);
-	return ret;
+	return bootm_boot_overrides(bootm_data, &be->overrides);
 }
diff --git a/common/bootchooser.c b/common/bootchooser.c
index 2049425ab3..c8f72871f3 100644
--- a/common/bootchooser.c
+++ b/common/bootchooser.c
@@ -840,8 +840,7 @@ void bootchooser_lock_attempts(bool locked)
 	attempts_locked = locked;
 }
 
-static int bootchooser_boot_one(struct bootchooser *bc, int *tryagain,
-				struct bootentry *parent_entry)
+static int bootchooser_boot_one(struct bootchooser *bc, int *tryagain)
 {
 	char *system;
 	struct bootentries *entries;
@@ -878,10 +877,6 @@ static int bootchooser_boot_one(struct bootchooser *bc, int *tryagain,
 	ret = -ENOENT;
 
 	bootentries_for_each_entry(entries, entry) {
-		if (parent_entry)
-			bootm_merge_overrides(&entry->overrides,
-					      &parent_entry->overrides);
-
 		ret = boot_entry(entry, bc->verbose, bc->dryrun);
 		if (!ret) {
 			*tryagain = 0;
@@ -898,12 +893,12 @@ static int bootchooser_boot_one(struct bootchooser *bc, int *tryagain,
 	return ret;
 }
 
-int bootchooser_boot(struct bootchooser *bc, struct bootentry *entry)
+int bootchooser_boot(struct bootchooser *bc)
 {
 	int ret, tryagain;
 
 	do {
-		ret = bootchooser_boot_one(bc, &tryagain, entry);
+		ret = bootchooser_boot_one(bc, &tryagain);
 
 		if (!retry)
 			break;
@@ -924,7 +919,7 @@ int bootchooser_entry_boot(struct bootentry *entry, int verbose, int dryrun)
 	bc->verbose = verbose;
 	bc->dryrun = dryrun;
 
-	ret = bootchooser_boot(bc, entry);
+	ret = bootchooser_boot(bc);
 
 	bootchooser_put(bc);
 
diff --git a/common/bootm.c b/common/bootm.c
index 2ad8c34bb4..2ed377da18 100644
--- a/common/bootm.c
+++ b/common/bootm.c
@@ -785,7 +785,15 @@ void bootm_boot_cleanup(struct image_data *data)
 /*
  * bootm_boot - Boot an application image described by bootm_data
  */
-int bootm_boot(const struct bootm_data *bootm_data)
+/**
+ * bootm_boot_overrides - boot with overrides applied
+ * @bootm_data: bootm data
+ * @overrides: overrides to apply, may be NULL
+ *
+ * Return: 0 on success, or negative error code otherwise
+ */
+int bootm_boot_overrides(const struct bootm_data *bootm_data,
+			 const struct bootm_overrides *overrides)
 {
 	struct image_data *data;
 	int ret;
@@ -794,12 +802,24 @@ int bootm_boot(const struct bootm_data *bootm_data)
 	if (IS_ERR(data))
 		return PTR_ERR(data);
 
+	if (overrides) {
+		ret = bootm_apply_overrides(data, overrides);
+		if (ret)
+			goto out;
+	}
+
 	ret = bootm_boot_handler(data);
 
+out:
 	bootm_boot_cleanup(data);
 	return ret;
 }
 
+int bootm_boot(const struct bootm_data *bootm_data)
+{
+	return bootm_boot_overrides(bootm_data, NULL);
+}
+
 bool bootm_efi_check_image(struct image_handler *handler,
 			   struct image_data *data,
 			   enum filetype detected_filetype)
diff --git a/include/boot.h b/include/boot.h
index bb99dacc85..72cc769d59 100644
--- a/include/boot.h
+++ b/include/boot.h
@@ -56,6 +56,14 @@ int bootentry_create_from_name(struct bootentries *bootentries,
 void bootsources_menu(struct bootentries *bootentries, unsigned default_entry, int timeout);
 void bootsources_list(struct bootentries *bootentries);
 int boot_entry(struct bootentry *be, int verbose, int dryrun);
+#ifdef CONFIG_BOOT
+const struct bootm_overrides *boot_get_active_overrides(void);
+#else
+static inline const struct bootm_overrides *boot_get_active_overrides(void)
+{
+	return NULL;
+}
+#endif
 
 struct bootm_data;
 int bootm_entry(struct bootentry *be, const struct bootm_data *data);
diff --git a/include/bootchooser.h b/include/bootchooser.h
index da3b974ef6..36cbd0dd0a 100644
--- a/include/bootchooser.h
+++ b/include/bootchooser.h
@@ -14,7 +14,7 @@ int bootchooser_put(struct bootchooser *bootchooser);
 
 void bootchooser_info(struct bootchooser *bootchooser);
 
-int bootchooser_boot(struct bootchooser *bc, struct bootentry *entry);
+int bootchooser_boot(struct bootchooser *bc);
 
 struct bootchooser_target *bootchooser_get_last_chosen(struct bootchooser *bootchooser);
 void bootchooser_lock_attempts(bool locked);
diff --git a/include/bootm.h b/include/bootm.h
index 06eb90c5ed..d63e9fc4f6 100644
--- a/include/bootm.h
+++ b/include/bootm.h
@@ -75,6 +75,9 @@ struct bootm_data {
 int bootm_image_name_and_part(const char *name, char **filename, char **part);
 
 int bootm_boot(const struct bootm_data *data);
+struct bootm_overrides;
+int bootm_boot_overrides(const struct bootm_data *data,
+			 const struct bootm_overrides *overrides);
 
 struct image_data *bootm_boot_prep(const struct bootm_data *bootm_data);
 int bootm_open_os_compressed(struct image_data *data);
diff --git a/test/py/test_boot_overrides.py b/test/py/test_boot_overrides.py
new file mode 100644
index 0000000000..674b3a2116
--- /dev/null
+++ b/test/py/test_boot_overrides.py
@@ -0,0 +1,92 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+import pytest
+from .helper import skip_disabled
+from .test_boot_globalvar_scope import write_bootscript
+
+OVERRIDE = "/tmp/override-image"
+BASE = "/tmp/base-image"
+OUTER = "/tmp/outer-image"
+
+
+def make_image(barebox, path):
+    # bootm wants a full page of a known image type. There's no handler for
+    # ELF images here, but it only fails after reporting the image it boots
+    barebox.run_check(f"echo -o {path} image")
+    for _ in range(6):
+        barebox.run_check(f"cat -o {path}.tmp {path} {path}")
+        barebox.run_check(f"cat -o {path} {path}.tmp {path}.tmp")
+    barebox.run_check(f"rm {path}.tmp")
+    barebox.run_check(f"mw -b -d {path} 0 0x7f 0x45 0x4c 0x46")
+
+
+def booted(stdout):
+    return [line for line in stdout if line.startswith("Loading ")]
+
+
+@pytest.fixture(scope="function")
+def override_env(barebox, barebox_config):
+    skip_disabled(barebox_config, "CONFIG_BOOT_OVERRIDE", "CONFIG_CMD_ECHO",
+                  "CONFIG_CMD_CAT", "CONFIG_CMD_MW")
+
+    make_image(barebox, BASE)
+    make_image(barebox, OVERRIDE)
+    make_image(barebox, OUTER)
+
+    write_bootscript(barebox, "ovr-target", [
+        f"global.bootm.image={BASE}",
+    ])
+    write_bootscript(barebox, "ovr-nested", [
+        "boot ovr-target",
+    ])
+    write_bootscript(barebox, "ovr-nested-override", [
+        f"boot -o bootm.image={OVERRIDE} ovr-target",
+    ])
+    write_bootscript(barebox, "ovr-bootm", [
+        f"bootm {BASE}",
+    ])
+
+    yield
+
+    barebox.run(f"rm {BASE} {OVERRIDE} {OUTER}")
+    barebox.run("rm /env/boot/ovr-target /env/boot/ovr-nested "
+                "/env/boot/ovr-nested-override /env/boot/ovr-bootm")
+
+
+@pytest.mark.usefixtures("override_env")
+def test_override_entry(barebox):
+    stdout, _, _ = barebox.run("boot ovr-target")
+    assert any(BASE in line for line in booted(stdout))
+
+    stdout, _, _ = barebox.run(f"boot -o bootm.image={OVERRIDE} ovr-target")
+    assert any(OVERRIDE in line for line in booted(stdout))
+
+
+@pytest.mark.usefixtures("override_env")
+def test_override_nested_boot(barebox):
+    stdout, _, _ = barebox.run(f"boot -o bootm.image={OVERRIDE} ovr-nested")
+    assert any(OVERRIDE in line for line in booted(stdout))
+    assert not any(BASE in line for line in booted(stdout))
+
+
+@pytest.mark.usefixtures("override_env")
+def test_override_outer_takes_precedence(barebox):
+    stdout, _, _ = barebox.run(f"boot -o bootm.image={OUTER} ovr-nested-override")
+    assert any(OUTER in line for line in booted(stdout))
+    assert not any(OVERRIDE in line for line in booted(stdout))
+
+
+@pytest.mark.usefixtures("override_env")
+def test_override_bootm_command(barebox):
+    stdout, _, _ = barebox.run(f"boot -o bootm.image={OVERRIDE} ovr-bootm")
+    assert any(OVERRIDE in line for line in booted(stdout))
+    assert not any(BASE in line for line in booted(stdout))
+
+
+@pytest.mark.usefixtures("override_env")
+def test_override_ends_with_boot(barebox):
+    # the override must not stick to a later bootm outside of the boot command
+    barebox.run(f"boot -o bootm.image={OVERRIDE} ovr-target")
+
+    stdout, _, _ = barebox.run(f"bootm {BASE}")
+    assert any(BASE in line for line in booted(stdout))

-- 
2.47.3




  parent reply	other threads:[~2026-10-30 14:54 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-30 14:13 [PATCH 0/5] boot: implement devboot in C Sascha Hauer
2026-10-30 14:13 ` [PATCH 1/5] boot: report the exit status of failing boot scripts Sascha Hauer
2026-10-30 14:13 ` [PATCH 2/5] hush: export source_script() Sascha Hauer
2026-10-30 14:13 ` Sascha Hauer [this message]
2026-10-30 14:13 ` [PATCH 4/5] boot: implement devboot in C Sascha Hauer
2026-10-30 14:13 ` [PATCH 5/5] sandbox: defconfig: enable boot overrides and devboot Sascha Hauer

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261030-devboot-v1-3-ab9dd3a92c66@pengutronix.de \
    --to=s.hauer@pengutronix.de \
    --cc=barebox@lists.infradead.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox