imx6 uboot启动流程分析

参考http://blog.csdn.net/skyflying2012/article/details/25804209

这里以imx6平台为例,分析uboot启动流程
对于任何程序,入口函数是在链接时觉得的,uboot的入口是由链接脚本决定的.uboot下armv7链接脚本默认目录为arch/arm/cpu/u-boot.lds.这个可以在配置文件中与CONFIG_SYS_LDSCRIPT来指定


1.由于imx6dl芯片属于armv7架构,在arch/arm/cpu/目录下,通过分析链接脚本u-boot.lds代码段.text可知:可执行程序的入口是_start
2._start在arch/arm/lib/vectors.S中,然后vectors.S中有跳转到reset函数,reset函数在arch/arm/cpu/armv7/start.S中,.....,待一系列硬件初始化后,跳转到_main函数执行
3.ARM32平台的_main位于arch/arm/lib/crt0.S中
4.crt0是C-runtime Startup Code的简称,意思就是运行C代码之前的准备工作.关于_main函数,crt0.S有详细注释:
1).设置C代码的运行环境,为调用board_init_f接口做准备
a)设置堆栈(C代码的函数调用,堆栈是必须的).如果当前的编译是SPL(由CONFIG_SPL_BUILD定义),可单独定义堆栈基址(CONFIG_SPL_STACK),否则,通过CONFIG_SYS_INIT_SP_ADDR定义堆栈基址.
b)调用board_init_f接口,从堆栈开始的地方,为u-boot中大名鼎鼎的GD('global data')数据结构,分配空间
c)调用board_init_f_init_reserve接口,对GD进行初始化
2).调用board_init_f函数,完成一些前期的初始化工作,例如:
a)点亮一个Debug用的LED灯,表示u-boot已经活了
b)初始化DRAM,DDR等system范围的RAM等
c)计算后续代码需要使用的一些参数,包括relocation destination,the future stack,the future GD location等.
3).如果当前是SPL(由CONFIG_SPL_BUILD控制),则_main函数结束,直接返回.如果是正常的u-boot,则继续执行后续的动作
4).根据board_init_f指定的参数,执行u-boot的relocation操作
5).清除BSS段
6).调用board_init_r函数,执行后续的初始化操作

4.crt0是C-runtime Startup Code的简称,意思就是运行C代码之前的准备工作.关于_main函数,crt0.S有详细注释:
1).设置初始化运行环境,为调用board_init_f()接口做准备
初始化运行环境仅仅设置堆栈并且为GD('global data')数据结构分配空间,这两者都位于一些已经可用的RAM(SRAM, locked cache...)空间.在这上下文,未初始化的全局变量或BSS段不能使用,只有初始化的常量可用
2).调用board_init_f()接口
该接口为系统的执行做好硬件准备.因为RAM还不能使用,board_init_f()必须使用当前的GD去存储一些数据,这些数据要传送到启动的下一阶段.这些数据包括relocation destination, the future stack, the future GD location
---------------------------------------------------------------------------
以下步骤仅仅适用于non-SPL builds(SPL是Secondary Program Loader的简称,之所以称作secondary,是相对于ROM code来说的.SPL是u-boot中独立的一个代码分支,由CONFIG_SPL_BUILD配置项控制,是为了在正常的u-boot image之外,提供一个独立的,小size的SPL image,通常用于那些SRAM比较小(或者其它限制),无法直接装载并运行整个u-boot的平台),即如果当前时SPL(由CONFIG_SPL_BUILD控制),则_main函数结束,直接返回.如果时正常的u-boot,则继续执行后续动作.
3).设置中间环境变量,board_init_f()函数在系统的RAM中为堆栈和GD申请空间,但是BSS段和非常量依然不可用.
4).调用relocate_code()接口
该接口通过board_init_f()接口,从当前地址到目的地址,重新定位u-boot
5).设置初始化最终的运行环境,为调用board_init_r()接口做准备
初始化运行环境包括初始化BBS段(初始化为0),初始化非常量数据(初始化成需要的值)和初始化系统RAM中堆栈.通过board_init_f()接口GD已被保留.除了内存的初始化,一些CPUs还有别的一些工作需要做,引出调用c_runtime_cpu_setup()接口
6).分支到board_init_r执行

imx6sabresd board uboot启动流程分析:
前面都相同,这里从_main(crt0.S)开始分析:
其中board_init_f接口imx6sabresd有自己的接口,该接口位于board/freescale/mx6sabresd/mx6sabresd.c中,具体做了如下初始化:
a.arch_cpu_init(),设置AIPS和关看门狗,位于arch/arm/cpu/armv7/mx6/soc.c中
b.ccgr_init(),初始化时钟模块ccm,位于board/freescale/mx6sabresd/mx6sabresd.c中
c.gpr_init(),初始化AXI,IPU,位于board/freescale/mx6sabresd/mx6sabresd.c中
d.board_early_init_f(),IOMUX(IO多路复用),设置i2c,位于board/fresscale/mx6sabresd/mx6sabresd.c中
e.timer_init(),设置系统的timer,位于arch/arm/imx-common/timer.c中
f.preloader_console_init(),串口时钟使能和控制台初始化,位于common/spl/spl.c中
g.spl_dram_init(),dram初始化,位于board/freescale/mx6sabresd/mx6sabresd.c中
h.memset(),清空bss段
i.board_init_r(),进入到后置的班级初始化

进入board_init_r接口,位于arch/arm/lib/board.c中,具体做了如下工作:
a.bootstage_mark_name(),登记boot启动阶段
b.enable_caches(),使能缓存
c.board_init(),具体的板级初始化,位于board/freescale/mx6sabresd/mx6sabresd.c中,具体又做了如下工作:setup_spi,setup_i2c,setup_usb,setup_epdc,setup_sata,setup_yaxon(加入自己想要做的工作,imx6电源LED灯显示)
d.set_cpu_clk_info(),初始化时钟框架
e.serial_initialize(),初始化串口
f.

进入main_loop():
a.bootstage_mark_name(),调用了show_boot_progress,利用它显示启动进程(progress),此处为空函数,这里未实现
b.modem_init(),这里未实现
c.setenv(),用于显示uboot版本号,编译日期和事件,以及时间,这些都由u-boot构建系统自动生成
d.cli_init(),初始化hush shell使用的一些变量
e.run_preboot_environment_command(),从环境变量中获取"preboot"的定义,该变量包含一些预启动命令,一般环境变量中不包含该项配置
f.bootdelay_process(),从环境变量中取出"bootdelay"和"bootcmd"的配置值,将取出的"bootdelay"配置值转换成整数,赋值给全局变量stored_bootdelay,最后返回"bootcmd"的配置值.bootdelay为u-boot的启动延时计数值,计数期间内如无用户按键输入干预,那么执行"bootcmd"配置中的命令(其实时执行返回字符串s配置的命令,我们可以通过返回不同的字符串来执行不同的启动命令)
g.由于没有定义CONFIG_OF_CONTROL宏,函数cli_process_fdt返回false,即不会执行cli_secure_boot_cmd()
h.进入autoboot_command(),stored_bootdelay != -1, s = "bootcmd", abortboot(stored_bootdelay)
进入stored_bootdelay,由于没有定义CONFIG_AUTOBOOT_KEYED(该宏用来使能用户名密码登录),直接调用abortboot_normal()
进入abortboot_normal(),在执行时间stored_bootdelay(秒)内,如无用户按键输入干预,那么abortboot_normal函数返回0,否则返回1.
由判断条件if (stored_bootdelay != 1 && s && !abortboot(stored_bootdelay))可知
若在计数期间无用户按键输入干预,那么abortboot(stored_bootdelay)返回0,即条件成立,执行run_command_list(s, -1, 0),该函数执行环境变量s("bootcmd")配置值.函数run_command_list调用hush shell命令解释器(parse_stream_outer函数),结束s("bootcmd")中的启动命令.环境变量s("bootcmd")中的启动命令,用来设置linux必要的启动环境,然后加载和启动linux内核.uboot启动linux内核后,将控制权交给linux内核,至此不再返回
否则有用户按键输入干预,abortboot(stored_bootdelay)返回1,即条件不成立,不执行任何操作,结束autoboot_command()函数,然后继续执行main_loop()函数中的cli_loop()函数,cli_loop()执行hush shell命令解释器(parse_file_outer函数),parse_file_outer函数进行必要的初始化后,也将调用hush shell命令解释器(parse_stream_outer函数)
static int parse_stream_outer(structin_str*inp,intflag)
{
do {
...
...
run_list(...);
} while (rcode != -1 && !(flag & FLAG_EXIT_FROM_LOOP) && //#define FLAG_EXIT_FROM_LOOP 1
(inp->peek != static_peek || b_peek(inp)));
}
parse_stream_outer()函数里有个do-while会循环命令解析器的"命令输入解析-执行"运行模式
其中run_list(...)执行如下函数调用流程:
run_list-->run_list_real-->run_pipe_real,最后在函数run_pipe_real中有return cmd_process(...),函数cmd_process最后完成u-boot命令的定位和执行
------------------------------------------------------------------------------------------------------------------------------------------
至此main_loop()分析完毕

-----------------------------------------------------------------------------------------------------------------------------------------
以下分析uboot命令执行,即cmd_process(...)函数,函数cmd_process在common/command.c文件中


-----------------------------------------------------------------------------------------------------------------------------------------
以下是项目中用来实现系统恢复策略方法:
目的:在uboot阶段通过检测硬件gpio的电平变化来实现uboot启动设置
方法:添加一个gpio电平检测函数,检测到电平变化通过修改传入给autoboot_command(s)函数参数s来改变uboot启动设置,所以具体到实施是要修改参数s,而参数s是通过bootdelay_process()函数来修改的

具体实施步骤:
由于原项目中emmc分区为4个分区(即par1(dtb, zImage), par2(rootfs), par3(user), par4(data)),所有我们需要重新分区, 分为6个分区(par1(zImage, dtb), par2(zImage, dtb, ramdisk.image.gz.uboot这三部分是为了启动最小系统, 跟u盘升级一样(把最小系统放在u盘里), 这里只是把最小系统放在emmc的第二个分区里), par3(zImage, dtb, rootfs.tar, user.tar, data.tar这些原厂设置的文件, 后面需要把他们依次解压到对应的分区), par4(rootfs), par5(user), par6(data)), 并且要修改uboot源码使其支持这个功能, 所有我们还得重新烧写uboot到emmc中
1.重新分为6个分区, 并把相应的文件(原厂设置的文件)烧写到对应分区
2.

uboot启动第一阶段,start.S文件在arch/arm/cpu/armv7/目录下

uboot启动第二阶段,board.c文件arch/arm/lib/目录下