(1/3) allow earlier command line parsing
PATCH 1/1 (1/3) allow earlier command line parsing
I stumbled upon this code when I was trying to get an early printk working for 386. There is a real need for things to be able to parse the command line in setup_arch(), for many architectures.
parse_options() no longer runs the setup functions, it only parses.
It may be called more than once with no ill effects. This way it can be called in setup_arch(), then again in the generic start_kernel().
run_setup() comes along later and actually runs the __setup functions.
It can do this in many phases, as long as parse_options() is called
first. The last run_setup() is special because it goes through and picks up all of the environment variables and init arguments that are left over, setting them with setup_setenv()
Signature · 2 lines
Dave Hansen [email protected]
# This is a BitKeeper generated patch for the following project:
# Project Name: Linux kernel tree
# This patch format is intended for GNU patch command version 2.5 or higher. # This patch includes the following deltas:
# ChangeSet 1.968 -> 1.969 # include/linux/init.h 1.18 -> 1.19 # init/main.c 1.83 -> 1.84
#
# The following is the BitKeeper ChangeSet Log
# --------------------------------------------
# 02/12/02 haveblue@elm3b96.(none) 1.969
# c-1 # -------------------------------------------- # diff -Nru a/include/linux/init.h b/include/linux/init.h --- a/include/linux/init.h Mon Dec 2 15:59:01 2002 +++ b/include/linux/init.h Mon Dec 2 15:59:01 2002 @@ -86,21 +86,27 @@ #define __exitcall(fn) \ static exitcall_t __exitcall_##fn __exit_call = fn
+#define SETUP_ARCH_BEGIN 0
+#define SETUP_ARCH_LATE 1
+#define SETUP_DEFAULT 2
+#define SETUP_ENV 3
/*
* Used for kernel command line parameter setup
*/
struct kernel_param {
const char *str;
int (*setup_func)(char *);
+ int level;
};
extern struct kernel_param __setup_start, __setup_end;
-
-#define __setup(str, fn) \
+#define __setup(str, fn) __ordered_setup(SETUP_DEFAULT, str, fn) +
+#define __ordered_setup(level, str, fn) \
static char __setup_str_##fn[] __initdata = str; \
static struct kernel_param __setup_##fn \
__attribute__((unused,__section__ (".init.setup"))) \
- = { __setup_str_##fn, fn }
+ = { __setup_str_##fn, fn, level }
#endif /* __ASSEMBLY__ */
init/main.c +100 -44
@@ -136,20 +136,73 @@__setup ("profile=" , profile_setup); -static int __init checksetup (char *line) +#define MAX_KERNEL_ARGS 64 +static struct kernel_arg { +char * str; +int handled; +} kernel_argv[MAX_KERNEL_ARGS ]; +static int kernel_argc =-1 ; + +/* +* Any cmd-line option is taken to be an environment variable if it contains +* the character '='. +* +* This routine also checks for options meant for the kernel. +* These options are not given to init - they are for internal kernel use only. +*/ +static void __init setup_setenv (char *line) +{ +static int args =0 ; +static int envs =1 ;/* TERM is set to 'linux' by default */ + +if (!strncmp (line,"init=" ,5 )) { + line +=5 ; + execute_command = line; +/* In case LILO is going to boot us with default command line, +* it prepends "auto" before the whole cmdline which makes +* the shell think it should execute a script with such name. +* So we ignore all arguments entered _before_ init=... [MJ] +*/ + args =0 ; + argv_init[0 ] =NULL ; +return ; + } + +/* +* check if it's an environment variable or +* an option. +*/ + +if (strchr (line,'=' )) { +if (envs >=MAX_INIT_ENVS ) +return ; + envp_init[++envs] = line; + envp_init[envs+1 ] =NULL ; + }else { +if (args >=MAX_INIT_ARGS ) +return ; +if (*line) { + argv_init[++args] = line; + argv_init[args+1 ] =NULL ; + } + } +} + +static void __init checksetup (int setup_level,struct kernel_arg *arg) {struct kernel_param *p; +char *line = arg->str; p = &__setup_start;do {int n =strlen (p->str); -if (!strncmp (line,p->str,n)) { -if (p->setup_func (line+n)) -return 1 ; +if (p->level == setup_level && !strncmp (line, p->str, n)) { + arg->handled = p->setup_func (line+n); +if (arg->handled) +return ; } p++; }while (p < &__setup_end); -return 0 ; }/* this should be approx 2 Bo*oMips to start (note initial shift), and will
@@ -223,22 +276,25 @@/* * This is a simple kernel command line parsing function: it parses -* the command line, and fills in the arguments/environment to init -* as appropriate. Any cmd-line option is taken to be an environment -* variable if it contains the character '='. +* the command line, and saves it for later processing by run_setup(). +* This is designed to be called more than once. This way, individual +* architectures can call it as early as they want, or they can ignore +* it and let the generic code call it. * -* This routine also checks for options meant for the kernel. -* These options are not given to init - they are for internal kernel use only. +* Make sure not to modify "line" after you call this, because the +* argv array keeps references inside of it. */ -static void __init parse_options (char *line) +void __init parse_options (char *line) {char *next,*quote; -int args, envs; - +if (!*line)return ; - args =0 ; - envs =1 ;/* TERM is set to 'linux' by default */ + +if ( kernel_argc !=-1 ) +return ; + kernel_argc =0 ; + next = line;while ((line = next) !=NULL ) { quote =strchr (line,'"' );
@@ -255,39 +311,37 @@ }if (next !=NULL ) *next++ =0 ; -if (!strncmp (line,"init=" ,5 )) { - line +=5 ; - execute_command = line; -/* In case LILO is going to boot us with default command line, -* it prepends "auto" before the whole cmdline which makes -* the shell think it should execute a script with such name. -* So we ignore all arguments entered _before_ init=... [MJ] -*/ - args =0 ; -continue ; - } -if (checksetup (line)) -continue ; - -/* -* Then check if it's an environment variable or -* an option. -*/ -if (strchr (line,'=' )) { -if (envs >=MAX_INIT_ENVS ) -break ; - envp_init[++envs] = line; - }else { -if (args >=MAX_INIT_ARGS ) -break ; -if (*line) - argv_init[++args] = line; + + kernel_argv[kernel_argc++].str = line; +if (kernel_argc ==MAX_KERNEL_ARGS ) { + kernel_argc--; +return ; } } - argv_init[args+1 ] =NULL ; - envp_init[envs+1 ] =NULL ; } +/* +* This routine depends on parse_options() being called first. If you +* want to use this in setup_arch(), make sure you can parse_options() +* first. +* +* during the SETUP_ENV level, all previously unused variables will be +* filled in as arguments/environment to init as appropriate. +* +*/ +void __init run_setup (int setup_level) +{ +int i; + +for ( i=0 ; i<kernel_argc; i++ ) { +struct kernel_arg *arg = &kernel_argv[i]; +if (setup_level ==SETUP_ENV && !arg->handled) +setup_setenv (arg->str); +else +checksetup (setup_level,arg); + } + +}extern void setup_arch (char **);extern void cpu_idle (void );
@@ -394,6 +448,8 @@page_alloc_init ();printk ("Kernel command line: %s\n" , saved_command_line);parse_options (command_line); +run_setup (SETUP_DEFAULT ); +run_setup (SETUP_ENV );trap_init ();extable_init ();rcu_init ();
Re: [PATCH] (2/3) do early command line parsing for 386
This actually implements the early command-line parsing for i386. It actually decreases the amount of code in setup.c, and makes it a lot more readable.
setup.c | 138 +++++++++++++++++++++++++++++++---------------------------------
1 files changed, 68 insertions(+), 70 deletions(-)
Signature · 2 lines
Dave Hansen [email protected]
# This is a BitKeeper generated patch for the following project:
# Project Name: Linux kernel tree
# This patch format is intended for GNU patch command version 2.5 or higher. # This patch includes the following deltas:
# ChangeSet 1.969 -> 1.970
# arch/i386/kernel/setup.c 1.63 -> 1.64
#
# The following is the BitKeeper ChangeSet Log
# --------------------------------------------
# 02/12/02 haveblue@elm3b96.(none) 1.970
# 1-2
# --------------------------------------------
#
arch/i386/kernel/setup.c +68 -70
@@ -498,82 +498,68 @@print_memory_map (who); }/* setup_memory_region */ - -static void __init parse_cmdline_early (char ** cmdline_p) +/* +* "mem=nopentium" disables the 4MB page tables. +*/ +static int arch_setup_mem_nopentium (char * arg) { -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 <mem>, overriding the bios size. -* "mem=XXX[KkmM]@XXX[KkmM]" defines a memory region from -* <start> to <start>+<mem>, overriding the bios size. -*/ -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); - }else if (!memcmp (from+4 ,"exactmap" ,8 )) { - from +=8 +4 ; - 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+4 , &from); -if (*from =='@' ) { - start_at =memparse (from+1 , &from); -add_memory_region (start_at, mem_size,E820_RAM ); - }else { -limit_regions (mem_size); - userdef=1 ; - } - } - } +clear_bit (X86_FEATURE_PSE , boot_cpu_data.x86_capability); +return 1 ; +} -/* "acpi=off" disables both ACPI table parsing and interpreter init */ -if (c ==' ' && !memcmp (from,"acpi=off" ,8 )) - acpi_disabled =1 ; +static int arch_setup_mem_exactmap (char * arg) +{ + e820.nr_map =0 ; +printk (KERN_INFO "user-defined physical RAM map:\n" ); +print_memory_map ("user" ); + +return 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 ; +/* +* "mem=XXX[kKmM]" defines a memory region from HIGH_MEM +* to <mem>, overriding the bios size. +* "mem=XXX[KkmM]@XXX[KkmM]" defines a memory region from +* <start> to <start>+<mem>, overriding the bios size. +*/ +static int arch_setup_mem (char * arg) +{ +unsigned long long start_at, mem_size; +char * next; - 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" ); + mem_size =memparse (arg, &next); +if ( arg == next ) +return 0 ; +if (*next =='@' ) { + start_at =memparse (next+1 , &next); +add_memory_region (start_at, mem_size,E820_RAM ); + }else { +limit_regions (mem_size); +printk (KERN_INFO "user-defined physical RAM size:\n" );print_memory_map ("user" ); } +return 1 ; +} + +static int arch_setup_acpi_off (char * arg) +{ + acpi_disabled =1 ; +return 1 ; +} + + +static int arch_setup_highmem (char * arg) +{ + highmem_pages =memparse (arg, &arg) >>PAGE_SHIFT ; +return 1 ; } +__ordered_setup (SETUP_ARCH_LATE ,"mem=nopentium" , arch_setup_mem_nopentium); +__ordered_setup (SETUP_ARCH_LATE ,"mem=exactmap" , arch_setup_mem_exactmap); +__ordered_setup (SETUP_ARCH_LATE ,"mem=" , arch_setup_mem); +__ordered_setup (SETUP_ARCH_LATE ,"acpi=off" , arch_setup_acpi_off); +__ordered_setup (SETUP_ARCH_LATE ,"highmem=" , arch_setup_highmem); +/* * Find the highest page frame number we have available */
@@ -833,10 +819,22 @@ pci_mem_start = low_mem_size; } +extern void __init parse_options (char *line); +extern void __init run_setup (int setup_level); +void __init setup_arch (char **cmdline_p) {unsigned long max_low_pfn; +/* 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' ; +strncpy (command_line, saved_command_line,COMMAND_LINE_SIZE ); + *cmdline_p = command_line; + +parse_options (command_line); +run_setup (SETUP_ARCH_BEGIN ); +pre_setup_arch_hook ();early_cpu_init ();
@@ -875,8 +873,8 @@ data_resource.start =virt_to_phys (&_etext); data_resource.end =virt_to_phys (&_edata)-1 ; -parse_cmdline_early (cmdline_p); - +run_setup (SETUP_ARCH_LATE ); + max_low_pfn =setup_memory ();/*
Re: [PATCH] (3/3) early printk for 386
In reply to Dave HansenFinally, do early printk for i386. Only serial is supported for now, but there is nothing to stop other console types from being added.
arch/i386/kernel/setup.c | 14 ++++++++++++++ drivers/serial/8250.c | 3 +++ kernel/printk.c | 2 +- 3 files changed, 18 insertions(+), 1 deletion(-)
Signature · 2 lines
Dave Hansen [email protected]
# This is a BitKeeper generated patch for the following project:
# Project Name: Linux kernel tree
# This patch format is intended for GNU patch command version 2.5 or higher. # This patch includes the following deltas:
# ChangeSet 1.970 -> 1.971
# drivers/serial/8250.c 1.24 -> 1.25
# arch/i386/kernel/setup.c 1.64 -> 1.65
# kernel/printk.c 1.15 -> 1.16
#
# The following is the BitKeeper ChangeSet Log
# --------------------------------------------
# 02/12/02 haveblue@elm3b96.(none) 1.971
# 2-3
# --------------------------------------------
#
arch/i386/kernel/setup.c +14 -0
@@ -554,6 +554,20 @@return 1 ; } +static int __init early_printk_setup (char * arg) +{ +/* early printk only works for serial ports now */ +if (strncmp (arg,"ttyS" ,4 ) &&strncmp (arg,"/dev/ttyS" ,9 )) +return 0 ; + +console_setup (arg); +serial8250_console_init (); + +printk ("early printk enabled \n" ); +return 1 ; +} + +__ordered_setup (SETUP_ARCH_BEGIN ,"console=" ,early_printk_setup);__ordered_setup (SETUP_ARCH_LATE ,"mem=nopentium" , arch_setup_mem_nopentium);__ordered_setup (SETUP_ARCH_LATE ,"mem=exactmap" , arch_setup_mem_exactmap);__ordered_setup (SETUP_ARCH_LATE ,"mem=" , arch_setup_mem);
drivers/serial/8250.c +3 -0
@@ -1910,6 +1910,9 @@void __init serial8250_console_init (void ) { +if (serial8250_console.flags &CON_ENABLED ) +return ; +serial8250_isa_init_ports ();register_console (&serial8250_console); }
kernel/printk.c +1 -1
@@ -99,7 +99,7 @@/* * Setup a list of consoles. Called from init/main.c */ -static int __init console_setup (char *str) +int __init console_setup (char *str) {struct console_cmdline *c;char name[sizeof (c->name)];