是用mindmanager转的,格式有点不太好。
1 ATAG
1.1 为什么用ATAG
uboot用atag向kernel传递信息:
atag的定义可以在uboot的include/asm/setup.h 中找到,对应linux中的定义位于arch/arm/include/asm/setup.h中。两者的定义要相同。
参数链表必须以ATAG_CORE 开始,以ATAG_NONE 结束。这里的ATAG_CORE,ATAG_NONE 是各个参数的标记,本身是一个32 位值,例如在setup.h中的定义:
/* The list must start with an ATAG_CORE node */
#define ATAG_CORE0x54410001
其它的参数标记还包括: ATAG_MEM32 , ATAG_INITRD , ATAG_RAMDISK ,ATAG_COMDLINE 等。每个参数标记就代表一个参数结构体,由各个参数结构体构成了参数链表。参数链表的结构体为:
struct tag {
struct tag_header hdr;
union {
struct tag_core core;
struct tag_mem32 mem;
struct tag_videotext videotext;
struct tag_ramdisk ramdisk;
struct tag_initrdinitrd;
struct tag_serialnr serialnr;
struct tag_revision revision;
struct tag_videolfb videolfb;
struct tag_cmdline cmdline;
/*
* Acorn specific
*/
struct tag_acorn acorn;
/*
* DC21285 specific
*/
struct tag_memclkmemclk;
} u;
};
参数结构体包括两个部分,一个是 tag_header 结构体,一个是u 联合体。
tag_header 结构体的定义如下:
struct tag_header
{
u32 size;
u32 tag;
};
其中 size:表示整个tag 结构体的大小(用字的个数来表示,而不是字节的个数),等于tag_header 的大小加上u 联合体的大小,例如,参数结构体ATAG_CORE 的size= (sizeof(tag->tag_header)+sizeof(tag->u.core))>>2,一般通过函数 tag_size(struct * tag_xxx)来获得每个参数结构体的size。其中tag:表示整个tag 结构体的标记,如:ATAG_CORE等。
1.2 uboot传递ATAG
在uboot下的do_bootm_linux函数中,调用:
theKernel (0, machid, bd->bi_boot_params);
来启动内核。
将bi_boot_params即ATAG作为参数传递给linux内核。
调用的时候,R0为0,R1为machid,R2为ATAG的地址。
同时在该函数中,会看到一堆的setup_xxx_tag的函数。cmdline就是这样传的。
1.3 linux接收ATAG
在arch/arm/kernel/head.S中的stext中有这么一句:
/*
* r1 = machine no, r2 = atags or dtb,
* r8 = phys_offset, r9 = cpuid, r10 = procinfo
*/
bl __vet_atags
与uboot中theKernel 的参数是对应的。
__vet_atags是读r2地址上的size和tag看是否是正确的ATAG_CORE。
2 linux接收ATAG流程
2.1 stext (arch/arm/kernel/head.S)
2.2 __mmap_switched (arch/arm/kernel/head-common.S)
str r2, [r6]
.long __atags_pointer @ r6
将ATAG指针存到__atags_pointer变量中。
2.3 start_kernel init/mai.c中
2.3.1 setup_arch(&command_line); 在这里面将command_line指向static char __initdata cmd_line[COMMAND_LINE_SIZE];
setup_machine_tags
从__atags_pointer得到tags。
if (__atags_pointer)
tags = phys_to_virt(__atags_pointer);
else if (mdesc->atag_offset)
tags = (void *)(PAGE_OFFSET + mdesc->atag_offset);
。。。。
if (tags->hdr.tag == ATAG_CORE) {
if (meminfo.nr_banks != 0)
squash_mem_tags(tags);
save_atags(tags);
parse_tags(tags);解析tags
}
parse_tags 解析tags
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;
}
在/arch/arm/kernel/vmlinux.lds中有__tagtable_begin。
.init.tagtable : {
__tagtable_begin = .;
*(.taglist.init)
__tagtable_end = .;
}
所有的解析函数用__tagtable声明:
__tagtable(ATAG_CORE, parse_tag_core);
#define __tag __used __attribute__((__section__(".taglist.init")))
#define __tagtable(tag, fn) \
static const struct tagtable __tagtable_##fn __tag = { tag, fn }
所以所有的解析函数都是放在了taglist.init区域。
在parse_tag_cmdline函数中,将cmdline复制到default_command_line变量中。
strlcat(default_command_line, tag->u.cmdline.cmdline,
保存到boot_command_line
在setup_machine_tags中:
strlcpy(boot_command_line, from, COMMAND_LINE_SIZE);
保存到command_line中
在setup_arch中,将参数command_line指向cmd_line。
/* populate cmd_line too for later use, preserving boot_command_line */
strlcpy(cmd_line, boot_command_line, COMMAND_LINE_SIZE);
*cmdline_p = cmd_line;