Right not is impossible to set early parameters in bootconfig, which limits the usage of bootconfig to some critical parameters.
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 kernel.irqchip.gicv3_pseudo_nmi = 1 to be placed in the embedded bootconfig and take effect, without requiring them to be on the kernel command line. 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 <[email protected]> --- Documentation/admin-guide/bootconfig.rst | 4 ++ init/Kconfig | 6 +++ init/main.c | 67 +++++++++++++++++++++++++++++++- 3 files changed, 76 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..eba42b2351d47 100644 --- a/init/main.c +++ b/init/main.c @@ -416,9 +416,63 @@ 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) +{ + char val_buf[COMMAND_LINE_SIZE]; + struct xbc_node *knode, *root; + const char *val; + + 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; + + if (!val) { + do_early_param(xbc_namebuf, NULL, NULL, NULL); + 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. + */ + strscpy(val_buf, val, sizeof(val_buf)); + 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 +481,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 +529,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 <[email protected]>
