Name: Explicit Two Stage Parameter Parsing Author: Rusty Russell Status: Tested on 2.5.68-bk10. Breaks all non-x86 architectures. D: Bartlomiej noted that IDE parameter parsing would be simpler if it D: was done later (when kmalloc was available, for example). It seems D: simplest to do parameter parsing in two stages: __setup very early D: (even earlier than now, called directly from each arch's D: setup_arch()), and module_params later, just before initcalls. D: D: This also allows most archs which hand-parse the command line, some D: incorrectly (leaving the parameters to fall though), to use D: __setup. diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .25546-linux-2.5.68-bk10/arch/i386/kernel/setup.c .25546-linux-2.5.68-bk10.updated/arch/i386/kernel/setup.c --- .25546-linux-2.5.68-bk10/arch/i386/kernel/setup.c 2003-05-01 09:29:12.000000000 +1000 +++ .25546-linux-2.5.68-bk10.updated/arch/i386/kernel/setup.c 2003-05-01 16:00:00.000000000 +1000 @@ -431,109 +431,90 @@ static void __init setup_memory_region(v print_memory_map(who); } /* setup_memory_region */ - -static void __init parse_cmdline_early (char ** cmdline_p) +/* + * "mem=nopentium" disables the 4MB page tables. + * "mem=XXX[kKmM]" defines a memory region from HIGH_MEM + * to , overriding the bios size. + * "memmap=XXX[KkmM]@XXX[KkmM]" defines a memory region from + * to +, overriding the bios size. + * + * HPA tells me bootloaders need to parse mem=, so no new + * option should be mem= [also see Documentation/i386/boot.txt] + */ +static __init int mem_setup(char *s) { - char c = ' ', *to = command_line, *from = COMMAND_LINE; - int len = 0; - int userdef = 0; - - /* Save unparsed command line copy for /proc/cmdline */ - memcpy(saved_command_line, COMMAND_LINE, COMMAND_LINE_SIZE); - saved_command_line[COMMAND_LINE_SIZE-1] = '\0'; + unsigned long long mem_size; - for (;;) { - /* - * "mem=nopentium" disables the 4MB page tables. - * "mem=XXX[kKmM]" defines a memory region from HIGH_MEM - * to , overriding the bios size. - * "memmap=XXX[KkmM]@XXX[KkmM]" defines a memory region from - * to +, overriding the bios size. - * - * HPA tells me bootloaders need to parse mem=, so no new - * option should be mem= [also see Documentation/i386/boot.txt] - */ - if (c == ' ' && !memcmp(from, "mem=", 4)) { - if (to != command_line) - to--; - if (!memcmp(from+4, "nopentium", 9)) { - from += 9+4; - clear_bit(X86_FEATURE_PSE, boot_cpu_data.x86_capability); - disable_pse = 1; - } else { - /* If the user specifies memory size, we - * limit the BIOS-provided memory map to - * that size. exactmap can be used to specify - * the exact map. mem=number can be used to - * trim the existing memory map. - */ - unsigned long long mem_size; - - mem_size = memparse(from+4, &from); - limit_regions(mem_size); - userdef=1; - } - } + if (!memcmp(s+4, "nopentium", 9)) { + clear_bit(X86_FEATURE_PSE, boot_cpu_data.x86_capability); + disable_pse = 1; + return 1; + } - if (c == ' ' && !memcmp(from, "memmap=", 7)) { - if (to != command_line) - to--; - if (!memcmp(from+7, "exactmap", 8)) { - from += 8+7; - e820.nr_map = 0; - userdef = 1; - } else { - /* If the user specifies memory size, we - * limit the BIOS-provided memory map to - * that size. exactmap can be used to specify - * the exact map. mem=number can be used to - * trim the existing memory map. - */ - unsigned long long start_at, mem_size; - - mem_size = memparse(from+7, &from); - if (*from == '@') { - start_at = memparse(from+1, &from); - add_memory_region(start_at, mem_size, E820_RAM); - } else if (*from == '#') { - start_at = memparse(from+1, &from); - add_memory_region(start_at, mem_size, E820_ACPI); - } else if (*from == '$') { - start_at = memparse(from+1, &from); - add_memory_region(start_at, mem_size, E820_RESERVED); - } else { - limit_regions(mem_size); - userdef=1; - } - } - } + /* If the user specifies memory size, we limit the + * BIOS-provided memory map to that size. exactmap can be used + * to specify the exact map. mem=number can be used to trim + * the existing memory map. */ + mem_size = memparse(s+4, &s); + limit_regions(mem_size); + printk(KERN_INFO "user-defined physical RAM map:\n"); + print_memory_map("user"); + return 1; +} +__setup("mem=", mem_setup); - /* "acpi=off" disables both ACPI table parsing and interpreter init */ - if (c == ' ' && !memcmp(from, "acpi=off", 8)) - acpi_disabled = 1; +static __init int memmap_setup(char *s) +{ + unsigned long long start_at, mem_size; - /* - * highmem=size forces highmem to be exactly 'size' bytes. - * This works even on boxes that have no highmem otherwise. - * This also works to reduce highmem size on bigger boxes. - */ - if (c == ' ' && !memcmp(from, "highmem=", 8)) - highmem_pages = memparse(from+8, &from) >> PAGE_SHIFT; - - c = *(from++); - if (!c) - break; - if (COMMAND_LINE_SIZE <= ++len) - break; - *(to++) = c; + if (!memcmp(s+7, "exactmap", 8)) { + e820.nr_map = 0; + goto show_map; } - *to = '\0'; - *cmdline_p = command_line; - if (userdef) { + + /* If the user specifies memory size, we limit the + * BIOS-provided memory map to that size. exactmap can be used + * to specify the exact map. mem=number can be used to trim + * the existing memory map. */ + mem_size = memparse(s+7, &s); + if (*s == '@') { + start_at = memparse(s+1, &s); + add_memory_region(start_at, mem_size, E820_RAM); + } else if (*s == '#') { + start_at = memparse(s+1, &s); + add_memory_region(start_at, mem_size, E820_ACPI); + } else if (*s == '$') { + start_at = memparse(s+1, &s); + add_memory_region(start_at, mem_size, E820_RESERVED); + } else { + limit_regions(mem_size); + show_map: printk(KERN_INFO "user-defined physical RAM map:\n"); print_memory_map("user"); } + return 1; +} +__setup("memmap=", memmap_setup); + +/* "acpi=off" disables both ACPI table parsing and interpreter init */ +static __init int acpi_setup(char *s) +{ + acpi_disabled = 1; + return 1; } +__setup("acpi=off", acpi_setup); + +/* + * highmem=size forces highmem to be exactly 'size' bytes. + * This works even on boxes that have no highmem otherwise. + * This also works to reduce highmem size on bigger boxes. + */ +static __init int highmem_setup(char *s) +{ + highmem_pages = memparse(s+8, &s) >> PAGE_SHIFT; + return 1; +} +__setup("highmem=", highmem_setup); /* * Find the highest page frame number we have available @@ -852,6 +833,111 @@ static int __init noreplacement_setup(ch __setup("noreplacement", noreplacement_setup); +#if 1 +static void __init parse_cmdline_early (char ** cmdline_p) +{ + char c = ' ', *to = command_line, *from = COMMAND_LINE; + int len = 0; + int userdef = 0; + + /* Save unparsed command line copy for /proc/cmdline */ + memcpy(saved_command_line, COMMAND_LINE, COMMAND_LINE_SIZE); + saved_command_line[COMMAND_LINE_SIZE-1] = '\0'; + + for (;;) { + /* + * "mem=nopentium" disables the 4MB page tables. + * "mem=XXX[kKmM]" defines a memory region from HIGH_MEM + * to , overriding the bios size. + * "memmap=XXX[KkmM]@XXX[KkmM]" defines a memory region from + * to +, overriding the bios size. + * + * HPA tells me bootloaders need to parse mem=, so no new + * option should be mem= [also see Documentation/i386/boot.txt] + */ + if (c == ' ' && !memcmp(from, "mem=", 4)) { + if (to != command_line) + to--; + if (!memcmp(from+4, "nopentium", 9)) { + from += 9+4; + clear_bit(X86_FEATURE_PSE, boot_cpu_data.x86_capability); + disable_pse = 1; + } else { + /* If the user specifies memory size, we + * limit the BIOS-provided memory map to + * that size. exactmap can be used to specify + * the exact map. mem=number can be used to + * trim the existing memory map. + */ + unsigned long long mem_size; + + mem_size = memparse(from+4, &from); + limit_regions(mem_size); + userdef=1; + } + } + + if (c == ' ' && !memcmp(from, "memmap=", 7)) { + if (to != command_line) + to--; + if (!memcmp(from+7, "exactmap", 8)) { + from += 8+7; + e820.nr_map = 0; + userdef = 1; + } else { + /* If the user specifies memory size, we + * limit the BIOS-provided memory map to + * that size. exactmap can be used to specify + * the exact map. mem=number can be used to + * trim the existing memory map. + */ + unsigned long long start_at, mem_size; + + mem_size = memparse(from+7, &from); + if (*from == '@') { + start_at = memparse(from+1, &from); + add_memory_region(start_at, mem_size, E820_RAM); + } else if (*from == '#') { + start_at = memparse(from+1, &from); + add_memory_region(start_at, mem_size, E820_ACPI); + } else if (*from == '$') { + start_at = memparse(from+1, &from); + add_memory_region(start_at, mem_size, E820_RESERVED); + } else { + limit_regions(mem_size); + userdef=1; + } + } + } + + /* "acpi=off" disables both ACPI table parsing and interpreter init */ + if (c == ' ' && !memcmp(from, "acpi=off", 8)) + acpi_disabled = 1; + + /* + * highmem=size forces highmem to be exactly 'size' bytes. + * This works even on boxes that have no highmem otherwise. + * This also works to reduce highmem size on bigger boxes. + */ + if (c == ' ' && !memcmp(from, "highmem=", 8)) + highmem_pages = memparse(from+8, &from) >> PAGE_SHIFT; + + c = *(from++); + if (!c) + break; + if (COMMAND_LINE_SIZE <= ++len) + break; + *(to++) = c; + } + *to = '\0'; + *cmdline_p = command_line; + if (userdef) { + printk(KERN_INFO "user-defined physical RAM map:\n"); + print_memory_map("user"); + } +} +#endif + void __init setup_arch(char **cmdline_p) { unsigned long max_low_pfn; @@ -896,7 +982,12 @@ void __init setup_arch(char **cmdline_p) data_resource.start = virt_to_phys(&_etext); data_resource.end = virt_to_phys(&_edata)-1; - parse_cmdline_early(cmdline_p); + /* Save unparsed command line copy for /proc/cmdline */ + memcpy(saved_command_line, COMMAND_LINE, COMMAND_LINE_SIZE); + saved_command_line[COMMAND_LINE_SIZE-1] = '\0'; + strcpy(command_line, saved_command_line); + early_parse_cmdline(command_line); + *cmdline_p = command_line; max_low_pfn = setup_memory(); diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .25546-linux-2.5.68-bk10/include/linux/kernel.h .25546-linux-2.5.68-bk10.updated/include/linux/kernel.h --- .25546-linux-2.5.68-bk10/include/linux/kernel.h 2003-04-20 18:05:14.000000000 +1000 +++ .25546-linux-2.5.68-bk10.updated/include/linux/kernel.h 2003-05-01 16:00:00.000000000 +1000 @@ -82,6 +82,7 @@ extern int vsscanf(const char *, const c extern int get_option(char **str, int *pint); extern char *get_options(const char *str, int nints, int *ints); extern unsigned long long memparse(char *ptr, char **retptr); +extern void early_parse_cmdline(char *command_line); extern void dev_probe_lock(void); extern void dev_probe_unlock(void); diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .25546-linux-2.5.68-bk10/init/main.c .25546-linux-2.5.68-bk10.updated/init/main.c --- .25546-linux-2.5.68-bk10/init/main.c 2003-05-01 09:29:34.000000000 +1000 +++ .25546-linux-2.5.68-bk10.updated/init/main.c 2003-05-01 16:02:40.000000000 +1000 @@ -146,21 +146,18 @@ char * envp_init[MAX_INIT_ENVS+2] = { "H __setup("profile=", profile_setup); -static int __init obsolete_checksetup(char *line) -{ - struct obs_kernel_param *p; - extern struct obs_kernel_param __setup_start, __setup_end; +/* Created by linker script magic. */ +extern struct obs_kernel_param __setup_start, __setup_end; - p = &__setup_start; - do { - int n = strlen(p->str); - if (!strncmp(line,p->str,n)) { - if (p->setup_func(line+n)) - return 1; - } +static __init struct obs_kernel_param *early_param(char *line, + struct obs_kernel_param *p) +{ + while (p < &__setup_end) { + if (!strncmp(line,p->str,strlen(p->str))) + return p; p++; } while (p < &__setup_end); - return 0; + return NULL; } /* this should be approx 2 Bo*oMips to start (note initial shift), and will @@ -232,6 +229,33 @@ static int __init quiet_kernel(char *str __setup("debug", debug_kernel); __setup("quiet", quiet_kernel); +static int __init setup_option(char *param, char *val) +{ + struct obs_kernel_param *setup; + + /* Change NUL term back to "=", to make "param" the whole string, + and make this process non-destructive. */ + if (val) + val[-1] = '='; + + /* We may have multiple matches: keep calling until one says Done. */ + for (setup = early_param(param, &__setup_start); + setup; + setup = early_param(param, setup+1)) + if (setup->setup_func(param + strlen(setup->str))) + break; + return 0; +} + +static int called_early_parse = 0; + +/* Handle all __setup options here (really early). */ +void early_parse_cmdline(char *command_line) +{ + parse_args("Early Boot Parsing", command_line, NULL, 0, setup_option); + called_early_parse++; +} + /* Unknown boot options get handed to init, unless they look like failed parameters */ static int __init unknown_bootoption(char *param, char *val) @@ -240,8 +264,8 @@ static int __init unknown_bootoption(cha if (val) val[-1] = '='; - /* Handle obsolete-style parameters */ - if (obsolete_checksetup(param)) + /* Ignore __setup parameters: they're already done. */ + if (early_param(param, &__setup_start)) return 0; /* Preemptive maintenance for "why didn't my mispelled command @@ -365,9 +389,9 @@ static void __init smp_init(void) * cpu_idle. */ -static void rest_init(void) +static void rest_init(char *command_line) { - kernel_thread(init, NULL, CLONE_KERNEL); + kernel_thread(init, command_line, CLONE_KERNEL); unlock_kernel(); cpu_idle(); } @@ -380,7 +404,6 @@ asmlinkage void __init start_kernel(void { char * command_line; extern char saved_command_line[]; - extern struct kernel_param __start___param, __stop___param; /* * Interrupts are still disabled. Do necessary setups, then * enable them @@ -399,9 +422,6 @@ asmlinkage void __init start_kernel(void build_all_zonelists(); page_alloc_init(); printk("Kernel command line: %s\n", saved_command_line); - parse_args("Booting kernel", command_line, &__start___param, - &__stop___param - &__start___param, - &unknown_bootoption); trap_init(); rcu_init(); init_IRQ(); @@ -416,6 +436,9 @@ asmlinkage void __init start_kernel(void * this. But we do want output early, in case something goes wrong. */ console_init(); + if (called_early_parse != 1) + panic("setup_arch MUST CALL early_parse_cmdline()!\n") + profile_init(); kmem_cache_init(); local_irq_enable(); @@ -461,7 +484,7 @@ asmlinkage void __init start_kernel(void init_idle(current, smp_processor_id()); /* Do the rest non-__init'ed, we're now alive */ - rest_init(); + rest_init(command_line); } int __initdata initcall_debug; @@ -516,8 +539,10 @@ static void __init do_initcalls(void) * * Now we can finally start doing some real work.. */ -static void __init do_basic_setup(void) +static void __init do_basic_setup(char *command_line) { + extern struct kernel_param __start___param, __stop___param; + driver_init(); #ifdef CONFIG_SYSCTL @@ -528,6 +553,9 @@ static void __init do_basic_setup(void) sock_init(); init_workqueues(); + parse_args("Booting kernel", command_line, &__start___param, + &__stop___param - &__start___param, + &unknown_bootoption); do_initcalls(); } @@ -545,7 +573,7 @@ static void do_pre_smp_initcalls(void) extern void prepare_namespace(void); -static int init(void * unused) +static int init(void *command_line) { static char * argv_sh[] = { "sh", NULL, }; @@ -566,7 +594,7 @@ static int init(void * unused) do_pre_smp_initcalls(); smp_init(); - do_basic_setup(); + do_basic_setup(command_line); prepare_namespace(); diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .25546-linux-2.5.68-bk10/net/core/netfilter.c .25546-linux-2.5.68-bk10.updated/net/core/netfilter.c --- .25546-linux-2.5.68-bk10/net/core/netfilter.c 2003-05-01 09:29:35.000000000 +1000 +++ .25546-linux-2.5.68-bk10.updated/net/core/netfilter.c 2003-05-01 16:00:00.000000000 +1000 @@ -549,7 +549,7 @@ int nf_hook_slow(int pf, unsigned int ho void nf_reinject(struct sk_buff *skb, struct nf_info *info, unsigned int verdict) { - struct list_head *elem = &info->elem->list; + struct list_head *i, *elem = &info->elem->list; rcu_read_lock();