参考http://blog.youkuaiyun.com/xieweihua2012/article/details/8474655根据X4412-uboot源码理解整理
board_init_f函数的最后返回到relocate_code,调用该函数的原型是Relocate_code(addr_sp,id,addr),其中三个参数的意义:
addr_sp是地址空间里面堆栈的首地址0xc3cf_bf50
id是存储gd_t类型全局参数的首地址0xc3cf_bf58
addr是uboot的重定位地址,也就是加载地址0XC3E0_0000
这三个参数的值都是在board_init_f函数里面定义好了的(UBOOT之源码分析(X4412)-----板级初始化)。
现在跳到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,不过在这段程(ldr r2,_TEXT_BASE
)序中好像没什么用,add r2,r0,r3 使得r2 =r3(因为r0=0),即BSS段的开始地址,也就是代码段的结束地址。整个copy_loop循环就是将uboot代码拷贝到重定位地址处(即addr指明的地址)
/*
* fix .rel.dyn relocations
*/
ldr r0, _TEXT_BASE /* r0 <- Text base */
sub r9, r6, r0 /* r9 <- relocation offset */
ldr r10, _dynsym_start_ofs /* r10 <- sym table ofs */
add r10, r10, r0 /* r10 <- sym table in FLASH */
ldr r2, _rel_dyn_start_ofs /* r2 <- rel dyn start ofs */
add r2, r2, r0 /* r2 <- rel dyn start in FLASH */
ldr r3, _rel_dyn_end_ofs /* r3 <- rel dyn end ofs */
add r3, r3, r0 /* r3 <- rel dyn end in FLASH */
fixloop:
ldr r0, [r2] /* r0 <- location to fix up, IN FLASH! */
add r0, r0, r9 /* r0 <- location to fix up in RAM */
ldr r1, [r2, #4]
and r7, r1, #0xff
cmp r7, #23 /* relative fixup? */
beq fixrel
cmp r7, #2 /* absolute fixup? */
beq fixabs
/* ignore unknown type of fixup */
b fixnext
fixabs:
/* absolute fix: set location to (offset) symbol value */
mov r1, r1, LSR #4 /* r1 <- symbol index in .dynsym */
add r1, r10, r1 /* r1 <- address of symbol in table */
ldr r1, [r1, #4] /* r1 <- symbol value */
add r1, r1, r9 /* r1 <- relocated sym addr */
b fixnext
fixrel:
/* relative fix: increase location by offset */
ldr r1, [r0]
add r1, r1, r9
fixnext:
str r1, [r0]
add r2, r2, #8 /* each rel.dyn entry is 8 bytes */
cmp r2, r3
blo fixloop
在链接文件arch/arm/cpu/armv7/u-boot.lds中有两段:dynsym动态符号表,.rel.dyn动态重定位表。上面程序的主要工作就是将dynsym和.rel.dyn重定位,并遍历.rel.dyn,根据重来位表中的信息将符号表中的函数地址等进行重定位。
clear_bss:
ldr r0, _bss_start_ofs
ldr r1, _bss_end_ofs
ldr r3, _TEXT_BASE /* Text base */
mov r4, r6 /* reloc addr */
add r0, r0, r4
add r1, r1, r4
mov r2, #0x00000000 /* clear */
clbss_l:str r2, [r0] /* clear loop... */
add r0, r0, #4
cmp r0, r1
bne clbss_l
#endif /* #ifndef CONFIG_PRELOADER */
找到BSS段的起始地址与结束地址,然后循环清0整个BSS段。
接下来跳转到重定位后的内存位置,在内存中运行代码进入第二阶段的初始化
/*
* We are done. Do not return, instead branch to second part of board
* initialization, now running from RAM.
*/
jump_2_ram:
ldr r0, _board_init_r_ofs
adr r1, _start
add lr, r0, r1
@ add lr, lr, r9
/* setup parameters for board_init_r */
mov r0, r5 /* gd_t */
mov r1, r6 /* dest_addr */
/* jump to it ... */
mov pc, lr
_board_init_r_ofs:
.word board_init_r - _start
_rel_dyn_start_ofs:
.word __rel_dyn_start - _start
_rel_dyn_end_ofs:
.word __rel_dyn_end - _start
_dynsym_start_ofs:
.word __dynsym_start - _start
此时的_start为重定位后代码起始地址。起始地址+board_init_r位置的偏移量即为 board_init_r函数的入口地址保存到lr。通过mov pc,lr中转到 board_init_r进行下一阶段的初始化。