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是个宏定义,他的展开我列了出来
1
2
3
4
5
6
7
8
9
|
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
|
1
2
3
4
5
6
7
8
9
10
11
12
|
#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 \
}
;
// 使用 __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
)
{
struct
machine_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
1
2
3
4
5
6
7
8
|
static
int
__init
customize_machine
(
void
)
{
/* customizes platform devices, or adds new ones */
if
(
machine_desc
->
init_machine
)
machine_desc
->
init_machine
(
)
;
return
0
;
}
arch_initcall
(
customize_machine
)
;
|
这个arch_initcall 执行了machine_desc->init_machine(),那么arch_initcall 是何时调用的呢?
在main.c ->do_basic_setup();->do_initcalls();中
1
2
3
4
5
6
7
|
static
void
__init
do_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)
|
1
2
3
|
#define __define_initcall(level,fn,id) \
static
initcall_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中的函数按顺序都执行一遍。