Bootconfig updates for v7.2

- bootconfig: Render kernel subtree as cmdline string at build time
   . bootconfig: move xbc_snprint_cmdline() to lib/bootconfig.c. Move the
     xbc_snprint_cmdline() function and its buffer from main.c to the shared
     lib/bootconfig.c parser library so it can be reused by userspace tools.
   . tools/bootconfig: render kernel.* subtree as cmdline string with -C. Add a
     new -C option to print the kernel.* subtree as a flat command-line string
     at build time, allowing early parameter injection without runtime parsing.
 -----BEGIN PGP SIGNATURE-----
 
 iQFPBAABCgA5FiEEh7BulGwFlgAOi5DV2/sHvwUrPxsFAmown+YbHG1hc2FtaS5o
 aXJhbWF0c3VAZ21haWwuY29tAAoJENv7B78FKz8blVEIAJpMmmEIjiiCIdAEfJKL
 MTZo8C7V8sX+N3jmeaMmQNjkVfQuBbc4ORUtaZdxBs3E8BznN/zDs3ujSXfzbCe5
 1Hc5A95g+ZXY+83ylCCAem6qTsWfYSN3j7oiyBx0CrRrXy7KupInE1BePMTg1DnZ
 cAas3RLn5Qjyzg/yKMpkJNgCV/HxBCIAOXOF3F00S5THU5F1/W6VU3s8BpCU2mJK
 nQXYGW7XfRkVhhQlkmBF5pfo5yPDeq7louxVCIw4AVJLHWIgxQ3v/d1wR24wu+kT
 bZfDnsq0FVGeyjtRiX6iqFVc/zkQWhWrEFMbY3JNwW9lq4PT6nMH1ss1fNC3Ub1i
 CJ8=
 =apxi
 -----END PGP SIGNATURE-----

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

Pull bootconfig updates from Masami Hiramatsu:

 - bootconfig: move xbc_snprint_cmdline() to lib/bootconfig.c

   Move the xbc_snprint_cmdline() function and its buffer from
   main.c to the shared lib/bootconfig.c parser library so it
   can be reused by userspace tools.

 - render kernel.* subtree as cmdline string with -C

   Add a new -C option to print the kernel.* subtree as a flat
   command-line string at build time, allowing early parameter
   injection without runtime parsing.

* tag 'bootconfig-v7.2' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace:
  tools/bootconfig: render kernel.* subtree as cmdline string with -C
  bootconfig: move xbc_snprint_cmdline() to lib/bootconfig.c
This commit is contained in:
Linus Torvalds 2026-06-16 17:29:24 +05:30
commit 4f7e89065e
4 changed files with 111 additions and 53 deletions

View File

@ -265,6 +265,9 @@ static inline struct xbc_node * __init xbc_node_get_subkey(struct xbc_node *node
int __init xbc_node_compose_key_after(struct xbc_node *root,
struct xbc_node *node, char *buf, size_t size);
/* Render key/value pairs under @root as a flat cmdline string */
int __init xbc_snprint_cmdline(char *buf, size_t size, struct xbc_node *root);
/**
* xbc_node_compose_key() - Compose full key string of the XBC node
* @node: An XBC node.

View File

@ -324,51 +324,6 @@ static void * __init get_boot_config_from_initrd(size_t *_size)
#ifdef CONFIG_BOOT_CONFIG
static char xbc_namebuf[XBC_KEYLEN_MAX] __initdata;
#define rest(dst, end) ((end) > (dst) ? (end) - (dst) : 0)
static int __init xbc_snprint_cmdline(char *buf, size_t size,
struct xbc_node *root)
{
struct xbc_node *knode, *vnode;
char *end = buf + size;
const char *val, *q;
int ret;
xbc_node_for_each_key_value(root, knode, val) {
ret = xbc_node_compose_key_after(root, knode,
xbc_namebuf, XBC_KEYLEN_MAX);
if (ret < 0)
return ret;
vnode = xbc_node_get_child(knode);
if (!vnode) {
ret = snprintf(buf, rest(buf, end), "%s ", xbc_namebuf);
if (ret < 0)
return ret;
buf += ret;
continue;
}
xbc_array_for_each_value(vnode, val) {
/*
* For prettier and more readable /proc/cmdline, only
* quote the value when necessary, i.e. when it contains
* whitespace.
*/
q = strpbrk(val, " \t\r\n") ? "\"" : "";
ret = snprintf(buf, rest(buf, end), "%s=%s%s%s ",
xbc_namebuf, q, val, q);
if (ret < 0)
return ret;
buf += ret;
}
}
return buf - (end - size);
}
#undef rest
/* Make an extra command line under given key word */
static char * __init xbc_make_cmdline(const char *key)
{

View File

@ -408,6 +408,62 @@ const char * __init xbc_node_find_next_key_value(struct xbc_node *root,
return ""; /* No value key */
}
static char xbc_namebuf[XBC_KEYLEN_MAX] __initdata;
#define rest(dst, end) ((end) > (dst) ? (end) - (dst) : 0)
/**
* xbc_snprint_cmdline() - Render bootconfig keys under @root as a cmdline string
* @buf: Destination buffer (may be NULL when @size is 0 to query the length)
* @size: Size of @buf in bytes
* @root: Subtree root whose key=value pairs should be rendered
*
* Walk all key/value pairs under @root and emit them as a space-separated
* cmdline string into @buf. Values containing whitespace are quoted with
* double quotes. Returns the number of bytes that would be written if @buf
* were large enough (matching snprintf semantics), or a negative errno on
* failure.
*/
int __init xbc_snprint_cmdline(char *buf, size_t size, struct xbc_node *root)
{
struct xbc_node *knode, *vnode;
char *end = buf + size;
const char *val, *q;
int ret;
xbc_node_for_each_key_value(root, knode, val) {
ret = xbc_node_compose_key_after(root, knode,
xbc_namebuf, XBC_KEYLEN_MAX);
if (ret < 0)
return ret;
vnode = xbc_node_get_child(knode);
if (!vnode) {
ret = snprintf(buf, rest(buf, end), "%s ", xbc_namebuf);
if (ret < 0)
return ret;
buf += ret;
continue;
}
xbc_array_for_each_value(vnode, val) {
/*
* For prettier and more readable /proc/cmdline, only
* quote the value when necessary, i.e. when it contains
* whitespace.
*/
q = strpbrk(val, " \t\r\n") ? "\"" : "";
ret = snprintf(buf, rest(buf, end), "%s=%s%s%s ",
xbc_namebuf, q, val, q);
if (ret < 0)
return ret;
buf += ret;
}
}
return buf - (end - size);
}
#undef rest
/* XBC parse and tree build */
static int __init xbc_init_node(struct xbc_node *node, char *data, uint16_t flag)

View File

@ -286,7 +286,41 @@ static int init_xbc_with_error(char *buf, int len)
return ret;
}
static int show_xbc(const char *path, bool list)
static int show_xbc_kernel_cmdline(void)
{
struct xbc_node *root;
char *buf = NULL;
int len, ret;
root = xbc_find_node("kernel");
if (!root)
return 0; /* no kernel.* keys: emit empty output */
len = xbc_snprint_cmdline(NULL, 0, root);
if (len < 0) {
pr_err("Failed to size cmdline output: %d\n", len);
return len;
}
if (len == 0)
return 0;
buf = malloc(len + 1);
if (!buf)
return -ENOMEM;
ret = xbc_snprint_cmdline(buf, len + 1, root);
if (ret < 0) {
pr_err("Failed to render cmdline output: %d\n", ret);
free(buf);
return ret;
}
fputs(buf, stdout);
free(buf);
return 0;
}
static int show_xbc(const char *path, bool list, bool render_cmdline)
{
int ret, fd;
char *buf = NULL;
@ -322,11 +356,14 @@ static int show_xbc(const char *path, bool list)
if (init_xbc_with_error(buf, ret) < 0)
goto out;
}
if (list)
if (render_cmdline)
ret = show_xbc_kernel_cmdline();
else if (list)
xbc_show_list();
else
xbc_show_compact_tree();
ret = 0;
if (ret > 0)
ret = 0;
out:
free(buf);
@ -488,7 +525,10 @@ static int usage(void)
" Options:\n"
" -a <config>: Apply boot config to initrd\n"
" -d : Delete boot config file from initrd\n"
" -l : list boot config in initrd or file\n\n"
" -l : list boot config in initrd or file\n"
" -C : render the kernel.* subtree as a flat cmdline\n"
" string (suitable for embedding in a kernel image)\n"
" and print it to stdout\n\n"
" If no option is given, show the bootconfig in the given file.\n");
return -1;
}
@ -497,10 +537,11 @@ int main(int argc, char **argv)
{
char *path = NULL;
char *apply = NULL;
bool render_cmdline = false;
bool delete = false, list = false;
int opt;
while ((opt = getopt(argc, argv, "hda:l")) != -1) {
while ((opt = getopt(argc, argv, "hda:lC")) != -1) {
switch (opt) {
case 'd':
delete = true;
@ -511,14 +552,17 @@ int main(int argc, char **argv)
case 'l':
list = true;
break;
case 'C':
render_cmdline = true;
break;
case 'h':
default:
return usage();
}
}
if ((apply && delete) || (delete && list) || (apply && list)) {
pr_err("Error: You can give one of -a, -d or -l at once.\n");
if ((!!apply + !!delete + !!list + !!render_cmdline) > 1) {
pr_err("Error: You can give one of -a, -d, -l or -C at once.\n");
return usage();
}
@ -534,5 +578,5 @@ int main(int argc, char **argv)
else if (delete)
return delete_xbc(path);
return show_xbc(path, list);
return show_xbc(path, list, render_cmdline);
}