前言 以前接觸到的u boot啟動方式只有Flash和Nand Flash這兩種方式,最近接觸是SD卡啟動方式,SoC是S5P4418,啟動方式也第一次接觸到,根據S5P4418用戶手冊可以找到系統使用SD卡啟動時,片內iROM內固化的代碼會自動映射到0x00地址,也被稱為第一階段引導程式,該程式會 ...
前言
以前接觸到的u-boot啟動方式只有Flash和Nand Flash這兩種方式,最近接觸是SD卡啟動方式,SoC是S5P4418,啟動方式也第一次接觸到,根據S5P4418用戶手冊可以找到系統使用SD卡啟動時,片內iROM內固化的代碼會自動映射到0x00地址,也被稱為第一階段引導程式,該程式會檢測啟動引腳配置,然後從SD卡讀取block1~block32內的程式到內置SRAM中執行,讀取出來並執行的這段程式被稱為第二階段引導程式,由於其最大為16KB,無法存放完整的u-boot。以上兩個引導程式均由晶元廠商提供,沒有源碼,不開源。第二階段引導程式從SD卡中載入u-boot到DRAM中執行,至此u-boot才開始登場。
SDHCBOOT --> 2nboot --> u-boot
代碼分析
程式入口
由於S5P4418採用的指令集為ARMv7,通常應該去該文件arch/arm/cpu/armv7下尋找該SoC,然而並沒有,通過查看board.cfg可以看到它的CPU類型被定義為了slsiap,通過查看arch/arm/cpu/slsiap也確實存在s5p4418文件夾。
# Status, Arch, CPU:SPLCPU, SoC, Vendor
Active arm slsiap s5p4418 s5p4418
接下來就是找到第一個執行的程式在哪裡文件里,即找到程式入口,查看slsiap下的鏈接文件
.text :
{
*(.__image_copy_start)
SDIR/start.o (.text*)
*(.text*)
}
而SDIR是什麼並不知道,打開同文件夾下的config.mk
SDIR=arch/arm/cpu$(if $(CPU),/$(CPU),)$(if $(SOC),/$(SOC),)
替換變數之後
SDIR=arch/arm/cpu/slsiap/s5p4418
即第一個文件是arch/arm/cpu/slsiap/s5p4418/start.S
start.S
不同 CPU 的改文件大致功能都大同小異,設置中斷向量表,將 CPU 設置為 SVC 模式,然後設置 CP15,設置 Cache,初始化記憶體,然後對代碼進行重定位,之後開始清理 bss 段,為準備 C 語言運行環境,然後設置棧指針,執行 board_init_f 函數填充 global_data 結構體數據,最後執行 board_init_r,離開start.S不再返回。
board_init_r
該函數中調用的兩個有意思的函數:serial_initialize、mmc_initialize。
void serial_initialize(void)
{
mpc8xx_serial_initialize();
...省略若幹類似函數...
s5p_serial_initialize();
...省略若幹類似函數...
arc_serial_initialize();
}
可以看到,該函數中不加判斷的把各種種類的串口初始化都給寫上,也沒有使用巨集去自動選擇使用哪個,給人的第一感覺就是這不是浪費存儲空間麽,繼續查看該函數所在文件其他代碼可以發現
/**
* serial_initfunc() - Forward declare of driver registration routine
* @name: Name of the real driver registration routine.
*
* This macro expands onto forward declaration of a driver registration
* routine, which is then used below in serial_initialize() function.
* The declaration is made weak and aliases to serial_null() so in case
* the driver is not compiled in, the function is still declared and can
* be used, but aliases to serial_null() and thus is optimized away.
*/
#define serial_initfunc(name) \
void name(void) \
__attribute__((weak, alias("serial_null")));
serial_initfunc(mpc8xx_serial_initialize);
...省略若幹類似函數...
serial_initfunc(s5p_serial_initialize);
...省略若幹類似函數...
serial_initfunc(arc_serial_initialize);
英文註釋說的很清楚了,這是在利用編譯器特性進行自動優化的,使用 __weak 屬性先聲明驅動程式,因為編譯程式之前會先配置程式,在編譯的時候在配置階段產生的巨集在預編譯階段會決定哪些驅動程式被編譯進來,比如 S5P4418 被選擇時,相關的串口驅動 s5p_serial_initialize 就會編譯進來,由於其聲明為 __weak 屬性,編譯時會自動覆蓋掉前項聲明的函數,這樣函數 serial_initialize 就不會出現把不必要的程式編譯進來的情況了。
mmc_initialize 函數的分析見下回分解吧~