Re: [PATCH v5 3/7] bootconfig: render embedded bootconfig as a kernel cmdline at build time
From: Nicolas Schier <hidden>
Date: 2026-06-17 13:35:20
Also in:
bpf, linux-kbuild, lkml
On Wed, Jun 17, 2026 at 04:23:35AM -0700, Breno Leitao wrote:
Add the build-time pipeline that renders the "kernel" subtree of
CONFIG_BOOT_CONFIG_EMBED_FILE into a flat cmdline string and stashes
it in .init.rodata as embedded_kernel_cmdline[]. A follow-up patch
adds the runtime helper that prepends this string to boot_command_line
during early architecture setup so parse_early_param() sees the values.
The build wires up:
tools/bootconfig -C kernel - userspace tool already shared with
lib/bootconfig.c, used here in -C mode
to render a bootconfig file to a cmdline
lib/embedded-cmdline.S - .incbin's the rendered text plus a NUL
(listed under the EXTRA BOOT CONFIG
MAINTAINERS entry)
lib/Makefile rule - runs tools/bootconfig at build time
Makefile prepare dep - ensures tools/bootconfig is built first,
same pattern as tools/objtool and
tools/bpf/resolve_btfids[...]
quoted hunk ↗ jump to hunk
Drop the test target from tools/bootconfig/Makefile's default 'all' recipe so that hooking the binary into the kernel build does not run test-bootconfig.sh on every prepare. The tests stay available as 'make -C tools/bootconfig test', matching the convention of tools/objtool and tools/bpf/resolve_btfids whose 'all' targets only build the binary. Require BOOT_CONFIG_EMBED_FILE to be non-empty before the new option can be enabled, otherwise tools/bootconfig -C runs against an empty file and prints a parse error on every kernel build. The feature gates on CONFIG_ARCH_SUPPORTS_CMDLINE_FROM_BOOTCONFIG, a silent symbol arches select once they've wired the prepend call into setup_arch(). No arch selects it in this patch, so the user-visible CONFIG_BOOT_CONFIG_EMBED_CMDLINE is not yet enableable; when an arch later opts in, the runtime behavior is added by the follow-up patches. tools/bootconfig also installs on target systems, so its own Makefile keeps $(CC) and stays cross-buildable as a standalone tool. The kernel build, which runs the tool on the build host during prepare, instead forces CC=$(HOSTCC) from a dedicated tools/bootconfig rule and clears CROSS_COMPILE= in the sub-make. Without that clear, an LLVM=1 cross build would inherit CROSS_COMPILE and tools/scripts/Makefile.include would inject --target=/--sysroot= flags into the host clang invocation, producing a target binary that fails to exec ("Exec format error"). embedded-cmdline.S places the rendered string in its own .init.rodata subsection (.init.rodata.embed_cmdline) with the "a" (allocatable, read-only) flag and %progbits. lib/bootconfig-data.S already places the embedded bootconfig blob in .init.rodata with the "aw" flag (xbc_init() rewrites separators in place, so that data must be writable). Using a distinct subsection name avoids the ld.lld section- type mismatch that would otherwise arise from mixing "a" and "aw" under the same name; the linker's "*(.init.rodata .init.rodata.*)" glob still folds both into the init image and frees them after boot. A follow-up patch wires the build-time tools/bootconfig into the top-level clean target. Signed-off-by: Breno Leitao <leitao@debian.org> --- MAINTAINERS | 1 + Makefile | 15 +++++++++++++++ init/Kconfig | 35 +++++++++++++++++++++++++++++++++++ lib/Makefile | 16 ++++++++++++++++ lib/embedded-cmdline.S | 16 ++++++++++++++++ tools/bootconfig/Makefile | 2 +- 6 files changed, 84 insertions(+), 1 deletion(-)diff --git a/MAINTAINERS b/MAINTAINERS index 57656ec0e9d5d..953231df1911d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS@@ -9844,6 +9844,7 @@ F: fs/proc/bootconfig.c F: include/linux/bootconfig.h F: lib/bootconfig-data.S F: lib/bootconfig.c +F: lib/embedded-cmdline.S F: tools/bootconfig/* F: tools/bootconfig/scripts/*diff --git a/Makefile b/Makefile index bf196c6df5b92..a7abb3f9a6264 100644 --- a/Makefile +++ b/Makefile@@ -1545,6 +1545,21 @@ prepare: tools/bpf/resolve_btfids endif endif +# tools/bootconfig renders the embedded bootconfig into a cmdline at build time. +ifdef CONFIG_BOOT_CONFIG_EMBED_CMDLINE +prepare: tools/bootconfig +endif + +# tools/bootconfig is run on the build host during prepare, so force a host +# binary here; its own Makefile keeps $(CC) for standalone and cross builds. +# CROSS_COMPILE= is cleared so tools/scripts/Makefile.include does not inject +# the target's --target=/--sysroot= flags into the host clang invocation under +# LLVM=1 cross builds (which would produce a target binary that fails to exec). +tools/bootconfig: FORCE + $(Q)mkdir -p $(objtree)/tools + $(Q)$(MAKE) O=$(abspath $(objtree)) subdir=tools -C $(srctree)/tools/ \ + bootconfig CC=$(HOSTCC) CROSS_COMPILE=
sashiko whines (priority: low) about the 'CC=$(HOSTCC)' as HOSTCC might contains spaces (e.g. "ccache gcc") [1]. Instead of adding quotes (as sashiko suggests), the CC could be redefined locally for the target, for example: tools/bootconfig: export CC := $(HOSTCC) tools/bootconfig: FORCE $(Q)mkdir -p $(objtree)/tools $(Q)$(MAKE) O=$(abspath $(objtree)) subdir=tools -C $(srctree)/tools/ \ bootconfig CROSS_COMPILE= That way, make handles the variable definition as it should and there is no interference with shell escaping. for Kbuild: Reviewed-by: Nicolas Schier <redacted> Kind regards, Nicolas [1]: http://sashiko.dev/#/message/20260617113701.0405E1F000E9%40smtp.kernel.org
quoted hunk ↗ jump to hunk
+ # The tools build system is not a part of Kbuild and tends to introduce # its own unique issues. If you need to integrate a new tool into Kbuild, # please consider locating that tool outside the tools/ tree and using thediff --git a/init/Kconfig b/init/Kconfig index 5230d4879b1c8..d2b8613a6b927 100644 --- a/init/Kconfig +++ b/init/Kconfig@@ -1566,6 +1566,41 @@ config BOOT_CONFIG_EMBED_FILE This bootconfig will be used if there is no initrd or no other bootconfig in the initrd. +config ARCH_SUPPORTS_CMDLINE_FROM_BOOTCONFIG + bool + help + Silent symbol; no C code reads it directly. Architectures + select it once their setup_arch() calls + xbc_prepend_embedded_cmdline() before parse_early_param(). + Its only role is to gate the user-visible + BOOT_CONFIG_EMBED_CMDLINE option per-arch, the same + ARCH_SUPPORTS_* idiom used by ARCH_SUPPORTS_CFI, etc. + +config BOOT_CONFIG_EMBED_CMDLINE + bool "Render embedded bootconfig as kernel cmdline at build time" + depends on BOOT_CONFIG_EMBED_FILE != "" + depends on ARCH_SUPPORTS_CMDLINE_FROM_BOOTCONFIG + default n + help + Render the "kernel" subtree of the embedded bootconfig file into a + flat cmdline string at kernel build time and prepend it to + boot_command_line during early architecture setup. This makes + early_param() handlers (e.g. mem=, earlycon=, loglevel=) see the + values supplied via the embedded bootconfig. + + The runtime bootconfig parser is unaffected, so tree-structured + consumers such as ftrace boot-time tracing keep working. + + Note: when an initrd also carries a bootconfig, its "kernel" + subtree is still parsed at runtime, but the embedded "kernel" + keys remain in boot_command_line for parse_early_param() and + end up later than the initrd keys in saved_command_line, so + parse_args() last-wins favors the embedded values. If you need + initrd to override embedded kernel.* keys, leave this option + off. + + If unsure, say N. + config CMDLINE_LOG_WRAP_IDEAL_LEN int "Length to try to wrap the cmdline when logged at boot" default 1021diff --git a/lib/Makefile b/lib/Makefile index 7f75cc6edf94a..4ace86a5cb6de 100644 --- a/lib/Makefile +++ b/lib/Makefile@@ -273,6 +273,22 @@ filechk_defbconf = cat $(or $(real-prereqs), /dev/null) $(obj)/default.bconf: $(CONFIG_BOOT_CONFIG_EMBED_FILE) FORCE $(call filechk,defbconf) +obj-$(CONFIG_BOOT_CONFIG_EMBED_CMDLINE) += embedded-cmdline.o +$(obj)/embedded-cmdline.o: $(obj)/embedded_cmdline.bin + +# Render the bootconfig "kernel" subtree to a flat cmdline string using +# the userspace tools/bootconfig parser (-C mode). The runtime prepend +# helper enforces COMMAND_LINE_SIZE at boot, so no build-time size +# check is performed here (COMMAND_LINE_SIZE is an arch header +# constant, not a Kconfig value). +quiet_cmd_render_cmdline = BCONF2C $@ + cmd_render_cmdline = \ + $(objtree)/tools/bootconfig/bootconfig -C $< > $@ + +targets += embedded_cmdline.bin +$(obj)/embedded_cmdline.bin: $(obj)/default.bconf $(objtree)/tools/bootconfig/bootconfig FORCE + $(call if_changed,render_cmdline) + obj-$(CONFIG_RBTREE_TEST) += rbtree_test.o obj-$(CONFIG_INTERVAL_TREE_TEST) += interval_tree_test.odiff --git a/lib/embedded-cmdline.S b/lib/embedded-cmdline.S new file mode 100644 index 0000000000000..bda81b4a42bea --- /dev/null +++ b/lib/embedded-cmdline.S@@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Embed the build-time-rendered bootconfig "kernel" subtree as a flat + * cmdline string. setup_arch() prepends this to boot_command_line on + * architectures that select ARCH_SUPPORTS_CMDLINE_FROM_BOOTCONFIG. + * + * Copyright (c) 2026 Meta Platforms, Inc. and affiliates + * Copyright (c) 2026 Breno Leitao <leitao@debian.org> + */ + .section .init.rodata.embed_cmdline, "a", %progbits + .global embedded_kernel_cmdline +embedded_kernel_cmdline: + .incbin "lib/embedded_cmdline.bin" + .byte 0 + .global embedded_kernel_cmdline_end +embedded_kernel_cmdline_end:diff --git a/tools/bootconfig/Makefile b/tools/bootconfig/Makefile index 90eb47c9d8de6..4e82fd9553cde 100644 --- a/tools/bootconfig/Makefile +++ b/tools/bootconfig/Makefile@@ -15,7 +15,7 @@ override CFLAGS += -Wall -g -I$(CURDIR)/include ALL_TARGETS := bootconfig ALL_PROGRAMS := $(patsubst %,$(OUTPUT)%,$(ALL_TARGETS)) -all: $(ALL_PROGRAMS) test +all: $(ALL_PROGRAMS) $(OUTPUT)bootconfig: main.c include/linux/bootconfig.h $(LIBSRC) $(CC) $(filter %.c,$^) $(CFLAGS) $(LDFLAGS) -o $@-- 2.53.0-Meta