U-Boot启动的函数调用过程(以arm926ejs为例)
调用链如下,后面是文字解说:
reset -> cpu_init_crit -> lowlevel_init (板子相关)
-> relocate (将U-boot重定位到RAM)
-> stack_setup
-> clear_bss
-> start_armboot() (C程序) -> main_loop()
系统上电复位后首先跳转到一个指定的地址,ARM的话是0x00000000地址,即通常所说的复位异常向量地址。然后在这里存放了一条指令b reset,即跳转到reset过程执行。至于怎么把这条指令放在这里则是链接过程指定的。
reset过程会将首先将CPU设定为SVC32模式,然后调用cpu_init_crit过程,这个过程对Cache和MMU进行一些操作,主要是冲刷Cache和关闭Cache和MMU。为什么要做这些呢,这主要是为了将CPU设定到一个确定的状态,因为启动的时候CPU处于什么状态是未知的,Cache里面的内容也是不确定的,如果不关闭的话,可能会让CPU取到错误的指令。
然后它会调用lowlevel_init过程,注意lowlevel_init过程是与特定板子相关的,是移植时需要实现的过程。它位于./board/<board_name>/lowlevel_init.S文件中,主要是初始化一些板上的硬件控制器等,lowlevel_init结束后返回cpu_crit_init, cpu_crit_init返回到reset,然后接着执行。
接下来要执行的是relocate过程,它负责把U-Boot从Flash从定位到RAM中,执行完之后接下来依次执行stack_setup,clear_bss。
这两个都执行完毕,执行一条跳转指令(采用ldr pc, <addr>的形式)跳转到start_armboot执行。
在start_armboot之前都是arm汇编代码,从start_armboot开始就进入了C语言过程。它按照顺序执行一大堆初始化任务,这个和Linux内核的start_kernel有类似的地方。最后它调用main_loop()进入这么个死循环中,此后的事情就暂且先不关心了。
关于relocate
这个直译叫重定位,另外有一个remap,直译为重映射,要注意这两者是不一样的,这里现来看看重定位。
首先,系统启动时U-Boot的代码是在FLASH上面的,所以在relocate之前的所有代码都是在flash上面执行的,而且所有代码都是位于FLASH上的。而relocate则是把FLASH上的的代码搬移到RAM中。但是怎么保证relocate之后执行的是RAM中的代码而不是FLASH中的代码呢?
这个待会儿说,似乎涉及到PIC,大意是程序中的指令偏移会被替换成重定位后正确的地址位置,注意这个过程是在链接时,而不是运行时。具体怎么情况,稍后奉上。
关于remap
一般是CPU提供的硬件设置功能,它会允许把内存地址空间的某一段映射到某个位置,ARM中常见的是在上电复位的时候自动把引导加载程序锁在FLASH的地址映射到0x00000000,在引导加载的过程中由软件通过设置某个内存重映射寄存器,然后把主存地址重映射到0x00000000。在这种重映射的情况下,访问映射的地址相当于直接访问对应的FLASH或者主存的地址。