一.内核参数的获取
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.2 将参数的读取到内核的过程
start_kernel
--> setup_arch
1.2.1 查找machine_desc并取出参数
start_kernel
--> setup_arch
--> setup_machine_tags
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中
继续调用
继续调用
二.内核参数的解析
2.1 整理一下流程
start_kernel
--> setup_arch
--> parse_early_param();
在init/main.c中
2.2 将内核参数分解,并调用各个回调
start_kernel
--> setup_arch
--> parse_early_param();
--> parse_early_options
在init/main.c中
这个函数有两个作用:
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:
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 ) ;为例说明一下:
start_kernel
--> setup_arch
--> parse_early_param();
--> parse_early_options
--> early_initrd
在arch/arm/mm/init.c中
param=initrd, val=0x62000000,0x00130000
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中
- __INIT
- __mmap_switched:
- ....
- str r9, [r4] @ Save processor ID
- str r1, [r5] @ Save machine type
- str r2, [r6] @ Save atags pointer //将参数地址保存在r6中
- bic r4, r0, #CR_A @ Clear 'A' bit
- stmia r7, {r0, r4} @ Save control register values
- b start_kernel //下一步进入c程序的start_kernel
- ENDPROC(__mmap_switched)
- .align 2
- .type __mmap_switched_data, %object
- __mmap_switched_data:
- .long __data_loc @ r4
- .long _sdata @ r5
- .long __bss_start @ r6
- .long _end @ r7
- .long processor_id @ r4
- .long __machine_arch_type @ r5
- .long __atags_pointer @ r6 //参数将会保存在__atags_pointer中
- .long cr_alignment @ r7
- .long init_thread_union + THREAD_START_SP @ sp
- .size __mmap_switched_data, . - __mmap_switched_data
start_kernel
--> setup_arch
- void __init setup_arch(char **cmdline_p)
- {
- struct machine_desc *mdesc;
- mdesc = setup_machine_fdt(__atags_pointer); //这是个空函数,mdesc仍为NULL
- if (!mdesc)
- mdesc = setup_machine_tags(machine_arch_type); //1.2.1
- machine_desc = mdesc;
- machine_name = mdesc->name;
- init_mm.start_code = (unsigned long) _text;
- init_mm.end_code = (unsigned long) _etext;
- init_mm.end_data = (unsigned long) _edata;
- init_mm.brk = (unsigned long) _end;
-
- strlcpy(cmd_line, boot_command_line, COMMAND_LINE_SIZE);
- *cmdline_p = cmd_line; //将读取出来的参数赋给cmdline_p,一会就打印出来
- parse_early_param(); //这个函数在2.1分析-->内核参数的解析
- }
start_kernel
--> setup_arch
--> setup_machine_tags
- static struct machine_desc * __init setup_machine_tags(unsigned int nr)
- {
- struct tag *tags = (struct tag *)&init_tags; //这个初始化实际上没有用
- struct machine_desc *mdesc = NULL, *p;
- char *from = default_command_line;
- init_tags.mem.start = PHYS_OFFSET;
- //查找当前的machine_desc结构体,即定义在board-rk3188-ds1006h.c中的
//MACHINE_START(RK30, "RK30board")
- for_each_machine_desc(p)
- if (nr == p->nr) {
- printk("Machine: %s\n", p->name);
- mdesc = p;
- break;
- }
- //__atags_pointer=0x60000800
- if (__atags_pointer)
- tags = phys_to_virt(__atags_pointer);
- if (tags->hdr.tag == ATAG_CORE) {
- if (meminfo.nr_banks != 0)
- squash_mem_tags(tags);
- save_atags(tags);
- parse_tags(tags); //1.2.1.1将参数复制到全局default_command_line中
- }
- //将uboot的参数复制到boot_command_line中
- strlcpy(boot_command_line, from, COMMAND_LINE_SIZE);
- return mdesc;
- }
start_kernel
--> setup_arch
--> setup_machine_tags
--> parse_tags
--> parse_tag
--> parse_tag_cmdline
在arch/arm/kernel/setup.c中
- static void __init parse_tags(const struct tag *t)
- {
- for (; t->hdr.size; t = tag_next(t))
- parse_tag(t);
- }
- static int __init parse_tag(const struct tag *tag)
- {
- extern struct tagtable __tagtable_begin, __tagtable_end;
- struct tagtable *t;
- for (t = &__tagtable_begin; t < &__tagtable_end; t++)
- if (tag->hdr.tag == t->tag) {
- t->parse(tag);
- break;
- }
- return t < &__tagtable_end;
- }
- static int __init parse_tag_cmdline(const struct tag *tag)
- {
- strlcpy(default_command_line, tag->u.cmdline.cmdline, COMMAND_LINE_SIZE);
- }
二.内核参数的解析
2.1 整理一下流程
start_kernel
--> setup_arch
--> parse_early_param();
在init/main.c中
- void __init parse_early_param(void)
- {
- static __initdata int done = 0;
- static __initdata char tmp_cmdline[COMMAND_LINE_SIZE];
- strlcpy(tmp_cmdline, boot_command_line, COMMAND_LINE_SIZE);
- parse_early_options(tmp_cmdline);
- done = 1;
- }
start_kernel
--> setup_arch
--> parse_early_param();
--> parse_early_options
在init/main.c中
- void __init parse_early_options(char *cmdline)
- {
- parse_args("early options", cmdline, NULL, 0, do_early_param);
- }
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
- static int __init do_early_param(char *param, char *val)
- {
- const struct obs_kernel_param *p;
- for (p = __setup_start; p < __setup_end; p++) {
- //p->early=1的,内核参数的名字与结构体名字相符合
- //这儿也解释了, 所有的内核代码里面都有early_initrd这个函数,
- // 却并不是每一个都会调用early_initrd,只有在内核参数中有initrd时才会调用
- if ((p->early && strcmp(param, p->str) == 0) ||
- (strcmp(param, "console") == 0 &&
- strcmp(p->str, "earlycon") == 0)
- )
- p->setup_func(val); //对.init.setup段中符合条件的结构体,调用setup_func函数.
- return 0;
- }
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 ) ;为例说明一下:
- 其中early_param是定义在include/linux/init.h中
- #define early_param(str, fn) \
- __setup_param(str, fn, fn, 1)
- #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 }
- 展开后:
- __setup_param("initrd", early_initrd, early_initrd, 1)
- 再展开:
- static const char __setup_str_early_initrd[] __initconst __aligned(1) = "initrd";
- static struct obs_kernel_param __setup_early_initrd __used __section(.init.setup)
- __attribute__((aligned((sizeof(long))))) = { __setup_str_early_initrd, early_initrd, early }
- 最终的展开形式:
- static struct obs_kernel_param __setup_early_initrd __used __section(.init.setup)
- = { "initrd", early_initrd, 1 }
- 也就是定义了一个结构体:
- struct obs_kernel_param {
- const char *str;
- int (*setup_func)(char *);
- int early;
- };
- 其中section是定义在:arch/arm/kernel/vmlinux.lds中
- . = ALIGN(16); __setup_start = .; *(.init.setup) __setup_end = .;
- 这样遍历段.init.setup就可以找到所有的结构体
--> setup_arch
--> parse_early_param();
--> parse_early_options
--> early_initrd
在arch/arm/mm/init.c中
param=initrd, val=0x62000000,0x00130000
- static int __init early_initrd(char *p) //只用了val,所以p=val
- {
- unsigned long start, size;
- char *endp;
- start = memparse(p, &endp);
- if (*endp == ',') {
- size = memparse(endp + 1, NULL);
- phys_initrd_start = start; //start=0x62000000
- phys_initrd_size = size; //size=0x00130000
- }
- return 0;
- }
- early_param("initrd", early_initrd);