u-boot2020.04移植(5、u-boot重定位)

如果想了解重定位是怎么一回事,可以参考这位老哥的博客:https://blog.csdn.net/skyflying2012/article/details/37660265

第一阶段结束,便开始u-boot的重定位(这里有点小疑惑,在前面将u-boot从SD卡拷贝到DDR中时,我就是拷贝到链接地址的,也就是说那个时候u-boot的运行地址就和链接地址一致了,可以正常运行完整个u-boot,这里又再次重定位,只不过是将u-boot搬到内存的高地址去运行,网上搜了一下,有说是因为防止内核解压的时候覆盖了u-boot本身,那也就是说只要解压内核不会覆盖u-boot这部分内存,理论上就不需要再次重定位了,等之后有时间我测试一下,去掉这部分重定位代码,看u-boot是否还能正常运行,以验证自己的想法),关于这部分新版u-boot和以前的不太一样,个人感觉新版的设计更加简单便于理解。

现在回到arch/arm/lib/crt0.S文件:

重定位前的准备工作

/*arch/arm/lib/crt0.S*/

/*GD_START_ADDR_SP定义在include/generated/generic-asm-offsets.h文件(这个文件是u-boot自动生成的,而且过程也是有点高端,
感兴趣的可以网上了解一下,贴一个我看过的链接:http://blog.chinaunix.net/uid-25000873-id-4134037.html),
	在这里表示在gd这个数据结构中start_addr_sp的偏移值,前面我们也知道r9这个寄存器绑定的就是gd全局变量这个指针,
	所以相当于将gd->start_addr_sp里面的值加载到r0中*/
	ldr	r0, [r9, #GD_START_ADDR_SP]	/* sp = gd->start_addr_sp */
	bic	r0, r0, #7	/* 8-byte alignment for ABI compliance */
	/*设置栈指针*/
	mov	sp, r0
	/*获取gd->bd的位置*/
	ldr	r9, [r9, #GD_BD]		/* r9 = gd->bd */
	/*在内存图上可以看出,新的gd结构在bd结构的下面紧挨着,所以减去gd的大小就是新的gd起始地址*/
	sub	r9, r9, #GD_SIZE		/* new GD is below bd */

	/*这里将here标号的地址值读取到lr中,注意这里是adr,关于adr与ldr的区别可自行网上搜索*/
	adr	lr, here
	/*将重定位偏移值加载到r0寄存器中*/
	ldr	r0, [r9, #GD_RELOC_OFF]		/* r0 = gd->reloc_off */
	/*链接寄存器加上偏移值后,lr的地址就变成重定位后的地址了*/
	add	lr, lr, r0
	/*未定义*/
#if defined(CONFIG_CPU_V7M)
	orr	lr, #1				/* As required by Thumb-only */
#endif
	/*将重定位地址加载到r0中,作为参数传给relocate_code*/
	ldr	r0, [r9, #GD_RELOCADDR]		/* r0 = gd->relocaddr */
	/*执行重定位*/
	b	relocate_code
	/*从relocate_code回来后,就直接运行在重定位后的u-boot中了,here标号已经是重定位后那个here标号了*/
here:

/*
省略
*/

上面的步骤结束后,sp栈指针就设置成gd->start_addr_sp指向的地方了,而r9之前绑定的那个gd也变成重定位后的新地址的gd结构了,内存图如下:

图1

执行重定位

/*arch/arm/lib/relocate.S*/

ENTRY(relocate_code)
	/*从链接脚本可知,主要是将代码段和数据段拷贝到新的地址*/
	ldr	r1, =__image_copy_start	/* r1 <- SRC &__image_copy_start */
	/*r0是重定位地址,r1是目前u-boot运行地址,这里也有个疑问,要是u-boot运行地址现在和链接地址不一样,
	它要拷贝的地方可能根本就是空的或者是错的,那这里不就有问题吗?这个我之后得验证一下*/
	subs	r4, r0, r1		/* r4 <- relocation offset */
	/*上一句subs指令会影响CPSR的状态,这里如果r4的值为0,表示r0和r1是一样的,不需要再重定位*/
	beq	relocate_done		/* skip relocation */
	/*将拷贝结束地址放到r2中*/
	ldr	r2, =__image_copy_end	/* r2 <- SRC &__image_copy_end */

copy_loop:
	/*以r1为起始地址(也就是目前u-boot的起始地址),加载两个字到r10,r11中*/
	ldmia	r1!, {r10-r11}		/* copy from source address [r1]    */
	/*以r0为起始地址(也就是重定位的新地址),加载r10,r11的值到r0,r0+4中*/
	stmia	r0!, {r10-r11}		/* copy to   target address [r0]    */
	/*比较是否读取到结束地址*/
	cmp	r1, r2			/* until source end address [r2]    */
	/*一直循环,直到拷贝结束*/
	blo	copy_loop

	/*
	 * fix .rel.dyn relocations
	 */
	 /*下面这些才是重定位最重要的,上面虽然将u-boot拷贝到新地址了,但是我们要使用的变量,
	 和一些数据这些的地址(这里说地址可能有些不太准确)还是没变的,重定位后的u-boot应该访问的是新地址的变量和数据等,
	 下面这段代码就是干这个事的,我们给新地址的u-boot每一个要访问的变量地址等加上一个偏移,这样就能正确访问了*/
	ldr	r2, =__rel_dyn_start	/* r2 <- SRC &__rel_dyn_start */
	ldr	r3, =__rel_dyn_end	/* r3 <- SRC &__rel_dyn_end */
fixloop:
	/*以r2为起始地址(也就是动态符号表的起始地址),加载两个字到r0,r1中*/
	ldmia	r2!, {r0-r1}		/* (r0,r1) <- (SRC location,fixup) */
	/*取出r1中数据的低8位*/
	and	r1, r1, #0xff
	/*R_ARM_RELATIVE用来检查这个符号是不是需要被重定位*/
	cmp	r1, #R_ARM_RELATIVE
	/*不需要的话就跳过*/
	bne	fixnext

	/* relative fix: increase location by offset */
	/*r4是重定位偏移,这里就是要加上这个偏移*/
	add	r0, r0, r4
	/*取出加上偏移后的以r0为地址内的数据存到r1中,这里取出的也就是新的u-boot里面存放变量的那个地址*/
	ldr	r1, [r0]
	/*给新的u-boot存放变量的那个地址加上偏移*/
	add	r1, r1, r4
	/*将加了偏移后的值(变量的地址)写回,这样新的u-boot就能正确访问了*/
	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)

接下来重定位向量表,这个很简单,就是操作协处理器

/*arch/arm/lib/relocate.S*/

	.section	.text.relocate_vectors,"ax",%progbits
	.weak		relocate_vectors

ENTRY(relocate_vectors)

/*未定义*/
#ifdef CONFIG_CPU_V7M
	/*
        省略
    */
#else
/*定义了,前面也讲过,这个就是向量表基地址寄存器*/
#ifdef CONFIG_HAS_VBAR
	/*
	 * If the ARM processor has the security extensions,
	 * use VBAR to relocate the exception vectors.
	 */
	 /*这个还是操作协处理器,和start.S里面设置重设向量表是一样的*/
	ldr	r0, [r9, #GD_RELOCADDR]	/* r0 = gd->relocaddr */
	mcr     p15, 0, r0, c12, c0, 0  /* Set VBAR */
#else
	/*
        省略
    */
#endif
#endif
	bx	lr

ENDPROC(relocate_vectors)
/*arch/arm/cpu/armv7/start.S*/

ENTRY(c_runtime_cpu_setup)
/*
 * If I-cache is enabled invalidate it
 */
 /*因为u-boot又已经重定位了,现在流水线和缓存里面的内容都是旧的,需要清掉重新填充*/
#if !CONFIG_IS_ENABLED(SYS_ICACHE_OFF)
	mcr	p15, 0, r0, c7, c5, 0	@ invalidate icache
	mcr     p15, 0, r0, c7, c10, 4	@ DSB
	mcr     p15, 0, r0, c7, c5, 4	@ ISB
#endif

	bx	lr

ENDPROC(c_runtime_cpu_setup)

然后是清bss段,这是C语言运行环境需要的,然后执行board_init_r

/*arch/arm/lib/crt0.S*/

/* Set up final (full) environment */

	bl	c_runtime_cpu_setup	/* we still call old routine here */
#endif
/*CONFIG_SPL_BUILD未定义,要执行*/
#if !defined(CONFIG_SPL_BUILD) || CONFIG_IS_ENABLED(FRAMEWORK)

/*CONFIG_SPL_EARLY_BSS未定义,要执行*/
#if !defined(CONFIG_SPL_EARLY_BSS)
	/*清BSS段,这是一个宏定义,执行了一个memset,就不追进去了*/
	SPL_CLEAR_BSS
#endif

/*未定义*/
# ifdef CONFIG_SPL_BUILD
	/* Use a DRAM stack for the rest of SPL, if requested */
	bl	spl_relocate_stack_gd
	cmp	r0, #0
	movne	sp, r0
	movne	r9, r0
# endif

/*未定义,要执行,就是点亮一颗led灯,执不执行都无所谓*/
#if ! defined(CONFIG_SPL_BUILD)
	bl coloured_LED_init
	bl red_led_on
#endif
	/*将gd_t这个数据结构放到r0中,将重定位地址放到r1中,作为参数传给board_init_r*/
	/* call board_init_r(gd_t *id, ulong dest_addr) */
	mov     r0, r9                  /* gd_t */
	ldr	r1, [r9, #GD_RELOCADDR]	/* dest_addr */
	/* call board_init_r */
#if CONFIG_IS_ENABLED(SYS_THUMB_BUILD)
	ldr	lr, =board_init_r	/* this is auto-relocated! */
	bx	lr
#else
	/*调用board_init_r,进入u-boot第二阶段*/
	ldr	pc, =board_init_r	/* this is auto-relocated! */
#endif
	/* we should not return here. */
#endif

到了这里,u-boot重定位就结束了,其实u-boot重定位的知识远不止代码中的这点儿,有很多的细节,建议读者有时间的话,多去网上查阅一下相关的资料,比如位置无关代码与位置有关代码,为什么需要重定位等问题,这样对重定位才有更加深入和深刻的理解。

欢迎扫码关注我的微信公众号

漫长当下

猜你喜欢

转载自blog.csdn.net/a1598025967/article/details/107009583