在U-BOOT的最后提到传递参数给内核,调用如下 //调用内核,寄存器R0=0,R1=机器类型,R2=参数块地址 theKernel (0, bd->bi_arch_number, bd->bi_boot_params); 而这两个参数是如何传递给kernel的分析如下(只涉及到非汇编部分) 一.u-boot传递TAG到kernel的解析 在setup_arch函数的parse_tags中对传递过来的TAGLIST进行了解析 对每一项的tag使用parse_tag分析, for (t = &__tagtable_begin; t < &__tagtable_end; t++) if (tag->hdr.tag == t->tag) { t->parse(tag); break; } 其中__tagtable_begin,__tagtable_end在vmlinux.ld中也有定义,这里看tagtable的建立过程 #define __tagtalbe(tag,fn)/ Static struct tagtable __tagtable_##fn __tag={tag,fn} #define __tag __userd __attribute__((__section__(“.taglist.init”))) 对于上述宏中的fn,就是tagtable结构中的parse指针所指向的函数。 而在setup.c中,已经通过__tagtalbe(ATAG_XXX,XXX)建立起所有可能的tagtable,所以可以通过遍历__tagtable_begin~__tagtable_end找到对应的tagtable,并调用对应的parse进行解析并配置 对于如何在指定地址找到传递过来的TAGLIST(U-BOOT传递过来的TAGLIST位于SDRAM+0x100位置上,一般情况下这么设定)如下, 在setup_arch中定义struct tag* tags=(struct tag*)&init_tags, init_tags 是struct init_tags结构定义的一个default init_tags,将此变量存储于“.init.data”段中,在vmlinux.ld中有定义 同时在setup_arch中, if (__atags_pointer) tags = phys_to_virt(__atags_pointer); else if (mdesc->boot_params) tags = phys_to_virt(mdesc->boot_params); 将传递过来的boot_params的物理地址转换成虚拟地址赋值给tags,而boot_params的物理地址为SDRAM+0x100 附带一句,由于taglist中有cmd,解析完taglist后,会调用parse_cmdline解析传递过来的命令 二.MACHINE_START 在u-boot传递参数给kernel时,包括机器类型和TAGLIST。机器类型就用来决定相应的machine_desc结构。 #define MACHINE_START(_type,_name) / static const struct machine_desc __mach_desc_##_type / __used / __attribute__((__section__(".arch.info.init"))) = { / .nr = MACH_TYPE_##_type, / .name = _name, #define MACHINE_END / }; 假设MACH_TYPE_A,对应的结构为__mach_desc_A, MACHINE_START(A, "A") //这里由于U-BOOT传递过来的机器型号与machine_start中的NR进行匹配 //如果匹配,则返回对应的machine_desc结构 /* Maintainer: Atmel */ .phys_io = AT91_BASE_SYS, .io_pg_offst = (AT91_VA_BASE_SYS >> 18) & 0xfffc, .boot_params = AT91_SDRAM_BASE + 0x100, //其余参量在setup_arch中用到,如boot_params就决定taglist地址 .timer = &at91sam926x_timer, .map_io =board_map_io, // ------devicemaps_init() .init_irq = board_init_irq, //init_arch_irq .init_machine = board_init,//customize_machine-----将该函数放在arch_initcall段里,自动调用 MACHINE_END 通过得到machine_desc结构之后,就可以具体对板级做相应的初始化,特别是.init_machine=board_init中,对各个硬件做了相关配置初始化。 三.INITCALLS 另外写点关于__initcall__ 在vmlinux.lds的.init中定义了__initcall_ __initcall_start = .; *(.initcall0.init) *(.initcall0s.init) *(.initcall1.init) *(.initcall1s.init) *(.initcall2.init) *(.initcall2s.init) *(.initcall3.init) *(.initcall3s.init) *(.initcall4.init) *(.initcall4s.init) *(.initcall5.init) *(.initcall5s.init) *(.initcallrootfs.init) *(.initcall6.init) *(.initcall6s.init) *(.initcall7.init) *(.initcall7s.init) __initcall_end = .; 经常在驱动中能看到宏 subsys_initcall和module_init #define subsys_initcall(fn) __define_initcall(“4”,fn,4) #define __define_initcall(level,fn,id) / static initcall_t __initcall_##fn##id __attribute_used__/ __attribute__((__section(“.initcall”level”.init”)))=fn 这里是将fn存放在__section 名为 .initcall4.init中 而initcall_t 则是函数指针 在系统启动过程中会根据上面的initcall表逐个init,而subsys_initcall初始化的函数位于.initcall4.init段中 调用的代码大致如下,在函数do_initcalls中 initcall_t *call for(call=__initcall_start;call<__initcall_end;call++) result=(*call)(); 同样的,#define module_init(x) __initcall(x) #define __initcall(fn) device_initcall(fn) #define device_initcall(fn) __define_initcall(“6”,fn,6) 即module_init初始化的函数位于initcall6.init段中 在init.h中,还能看到 initcall的段1是core_initcall,段2是postcore_initcall, 段3是arch_initcall,段4是subsys_initcall,段5是fs_initcall,段6是device_initcall 段7是late_initcall