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
> 

Reply via email to