Bootconfig updates for v7.3:

- bootconfig: Support build-time command line rendering for embedded bootconfig
   . bootconfig: render descendant keys when xbc_snprint_cmdline() root has a
     value. Fix xbc_snprint_cmdline() to render descendant keys when the root
     has both a value and subkeys, and treats empty subtrees correctly.
   . bootconfig: render embedded bootconfig as a kernel cmdline at build time.
     Add build-time pipeline using tools/bootconfig -C to render the embedded
     bootconfig "kernel" subtree into .init.rodata as a cmdline string.
   . bootconfig: clean build-time tools/bootconfig from make clean.
   . bootconfig: add xbc_prepend_embedded_cmdline() helper.
     Add helper to prepend embedded bootconfig cmdline into boot_command_line
     early before parse_early_param().
   . x86/setup: prepend embedded bootconfig cmdline before parse_early_param.
     Wire early prepend helper in x86 setup_arch() so early_param handlers see
     values from the embedded bootconfig. (So this feature is currently x86 only)
   . bootconfig: skip runtime kernel.* render once prepended early.
     Avoid duplicating "kernel" keys in setup_boot_config().
   . init/main.c: use bootconfig_cmdline_requested() for the runtime opt-in.
     Refactor setup_boot_config() to share bootconfig_cmdline_requested().
   . Documentation: bootconfig: document build-time cmdline rendering.
     Document CONFIG_CMDLINE_FROM_BOOTCONFIG usage, requirements, and precedence.
 -----BEGIN PGP SIGNATURE-----
 
 iQFPBAABCgA5FiEEh7BulGwFlgAOi5DV2/sHvwUrPxsFAmqNQwAbHG1hc2FtaS5o
 aXJhbWF0c3VAZ21haWwuY29tAAoJENv7B78FKz8bcLcH/1GHYfxZHrl1aA112j60
 YqBgn98hnCiH+Q1wMhqgDIF7ks1qePT9pq0zC2vJawVa1BM6OM5RpnC4BW+YSpCa
 7tRf209Hrr5sd2i0vmsnYpmd9SV9JWKsuyz6VJjFjPsfEFtI9Nj7d3jm7WfGypUc
 aNEAjf3sntEaFB44MoKekHlLzTXDFrgBaUaAcdw3aGjUU1CE9ltdT+QooQGv6CHe
 7Z+UlmGhg+jkQa1tNNpRnKZErD5OBLnTkoMU5ca3M7PQLlPjIcOTf62SInm47Ma/
 viOyzkSYIXC/yq2U4Je4qKcZaKhNIL61jtQGFI8EbbZ0x1BzVbPjbr0DB3iq2YW4
 4Xk=
 =8++3
 -----END PGP SIGNATURE-----

Merge tag 'bootconfig-v7.3' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace

Pull bootconfig updates from Masami Hiramatsu:

 - Support build-time command line building for embedded bootconfig

 - Fix xbc_snprint_cmdline() to render descendant keys when the root has
   both a value and subkeys, and treats empty subtrees correctly.

 - Add build-time pipeline using tools/bootconfig -C to render the
   embedded bootconfig "kernel" subtree into .init.rodata as a cmdline
   string.

 - Clean build-time tools/bootconfig from make clean

 - Add helper to prepend embedded bootconfig cmdline into
   boot_command_line early before parse_early_param()

 - Wire early prepend helper in x86 setup_arch() so early_param handlers
   see values from the embedded bootconfig (currently x86 only)

 - Avoid duplicating "kernel" keys in setup_boot_config()

 - Refactor setup_boot_config() to share bootconfig_cmdline_requested()

 - Document CONFIG_CMDLINE_FROM_BOOTCONFIG usage, requirements, and
   precedence

* tag 'bootconfig-v7.3' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace:
  init/main.c: use bootconfig_cmdline_requested() for the runtime opt-in
  bootconfig: skip runtime kernel.* render once prepended early
  x86/setup: prepend embedded bootconfig cmdline before parse_early_param
  Documentation: bootconfig: document build-time cmdline rendering
  bootconfig: add xbc_prepend_embedded_cmdline() helper
  bootconfig: clean build-time tools/bootconfig from make clean
  bootconfig: render embedded bootconfig as a kernel cmdline at build time
  bootconfig: render descendant keys when xbc_snprint_cmdline() root has a value
This commit is contained in:
Linus Torvalds 2026-08-25 08:59:09 -07:00
commit 5f5ef9c407
12 changed files with 372 additions and 29 deletions

View File

@ -234,6 +234,87 @@ Kconfig option selected.
Note that even if you set this option, you can override the embedded
bootconfig by another bootconfig which attached to the initrd.
Rendering Embedded kernel.* Keys at Build Time
----------------------------------------------
By default, the embedded bootconfig (``CONFIG_BOOT_CONFIG_EMBED=y``) is
parsed at runtime, after ``parse_early_param()`` has already run. Early
parameter handlers (``mem=``, ``earlycon=``, ``loglevel=``, ...) therefore
cannot see values supplied via the embedded ``kernel`` subtree.
``CONFIG_CMDLINE_FROM_BOOTCONFIG`` resolves this by rendering the
``kernel`` subtree of ``CONFIG_BOOT_CONFIG_EMBED_FILE`` into a flat cmdline
string at kernel build time (via ``tools/bootconfig -C``) and prepending
it to ``boot_command_line`` during early architecture setup, so the keys
are visible to ``parse_early_param()``.
The option requires ``CONFIG_BOOT_CONFIG_EMBED=y``, a non-empty
``CONFIG_BOOT_CONFIG_EMBED_FILE``, ``CONFIG_CMDLINE`` to be empty, and
an architecture that selects ``CONFIG_ARCH_SUPPORTS_CMDLINE_FROM_BOOTCONFIG``.
Currently only x86 selects it; on other architectures the embedded
bootconfig still works, but only through the late runtime parser.
The same ``bootconfig`` opt-in applies as elsewhere: the rendered keys
are prepended only when ``bootconfig`` (in any form) appears on the
kernel command line, or when ``CONFIG_BOOT_CONFIG_FORCE`` is set, which
defaults to ``y`` when ``CONFIG_BOOT_CONFIG_EMBED`` is set.
For example, given::
kernel {
loglevel = 7
mem = 4G
}
the kernel boots as if ``loglevel=7 mem=4G`` had been prepended to the
bootloader command line, with the values visible to early-parsed
handlers. Comma-separated values are still expanded into multiple
cmdline entries per the bootconfig array convention -- the embedded
``kernel.earlycon = "uart8250,io,0x3f8"`` must be quoted to land as a
single ``earlycon=`` entry, exactly as for the runtime parser.
If the rendered string would not fit in ``COMMAND_LINE_SIZE`` together
with the existing command line, the prepend is skipped and an error is
logged, so an oversized embedded bootconfig cannot brick a boot.
Interaction with other command line and bootconfig sources
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
With ``CONFIG_CMDLINE_FROM_BOOTCONFIG=y`` the rendered ``kernel``
subtree behaves like a build-time command line (similar to
``CONFIG_CMDLINE``), not like a bootconfig source. It is prepended to
``boot_command_line`` in ``setup_arch()``, before ``parse_early_param()``
and long before the runtime parser looks at an initrd. Options can reach
the kernel from up to four places:
- Bootloader command line: the arguments the boot loader passes. The
embedded cmdline is prepended in front of them, so for last-one-wins
parameters a bootloader option still overrides the embedded value.
Visible in /proc/cmdline.
- Embedded cmdline (this option): the rendered ``kernel`` subtree,
prepended early so it is seen by ``parse_early_param()``. Visible in
/proc/cmdline.
- Initrd bootconfig: parsed late in ``setup_boot_config()``; its
``kernel`` keys are placed ahead of ``boot_command_line``, i.e. before
the embedded cmdline, so last-wins favors the embedded values. As a
bootconfig source, an initrd bootconfig still replaces the embedded
bootconfig. Visible in /proc/cmdline and /proc/bootconfig.
- Embedded bootconfig (runtime): parsed late, only when no initrd
bootconfig is present. Visible in /proc/cmdline and /proc/bootconfig.
So with this option the embedded ``kernel.*`` values take precedence
over an initrd bootconfig's ``kernel.*`` values: for early parameters
the initrd is not parsed yet, and for ordinary parameters the embedded
keys land later in the command line. If you need an initrd bootconfig to
override the embedded ``kernel.*`` keys, leave this option off and rely
on the runtime parser.
The rendered string is part of the command line, so it appears in
/proc/cmdline. It is deliberately not shown in /proc/bootconfig: that
file keeps reporting the parsed bootconfig tree -- the initrd bootconfig
if present, otherwise the embedded bootconfig -- independent of whether
build-time cmdline rendering is enabled.
Kernel parameters via Boot Config
=================================

View File

@ -9898,6 +9898,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/*

View File

@ -1561,6 +1561,22 @@ prepare: tools/bpf/resolve_btfids
endif
endif
# tools/bootconfig renders the embedded bootconfig into a cmdline at build time.
ifdef CONFIG_CMDLINE_FROM_BOOTCONFIG
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: export CC := $(HOSTCC)
tools/bootconfig: FORCE
$(Q)mkdir -p $(objtree)/tools
$(Q)$(MAKE) O=$(abspath $(objtree)) subdir=tools -C $(srctree)/tools/ \
bootconfig CROSS_COMPILE=
# 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
@ -1587,6 +1603,15 @@ ifneq ($(wildcard $(objtool_O)),)
$(Q)$(MAKE) -sC $(abs_srctree)/tools/objtool O=$(objtool_O) srctree=$(abs_srctree) $(patsubst objtool_%,%,$@)
endif
PHONY += bootconfig_clean
bootconfig_O = $(abspath $(objtree))/tools/bootconfig
bootconfig_clean:
ifneq ($(wildcard $(bootconfig_O)),)
$(Q)$(MAKE) -sC $(srctree)/tools/bootconfig O=$(bootconfig_O) clean
endif
tools/: FORCE
$(Q)mkdir -p $(objtree)/tools
$(Q)$(MAKE) O=$(abspath $(objtree)) subdir=tools -C $(srctree)/tools/
@ -1762,7 +1787,7 @@ vmlinuxclean:
$(Q)$(CONFIG_SHELL) $(srctree)/scripts/link-vmlinux.sh clean
$(Q)$(if $(ARCH_POSTLINK), $(MAKE) -f $(ARCH_POSTLINK) clean)
clean: archclean vmlinuxclean resolve_btfids_clean objtool_clean
clean: archclean vmlinuxclean resolve_btfids_clean objtool_clean bootconfig_clean
# mrproper - Delete all generated files, including .config
#

View File

@ -129,6 +129,7 @@ config X86
select ARCH_SUPPORTS_NUMA_BALANCING if X86_64
select ARCH_SUPPORTS_KMAP_LOCAL_FORCE_MAP if NR_CPUS <= 4096
select ARCH_SUPPORTS_CFI if X86_64
select ARCH_SUPPORTS_CMDLINE_FROM_BOOTCONFIG
select ARCH_USES_CFI_TRAPS if X86_64 && CFI
select ARCH_SUPPORTS_LTO_CLANG
select ARCH_SUPPORTS_LTO_CLANG_THIN

View File

@ -6,6 +6,7 @@
* parts of early kernel initialization.
*/
#include <linux/acpi.h>
#include <linux/bootconfig.h>
#include <linux/console.h>
#include <linux/cpu.h>
#include <linux/crash_dump.h>
@ -880,7 +881,6 @@ static void __init x86_report_nx(void)
*
* Note: On x86_64, fixmaps are ready for use even before this is called.
*/
void __init setup_arch(char **cmdline_p)
{
#ifdef CONFIG_X86_32
@ -924,6 +924,18 @@ void __init setup_arch(char **cmdline_p)
builtin_cmdline_added = true;
#endif
#ifdef CONFIG_CMDLINE_FROM_BOOTCONFIG
/*
* Prepend the build-time-rendered embedded "kernel" keys here so
* parse_early_param() below sees them, using the same opt-in as the
* runtime parser, plus the build-time CONFIG_BOOT_CONFIG_FORCE.
*/
if (bootconfig_cmdline_requested(boot_command_line, NULL) ||
IS_ENABLED(CONFIG_BOOT_CONFIG_FORCE))
xbc_prepend_embedded_cmdline(boot_command_line,
COMMAND_LINE_SIZE);
#endif
strscpy(command_line, boot_command_line, COMMAND_LINE_SIZE);
*cmdline_p = command_line;

View File

@ -308,4 +308,18 @@ static inline const char *xbc_get_embedded_bootconfig(size_t *size)
}
#endif
/* Bootconfig opt-in detection, shared by setup_arch() and setup_boot_config() */
#ifdef CONFIG_BOOT_CONFIG
bool __init bootconfig_cmdline_requested(const char *boot_cmdline, int *end_offset);
#endif
/* Build-time-rendered bootconfig cmdline prepended in setup_arch() */
#ifdef CONFIG_CMDLINE_FROM_BOOTCONFIG
void __init xbc_prepend_embedded_cmdline(char *dst, size_t size);
bool __init xbc_embedded_cmdline_applied(void);
#else
static inline void xbc_prepend_embedded_cmdline(char *dst, size_t size) { }
static inline bool xbc_embedded_cmdline_applied(void) { return false; }
#endif
#endif

View File

@ -1578,6 +1578,42 @@ 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
CMDLINE_FROM_BOOTCONFIG option per-arch, the same
ARCH_SUPPORTS_* idiom used by ARCH_SUPPORTS_CFI, etc.
config CMDLINE_FROM_BOOTCONFIG
bool "Render embedded bootconfig as kernel cmdline at build time"
depends on BOOT_CONFIG_EMBED_FILE != ""
depends on ARCH_SUPPORTS_CMDLINE_FROM_BOOTCONFIG
depends on CMDLINE = ""
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

View File

@ -357,45 +357,33 @@ static char * __init xbc_make_cmdline(const char *key)
return new_cmdline;
}
static int __init bootconfig_params(char *param, char *val,
const char *unused, void *arg)
{
if (strcmp(param, "bootconfig") == 0) {
bootconfig_found = true;
}
return 0;
}
static int __init warn_bootconfig(char *str)
{
/* The 'bootconfig' has been handled by bootconfig_params(). */
/* The 'bootconfig' option is handled by setup_boot_config(). */
return 0;
}
static void __init setup_boot_config(void)
{
static char tmp_cmdline[COMMAND_LINE_SIZE] __initdata;
const char *msg, *data;
int pos, ret;
int pos, ret, offs;
size_t size;
char *err;
bool from_embedded = false;
/* Cut out the bootconfig data even if we have no bootconfig option */
data = get_boot_config_from_initrd(&size);
/* If there is no bootconfig in initrd, try embedded one. */
if (!data)
if (!data) {
data = xbc_get_embedded_bootconfig(&size);
from_embedded = true;
}
strscpy(tmp_cmdline, boot_command_line, COMMAND_LINE_SIZE);
err = parse_args("bootconfig", tmp_cmdline, NULL, 0, 0, 0, NULL,
bootconfig_params);
if (IS_ERR(err) || !(bootconfig_found || IS_ENABLED(CONFIG_BOOT_CONFIG_FORCE)))
bootconfig_found = bootconfig_cmdline_requested(boot_command_line, &offs);
if (!(bootconfig_found || IS_ENABLED(CONFIG_BOOT_CONFIG_FORCE)))
return;
/* parse_args() stops at the next param of '--' and returns an address */
if (err)
initargs_offs = err - tmp_cmdline;
/* Offset of the init arguments after a "--", located by the helper. */
initargs_offs = offs;
if (!data) {
/* If user intended to use bootconfig, show an error level message */
@ -422,8 +410,24 @@ static void __init setup_boot_config(void)
} else {
xbc_get_info(&ret, NULL);
pr_info("Load bootconfig: %ld bytes %d nodes\n", (long)size, ret);
/* keys starting with "kernel." are passed via cmdline */
extra_command_line = xbc_make_cmdline("kernel");
/*
* keys starting with "kernel." are passed via cmdline. When
* this bootconfig came from the embedded source and
* setup_arch() already prepended the rendered "kernel" subtree
* to boot_command_line, rendering again here would duplicate
* the keys in saved_command_line and make accumulating handlers
* (console=, earlycon=, ...) re-register the same value. Skip
* only when the prepend really happened.
*
* On arches that do not select ARCH_SUPPORTS_CMDLINE_FROM_BOOTCONFIG,
* CONFIG_CMDLINE_FROM_BOOTCONFIG is unselectable and
* xbc_embedded_cmdline_applied() collapses to a stub returning
* false, so this path still runs and the embedded "kernel"
* keys reach the cmdline via the runtime parser exactly as
* before this series.
*/
if (!from_embedded || !xbc_embedded_cmdline_applied())
extra_command_line = xbc_make_cmdline("kernel");
/* Also, "init." keys are init arguments */
extra_init_args = xbc_make_cmdline("init");
}

View File

@ -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_CMDLINE_FROM_BOOTCONFIG) += 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

View File

@ -19,9 +19,13 @@
#include <linux/errno.h>
#include <linux/cache.h>
#include <linux/compiler.h>
#include <linux/init.h>
#include <linux/moduleparam.h>
#include <linux/printk.h>
#include <linux/sprintf.h>
#include <linux/memblock.h>
#include <linux/string.h>
#include <asm/setup.h> /* COMMAND_LINE_SIZE */
#ifdef CONFIG_BOOT_CONFIG_EMBED
/* embedded_bootconfig_data is defined in bootconfig-data.S */
@ -34,7 +38,129 @@ const char * __init xbc_get_embedded_bootconfig(size_t *size)
return (*size) ? embedded_bootconfig_data : NULL;
}
#endif
#endif
#ifdef CONFIG_CMDLINE_FROM_BOOTCONFIG
/* embedded_kernel_cmdline is defined in embedded-cmdline.S */
extern __visible const char embedded_kernel_cmdline[];
extern __visible const char embedded_kernel_cmdline_end[];
/* Set once the embedded cmdline has actually been prepended. */
static bool xbc_cmdline_applied __initdata;
/*
* str_prepend() - Prepend @src in front of the string in @dst, in place
* @dst: NUL-terminated destination buffer, currently @dst_len bytes long
* @dst_len: length of the current @dst string (excluding its NUL)
* @src: bytes to prepend (not NUL-terminated)
* @src_len: number of bytes from @src to prepend
*
* The caller must guarantee @dst has room for src_len + dst_len + 1 bytes.
* Moving dst_len + 1 bytes carries @dst's NUL terminator too, so an empty
* @dst needs no special case.
*/
static void __init str_prepend(char *dst, size_t dst_len,
const char *src, size_t src_len)
{
memmove(dst + src_len, dst, dst_len + 1);
memcpy(dst, src, src_len);
}
/**
* xbc_prepend_embedded_cmdline() - Prepend embedded bootconfig cmdline
* @dst: cmdline buffer to prepend into (must already contain a NUL byte)
* @size: total capacity of @dst in bytes
*
* Prepend the build-time-rendered "kernel" subtree of the embedded
* bootconfig to @dst. The rendered string already ends with a single
* space (the xbc_snprint_cmdline() invariant), which serves as the
* separator between the embedded keys and any existing content of @dst.
* On overflow, log an error and leave @dst untouched rather than
* silently truncating: booting without the embedded values is better
* than refusing to boot, and the error message tells the user why
* their embedded keys are missing.
*
* Intended to be called from setup_arch() before parse_early_param() so
* that early_param() handlers see the embedded values.
*/
void __init xbc_prepend_embedded_cmdline(char *dst, size_t size)
{
size_t embed_len = embedded_kernel_cmdline_end - embedded_kernel_cmdline;
size_t dst_len;
if (!size || embed_len <= 1) /* trailing NUL only */
return;
embed_len--; /* exclude trailing NUL byte */
dst_len = strnlen(dst, size);
if (embed_len + dst_len + 1 > size) {
pr_err("embedded bootconfig cmdline (%zu bytes) does not fit in COMMAND_LINE_SIZE with %zu bytes already used; ignoring embedded values\n",
embed_len, dst_len);
return;
}
str_prepend(dst, dst_len, embedded_kernel_cmdline, embed_len);
xbc_cmdline_applied = true;
}
/**
* xbc_embedded_cmdline_applied() - Did the embedded cmdline get prepended?
*
* Return true if xbc_prepend_embedded_cmdline() actually prepended the
* embedded "kernel" subtree. setup_boot_config() uses this to avoid
* rendering the same keys a second time.
*/
bool __init xbc_embedded_cmdline_applied(void)
{
return xbc_cmdline_applied;
}
#endif /* CONFIG_CMDLINE_FROM_BOOTCONFIG */
/* parse_args() callback: flag when the "bootconfig" parameter is present. */
static int __init bootconfig_optin(char *param, char *val,
const char *unused, void *arg)
{
if (!strcmp(param, "bootconfig"))
*(bool *)arg = true;
return 0;
}
/**
* bootconfig_cmdline_requested() - Was "bootconfig" passed on the cmdline?
* @boot_cmdline: kernel command line to inspect (not modified)
* @end_offset: if non-NULL, set to the offset of the init arguments that
* follow a "--" separator, or 0 when there is none
*
* Parse a private copy of @boot_cmdline (parse_args() is destructive) and
* report whether "bootconfig" is present before the "--" separator.
* setup_arch() uses this to gate prepending the build-time embedded cmdline;
* setup_boot_config() uses it for the runtime opt-in and to locate the init
* arguments via @end_offset. Sharing one parser keeps the early and late
* paths agreeing on what counts as opt-in. CONFIG_BOOT_CONFIG_FORCE is not
* folded in here; callers apply it where they need it.
*/
bool __init bootconfig_cmdline_requested(const char *boot_cmdline, int *end_offset)
{
static char tmp_cmdline[COMMAND_LINE_SIZE] __initdata;
bool found = false;
char *err;
if (end_offset)
*end_offset = 0;
strscpy(tmp_cmdline, boot_cmdline, COMMAND_LINE_SIZE);
err = parse_args("bootconfig", tmp_cmdline, NULL, 0, 0, 0,
&found, bootconfig_optin);
if (IS_ERR(err))
return false;
/* parse_args() stops at "--" and returns the address of the rest. */
if (end_offset && err)
*end_offset = err - tmp_cmdline;
return found;
}
#endif /* __KERNEL__ */
/*
* Extra Boot Config (XBC) is given as tree-structured ascii text of
@ -440,6 +566,17 @@ int __init xbc_snprint_cmdline(char *buf, size_t size, struct xbc_node *root)
* itself is well defined and returns the would-be length.
*/
xbc_node_for_each_key_value(root, knode, val) {
/*
* An empty or value-only @root (e.g. "kernel {}" or
* "kernel = x", possibly alongside "kernel.foo = bar")
* yields @root itself here. Skip it: composing a key for it
* would fail with -EINVAL, yet any real descendant keys must
* still be rendered. An entirely empty subtree then renders
* nothing and returns 0 rather than an error.
*/
if (knode == root)
continue;
ret = xbc_node_compose_key_after(root, knode,
xbc_namebuf, XBC_KEYLEN_MAX);
if (ret < 0)

16
lib/embedded-cmdline.S Normal file
View File

@ -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:

View File

@ -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 $@
@ -27,4 +27,4 @@ install: $(ALL_PROGRAMS)
install $(OUTPUT)bootconfig $(DESTDIR)$(bindir)
clean:
$(RM) -f $(OUTPUT)*.o $(ALL_PROGRAMS)
rm -f $(OUTPUT)*.o $(ALL_PROGRAMS)