diff options
Diffstat (limited to 'init/main.c')
-rw-r--r-- | init/main.c | 229 |
1 files changed, 212 insertions, 17 deletions
diff --git a/init/main.c b/init/main.c index d8c7e86c2d28..cc0ee4873419 100644 --- a/init/main.c +++ b/init/main.c @@ -28,6 +28,7 @@ #include <linux/initrd.h> #include <linux/memblock.h> #include <linux/acpi.h> +#include <linux/bootconfig.h> #include <linux/console.h> #include <linux/nmi.h> #include <linux/percpu.h> @@ -136,8 +137,10 @@ char __initdata boot_command_line[COMMAND_LINE_SIZE]; char *saved_command_line; /* Command line for parameter parsing */ static char *static_command_line; -/* Command line for per-initcall parameter parsing */ -static char *initcall_command_line; +/* Untouched extra command line */ +static char *extra_command_line; +/* Extra init arguments */ +static char *extra_init_args; static char *execute_command; static char *ramdisk_execute_command; @@ -245,6 +248,156 @@ static int __init loglevel(char *str) early_param("loglevel", loglevel); +#ifdef CONFIG_BOOT_CONFIG + +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; + char c = '\"'; + const char *val; + 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); + ret = snprintf(buf, rest(buf, end), "%s%c", xbc_namebuf, + vnode ? '=' : ' '); + if (ret < 0) + return ret; + buf += ret; + if (!vnode) + continue; + + c = '\"'; + xbc_array_for_each_value(vnode, val) { + ret = snprintf(buf, rest(buf, end), "%c%s", c, val); + if (ret < 0) + return ret; + buf += ret; + c = ','; + } + if (rest(buf, end) > 2) + strcpy(buf, "\" "); + buf += 2; + } + + return buf - (end - size); +} +#undef rest + +/* Make an extra command line under given key word */ +static char * __init xbc_make_cmdline(const char *key) +{ + struct xbc_node *root; + char *new_cmdline; + int ret, len = 0; + + root = xbc_find_node(key); + if (!root) + return NULL; + + /* Count required buffer size */ + len = xbc_snprint_cmdline(NULL, 0, root); + if (len <= 0) + return NULL; + + new_cmdline = memblock_alloc(len + 1, SMP_CACHE_BYTES); + if (!new_cmdline) { + pr_err("Failed to allocate memory for extra kernel cmdline.\n"); + return NULL; + } + + ret = xbc_snprint_cmdline(new_cmdline, len + 1, root); + if (ret < 0 || ret > len) { + pr_err("Failed to print extra kernel cmdline.\n"); + return NULL; + } + + return new_cmdline; +} + +u32 boot_config_checksum(unsigned char *p, u32 size) +{ + u32 ret = 0; + + while (size--) + ret += *p++; + + return ret; +} + +static void __init setup_boot_config(const char *cmdline) +{ + u32 size, csum; + char *data, *copy; + const char *p; + u32 *hdr; + int ret; + + p = strstr(cmdline, "bootconfig"); + if (!p || (p != cmdline && !isspace(*(p-1))) || + (p[10] && !isspace(p[10]))) + return; + + if (!initrd_end) + goto not_found; + + hdr = (u32 *)(initrd_end - 8); + size = hdr[0]; + csum = hdr[1]; + + if (size >= XBC_DATA_MAX) { + pr_err("bootconfig size %d greater than max size %d\n", + size, XBC_DATA_MAX); + return; + } + + data = ((void *)hdr) - size; + if ((unsigned long)data < initrd_start) + goto not_found; + + if (boot_config_checksum((unsigned char *)data, size) != csum) { + pr_err("bootconfig checksum failed\n"); + return; + } + + copy = memblock_alloc(size + 1, SMP_CACHE_BYTES); + if (!copy) { + pr_err("Failed to allocate memory for bootconfig\n"); + return; + } + + memcpy(copy, data, size); + copy[size] = '\0'; + + ret = xbc_init(copy); + if (ret < 0) + pr_err("Failed to parse bootconfig\n"); + else { + pr_info("Load bootconfig: %d bytes %d nodes\n", size, ret); + /* keys starting with "kernel." are passed via cmdline */ + extra_command_line = xbc_make_cmdline("kernel"); + /* Also, "init." keys are init arguments */ + extra_init_args = xbc_make_cmdline("init"); + } + return; +not_found: + pr_err("'bootconfig' found on command line, but no bootconfig found\n"); +} +#else +#define setup_boot_config(cmdline) do { } while (0) +#endif + /* Change NUL term back to "=", to make "param" the whole string. */ static void __init repair_env_string(char *param, char *val) { @@ -373,22 +526,50 @@ static inline void smp_prepare_cpus(unsigned int maxcpus) { } */ static void __init setup_command_line(char *command_line) { - size_t len = strlen(boot_command_line) + 1; + size_t len, xlen = 0, ilen = 0; - saved_command_line = memblock_alloc(len, SMP_CACHE_BYTES); - if (!saved_command_line) - panic("%s: Failed to allocate %zu bytes\n", __func__, len); + if (extra_command_line) + xlen = strlen(extra_command_line); + if (extra_init_args) + ilen = strlen(extra_init_args) + 4; /* for " -- " */ - initcall_command_line = memblock_alloc(len, SMP_CACHE_BYTES); - if (!initcall_command_line) - panic("%s: Failed to allocate %zu bytes\n", __func__, len); + len = xlen + strlen(boot_command_line) + 1; + + saved_command_line = memblock_alloc(len + ilen, SMP_CACHE_BYTES); + if (!saved_command_line) + panic("%s: Failed to allocate %zu bytes\n", __func__, len + ilen); static_command_line = memblock_alloc(len, SMP_CACHE_BYTES); if (!static_command_line) panic("%s: Failed to allocate %zu bytes\n", __func__, len); - strcpy(saved_command_line, boot_command_line); - strcpy(static_command_line, command_line); + if (xlen) { + /* + * We have to put extra_command_line before boot command + * lines because there could be dashes (separator of init + * command line) in the command lines. + */ + strcpy(saved_command_line, extra_command_line); + strcpy(static_command_line, extra_command_line); + } + strcpy(saved_command_line + xlen, boot_command_line); + strcpy(static_command_line + xlen, command_line); + + if (ilen) { + /* + * Append supplemental init boot args to saved_command_line + * so that user can check what command line options passed + * to init. + */ + len = strlen(saved_command_line); + if (!strstr(boot_command_line, " -- ")) { + strcpy(saved_command_line + len, " -- "); + len += 4; + } else + saved_command_line[len++] = ' '; + + strcpy(saved_command_line + len, extra_init_args); + } } /* @@ -595,6 +776,7 @@ asmlinkage __visible void __init start_kernel(void) pr_notice("%s", linux_banner); early_security_init(); setup_arch(&command_line); + setup_boot_config(command_line); setup_command_line(command_line); setup_nr_cpu_ids(); setup_per_cpu_areas(); @@ -604,7 +786,7 @@ asmlinkage __visible void __init start_kernel(void) build_all_zonelists(NULL); page_alloc_init(); - pr_notice("Kernel command line: %s\n", boot_command_line); + pr_notice("Kernel command line: %s\n", saved_command_line); /* parameters may set static keys */ jump_label_init(); parse_early_param(); @@ -615,6 +797,9 @@ asmlinkage __visible void __init start_kernel(void) if (!IS_ERR_OR_NULL(after_dashes)) parse_args("Setting init args", after_dashes, NULL, 0, -1, -1, NULL, set_init_arg); + if (extra_init_args) + parse_args("Setting extra init args", extra_init_args, + NULL, 0, -1, -1, NULL, set_init_arg); /* * These use large bootmem allocations and must precede @@ -996,13 +1181,12 @@ static int __init ignore_unknown_bootoption(char *param, char *val, return 0; } -static void __init do_initcall_level(int level) +static void __init do_initcall_level(int level, char *command_line) { initcall_entry_t *fn; - strcpy(initcall_command_line, saved_command_line); parse_args(initcall_level_names[level], - initcall_command_line, __start___param, + command_line, __start___param, __stop___param - __start___param, level, level, NULL, ignore_unknown_bootoption); @@ -1015,9 +1199,20 @@ static void __init do_initcall_level(int level) static void __init do_initcalls(void) { int level; + size_t len = strlen(saved_command_line) + 1; + char *command_line; + + command_line = kzalloc(len, GFP_KERNEL); + if (!command_line) + panic("%s: Failed to allocate %zu bytes\n", __func__, len); + + for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++) { + /* Parser modifies command_line, restore it each time */ + strcpy(command_line, saved_command_line); + do_initcall_level(level, command_line); + } - for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++) - do_initcall_level(level); + kfree(command_line); } /* |