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 ")]
+
+
[email protected](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")
+
+
[email protected]("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))
+
+
[email protected]("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))
+
+
[email protected]("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))
+
+
[email protected]("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))
+
+
[email protected]("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