uboot之 relocate功能代码分析

uboot的relocate功能代码是分析uboot的必经之路,非常巧妙,也非常经典,当然理解起来也有些麻烦:

.globl _start
_start:	b       reset
	......
	......
	......
	......
	......

_TEXT_BASE:
	.word	TEXT_BASE

.globl _armboot_start
_armboot_start:
	.word _start



reset:
	......
	......
	......
	......
	......
	
relocate:				/* 重新搬迁加载 U-Boot 到 RAM	    */
	adr	r0, _start		/* adr命令将标志_start基于PC的相对地址存放到r0中   */
	ldr	r1, _TEXT_BASE		/* 将标志_TEXT_BASE的内容放到r1中,其值在上面用.word  赋值了,这个很巧妙,很规范 */
	cmp     r0, r1                  /* 比较r0与r1,就能够判断当前是在flash中运行,还是在RAM中运行*/
	beq     stack_setup		//如果r0 = r1,表示是在内存中,属于调试模式,就直接进行堆栈初始化了

	ldr	r2, _armboot_start	//_armboot_start用.word 在上面定义了,就是_start的地址,赋值给r2
	ldr	r3, _bss_start		//将_bss_start地址赋值给r3,其实就是u-boot的结尾
	sub	r2, r3, r2		// r2 = r3 - r2,其实就是要搬迁的 uboot代码的总长度(bss - start)
	add	r2, r0, r2		/* r2 = r0 + r2,这里就计算出了要搬迁的uboot代码结尾的 地址,跟上面的长度是两码事儿*/

copy_loop:				//执行循环复制
	ldmia	r0!, {r3-r10}		/* ldmia 批量读取,从首地址r0,批量读8个字,共32个字节到r3~r10,ro!表示自动递增    */
	stmia	r1!, {r3-r10}		/* stmia 批量写内存,将r3~r10 批量写入到首地址r1中,由上分析,r1就是执行了内存首地址,自递增*/
	cmp	r0, r2			/* 判断 r0 首地址,是否与 末地址相等重合    */
	ble	copy_loop		//如果不等,表示没有复制完,重新跳转到copy_loop,相等则下一步

	......
	......
	......
	......
	......
	......				//各种堆栈初始化

	ldr	pc, _start_armboot	//跳入到第二阶段,执行_start_armboot函数,这个时候由于已经将uboot搬迁到内存中,所以下面的
					//所有命令已经是在RAM中运行了。

_start_armboot:	.word start_armboot

 总结:这里面有两种理解方式,

    (1)cpu在上电重启后,代码是在Nor Flash中,我们总认为程序代码只有在RAM中才能执行,所以就不明白,为什么上电后,程序代码还能运行,这个概念是有问题的,Nor Flash或Nand Flash虽然是外部flash,虽然相对于RAM执行速度慢,但是还是可以执行代码的,此时的程序执行速度可以理解为“走起来”,所以需要尽可能的先去初始化内存等必要外围,然后立即将uboot代码搬迁到RAM中,再从RAM中进行重新运行。这样程序就能“跑起来”。

    (2)CPU中有一块自带的SRAM,大小为4Kb,CPU在设计时,在上电后,会自动将Flash中前4KB的代码搬迁到这4Kb的内存中,然后程序也能在这块“小内存”中快速执行那些特别紧迫,特别必要的初始化动作,然后尽快把uboot搬迁到 外部RAM中,让程序能够大步跑起来。这里的 4Kb小内存,有人成为垫脚石,反正就是类似的意思。

    最后,这里所有的实现,都用了adr命令的巧妙,它能自动的根据当前PC的地址,找到对应的标号,相当于反推判断当前程序是在RAM中运行,还是在Flash中运行,以便后面进行判断,然后进行copy动作。

猜你喜欢

转载自blog.csdn.net/u012351051/article/details/80905305