參考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/目錄下
