zoukankan      html  css  js  c++  java
  • 启动参数处理

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

    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] = '';
            -->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 != '')
                            from++;
                        break;
                    }
                }
            }
            c = *from++;
            if (!c)
                break;
            if (COMMAND_LINE_SIZE <= ++len)
                break;
            *to++ = c;
        }
        *to = '';
        *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)
  • 相关阅读:
    EOS之session的数据获取
    c# 数据库操作之ACCESS
    基础之创建与导出
    dotNET5的MVC页面传值方式总结
    dotNET开发之MVC中Controller返回值类型ActionResult方法总结
    C# 计算农历日期方法(2021版)
    普通邮箱设置客户端授权码并开启stmp服务以及关于QQ邮箱“命令顺序不正确。 服务器响应为:Error: need EHLO and AUTH first !”问题全指导
    13 张图,深入理解 Synchronized
    Springboot 注解大全
    python中的print()函数的学习-1
  • 原文地址:https://www.cnblogs.com/yangjiguang/p/8111536.html
Copyright © 2011-2022 走看看