上篇讲到了barebox的安装,并针对pcm038做了编译,这次以pcm038为例大致分析下barebox的结构。
barebox的启动文件是startup.c,通常arm的启动代码都用汇编语言写成,不过用c语言也没什么差--批一层羊皮而已,没错,就是在c语言里调用汇编语句,来看一下入口函数:
void __naked __section(.text_entry) exception_vectors(void)
{
__asm__ __volatile__ (
" b reset\n " /* reset */
" ldr pc, =undefined_instruction\n " /* undefined instruction */
" ldr pc, =software_interrupt\n " /* software interrupt (SWI) */
" ldr pc, =prefetch_abort\n " /* prefetch abort */
" ldr pc, =data_abort\n " /* data abort */
" ldr pc, =not_used\n " /* (reserved) */
" ldr pc, =irq\n " /* irq (interrupt) */
" ldr pc, =fiq\n " /* fiq (fast interrupt) */
);
}
这个函数内容没啥可说的,就是arm异常表的一个映射,注意__section(.text_entry) 这个前缀,它指定该函数会被link到.text_entry区域,在arch/arm下的Makefile里可以看到:
lds-$(CONFIG_GENERIC_LINKER_SCRIPT) := arch/arm/lib/barebox.lds (CONFIG_GENERIC_LINKER_SCRIPT在menuconfig可以看到被选中)
打开arch/arm/lib/barebox.lds,可以看到:
1 OUTPUT_FORMAT( " elf32-littlearm " , " elf32-littlearm " , " elf32-littlearm " )
2 OUTPUT_ARCH(arm)
3 ENTRY(exception_vectors)
4 SECTIONS
5 {
6 . = 0xa7f00000 ;
7
8 . = ALIGN( 4 );
9 .text :
10 {
11 _stext = .;
12 _text = .;
13 * (.text_entry * )
14
15
16
17
18
19 * (.text_bare_init * )
20 * (.text * )
21 }
22
23 . = ALIGN( 4 );
24 .rodata : { * (.rodata * ) }
25
26 _etext = .; /* End of text and rodata section */
27
28 . = ALIGN( 4 );
29 .data : { * (.data * ) }
30
31 . = ALIGN( 4 );
32 .got : { * (.got * ) }
33
34 . = .;
35 __barebox_cmd_start = .;
36 .barebox_cmd : { KEEP( * (SORT_BY_NAME(.barebox_cmd * ))) }
37 __barebox_cmd_end = .;
38
39 __barebox_initcalls_start = .;
40 .barebox_initcalls : { KEEP( * (.initcall. 0 )) KEEP( * (.initcall. 1 )) KEEP( * (.initcall. 2 )) KEEP( * (.initcall. 3 )) KEEP( * (.initcall. 4 )) KEEP( * (.initcall. 5 )) KEEP( * (.initcall. 6 )) KEEP( * (.initcall. 7 )) KEEP( * (.initcall. 8 )) }
41 __barebox_initcalls_end = .;
42
43 __usymtab_start = .;
44 __usymtab : { KEEP( * (__usymtab)) }
45 __usymtab_end = .;
46
47 . = ALIGN( 4 );
48 __bss_start = .;
49 .bss : { * (.bss * ) }
50 _end = .;
51 }
可以看到入口处即为__section(.text_entry) 。
然后找到b reset的reset函数:
void __naked __bare_init reset(void)
{...}
注意这个_bare_init,对应的是__section(.text_bare_init.text),在上面的link文件中可以看到紧跟__section(.text_entry)。这是因为从nand flash等设备启动时,i.MX27会拷贝nand flash的前2K内容到buffer,如果reset函数不放在前面的话可能拷不到这个buffer中,就没办法执行了,另从中可以知道如果在map文件中__section(.text_bare_init.text)的结尾偏移首地址2KB的话,那么barebox就没法运行了。
reset函数中清空cache,disable掉mmu后跳去执行board_init_lowlevel(CONFIG_MACH_DO_LOWLEVEL_INIT可以在menuconfig中看到被选中)。board_init_lowlevel就是板子特定的文件了,现在转去arch/arm/boards/pcm038/lowlevel.c。
board_init_lowlevel函数建立板子运行的基本条件如时钟,SDRAM初始化等,然后判断自己是否运行在SDRAM中。在nand buffer中运行的时候PC是在[0xD800 0000~0xD800 0800),这个由cpu决定,之所以不是从0开始是因为CPU选中nand启动时对reset入口地址做了重映射。由于这部分代码都是相当地址寻址,所以同link文件指定的入口地址无关。
board_init_lowlevel函数发现自己还不在SDRAM中,就把自己(2KB的buffer)拷到SDRAM中,然后跳去SDRAM (函数入口insdram)执行,跳转的方式是取绝对地址:
r = (unsigned int)&insdram;
__asm__ __volatile__("mov pc, %0" : : "r"(r));
这个r就跟link文件有关了,是在入口0xa7f00000+__section(.text_bare_init.text)里insdram对应的offset,具体地址可以从map文件中看到。
跳到insdram后首要做的事就是把bootloader整个文件拷到SDRAM,这个过程就是清空nand buffer->读nand->写SDRAM的循环,读完之后以后的函数就不用加_bare_init前缀了,反正不管你放哪里都能执行了。注意board_init_lowlevel_return还是要加,因为CONFIG_MACH_DO_LOWLEVEL_INIT没enable的话就在reset里直接跳去执行它了。
insdram最后调用board_init_lowlevel_return,设定下stack,再多余的判断一次自己是否在SDRAM(仅对当前的设定多余),然后就调用barebox的入口函数--start_barebox。
start_barebox首先读取__barebox_initcalls_start开头的一堆函数指针,然后挨个去执行它,执行完后调用environment设定,接着就是一个死循环run_shell。那驱动/文件系统呢,跑哪里去了?答案就在__barebox_initcalls_start开头的函数指针处。barebox有9个宏定义,分别是pure_initcall/core_initcall/postcore_initcall/console_initcall/postconsole_initcall/fs_initcall/device_initcall/late_initcall,是__define_initcall的按顺序定义,至于__define_initcall,它被定义到.initcall.n,即上面link指定的从__barebox_initcalls_start到__barebox_initcalls_end对应的空间,也就是start_barebox一开始读取一堆函数指针的地址空间。
之后的事情就是一个个的查找那些xxxx_initcall然后来做具体的分析了。