一.内核参数的获取
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
1.2 将参数的读取到内核的过程
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分析-->内核参数的解析
-
}
1.2.1 查找machine_desc并取出参数
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;
-
}
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中
-
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;
-
}
2.2 将内核参数分解,并调用各个回调
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;
-
}
注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
);为例说明一下:
-
其中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就可以找到所有的结构体
start_kernel
--> 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);
阅读(4882) | 评论(0) | 转发(1) |