[PATCH 11/16] bootm: implement plain and FIT bootm.image override

Ahmad Fatoum
2026-03-12T15:03:22Z

Add support for overriding the boot image via bootm.image when using
either plain files or FIT images. The override behavior depends on the
file types involved:

- If the override is not a FIT image, it replaces only the OS loadable,
regardless of whether the original was a FIT image or not.

- If the override is a FIT image, it replaces the current OS, whether
it's a FIT or not.

This allows developers to use devboot scripts to redirect FIT image
loading to alternate locations (e.g., network) while preserving the
configuration selection from the original boot entry.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
commands/boot.c | 2 +-
common/bootm-fit.c | 11 +++-
common/bootm-overrides.c | 119 +++++++++++++++++++++++++++++++++++++-
common/bootm.c | 4 +-
include/bootm-fit.h | 4 +-
include/bootm-overrides.h | 3 +
include/bootm.h | 2 +
7 files changed, 134 insertions(+), 11 deletions(-)

diff --git a/commands/boot.c b/commands/boot.c
index 555667ee098c..04bb9eb9186f 100644
--- a/commands/boot.c
+++ b/commands/boot.c
@@ -44,7 +44,7 @@ static int boot_add_override(struct bootm_overrides *overrides, char *var)
if (!strcmp(var, "bootm.image")) {
if (isempty(val))
return -EINVAL;
- return -ENOSYS;
+ overrides->os_file = val;
} else if (!strcmp(var, "bootm.oftree")) {
overrides->oftree_file = val;
} else if (!strcmp(var, "bootm.initrd")) {
diff --git a/common/bootm-fit.c b/common/bootm-fit.c
index 5b110ad2f19e..995bd7181223 100644
--- a/common/bootm-fit.c
+++ b/common/bootm-fit.c
@@ -4,6 +4,7 @@
#include <image-fit.h>
#include <bootm-fit.h>
#include <memory.h>
+#include <string.h>
#include <zero_page.h>
#include <filetype.h>
#include <fs.h>
@@ -143,7 +144,7 @@ static enum filetype bootm_fit_update_os_header(struct image_data *data)
return os_type;
}

-int bootm_open_fit(struct image_data *data)
+int bootm_open_fit(struct image_data *data, bool override)
{
struct fit_handle *fit;
void *fit_config;
@@ -164,8 +165,12 @@ int bootm_open_fit(struct image_data *data)
}

loadable_from_fit_os(data, fit, fit_config);
- loadable_from_fit_initrd(data, fit, fit_config);
- loadable_from_fit_oftree(data, fit, fit_config);
+ if (override)
+ data->is_override.os = true;
+ if (loadable_from_fit_initrd(data, fit, fit_config) && override)
+ data->is_override.initrd = true;
+ if (loadable_from_fit_oftree(data, fit, fit_config) && override)
+ data->is_override.oftree = true;
loadable_from_fit_tee(data, fit, fit_config);

data->kernel_type = bootm_fit_update_os_header(data);
diff --git a/common/bootm-overrides.c b/common/bootm-overrides.c
index 59ca90a58684..8c71ceb5f80d 100644
--- a/common/bootm-overrides.c
+++ b/common/bootm-overrides.c
@@ -2,14 +2,118 @@

#include <bootm.h>
#include <bootm-overrides.h>
+#include <bootm-fit.h>
+#include <libfile.h>
+#include <linux/pagemap.h>
+
+/**
+ * bootm_apply_image_override - apply bootm.image override
+ * @data: image data context
+ * @override: override path (may include @config suffix for FIT)
+ *
+ * Applies a bootm.image override according to these rules:
+ * - If the override is not a FIT image, it replaces only data->os.
+ * - If the override is a FIT image, it is re-opened via bootm_open_fit()
+ * using the default configuration or the @config suffix if given.
+ *
+ * Return: 0 on success, negative errno on failure
+ */
+static int bootm_apply_image_override(struct image_data *data,
+ const char *override)
+{
+ char *override_file, *override_part;
+ void *override_header = NULL;
+ int ret;
+
+ ret = bootm_image_name_and_part(override, &override_file, &override_part);
+ if (ret)
+ return ret;
+
+ /* Read header to detect file type */
+ ret = file_read_and_detect_boot_image_type(override_file, &override_header);
+ if (ret < 0) {
+ free(override_file);
+ return ret;
+ }
+
+ data->image_type = ret;
+
+ /*
+ * bootm_image_name_and_part() returns os_part pointing inside the
+ * os_file allocation, so we must NULLify os_part before freeing
+ * os_file to avoid a use-after-free.
+ */
+ data->os_part = NULL;
+ free(data->os_file);
+ data->os_file = override_file;
+ if (override_part)
+ data->os_part = override_part;
+
+ /* Update the detected type for the handler */
+ free(data->os_header);
+ data->os_header = override_header;
+
+ switch (data->image_type) {
+ case filetype_fit:
+ /* Restore, so bootm_open_fit looks for them in the new FIT */
+ data->os_address = data->os_address_hint;
+ data->os_entry = data->os_entry_hint;
+
+ /* Re-open configuration and collect loadables */
+ return bootm_open_fit(data, true);
+ default:
+ loadable_release(&data->os);
+ data->os = loadable_from_file(override_file, LOADABLE_KERNEL);
+ if (IS_ERR(data->os))
+ return PTR_ERR(data->os);
+
+ data->kernel_type = data->image_type;
+ data->is_override.os = true;
+
+ if (file_is_compressed_file(data->kernel_type))
+ return bootm_open_os_compressed(data);
+
+ return 0;
+ }
+}
+
+static void report_active_override(const char *type_str, struct loadable *l)
+{
+ struct loadable *lc;
+ char *buf = NULL;
+
+ if (!l) {
+ pr_notice("[bootm override active] removed %s\n", type_str);
+ return;
+ }
+
+ buf = xrasprintf(buf, "[bootm override active] replacement %s: %s",
+ type_str, l->name);
+
+ list_for_each_entry(lc, &l->chained_loadables, list)
+ buf = xrasprintf(buf, ", %s", lc->name);
+
+ pr_notice("%s\n", buf);
+ free(buf);
+}
+
+static void report_active_overrides(const struct image_data *data)
+{
+ if (data->is_override.os)
+ report_active_override("OS", data->os);
+ if (data->is_override.initrd)
+ report_active_override("initrd", data->initrd);
+ if (data->is_override.oftree)
+ report_active_override("FDT", data->oftree);
+}

/**
* bootm_apply_overrides - apply overrides
* @data: image data context
* @overrides: overrides to apply
*
- * Applies bootm.initrd and bootm.oftree overrides by translating
- * them into file-based loadables.
+ * Applies bootm.image, bootm.initrd, and bootm.oftree overrides by translating
+ * them into file-based loadables or FIT image replacements.
*
* Context: Called during boot preparation
* Return: 0 on success, negative error code otherwise
@@ -17,9 +121,17 @@
int bootm_apply_overrides(struct image_data *data,
const struct bootm_overrides *overrides)
{
+ int ret;
+
if (bootm_signed_images_are_forced())
return 0;

+ if (nonempty(overrides->os_file)) {
+ ret = bootm_apply_image_override(data, overrides->os_file);
+ if (ret)
+ return ret;
+ }
+
if (IS_ENABLED(CONFIG_BOOTM_INITRD) && overrides->initrd_file) {
/* loadables_from_files() will set data->initrd on empty initrd_file */
int ret = loadables_from_files(&data->initrd, overrides->initrd_file, ":",
@@ -42,6 +154,7 @@ int bootm_apply_overrides(struct image_data *data,
data->is_override.oftree = true;
}

+ report_active_overrides(data);
+
return 0;
}
-
diff --git a/common/bootm.c b/common/bootm.c
index ddc656fb34c8..ed79c20c0972 100644
--- a/common/bootm.c
+++ b/common/bootm.c
@@ -400,7 +400,7 @@ static void bootm_print_info(struct image_data *data)
printf("OS image not yet relocated\n");
}

-static int bootm_image_name_and_part(const char *name, char **filename, char **part)
+int bootm_image_name_and_part(const char *name, char **filename, char **part)
{
char *at, *ret;

@@ -570,7 +570,7 @@ struct image_data *bootm_boot_prep(const struct bootm_data *bootm_data)

switch (data->image_type) {
case filetype_fit:
- ret = bootm_open_fit(data);
+ ret = bootm_open_fit(data, false);
break;
case filetype_uimage:
ret = bootm_open_uimage(data);
diff --git a/include/bootm-fit.h b/include/bootm-fit.h
index 05f4f5acfe9a..972d9e4fac8e 100644
--- a/include/bootm-fit.h
+++ b/include/bootm-fit.h
@@ -10,11 +10,11 @@ struct image_data;

#ifdef CONFIG_BOOTM_FITIMAGE

-int bootm_open_fit(struct image_data *data);
+int bootm_open_fit(struct image_data *data, bool override);

#else

-static inline int bootm_open_fit(struct image_data *data)
+static inline int bootm_open_fit(struct image_data *data, bool override)
{
return -ENOSYS;
}
diff --git a/include/bootm-overrides.h b/include/bootm-overrides.h
index a52a498b97a2..9b133928a923 100644
--- a/include/bootm-overrides.h
+++ b/include/bootm-overrides.h
@@ -3,6 +3,7 @@
#define __BOOTM_OVERRIDES_H

struct bootm_overrides {
+ const char *os_file;
const char *oftree_file;
const char *initrd_file;
};
@@ -13,6 +14,8 @@ struct image_data;
static inline void bootm_merge_overrides(struct bootm_overrides *dst,
const struct bootm_overrides *src)
{
+ if (src->os_file)
+ dst->os_file = src->os_file;
if (src->oftree_file)
dst->oftree_file = src->oftree_file;
if (src->initrd_file)
diff --git a/include/bootm.h b/include/bootm.h
index 570ac4d289f2..85199fc702b4 100644
--- a/include/bootm.h
+++ b/include/bootm.h
@@ -53,6 +53,8 @@ struct bootm_data {
unsigned long os_entry;
};

+int bootm_image_name_and_part(const char *name, char **filename, char **part);
+
int bootm_boot(const struct bootm_data *data);

struct image_data *bootm_boot_prep(const struct bootm_data *bootm_data);
--
2.47.3