1. 程式人生 > >Bootloader的結構和啟動過程

Bootloader的結構和啟動過程

CPU上電後,會在某個地址開始執行,比如MIPS結構的CPU會從0xBFC00000取第一條指令,而ARM結構的CPU則從0x00000000開始,嵌入式開發板中,需要把儲存器件ROM或Flash等對映到這個地址。而Bootloader就存在這個地址的開始處,這樣一上電後就會從這個地址處執行。Bootloader執行後從板子上的某個固態儲存裝置上將作業系統OS載入到RAM中執行。(一些功能強大的Bootloader,比如U-boot在正常啟動載入後可以延時若干秒(也可以自己設定),等待終端使用者按下任意鍵後便可進入到下載模式;如果在指定的時間內沒有按鍵,U-boot則會啟動Linux核心,核心的啟動引數可以是預設的或是由U-boot傳遞給它的)。

注意:有的CPU在執行Bootloader之前 先執行一段韌體(firmware)中固化的boot程式碼,比如x86結構的CPU就是先執行BIOS中的 韌體然後才開始執行硬碟第一個分割槽中的Bootloader。在大多數的嵌入式系統中並沒有韌體,Bootloader是上電後執行的第一個程式碼。

下面便更細緻得說明Bootloader的啟動過程:
從固態儲存器上啟動的Bootloader大多數是分二個階段來啟動的。
第一個階段使用匯編程式碼來實現,它主要完成一些依賴於CPU體系結構的初始化,比如關看門狗、關中斷、初始化RAM、將第二階段呼叫的C語言程式碼複製到RAM(非必須,例如對於NOR Flash等裝置可以直接在上面執行,只不過比在RAM上執行效率低),設定CPU的速度和時鐘頻率(非必需,也可以放在第二階段),設定好棧,跳轉到第二階段的C語言入口處等;
第二個階段

通常用C語言來實現,它主要用來:初始化本階段要用到的硬體裝置、檢測系統記憶體對映(就是確定板上使用了多少記憶體,他們的地址空間是什麼)、將核心映像和根檔案系統映像從Flash上覆制到記憶體RAM並且在記憶體中的某個固定位置為核心設定啟動引數Boot parameters(Flash上的核心映像有可能是經過壓縮的,那麼讀到RAM後還要進行解壓。對於有自解壓功能的核心不需要Bootloader來解壓。另外將根檔案系統映像複製到RAM是非必須的,這取決於是什麼型別的根檔案系統,以及核心訪問它的方法)、呼叫核心(核心啟動後會掛載根檔案系統,所以典型的嵌入式Linux系統的分割槽結構為Botloader + Boot parameters + Kernel + Root filesystem)。

從上面的分析可知將核心放到適當的位置後,直接跳到其入口點便可以啟動核心,在核心之前需要滿足那些條件呢,下面我們來具體分析:

CPU暫存器R0、R1和R2值的設定
由於U-boot在設定完啟動引數標記列表後最終是呼叫theKernel函式來跳轉執行linux核心的,uboot呼叫這個函式(其實就是linux核心)時會直接傳遞給linux核心3個引數,而這3個引數就是通過暫存器來實現傳參的。其中第1個引數固定為0,就放在r0暫存器中,第二個引數為機器型別ID也就是我們常說的機器碼,就放在r1暫存器中,第3個引數就是啟動引數標記列表在RAM中的首地址,就放在r2暫存器中。

CPU工作模式的設定
必須禁止中斷(IRQs和FIQs),並且要將CPU設定為SVC模式。
這是因為uboot只是完成硬體初始化,環境引數設定,程式碼搬運等工作,用不到中斷。遮蔽中斷是為了避免因為意外中斷使得boot失敗,畢竟很多外設還沒有初始化,對應中斷程式碼也都沒有準備好。
那麼為什麼要將CPU設定為SVC模式呢?我們先簡單的來分析一下CPU的7種模式:
中止abt和未定義und模式:
首先可以排除的是,中止abt和未定義und模式,那都是不太正常的模式,此處程式是正常執行的,所以不應該設定CPU為其中任何一種模式,所以可以排除。

快中斷fiq和中斷irq模式:
其次,對於快中斷fiq和中斷irq來說,此處uboot初始化的時候,也還沒啥中斷要處理和能夠處理,而且即使是註冊了終端服務程式後,能夠處理中斷,那麼這兩種模式,也是自動切換過去的,所以,此處也不應該設定為其中任何一種模式。

使用者usr模式:
雖然從理論上來說,可以設定CPU為使用者usr模式,但是由於此模式無法直接訪問很多的硬體資源,而uboot初始化,就必須要去訪問這類資源,所以此處可以排除,不能設定為使用者usr模式。

系統sys模式 vs 管理svc模式:
首先,sys模式和usr模式相比,所用的暫存器組,都是一樣的,但是增加了一些訪問一些在usr模式下不能訪問的資源。

而svc模式本身就屬於特權模式,本身就可以訪問那些受控資源,而且,比sys模式還多了些自己模式下的影子暫存器,所以,相對sys模式來說,可以訪問資源的能力相同,但是擁有更多的硬體資源。

所以,從理論上來說,雖然可以設定為sys和svc模式的任一種,但是從uboot方面考慮,其要做的事情是初始化系統相關硬體資源,需要獲取儘量多的許可權,以方便操作硬體,初始化硬體。

從uboot的目的是初始化硬體的角度來說,設定為svc模式,更有利於其工作。

因此,此處將CPU設定為SVC模式。

另外uboot作為一個bootloader來說,最終目的是為了啟動Linux的kernel,在做好準備工作(即初始化硬體,準備好kernel和rootfs等)跳轉到kernel之前,本身就要滿足一些條件,其中一個條件,就是要求CPU處於SVC模式的。

所以,uboot在最初的初始化階段,就將CPU設定為SVC模式,也是最合適的。

Cache和MMU的設定
MMU和資料Cache必須必須關閉,指令Cache可以開啟也可以關閉。
由於MMU在上電之初是沒有任何作用的,也就是說U-boo第一階段的彙編程式碼以及第二階段的原始碼初始化相關外設時訪問的都是都是實際地址,MMU起不到任何作用,為了啟動之初不影響對程式的啟動常關閉MMU。
Cache是位於RAM和CPU內部暫存器之間的一個儲存設施,用來加速二者之間的資料傳輸速度,即用來加快CPU從記憶體中取出指令的速度。但是在上電後CPU的初始化要比記憶體RAM快一拍,當CPU初始化完成後需要讀取來自記憶體的資料,若記憶體還沒有準備好那勢必會造成異常,系統就掛掉了,因此需要關閉資料Cache,而指令Cache關與不關影響不大。