您的位置:首页 > 其它

uboot-2012.04.01移植到mini2440(一)启动流程、内存分布及重定位初步分析

2013-12-18 16:58 585 查看
编译环境:ubuntu9.10uboot版本:u-boot-2012.04.01开发平台:mini2440
u-boot-2012.04.01启动流程:


1. 设置为管理模式

2. 关闭看门狗

3. 关中断

4. 设置时钟分频比

5. 跳转到cpu_init_crit执行


a. 禁止MMU、关闭caches


b. 跳入到lowlevel_init执行


*. 初始化内存控制器


6. 设置堆栈,为跳入board_init_f做准备


a. 跳入board_init_f执行


*. board_init_f函数中进行了各种初始化,并重新设置了堆栈,为重定位代码做准备


*. 调用relocate_code(addr_sp, id, addr)进行代码重定位


7. 清bss段

8. 跳转到第二阶段board_init_r执行


自此,Uboot第一阶段执行完毕。我们知道重定位代码是在board_init_f函数中调用relocate_code(addr_sp, id, addr)函数完成的。relocate_code是用汇编实现的,根据ATPCS约定addr_sp、id、addr分别存放在r0,r1,r2寄存器中。那么这个三个参数的值是多少呢?这需要分析在relocate_code之前的代码。board_init_f是用C语言编写的,从汇编中调用C函数,必须首先设置好堆栈。


/* Set stackpointer in internal RAM tocall board_init_f */

call_board_init_f:


ldr	sp, =(CONFIG_SYS_INIT_SP_ADDR)//sp =CONFIG_SYS_INIT_SP_ADDR


bic	sp, sp, #7//清除sp低3位,也就是8字节对齐


ldr     r0, 0x00000000//r0 = 0


bl	board_init_f


其中宏定义CONFIG_SYS_INIT_SP_ADDR于文件include/configs/smdk2410.h中定义,原型为:


#define CONFIG_SYS_INIT_SP_ADDR	(CONFIG_SYS_SDRAM_BASE+ 0x1000 - \

GENERATED_GBL_DATA_SIZE)

#define CONFIG_SYS_SDRAM_BASE	PHYS_SDRAM_1

#define PHYS_SDRAM_1			0x30000000


GENERATED_GBL_DATA_SIZE暂时未找到定义处,但它是全局数据gd_t结构体的大小。跳入board_init_f之前内存分布如下:


接下来需要找出addr和addr_sp的值是什么,代码摘抄如下:


addr =CONFIG_SYS_SDRAM_BASE + gd->ram_size;	// 1.  addr = 0x3400,0000
addr -= (4096 * 4); 				// 2.  16k保留给TLB, addr = 0x33ff,c000
addr &= ~(0x10000 - 1); 			// 3.  低16位清零,64k对齐,addr = 0x33ff,0000
addr &= ~(4096 - 1);				// 4.  4k对齐,addr = 0x33ff,0000
addr -= gd->mon_len;				// 5.  gd->mon_len = _bss_end_ofs, bss_end_ofs定义为__bss_end__ -_start,表示整						       个Uboot代码大小
addr &= ~(4096 - 1);				// 6.  4k对齐

addr_sp =addr - TOTAL_MALLOC_LEN;		// 7.  留出大小为TOTAL_MALLOC_LEN内存,分配给堆
addr_sp -= sizeof (bd_t);			// 8.  留出bd_t大小的内存
bd = (bd_t *) addr_sp;				// 9.  bd指向此时add_sp
addr_sp -= sizeof (gd_t);			// 10. 留出gd_t大小的内存
id = (gd_t *) addr_sp;				// 11. gd指向此时add_sp
addr_sp -= 12;					// 12. 留出12Bytes空间
addr_sp &= ~0x07;				// 13. addr_sp 8字节对齐


另外board_init_f代码后面有memcpy(id, (void *)gd, sizeof(gd_t)),该代码会把之前设置过的gd全局变量(保存在调用board_init_f之前设置的栈中,也就是上图所示红色SP所指位置)原原本本拷贝到id所指向的内存中。此外,gd全局变量还包含有指向bd变量的指针,代码为gd->bd = bd。知道addr_sp、id、addr三个参数的含义后,接下来分析relocate_code函数的具体过程。relocate_code:
mov  r4, r0		/* 保存addr_sp到r4 */
mov  r5, r1		/* 保存gd地址到r5,addr指向uboot在内存中的存放地址 */
mov  r6, r2		/* 保存addr地址到r6,即需要拷贝到的位置 */

/* Set up the stack */
stack_setup:
mov  sp, r4  /* sp = addr_sp, 即栈指针 */

adr  r0, _start 	/* r0 = 0x0000,0000,即r0指向代码段起始地址 */
cmp  r0, r6		/* 判断代码是否需要拷贝 */
beq  clear_bss		/* 不需要拷贝,则跳转到clear_bss */
mov  r1, r6		/* r1为uboot需要拷贝到的位置,即addr */
ldr  r3, _bss_start_ofs // r3为除bss段外的代码大小,或者称为偏移量,定义为__bss_start - _start
add  r2, r0, r3		/* r2等于拷贝前代码的结束地址 */

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  	/* BLO指令:小于(无符号数) */


上面的汇编实现了把代码从nor flash(不支持nand flash,因为nand flash不能像内存一样读)中。该版本的uboot重定位和之前版本的重定位的区别是,该uboot的链接地址不是通过_TEXT_BASE指定的,而是根据编译后uboot文件大小来确定的。所以,在上面代码执行完毕后,实际重定位的工作并未全部完成,还需要重定位诸如.rel.dyn、.dynsym等各种段,这部分留待下次分析。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: