// SPDX-License-Identifier: GPL-2.0
/*
* Extra Boot Config
* Masami Hiramatsu <mhiramat@kernel.org>
*/
/*
* NOTE: This is only for tools/bootconfig, because tools/bootconfig will
* run the parser sanity test.
* This does NOT mean lib/bootconfig.c is available in the user space.
* However, if you change this file, please make sure the tools/bootconfig
* has no issue on building and running.
*/
#include <linux/bootconfig.h>
#ifdef __KERNEL__
#include <linux/bug.h>
#include <linux/ctype.h>
#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 */
extern __visible const char embedded_bootconfig_data[];
extern __visible const char embedded_bootconfig_data_end[];
const char * __init xbc_get_embedded_bootconfig(size_t *size)
{
*size = embedded_bootconfig_data_end - embedded_bootconfig_data;
return (*size) ? embedded_bootconfig_data : NULL;
}
#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,