u-boot分析與使用—源碼分析
- 硬件平臺:韋東山嵌入式Linxu開發板(S3C2440.v3)
- 軟件平臺:運行於VMware Workstation 12 Player下UbuntuLTS16.04_x64 系統
- 參考資料:《嵌入式Linux應用開發手冊》、《嵌入式Linux應用開發手冊第2版》
- 開發環境:Linux 2.6.22.6 內核、arm-linux-gcc-3.4.5-glibc-2.3.6工具鏈、u-boot-1.1.6
目錄
u-boot分析與使用—源碼分析 - 一、前言
- 二、第一階段
- 1、設置CPU爲管理模式
- 2、關看門狗
- 3、關中斷
- 4、設置時鐘
- 5、根據情況判斷是否初始化SDARM
- 6、設置棧(只有設置棧之後纔可以調用C函數)
- 7、根據情況判斷是否提前清bss段
- 8、重定位
- 9、調用start_armboot(u-boot的第二階段)
- 10、總結
- 三、第二階段分析
- 1、進行一系列的初始化工作
- 2、初始化NOR FLASH
- 3、在內存中分配堆區
- 4、初始化NAND FLASH
- 5、設置環境變量
- 6、進入到`main_lopp()`死循環過程
- 6.1 資源的加載與版本號的設置
- 6.2 設置超時時間並打印一些信息
- 6.3 獲取環境變量`bootcmd`,以便後面內核的啓動
- 6.4 在超時時間內判斷鍵盤是否有輸入,無的話
- 6.5 在超時時間內判斷鍵盤是否有輸入,有的話,此時如果想啓動內核的話,需要通過輸入命令實現
- 7、總結
- 8、分析`run_command()`
- 四、總結
一、前言
在linux系統下,執行make
對u-boot
進行編譯時,輸出以下信息:
可以看到,除了關注腳本文件,也要關注cpu/arm920t/start.o
文件。
通過前面的分析可知,u-boot的主要目的如下:
- 從FLASH讀出內核
- 啓動內核
抱着這個目的,我們從cpu/arm920t/start.o
文件入手進行分析。
二、第一階段
這個階段分析的是cpu/arm920t/start.o
文件中的彙編代碼實現的功能。
展示的代碼並不是完整的代碼,是根據前言中的目的來進行分析與展示。
1、設置CPU爲管理模式
可以看到彙編文件一開始會跳到reset:
中執行
2、關看門狗
3、關中斷
4、設置時鐘
5、根據情況判斷是否初始化SDARM
- 比較
r0+_start
的值與_TEXT_BASE
的值,不相等則跳去執行cpu_init_crit
- 在
cpu_init_crit
中進行:關閉flush、清除caches、關閉MMU、跳去執行lowlevel_init
- 在
lowlevel_init
中,對SDRAM進行相關初始化
6、設置棧(只有設置棧之後纔可以調用C函數)
7、根據情況判斷是否提前清bss段
- 比較
r0+_start
的值與_TEXT_BASE
的值,不相等則跳去執行clean_bss
- 在
clean_bss
中進行:清除操作
就算這裏不清除,後面彙編代碼也會清除。
8、重定位
- 在
CopyCode2Ram
中判斷爲NOR啓動還是NAND啓動 - 從NOR啓動,
copy
工作簡單 - 從NAND啓動,先初始化NAND FLASH,後進行
copy
9、調用start_armboot(u-boot的第二階段)
10、總結
到此爲止,u-boot的第一階段的分析已經結束。
總體的流程如下:
- 設置爲管理模式
- 關看門狗
- 關中斷
- 設置時鐘
- 根據情況判斷是否初始化SDARM
- 設置棧(只有設置棧之後纔可以調用C函數)
- 根據情況判斷是否提前清bss段
- 重定位
- 調用start_armboot(u-boot的第二階段)
- 其中1~8步驟的設置稱爲硬件相關的設置,主要通過彙編代碼完成。
- 9步驟後的設置纔是啓動內核的代碼實現,比較複雜,需要通過C語言實現。
三、第二階段分析
這裏抱着啓動內核的目標,分析start_armboot()
函數,所在的位置爲u-boot-1.1.6/lib_arm/board.c
1、進行一系列的初始化工作
- 通過如下函數進行對應的初始化,如CPU初始化、板載資源的初始化、中斷初始化等等
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if ((*init_fnc_ptr)() != 0) {
hang ();
}
}
- 分析板載資源初始化
2_1. 進行把對應IO口拉高
2_2. 根據判斷結果設置機器ID
2_3. 設置啓動參數
2、初始化NOR FLASH
3、在內存中分配堆區
4、初始化NAND FLASH
5、設置環境變量
6、進入到main_lopp()
死循環過程
運行到這個函數這裏,就可以
6.1 資源的加載與版本號的設置
6.2 設置超時時間並打印一些信息
6.3 獲取環境變量bootcmd
,以便後面內核的啓動
這個參數當我們啓動bootloader時,執行print指令輸出的信息有它的顯示,這個參數與內核的啓動有關,後面會進行介紹。
6.4 在超時時間內判斷鍵盤是否有輸入,無的話
如果此時鍵盤無輸入,則會與如下信息:最明顯的就是打印"Booting Linux ...\n"
-
實際運行bootlloader的界面
-
此時會運行
run_command(s, 0)
,根據上面的到的**bootcmd
環境變量來啓動內核**
如何啓動我這裏只說結論,具體的分析過程可以看韋老師的分析博客【Uboot到底如何啓動內核】 -
nand read.jffs2 0x30007FC0 kerne
l等價於:nand read.jffs20x30007FC0 0x00060000 0x00200000
-
bootm 0x30007FC0
關鍵函數do_bootm()
,具體做了如下:讀取頭部,將內核移動到加載地址,啓動內核
6.5 在超時時間內判斷鍵盤是否有輸入,有的話,此時如果想啓動內核的話,需要通過輸入命令實現
6.5.1 進入菜單 run_command("menu", 0);
- 實際運行的顯示
6.5.2 等待此時鍵盤輸入的命令,若按下’q’
- 如果此時按下的是’q’,則程序會卡死在這個界面,並且等待命令的輸入:
7、總結
第二階段的作用大致如下:
- 進行CPU、中斷、設置啓動參數等初始化
- 初始化NOR FLASH、NAND FLASH,以便後面代碼的重定位
- 設置好環境變量
- 進入bootloader啓動界面,根據超時時間進行對應操作
4.1 超時時間內鍵盤不輸入,過了超時時間則啓動內核
4.2 超時時間內鍵盤輸入,進入菜單界面,根據命令進行相關操作
其中,在4.1和4.2中都有執行了相同的函數run_command()
,下面來分析它。
8、分析run_command()
8.1 處理多條命令情況
8.2 拆分命令
如果此時命令爲md.w 0
則拆分後結果如下:
8.3 分析find_cmd()
這個函數的作用是對拆分後得到的命令進行匹配
8.4 分析cmd_tbl_s
結構體
- 原型如下:
- 在鏈接腳本中有如下段
.u_boot_cmd : { *(.u_boot_cmd) }
通過搜索.u_boot_cmd
,並進行以下關鍵詞搜索得到如下結果:
- 對
cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage, help}
進行拆分得到: