mail archive of the barebox mailing list
 help / color / mirror / Atom feed
From: Steffen Trumtrar <s.trumtrar@pengutronix.de>
To: barebox@lists.infradead.org, Sascha Hauer <s.hauer@pengutronix.de>
Cc: Steffen Trumtrar <s.trumtrar@pengutronix.de>
Subject: [PATCH 2/4] bootm: append a Linux bootconfig to the initrd
Date: Tue, 27 Oct 2026 14:23:54 +0100	[thread overview]
Message-ID: <20261027-v2026-10-0-topic-bootconfig-v1-2-2758d3d9a165@pengutronix.de> (raw)
In-Reply-To: <20261027-v2026-10-0-topic-bootconfig-v1-0-2758d3d9a165@pengutronix.de>

Linux reads a boot configuration (bootconfig, see
Documentation/admin-guide/bootconfig.rst) from the end of the initrd:
keys for the kernel command line, for init, and for boot-time tracing,
without changing the image or the boot entry. tools/bootconfig appends it
to an initrd file; with this, barebox does that at boot time.

global.bootm.bootconfig or bootm -b names a text file. barebox appends
it with the footer (size, checksum, magic) as the last loadable of the
initrd chain, after all initrds, and adds "bootconfig" to the kernel
command line. It is chained only in bootm_load_initrd(), as FIT, uImage
and overrides may replace the initrd until then. Without an initrd, the
bootconfig alone is passed as one; the kernel removes it and is left
with an empty initrd.

The format follows Linux v7.3 (c03c1a14507d), no code is taken from it:
magic and size limit (XBC_DATA_MAX) from include/linux/bootconfig.h,
checksum and padding as apply_xbc() in tools/bootconfig/main.c writes
them, read by get_boot_config_from_initrd() in init/main.c. As there, the
padding makes the initrd end 4-byte aligned; it depends on the size of
the initrd and is added once that is loaded. Kernels before v7.0 read the
footer as u32, 499f86de4f8c ("init/main: read bootconfig header with
get_unaligned_le32()") made that unaligned. The result is identical to
tools/bootconfig -a of v7.3. The syntax is checked by the kernel, not
here.

Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Steffen Trumtrar <s.trumtrar@pengutronix.de>
---
 Documentation/user/booting-linux.rst |  19 ++++++
 commands/bootm.c                     |  18 ++++-
 common/Kconfig                       |  15 ++++
 common/Makefile                      |   1 +
 common/bootconfig.c                  | 129 +++++++++++++++++++++++++++++++++++
 common/bootm.c                       |  48 ++++++++++++-
 include/bootconfig.h                 |  27 ++++++++
 include/bootm.h                      |   5 ++
 include/loadable.h                   |   2 +
 lib/loadable.c                       |   2 +
 10 files changed, 262 insertions(+), 4 deletions(-)

diff --git a/Documentation/user/booting-linux.rst b/Documentation/user/booting-linux.rst
index 32a731bfd2..11c26e663c 100644
--- a/Documentation/user/booting-linux.rst
+++ b/Documentation/user/booting-linux.rst
@@ -63,6 +63,25 @@ To use an initramfs, use the ``-r`` option or the
   global.bootm.image=/path/to/zImage
   bootm
 
+With ``CONFIG_BOOTM_BOOTCONFIG``, a Linux boot configuration
+(`bootconfig <https://docs.kernel.org/admin-guide/bootconfig.html>`_) can be
+appended to the initrd with the ``-b`` option or the
+:ref:`global.bootm.bootconfig <magicvar_global_bootm_bootconfig>` variable.
+The file is plain text, barebox adds the footer the kernel looks for and
+``bootconfig`` to the kernel command line. Without an initrd, the bootconfig
+alone is passed as one. The kernel needs ``CONFIG_BOOT_CONFIG``:
+
+.. code-block:: sh
+
+  bootm -r /path/to/initrd -b /path/to/bootconfig /path/to/Image
+
+  # same as:
+
+  global.bootm.initrd=/path/to/initrd
+  global.bootm.bootconfig=/path/to/bootconfig
+  global.bootm.image=/path/to/Image
+  bootm
+
 FIT image configurations will be matched by comparing the ``compatible`` property
 inside the configuration node with the barebox live tree's ``/compatible``.
 It's also possible to select a specific configuration explicitly:
diff --git a/commands/bootm.c b/commands/bootm.c
index 44abb953f6..391d51292a 100644
--- a/commands/bootm.c
+++ b/commands/bootm.c
@@ -31,9 +31,15 @@
 #define BOOTM_OPTS_COMMON "a:e:vo:fd"
 
 #ifdef CONFIG_BOOTM_INITRD
-#define BOOTM_OPTS BOOTM_OPTS_COMMON "L:r:"
+#define BOOTM_OPTS_INITRD BOOTM_OPTS_COMMON "L:r:"
 #else
-#define BOOTM_OPTS BOOTM_OPTS_COMMON
+#define BOOTM_OPTS_INITRD BOOTM_OPTS_COMMON
+#endif
+
+#ifdef CONFIG_BOOTM_BOOTCONFIG
+#define BOOTM_OPTS BOOTM_OPTS_INITRD "b:"
+#else
+#define BOOTM_OPTS BOOTM_OPTS_INITRD
 #endif
 
 static unsigned command_attempts;
@@ -62,6 +68,11 @@ static int do_bootm(int argc, char *argv[])
 		case 'r':
 			data.initrd_file = optarg;
 			break;
+#endif
+#ifdef CONFIG_BOOTM_BOOTCONFIG
+		case 'b':
+			data.bootconfig_file = optarg;
+			break;
 #endif
 		case 'a':
 			data.os_address = simple_strtoul(optarg, NULL, 0);
@@ -112,6 +123,9 @@ BAREBOX_CMD_HELP_OPT ("-f\t",  "load images even if type is undetectable")
 BAREBOX_CMD_HELP_OPT ("-r INITRD","specify an initrd image")
 BAREBOX_CMD_HELP_OPT ("-L ADDR\t","specify initrd load address")
 #endif
+#ifdef CONFIG_BOOTM_BOOTCONFIG
+BAREBOX_CMD_HELP_OPT ("-b FILE\t","append Linux bootconfig FILE to the initrd")
+#endif
 BAREBOX_CMD_HELP_OPT ("-a ADDR\t","specify os load address")
 BAREBOX_CMD_HELP_OPT ("-e OFFS\t","entry point to the image relative to start (0)")
 #ifdef CONFIG_OFTREE
diff --git a/common/Kconfig b/common/Kconfig
index 9adecfd2e4..5ae8b4302c 100644
--- a/common/Kconfig
+++ b/common/Kconfig
@@ -706,6 +706,21 @@ config BOOTM_INITRD
 	  -r INITRD  specify an initrd image
 	  -L ADDR    specify initrd load address
 
+config BOOTM_BOOTCONFIG
+	bool
+	prompt "Linux boot configuration (bootconfig) support"
+	depends on BOOTM_INITRD
+	help
+	  Append a Linux boot configuration (bootconfig, see
+	  Documentation/admin-guide/bootconfig.rst in Linux) to the initrd,
+	  from global.bootm.bootconfig or the bootm option:
+
+	  -b FILE    append FILE as bootconfig to the initrd
+
+	  Without an initrd, the bootconfig alone is passed as one.
+	  "bootconfig" is added to the kernel command line. The kernel needs
+	  CONFIG_BOOT_CONFIG.
+
 config BOOTM_OFTREE
 	def_bool y
 	depends on BOOTM && HAVE_ARCH_BOOTM_OFTREE
diff --git a/common/Makefile b/common/Makefile
index b1045c3acb..31f562c9ea 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_BINFMT)		+= binfmt.o
 obj-$(CONFIG_BLOCK)		+= block.o
 obj-$(CONFIG_BLSPEC)		+= blspec.o
 obj-$(CONFIG_BOOTM)		+= bootm.o booti.o
+obj-$(CONFIG_BOOTM_BOOTCONFIG)	+= bootconfig.o
 obj-$(CONFIG_BOOTM_AIMAGE)	+= bootm-android-image.o
 obj-$(CONFIG_BOOT_OVERRIDE)	+= bootm-overrides.o
 obj-$(CONFIG_CMD_LOADS)		+= s_record.o
diff --git a/common/bootconfig.c b/common/bootconfig.c
new file mode 100644
index 0000000000..8c5cab5fb3
--- /dev/null
+++ b/common/bootconfig.c
@@ -0,0 +1,129 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Linux boot configuration (bootconfig): a text file the kernel reads from
+ * the end of the initrd, see Documentation/admin-guide/bootconfig.rst in
+ * Linux. barebox appends it as the last loadable of the initrd chain, so its
+ * footer ends up right at the end of the initrd handed to the kernel. The
+ * kernel only looks for it with "bootconfig" on its command line (or
+ * CONFIG_BOOT_CONFIG_FORCE), bootm adds that.
+ *
+ * The format is the one of Linux v7.3: magic and size limit from
+ * include/linux/bootconfig.h, checksum and padding as apply_xbc() in
+ * tools/bootconfig/main.c writes them, read by get_boot_config_from_initrd()
+ * in init/main.c. No code is taken from there.
+ */
+#define pr_fmt(fmt) "bootconfig: " fmt
+
+#include <common.h>
+#include <bootconfig.h>
+#include <fs.h>
+#include <libfile.h>
+#include <loadable.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+
+#define BOOTCONFIG_MAGIC	"#BOOTCONFIG\n"
+#define BOOTCONFIG_MAGIC_LEN	12
+#define BOOTCONFIG_ALIGN	4
+/* XBC_DATA_MAX in Linux: largest size in the footer, padding included */
+#define BOOTCONFIG_DATA_MAX	32767
+
+struct bootconfig_footer {
+	__le32 size;
+	__le32 csum;
+	char magic[BOOTCONFIG_MAGIC_LEN];
+} __packed;
+
+/**
+ * bootconfig_loadable - read a boot configuration for the initrd
+ * @path: file with the boot configuration (text)
+ *
+ * Builds what tools/bootconfig in Linux appends to an initrd: the text with
+ * a terminating NUL, then size, checksum and magic. The padding depends on
+ * the size of the initrd, bootconfig_align() adds it once loaded behind it.
+ * The syntax is checked by the kernel, not here.
+ *
+ * Return: loadable for loadable_chain() onto the initrd, or an error pointer
+ */
+struct loadable *bootconfig_loadable(const char *path)
+{
+	struct bootconfig_footer *footer;
+	struct loadable *l;
+	size_t len, size;
+	char *buf;
+	u32 csum = 0;
+	int ret, i;
+
+	ret = read_file_2(path, &len, (void **)&buf, FILESIZE_MAX);
+	if (ret) {
+		pr_err("%s: %pe\n", path, ERR_PTR(ret));
+		return ERR_PTR(ret);
+	}
+
+	/* read_file_2() zeroes behind the data: buf[len] is the NUL */
+	len = strnlen(buf, len);
+	size = len + 1;
+	/* room for the padding bootconfig_align() may add */
+	if (size + BOOTCONFIG_ALIGN - 1 > BOOTCONFIG_DATA_MAX) {
+		pr_err("%s: %zu bytes, the kernel accepts at most %d\n",
+		       path, size, BOOTCONFIG_DATA_MAX - (BOOTCONFIG_ALIGN - 1));
+		free(buf);
+		return ERR_PTR(-E2BIG);
+	}
+
+	buf = xrealloc(buf, size + sizeof(*footer));
+
+	for (i = 0; i < size; i++)
+		csum += (u8)buf[i];
+
+	footer = (void *)buf + size;
+	footer->size = cpu_to_le32(size);
+	footer->csum = cpu_to_le32(csum);
+	memcpy(footer->magic, BOOTCONFIG_MAGIC, BOOTCONFIG_MAGIC_LEN);
+
+	l = loadable_from_buf(buf, size + sizeof(*footer), LOADABLE_BOOTCONFIG);
+	free(l->name);
+	l->name = xstrdup(path);
+
+	return l;
+}
+
+/**
+ * bootconfig_align - align the bootconfig loaded at the end of an initrd
+ * @res: initrd in SDRAM, ending with the bootconfig
+ * @end_address: last address the initrd may use
+ *
+ * tools/bootconfig in Linux pads the bootconfig so that the initrd ends
+ * 4-byte aligned: kernels before v7.0 read the footer as u32 (since
+ * 499f86de4f8c "init/main: read bootconfig header with get_unaligned_le32()"
+ * they also take it unaligned). The size of the initrd before it is only
+ * known now: add the padding by moving the footer up to 3 bytes further.
+ * The size in the footer includes the padding, the checksum does not change
+ * (the padding is zero).
+ *
+ * Return: 0 on success, negative error code otherwise
+ */
+int bootconfig_align(struct resource *res, unsigned long end_address)
+{
+	void *end = (void *)(unsigned long)(res->end + 1);
+	struct bootconfig_footer *footer = end - sizeof(*footer);
+	unsigned int pad = -(res->end + 1) & (BOOTCONFIG_ALIGN - 1);
+	int ret;
+
+	if (!pad)
+		return 0;
+
+	if (res->end + pad > end_address)
+		return -ENOSPC;
+
+	ret = resize_region(res, resource_size(res) + pad);
+	if (ret)
+		return ret;
+
+	memmove((void *)footer + pad, footer, sizeof(*footer));
+	memset(footer, 0, pad);
+	footer = (void *)footer + pad;
+	footer->size = cpu_to_le32(le32_to_cpu(footer->size) + pad);
+
+	return 0;
+}
diff --git a/common/bootm.c b/common/bootm.c
index 27da1a590b..a8ac6bea21 100644
--- a/common/bootm.c
+++ b/common/bootm.c
@@ -3,6 +3,7 @@
 #include <common.h>
 #include <bootargs.h>
 #include <bootm.h>
+#include <bootconfig.h>
 #include <fs.h>
 #include <fcntl.h>
 #include <efi/mode.h>
@@ -101,6 +102,8 @@ void bootm_data_init_defaults(struct bootm_data *data)
 		getenv_ul("global.bootm.initrd.loadaddr", &data->initrd_address);
 		data->initrd_file = getenv_nonempty("global.bootm.initrd");
 	}
+	if (IS_ENABLED(CONFIG_BOOTM_BOOTCONFIG))
+		data->bootconfig_file = getenv_nonempty("global.bootm.bootconfig");
 	data->root_dev = getenv_nonempty("global.bootm.root_dev");
 	data->root_param = getenv_nonempty("global.bootm.root_param");
 	data->verify = bootm_get_verify_mode();
@@ -122,6 +125,8 @@ void bootm_data_restore_defaults(const struct bootm_data *data)
 		pr_setenv("global.bootm.initrd.loadaddr", "0x%lx", data->initrd_address);
 		globalvar_set("bootm.initrd", data->initrd_file);
 	}
+	if (IS_ENABLED(CONFIG_BOOTM_BOOTCONFIG))
+		globalvar_set("bootm.bootconfig", data->bootconfig_file);
 	globalvar_set("bootm.root_dev", data->root_dev);
 	globalvar_set("bootm.root_param", data->root_param);
 	bootm_set_verify_mode(data->verify);
@@ -265,6 +270,7 @@ const struct resource *
 bootm_load_initrd(struct image_data *data, ulong load_address, ulong end_address)
 {
 	struct resource *res = NULL;
+	bool bootconfig = false;
 
 	if (!IS_ENABLED(CONFIG_BOOTM_INITRD))
 		return NULL;
@@ -275,6 +281,18 @@ bootm_load_initrd(struct image_data *data, ulong load_address, ulong end_address
 	if (WARN_ON(data->initrd_res))
 		return data->initrd_res;
 
+	/*
+	 * Last in the chain, after all initrds: the kernel looks for the
+	 * bootconfig footer at the end of the initrd. Chained only now, as
+	 * FIT, uImage and overrides may replace the initrd until here.
+	 */
+	if (data->bootconfig) {
+		loadable_chain(&data->initrd, data->bootconfig);
+		data->bootconfig = NULL;
+		bootconfig = true;
+		globalvar_add_simple(LINUX_BOOTARGS_BOOTM_LATE "bootconfig", "bootconfig");
+	}
+
 	if (!data->initrd)
 		return NULL;
 
@@ -282,8 +300,19 @@ bootm_load_initrd(struct image_data *data, ulong load_address, ulong end_address
 		return ERR_PTR(-EINVAL);
 
 	res = loadable_extract_into_sdram_all(data->initrd, load_address, end_address);
-	if (!IS_ERR(res))
-		data->initrd_res = res;
+	if (IS_ERR_OR_NULL(res))
+		return res;
+
+	if (bootconfig) {
+		int ret = bootconfig_align(res, end_address);
+
+		if (ret) {
+			release_sdram_region(res);
+			return ERR_PTR(ret);
+		}
+	}
+
+	data->initrd_res = res;
 	return res;
 }
 
@@ -623,6 +652,16 @@ struct image_data *bootm_boot_prep(const struct bootm_data *bootm_data)
 			goto err_out;
 	}
 
+	if (IS_ENABLED(CONFIG_BOOTM_BOOTCONFIG) && bootm_data->bootconfig_file) {
+		data->bootconfig_file = xstrdup(bootm_data->bootconfig_file);
+		data->bootconfig = bootconfig_loadable(data->bootconfig_file);
+		if (IS_ERR(data->bootconfig)) {
+			ret = PTR_ERR(data->bootconfig);
+			data->bootconfig = NULL;
+			goto err_out;
+		}
+	}
+
 	if (bootm_data->appendroot) {
 		const char *root = NULL;
 		const char *rootopts = NULL;
@@ -798,6 +837,7 @@ void bootm_boot_cleanup(struct image_data *data)
 	release_sdram_region(data->initrd_res);
 	release_sdram_region(data->oftree_res);
 	loadable_release(&data->oftree);
+	loadable_release(&data->bootconfig);
 	loadable_release(&data->initrd);
 	loadable_release(&data->os);
 	if (data->of_root_node)
@@ -808,6 +848,7 @@ void bootm_boot_cleanup(struct image_data *data)
 	free(data->os_file);
 	free(data->oftree_file);
 	free(data->initrd_file);
+	free(data->bootconfig_file);
 	free(data);
 }
 
@@ -879,6 +920,8 @@ static int bootm_init(void)
 		globalvar_add_simple("bootm.initrd", NULL);
 		globalvar_add_simple("bootm.initrd.loadaddr", NULL);
 	}
+	if (IS_ENABLED(CONFIG_BOOTM_BOOTCONFIG))
+		globalvar_add_simple("bootm.bootconfig", NULL);
 
 	globalvar_add_simple_bool("bootm.dryrun", &bootm_dryrun);
 	globalvar_add_simple_int("bootm.verbose", &bootm_verbosity, "%u");
@@ -911,6 +954,7 @@ BAREBOX_MAGICVAR(global.bootm.image, "bootm default boot image");
 BAREBOX_MAGICVAR(global.bootm.image.loadaddr, "bootm default boot image loadaddr");
 BAREBOX_MAGICVAR(global.bootm.initrd, "bootm default initrd");
 BAREBOX_MAGICVAR(global.bootm.initrd.loadaddr, "bootm default initrd loadaddr");
+BAREBOX_MAGICVAR(global.bootm.bootconfig, "bootm default Linux bootconfig, appended to the initrd");
 BAREBOX_MAGICVAR(global.bootm.oftree, "bootm default oftree");
 BAREBOX_MAGICVAR(global.bootm.dryrun, "bootm default dryrun level");
 BAREBOX_MAGICVAR(global.bootm.verify, "bootm default verify level");
diff --git a/include/bootconfig.h b/include/bootconfig.h
new file mode 100644
index 0000000000..37ef8e0580
--- /dev/null
+++ b/include/bootconfig.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef __BOOTCONFIG_H
+#define __BOOTCONFIG_H
+
+#include <linux/err.h>
+#include <linux/types.h>
+
+struct loadable;
+struct resource;
+
+#ifdef CONFIG_BOOTM_BOOTCONFIG
+struct loadable *bootconfig_loadable(const char *path);
+int bootconfig_align(struct resource *res, unsigned long end_address);
+#else
+static inline struct loadable *bootconfig_loadable(const char *path)
+{
+	return ERR_PTR(-ENOSYS);
+}
+
+static inline int bootconfig_align(struct resource *res,
+				   unsigned long end_address)
+{
+	return -ENOSYS;
+}
+#endif
+
+#endif /* __BOOTCONFIG_H */
diff --git a/include/bootm.h b/include/bootm.h
index 946713971e..134b01d453 100644
--- a/include/bootm.h
+++ b/include/bootm.h
@@ -24,6 +24,7 @@ struct bootm_data {
 	const char *os_file;
 	const char *initrd_file;
 	const char *oftree_file;
+	const char *bootconfig_file;
 	const char *root_dev;
 	const char *root_param;
 	int verbose;
@@ -111,6 +112,10 @@ struct image_data {
 
 	unsigned long initrd_address;
 
+	/* Linux bootconfig, appended to the initrd by bootm_load_initrd() */
+	char *bootconfig_file;
+	struct loadable *bootconfig;
+
 	char *oftree_file;
 	char *oftree_part;
 
diff --git a/include/loadable.h b/include/loadable.h
index 6ab1692c5d..489c0ef7d8 100644
--- a/include/loadable.h
+++ b/include/loadable.h
@@ -17,12 +17,14 @@ struct loadable;
  * @LOADABLE_KERNEL: kernel image
  * @LOADABLE_INITRD: initial ramdisk
  * @LOADABLE_FDT: flattened device tree
+ * @LOADABLE_BOOTCONFIG: Linux boot configuration, appended to the initrd
  */
 enum loadable_type {
 	LOADABLE_UNSPECIFIED,
 	LOADABLE_KERNEL,
 	LOADABLE_INITRD,
 	LOADABLE_FDT,
+	LOADABLE_BOOTCONFIG,
 };
 
 /**
diff --git a/lib/loadable.c b/lib/loadable.c
index 8c016c125d..b410ef477c 100644
--- a/lib/loadable.c
+++ b/lib/loadable.c
@@ -31,6 +31,8 @@ const char *loadable_type_tostr(enum loadable_type type)
 		return "initrd";
 	case LOADABLE_FDT:
 		return "fdt";
+	case LOADABLE_BOOTCONFIG:
+		return "bootconfig";
 	default:
 		return NULL;
 	}

-- 
2.56.0




  parent reply	other threads:[~2026-10-27 13:25 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-27 13:23 [PATCH 0/4] Linux bootconfig support Steffen Trumtrar
2026-10-27 13:23 ` [PATCH 1/4] loadable: add loadable_from_buf() Steffen Trumtrar
2026-10-27 13:23 ` Steffen Trumtrar [this message]
2026-10-27 13:23 ` [PATCH 3/4] boot: add bootm.bootconfig override Steffen Trumtrar
2026-10-27 13:23 ` [PATCH 4/4] defaultenv: devboot: add devboot_bootconfig Steffen Trumtrar

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=20261027-v2026-10-0-topic-bootconfig-v1-2-2758d3d9a165@pengutronix.de \
    --to=s.trumtrar@pengutronix.de \
    --cc=barebox@lists.infradead.org \
    --cc=s.hauer@pengutronix.de \
    /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