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

news/2025/2/11 12:35:56/

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_ADDRgd->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_TYPEgd->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_IRQaddr_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;	/* 新的栈地址 */
#endifdebug("New Stack Pointer is: %08lx\n", addr_sp);#ifdef CONFIG_POSTpost_bootmode_init();post_run(NULL, POST_ROM | post_bootmode_get(0));
#endifgd->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, r4adr	r0, _startcmp	r0, r6beq	clear_bss		/* skip relocation */mov	r1, r6			/* r1 <- scratch for copy_loop */ldr	r3, _bss_start_ofsadd	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, #0xffcmp	r7, #23			/* relative fixup? */beq	fixrelcmp	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, r3blo	fixloop
#endif

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

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

clear_bss:
#ifndef CONFIG_SPL_BUILDldr	r0, _bss_start_ofsldr	r1, _bss_end_ofsmov	r4, r6			/* reloc addr */add	r0, r0, r4add	r1, r1, r4mov	r2, #0x00000000		/* clear			    */clbss_l:str	r2, [r0]		/* clear loop...		    */add	r0, r0, #4cmp	r0, r1bne	clbss_lbl 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_SPLldr     r0, _nand_boot_ofsmov	pc, r0_nand_boot_ofs:.word nand_boot
#elseldr	r0, _board_init_r_ofsadr	r1, _startadd	lr, r0, r1add	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语言。


http://www.ppmy.cn/news/639922.html

相关文章

mini2440驱动分析之LCD

mini2440驱动分析之LCD mini2440集成了lcd控制器的接口&#xff0c;板子上接的lcd硬件是统宝240*320&#xff0c;TFT型lcd。lcd驱动对应的文件为s3c2410fb.c。要读懂这个驱动必须了解linux platform子系统的知识。因为这个驱动是以platform驱动的形式注册到内核。而且还需要fra…

【MySQL Shell】8.9.2 InnoDB ClusterSet 集群中的不一致事务集(GTID集)

AdminAPI 的 clusterSet.status() 命令警告您&#xff0c;如果 InnoDB 集群的 GTID 集与 InnoDB ClusterSet 中主集群上的 GTID 集不一致。与 InnoDB ClusterSet 中的其他集群相比&#xff0c;处于此状态的集群具有额外的事务&#xff0c;并且具有全局状态 OK_NOT_CONSISTENT 。…

pvx什么材质_100818 常用塑胶材料牌号

日本宝理 (Polyplstics) &#xff0c; 台湾奇美 (Chimei) &#xff0c; 美国通用( GE )&#xff0c; 美国 GLS, 美国杜邦 (Dupont) &#xff0c; 美国山都坪( Santoprene )&#xff0c; 德国拜耳 (Bayer) &#xff0c; 德国巴赛尔( Basell )&#xff0c; 德国巴斯夫( Basf ) , …

Tiny 4412 lcd 驱动分析 2

/home/yangjia/samba/linux-3.5/arch/arm/mach-exynos/mach-tiny4412.c 。_initdata 定义在此文件中。平台驱动都重要在这里注册。平台驱动是匹配名字的。 static struct platform_device *smdk4x12_devices[] __initdata { #ifdef CONFIG_EXYNOS4_DEV_DWMCI&exynos_devi…

AcWing 4410. 吃鸡蛋 (简单模拟)

小明家里有 nn 个鸡蛋。 每天早上&#xff0c;小明都要吃 11 个鸡蛋作为早餐。 小明家里还有一个母鸡。 母鸡会在第 m,2m,3m…m,2m,3m… 天的晚上下蛋&#xff0c;每次只下 11 个蛋。 请问&#xff0c;连续多少天以后&#xff0c;小明就没有早餐鸡蛋吃了&#xff1f; 输入…

iTOP4412 LCD驱动(FB)

0 前言 我购买的LCD的接口是LVDS的,然而Exynos4412并不支持LVDS接口,因此iTOP4412需要通过转接芯片实现RGB到LVDS信号的转换,本文重点分析相关硬件电路和软件驱动,内核版本为linux-3.0.15。 1 电路 Exynos4412支持MIPI-DSI、RGB和HDMI三种显示接口,并不支持LVDS接口,那么…

tiny4412 lcd驱动

fbmem.c文件提供了framebuffer驱动程序的通用文件操作接口&#xff0c;自定义的framebuffer驱动程序可以使用fbmem.c中提供默认的接口。用EXPORT_SYMBOL导出到其他文件中应用 s3c-fb.c是针对的三星开发板的lcd驱动文件接口&#xff08;s3c_fb_probe等&#xff09;。 一下都是在…

【EI会议2023】12.20之后ddl

csdn 摘出来上文中的一些ddl var code "7aecbc27-0665-4410-875f-e4b16853eb64" # ICET 2023(成都 5月12日-5月15日) http://www.icet.net/track9.html 截稿时间2022.12.20 通知录用:2023.1.20 SEGRE 2023(长沙 4月21日-4月23日) http://www.icsegre.org/ 截…