Merge tag 'trace-v5.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt...
[sfrench/cifs-2.6.git] / init / main.c
index d8c7e86c2d28d0642e4dd694a5fab2fc7bd1deae..f95b014a5479712176e385174cb81ff51e1b65df 100644 (file)
@@ -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,19 @@ 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;
+
+#ifdef CONFIG_BOOT_CONFIG
+/* Is bootconfig on command line? */
+static bool bootconfig_found;
+static bool initargs_found;
+#else
+# define bootconfig_found false
+# define initargs_found false
+#endif
 
 static char *execute_command;
 static char *ramdisk_execute_command;
@@ -245,6 +257,169 @@ 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 int __init bootconfig_params(char *param, char *val,
+                                   const char *unused, void *arg)
+{
+       if (strcmp(param, "bootconfig") == 0) {
+               bootconfig_found = true;
+       } else if (strcmp(param, "--") == 0) {
+               initargs_found = true;
+       }
+       return 0;
+}
+
+static void __init setup_boot_config(const char *cmdline)
+{
+       static char tmp_cmdline[COMMAND_LINE_SIZE] __initdata;
+       u32 size, csum;
+       char *data, *copy;
+       u32 *hdr;
+       int ret;
+
+       strlcpy(tmp_cmdline, boot_command_line, COMMAND_LINE_SIZE);
+       parse_args("bootconfig", tmp_cmdline, NULL, 0, 0, 0, NULL,
+                  bootconfig_params);
+
+       if (!bootconfig_found)
+               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 +548,51 @@ 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 (initargs_found) {
+                       saved_command_line[len++] = ' ';
+               } else {
+                       strcpy(saved_command_line + len, " -- ");
+                       len += 4;
+               }
+
+               strcpy(saved_command_line + len, extra_init_args);
+       }
 }
 
 /*
@@ -595,6 +799,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 +809,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 +820,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 +1204,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 +1222,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);
 }
 
 /*