rk3188--2.linux-3.0.36中内核参数的读取与传递

来源:互联网 发布:java redis 订阅发布 编辑:程序博客网 时间:2024/05/24 05:57

一.内核参数的获取
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);

0 0
原创粉丝点击