arch/arm/lib/crt0_64.S
上一節start.S主要做了以下工作:
reset初始化,save_boot_params(nxp定義)儲存參數到大小256byte的sram中
設定cpu狀态:小端模式,MMU 、 i/d cache 都關閉,段定義;
判斷目前的異常等級并将中斷向量的位址寫到各個EL3/2/1對應的VBAR寄存器中;
打開EA、FIQ、IRQ、NS四種中斷;
打開SIMD和FP浮點運算功能;
配置cntfrq_el0 系統時鐘計數器的頻率。
最後主核跳轉_main,從核等待主核喚醒。
_main函數主要完成的工作:
先設定用于調用board_init_f()函數的初始環境,該環境僅僅是提供了堆棧和存儲位置GD(‘global data’)結構,兩者都是位于可以使用的RAM(SRAM,locked cache…)中,在調用board_init_f()函數前,GD應該被清0;
調用board_init_f()函數,該函數的功能為從system RAM(DRAM,DDR…)中執行準備硬體,當system RAM還不能夠使用的時,必須要使用目前的GD存儲傳遞到後續階段的所有資料,這些資料包括重定位的目标,将來的堆棧和GD的位置;
設定中間環境,其中堆棧和GD是由board_init_f()函數在system RAM中進行配置設定的,但此時的bss和初始化的非常量仍然不能使用;
對于正常的uboot引導(非SPL),調用relocate_code()函數,該函數的功能将uboot從目前的位置重新轉移到由board_init_f()函數計算的目标位置;
對于SPL,board_init_f()函數僅僅是傳回(crt0),沒有代碼的重定位;
設定用于調用board_init_r()函數的最終環境,該環境将有bss段(初始化為0),初始化非常量資料(初始化為預期值),并入棧到system RAM中,GD保留了board_init_f()函數設定的值;
為了使uboot正常運作(非SPL),某些CPU還有一些關于記憶體的工作要做,調用c_runtime_cpu_setup()函數;
調用board_init_r()函數。
首先加載spl的棧位址,将其儲存到x0中,對其進行16byte對齊後指向sp指針。在board_init_f_alloc_reserve之前儲存sp指針棧到x0,board_init_f_alloc_reserve執行後需要還原sp棧指針。
從“top”(這裡指sp指針)位址配置設定保留白間用作“全局變量”,傳回配置設定空間的“top”位址(傳回sp指針位址)
GD 在 16 位元組邊界上向下對齊。早期的 malloc area 未對齊,是以它遵循堆棧我們正在建構的架構的對齊限制。GD 是最後配置設定的,是以這個函數的傳回值是保留區的底部和GD的位址,都應該調用上下文需要它。
board_init_f_alloc_reserve預留了早期malloc的空間和global_data結構體空間,大小為SYS_MALLOC_F_LEN=0x10000(65536bytes),board_init_f_init_reserve現在可以開始初始化gd結構了。board_init_f_init_stack_protection_addr為gd->start_addr_sp設定了spl的棧指針,然後sp向上對齊16位元組。将早期malloc空間的基位址儲存到gd->malloc_base;board_init_f_init_stack_protection隻有在設定完spl的棧指針才可以進行memset。
bootrom是固化在晶片内部的一塊rom,初始化各種接口,并從中讀取内容加載到片内SRAM中。因為儲存設備接口相對簡單,大部分不需要适配即可存取。但是DDR等需要修改代碼進行适配。
是以就需要spl,spl被加載到片内SRAM中,片内SRAM不需要初始化即可運作,但是容量有限。spl運作起來後進行必要的初始化後,初始化DDR,并将uboot從儲存設備中讀到DDR中。
uboot運作在DDR中,則不受空間大小限制,可以進行複雜的操作。支援包括不同檔案系統、腳本執行、多種作業系統加載等等操作。其中主要的工作是從儲存設備中讀取kernel,解析後跳轉到kernel執行。
準備完sp棧和早期malloc空間後,清空x0寄存器,調用board_init_f(定義在board/freescale/imx8mp_evk/spl.c)。這裡i.mx系列對board_init_f函數進行了覆寫,實作了自己的啟動步驟。
uboot重定位,将uboot從cpu内部flash拷貝到ram中。從image_copy_start開始拷貝,截止到image_copy_end。x1寄存器熟先存儲着内部ram中uboot的image_copy_start,而x2寄存器存儲的是image_copy_end。拷貝實作的關鍵指令ldp和stp。拷貝路徑是x1->x11->x0,直至到x2中存儲的image_copy_end末尾。
當x0中的資料長度和x1中的資料長度相等時,停止拷貝重定位完成,調用relocate_done。switch_el宏擷取異常登記并跳轉到對應标簽處。這部分在start.S中已經分析過了。不管是異常EL3、EL2、EL1,都會走到标簽0。關閉i-cache,加入指令屏障。
對于重定位後連結位址與運作位址不一緻的解決辦法就是使用位置無關碼,在uboot編譯使用ld連結的時候使用參數"-pie"可生成與位置無關的可執行程式,使用該參數後,會生成一個.rel.dyn段,uboot則是靠該段去修複重定位後産生的問題的,在uboot的反彙編檔案中,有.rel.dyn段代碼。
重定位各異常等級的vbar,即重定位中斷向量表。
uboot已經在relocate_code中被拷貝到ram中了,還需要重定位spl 的gd棧。這個函數功能就是計算棧的位置,使用memcpy将gd結構拷貝給一個新的gd結構,并傳回新的gd棧指針。
在board_init_r之前必須要清bss段,不清零的話在首次使用全局變量的時候會發生一些錯誤。
/common/board_r.c
init_sequence_r中存儲着一系列的初始化函數,initcall_run_list確定了各系統初始化的順序運作。根據CONFIG_XX來使能相應的驅動。最後run_main_loop進入循環。
其中有一些是需要廠商覆寫的,比如board_init 、board_late_init。
這一篇的重點内容:uboot是如何從sdram拷貝到ram中的?