Re: [PATCH v5 3/7] bootconfig: render embedded bootconfig as a kernel cmdline at build time

Nicolas Schier <[email protected]>
Newsgroups org.kernel.vger.linux-kbuild,org.kernel.vger.bpf,org.kernel.vger.linux-kernel,org.kernel.vger.linux-trace-kernel
Organization FRITZ! Technology GmbH
Message-ID <20260617-congenial-porcupine-of-rain-bb4d8e@l-nschier-aarch64>
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
[...]
> 
> 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 <[email protected]>
> ---
>  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 <[email protected]>


Kind regards,
Nicolas


[1]: http://sashiko.dev/#/message/20260617113701.0405E1F000E9%40smtp.kernel.org


> +
>  # 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 the
> diff --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 1021
> diff --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.o
>  
> diff --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 <[email protected]>
> + */
> +	.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
>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.