Documentation/admin-guide/bootconfig.rst | 4 ++ init/Kconfig | 6 +++ init/main.c | 68 +++++++++++++++++++++++++++++++- 3 files changed, 77 insertions(+), 1 deletion(-)
Bootconfig currently cannot be used to configure early kernel
parameters. For example, the "mitigations=" parameter must be passed
through traditional boot methods because bootconfig parsing happens
after these early parameters need to be processed.
This patch allows early options such as:
kernel.mitigations = off
to be placed in the embedded bootconfig and take effect, without
requiring them to be on the kernel command line.
Add bootconfig_apply_early_params() which walks all kernel.* keys in the
parsed XBC tree and calls do_early_param() for each one. It is called
from setup_boot_config() immediately after a successful xbc_init() on
the embedded data, which happens before parse_early_param() runs in
start_kernel().
Early options in initrd bootconfig are still silently ignored, as the
initrd is only available after the early param window has closed.
Document this behaviour in both Kconfig and the admin guide.
Signed-off-by: Breno Leitao <leitao@debian.org>
---
Changes in v2:
- Made val_buf static __initdata to keep 2KB off the stack
- Removed dead !val branch — xbc_node_find_next_key_value() returns "" for boolean keys, never NULL
- Added pr_warn + continue when strscpy truncates the value
- Link to v1: https://patch.msgid.link/20260324-early_bootconfig-v1-1-1c0e625aff06@debian.org
---
Documentation/admin-guide/bootconfig.rst | 4 ++
init/Kconfig | 6 +++
init/main.c | 68 +++++++++++++++++++++++++++++++-
3 files changed, 77 insertions(+), 1 deletion(-)
diff --git a/Documentation/admin-guide/bootconfig.rst b/Documentation/admin-guide/bootconfig.rst
index f712758472d5c..e820f33d3ad16 100644
--- a/Documentation/admin-guide/bootconfig.rst
+++ b/Documentation/admin-guide/bootconfig.rst
@@ -169,6 +169,10 @@ Boot Kernel With a Boot Config
There are two options to boot the kernel with bootconfig: attaching the
bootconfig to the initrd image or embedding it in the kernel itself.
+Early options (those registered with ``early_param()``) may only be
+specified in the embedded bootconfig, because the initrd is not yet
+available when early parameters are processed.
+
Attaching a Boot Config to Initrd
---------------------------------
diff --git a/init/Kconfig b/init/Kconfig
index 938fbe6a91e15..5e8057e73fe06 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -1534,6 +1534,12 @@ config BOOT_CONFIG_EMBED
image. But if the system doesn't support initrd, this option will
help you by embedding a bootconfig file while building the kernel.
+ Unlike bootconfig attached to initrd, the embedded bootconfig also
+ supports early options (those registered with early_param()). Any
+ kernel.* key in the embedded bootconfig is applied before
+ parse_early_param() runs. Early options in initrd bootconfig will
+ not be applied.
+
If unsure, say N.
config BOOT_CONFIG_EMBED_FILE
diff --git a/init/main.c b/init/main.c
index 453ac9dff2da0..14a04c283fa48 100644
--- a/init/main.c
+++ b/init/main.c
@@ -416,9 +416,64 @@ static int __init warn_bootconfig(char *str)
return 0;
}
+/*
+ * do_early_param() is defined later in this file but called from
+ * bootconfig_apply_early_params() below, so we need a forward declaration.
+ */
+static int __init do_early_param(char *param, char *val,
+ const char *unused, void *arg);
+
+/*
+ * bootconfig_apply_early_params - dispatch kernel.* keys from the embedded
+ * bootconfig as early_param() calls.
+ *
+ * early_param() handlers must run before most of the kernel initialises
+ * (e.g. before the GIC driver reads irqchip.gicv3_pseudo_nmi). A bootconfig
+ * attached to the initrd arrives too late for this because the initrd is not
+ * mapped yet when early params are processed. The embedded bootconfig lives
+ * in the kernel image itself (.init.data), so it is always reachable.
+ *
+ * This function is called from setup_boot_config() which runs in
+ * start_kernel() before parse_early_param(), making the timing correct.
+ */
+static void __init bootconfig_apply_early_params(void)
+{
+ static char val_buf[COMMAND_LINE_SIZE] __initdata;
+ struct xbc_node *knode, *root;
+ const char *val;
+ ssize_t ret;
+
+ root = xbc_find_node("kernel");
+ if (!root)
+ return;
+
+ /*
+ * Keys that do not match any early_param() handler are silently
+ * ignored — do_early_param() always returns 0.
+ */
+ xbc_node_for_each_key_value(root, knode, val) {
+ if (xbc_node_compose_key_after(root, knode, xbc_namebuf, XBC_KEYLEN_MAX) < 0)
+ continue;
+
+ /*
+ * We need to copy const char *val to a char pointer,
+ * which is what do_early_param() need, given it might
+ * call strsep(), strtok() later.
+ */
+ ret = strscpy(val_buf, val, sizeof(val_buf));
+ if (ret < 0) {
+ pr_warn("ignoring bootconfig value '%s', too long\n",
+ xbc_namebuf);
+ continue;
+ }
+ do_early_param(xbc_namebuf, val_buf, NULL, NULL);
+ }
+}
+
static void __init setup_boot_config(void)
{
static char tmp_cmdline[COMMAND_LINE_SIZE] __initdata;
+ bool using_embedded = false;
const char *msg, *data;
int pos, ret;
size_t size;
@@ -427,8 +482,17 @@ static void __init setup_boot_config(void)
/* 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);
+ /*
+ * Record that we are using the embedded config so that
+ * bootconfig_apply_early_params() is called below.
+ * When CONFIG_BOOT_CONFIG_EMBED is not set,
+ * xbc_get_embedded_bootconfig() is a stub returning NULL, so
+ * data is always NULL here and using_embedded stays false.
+ */
+ using_embedded = data;
+ }
strscpy(tmp_cmdline, boot_command_line, COMMAND_LINE_SIZE);
err = parse_args("bootconfig", tmp_cmdline, NULL, 0, 0, 0, NULL,
@@ -466,6 +530,8 @@ 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);
+ if (using_embedded)
+ bootconfig_apply_early_params();
/* keys starting with "kernel." are passed via cmdline */
extra_command_line = xbc_make_cmdline("kernel");
/* Also, "init." keys are init arguments */
---
base-commit: 785f0eb2f85decbe7c1ef9ae922931f0194ffc2e
change-id: 20260323-early_bootconfig-2efc4509af3d
Best regards,
--
Breno Leitao <leitao@debian.org>
Hi Breno,
On Wed, 25 Mar 2026 03:05:38 -0700
Breno Leitao <leitao@debian.org> wrote:
> Bootconfig currently cannot be used to configure early kernel
> parameters. For example, the "mitigations=" parameter must be passed
> through traditional boot methods because bootconfig parsing happens
> after these early parameters need to be processed.
>
> This patch allows early options such as:
>
> kernel.mitigations = off
>
> to be placed in the embedded bootconfig and take effect, without
> requiring them to be on the kernel command line.
>
> Add bootconfig_apply_early_params() which walks all kernel.* keys in the
> parsed XBC tree and calls do_early_param() for each one. It is called
> from setup_boot_config() immediately after a successful xbc_init() on
> the embedded data, which happens before parse_early_param() runs in
> start_kernel().
>
> Early options in initrd bootconfig are still silently ignored, as the
> initrd is only available after the early param window has closed.
>
> Document this behaviour in both Kconfig and the admin guide.
AI review made some comments. Some of the review comments seem
reasonable.
https://sashiko.dev/#/patchset/20260325-early_bootconfig-v2-1-6b05a36fbfb5%40debian.org
[..]
>
> diff --git a/init/main.c b/init/main.c
> index 453ac9dff2da0..14a04c283fa48 100644
> --- a/init/main.c
> +++ b/init/main.c
> @@ -416,9 +416,64 @@ static int __init warn_bootconfig(char *str)
> return 0;
> }
>
> +/*
> + * do_early_param() is defined later in this file but called from
> + * bootconfig_apply_early_params() below, so we need a forward declaration.
> + */
> +static int __init do_early_param(char *param, char *val,
> + const char *unused, void *arg);
> +
> +/*
> + * bootconfig_apply_early_params - dispatch kernel.* keys from the embedded
> + * bootconfig as early_param() calls.
> + *
> + * early_param() handlers must run before most of the kernel initialises
> + * (e.g. before the GIC driver reads irqchip.gicv3_pseudo_nmi). A bootconfig
> + * attached to the initrd arrives too late for this because the initrd is not
> + * mapped yet when early params are processed. The embedded bootconfig lives
> + * in the kernel image itself (.init.data), so it is always reachable.
> + *
> + * This function is called from setup_boot_config() which runs in
> + * start_kernel() before parse_early_param(), making the timing correct.
> + */
> +static void __init bootconfig_apply_early_params(void)
[sashiko comment]
| Does this run early enough for architectural parameters?
| While setup_boot_config() runs before parse_early_param() in start_kernel(),
| it runs after setup_arch(). setup_boot_config() relies on xbc_init() which
| uses the memblock allocator, requiring setup_arch() to have already
| initialized it.
| However, the kernel expects many early parameters (like mem=, earlycon,
| noapic, and iommu) to be parsed during setup_arch() via the architecture's
| call to parse_early_param(). Since setup_arch() completes before
| setup_boot_config() runs, will these architectural early parameters be
| silently ignored because the decisions they influence were already
| finalized?
This is the major reason that I did not support early parameter
in bootconfig. Some archs initialize kernel_cmdline in setup_arch()
and setup early parameters in it.
To fix this, we need to change setup_arch() for each architecture so
that it calls this bootconfig_apply_early_params().
> +{
> + static char val_buf[COMMAND_LINE_SIZE] __initdata;
[sashiko comment]
| Can using a single shared static buffer cause data corruption for handlers
| that save the argument pointer?
| Several early_param handlers assume the passed string pointer is persistent
| (like the boot_command_line) and retain it internally. For example,
| setup_earlycon() calls register_earlycon(), which sets
| early_console_dev.con->options = options, where options is a pointer
| directly into the passed buffer.
| Because val_buf is overwritten on every loop iteration, the stored pointer
| will point to the value of the last bootconfig key processed.
Ah, good catch. Since we don't have any standard way to handle the
parameters, some of them does not copy the value but try to keep
reference to the given string.
> + struct xbc_node *knode, *root;
> + const char *val;
> + ssize_t ret;
> +
> + root = xbc_find_node("kernel");
> + if (!root)
> + return;
> +
> + /*
> + * Keys that do not match any early_param() handler are silently
> + * ignored — do_early_param() always returns 0.
> + */
> + xbc_node_for_each_key_value(root, knode, val) {
[sashiko comment]
| Does this loop handle array values correctly?
| xbc_node_for_each_key_value() only assigns the first value of an array to
| the val pointer before advancing to the next key. It does not iterate over
| the child nodes of the array.
| If the bootconfig contains a multi-value key like
| kernel.console = "ttyS0", "tty0", will the subsequent values in the array
| be silently dropped instead of passed to the early_param handlers?
Also, good catch :) we need to use xbc_node_for_each_array_value()
for inner loop.
> + if (xbc_node_compose_key_after(root, knode, xbc_namebuf, XBC_KEYLEN_MAX) < 0)
> + continue;
> +
> + /*
> + * We need to copy const char *val to a char pointer,
> + * which is what do_early_param() need, given it might
> + * call strsep(), strtok() later.
> + */
> + ret = strscpy(val_buf, val, sizeof(val_buf));
> + if (ret < 0) {
> + pr_warn("ignoring bootconfig value '%s', too long\n",
> + xbc_namebuf);
> + continue;
> + }
> + do_early_param(xbc_namebuf, val_buf, NULL, NULL);
[sashiko comment]
| How does this handle valueless parameters (boolean flags)?
| When parsing the standard kernel command line, parse_args() passes a NULL
| value to the setup function for flags that lack an = sign (e.g., ro or
| earlycon).
| However, the bootconfig parser returns a zero-length string for valueless
| keys, which gets copied into val_buf as "" and passed to do_early_param().
| This semantic deviation breaks handlers that explicitly check if (!val).
| For instance, param_setup_earlycon() and parse_lapic() check for a NULL
| argument to enable features. Will passing "" instead of NULL prevent these
| handlers from working correctly?
See fs/proc/bootconfig.c. You can check whether the key has a value or
not by checking xbc_node_get_child(knode) != NULL.
Thank you,
> + }
> +}
> +
> static void __init setup_boot_config(void)
> {
> static char tmp_cmdline[COMMAND_LINE_SIZE] __initdata;
> + bool using_embedded = false;
> const char *msg, *data;
> int pos, ret;
> size_t size;
> @@ -427,8 +482,17 @@ static void __init setup_boot_config(void)
> /* 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);
> + /*
> + * Record that we are using the embedded config so that
> + * bootconfig_apply_early_params() is called below.
> + * When CONFIG_BOOT_CONFIG_EMBED is not set,
> + * xbc_get_embedded_bootconfig() is a stub returning NULL, so
> + * data is always NULL here and using_embedded stays false.
> + */
> + using_embedded = data;
> + }
>
> strscpy(tmp_cmdline, boot_command_line, COMMAND_LINE_SIZE);
> err = parse_args("bootconfig", tmp_cmdline, NULL, 0, 0, 0, NULL,
> @@ -466,6 +530,8 @@ 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);
> + if (using_embedded)
> + bootconfig_apply_early_params();
> /* keys starting with "kernel." are passed via cmdline */
> extra_command_line = xbc_make_cmdline("kernel");
> /* Also, "init." keys are init arguments */
>
> ---
> base-commit: 785f0eb2f85decbe7c1ef9ae922931f0194ffc2e
> change-id: 20260323-early_bootconfig-2efc4509af3d
>
> Best regards,
> --
> Breno Leitao <leitao@debian.org>
>
--
Masami Hiramatsu (Google) <mhiramat@kernel.org>
On Wed, Mar 25, 2026 at 11:22:04PM +0900, Masami Hiramatsu wrote: > > diff --git a/init/main.c b/init/main.c > > index 453ac9dff2da0..14a04c283fa48 100644 > > --- a/init/main.c > > +++ b/init/main.c > > @@ -416,9 +416,64 @@ static int __init warn_bootconfig(char *str) > > return 0; > > } > > > > +/* > > + * do_early_param() is defined later in this file but called from > > + * bootconfig_apply_early_params() below, so we need a forward declaration. > > + */ > > +static int __init do_early_param(char *param, char *val, > > + const char *unused, void *arg); > > + > > +/* > > + * bootconfig_apply_early_params - dispatch kernel.* keys from the embedded > > + * bootconfig as early_param() calls. > > + * > > + * early_param() handlers must run before most of the kernel initialises > > + * (e.g. before the GIC driver reads irqchip.gicv3_pseudo_nmi). A bootconfig > > + * attached to the initrd arrives too late for this because the initrd is not > > + * mapped yet when early params are processed. The embedded bootconfig lives > > + * in the kernel image itself (.init.data), so it is always reachable. > > + * > > + * This function is called from setup_boot_config() which runs in > > + * start_kernel() before parse_early_param(), making the timing correct. > > + */ > > +static void __init bootconfig_apply_early_params(void) > > [sashiko comment] > | Does this run early enough for architectural parameters? > | While setup_boot_config() runs before parse_early_param() in start_kernel(), > | it runs after setup_arch(). setup_boot_config() relies on xbc_init() which > | uses the memblock allocator, requiring setup_arch() to have already > | initialized it. > | However, the kernel expects many early parameters (like mem=, earlycon, > | noapic, and iommu) to be parsed during setup_arch() via the architecture's > | call to parse_early_param(). Since setup_arch() completes before > | setup_boot_config() runs, will these architectural early parameters be > | silently ignored because the decisions they influence were already > | finalized? > > This is the major reason that I did not support early parameter > in bootconfig. Some archs initialize kernel_cmdline in setup_arch() > and setup early parameters in it. > To fix this, we need to change setup_arch() for each architecture so > that it calls this bootconfig_apply_early_params(). Hi Masami, I have a question about bootconfig design. Is it necessary to parse the bootconfig at boot time? We could avoid a lot of complexity if we flattened the bootconfig into a simple key-value list before embedding it in the kernel image or attaching it to the initrd. That would eliminate the need for memory allocations and allow the config to be used earlier during boot. What am I missing? -- Kiryl Shutsemau / Kirill A. Shutemov
On Tue, 31 Mar 2026 11:18:33 +0100 Kiryl Shutsemau <kirill@shutemov.name> wrote: > On Wed, Mar 25, 2026 at 11:22:04PM +0900, Masami Hiramatsu wrote: > > > diff --git a/init/main.c b/init/main.c > > > index 453ac9dff2da0..14a04c283fa48 100644 > > > --- a/init/main.c > > > +++ b/init/main.c > > > @@ -416,9 +416,64 @@ static int __init warn_bootconfig(char *str) > > > return 0; > > > } > > > > > > +/* > > > + * do_early_param() is defined later in this file but called from > > > + * bootconfig_apply_early_params() below, so we need a forward declaration. > > > + */ > > > +static int __init do_early_param(char *param, char *val, > > > + const char *unused, void *arg); > > > + > > > +/* > > > + * bootconfig_apply_early_params - dispatch kernel.* keys from the embedded > > > + * bootconfig as early_param() calls. > > > + * > > > + * early_param() handlers must run before most of the kernel initialises > > > + * (e.g. before the GIC driver reads irqchip.gicv3_pseudo_nmi). A bootconfig > > > + * attached to the initrd arrives too late for this because the initrd is not > > > + * mapped yet when early params are processed. The embedded bootconfig lives > > > + * in the kernel image itself (.init.data), so it is always reachable. > > > + * > > > + * This function is called from setup_boot_config() which runs in > > > + * start_kernel() before parse_early_param(), making the timing correct. > > > + */ > > > +static void __init bootconfig_apply_early_params(void) > > > > [sashiko comment] > > | Does this run early enough for architectural parameters? > > | While setup_boot_config() runs before parse_early_param() in start_kernel(), > > | it runs after setup_arch(). setup_boot_config() relies on xbc_init() which > > | uses the memblock allocator, requiring setup_arch() to have already > > | initialized it. > > | However, the kernel expects many early parameters (like mem=, earlycon, > > | noapic, and iommu) to be parsed during setup_arch() via the architecture's > > | call to parse_early_param(). Since setup_arch() completes before > > | setup_boot_config() runs, will these architectural early parameters be > > | silently ignored because the decisions they influence were already > > | finalized? > > > > This is the major reason that I did not support early parameter > > in bootconfig. Some archs initialize kernel_cmdline in setup_arch() > > and setup early parameters in it. > > To fix this, we need to change setup_arch() for each architecture so > > that it calls this bootconfig_apply_early_params(). > > Hi Masami, > > I have a question about bootconfig design. Is it necessary to parse the > bootconfig at boot time? > > We could avoid a lot of complexity if we flattened the bootconfig into a > simple key-value list before embedding it in the kernel image or > attaching it to the initrd. That would eliminate the need for memory > allocations and allow the config to be used earlier during boot. Hi Kiryl, Thanks for a good question. If it is embedded in the kernel, yes, we can compile it. But if it is attached to initrd, the kernel needs to verify it. So anyway we have to parse the key-value. Of course we can make it a binary data structure, but it still needs verification. Moreover, memory allocation is not required by design (the first design uses a statically allocated data). Even if it is normalized as key-value, we can not trust the contents without outer verification system, like verified boot. Therefore, our basic strategy is to parse the text. However, if the content can be verified externally, for example, if a verified boot program verifies the compiled boot configuration, then it would be possible to use that directly. Thank you, > > What am I missing? > > -- > Kiryl Shutsemau / Kirill A. Shutemov -- Masami Hiramatsu (Google) <mhiramat@kernel.org>
On Wed, Apr 01, 2026 at 10:02:37AM +0900, Masami Hiramatsu wrote: > On Tue, 31 Mar 2026 11:18:33 +0100 > Kiryl Shutsemau <kirill@shutemov.name> wrote: > > > On Wed, Mar 25, 2026 at 11:22:04PM +0900, Masami Hiramatsu wrote: > > > > diff --git a/init/main.c b/init/main.c > > > > index 453ac9dff2da0..14a04c283fa48 100644 > > > > --- a/init/main.c > > > > +++ b/init/main.c > > > > @@ -416,9 +416,64 @@ static int __init warn_bootconfig(char *str) > > > > return 0; > > > > } > > > > > > > > +/* > > > > + * do_early_param() is defined later in this file but called from > > > > + * bootconfig_apply_early_params() below, so we need a forward declaration. > > > > + */ > > > > +static int __init do_early_param(char *param, char *val, > > > > + const char *unused, void *arg); > > > > + > > > > +/* > > > > + * bootconfig_apply_early_params - dispatch kernel.* keys from the embedded > > > > + * bootconfig as early_param() calls. > > > > + * > > > > + * early_param() handlers must run before most of the kernel initialises > > > > + * (e.g. before the GIC driver reads irqchip.gicv3_pseudo_nmi). A bootconfig > > > > + * attached to the initrd arrives too late for this because the initrd is not > > > > + * mapped yet when early params are processed. The embedded bootconfig lives > > > > + * in the kernel image itself (.init.data), so it is always reachable. > > > > + * > > > > + * This function is called from setup_boot_config() which runs in > > > > + * start_kernel() before parse_early_param(), making the timing correct. > > > > + */ > > > > +static void __init bootconfig_apply_early_params(void) > > > > > > [sashiko comment] > > > | Does this run early enough for architectural parameters? > > > | While setup_boot_config() runs before parse_early_param() in start_kernel(), > > > | it runs after setup_arch(). setup_boot_config() relies on xbc_init() which > > > | uses the memblock allocator, requiring setup_arch() to have already > > > | initialized it. > > > | However, the kernel expects many early parameters (like mem=, earlycon, > > > | noapic, and iommu) to be parsed during setup_arch() via the architecture's > > > | call to parse_early_param(). Since setup_arch() completes before > > > | setup_boot_config() runs, will these architectural early parameters be > > > | silently ignored because the decisions they influence were already > > > | finalized? > > > > > > This is the major reason that I did not support early parameter > > > in bootconfig. Some archs initialize kernel_cmdline in setup_arch() > > > and setup early parameters in it. > > > To fix this, we need to change setup_arch() for each architecture so > > > that it calls this bootconfig_apply_early_params(). > > > > Hi Masami, > > > > I have a question about bootconfig design. Is it necessary to parse the > > bootconfig at boot time? > > > > We could avoid a lot of complexity if we flattened the bootconfig into a > > simple key-value list before embedding it in the kernel image or > > attaching it to the initrd. That would eliminate the need for memory > > allocations and allow the config to be used earlier during boot. > > Hi Kiryl, > > Thanks for a good question. > > If it is embedded in the kernel, yes, we can compile it. But if it is > attached to initrd, the kernel needs to verify it. So anyway we have to > parse the key-value. Of course we can make it a binary data structure, > but it still needs verification. Moreover, memory allocation is not > required by design (the first design uses a statically allocated data). > > Even if it is normalized as key-value, we can not trust the contents > without outer verification system, like verified boot. Therefore, our > basic strategy is to parse the text. Hm. I am not sure what issues verification aims to solve. Could you elaborate. With normalized key-value structure, we should be able to extract the needed value in the same way as with normal kernel command line, no? -- Kiryl Shutsemau / Kirill A. Shutemov
Hi Masami, On Wed, Mar 25, 2026 at 11:22:04PM +0900, Masami Hiramatsu wrote: > On Wed, 25 Mar 2026 03:05:38 -0700 > Breno Leitao <leitao@debian.org> wrote: > > +/* > > + * bootconfig_apply_early_params - dispatch kernel.* keys from the embedded > > + * bootconfig as early_param() calls. > > + * > > + * early_param() handlers must run before most of the kernel initialises > > + * (e.g. before the GIC driver reads irqchip.gicv3_pseudo_nmi). A bootconfig > > + * attached to the initrd arrives too late for this because the initrd is not > > + * mapped yet when early params are processed. The embedded bootconfig lives > > + * in the kernel image itself (.init.data), so it is always reachable. > > + * > > + * This function is called from setup_boot_config() which runs in > > + * start_kernel() before parse_early_param(), making the timing correct. > > + */ > > +static void __init bootconfig_apply_early_params(void) > > [sashiko comment] > | Does this run early enough for architectural parameters? > | While setup_boot_config() runs before parse_early_param() in start_kernel(), > | it runs after setup_arch(). setup_boot_config() relies on xbc_init() which > | uses the memblock allocator, requiring setup_arch() to have already > | initialized it. > | However, the kernel expects many early parameters (like mem=, earlycon, > | noapic, and iommu) to be parsed during setup_arch() via the architecture's > | call to parse_early_param(). Since setup_arch() completes before > | setup_boot_config() runs, will these architectural early parameters be > | silently ignored because the decisions they influence were already > | finalized? > > This is the major reason that I did not support early parameter > in bootconfig. Some archs initialize kernel_cmdline in setup_arch() > and setup early parameters in it. Would it be feasible to document which parameters are architecture-specific and must be processed during setup_arch()? We could potentially introduce a third parameter category alongside the existing early_param() and __setup(): * early_param() * __setup() * early_arch_param() (New) This would allow bootconfig to support __setup() and early_param() while explicitly excluding early_arch_param() from bootconfig processing. This would move break down the early parameters in those that can be easily handled. > To fix this, we need to change setup_arch() for each architecture so > that it calls this bootconfig_apply_early_params(). Could we instead integrate this into parse_early_param() itself? That approach would avoid the need to modify each architecture individually. Thanks for looking at it, --breno
On Fri, 27 Mar 2026 03:06:41 -0700 Breno Leitao <leitao@debian.org> wrote: > Hi Masami, > > On Wed, Mar 25, 2026 at 11:22:04PM +0900, Masami Hiramatsu wrote: > > On Wed, 25 Mar 2026 03:05:38 -0700 > > Breno Leitao <leitao@debian.org> wrote: > > > > +/* > > > + * bootconfig_apply_early_params - dispatch kernel.* keys from the embedded > > > + * bootconfig as early_param() calls. > > > + * > > > + * early_param() handlers must run before most of the kernel initialises > > > + * (e.g. before the GIC driver reads irqchip.gicv3_pseudo_nmi). A bootconfig > > > + * attached to the initrd arrives too late for this because the initrd is not > > > + * mapped yet when early params are processed. The embedded bootconfig lives > > > + * in the kernel image itself (.init.data), so it is always reachable. > > > + * > > > + * This function is called from setup_boot_config() which runs in > > > + * start_kernel() before parse_early_param(), making the timing correct. > > > + */ > > > +static void __init bootconfig_apply_early_params(void) > > > > [sashiko comment] > > | Does this run early enough for architectural parameters? > > | While setup_boot_config() runs before parse_early_param() in start_kernel(), > > | it runs after setup_arch(). setup_boot_config() relies on xbc_init() which > > | uses the memblock allocator, requiring setup_arch() to have already > > | initialized it. > > | However, the kernel expects many early parameters (like mem=, earlycon, > > | noapic, and iommu) to be parsed during setup_arch() via the architecture's > > | call to parse_early_param(). Since setup_arch() completes before > > | setup_boot_config() runs, will these architectural early parameters be > > | silently ignored because the decisions they influence were already > > | finalized? > > > > This is the major reason that I did not support early parameter > > in bootconfig. Some archs initialize kernel_cmdline in setup_arch() > > and setup early parameters in it. > > Would it be feasible to document which parameters are architecture-specific > and must be processed during setup_arch()? Yeah, at least we can mark what is not available in bootconfig. Or, maybe we can export this function to setup_arch() for each architecture. Anyway, some cmdline options are not possible to be passed via bootconfig. IIRC, for example, the initrd image address is passed via cmdline (via devicetree) on arm64 from bootloader. > > We could potentially introduce a third parameter category alongside the > existing early_param() and __setup(): > > * early_param() > * __setup() > * early_arch_param() (New) > > This would allow bootconfig to support __setup() and early_param() while > explicitly excluding early_arch_param() from bootconfig processing. Yeah, that maybe possible. > > This would move break down the early parameters in those that can be > easily handled. > > > To fix this, we need to change setup_arch() for each architecture so > > that it calls this bootconfig_apply_early_params(). > > Could we instead integrate this into parse_early_param() itself? That > approach would avoid the need to modify each architecture individually. Ah, indeed. Thanks! > > Thanks for looking at it, > --breno -- Masami Hiramatsu (Google) <mhiramat@kernel.org>
On Fri, Mar 27, 2026 at 10:37:44PM +0900, Masami Hiramatsu wrote: > On Fri, 27 Mar 2026 03:06:41 -0700 > Breno Leitao <leitao@debian.org> wrote: > > > To fix this, we need to change setup_arch() for each architecture so > > > that it calls this bootconfig_apply_early_params(). > > > > Could we instead integrate this into parse_early_param() itself? That > > approach would avoid the need to modify each architecture individually. > > Ah, indeed. I investigated integrating bootconfig into parse_early_param() and hit a blocker: xbc_init() and xbc_make_cmdline() depend on memblock_alloc(), but on most architectures (x86, arm64, arm, s390, riscv) parse_early_param() is called from setup_arch() _before_ memblock is initialized. So, bootconfig will not be available as early as parse_early_param(). An alternative is replace memblock allocations in lib/bootconfig.c with static __initdata buffers, similar to Petr's approach in 2023: https://lore.kernel.org/all/20231121231342.193646-3-oss@malat.biz/ But, there was concerns about the allocation size: Petr Malat <oss@malat.biz> wrote: > To allow handling of early options, it's necessary to eliminate allocations > from embedded bootconfig handling "Hm, my concern is that this can introduce some sort of overhead to parse the bootconfig."
On Mon, 30 Mar 2026 06:15:17 -0700 Breno Leitao <leitao@debian.org> wrote: > On Fri, Mar 27, 2026 at 10:37:44PM +0900, Masami Hiramatsu wrote: > > On Fri, 27 Mar 2026 03:06:41 -0700 > > Breno Leitao <leitao@debian.org> wrote: > > > > > To fix this, we need to change setup_arch() for each architecture so > > > > that it calls this bootconfig_apply_early_params(). > > > > > > Could we instead integrate this into parse_early_param() itself? That > > > approach would avoid the need to modify each architecture individually. > > > > Ah, indeed. > > I investigated integrating bootconfig into parse_early_param() and hit a > blocker: xbc_init() and xbc_make_cmdline() depend on memblock_alloc(), but on > most architectures (x86, arm64, arm, s390, riscv) parse_early_param() is called > from setup_arch() _before_ memblock is initialized. Yeah, that's right. > > So, bootconfig will not be available as early as parse_early_param(). > > An alternative is replace memblock allocations in lib/bootconfig.c with static > __initdata buffers, similar to Petr's approach in 2023: > > https://lore.kernel.org/all/20231121231342.193646-3-oss@malat.biz/ > > But, there was concerns about the allocation size: > > Petr Malat <oss@malat.biz> wrote: > > To allow handling of early options, it's necessary to eliminate allocations > > from embedded bootconfig handling > > "Hm, my concern is that this can introduce some sort of overhead to parse the bootconfig." > As far as we can correctly handle the early params and it is limited only with the embedded bootconfig, I think it is OK to allocate it statically. Thank you, -- Masami Hiramatsu (Google) <mhiramat@kernel.org>
On Mon, Mar 30, 2026 at 06:15:17AM -0700, Breno Leitao wrote:
> On Fri, Mar 27, 2026 at 10:37:44PM +0900, Masami Hiramatsu wrote:
> > On Fri, 27 Mar 2026 03:06:41 -0700
> > Breno Leitao <leitao@debian.org> wrote:
>
> > > > To fix this, we need to change setup_arch() for each architecture so
> > > > that it calls this bootconfig_apply_early_params().
> > >
> > > Could we instead integrate this into parse_early_param() itself? That
> > > approach would avoid the need to modify each architecture individually.
> >
> > Ah, indeed.
>
> I investigated integrating bootconfig into parse_early_param() and hit a
> blocker: xbc_init() and xbc_make_cmdline() depend on memblock_alloc(), but on
> most architectures (x86, arm64, arm, s390, riscv) parse_early_param() is called
> from setup_arch() _before_ memblock is initialized.
That said, I'd like to propose a simpler approach as a first step:
1) Keep calling bootconfig_apply_early_params() from setup_boot_config().
This is the least intrusive approach and expands bootconfig support to
additional early boot parameters.
2) Document that architecture-specific early parameters might be ignored.
If a parameter is consumed early enough (during setup_arch()), it will
not see the bootconfig value.
3) Ensure that early bootconfig parameters don't overwrite the boot command
line. For example, if the boot command line has foo=bar and bootconfig
later has foo=baz, the command line value should take precedence.
This prevents early boot code (in setup_arch()) from seeing a parameter
value that will be changed later.
If that is OK, that is what I have right now:
commit dd6e00e41c381e5fef9d22dda02b104aa8f83101
Author: Breno Leitao <leitao@debian.org>
Date: Mon Mar 30 06:50:28 2026 -0700
bootconfig: Apply early options from embedded config
Bootconfig currently cannot apply early kernel parameters. For example,
the "mitigations=" parameter must be passed through traditional boot
methods because bootconfig parsing happens after these early parameters
need to be processed.
Add bootconfig_apply_early_params() which walks all kernel.* keys in the
parsed XBC tree and calls do_early_param() for each one. It is called
from setup_boot_config() immediately after a successful xbc_init() on
the embedded data, which happens before parse_early_param() runs in
start_kernel().
This allows early options such as:
kernel.mitigations = off
to be placed in the embedded bootconfig and take effect, without
requiring them on the kernel command line.
If the same parameter appears on both the kernel command line and in
the embedded bootconfig, the command-line value takes precedence:
bootconfig_apply_early_params() checks boot_command_line and skips
any parameter already present there.
Known limitations are documented:
- Early options in initrd bootconfig are still silently ignored, as the
initrd is only available after the early param window has closed.
- Arch-specific early params consumed during setup_arch() (e.g. mem=,
earlycon, noapic) may not take effect from bootconfig.
Signed-off-by: Breno Leitao <leitao@debian.org>
diff --git a/Documentation/admin-guide/bootconfig.rst b/Documentation/admin-guide/bootconfig.rst
index f712758472d5c..6ed852a0c66d8 100644
--- a/Documentation/admin-guide/bootconfig.rst
+++ b/Documentation/admin-guide/bootconfig.rst
@@ -169,6 +169,15 @@ Boot Kernel With a Boot Config
There are two options to boot the kernel with bootconfig: attaching the
bootconfig to the initrd image or embedding it in the kernel itself.
+Early options (those registered with ``early_param()``) may only be
+specified in the embedded bootconfig, because the initrd is not yet
+available when early parameters are processed.
+
+Note that embedded bootconfig is parsed after ``setup_arch()``, so
+early options that are consumed during architecture initialization
+(e.g., ``mem=``, ``memmap=``, ``earlycon``, ``noapic``, ``nolapic``,
+``acpi=``, ``numa=``, ``iommu=``) may not take effect from bootconfig.
+
Attaching a Boot Config to Initrd
---------------------------------
diff --git a/init/Kconfig b/init/Kconfig
index 7484cd703bc1a..34adcc1feb9b6 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -1525,6 +1525,16 @@ config BOOT_CONFIG_EMBED
image. But if the system doesn't support initrd, this option will
help you by embedding a bootconfig file while building the kernel.
+ Unlike bootconfig attached to initrd, the embedded bootconfig also
+ supports early options (those registered with early_param()). Any
+ kernel.* key in the embedded bootconfig is applied before
+ parse_early_param() runs. Early options in initrd bootconfig will
+ not be applied. Early options consumed during setup_arch() (e.g.
+ mem=, memmap=, earlycon, noapic, acpi=, numa=, iommu=) may not
+ take effect. If the same early option
+ appears in both bootconfig and the kernel command line, the
+ command line value takes precedence.
+
If unsure, say N.
config BOOT_CONFIG_EMBED_FILE
diff --git a/init/main.c b/init/main.c
index 1cb395dd94e43..487fe86ab5c09 100644
--- a/init/main.c
+++ b/init/main.c
@@ -414,10 +414,112 @@ static int __init warn_bootconfig(char *str)
return 0;
}
+/*
+ * do_early_param() is defined later in this file but called from
+ * bootconfig_apply_early_params() below, so we need a forward declaration.
+ */
+static int __init do_early_param(char *param, char *val,
+ const char *unused, void *arg);
+
+/*
+ * Check if a parameter name appears on the kernel command line.
+ * Returns true if the parameter was explicitly passed by the bootloader.
+ */
+static bool __init cmdline_has_param(const char *param)
+{
+ const char *p = boot_command_line;
+ int len = strlen(param);
+
+ while ((p = strstr(p, param)) != NULL) {
+ /* Check it's a whole-word match: preceded by space/start */
+ if (p != boot_command_line && *(p - 1) != ' ') {
+ p += len;
+ continue;
+ }
+ /* Followed by =, space, or end of string */
+ if (p[len] == '=' || p[len] == ' ' || p[len] == '\0')
+ return true;
+ p += len;
+ }
+ return false;
+}
+
+/*
+ * bootconfig_apply_early_params - apply kernel.* keys from the embedded
+ * bootconfig as early_param() calls.
+ *
+ * early_param() handlers run before most of the kernel initialises.
+ * A bootconfig attached to initrd arrives too late because the initrd is
+ * not mapped when early params are processed. The embedded bootconfig
+ * lives in the kernel image itself (.init.data), so it is always
+ * reachable.
+ *
+ * Called from setup_boot_config() which runs before parse_early_param()
+ * in start_kernel(), but after setup_arch(). Arch-specific early params
+ * parsed during setup_arch() will not see bootconfig values.
+ */
+static void __init bootconfig_apply_early_params(void)
+{
+ struct xbc_node *knode, *vnode, *root;
+ const char *val;
+ char *val_copy;
+
+ root = xbc_find_node("kernel");
+ if (!root)
+ return;
+
+ xbc_node_for_each_key_value(root, knode, val) {
+ if (xbc_node_compose_key_after(root, knode,
+ xbc_namebuf,
+ XBC_KEYLEN_MAX) < 0)
+ continue;
+
+ /* Command-line values take precedence over bootconfig */
+ if (cmdline_has_param(xbc_namebuf)) {
+ pr_info("bootconfig: skipping '%s', already on command line\n",
+ xbc_namebuf);
+ continue;
+ }
+
+ /* Boolean key with no value — pass NULL like parse_args() */
+ if (!xbc_node_get_child(knode)) {
+ do_early_param(xbc_namebuf, NULL, NULL, NULL);
+ continue;
+ }
+
+ /*
+ * Iterate array values: "foo = bar, buz" becomes two
+ * calls: do_early_param("foo", "bar") and
+ * do_early_param("foo", "buz").
+ */
+ vnode = xbc_node_get_child(knode);
+ xbc_array_for_each_value(vnode, val) {
+ /*
+ * Some early_param handlers save the pointer to
+ * val, so each value needs its own persistent
+ * copy. memblock is available here since we run
+ * after setup_arch(). These allocations are
+ * intentionally never freed because the handlers
+ * may retain references indefinitely.
+ */
+ val_copy = memblock_alloc(strlen(val) + 1,
+ SMP_CACHE_BYTES);
+ if (!val_copy) {
+ pr_err("Failed to allocate bootconfig value for '%s'\n",
+ xbc_namebuf);
+ continue;
+ }
+ strcpy(val_copy, val);
+ do_early_param(xbc_namebuf, val_copy, NULL, NULL);
+ }
+ }
+}
+
static void __init setup_boot_config(void)
{
static char tmp_cmdline[COMMAND_LINE_SIZE] __initdata;
const char *msg, *data;
+ bool embedded = false;
int pos, ret;
size_t size;
char *err;
@@ -425,8 +527,11 @@ static void __init setup_boot_config(void)
/* 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);
+ /* tag we have embedded data */
+ embedded = !!data;
+ }
strscpy(tmp_cmdline, boot_command_line, COMMAND_LINE_SIZE);
err = parse_args("bootconfig", tmp_cmdline, NULL, 0, 0, 0, NULL,
@@ -464,6 +569,8 @@ 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);
+ if (embedded)
+ bootconfig_apply_early_params();
/* keys starting with "kernel." are passed via cmdline */
extra_command_line = xbc_make_cmdline("kernel");
/* Also, "init." keys are init arguments */
On Mon, 30 Mar 2026 08:04:23 -0700
Breno Leitao <leitao@debian.org> wrote:
> On Mon, Mar 30, 2026 at 06:15:17AM -0700, Breno Leitao wrote:
> > On Fri, Mar 27, 2026 at 10:37:44PM +0900, Masami Hiramatsu wrote:
> > > On Fri, 27 Mar 2026 03:06:41 -0700
> > > Breno Leitao <leitao@debian.org> wrote:
> >
> > > > > To fix this, we need to change setup_arch() for each architecture so
> > > > > that it calls this bootconfig_apply_early_params().
> > > >
> > > > Could we instead integrate this into parse_early_param() itself? That
> > > > approach would avoid the need to modify each architecture individually.
> > >
> > > Ah, indeed.
> >
> > I investigated integrating bootconfig into parse_early_param() and hit a
> > blocker: xbc_init() and xbc_make_cmdline() depend on memblock_alloc(), but on
> > most architectures (x86, arm64, arm, s390, riscv) parse_early_param() is called
> > from setup_arch() _before_ memblock is initialized.
>
> That said, I'd like to propose a simpler approach as a first step:
>
> 1) Keep calling bootconfig_apply_early_params() from setup_boot_config().
> This is the least intrusive approach and expands bootconfig support to
> additional early boot parameters.
>
> 2) Document that architecture-specific early parameters might be ignored.
> If a parameter is consumed early enough (during setup_arch()), it will
> not see the bootconfig value.
We have to carefully do this because what parameter is arch specific or not
depends on architecture and undocumented.
How about introducing a new Kconfig, which supports early params by
embedded bootconfig?
>
> 3) Ensure that early bootconfig parameters don't overwrite the boot command
> line. For example, if the boot command line has foo=bar and bootconfig
> later has foo=baz, the command line value should take precedence.
> This prevents early boot code (in setup_arch()) from seeing a parameter
> value that will be changed later.
OK, this also needs to be considered. Currently we just pass the bootconfig
parameters right before bootloader given parameters as "extra_command_line"
if "bootconfig" in cmdline or CONFIG_BOOT_CONFIG_FORCE=y.
[boot_config(.kernel)]<command_line>[ -- [boot_config(.init)][init_command_line]]
This is currently expected behavior. The bootconfig parameters are
expected to be overridden by command_line or command_line are appended.
If we change this for early params, we also should change the expected
output of /proc/cmdline too. I think we have 2 options;
- As before, we expect the parameters provided by the boot configuration
to be processed first and then overridden later by the command line.
Or,
- ignore all parameters which is given from the command line, this also
updates existing setup_boot_config() (means xbc_snprint_cmdline() ).
Anyway, this behavior change will also be a bit critical... We have
to announce it.
>
>
> If that is OK, that is what I have right now:
Let me comment on it.
> diff --git a/Documentation/admin-guide/bootconfig.rst b/Documentation/admin-guide/bootconfig.rst
> index f712758472d5c..6ed852a0c66d8 100644
> --- a/Documentation/admin-guide/bootconfig.rst
> +++ b/Documentation/admin-guide/bootconfig.rst
> @@ -169,6 +169,15 @@ Boot Kernel With a Boot Config
> There are two options to boot the kernel with bootconfig: attaching the
> bootconfig to the initrd image or embedding it in the kernel itself.
>
> +Early options (those registered with ``early_param()``) may only be
> +specified in the embedded bootconfig, because the initrd is not yet
> +available when early parameters are processed.
> +
> +Note that embedded bootconfig is parsed after ``setup_arch()``, so
> +early options that are consumed during architecture initialization
> +(e.g., ``mem=``, ``memmap=``, ``earlycon``, ``noapic``, ``nolapic``,
> +``acpi=``, ``numa=``, ``iommu=``) may not take effect from bootconfig.
> +
This is easy to explain, but it's quite troublesome for users to
determine which parameters are unavailable. Currently we can identify
it by `git grep early_param -- arch/${ARCH}`. But it is setup in
setup_arch() we need to track the source code. (Or ask AI :))
> Attaching a Boot Config to Initrd
> ---------------------------------
>
> diff --git a/init/Kconfig b/init/Kconfig
> index 7484cd703bc1a..34adcc1feb9b6 100644
> --- a/init/Kconfig
> +++ b/init/Kconfig
> @@ -1525,6 +1525,16 @@ config BOOT_CONFIG_EMBED
> image. But if the system doesn't support initrd, this option will
> help you by embedding a bootconfig file while building the kernel.
>
> + Unlike bootconfig attached to initrd, the embedded bootconfig also
> + supports early options (those registered with early_param()). Any
> + kernel.* key in the embedded bootconfig is applied before
> + parse_early_param() runs. Early options in initrd bootconfig will
> + not be applied. Early options consumed during setup_arch() (e.g.
> + mem=, memmap=, earlycon, noapic, acpi=, numa=, iommu=) may not
> + take effect. If the same early option
> + appears in both bootconfig and the kernel command line, the
> + command line value takes precedence.
> +
As a compromise, how about making this a separate Kconfig?
config BOOT_CONFIG_EMBED_EARLY_PARAM
bool "Support early_params in embedded bootconfig"
and afterwards, we can introduce
depends on ARCH_BOOT_CONFIG_EMBED_EARLY_PARAM
Thank you,
--
Masami Hiramatsu (Google) <mhiramat@kernel.org>
hello Masami,
On Tue, Mar 31, 2026 at 12:58:27PM +0900, Masami Hiramatsu wrote:
> > 3) Ensure that early bootconfig parameters don't overwrite the boot command
> > line. For example, if the boot command line has foo=bar and bootconfig
> > later has foo=baz, the command line value should take precedence.
> > This prevents early boot code (in setup_arch()) from seeing a parameter
> > value that will be changed later.
>
> OK, this also needs to be considered. Currently we just pass the bootconfig
> parameters right before bootloader given parameters as "extra_command_line"
> if "bootconfig" in cmdline or CONFIG_BOOT_CONFIG_FORCE=y.
>
> [boot_config(.kernel)]<command_line>[ -- [boot_config(.init)][init_command_line]]
>
> This is currently expected behavior. The bootconfig parameters are
> expected to be overridden by command_line or command_line are appended.
That's correct, and I have no intention of changing this behavior. Here's
the current approach:
1) Early parameters from the bootloader are parsed first in setup_arch()
2) Subsequently, bootconfig_apply_early_params() is invoked. Any early
parameter that was already parsed from the bootloader (in setup_arch())
will be skipped at this stage.
> If we change this for early params, we also should change the expected
> output of /proc/cmdline too. I think we have 2 options;
>
> - As before, we expect the parameters provided by the boot configuration
> to be processed first and then overridden later by the command line.
>
> Or,
>
> - ignore all parameters which is given from the command line, this also
> updates existing setup_boot_config() (means xbc_snprint_cmdline() ).
>
> Anyway, this behavior change will also be a bit critical... We have
> to announce it.
As mentioned above, I don't anticipate any changes to existing behavior.
Bootconfig parsing remains unchanged. The only modification is that
bootconfig_apply_early_params() will skip any early config parameter
that's already present in the bootloader command line.
> > +Note that embedded bootconfig is parsed after ``setup_arch()``, so
> > +early options that are consumed during architecture initialization
> > +(e.g., ``mem=``, ``memmap=``, ``earlycon``, ``noapic``, ``nolapic``,
> > +``acpi=``, ``numa=``, ``iommu=``) may not take effect from bootconfig.
> > +
>
> This is easy to explain, but it's quite troublesome for users to
> determine which parameters are unavailable.
Agreed. This turned out to be significantly more complex than I
initially anticipated.
I'm uncertain whether we can accomplish this without examining every
early_parameter() implementation in depth.
> Currently we can identify
> it by `git grep early_param -- arch/${ARCH}`. But it is setup in
> setup_arch() we need to track the source code. (Or ask AI :))
The challenge extends beyond that. There are numerous early_parameter()
definitions scattered throughout the kernel that may or may not be
utilized by setup_arch().
For example, consider `early_param("mitigations", ..)` in
./kernel/cpu.c. This modifies the cpu_mitigations global variable, which
is referenced in various locations across different architectures.
It's worth noting that we have over 300 early_parameter() instances in
the kernel.
Given this, analyzing all these early parameters and examining each one
individually represents a substantial amount of work.
Are there alternative approaches? At this point, I'm leaning toward
breaking bootconfig's dependency on memblock, allowing us to invoke it
before setup_arch(). Is this the only practical solution available?!
Thanks,
--breno
Hi Breno,
On Tue, 31 Mar 2026 08:27:59 -0700
Breno Leitao <leitao@debian.org> wrote:
> hello Masami,
>
> On Tue, Mar 31, 2026 at 12:58:27PM +0900, Masami Hiramatsu wrote:
>
> > > 3) Ensure that early bootconfig parameters don't overwrite the boot command
> > > line. For example, if the boot command line has foo=bar and bootconfig
> > > later has foo=baz, the command line value should take precedence.
> > > This prevents early boot code (in setup_arch()) from seeing a parameter
> > > value that will be changed later.
> >
> > OK, this also needs to be considered. Currently we just pass the bootconfig
> > parameters right before bootloader given parameters as "extra_command_line"
> > if "bootconfig" in cmdline or CONFIG_BOOT_CONFIG_FORCE=y.
> >
> > [boot_config(.kernel)]<command_line>[ -- [boot_config(.init)][init_command_line]]
> >
> > This is currently expected behavior. The bootconfig parameters are
> > expected to be overridden by command_line or command_line are appended.
>
> That's correct, and I have no intention of changing this behavior. Here's
> the current approach:
>
> 1) Early parameters from the bootloader are parsed first in setup_arch()
>
> 2) Subsequently, bootconfig_apply_early_params() is invoked. Any early
> parameter that was already parsed from the bootloader (in setup_arch())
> will be skipped at this stage.
Ah, I meant if we skip these parameters, we should not show it in the
command line via extra_command_line. This is still a minor issue at this
point. It should find early parameters in kernel.* parameters and do not
show it in extra_command_line, because those parameters are ignored.
So it is better to make a separated patch to fix that.
For example, if we pass
kernel.mem=1G
via bootconfig, it will be shown in the /proc/cmdline, but it is
not applied. This can confuse user.
>
> > If we change this for early params, we also should change the expected
> > output of /proc/cmdline too. I think we have 2 options;
> >
> > - As before, we expect the parameters provided by the boot configuration
> > to be processed first and then overridden later by the command line.
> >
> > Or,
> >
> > - ignore all parameters which is given from the command line, this also
> > updates existing setup_boot_config() (means xbc_snprint_cmdline() ).
> >
> > Anyway, this behavior change will also be a bit critical... We have
> > to announce it.
>
> As mentioned above, I don't anticipate any changes to existing behavior.
> Bootconfig parsing remains unchanged. The only modification is that
> bootconfig_apply_early_params() will skip any early config parameter
> that's already present in the bootloader command line.
Yes, but it is just different from existing one.
Suppose that if we have "early" and "normal" keys in the kernel, those
are handled by early_param() and __setup() respectively.
If we use bootconfig, like
kernel {
early = bconf_val
normal = bconf_val
}
And passes "early=foo normal=bar" via cmdline.
In this case, the /proc/cmdline eventually has
"early=bconf_val normal=bconf_val early=foo normal=bar"
And the "normal" callback called with "bconf_val" and "bar" twice.
However, "early" callback will be called with "foo" only once.
That can confuse users too.
I believe it's important for the system to behave in a way that is
as close as possible to the user's mind model.
Because the behavior is inconsistent when multiple parameters with
the same name are specified on the kernel command line, it is
necessary to ensure that users can later look at it and infer what
happened.
I mean, if a parameter is skipped, it should not be printed at
/proc/cmdline, because it can mislead user (and maybe bug reporter)
when a problem happens.
>
> > > +Note that embedded bootconfig is parsed after ``setup_arch()``, so
> > > +early options that are consumed during architecture initialization
> > > +(e.g., ``mem=``, ``memmap=``, ``earlycon``, ``noapic``, ``nolapic``,
> > > +``acpi=``, ``numa=``, ``iommu=``) may not take effect from bootconfig.
> > > +
> >
> > This is easy to explain, but it's quite troublesome for users to
> > determine which parameters are unavailable.
>
> Agreed. This turned out to be significantly more complex than I
> initially anticipated.
Yeah, that's complicated.
>
> I'm uncertain whether we can accomplish this without examining every
> early_parameter() implementation in depth.
Agreed. My proposal is something like a divide and conquer approach.
Since these are implemented architecture by architecture, you need to
check the implementation for each architecture, and I think it's best
to implement them one by one using Kconfig.
>
> > Currently we can identify
> > it by `git grep early_param -- arch/${ARCH}`. But it is setup in
> > setup_arch() we need to track the source code. (Or ask AI :))
>
> The challenge extends beyond that. There are numerous early_parameter()
> definitions scattered throughout the kernel that may or may not be
> utilized by setup_arch().
>
> For example, consider `early_param("mitigations", ..)` in
> ./kernel/cpu.c. This modifies the cpu_mitigations global variable, which
> is referenced in various locations across different architectures.
>
> It's worth noting that we have over 300 early_parameter() instances in
> the kernel.
>
> Given this, analyzing all these early parameters and examining each one
> individually represents a substantial amount of work.
Yes, that may require a substantial amount of work. But to improve
the kernel framework around the parameter handling, eventually we
need to examine each early parameter.
>
> Are there alternative approaches? At this point, I'm leaning toward
> breaking bootconfig's dependency on memblock, allowing us to invoke it
> before setup_arch(). Is this the only practical solution available?!
Basically, the memblock dependency comes from allocating copy of data.
Only for the embedded bootconfig, we can just pass copy memory block
to the xbc_init(). Something like;
xbc_init() {
xbc_data = memblock_alloc();
memcpy(xbc_data, data);
__xbc_init(xbc_data);
}
embedded_xbc_init() {
__xbc_init(embedded_bootconfig_data);
}
Afterwards, we can pass mixture of embedded bootcofnigt and initrd
bootconfig data to parser again.
(But in this case, we must be careful not to override the early
parameters that we have already applied.)
Thank you,
--
Masami Hiramatsu (Google) <mhiramat@kernel.org>
On Wed, Apr 01, 2026 at 10:48:53PM +0900, Masami Hiramatsu wrote:
> > The challenge extends beyond that. There are numerous early_parameter()
> > definitions scattered throughout the kernel that may or may not be
> > utilized by setup_arch().
> >
> > For example, consider `early_param("mitigations", ..)` in
> > ./kernel/cpu.c. This modifies the cpu_mitigations global variable, which
> > is referenced in various locations across different architectures.
> >
> > It's worth noting that we have over 300 early_parameter() instances in
> > the kernel.
> >
> > Given this, analyzing all these early parameters and examining each one
> > individually represents a substantial amount of work.
>
> Yes, that may require a substantial amount of work. But to improve
> the kernel framework around the parameter handling, eventually we
> need to examine each early parameter.
I'm still uncertain about this approach. The goal is to identify and
categorize the early parameters that are parsed prior to bootconfig
initialization.
Moreover, this work could become obsolete if bootconfig's initialization
point shifts earlier or later in the boot sequence, necessitating
another comprehensive analysis.
Conversely, if we successfully move bootconfig initialization earlier
by breaking the dependency of memblock (assuming this is feasible), the
vast majority of early parameters would execute after bootconfig is
configured, eliminating the need for this extensive categorization work.
Please, feel free to tell what approach might be better for the project.
> > Are there alternative approaches? At this point, I'm leaning toward
> > breaking bootconfig's dependency on memblock, allowing us to invoke it
> > before setup_arch(). Is this the only practical solution available?!
>
> Basically, the memblock dependency comes from allocating copy of data.
> Only for the embedded bootconfig, we can just pass copy memory block
> to the xbc_init(). Something like;
>
> xbc_init() {
> xbc_data = memblock_alloc();
> memcpy(xbc_data, data);
> __xbc_init(xbc_data);
> }
>
> embedded_xbc_init() {
> __xbc_init(embedded_bootconfig_data);
> }
>
> Afterwards, we can pass mixture of embedded bootcofnigt and initrd
> bootconfig data to parser again.
>
> (But in this case, we must be careful not to override the early
> parameters that we have already applied.)
Do you have any additional recommendations if I proceed with this
approach?
Thank you for your detailed responses and insights.
--breno
On Wed, 25 Mar 2026 23:22:04 +0900
Masami Hiramatsu (Google) <mhiramat@kernel.org> wrote:
> > + /*
> > + * Keys that do not match any early_param() handler are silently
> > + * ignored — do_early_param() always returns 0.
> > + */
> > + xbc_node_for_each_key_value(root, knode, val) {
>
> [sashiko comment]
> | Does this loop handle array values correctly?
> | xbc_node_for_each_key_value() only assigns the first value of an array to
> | the val pointer before advancing to the next key. It does not iterate over
> | the child nodes of the array.
> | If the bootconfig contains a multi-value key like
> | kernel.console = "ttyS0", "tty0", will the subsequent values in the array
> | be silently dropped instead of passed to the early_param handlers?
>
> Also, good catch :) we need to use xbc_node_for_each_array_value()
> for inner loop.
FYI, xbc_snprint_cmdline() translates the arraied parameter as
multiple parameters. For example,
foo = bar, buz;
will be converted to
foo=bar foo=buz
Thus, I think we should do the same thing below;
>
> > + if (xbc_node_compose_key_after(root, knode, xbc_namebuf, XBC_KEYLEN_MAX) < 0)
> > + continue;
> > +
> > + /*
> > + * We need to copy const char *val to a char pointer,
> > + * which is what do_early_param() need, given it might
> > + * call strsep(), strtok() later.
> > + */
> > + ret = strscpy(val_buf, val, sizeof(val_buf));
> > + if (ret < 0) {
> > + pr_warn("ignoring bootconfig value '%s', too long\n",
> > + xbc_namebuf);
> > + continue;
> > + }
> > + do_early_param(xbc_namebuf, val_buf, NULL, NULL);
So instead of this;
xbc_array_for_each_value(vnode, val) {
do_early_param(xbc_namebuf, val, NULL, NULL);
}
Maybe it is a good timing to recondier unifying kernel cmdline and bootconfig
from API viewpoint.
Thanks,
--
Masami Hiramatsu (Google) <mhiramat@kernel.org>
On Thu, Mar 26, 2026 at 11:30:42PM +0900, Masami Hiramatsu wrote:
> On Wed, 25 Mar 2026 23:22:04 +0900
> Masami Hiramatsu (Google) <mhiramat@kernel.org> wrote:
>
> > > + /*
> > > + * Keys that do not match any early_param() handler are silently
> > > + * ignored — do_early_param() always returns 0.
> > > + */
> > > + xbc_node_for_each_key_value(root, knode, val) {
> >
> > [sashiko comment]
> > | Does this loop handle array values correctly?
> > | xbc_node_for_each_key_value() only assigns the first value of an array to
> > | the val pointer before advancing to the next key. It does not iterate over
> > | the child nodes of the array.
> > | If the bootconfig contains a multi-value key like
> > | kernel.console = "ttyS0", "tty0", will the subsequent values in the array
> > | be silently dropped instead of passed to the early_param handlers?
> >
> > Also, good catch :) we need to use xbc_node_for_each_array_value()
> > for inner loop.
>
> FYI, xbc_snprint_cmdline() translates the arraied parameter as
> multiple parameters. For example,
>
> foo = bar, buz;
>
> will be converted to
>
> foo=bar foo=buz
>
> Thus, I think we should do the same thing below;
>
> >
> > > + if (xbc_node_compose_key_after(root, knode, xbc_namebuf, XBC_KEYLEN_MAX) < 0)
> > > + continue;
> > > +
> > > + /*
> > > + * We need to copy const char *val to a char pointer,
> > > + * which is what do_early_param() need, given it might
> > > + * call strsep(), strtok() later.
> > > + */
> > > + ret = strscpy(val_buf, val, sizeof(val_buf));
> > > + if (ret < 0) {
> > > + pr_warn("ignoring bootconfig value '%s', too long\n",
> > > + xbc_namebuf);
> > > + continue;
> > > + }
> > > + do_early_param(xbc_namebuf, val_buf, NULL, NULL);
>
> So instead of this;
>
> xbc_array_for_each_value(vnode, val) {
> do_early_param(xbc_namebuf, val, NULL, NULL);
> }
>
> Maybe it is a good timing to recondier unifying kernel cmdline and bootconfig
> from API viewpoint.
I'm not familiar with the history on this topic. Has unifying the APIs been
previously considered and set aside?
Given all the feedback on this series, I see three types of issues to address:
1) Minor patch improvements
2) Architecture-specific super early parameters being parsed before bootconfig
is available
3) Unifying kernel cmdline and bootconfig interfaces
Which of these areas would you recommend I prioritize?
Thanks for the guidance,
--breno
On Fri, 27 Mar 2026 03:18:31 -0700
Breno Leitao <leitao@debian.org> wrote:
> On Thu, Mar 26, 2026 at 11:30:42PM +0900, Masami Hiramatsu wrote:
> > On Wed, 25 Mar 2026 23:22:04 +0900
> > Masami Hiramatsu (Google) <mhiramat@kernel.org> wrote:
> >
> > > > + /*
> > > > + * Keys that do not match any early_param() handler are silently
> > > > + * ignored — do_early_param() always returns 0.
> > > > + */
> > > > + xbc_node_for_each_key_value(root, knode, val) {
> > >
> > > [sashiko comment]
> > > | Does this loop handle array values correctly?
> > > | xbc_node_for_each_key_value() only assigns the first value of an array to
> > > | the val pointer before advancing to the next key. It does not iterate over
> > > | the child nodes of the array.
> > > | If the bootconfig contains a multi-value key like
> > > | kernel.console = "ttyS0", "tty0", will the subsequent values in the array
> > > | be silently dropped instead of passed to the early_param handlers?
> > >
> > > Also, good catch :) we need to use xbc_node_for_each_array_value()
> > > for inner loop.
> >
> > FYI, xbc_snprint_cmdline() translates the arraied parameter as
> > multiple parameters. For example,
> >
> > foo = bar, buz;
> >
> > will be converted to
> >
> > foo=bar foo=buz
> >
> > Thus, I think we should do the same thing below;
> >
> > >
> > > > + if (xbc_node_compose_key_after(root, knode, xbc_namebuf, XBC_KEYLEN_MAX) < 0)
> > > > + continue;
> > > > +
> > > > + /*
> > > > + * We need to copy const char *val to a char pointer,
> > > > + * which is what do_early_param() need, given it might
> > > > + * call strsep(), strtok() later.
> > > > + */
> > > > + ret = strscpy(val_buf, val, sizeof(val_buf));
> > > > + if (ret < 0) {
> > > > + pr_warn("ignoring bootconfig value '%s', too long\n",
> > > > + xbc_namebuf);
> > > > + continue;
> > > > + }
> > > > + do_early_param(xbc_namebuf, val_buf, NULL, NULL);
> >
> > So instead of this;
> >
> > xbc_array_for_each_value(vnode, val) {
> > do_early_param(xbc_namebuf, val, NULL, NULL);
> > }
> >
> > Maybe it is a good timing to recondier unifying kernel cmdline and bootconfig
> > from API viewpoint.
>
> I'm not familiar with the history on this topic. Has unifying the APIs been
> previously considered and set aside?
Previously I considered but I found some early parameters must be composed by
bootloaders, and they does not support bootconfig. Thus, I introduced
setup_boot_config() to compose kernel.* parameters into cmdline buffer.
>
> Given all the feedback on this series, I see three types of issues to address:
>
> 1) Minor patch improvements
> 2) Architecture-specific super early parameters being parsed before bootconfig
> is available
> 3) Unifying kernel cmdline and bootconfig interfaces
I think we can start with 1) for embedded bootconfig for this series
with using bootconfig in parse_early_param().
For 2), I think it needs to check which parameters are expected to
be passed by bootloaders, which does not care bootconfig currently.
For 3), eventually it may be need to change how kernel handle the
parameters. I think I need to introduce CONFIG_BOOT_CONFIG_EXPOSED
option which keeps the xbc_*() API and parsed data accessible after
boot (Remove __init) and exposed to modules, so that all modules
can use xbc_* to get parameters from bootconfig directly.
Thanks,
>
> Which of these areas would you recommend I prioritize?
>
> Thanks for the guidance,
> --breno
--
Masami Hiramatsu (Google) <mhiramat@kernel.org>
On Fri, Mar 27, 2026 at 11:16:30PM +0900, Masami Hiramatsu wrote: > > Given all the feedback on this series, I see three types of issues to address: > > > > 1) Minor patch improvements > > 2) Architecture-specific super early parameters being parsed before bootconfig > > is available > > 3) Unifying kernel cmdline and bootconfig interfaces > > I think we can start with 1) for embedded bootconfig for this series > with using bootconfig in parse_early_param(). Thanks for the clear direction. I'll work on integrating bootconfig into parse_early_param() to see what can be achieved and identify any potential blockers. I should be back soon with more fun. Thanks so far, --breno
© 2016 - 2026 Red Hat, Inc.