s3c2440_uboot移植(一)s3c2410的uboot源码分析

uboot启动流程分析

进行uboot移植之前,我们需要对uboot有一个较为详细的了解,详细可以看这篇文章:

uboot启动流程分析_Bin Watson的博客-CSDN博客

初始uboot源码分析——s3c2410

start.S

​ 同启动流程,s3c2410的uboot的源码内容和启动流程中的4410的uboot大同小异,现在我们需要深入细节进行分析。我们依然从start.S开始分析:
start_code前部分

顺着start_code入口标号,我们一路往下可以看到,在上图中的start_code前部分代码进行的操作有:

  1. 设置CPU为SVC32模式
  2. 关看门狗
  3. 关中断
  4. 设置时钟比例

沿start_code继续往下追踪:
start_code_crt
我们可以看到cpu_init_crit,在启动流程中我们说过,其cpu_init_crit主要是进行初始化时钟、初始化网卡、初始化串口等操作。这里我们详细看看cpu_init_crit里面的细节是怎么样的:
在这里插入图片描述
从上面的cpu_init_crit的代码,我们可以看到这里有点稍微不同于启动流程的地方,这将cache和MMU的关闭移到了cpu_init_crit里面。接着,在第353行调用了lowlevel_init
在这里插入图片描述
值得注意的是,lowlevel_init的代码存放在board\samsung\smdk2410里面,这个很关键。这说明lowlevel_init是板级相关的初始化工作,如果我们需要移植uboot到我们自己的开发板上面,那么就可以在这里添加我们客制化的,用于初始化我们的板端的代码。
我们并没有看见有关于初始化串口、网卡之类的代码。这是因为这些代码可能是需要我们自己来实现的,也就是添加在board目录下面。这里三星只默认提供了一个模板,需要我们进行补充。

然后从lowlevel_init回到我们是start.S,继续沿cpu_init_crit往下追踪:

在start.S的第187行处,指定了栈地址,我们溯源下去后可以推测出其地址为 0 x 300 0 ′ 0 f 80 0x3000'0f80 0x30000f80,并且可以推测出 0 x 300 0 ′ 0 f 80 0x3000'0f80 0x30000f80 ~ 0 x 300 0 ′ 0000 0x3000'0000 0x30000000 存放的 gd_t,也就是我们的全局数据GD。(这里我们不详细展开推测过程)

其内存分布大致如下图所示:

到这里,我们就初步指定了global_data存放地址和栈地址。接着我们就可以调用C语言的函数,也就是board_init_f 函数。

启动流程分析时我们说,board_init_f 函数主要是对GD进行初始化工作,同时准备进行代码的自搬移操作的准备。

下面我们深入board_init_f 函数进行分析:
在这里插入图片描述
board_init_f的一开始是对gd处的存储空间进行清空。在第290行处调用了init_sequence数组里面的一些列初始化函数,下面是init_sequence的内部细节:
在这里插入图片描述

从上图的函数名,我们大概可以猜到了,在这里完成的工作有:

  1. board_early_init_f:CPU工作频率、GPIO引脚的设置(我们移植时,可能需要修改这里)

  2. timer_init:定时器的初始化工作

  3. env_init:环境初始化

  4. init_baudrate:波特率的初始化

  5. serial_init:串口的初始化(可见这里的串口并不是在lowlevel_init处进行初始化的)

  6. console_init_t:终端的一些操作

  7. display_banner:显存的操作

  8. print_cpuinfo:打印cpu的信息

  9. dram_init:进行DRAM的初始化工作

如果我们需要添加自定义的初始化代码,也可以考虑在这里面添加一个函数。
回到board_init_f,再往下的代码就是一些列对gd的初始化工作。我们详细来看看如何初始化的:(为了方便看,这里剔除一些调试用代码和与主体无关的代码)

	/* ram_size中记录的大小为64M 
	 * 这里addr的地址为0x3400'0000
	 * addr变量指定的是uboot重定位后的存放地址
	 */
	addr = CONFIG_SYS_SDRAM_BASE + gd->ram_size;	/* 0X3000'0000 + 64M = 0x3400'0000 */

	/* 分配TLB table的存储空间 */
	/* reserve TLB table */
	addr -= (4096 * 4);		/* 0X3000'0000 - 4*4K = 0x33FF'C000 */

	/* round down to next 64 kB limit 进行64KB的对齐 */
	addr &= ~(0x10000 - 1);	/* 0X3000'0000 ^ 0xffff = 0x33FF'0000 */

	gd->tlb_addr = addr;

	/* round down to next 4 kB limit */	
	addr &= ~(4096 - 1); 	/* 对齐 */

/* 如果使用了LCD,就需要在内存中划出一块显存空间 */
#ifdef CONFIG_LCD
#ifdef CONFIG_FB_ADDR
	gd->fb_base = CONFIG_FB_ADDR;
#else
	/* reserve memory for LCD display (always full pages) */
	addr = lcd_setmem(addr);
	gd->fb_base = addr;
#endif /* CONFIG_FB_ADDR */
#endif /* CONFIG_LCD */

	/* 
	 * reserve memory for U-Boot code, data & bss
	 * round down to next 4 kB limit
	 */
	addr -= gd->mon_len;	/* 0x33FF'0000 - 0x000a'e4e0 = 0x33F4'1B20 */
	addr &= ~(4096 - 1);	/* U-Boot重定位后的地址 0x33F4'0000 */

#ifndef CONFIG_SPL_BUILD
	/* 开始设置栈地址,先设置堆地址
	 * reserve memory for malloc() arena
	 */
	addr_sp = addr - TOTAL_MALLOC_LEN;	/* 0x33F4'0000 */
	debug("Reserving %dk for malloc() at: %08lx\n",
			TOTAL_MALLOC_LEN >> 10, addr_sp);

	/* 接着分配bd的存储空间,board_info记录了板端的一些信息
	 * (permanently) allocate a Board Info struct
	 * and a permanent copy of the "global" data
	 */
	addr_sp -= sizeof (bd_t);	/*  */
	bd = (bd_t *) addr_sp;
	gd->bd = bd;

#ifdef CONFIG_MACH_TYPE
	gd->bd->bi_arch_number = CONFIG_MACH_TYPE; /* board id for Linux */
#endif

	/* 再往下,指定GD新的存放位置 */
	addr_sp -= sizeof (gd_t);
	id = (gd_t *) addr_sp;

	/* setup stackpointer for exeptions */
	gd->irq_sp = addr_sp;
	
	/* 如果在uboot中配置了使用中断,
     * 则需要在这里分配IRQ和FIQ的中断栈空间
     */
#ifdef CONFIG_USE_IRQ
	addr_sp -= (CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ);
	debug("Reserving %zu Bytes for IRQ stack at: %08lx\n",
		CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ, addr_sp);
#endif
	/* leave 3 words for abort-stack    */
	addr_sp -= 12;

	/* 8-byte alignment for ABI compliance */
	addr_sp &= ~0x07;	/* 新的栈地址 */
#endif

	debug("New Stack Pointer is: %08lx\n", addr_sp);

#ifdef CONFIG_POST
	post_bootmode_init();
	post_run(NULL, POST_ROM | post_bootmode_get(0));
#endif

	gd->bd->bi_baudrate = gd->baudrate;
	/* Ram ist board specific, so move it to board code ... */
	dram_init_banksize();
	display_dram_config();	/* and display it */

	gd->relocaddr = addr;				/* 指定重定位地址 */
	gd->start_addr_sp = addr_sp;		/* 指定新的栈地址 */
	gd->reloc_off = addr - _TEXT_BASE;	/* 指针重定位地址 外加偏移地址,这里为0 */
	debug("relocation Offset is: %08lx\n", gd->reloc_off);
	memcpy(id, (void *)gd, sizeof(gd_t));	/* 将旧的gd拷贝的新的gd位置 */

	relocate_code(addr_sp, id, addr);	/* 进行代码重定位 */

	/* NOTREACHED - relocate_code() does not return */
}

根据对源码的分析,我们大致可以画出下面这张内存分布图,从上往下:
在这里插入图片描述

  1. 首先是TLB table占 4096 * 4 个字节的空间
  2. 如果使用了LCD,那么紧接着需要划出一块显存的空间。暂不考虑LCD的,所以这里没有划出来。
  3. 接着是uboot代码新的存放空间,大小为 0x000a’e4e0。可以通过使用启动流程中使用的arm-linux-nm查找符号_bss_end_ofs的地址,再在反汇编代码中找到该地址,其值就是uboot所需要的空间。
  4. 往下是malloc的使用的堆空间,大小不好推测,这里我们就不细究。
  5. 往下存放的是board_info结构体,其记录的是单板相关的信息
  6. 再往下是gd新的存放位置,在上面代码的第94行,使用了memcpy(id, (void *)gd, sizeof(gd_t));将旧gd拷贝到新的位置。
  7. 如果需要在uboot里使用中断,就需要指定一个中断栈,在gd往下就是分配中断栈空间。每个栈的大小都是4K;
  8. 剩余往下到0x3000’0000的空间就是栈的空间

需要注意的是,这个内存分配图在uboot被指定不同了配置的情况下,可能会造成最终内存分布的不相同。例如:如果开启了调试功能,那么会在TLB表前面划出一块用于调试打印使用的空间。

​ 在board_init_f的最后一行调用了一个relocate_code(addr_sp, id, addr);的函数,而这个函数定义在start.S里面,我们随着relocate_code继续追踪。根据函数名我们就可以猜出,其是进行代码的重定位操作

/*------------------------------------------------------------------------------*/

/*
 * void relocate_code (addr_sp, gd, addr_moni)
 *
 * This "function" does not return, instead it continues in RAM
 * after relocating the monitor code.
 *
 */
	.globl	relocate_code
relocate_code:
	mov	r4, r0	/* save addr_sp */
	mov	r5, r1	/* save addr of gd */
	mov	r6, r2	/* save addr of destination */

	/* Set up the stack						    */
stack_setup:
	mov	sp, r4

	adr	r0, _start
	cmp	r0, r6
	beq	clear_bss		/* skip relocation */
	mov	r1, r6			/* r1 <- scratch for copy_loop */
	ldr	r3, _bss_start_ofs
	add	r2, r0, r3		/* r2 <- source end address	    */

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

#ifndef CONFIG_SPL_BUILD
	/*
	 * fix .rel.dyn relocations
	 */
	ldr	r0, _TEXT_BASE			/* r0 <- Text base */
	sub	r9, r6, r0				/* r9 <- relocation offset */
	ldr	r10, _dynsym_start_ofs	/* r10 <- sym table ofs */
	add	r10, r10, r0			/* r10 <- sym table in FLASH */
	ldr	r2, _rel_dyn_start_ofs	/* r2 <- rel dyn start ofs */
	add	r2, r2, r0				/* r2 <- rel dyn start in FLASH */
	ldr	r3, _rel_dyn_end_ofs	/* r3 <- rel dyn end ofs */
	add	r3, r3, r0				/* r3 <- rel dyn end in FLASH */
fixloop:
	ldr	r0, [r2]		/* r0 <- location to fix up, IN FLASH! */
	add	r0, r0, r9		/* r0 <- location to fix up in RAM */
	ldr	r1, [r2, #4]
	and	r7, r1, #0xff
	cmp	r7, #23			/* relative fixup? */
	beq	fixrel
	cmp	r7, #2			/* absolute fixup? */
	beq	fixabs
	/* ignore unknown type of fixup */
	b	fixnext
fixabs:
	/* absolute fix: set location to (offset) symbol value */
	mov	r1, r1, LSR #4		/* r1 <- symbol index in .dynsym */
	add	r1, r10, r1			/* r1 <- address of symbol in table */
	ldr	r1, [r1, #4]		/* r1 <- symbol value */
	add	r1, r1, r9			/* r1 <- relocated sym addr */
	b	fixnext
fixrel:
	/* relative fix: increase location by offset */
	ldr	r1, [r0]
	add	r1, r1, r9
fixnext:
	str	r1, [r0]
	add	r2, r2, #8		/* each rel.dyn entry is 8 bytes */
	cmp	r2, r3
	blo	fixloop
#endif

代码重定位包括将uboot的源码从Flash拷贝到SDRAM里、修改符号表内的信息。

往下是清除bss段、然后调用C语言的board_init_r函数。

clear_bss:
#ifndef CONFIG_SPL_BUILD
	ldr	r0, _bss_start_ofs
	ldr	r1, _bss_end_ofs
	mov	r4, r6			/* reloc addr */
	add	r0, r0, r4
	add	r1, r1, r4
	mov	r2, #0x00000000		/* clear			    */

clbss_l:str	r2, [r0]		/* clear loop...		    */
	add	r0, r0, #4
	cmp	r0, r1
	bne	clbss_l

	bl coloured_LED_init	/* 未实现 */
	bl red_led_on			/* 未实现 */
#endif

/*
 * We are done. Do not return, instead branch to second part of board
 * initialization, now running from RAM.
 */
#ifdef CONFIG_NAND_SPL
	ldr     r0, _nand_boot_ofs
	mov	pc, r0

_nand_boot_ofs:
	.word nand_boot
#else
	ldr	r0, _board_init_r_ofs
	adr	r1, _start
	add	lr, r0, r1
	add	lr, lr, r9
	/* setup parameters for board_init_r */
	mov	r0, r5		/* gd_t */
	mov	r1, r6		/* dest_addr */
	/* jump to it ... */
	mov	pc, lr		/* 跳转至board_init_r进入main_loop循环 */

_board_init_r_ofs:
	.word board_init_r - _start
#endif

至此,s3c2410的uboot源码就大致分析完成了。

s3c2410 uboot总结

对于s3c2440/s3c2410来说,机器上电后可以从两个不同的地方读取uboot,分别是NorFlash和NandFlash。

对于Nand启动,会将Nand Flash的前4KB的代码拷贝到内部的SRAM中,然后从SRAM的0地址处开始执行;而对于Nor启动,则直接从Nor Flash的零地址处开始执行,这时的Internal SRAM就放在内存的最高处。

程序开始执行后,会依次进行:

  1. 设置CPU为SVC32模式,关看门狗,关中断,设置时钟比例。

  2. 调用cpu_init_crit初始化、关闭cacahe、关闭MMU、调用lowlevel_init进行初始化;

  3. 设置全局数据gd,利用gd进行代码重定位。

    无论是Nor启动还是Nand启动,为了提高程序的运行效率,我们都需要将uboot从Flash里面拷贝到SDRAM里面,其起始地址为0x3000 000;

  4. 设置C语言运行环境,调用board_init_r进入C语言。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值