u boot 啟動流程(mips)

2021-05-12 19:32:54 字數 2744 閱讀 6369

u-boot的啟動過程比較簡單,大致做下面的工作:

1 cpu初始化

2 時鐘,串列埠,記憶體(ddr ram)初始化

3 記憶體劃分,分配棧,資料,配置引數,以及u-boot**在記憶體中的位置。

4 對u-boot**做relocate

5 初始化 malloc,flash,pci 以及外設(比如,網口)

6 進入命令列或者直接啟動linux kernel

基本上,這就是u-boot的啟動要做的事情,我也曾經大致看過arm的啟動**,也是類似。

不過,這裡以mips作為例子進行介紹。

啟動涉及到幾個檔案: start.s, cache.s, lowlevel_init.s 和 board.c 

前三個都是彙編**。

程式從start.s的_start開始執行。首先,初始化中斷向量,暫存器清零,大致包括32

個通用暫存器reg0-reg31和協處理器的一些暫存器:cp0_watchlo,     cp0_watchhi

,     cp0_cause, cp0_count, cp0_compare等等。

之後,配置暫存器cp0_status,設定所使用的協處理器,中斷以及cpu

執行級別(核心級)。

配置gp暫存器,把got段的位址賦給gp暫存器。(gp暫存器的用處會在後面relocate code

的部分詳細解釋)

這時,開始執行lowlevel_init.s的lowlevel_init,主要目的是工作頻率配置,比如cpu

的主頻,匯流排(ahb),ddr工作頻率等。

然後,呼叫cache.s的mips_cache_reset對cache進行初始化。接著呼叫cache.s的

mips_cache_lock。這個呼叫的目的,起初讓我不解,後來才知道。這時ddr ram

並沒有配置好,而如果直接呼叫c語言的函式必須完成棧的設定,而棧必定要在ram

中。所以,只有先把一部分cache拿來當ram用。做法就是把一部分cache

配置為棧的位址,鎖定。這樣,當讀寫棧的記憶體空間時,只會訪問cache

,而不會訪問真的ram位址了。

這時,配置棧的位址,進行呼叫函式board_init_f(board.c)

進入函式board_init_f後,首先做一系列初始化:

timer_init    時鐘初始化

env_init    環境變數初始化(取得環境變數存放的位址)

init_baudrate    串列埠速率

serial_init        串列埠初始化

console_init_f    配置控制台

display_banner    顯示u-boot啟動資訊,版本號等

checkboard        執行board相關的操作。

init_func_ram    初始化記憶體,配置ddr controller

這一系列工作完成後,串列埠和記憶體都已經可以用了。然後,就要把記憶體進行劃分,

在記憶體的最後一部分,留出u-boot**大小的空間,準備把u-boot**從flash搬移到這裡

然後,是堆的空間,malloc的記憶體就來自於這裡。緊接著放兩個全域性資料結構bd_info

global_data和環境變數boot_params。最後,是棧的空間。

記憶體劃分好,就準備進行relocate code了。關於relocate code

和其他啟動過程下次再介紹。

上次講到,記憶體劃分好,準備進行relocate code。

relocate code的意思是這樣的。通常u-boot的執行**肯定是在flash上(當除錯的時候也可以放在ram上)。當啟動起來以後,要把它從flash上搬移到ram裡執行。這個工作就叫做relocate code。

但是,問題在於,flash上的位址和ram上的位址是不同的。當我們把**從flash上搬移到ram上以後,當執行函式跳轉時,**裡的函式位址還是flash上的位址,所以一跳就跳回去了。

這怎麼辦呢?

在u-boot裡面用的是pic(position-independent code)的方式解決這個問題。

簡單介紹一下其原理。當你用pic方式時,在用gcc編譯時需加上 -fpic的選項。編譯器會為你的可執行**建立乙個got(global offset table)的段。乙個位址在got表中有一項,裡面存放位址的資訊,而在使用這個位址時,只要根據這個位址的編號(也可以叫做偏移量offset)找到表中相應的專案,就可以取得那個位址了。

而如果位置發生變化,只要對got表中的位址進行修改就可以了。

我們可以通過反彙編,看乙個簡單的函式呼叫例子:

lw    t9,1088(gp)

jalr    t9

這裡,gp存放的就是got表的起始位址,而1088就是要呼叫函式的offset,也就是說got表的那個位置存放著它的位址。lw    t9,1088(gp) 把函式位址放入t9, 然後呼叫就可以了。

知道了pic的原理,解釋u-boot relocate code的方法就簡單了。

簡單的說就把u-boot的執行**直接從flash裡copy到ram的相應區域。

然後,把got表中的位址都加上乙個偏移量,這個偏移量就是flash裡的位址與ram裡的位址的差。

還有其他一些工作比如:設定新的棧指標,從flash**裡跳轉到ram**裡 等等。

之後,就進入board.c的board_init_r函式,在這個函式裡初始化 malloc,flash,pci 以及外設(比如,網口),最後進入命令列或者直接啟動linux kernel。

這樣,u-boot的啟動工作就完成了。

基於mips架構的uboot啟動流程(3)

要注意mips具有流水線可見性,所以跟在跳轉指令後的下一條指令,在執行跳轉到的地方前,都會執行,這個叫分支延遲。但是編譯器會隱藏該特性,但可以通過設定 set noreorder 來禁止編譯器重新組織 順序。每個板子都有自己的lds檔案。這個主要是用來說明編譯生成的指令,及執行過程中用到的資料放置的...

基於mips架構的uboot啟動流程(4)

特點和功能 u boot yamon 支援的 cpu 和 board 1.支援種類繁多,包括 arm 的各個系列 ppc,mips 2.mips 的支援較差,目前支援 au1100,au1500 系列,4kec 的需要自己移植 工作量主要在起始 的初始化部分,包括 cahe 和中斷的初始化 1.基本...

基於mips架構的uboot啟動流程(4)

特點和功能 u boot yamon 支援的 cpu 和 board 1.支援種類繁多,包括 arm 的各個系列 ppc,mips 2.mips 的支援較差,目前支援 au1100,au1500 系列,4kec 的需要自己移植 工作量主要在起始 的初始化部分,包括 cahe 和中斷的初始化 1.基本...