启动参数处理
内核中定义了以下全局变量保存启动参数相关的字符串,
in init/main.c
/* Untouched command line saved by arch-specific code.与default_command_line一致 */ char __initdata boot_command_line[COMMAND_LINE_SIZE]; /* Untouched saved command line (eg. for /proc) 与default_command_line一致*/ char *saved_command_line; /* Command line for parameter parsing 与command_line一致,保存未处理参数*/ static char *static_command_line;
in arch/arm/kernel/setup.c
#define COMMAND_LINE_SIZE 1024 #define CONFIG_CMDLINE "console=ttySAC0 root=/dev/mtdblock2 rootfstype=cramfs init=/linuxrc" /*may overwritten in parse_tag_cmdline(const struct tag *tag)*/ static char default_command_line[COMMAND_LINE_SIZE] __initdata = CONFIG_CMDLINE; static char __initdata cmd_line[COMMAND_LINE_SIZE];
通过下面的调用关系,default_command_line保存启动参数,由parse_cmdline函数处理后。
asmlinkage void __init start_kernel(void) -->char * command_line; -->setup_arch(&command_line); //setup_arch(char ** cmdline_p) /*这里的default_command_line中保存的是u-boot传递过来的命令行参数*/ -->char *from = default_command_line; /*保存未修改的,u-boot传递过来的命令行参数*/ memcpy(boot_command_line, from, COMMAND_LINE_SIZE); boot_command_line[COMMAND_LINE_SIZE-1] = '\0'; -->parse_cmdline(cmdline_p, from);
1.__early_param(name,fn)定义的启动参数
start_kernel -->setup_arch() -->parse_cmdline(cmdline_p, from);
parse_cmdline函数只处理通过__early_param(name,fn)宏定义的启动参数,未处理参数保存在command_line[]数组中。
static void __init parse_cmdline(char **cmdline_p, char *from) { char c = ' ', *to = command_line; int len = 0; for (;;) { if (c == ' ') { extern struct early_params __early_begin, __early_end; struct early_params *p; for (p = &__early_begin; p < &__early_end; p++) { int len = strlen(p->arg); if (memcmp(from, p->arg, len) == 0) { if (to != command_line) to -= 1; from += len; p->fn(&from); while (*from != ' ' && *from != '\0') from++; break; } } } c = *from++; if (!c) break; if (COMMAND_LINE_SIZE <= ++len) break; *to++ = c; } *to = '\0'; *cmdline_p = command_line; }
__early_param(name,fn)宏定义如下,该宏定义的结构体存储在vmlinux.lds.S中指定的early_param.init断中,
__early_begin表示起始地址,__early_end表示结束地址。
/* * Early command line parameters. */ struct early_params { const char *arg; void (*fn)(char **p); }; #define __early_param(name,fn) \ static struct early_params __early_##fn __used \ __attribute__((__section__(".early_param.init"))) = { name, fn }
2. __setup(str, fn),early_param(str, fn)
#define __setup(str, fn) \ __setup_param(str, fn, fn, 0) #define early_param(str, fn) \ __setup_param(str, fn, fn, 1)
上面两个宏利用下面的宏分别定义early=0或者early=1的情况
struct obs_kernel_param { const char *str; //param_name int (*setup_func)(char *); //参数对应的处理函数 int early; //eraly 标志 1--用do_early_param()函数处理该参数 // 0--用unknown_bootoption()函数处理 }; /* 该宏用于声明obs_kernel_param结构体,并放入指定的.init.setup段中 */ #define __setup_param(str, unique_id, fn, early) \ static const char __setup_str_##unique_id[] __initconst \ __aligned(1) = str; \ static struct obs_kernel_param __setup_##unique_id \ __used __section(.init.setup) \ __attribute__((aligned((sizeof(long))))) \ = { __setup_str_##unique_id, fn, early }
在vmlinux.lds中定义了启动参数相关的段
__setup_start = .; *(.init.setup) __setup_end = .; __early_begin = .; *(.early_param.init) __early_end = .;
2.1 do_early_param处理参数为1的obs_kernel_param结构体,即early_param宏定义的参数
start_kernel -->parse_early_param(); -->parse_early_options(char *cmdline) -->parse_args("early options", cmdline, NULL, 0, do_early_param); /* 把args="root=/dev/mtdblock2 rootfstype=yaffs2 init=/linuxrc console=ttySAC0,115200"分解如下: param val root /dev/mtdblock2 rootfstype yaffs2 init /linuxrc console ttySAC0,115200 */ -->args = next_arg(args, ¶m, &val); -->parse_one(param, val, params, num, do_early_param); -->do_early_param(param, val) -->struct obs_kernel_param *p->setup_func(val)
以上注意一点,console=xxx&&earlycon=yyy 也在该函数中处理。
2.2 early=0的参数由下面的流程处理
start_kernel -->parse_args("Booting kernel", static_command_line, __start___param,__stop___param - __start___param,&unknown_bootoption); /* 把args="root=/dev/mtdblock2 rootfstype=yaffs2 init=/linuxrc console=ttySAC0,115200"分解如下: param val root /dev/mtdblock2 rootfstype yaffs2 init /linuxrc console ttySAC0,115200 */ -->args = next_arg(args, ¶m, &val); -->parse_one(param, val, params, num, unknown_bootoption); -->unknown_bootoption(param, val) -->obsolete_checksetup(param) -->struct obs_kernel_param *p->setup_func(line + n)
以上函数执行后,不能识别的参数做如下两种处理:
a.保存到全局环境变量,envp_init[i] = param;
char * envp_init[MAX_INIT_ENVS+2] = { "HOME=/", "TERM=linux", NULL, };
b. 保存到全局参数,argv_init[i] = param;
static char * argv_init[MAX_INIT_ARGS+2] = { "init", NULL, };
3.关于struct kernel_param的参数解析如下,
struct kernel_param { const char *name; unsigned int perm; param_set_fn set; param_get_fn get; union { void *arg; const struct kparam_string *str; const struct kparam_array *arr; }; };
通过module_param(name, type, perm)宏定义一个kernel_param结构体,
/* Returns 0, or -errno. arg is in kp->arg. */ typedef int (*param_set_fn)(const char *val, struct kernel_param *kp); /* Returns length written or -errno. Buffer is 4k (ie. be short!) */ typedef int (*param_get_fn)(char *buffer, struct kernel_param *kp); /* This is the fundamental function for registering boot/module parameters. perm sets the visibility in sysfs: 000 means it's not there, read bits mean it's readable, write bits mean it's writable. */ #define __module_param_call(prefix, name, set, get, arg, perm) \ /* Default value instead of permissions? */ \ static int __param_perm_check_##name __attribute__((unused)) = \ BUILD_BUG_ON_ZERO((perm) < 0 || (perm) > 0777 || ((perm) & 2)) \ + BUILD_BUG_ON_ZERO(sizeof(""prefix) > MAX_PARAM_PREFIX_LEN); \ static const char __param_str_##name[] = prefix #name; \ static struct kernel_param __moduleparam_const __param_##name \ __used \ __attribute__ ((unused,__section__ ("__param"),aligned(sizeof(void *)))) \ = { __param_str_##name, perm, set, get, { arg } } #define module_param_call(name, set, get, arg, perm) \ __module_param_call(MODULE_PARAM_PREFIX, name, set, get, arg, perm) /* Helper functions: type is byte, short, ushort, int, uint, long, ulong, charp, bool or invbool, or XXX if you define param_get_XXX, param_set_XXX and param_check_XXX. */ #define module_param_named(name, value, type, perm) \ param_check_##type(name, &(value)); \ module_param_call(name, param_set_##type, param_get_##type, &value, perm); \ __MODULE_PARM_TYPE(name, #type) #define module_param(name, type, perm) \ module_param_named(name, name, type, perm) #define param_check_int(name, p) __param_check(name, p, int) #define __param_check(name, p, type) \ static inline type *__check_##name(void) { return(p); }
这里以module_param(global_use_mmio, int, 0)为例,按照上面的宏展开为
static inline int *__check_global_use_mmio(void) { return(&(global_use_mmio)); }; static int __param_perm_check_global_use_mmio __attribute__((unused)) = 0; static const char __param_str_global_use_mmio[] = MODULE_PARAM_PREFIX "global_use_mmio"; static struct kernel_param __moduleparam_const __param_global_use_mmio __used __attribute__ ((unused,__section__ ("__param"),aligned(sizeof(void *)))) = { __param_str_global_use_mmio, 0, param_set_int, param_get_int, { &global_use_mmio } }; static const char __mod_global_use_mmiotype71[] __used __attribute__((section(".modinfo"),unused)) = "parmtype=global_use_mmio:int";
最后面解析的放在.modinfo段中的字符串用到了下面的宏
#ifdef MODULE #define ___module_cat(a,b) __mod_ ## a ## b #define __module_cat(a,b) ___module_cat(a,b) #define __MODULE_INFO(tag, name, info) \ static const char __module_cat(name,__LINE__)[] \ __used \ __attribute__((section(".modinfo"),unused)) = __stringify(tag) "=" info #else /* !MODULE */ #define __MODULE_INFO(tag, name, info) #endif #define __MODULE_PARM_TYPE(name, _type) \ __MODULE_INFO(parmtype, name##type, #name ":" _type)
4.下面的宏类似于3.x版本的earlyprintk参数,后面再分析
#define early_platform_init(class_string, platform_driver) \ static __initdata struct early_platform_driver early_driver = { \ .class_str = class_string, \ .pdrv = platform_driver, \ .requested_id = EARLY_PLATFORM_ID_UNSET, \ }; \ static int __init early_platform_driver_setup_func(char *buf) \ { \ return early_platform_driver_register(&early_driver, buf); \ } \ early_param(class_string, early_platform_driver_setup_func)