要分析boot启动流程,首先要找到程序入口地址,可以通过编译uboot生成u-boot.lds,通过查看链接脚本u-boot.lds知道入口点是 arch/arm/lib/vectors.S 文件中的_start。 OUTPUT_FORMAT('elf32-littlearm', 'elf32-littlearm', 'elf32-littlearm') OUTPUT_ARCH(arm) ENTRY(_start)//代码当前入口点:_start, _start 在文件arch/arm/lib/vectors.S 中有定义 SECTIONS { . = 0x00000000; . = ALIGN(4); .text : { *(.__image_copy_start)//uboot 拷贝的首地址 *(.vectors)//vectors 段保存中断向量表 arch/arm/cpu/armv7/start.o (.text*)//将 arch/arm/cpu/armv7/start.s 编译出来的代码放到中断向量表后面 *(.text*)//text 段,其他的代码段就放到这里 } . = ALIGN(4); .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) } . = ALIGN(4); .data : { *(.data*) } . = ALIGN(4); . = .; . = ALIGN(4); .u_boot_list : { KEEP(*(SORT(.u_boot_list*))); } . = ALIGN(4); .image_copy_end : { *(.__image_copy_end)//uboot 拷贝的结束地址 } .rel_dyn_start : { *(.__rel_dyn_start)//.rel.dyn 段起始地址 } .rel.dyn : { *(.rel*) } .rel_dyn_end : { *(.__rel_dyn_end)//.rel.dyn 段结束地址 } .end : { *(.__end) } _image_binary_end = .;//镜像结束地址 . = ALIGN(4096); .mmutable : { *(.mmutable) } .bss_start __rel_dyn_start (OVERLAY) : { KEEP(*(.__bss_start));//.bss 段起始地址 __bss_base = .; } .bss __bss_base (OVERLAY) : { *(.bss*) . = ALIGN(4); __bss_limit = .; } .bss_end __bss_limit (OVERLAY) : { KEEP(*(.__bss_end));//.bss 段结束地址 } .dynsym _image_binary_end : { *(.dynsym) } .dynbss : { *(.dynbss) } .dynstr : { *(.dynstr*) } .dynamic : { *(.dynamic*) } .plt : { *(.plt*) } .interp : { *(.interp*) } .gnu.hash : { *(.gnu.hash) } .gnu : { *(.gnu*) } .ARM.exidx : { *(.ARM.exidx*) } .gnu.linkonce.armexidx : { *(.gnu.linkonce.armexidx.*) } } 1. uboot启动总体流程uboot启动主要分为两部分arch级初始化和板级初始化。下面具体分析各个初始化。 2. arch级初始化
2.1 cpu_init_crit① 设置SP指向CONFIG_SYS_INIT_SP_ADDR=0X0091FF00,流程见上图。可结合下图自己分析 ② 设置SP 8字节对齐 ③ 设置gd(global data)大小为248B ④ sp地址(0x0091FE08)保存在r9寄存器中
3. 板级初始化_main主要分为六个部分,简单来说主要是把iROM中的程序拷贝至DDR,然后载重定向,最后初始化各种外设。 3.1 board_init_f_alloc_reserve函数和board_init_f_resrve函数board_init_f_alloc_reserve函数主要用于留出早期malloc和gd(global_data)内存区域;board_init_f_init_reserve函数主要功能初始化gd。两个函数主要功能如上图所示:
3.2 board_init_fboard_init_f函数主要用于初始化DDR,定时器,完成代码拷贝等
commmon/board_f.c initcall_run_list函数的具体主要功能如下:
3.3 relocate_code relocate_code函数主要用于代码拷贝,在relocate_code函数之前还有语句ldr r0,[r9,#GD_RELOCADDR],r0=gd-> relocaddr= 0X9FF47000,uboot重定位后的首地址。 relocate_code函数在arch/arm/lib/relocate.S中,下面结合代码分析该函数 1、ldr r1,=__image_copy_start//r1=0X8780000源地址起始地址 2、subs r4, r0, r1//r4=0X9FF47000-0X87800000=0X18747000 偏移 3、ldr r2, =__image_copy_end//r2=0X8785dc6c源地址结束地址 4、copy_loop: //拷贝,将uboot从源地址0X8780000拷贝至0X9FF47000 ldmia r1!, {r10-r11} /* copy from source address [r1] */ stmia r0!, {r10-r11} /* copy to target address [r0] */ cmp r1, r2 /* until source end address [r2] */ blo copy_loop
第5段的程序分析如下: 1、r2=__rel_dyn_start,也就是.rel.dyn 段的起始地址。 2、r3=__rel_dyn_end,也就是.rel.dyn 段的终止地址。 3、从.rel.dyn 段起始地址开始,每次读取两个 4 字节的数据存放到 r0 和 r1 寄存器中, r0 存放低 4 字节的数据,也就是 Label 地址;r1 存放高 4 字节的数据,也就是 Label 标志。 4、r1 中给的值与 0xff 进行与运算,其实就是取 r1 的低 8 位。 5、判断 r1 中的值是否等于 23(0X17)。//0X17就是判断是否是Label 6、如果 r1 不等于 23 的话就说明不是描述 Label 的,执行函数 fixnext ,否则的话继续执行下面的代码。 7、r0 保存着 Label 值, r4 保存着重定位后的地址偏移, r0+r4 就得到了重定位后的Label值。此时 r0 保存着重定位后的 Label 值。 8、读取重定位后 Label 所保存的变量地址,此时这个变量地址还是重定位前的地址,将得到的值放到 r1 寄存器中。 9、 r1+r4 即可得到重定位后的变量地址 。 10、重定位后的变量地址写入到重定位后的 Label 中。 11、比较 r2 和 r3,查看.rel.dyn 段重定位是否完成。 12、如果 r2 和 r3 不相等,说明.rel.dyn 重定位还未完成,因此跳到 fixloop 继续重定位 .rel.dyn 段。 3.4 relocate_vectors relocate_vectors函数主要用于重定位向量表。relocate_vectors函数位于arch/arm/lib/relocate.S中,用于设置VBAR寄存器为重定位后的中断向量表起始地址。 3.5 board_init_r board_init_r函数与board_init_f函数类似,用于初始化一系列外设。board_init_r函数位于commmon/board_r.c中。 board_init_f函数并没有初始化所有的外设,还需要做一些后续工作,这些后续工作就是由函数board_init_r 函数来完成。 board_init_r函数中含有init_sequence_r函数,该函数用于初始化序列。而init_sequence_r函数又含有run_main_loop函数,用于进入uboot命令模式或启动linux内核。run_main_loop函数中最重要的是main_loop函数,该函数位于common/main.c中。下面分析main_loop函数。 3.5.1 main_loop()该函数主要功能如下:
至此,uboot启动流程分析完毕,最后附上启动流程完整图 |
|
来自: 新用户0118F7lQ > 《文件夹1》