1. 程式人生 > >Exynos4412 Uboot 移植(二)—— Uboot 啟動流程分析

Exynos4412 Uboot 移植(二)—— Uboot 啟動流程分析

    uboot啟動流程分析如下:

第一階段:

a -- 設定cpu工作模式為SVC模式
b -- 關閉中斷,mmu,cache
v -- 關看門狗
d -- 初始化記憶體,串列埠
e -- 設定棧
f -- 程式碼自搬移
g -- 清bss
h -- 跳c

第二階段

a -- 初始化外設,進入超迴圈
b -- 超迴圈處理使用者命令

可見, U-Boot 屬於兩階段的Bootloader

第一階段的檔案:

arch/arm/cpu/armv7 /start.S                       平臺相關,CPU工作模式設為SVC模式,關MMU,關icahce(CPU相關)

board/samsung/fs4412/lowlevel_init.S

     開發板相關:關看門狗,記憶體初始化,時鐘初始化,串列埠初始化(board相關,初始化最基本裝置)

第二階段的檔案:

arch/arm/lib/crt0.S                     _main 函式所在處,初始化SP,為C語言準備,程式碼重定位,清BSS,設定R0 R1 R2 R8相應暫存器

arch/arm/lib/board.c                   board_init_f 函式 ,填充GD結構體,初始化外設, main_loop()函式超迴圈

arch/arm/cpu/armv7 /start.S 程式碼自搬移時會用到

針對uboot2013啟動流程圖如下:


下面是具體分析:

一、U-Boot 第一階段程式碼分析

通常我們通過連線檔案知曉程式入口點,入口檢視 u-boot.lds


通過連結指令碼可知入口為_start,位於arch/arm/cpu/armv7/start.o

第一階段開始:

1、進入arch/arm/cpu/armv7/start.S

a -- 異常向量表設定


b -- 設定CPU處於SVC工作模式


d -- 協處理器 p15 的 c12 暫存器來重新定位


e、Bl  cpu_init_cp15(使分支預測無效,資料)


關閉資料預取功能;

DSB:多核CPU對資料處理指令

ISB:流水線清空指令;


關閉MMU,使能I-cache

NOTE:

分支預測:在流水線裡,會將後面的程式碼優先載入到處理器中,由於是迴圈,會使後面載入的程式碼無效,故出現了分支預測技術。(統計跳的次數來選擇裝載迴圈的程式碼還是下面的程式碼)。


f、Bl  cpu_init_crit


2、跳到Low_level_init,位於board/samsung/fs4412/lowlevel_init.S

a、關閉看門狗

b、比較當前pc指標域TEXT_BASE的高8位是否一樣來判斷,當前程式碼是否在記憶體中


c、對系統時鐘初始化


d、對記憶體初始化


e、對串列埠初始化


結束後返回 start.S

第一階段結束,總結如下:

1 前面總結過的部分,初始化異常向量表,設定svc模式

2 配置cp15,初始化mmu cache tlb

3 板級初始化,clk,memory,uart初始化

二、第二階段開始:


按"CTRL + ] ", 發現 _main 在兩處有定義:


這裡我們選擇第一個Bl  _main ,跳轉到arch/arm/lib/crt0.S

1、初始c執行環境(看註釋就知道,初始化C執行環境,並呼叫board_init_f 函式


功能:

初始化sp ,為支援C語言做準備;

儲存128B 放GD結構體,存放全域性資訊,GD的地址存放在r8中;

跳轉到 board_init_f 函式,其在arch/arm/lib/board.c 處定義;

2、跳轉到arch/arm/lib/board.c


功能:

對全域性資訊GD結構體進行填充:

291行:mon_len 通過連結指令碼可以知道存放的是uboot程式碼大小;

294行:fdt_blob 存放裝置數地址;

303行:迴圈執行init_fnc_t陣列的函式,作硬體初始化;

a -- init_fnc_t陣列的函式定義

     初始化硬體

b -- Dram_init初始化成功之後,剩餘程式碼將會對sdram空間進行規劃。


可以看到addr的值由CONFIG_SYS_SDRAM_BASE加上ram_size。也就是到了可用sdram的頂端。

e--繼續對gd結構體填充


如果icahe 與 dcache 是開啟的,就留出 64K 的空間作為 tlb 空間,最後 addr 就是tlb 地址,4K對齊。

f --填充完成將資訊拷貝到記憶體指定位置


2 -- 繼續回到 _main

"CTRL + O"回到跳轉前的函式,即 arch/arm/lib/crt0.S


功能:

將 r8 指向新的 gd 地址;

程式碼重定位;

 對lr 的操作為了讓返回時,返回的是重定位的here處

3 -- 程式碼自搬移

程式碼自搬移,防止與核心衝突,程式碼位於arch/arm/cpu/armv7/start.S

迴圈將程式碼搬移到指定高地址

這裡只是將連結指令碼中_image_copy_end_start中的程式碼,其它段還沒有操作。

在這裡我們有疑惑就是將程式碼重定位到高地址,那執行的地址不就和連結地址不一樣了,那執行可能不正常?這個疑惑就是.rel.dyn幫我們解決了,主要還是編譯器幫我們做的工作,在連結中有如下:【參考:

4 -- 重定位到高地址之後,再次回到 _main(arch/arm/lib/crt0.S)

      此時回到的是剛才的重定位的 here 處


關 icache,保證資料從SDRAM中更新,更新異常向量表,因為程式碼被重定位了;

清BBS;


呼叫board_init_r主要是對外設的初始化。

R0=gd

R1=RELOCADDR

5 -- Main_loop 函式進入超迴圈(arch/arm/lib/board.c)


Main_loop函式主要功能是處理環境變數,解析命令

install_auto_complete();  //安裝自動補全的函式,分析如下

getenv(bootcmd)

bootdelay(自啟動)

如果延時大於等於零,並且沒有在延時過程中接收到按鍵,則引導核心。