文章目录
本系列使用u-boot版本为u-boot-2018.01;
前面的篇幅分析完board_init_r函数,完成了几乎都是初始化的工作,回到_main继续往下执行,执行到relocate_code函数;
relocate_code
relocate_code定义在arch/arm/lib/relocate.S文件中,原型如下:
/*
* void relocate_code(addr_moni)
*
* This function relocates the monitor code.
*
* NOTE:
* To prevent the code below from containing references with an R_ARM_ABS32
* relocation record type, we never refer to linker-defined symbols directly.
* Instead, we declare literals which contain their relative location with
* respect to relocate_code, and at run time, add relocate_code back to them.
*/
ENTRY(relocate_code)
ldr r1, =__image_copy_start /* r1 <- SRC &__image_copy_start */
subs r4, r0, r1 /* r4 <- relocation offset */
beq relocate_done /* skip relocation */
ldr r2, =__image_copy_end /* r2 <- SRC &__image_copy_end */
copy_loop:
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
/*
* fix .rel.dyn relocations
*/
ldr r2, =__rel_dyn_start /* r2 <- SRC &__rel_dyn_start */
ldr r3, =__rel_dyn_end /* r3 <- SRC &__rel_dyn_end */
fixloop:
ldmia r2!, {
r0-r1} /* (r0,r1) <- (SRC location,fixup) */
and r1, r1, #0xff
cmp r1, #R_ARM_RELATIVE
bne fixnext
/* relative fix: increase location by offset */
add r0, r0, r4
ldr r1, [r0]
add r1, r1, r4
str r1, [r0]
fixnext:
cmp r2, r3
blo fixloop
relocate_done:
#ifdef __XSCALE__
/*
* On xscale, icache must be invalidated and write buffers drained,
* even with cache disabled - 4.2.7 of xscale core developer's manual
*/
mcr p15, 0, r0, c7, c7, 0 /* invalidate icache */
mcr p15, 0, r0, c7, c10, 4 /* drain write buffer */
#endif
/* ARMv4- don't know bx lr but the assembler fails to see that */
#ifdef __ARM_ARCH_4__
mov pc, lr
#else
bx lr
#endif
ENDPROC(relocate_code)
该函数用于拷贝代码用,uboot链接脚本分析中提到,链接脚本决定内存空间的存放位置,通过u-boot.map文件可以检索到对应的标志的起始地址,如上述源码中的第14行,__image_copy_start,该标志位uboot拷贝的首地址开始地址0x400000,现在加载到寄存器r1中去;
- 第15行,这里的r0是上篇中经过
_main调用board_init_f_init_reserve函数完成计算的重定位后的地址,也就是uboot后面要拷贝到的地址;现在r4=r0-r1计算出uboot的偏移量存储到r4中; - 第16行,做了一个判断,针对r0-r1的结果进行判断,如果r0-r1==0,那代表着源地址和目的地址是相同的,则直接跳转执行
relocate_done函数,否则继续进行后面的拷贝; - 第17行,r2存储了
__image_copy_end地址,这是拷贝前原代码结束的地址,这些值都是可以检索或着计算的,这里不直接进行计算是因为大家编译出来的uboot长度存在差异,避免钻牛角尖,这里只说过程; - 第19行,正式开始拷贝,到这里总结前面各寄存器中存放的地址和意义,如下表:
| 寄存器 | 备注 |
|---|---|
| r0 | relocation,重定位后uboot要拷贝到的地址 |
| r1 | __image_copy_start,原uboot的起始地址 |
| r2 | __image_copy_end,原uboot的结束地址 |
| r4 | relocation offset,新旧拷贝地址的偏移量,目前只用于一次判断 |
-
第20行,这里涉及一个汇编指令
ldmia,他的作用与stmfd相反,一般成对出现在中断现场,stmfd用于保护现场和ldmia用于恢复现场,这里使用的意思是ldmia r1!, {r10-r11},就是从r1的地址地址上的内容,保存到r10和r11中,r10和r11都是32位寄存器,所以每次单次拷贝2个32位的数据,拷贝完成,r1会自动更新,递增到下一个数据地址; -
第21行,汇编指令
stmia,该指令与ldmia类似,不过前者是将寄存器中的值加载到地址,后者是将地址中的数据记载到寄存器,这里stmia r0!, {r10-r11}的意思是,将r10和r11寄存器中的数据依次装载到r0中,即将第20行从原地址拷贝到的数据装载到新地址,装载完成,r0也会自动更新递增地址;
插播一张飞机票,这里对ldm和stm指令有比较简明的介绍! -
第23~24行,先是比较r1和r2的值,判断是否相等来判断是否拷贝完成,如果不相等则跳转回
copy_loop,形成一个循环,直到拷贝完成; -
后面的.rel.dyn段的拷贝,没有具体分析,这里就不误导大家了;
-
完成uboot的拷贝,接下来回到
_main跳转执行relocate_vectors函数,该函数用于重定位向量表,只有一步操作比较重要,就是将uboot重定位完之后的地址,装载到CP15的VBAR寄存器中设置向量表偏移,该寄存器自行去学习;
board_init_r
完成了前面这么多工作,_main往下先是清除了BSS段,然后又来到一个新重要函数board_init_r,执行board_init_r函数前,该函数有两个参数,一个是gd,一个是重定位后的目的地址,这个后面会二次说明,board_init_r函数原型如下:
源文件见:common/board.c
void board_init_r(gd_t *new_gd, ulong dest_addr)
{
/*
* Set up the new global data pointer. So far only x86 does this
* here.
* TODO(sjg@chromium.org): Consider doing this for all archs, or
* dropping the new_gd parameter.
*/
#if CONFIG_IS_ENABLED(X86_64)
arch_setup_gd(new_gd);
#endif
#ifdef CONFIG_NEEDS_MANUAL_RELOC
int i;
#endif
#if !defined(CONFIG_X86) && !defined(CONFIG_ARM) && !defined(CONFIG_ARM64)
gd = new_gd;
#endif
gd->flags &= ~GD_FLG_LOG_READY;
#ifdef CONFIG_NEEDS_MANUAL_RELOC
for (i = 0; i < ARRAY_SIZE(init_sequence_r); i++)
init_sequence_r[i] += gd->reloc_off;
#endif
if (initcall_run_list(init_sequence_r))
hang();
/* NOTREACHED - run_main_loop() does not return */
hang();
}
该函数主要完成board_init_f未完成的初始化外设工作,该函数的构造与board_init_f函数很像,也是通过initcall_run_list来执行函数数组内的函数,init_sequence_r节选如下,删去了注释和部分没用到的条件编译以及无关紧要的函数,如下:
static init_fnc_t init_sequence_r[] = {
initr_trace,
initr_reloc,
#ifdef CONFIG_ARM
initr_caches,
#endif
initr_reloc_global_data,
...
initr_malloc,
...
initr_bootstage, /* Needs malloc() but has its own timer */
...
#ifdef CONFIG_DM
initr_dm,
#endif
#if defined(CONFIG_ARM) || defined(CONFIG_NDS32

本文详细解析了U-Boot的启动流程,重点介绍了relocate_code函数如何进行代码重定位,board_init_r函数如何完成板级初始化,以及bootdelay_process和autoboot_command如何控制自动启动过程。
最低0.47元/天 解锁文章
2099

被折叠的 条评论
为什么被折叠?



