mn10300: Use early_param() to parse "mem=" parameter
authorAkira Takeuchi <takeuchi.akr@jp.panasonic.com>
Fri, 28 Jun 2013 15:53:03 +0000 (16:53 +0100)
committerDavid Howells <dhowells@redhat.com>
Fri, 28 Jun 2013 15:53:03 +0000 (16:53 +0100)
This fixes the problem that "init=" options may not be passed to kernel
correctly.

parse_mem_cmdline() of mn10300 arch gets rid of "mem=" string from
redboot_command_line. Then init_setup() parses the "init=" options from
static_command_line, which is a copy of redboot_command_line, and keeps
the pointer to the init options in execute_command variable.

Since the commit 026cee0 upstream (params: <level>_initcall-like kernel
parameters), static_command_line becomes overwritten by saved_command_line at
do_initcall_level(). Notice that saved_command_line is a command line
which includes "mem=" string.

As a result, execute_command may point to weird string by the length of
"mem=" parameter.
I noticed this problem when using the command line like this:

    mem=128M console=ttyS0,115200 init=/bin/sh

Here is the processing flow of command line parameters.
    start_kernel()
      setup_arch(&command_line)
         parse_mem_cmdline(cmdline_p)
           * strcpy(boot_command_line, redboot_command_line);
           * Remove "mem=xxx" from redboot_command_line.
           * *cmdline_p = redboot_command_line;
      setup_command_line(command_line) <-- command_line is redboot_command_line
        * strcpy(saved_command_line, boot_command_line)
        * strcpy(static_command_line, command_line)
      parse_early_param()
        strlcpy(tmp_cmdline, boot_command_line, COMMAND_LINE_SIZE);
        parse_early_options(tmp_cmdline);
          parse_args("early options", cmdline, NULL, 0, 0, 0, do_early_param);
      parse_args("Booting ..", static_command_line, ...);
        init_setup() <-- save the pointer in execute_command
      rest_init()
        kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);

At this point, execute_command points to "/bin/sh" string.

    kernel_init()
      kernel_init_freeable()
        do_basic_setup()
          do_initcalls()
            do_initcall_level()
              (*) strcpy(static_command_line, saved_command_line);

Here, execute_command gets to point to "200" string !!

Signed-off-by: David Howells <dhowells@redhat.com>
arch/mn10300/kernel/setup.c

index 33c3bd1e5c6db1b03eef5b354495bc7a628541c5..ebac9c11f7966cbb6a52ebeefa7bb17579d727d6 100644 (file)
@@ -38,6 +38,7 @@ struct mn10300_cpuinfo boot_cpu_data;
 /* For PCI or other memory-mapped resources */
 unsigned long pci_mem_start = 0x18000000;
 
+static char __initdata cmd_line[COMMAND_LINE_SIZE];
 char redboot_command_line[COMMAND_LINE_SIZE] =
        "console=ttyS0,115200 root=/dev/mtdblock3 rw";
 
@@ -74,45 +75,19 @@ static const char *const mn10300_cputypes[] = {
 };
 
 /*
- *
+ * Pick out the memory size.  We look for mem=size,
+ * where size is "size[KkMm]"
  */
-static void __init parse_mem_cmdline(char **cmdline_p)
+static int __init early_mem(char *p)
 {
-       char *from, *to, c;
-
-       /* save unparsed command line copy for /proc/cmdline */
-       strcpy(boot_command_line, redboot_command_line);
-
-       /* see if there's an explicit memory size option */
-       from = redboot_command_line;
-       to = redboot_command_line;
-       c = ' ';
-
-       for (;;) {
-               if (c == ' ' && !memcmp(from, "mem=", 4)) {
-                       if (to != redboot_command_line)
-                               to--;
-                       memory_size = memparse(from + 4, &from);
-               }
-
-               c = *(from++);
-               if (!c)
-                       break;
-
-               *(to++) = c;
-       }
-
-       *to = '\0';
-       *cmdline_p = redboot_command_line;
+       memory_size = memparse(p, &p);
 
        if (memory_size == 0)
                panic("Memory size not known\n");
 
-       memory_end = (unsigned long) CONFIG_KERNEL_RAM_BASE_ADDRESS +
-               memory_size;
-       if (memory_end > phys_memory_end)
-               memory_end = phys_memory_end;
+       return 0;
 }
+early_param("mem", early_mem);
 
 /*
  * architecture specific setup
@@ -125,7 +100,20 @@ void __init setup_arch(char **cmdline_p)
        cpu_init();
        unit_setup();
        smp_init_cpus();
-       parse_mem_cmdline(cmdline_p);
+
+       /* save unparsed command line copy for /proc/cmdline */
+       strlcpy(boot_command_line, redboot_command_line, COMMAND_LINE_SIZE);
+
+       /* populate cmd_line too for later use, preserving boot_command_line */
+       strlcpy(cmd_line, boot_command_line, COMMAND_LINE_SIZE);
+       *cmdline_p = cmd_line;
+
+       parse_early_param();
+
+       memory_end = (unsigned long) CONFIG_KERNEL_RAM_BASE_ADDRESS +
+               memory_size;
+       if (memory_end > phys_memory_end)
+               memory_end = phys_memory_end;
 
        init_mm.start_code = (unsigned long)&_text;
        init_mm.end_code = (unsigned long) &_etext;