1. 程式人生 > 實用技巧 >u-boot 移植 --->6、u-bootl流程粗線條梳理

u-boot 移植 --->6、u-bootl流程粗線條梳理

通過前面的除錯瞭解到s5pv210這個晶片的啟動流程是需要將u-boot分為兩部分的分別為SPL和u-boot。這裡我使用網上的方式不直接使用u-boot的SPL連線指令碼單獨生成SPL的image而是用前面介紹的方法 [https://www.cnblogs.com/w-smile/p/13124631.html](u-boot 移植 --->3、S5PV210啟動序列)將u-boot的前16k直接截取出來作為SPL。
IROM中
這一部分可以參考其他博文,或者三星的文件。
在IRAM中
通過啟動連線指令碼arch\arm\cpu\u-boot.lds 我們找到了整個程式碼的入口_start。全域性尋找找到檔案arch\arm\lib\vectors.S符合本次架構所以整個程式碼入口就在這裡內容如下:

_start:

#ifdef CONFIG_SYS_DV_NOR_BOOT_CFG
	.word	CONFIG_SYS_DV_NOR_BOOT_CFG
#endif

	b	reset
	ldr	pc, _undefined_instruction
	ldr	pc, _software_interrupt
	ldr	pc, _prefetch_abort
	ldr	pc, _data_abort
	ldr	pc, _not_used
	ldr	pc, _irq
	ldr	pc, _fiq

所以可以看出來這裡也是符合ARM架構要求,在這裡定義了中斷向量表,然後第一句指令是一個跳轉指令,將執行流跳轉到reset符號了,經過查詢這個符號定義在arch\arm\cpu\armv7\start.S 這是架構通用的部分所以可以參考u-boot原始碼這裡僅羅列這段程式操作的內容:
1. 檢查是否使能了大物理頁面擴充套件,如果使能了則進行必要的配置。(CP15)
2. 禁止IRQ和FIQ,配置處理器模式為SVC。(CPSR)
3. 設定_start 符號地址為異常向量表基址,並禁止cache,TLB,BP,MMU(CP15)
4. 呼叫lowlevel_init(如果定義了CONFIG_SKIP_LOWLEVEL_INIT_ONLY則不執行),這一部分就是晶片移植人員需要早期初始化的內容可以放在這個標號下實現,我這裡就是配置時鐘,配置晶片DRAM介面,配置UART0。
5. 設定堆疊為IRAM空間,並拷貝SD卡中特定地址的完整u-boot映象到DRAM指定地址,SD的驅動實在晶片的IROM中完成的。(這一部分是本來的u-boot內容沒有的因為我們未使用u-boot的SPL方式單獨構建SPL程式所以修改了這部分內容)。

DRAM中

完成上面的操作後進行絕對跳轉就能到DRAM地址空間繼續執行程式碼了,因為程式碼本身就是連線在這個地址的。跳轉後執行_main 符號處的指令他實現在rch\arm\lib\crt0.S 中操作序列大致如下:
1. 重新修改棧基址到DRAM地址空間中
2. 調整棧指標 (實際則是在棧中分配記憶體)這裡是為malloc和struct global_data 的GD變數預留記憶體
3. 初始化 GD變數的部分成員,這是整個u-boot的一個重要的全域性資料用來記錄各種資料狀態等。

執行了以上的處理步驟後將又可以使用C程式碼呼叫了。然後呼叫board_init_f 這是在common\board_f.c 中實現的屬於u-boot的內容的程式碼,主要就是圍繞GD變數進行操作詳細的操作這裡不深入分析,就是進行一些配置初始化相關的主要就是呼叫initcall_run_list 這個函式依次執行一個初始化list進行初始化,這個初始化函式陣列init_sequence_f也是在common\board_f.c 中定義並根據u-boot的使用kbuild配置產生的配置檔案編譯生成。其中大部分初始化函式都是平臺無關的少量與平臺相關需要注意對應修改下,除此之外需要注意的是在這裡執行board_init_f 相關呼叫時有開啟cache。這裡執行完成後GD變數的成員也越來越完善,再次根據GD中記錄的資訊修改SP棧指標。然後還需要再次移動並重定位程式碼,這一次移動是為了為了將u-boot放到DRAM高地址為拷貝Linux核心image準備空間,拷貝完成後無效指令和資料cache(因為原來地址空間的快取已經無效了)。這裡還使用了一個技巧使拷貝完成後直接返回到新地址的下一條指令執行。使用的方式就是在LR暫存器上做文章,具體參考程式碼實現。然後在次修改異常向量表地址到新的地址空間。注意的是這裡程式碼重定向還是比較複雜一點的不是簡單的拷貝,因為前面的GD變數也是需要維護的,主要就是然後對新地址的資料段的拷貝。然後就是呼叫common\board_r.c 檔案中的board_init_r 函式,這個函式的實現和board_init_f 如出一轍他們末尾的字母就是標記他們呼叫的配置是在重定向前(f)和之後(r)執行的初始化。最後執行的就是 run_main_loop這裡就是u-boot程式的主迴圈,在這裡進行u-boot命令等相關功能的操作。

總結
u-boot的整個執行過程的粗流程其實是不復雜的,除錯過程最複雜的感覺就是DRAM的配置那些部分。整個移植實際上出去kbuild配置工具外就是,一部分關於底層的初始化配置相關內容,其餘就是u-boot的大致執行流程。流程我在做一下梳理總結如下:
_start(arch\arm\lib\vectors.S)向量表,然後跳轉(reset)--->arch\arm\cpu\armv7\start.SARM 架構相關的操作,呼叫(lowlevel_init)---> 平臺相關的底層或前提初始化部分的必要配置,呼叫(_main)--->arch\arm\lib\crt0.S 進行u-boot相關功能的變數,棧等部分的維護初始化等,呼叫(board_init_f)--->common\board_f.c 根據配置呼叫以系列的初始化呼叫,返回crt0.S,重定位u-boot程式碼在DRAM中的位置,呼叫(board_init_f)--->common\board_r.c 繼續進行必要的初始化--->u-boot loop(如果引導Linux則u-boot就執行完了)。如果像知道更詳細的內容可以參考這個博主的部落格:https://to-run-away.blog.csdn.net/article/details/81711413?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-7.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-7.control