uboot給linux傳遞引數流程
是用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;