启动参数处理

内核中定义了以下全局变量保存启动参数相关的字符串,

 

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, &param, &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, &param, &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)

 

posted @ 2017-12-25 22:24  bluebluebluesky  阅读(565)  评论(0编辑  收藏  举报