u-boot移植篇——u-boot启动流程分析 下

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

本系列使用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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大大棋

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值