CPU上電後,會在某個地址開始執行,比如MIPS結構的CPU會從0xBFC00000取第一條指令,而ARM結構的CPU則從0x00000000開始,嵌入式開發板中,需要把存儲器件ROM或Flash等映射到這個地址。而Bootloader就存在這個地址的開始處,這樣一上電後就會從這個地址處執行。Boo ...
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關與不關影響不大。