linux板级初始化
2017-07-26
开发板:IMAX6Q
内核:3.0.35
最近拿到了明远智睿 的EK314开发板,以前主要用2440,眼界过于狭隘,借此机会练习下。
首先看看它的板级文件
/arch/arm/mach-mx6/board-myimx6ek314.c
在他的末尾指定了map_io、init_irq、init_machine、timer等初始化函数,MACHINE_START是个宏定义,他的展开我列了出来
MACHINE_START(MYIMX6EK314,"MYZR i.MX6 Evaluation Kit ( MXM 314 )")
.boot_params= MX6_PHYS_OFFSET+ 0x100,
.fixup= fixup_mxc_board,
.map_io= mx6_map_io,
.init_irq= mx6_init_irq,
.init_machine= mx6_sabresd_board_init,
.timer= &mx6_sabresd_timer,
.reserve= mx6q_sabresd_reserve,
MACHINE_END
#define MACHINE_START(_type,_name) \
static conststruct machine_desc__mach_desc_##_type \
__used\
__attribute__((__section__(".arch.info.init")))= {\
.nr= MACH_TYPE_##_type, \
.name= _name,
#define MACHINE_END \
};
// 使用 __attribute__ ((packed)) ,让编译器取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐,
// 这样子两边都需要使用 __attribute__ ((packed))取消优化对齐,就不会出现对齐的错位现象。
这里的话,是定义了一个struct machine_desc __mach_desc__MYIMX6EK314 的结构体
这个结构体存放的段是.arch.info.init,这里注意一下,后边匹配machine_desc的时候就是到这个段中寻找,然后根据nr的值匹配。
这里的machine_desc指定的初始化函数的调用分别在以下阶段
main.c/start_kernel(void) —>setup_arch(&command_line);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
void__init setup_arch(char**cmdline_p)
{
structmachine_desc *mdesc;
unwind_init();
setup_processor();
mdesc= setup_machine_fdt(__atags_pointer);
if(!mdesc)
mdesc= setup_machine_tags(machine_arch_type);
machine_desc= mdesc;
machine_name= mdesc->name;
if(mdesc->soft_reboot)
reboot_setup("s");
.....................
parse_early_param();
sanity_check_meminfo();
arm_memblock_init(&meminfo,mdesc);
paging_init(mdesc); // ->paging_init(mdesc)->devicemaps_init
request_standard_resources(mdesc);
...................
early_trap_init();
if(mdesc->init_early)
mdesc->init_early(); //init_early:
}
init_irq:
/init/main.c/start_kernel(void)->init_IRQ()->machine_desc->init_irq()
(mdesc) ->devicemaps_init() -> mdesc->map_io()
time_init:
start_kernel() –> time_init()->system_timer = machine_desc->timer;system_timer->init()
init_machine:
他是用了arch/arm/kernel/setup.c
@729
staticint __initcustomize_machine(void)
{
/* customizes platform devices, or adds new ones */
if(machine_desc->init_machine)
machine_desc->init_machine();
return0;
}
arch_initcall(customize_machine);
这个arch_initcall 执行了machine_desc->init_machine(),那么arch_initcall 是何时调用的呢?
在main.c ->do_basic_setup();->do_initcalls();中
staticvoid __initdo_initcalls(void)
{
initcall_t*fn;
for(fn= __early_initcall_end;fn <__initcall_end;fn++)
do_one_initcall(*fn);
}
这里的一个for循环do_one_initcall,调用了所有的 xxxx_initcall,在include/linux/init.h 中
以下代码是对标号进行处理,方便统一调用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#define pure_initcall(fn) __define_initcall("0",fn,0)
#define core_initcall(fn) __define_initcall("1",fn,1)
#define core_initcall_sync(fn) __define_initcall("1s",fn,1s)
#define postcore_initcall(fn) __define_initcall("2",fn,2)
#define postcore_initcall_sync(fn) __define_initcall("2s",fn,2s)
#define arch_initcall(fn) __define_initcall("3",fn,3)
#define arch_initcall_sync(fn) __define_initcall("3s",fn,3s)
#define subsys_initcall(fn) __define_initcall("4",fn,4)
#define subsys_initcall_sync(fn) __define_initcall("4s",fn,4s)
#define fs_initcall(fn) __define_initcall("5",fn,5)
#define fs_initcall_sync(fn) __define_initcall("5s",fn,5s)
#define rootfs_initcall(fn) __define_initcall("rootfs",fn,rootfs)
#define device_initcall(fn) __define_initcall("6",fn,6)
#define device_initcall_sync(fn) __define_initcall("6s",fn,6s)
#define late_initcall(fn) __define_initcall("7",fn,7)
#define late_initcall_sync(fn) __define_initcall("7s",fn,7s)
#define __initcall(fn) device_initcall(fn)
#define __define_initcall(level,fn,id) \
staticinitcall_t __initcall_##fn##id __used \
__attribute__((__section__(".initcall"level ".init")))= fn
initcall_t 的原型是typedef int (*initcall_t)(void); 就是个函数指针
__define_initcall就是把函数指针指向xxx_initcall() ,对应我们的初始化函数
而属性 __attribute__((__section__())) 则表示把对象放在一个这个由括号中的名称所指代的section中。
所以__define_initcall的含义是:
1) 声明一个名称为__initcall_##fn的函数指针;
2) 将这个函数指针初始化为fn;
3) 编译的时候需要把这个函数指针变量放置到名称为 “.initcall” level “.init”的section中。
这个section是在/include/asm-generic/vmlinux.lds.h中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#define INITCALLS \
*(.initcallearly.init)\
VMLINUX_SYMBOL(__early_initcall_end)= .;\
*(.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)
#define INIT_CALLS \
VMLINUX_SYMBOL(__initcall_start)= .;\
INITCALLS\
VMLINUX_SYMBOL(__initcall_end)= .;
而这些SECTION里的函数在初始化时被顺序执行(init内核线程->do_basic_setup()[main.C#778]->do_initcalls())。
程序(init/main.c文件do_initcalls()函数)do_initcalls()把.initcallXX.init中的函数按顺序都执行一遍。