Name: Parameter Core Patch Author: Rusty Russell Status: Experimental Depends: Misc/typecheck.patch.gz D: This patch is a rewrite of the insmod and boot parameter handling, D: to unify them. It only contains the core of the infrastructure, D: and the conversion main boot parameters: all other boot parameters D: and module parameters will no longer work with this change D: (although stubs are in place to let them compile). The other D: follow-up patches do the conversion of the rest of the code. diff -urN -I \$.*\$ --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal linux-2.5.1/arch/i386/kernel/setup.c tmp/arch/i386/kernel/setup.c --- linux-2.5.1/arch/i386/kernel/setup.c Mon Dec 17 16:09:00 2001 +++ tmp/arch/i386/kernel/setup.c Thu Dec 20 16:15:30 2001 @@ -167,24 +167,24 @@ /* * This is set up by the setup-routine at boot-time */ -#define PARAM ((unsigned char *)empty_zero_page) -#define SCREEN_INFO (*(struct screen_info *) (PARAM+0)) -#define EXT_MEM_K (*(unsigned short *) (PARAM+2)) -#define ALT_MEM_K (*(unsigned long *) (PARAM+0x1e0)) -#define E820_MAP_NR (*(char*) (PARAM+E820NR)) -#define E820_MAP ((struct e820entry *) (PARAM+E820MAP)) -#define APM_BIOS_INFO (*(struct apm_bios_info *) (PARAM+0x40)) -#define DRIVE_INFO (*(struct drive_info_struct *) (PARAM+0x80)) -#define SYS_DESC_TABLE (*(struct sys_desc_table_struct*)(PARAM+0xa0)) -#define MOUNT_ROOT_RDONLY (*(unsigned short *) (PARAM+0x1F2)) -#define RAMDISK_FLAGS (*(unsigned short *) (PARAM+0x1F8)) -#define ORIG_ROOT_DEV (*(unsigned short *) (PARAM+0x1FC)) -#define AUX_DEVICE_INFO (*(unsigned char *) (PARAM+0x1FF)) -#define LOADER_TYPE (*(unsigned char *) (PARAM+0x210)) -#define KERNEL_START (*(unsigned long *) (PARAM+0x214)) -#define INITRD_START (*(unsigned long *) (PARAM+0x218)) -#define INITRD_SIZE (*(unsigned long *) (PARAM+0x21c)) -#define COMMAND_LINE ((char *) (PARAM+2048)) +#define PARAMS ((unsigned char *)empty_zero_page) +#define SCREEN_INFO (*(struct screen_info *) (PARAMS+0)) +#define EXT_MEM_K (*(unsigned short *) (PARAMS+2)) +#define ALT_MEM_K (*(unsigned long *) (PARAMS+0x1e0)) +#define E820_MAP_NR (*(char*) (PARAMS+E820NR)) +#define E820_MAP ((struct e820entry *) (PARAMS+E820MAP)) +#define APM_BIOS_INFO (*(struct apm_bios_info *) (PARAMS+0x40)) +#define DRIVE_INFO (*(struct drive_info_struct *) (PARAMS+0x80)) +#define SYS_DESC_TABLE (*(struct sys_desc_table_struct*)(PARAMS+0xa0)) +#define MOUNT_ROOT_RDONLY (*(unsigned short *) (PARAMS+0x1F2)) +#define RAMDISK_FLAGS (*(unsigned short *) (PARAMS+0x1F8)) +#define ORIG_ROOT_DEV (*(unsigned short *) (PARAMS+0x1FC)) +#define AUX_DEVICE_INFO (*(unsigned char *) (PARAMS+0x1FF)) +#define LOADER_TYPE (*(unsigned char *) (PARAMS+0x210)) +#define KERNEL_START (*(unsigned long *) (PARAMS+0x214)) +#define INITRD_START (*(unsigned long *) (PARAMS+0x218)) +#define INITRD_SIZE (*(unsigned long *) (PARAMS+0x21c)) +#define COMMAND_LINE ((char *) (PARAMS+2048)) #define COMMAND_LINE_SIZE 256 #define RAMDISK_IMAGE_START_MASK 0x07FF diff -urN -I \$.*\$ --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal linux-2.5.1/drivers/video/fbmem.c tmp/drivers/video/fbmem.c --- linux-2.5.1/drivers/video/fbmem.c Tue Nov 27 16:53:37 2001 +++ tmp/drivers/video/fbmem.c Thu Dec 20 16:15:30 2001 @@ -308,7 +308,8 @@ extern const char *global_mode_option; -static initcall_t pref_init_funcs[FB_MAX]; +/* FIXME: Should be start_func_t's --RR */ +static __init_func_t pref_init_funcs[FB_MAX]; static int num_pref_init_funcs __initdata = 0; diff -urN -I \$.*\$ --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal linux-2.5.1/include/linux/init.h tmp/include/linux/init.h --- linux-2.5.1/include/linux/init.h Thu Dec 20 14:30:28 2001 +++ tmp/include/linux/init.h Thu Dec 20 16:25:42 2001 @@ -1,7 +1,9 @@ #ifndef _LINUX_INIT_H #define _LINUX_INIT_H +/* Written by many people, modified heavily by Rusty Russell (C) 2001 */ #include +#include /* These macros are used to mark some functions or * initialized data (doesn't apply to uninitialized data) @@ -38,111 +40,32 @@ * Also note, that this data cannot be "const". */ -#ifndef MODULE - -#ifndef __ASSEMBLY__ - -/* - * Used for initialization calls.. - */ -typedef int (*initcall_t)(void); -typedef void (*exitcall_t)(void); - -extern initcall_t __initcall_start, __initcall_end; - -#define __initcall(fn) \ - static initcall_t __initcall_##fn __init_call = fn -#define __exitcall(fn) \ - static exitcall_t __exitcall_##fn __exit_call = fn - -/* - * Used for kernel command line parameter setup - */ -struct kernel_param { - const char *str; - int (*setup_func)(char *); -}; - -extern struct kernel_param __setup_start, __setup_end; - -#define __setup(str, fn) \ - static char __setup_str_##fn[] __initdata = str; \ - static struct kernel_param __setup_##fn __attribute__((unused)) __initsetup = { __setup_str_##fn, fn } - -#endif /* __ASSEMBLY__ */ +/* Roll on Keith Owens! */ +#define KBUILD_OBJECT "unknown" -/* - * Mark functions and data as being only used at initialization - * or exit time. - */ +/* These are for everybody (although not all archs will actually + discard it in modules) */ #define __init __attribute__ ((__section__ (".text.init"))) -#define __exit __attribute__ ((unused, __section__(".text.exit"))) #define __initdata __attribute__ ((__section__ (".data.init"))) -#define __exitdata __attribute__ ((unused, __section__ (".data.exit"))) -#define __initsetup __attribute__ ((unused,__section__ (".setup.init"))) -#define __init_call __attribute__ ((unused,__section__ (".initcall.init"))) -#define __exit_call __attribute__ ((unused,__section__ (".exitcall.exit"))) +#define __exit __attribute__ ((__section__(".text.exit"))) +#define __exitdata __attribute__ ((__section__(".data.exit"))) /* For assembly routines */ #define __INIT .section ".text.init","ax" #define __FINIT .previous #define __INITDATA .section ".data.init","aw" -/** - * module_init() - driver initialization entry point - * @x: function to be run at kernel boot time or module insertion - * - * module_init() will add the driver initialization routine in - * the "__initcall.int" code segment if the driver is checked as - * "y" or static, or else it will wrap the driver initialization - * routine with init_module() which is used by insmod and - * modprobe when the driver is used as a module. - */ -#define module_init(x) __initcall(x); - -/** - * module_exit() - driver exit entry point - * @x: function to be run when driver is removed - * - * module_exit() will wrap the driver clean-up code - * with cleanup_module() when used with rmmod when - * the driver is a module. If the driver is statically - * compiled into the kernel, module_exit() has no effect. - */ -#define module_exit(x) __exitcall(x); - +/* This means "can be init if no module support, otherwise module load + may call it." */ +#ifdef CONFIG_MODULES +#define __init_or_module +#define __initdata_or_module #else +#define __init_or_module __init +#define __initdata_or_module __initdata +#endif /*CONFIG_MODULES*/ -#define __init -#define __exit -#define __initdata -#define __exitdata -#define __initcall(fn) -/* For assembly routines */ -#define __INIT -#define __FINIT -#define __INITDATA - -/* These macros create a dummy inline: gcc 2.9x does not count alias - as usage, hence the `unused function' warning when __init functions - are declared static. We use the dummy __*_module_inline functions - both to kill the warning and check the type of the init/cleanup - function. */ -typedef int (*__init_module_func_t)(void); -typedef void (*__cleanup_module_func_t)(void); -#define module_init(x) \ - int init_module(void) __attribute__((alias(#x))); \ - static inline __init_module_func_t __init_module_inline(void) \ - { return x; } -#define module_exit(x) \ - void cleanup_module(void) __attribute__((alias(#x))); \ - static inline __cleanup_module_func_t __cleanup_module_inline(void) \ - { return x; } - -#define __setup(str,func) /* nothing */ - -#endif - +/* Devices may need to keep their init code around in case of hotplug */ #ifdef CONFIG_HOTPLUG #define __devinit #define __devinitdata @@ -154,5 +77,209 @@ #define __devexit __exit #define __devexitdata __exitdata #endif + +#ifndef __ASSEMBLY__ +/* We use different methods for module and non-module, because in a + module there can be only one initcall (otherwise, what if one + fails?) */ +typedef int (*__init_func_t)(void); +typedef void (*__start_func_t)(void); +typedef int (*__stop_func_t)(void); +typedef void (*__exit_func_t)(void); + +/* Please use a semicolon after these, even if not neccessary: they + may change! */ +#ifndef MODULE +/* Start function only called if and init succeeds */ +#define init_and_startcall(initfn, startfn) \ + static inline __init_func_t __inittest(void) \ + { return initfn; } \ + static inline __start_func_t __starttest(void) \ + { return startfn; } \ + static void __init __initfn(void) \ + { \ + if (initfn() == 0) startfn(); \ + } \ + static __start_func_t __call_##initfn##startfn \ + __attribute__((unused,__section__(".initcall.init"))) \ + = __initfn + +/* Only an initfn: ignore return value */ +#define initcall(initfn) \ + static inline __init_func_t __inittest(void) \ + { return initfn; } \ + static void __init __initfn(void) \ + { \ + initfn(); \ + } \ + static __start_func_t __call_##initfn \ + __attribute__((unused,__section__(".initcall.init"))) \ + = __initfn + +/* Only a startcall. */ +#define startcall(startfn) \ + static inline __start_func_t __starttest(void) \ + { return startfn; } \ + static void __init __initfn(void) \ + { \ + startfn(); \ + } \ + static __start_func_t __call_##startfn \ + __attribute__((unused,__section__(".initcall.init"))) \ + = __initfn + +/* If you're *never* a module, you can simply use this (can have more + than one per file). */ +#define bootcall(startfn) \ + static __start_func_t __call_##startfn \ + __attribute__((unused,__section__(".initcall.init"))) \ + = startfn + +/* These macros create a dummy inline: suppress unused messages, and + check type at the same time! */ +#define stopcall(fn) \ + static inline __stop_func_t __stoptest_##fn(void) \ + { return fn; } + +#define exitcall(fn) \ + static inline __exit_func_t __exittest_##fn(void) \ + { return fn; } + +/* This *almost* does want we want. */ +#define NAME_PREFIX KBUILD_OBJECT "." +#else /* MODULE */ +#define init_and_startcall(initfn, startfn) \ + static inline __init_func_t __inittest(void) \ + { return __initfn; } \ + static inline __start_func_t __starttest(void) \ + { return startfn; } \ + static int __init __initfn(void) \ + { \ + int __ret = 0; \ + if (initfn) __ret = initfn(); \ + if (__ret == 0 && startfn) startfn(); \ + return __ret; \ + } + +#define initcall(initfn) \ + static inline __init_func_t __inittest(void) \ + { return initfn; } \ + static int __attribute__((unused,__section__(".initcall.init"))) \ + __initfn(void) \ + { \ + return initfn(); \ + } + +#define startcall(startfn) \ + static inline __start_func_t __starttest(void) \ + { return startfn; } \ + static int __attribute__((unused,__section__(".initcall.init"))) \ + __initfn(void) \ + { \ + startfn(); \ + return 0; \ + } + +/* Declare these if you want your module unloadable. You must declare + a stopcall if you hav a startfn, and an exitcall if you have an + initfn */ +#define stopcall(fn) \ + extern inline __stop_func_t __stoptest_##fn(void) \ + { return fn; } \ + int __stopfn(void) __attribute__((alias(#fn))); + +#define exitcall(fn) \ + extern inline __exit_func_t __exittest_##fn(void) \ + { return fn; } \ + void __exitfn(void) __attribute__((alias(#fn))); + +#define NAME_PREFIX "" +#endif /*MODULE*/ + +/* This is the fundamental function for registering boot/module + parameters. The function (returns -errno) is called with the given + arg, and gets called with the part of the arg after the =, and a + pointer to the kernel_param (kp->arg is the third arg of this + macro). */ +#define PARAM_CALL(name, fn, arg) \ + static char __param_str_##name[] __initdata = NAME_PREFIX #name; \ + static struct kernel_param __param_##name \ + __attribute__ ((unused,__section__ (".setup.init"))) \ + = { __param_str_##name, fn, arg } + +/* Helper functions: type is byte, short, ushort, int, uint, long, + ulong, charp, bool or invbool, or XXX if you define parse_XXX + and __CHECK_XXX. perm is for future: it means the permissions for + the file in /proc/sys (0 means no file, any read bits means it's + readable, and any write bits means it's writable). */ +#define PARAM_ARRAY(name, valueptr, type, min, max, perm) \ + __CHECK_##type(name, valueptr) \ + static struct kparam_range __param_range_##name __initdata \ + = { min, max, perm, parse_##type, valueptr }; \ + PARAM_CALL(name, parse_range, &__param_range_##name) + +#define PARAM_NAMED(name, value, type, perm) \ + PARAM_ARRAY(name, &value, type, 1, 1, perm) + +#define PARAM(name, type, perm) \ + PARAM_ARRAY(name, &name, type, 1, 1, perm) + +/* Actually copy string: len param is maximum total length, including NUL. */ +#define PARAM_STRING(name, valueptr, len, perm) \ + __CHECK_string(name, valueptr) \ + static struct kparam_string __param_string_##name __initdata \ + = { len, perm, valueptr }; \ + PARAM_CALL(name, parse_kstring, &__param_string_##name) + +/* We need the extra void* ptr here; the usual trick of allowing users + to append any info they want on the end of the struct cannot be + used here, as we treat the .param.init section as an array. */ +struct kernel_param { + const char *name; + int (*param_func)(char *val, struct kernel_param *kp); + void *arg; +}; + +struct kparam_string { + unsigned int maxlen; + unsigned int perm; + void *ptr; +}; + +struct kparam_range { + unsigned int min, max; + unsigned int perm; + void *(*parse_one)(char *val, void *ptr); + void *ptr; +}; + +extern int parse_range(char *, struct kernel_param *) __init_or_module; +extern int parse_kstring(char *,struct kernel_param *) __init_or_module; + +/* These all return NULL, or the second arg incremented by size. */ +extern void *parse_byte(char *val, void *bytep) __init_or_module; +extern void *parse_short(char *val, void *shortp) __init_or_module; +extern void *parse_ushort(char *val, void *ushortp) __init_or_module; +extern void *parse_int(char *val, void *intp) __init_or_module; +extern void *parse_uint(char *val, void *uintp) __init_or_module; +extern void *parse_long(char *val, void *longp) __init_or_module; +extern void *parse_ulong(char *val, void *ulongp) __init_or_module; +extern void *parse_charp(char *val, void *charpp) __init_or_module; +extern void *parse_bool(char *val, void *intp) __init_or_module; +extern void *parse_invbool(char *val, void *intp) __init_or_module; + +/* Called on module insert or kernel boot */ +extern int parse_args(char *args, + struct kernel_param *params, + unsigned num, + int (*unknown)(char *param, char *val)); +#endif /* !__ASSEMBLY__ */ + +/* THESE ARE OBSOLETE AND WILL VANISH */ +/* Some people were lazy and missed the semicolon */ +#define module_init(initfn) initcall(initfn); +#define module_exit(exitfn) static inline void __use_##exitfn(void) { (void)exitfn; }; +#define __initcall(initfn) initcall(initfn); +#define __setup(string, func) #endif /* _LINUX_INIT_H */ diff -urN -I \$.*\$ --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal linux-2.5.1/init/do_mounts.c tmp/init/do_mounts.c --- linux-2.5.1/init/do_mounts.c Mon Dec 17 16:09:13 2001 +++ tmp/init/do_mounts.c Thu Dec 20 16:25:07 2001 @@ -52,24 +52,22 @@ static int do_devfs = 0; -static int __init readonly(char *str) +static int __init readonly(char *str, struct kernel_param *kp) { - if (*str) - return 0; + if (str) return -EINVAL; root_mountflags |= MS_RDONLY; - return 1; + return 0; } -static int __init readwrite(char *str) +static int __init readwrite(char *str, struct kernel_param *kp) { - if (*str) - return 0; + if (str) return -EINVAL; root_mountflags &= ~MS_RDONLY; - return 1; + return 0; } -__setup("ro", readonly); -__setup("rw", readwrite); +PARAM_CALL(ro, readonly, NULL); +PARAM_CALL(rw, readwrite, NULL); static struct dev_name_struct { const char *name; @@ -220,7 +218,7 @@ return to_kdev_t(base + simple_strtoul(line,NULL,base?10:16)); } -static int __init root_dev_setup(char *line) +static int __init root_dev_setup(char *line, struct kernel_param *kp) { int i; char ch; @@ -234,27 +232,15 @@ if ( isspace (ch) || (ch == ',') || (ch == '\0') ) break; root_device_name[i] = ch; } - return 1; + return 0; } -__setup("root=", root_dev_setup); - static char * __initdata root_mount_data; -static int __init root_data_setup(char *str) -{ - root_mount_data = str; - return 1; -} - static char * __initdata root_fs_names; -static int __init fs_names_setup(char *str) -{ - root_fs_names = str; - return 1; -} -__setup("rootflags=", root_data_setup); -__setup("rootfstype=", fs_names_setup); +PARAM_CALL(root, root_dev_setup, NULL); +PARAM_NAMED("rootflags", &root_mount_data, charp, 000); +PARAM_NAMED("rootfstype", &root_fs_names, charp, 000); static void __init get_fs_names(char *page) { diff -urN -I \$.*\$ --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal linux-2.5.1/init/main.c tmp/init/main.c --- linux-2.5.1/init/main.c Mon Dec 17 16:09:13 2001 +++ tmp/init/main.c Thu Dec 20 16:24:01 2001 @@ -71,6 +71,10 @@ #include #endif +/* We don't want prefix for this file (our args are special) */ +#undef NAME_PREFIX +#define NAME_PREFIX "" + /* * Versions of gcc older than that listed below may actually compile * and link okay, but the end product can have subtle run time bugs. @@ -112,43 +116,24 @@ /* * Boot command-line arguments */ -#define MAX_INIT_ARGS 8 -#define MAX_INIT_ENVS 8 +#define MAX_INIT_ARGS 9 +#define MAX_INIT_ENVS 9 extern void time_init(void); extern void softirq_init(void); int rows, cols; -char *execute_command; - -static char * argv_init[MAX_INIT_ARGS+2] = { "init", NULL, }; -char * envp_init[MAX_INIT_ENVS+2] = { "HOME=/", "TERM=linux", NULL, }; - -static int __init profile_setup(char *str) -{ - int par; - if (get_option(&str,&par)) prof_shift = par; - return 1; -} - -__setup("profile=", profile_setup); +/* Created by linker magic */ +extern struct kernel_param __setup_start, __setup_end; +extern __start_func_t __initcall_start, __initcall_end; -static int __init checksetup(char *line) -{ - struct kernel_param *p; +int root_mountflags = MS_RDONLY; +char *execute_command; /* = NULL */ +char root_device_name[64]; - p = &__setup_start; - do { - int n = strlen(p->str); - if (!strncmp(line,p->str,n)) { - if (p->setup_func(line+n)) - return 1; - } - p++; - } while (p < &__setup_end); - return 0; -} +static char * argv_init[MAX_INIT_ARGS+1] = { "init", NULL, }; +char * envp_init[MAX_INIT_ENVS+1] = { "HOME=/", "TERM=linux", NULL, }; /* this should be approx 2 Bo*oMips to start (note initial shift), and will still work even if initially too large, it will just take slightly longer */ @@ -200,93 +185,43 @@ (loops_per_jiffy/(5000/HZ)) % 100); } -static int __init debug_kernel(char *str) -{ - if (*str) - return 0; - console_loglevel = 10; - return 1; -} - -static int __init quiet_kernel(char *str) -{ - if (*str) - return 0; - console_loglevel = 4; - return 1; -} - -__setup("debug", debug_kernel); -__setup("quiet", quiet_kernel); +PARAM_CALL(debug, debug_kernel, NULL); +PARAM_CALL(quiet, quiet_kernel, NULL); +PARAM_NAMED(init, execute_command, charp, 000); -/* - * 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 '='. - * - * 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 parse_options(char *line) +/* Unknown boot options get handed to init, unless they look like + failed parameters */ +static int unknown_bootoption(char *param, char *val) { - char *next,*quote; - int args, envs; + printk("Handling unknown boot option %s = %s\n", + param, val ?: ""); + /* Preemptive maintenance for "why didn't my mispelled command + line work?" */ + if (strchr(param, '.')) + panic("Unknown boot option `%s'", param); - if (!*line) - return; - args = 0; - envs = 1; /* TERM is set to 'linux' by default */ - next = line; - while ((line = next) != NULL) { - quote = strchr(line,'"'); - next = strchr(line, ' '); - while (next != NULL && quote != NULL && quote < next) { - /* we found a left quote before the next blank - * now we have to find the matching right quote - */ - next = strchr(quote+1, '"'); - if (next != NULL) { - quote = strchr(next+1, '"'); - next = strchr(next+1, ' '); - } - } - 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 (val) { + /* Environment option */ + unsigned int i; + for (i = 0; !envp_init[i]; i++) { + if (i == MAX_INIT_ENVS) + panic("Too many boot env vars at `%s'", param); } - 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; + /* Change NUL term back to "=" */ + val[-1] = '='; + envp_init[i] = param; + } else { + /* Command line option */ + unsigned int i; + for (i = 0; !argv_init[i]; i++) { + if (i == MAX_INIT_ARGS) + panic("Too many boot init vars at `%s'",param); } + argv_init[i] = param; } - argv_init[args+1] = NULL; - envp_init[envs+1] = NULL; + return 0; } - extern void setup_arch(char **); extern void cpu_idle(void); @@ -360,7 +295,10 @@ printk(linux_banner); setup_arch(&command_line); printk("Kernel command line: %s\n", saved_command_line); - parse_options(command_line); + if (parse_args(command_line, &__setup_start, + &__setup_end - &__setup_start - 1, + &unknown_bootoption) != 0) + panic("Command line bad"); trap_init(); init_IRQ(); sched_init(); @@ -434,7 +372,7 @@ static void __init do_initcalls(void) { - initcall_t *call; + __start_func_t *call; call = &__initcall_start; do { diff -urN -I \$.*\$ --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal linux-2.5.1/kernel/Makefile tmp/kernel/Makefile --- linux-2.5.1/kernel/Makefile Mon Dec 17 16:09:13 2001 +++ tmp/kernel/Makefile Thu Dec 20 16:15:30 2001 @@ -9,13 +9,13 @@ O_TARGET := kernel.o -export-objs = signal.o sys.o kmod.o context.o ksyms.o pm.o exec_domain.o \ +export-objs = signal.o sys.o kmod.o context.o ksyms.o pm.o exec_domain.o init.o \ printk.o device.o obj-y = sched.o dma.o fork.o exec_domain.o panic.o printk.o \ module.o exit.o itimer.o info.o time.o softirq.o resource.o \ sysctl.o acct.o capability.o ptrace.o timer.o user.o \ - signal.o sys.o kmod.o context.o device.o + signal.o sys.o kmod.o context.o init.o device.o obj-$(CONFIG_UID16) += uid16.o obj-$(CONFIG_MODULES) += ksyms.o diff -urN -I \$.*\$ --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal linux-2.5.1/kernel/init.c tmp/kernel/init.c --- linux-2.5.1/kernel/init.c Thu Jan 1 10:00:00 1970 +++ tmp/kernel/init.c Thu Dec 20 16:15:30 2001 @@ -0,0 +1,327 @@ +/* Helpers for initial module or kernel cmdline parsing + Copyright (C) 2001 Rusty Russell. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#include +#include +#include +#include +#include +#include +#include +#include + +int parse_range(char *val, struct kernel_param *kp) +{ + const struct kparam_range *prange = kp->arg; + unsigned int count; + char *comma; + void *ptr; + + count = 0; + ptr = prange->ptr; + + /* No equals sign. */ + if (val == NULL) { + if (prange->min > 1 || prange->max > 1 + || !prange->parse_one(NULL, ptr)) { + printk("%s: expects arguments\n", kp->name); + return -EINVAL; + } + return 0; + } + + /* We expect a comma-separated list of values. */ + do { + if (count >= prange->max) { + printk("%s: can only take %u arguments\n", + kp->name, prange->max-1); + return -EINVAL; + } + comma = strchr(val, ','); + if (comma) *comma = '\0'; + /* parse_one is parse_byte, parse_int, etc... */ + ptr = prange->parse_one(val, ptr); + if (!ptr) { + printk("%s: malformed entry %u\n", kp->name, count+1); + return -EINVAL; + } + val = comma+1; + count++; + } while (comma); + + if (count < prange->min) { + printk("%s: needs at least %u arguments\n", + kp->name, prange->min); + return -EINVAL; + } + return 0; +} + +int parse_kstring(char *val, struct kernel_param *kp) +{ + const struct kparam_string *kstring = kp->arg; + + /* No equals sign in wrong */ + if (!val) { + printk("%s: expects a string\n", kp->name); + return -EINVAL; + } + if (strlen(val) >= kstring->maxlen) { + printk("%s: string must be less than %u characters\n", + kp->name, kstring->maxlen); + return -EINVAL; + } + strcpy(kstring->ptr, val); + return 0; +} + +void *parse_byte(char *val, void *bytep) +{ + char *endp; + unsigned long l; + + if (!val) return NULL; + l = simple_strtoul(val, &endp, 0); + if (endp == val || *endp || l > 0xFF) + return NULL; + *((unsigned char *)bytep) = l; + return bytep + 1; +} + +void *parse_short(char *val, void *shortp) +{ + char *endp; + long l; + + if (!val) return NULL; + l = simple_strtol(val, &endp, 0); + if (endp == val || *endp || l > 0x7FFF || l < -0x8000) + return NULL; + *((short *)shortp) = l; + return shortp + sizeof(short); +} + +void *parse_ushort(char *val, void *ushortp) +{ + char *endp; + unsigned long l; + + if (!val) return NULL; + l = simple_strtoul(val, &endp, 0); + if (endp == val || *endp || l > 0xFFFF) + return NULL; + *((unsigned short *)ushortp) = l; + return ushortp + sizeof(unsigned short); +} + +void *parse_int(char *val, void *intp) +{ + char *endp; + long l; + + if (!val) return NULL; + l = simple_strtol(val, &endp, 0); + if (endp == val || *endp || l > INT_MAX || l < INT_MIN) + return NULL; + *((int *)intp) = l; + return intp + 4; +} + +void *parse_uint(char *val, void *uintp) +{ + char *endp; + unsigned long l; + + if (!val) return NULL; + l = simple_strtoul(val, &endp, 0); + if (endp == val || *endp || l > 0xFFFFFFFF) + return NULL; + *((unsigned int *)uintp) = l; + return uintp + sizeof(unsigned int); +} + +void *parse_long(char *val, void *longp) +{ + char *endp; + + if (!val) return NULL; + *((long *)longp) = simple_strtol(val, &endp, 0); + if (endp == val || *endp) + return NULL; + return longp + sizeof(long); +} + +void *parse_ulong(char *val, void *ulongp) +{ + char *endp; + + if (!val) return NULL; + *((unsigned long *)ulongp) = simple_strtoul(val, &endp, 0); + if (endp == val || *endp) + return NULL; + return ulongp + sizeof(unsigned long); +} + +void *parse_charp(char *val, void *charpp) +{ + if (!val) return NULL; + + *(char **)charpp = val; + return charpp + sizeof(char *); +} + +void *parse_bool(char *val, void *intp) +{ + /* No equals means "set"... */ + if (!val) val = "1"; + + /* One of =[yYnN01] */ + switch (val[0]) { + case 'y': case 'Y': case '1': + *(int *)intp = 1; + break; + case 'n': case 'N': case '0': + *(int *)intp = 0; + break; + default: + return NULL; + } + + return intp + sizeof(int); +} + +void *parse_invbool(char *val, void *intp) +{ + /* No equals means "unset"... */ + if (!val) val = "0"; + + /* One of =[yYnN01] */ + switch (val[0]) { + case 'y': case 'Y': case '1': + *(int *)intp = 0; + break; + case 'n': case 'N': case '0': + *(int *)intp = 1; + break; + default: + return NULL; + } + + return intp + sizeof(int); +} + +/* hyphens and underscores equivalent */ +static int parameq(const char *param, const char *name) +{ + unsigned int i; + + for (i = 0; param[i] && name[i]; i++) { + if (param[i] == '-' && name[i] == '_') + continue; + if (param[i] != name[i]) + return 0; + } + return (param[i] == '\0' && name[i] == '\0'); +} + +static int parse_one(char *param, + char *val, + struct kernel_param *params, + unsigned num_params, + int (*handle_unknown)(char *param, char *val)) +{ + unsigned int i; + + /* Find parameter */ + for (i = 0; i < num_params; i++) { + printk("Comparing with %p (%s)...\n", + params[i].name, params[i].name); + if (parameq(param, params[i].name)) { + printk("They are equal! Calling %p\n", + params[i].param_func); + return params[i].param_func(val, ¶ms[i]); + } + } + + if (handle_unknown) { + printk("Unknown argument: calling %p\n", handle_unknown); + return handle_unknown(param, val); + } + + printk("Unknown argument `%s'", param); + return -ENOENT; +} + +/* Args looks like "foo=bar,bar2 baz=fuz wiz". */ +int parse_args(char *args, + struct kernel_param *params, + unsigned num, + int (*unknown)(char *param, char *val)) +{ + printk("Parsing ARGS: %s\n", args); + while (args && *args) { + int ret; + char *equals, *space, *quote; + + /* Find next separator */ + space = strchr(args, ' '); + /* Is there a quote before it? */ + quote = args; + while ((quote = strchr(quote, '"')) != NULL && quote < space) { + /* Find matching quote */ + quote = strchr(quote+1, '"'); + /* Push space marker back to beyond quote */ + if (quote > space) + space = strchr(quote, ' '); + } + + while (space && *space == ' ') *(space++) = '\0'; + + printk("Parsing ARG: %s\n", args); + equals = strchr(args, '='); + if (equals) { + *equals = '\0'; + ret = parse_one(args, equals+1, params, num, unknown); + } else { + ret = parse_one(args, NULL, params, num, unknown); + } + if (ret != 0) { + printk(KERN_ERR + "Option `%s' did not like argument `%s'\n", + args, equals ? equals + 1 : ""); + return ret; + } + + args = space; + } + + /* All parsed OK. */ + return 0; +} + +EXPORT_SYMBOL(parse_range); +EXPORT_SYMBOL(parse_kstring); +EXPORT_SYMBOL(parse_byte); +EXPORT_SYMBOL(parse_short); +EXPORT_SYMBOL(parse_ushort); +EXPORT_SYMBOL(parse_int); +EXPORT_SYMBOL(parse_uint); +EXPORT_SYMBOL(parse_long); +EXPORT_SYMBOL(parse_ulong); +EXPORT_SYMBOL(parse_charp); +EXPORT_SYMBOL(parse_bool); +EXPORT_SYMBOL(parse_invbool);