从上一篇我们知道,board_init_f函数的最后返回到relocate_code,调用该函数的原型是
Relocate_code(addr_sp,id,addr),再讲一下三个参数的意义:
addr_sp是地址空间里面堆栈的首地址
id是存储gd_t类型全局参数的首地址
addr是uboot的重定位地址,也就是加载地址
这三个参数的值都是在board_init_f函数里面定义好了的(具体可以参看上一篇日志《uboot系列之-----板级初始化(源码)》)。
现在跳到arch/arm/cpu/armv7/start.s中的relocate_code代码标号处。
.globl relocate_code
relocate_code:
mov r4, r0 /*save addr_sp */
mov r5, r1 /*save addr of gd */
mov r6, r2 /*save addr of destination */
这里面r0,r1和r2分别对应上面所讲的三个参数
r0 – addr_sp
r1 – id
r2 – addr
/* Setup the stack*/
stack_setup:
mov sp,r4
设置堆栈指针
adr r0,_start
#ifndef CONFIG_PRELOADER
cmp r0,r6
beq clear_bss /*skip relocation*/
#endif
adr是小范围的地址读取伪指令,这条指令也可以理解成ldr r0,=PC+x,PC是该条指令的地址,x是PC与_start标号之间的偏移量(_start-PC),_start永远位于代码的最开始,当PC>_start时,x为负值,否则x为正值。所以当代码此时还在flash中时,_start为0(内部ram),PC+x(x<0)=PC+_start-PC = _start=0,即r0=0,如果代码此时已经在SDRAM中了(即已经拷贝过了),_start = TEXT_BASE,r0 = TEXT_BASE。所以接下来就比较r0与r6(r6存储的重定位地址),如果相等,就说明已经拷贝过了,就跳过重定位代码,否则,就要执行重定位代码。我们这里是需要重定位的。
mov r1,r6 /*r1<-scratch for copy_loop*/
ldr r2,_TEXT_BASE
ldr r3,_bss_start_ofs
add r2,r0,r3 /*r2<-source end address*/
copy_loop:
ldmia r0!,{r9-r10} /*copy from source address [r0]*/
stmia r1!,{r9-r10} /**copy to target address[r1]/
cmp r0,r2 /*until source end address[r2]*/
blo copy_loop
_TEXT_BASE标号处存放的是CONFIG_SYS_TEXT_BAE,改变量定义在/board/samsung/smdk4212/config.mk 值为0xc3e0_0000,不过在这段程序中好像没什么用,addr r2,r0,r3 使得r2 =r3(因为r0=0),即BSS段的开始地址,也就是代码段的结束地址。整个copy_loop循环就是将uboot代码拷贝到重定位地址处(即addr指明的地址)