Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2159711
  • 博文数量: 438
  • 博客积分: 3871
  • 博客等级: 中校
  • 技术积分: 6075
  • 用 户 组: 普通用户
  • 注册时间: 2011-09-10 00:11
个人简介

邮箱: wangcong02345@163.com

文章分类

全部博文(438)

文章存档

2017年(15)

2016年(119)

2015年(91)

2014年(62)

2013年(56)

2012年(79)

2011年(16)

分类: Android平台

2014-01-09 15:12:51

一.内核参数的获取
1.1 kernel获取uboot传递的参数地址
a. 在arch/arm/kernel/head.S中,内核解压后就会跳到这个文件中
ldr r13, =__mmap_switched       @ address to jump to after
b. 在arch/arm/kernel/head-common.S中
  1. __INIT
  2. __mmap_switched:
  3.     ....
  4.     str    r9, [r4]            @ Save processor ID
  5.     str    r1, [r5]            @ Save machine type
  6.     str    r2, [r6]            @ Save atags pointer   //将参数地址保存在r6中
  7.     bic    r4, r0, #CR_A       @ Clear 'A' bit
  8.     stmia    r7, {r0, r4}      @ Save control register values
  9.     b    start_kernel          //下一步进入c程序的start_kernel
  10. ENDPROC(__mmap_switched)

  11.     .align    2
  12.     .type    __mmap_switched_data, %object
  13. __mmap_switched_data:
  14.     .long    __data_loc            @ r4
  15.     .long    _sdata                @ r5
  16.     .long    __bss_start           @ r6
  17.     .long    _end                  @ r7
  18.     .long    processor_id          @ r4
  19.     .long    __machine_arch_type   @ r5
  20.     .long    __atags_pointer       @ r6       //参数将会保存在__atags_pointer中
  21.     .long    cr_alignment          @ r7
  22.     .long    init_thread_union + THREAD_START_SP @ sp
  23.     .size    __mmap_switched_data, . - __mmap_switched_data
1.2 将参数的读取到内核的过程
start_kernel
    --> setup_arch
  1. void __init setup_arch(char **cmdline_p)
  2. {
  3.     struct machine_desc *mdesc;
  4.     mdesc = setup_machine_fdt(__atags_pointer);  //这是个空函数,mdesc仍为NULL
  5.     if (!mdesc)  
  6.         mdesc = setup_machine_tags(machine_arch_type);  //1.2.1
  7.     machine_desc = mdesc;
  8.     machine_name = mdesc->name;

  9.     init_mm.start_code = (unsigned long) _text;
  10.     init_mm.end_code = (unsigned long) _etext;
  11.     init_mm.end_data = (unsigned long) _edata;
  12.     init_mm.brk     = (unsigned long) _end;
  13.     
  14.     strlcpy(cmd_line, boot_command_line, COMMAND_LINE_SIZE);
  15.     *cmdline_p = cmd_line;    //将读取出来的参数赋给cmdline_p,一会就打印出来

  16.     parse_early_param();     //这个函数在2.1分析-->内核参数的解析
  17. }
1.2.1 查找machine_desc并取出参数
start_kernel
    --> setup_arch
        --> setup_machine_tags
  1. static struct machine_desc * __init setup_machine_tags(unsigned int nr)
  2. {
  3.     struct tag *tags = (struct tag *)&init_tags;  //这个初始化实际上没有用
  4.     struct machine_desc *mdesc = NULL, *p;
  5.     char *from = default_command_line;
  6.     init_tags.mem.start = PHYS_OFFSET;
  7.     //查找当前的machine_desc结构体,即定义在board-rk3188-ds1006h.c中的
        //MACHINE_START(RK30, "RK30board")
  8.     for_each_machine_desc(p)
  9.         if (nr == p->nr) {
  10.             printk("Machine: %s\n", p->name);
  11.             mdesc = p;
  12.             break;
  13.         }
  14.     //__atags_pointer=0x60000800
  15.     if (__atags_pointer)
  16.         tags = phys_to_virt(__atags_pointer);

  17.     if (tags->hdr.tag == ATAG_CORE) {
  18.         if (meminfo.nr_banks != 0)
  19.             squash_mem_tags(tags);
  20.         save_atags(tags);
  21.         parse_tags(tags);       //1.2.1.1将参数复制到全局default_command_line
  22.     }
  23.     //将uboot的参数复制到boot_command_line中
  24.     strlcpy(boot_command_line, from, COMMAND_LINE_SIZE);

  25.     return mdesc;
  26. }
1.2.1.1  取参数到全局变量default_command_line
start_kernel
    --> setup_arch
        --> setup_machine_tags
        --> parse_tags
        --> parse_tag
            --> parse_tag_cmdline
在arch/arm/kernel/setup.c中
  1. static void __init parse_tags(const struct tag *t)
  2. {
  3.     for (; t->hdr.size; t = tag_next(t))
  4.         parse_tag(t);
  5. }
继续调用
  1. static int __init parse_tag(const struct tag *tag)
  2. {
  3.     extern struct tagtable __tagtable_begin, __tagtable_end;
  4.     struct tagtable *t;

  5.     for (t = &__tagtable_begin; t < &__tagtable_end; t++)
  6.         if (tag->hdr.tag == t->tag) {
  7.             t->parse(tag);
  8.             break;
  9.         }

  10.     return t < &__tagtable_end;
  11. }
继续调用
  1. static int __init parse_tag_cmdline(const struct tag *tag)
  2. {
  3.     strlcpy(default_command_line, tag->u.cmdline.cmdline, COMMAND_LINE_SIZE);
  4. }

二.内核参数的解析
2.1 整理一下流程
start_kernel
    --> setup_arch
    --> parse_early_param();
在init/main.c中
  1. void __init parse_early_param(void)
  2. {
  3.     static __initdata int done = 0;
  4.     static __initdata char tmp_cmdline[COMMAND_LINE_SIZE];

  5.     strlcpy(tmp_cmdline, boot_command_line, COMMAND_LINE_SIZE);
  6.     parse_early_options(tmp_cmdline);
  7.     done = 1;
  8. }
2.2 将内核参数分解,并调用各个回调
start_kernel
    --> setup_arch
    --> parse_early_param();
        --> parse_early_options
在init/main.c中
  1. void __init parse_early_options(char *cmdline)
  2. {
  3.     parse_args("early options", cmdline, NULL, 0, do_early_param);
  4. }
这个函数有两个作用:
a是解析字符串
从uboot传入的字符串是:
Kernel command line: console=ttyFIQ0 androidboot.console=ttyFIQ0 init=/init initrd=0x62000000,0x00130000 mtdparts=rk29xxnand:0x00002000@0x00002000(misc),0x00004000@0x00004000(kernel),0x00008000@0x00008000(boot),0x00010000@0x00010000(recovery),0x00020000@0x00020000(backup),0x00040000@0x00040000(cache),0x00200000@0x00080000(userdata),0x00002000@0x00280000(kpanic),0x00100000@0x00282000(system),-@0x00382000(user) bootver=2013-06-20#1.24 firmware_ver=4.1.1
那么经过解析后的字符串是:
param=console, val=ttyFIQ0
param=androidboot.console, val=ttyFIQ0
param=init, val=/init
param=initrd, val=0x62000000,0x00130000
param=mtdparts, val=rk29xxnand:0x00002000@0x00002000(misc),0x00004000@0x00004000(kernel),0x00008000@0x00008000(boot),0x00010000@0x00010000(recovery),0x00020000@0x00020000(backup),0x00040000@0x00040000(cache),0x00200000@0x00080000(userdata),0x00002000@0x00280000(kpanic),0x00100000@0x00282000(system),-@0x00382000(user)
其中param与val都是两个char*,
b是对每一个param调用回调函数do_early_param
  1. static int __init do_early_param(char *param, char *val)
  2. {
  3.     const struct obs_kernel_param *p;
  4.     for (p = __setup_start; p < __setup_end; p++) {
  5.         //p->early=1的,内核参数的名字与结构体名字相符合
  6.         //这儿也解释了, 所有的内核代码里面都有early_initrd这个函数,
  7.        // 却并不是每一个都会调用early_initrd,只有在内核参数中有initrd时才会调用
  8.         if ((p->early && strcmp(param, p->str) == 0) ||  
  9.          (strcmp(param, "console") == 0 &&
  10.          strcmp(p->str, "earlycon") == 0)
  11.         )
  12.        p->setup_func(val);     //对.init.setup段中符合条件的结构体,调用setup_func函数.
  13.     return 0;
  14. }
注1:     
    do_early_param只是对p->early=1的调用其setup_func   
    对p->early!=1的在函数obsolete_checksetup中执行
注2:early_initrd会不会每一个内核都执行?
      所有的内核代码里面都有这一句: early_param("initrd", early_initrd);为什么不是所有的内核都会执行early_intrd这个函数呢?
  [解释]:
      在do_early_param中,要想执行回调p->setup_func(这儿对应early_initrd),必须満足条件 strcmp(param, p->str) == 0.
      p->str是定义的结构体__setup_early_initrd的str="initrd"
      param是uboot传给kernel的参数中的参数,如果内核参数没有initrd,那么early_initrd就不会即行.    
      例如这个: root=/dev/mtdblock2 rootfstype=yaffs2 init=/linuxrc nconsole=tty1 console=ttySAC0,115200 就不会执行early_initrd     
注3: 这个p->setup_func这个指针是哪儿来的呢?
以early_param("initrd", early_initrd);为例说明一下:
  1. 其中early_param是定义在include/linux/init.h中
  2. #define early_param(str, fn)                    \
  3.     __setup_param(str, fn, fn, 1)

  4. #define __setup_param(str, unique_id, fn, early)            \
  5.     static const char __setup_str_##unique_id[] __initconst    \
  6.         __aligned(1) = str; \
  7.     static struct obs_kernel_param __setup_##unique_id    \
  8.         __used __section(.init.setup)            \
  9.         __attribute__((aligned((sizeof(long)))))    \
  10.         = { __setup_str_##unique_id, fn, early }
  11. 展开后:
  12. __setup_param("initrd", early_initrd, early_initrd, 1)
  13. 再展开:
  14. static const char __setup_str_early_initrd[] __initconst __aligned(1) = "initrd";
  15. static struct obs_kernel_param __setup_early_initrd    __used __section(.init.setup)        
  16. __attribute__((aligned((sizeof(long))))) = { __setup_str_early_initrd, early_initrd, early }
  17. 最终的展开形式:
  18. static struct obs_kernel_param __setup_early_initrd    __used __section(.init.setup)        
  19. = { "initrd", early_initrd, 1 }
  20. 也就是定义了一个结构体:
  21. struct obs_kernel_param {
  22.     const char *str;
  23.     int (*setup_func)(char *);
  24.     int early;
  25. };
  26. 其中section是定义在:arch/arm/kernel/vmlinux.lds中
  27. . = ALIGN(16); __setup_start = .; *(.init.setup) __setup_end = .;
  28. 这样遍历段.init.setup就可以找到所有的结构体
start_kernel
    --> setup_arch
    --> parse_early_param();
        --> parse_early_options
            --> early_initrd
在arch/arm/mm/init.c中
param=initrd, val=0x62000000,0x00130000
  1. static int __init early_initrd(char *p)  //只用了val,所以p=val
  2. {
  3.     unsigned long start, size;
  4.     char *endp;
  5.     start = memparse(p, &endp);
  6.     if (*endp == ',') {
  7.         size = memparse(endp + 1, NULL);

  8.         phys_initrd_start = start;   //start=0x62000000
  9.         phys_initrd_size = size;     //size=0x00130000
  10.     }
  11.     return 0;
  12. }
  13. early_param("initrd", early_initrd);


阅读(4909) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~