嵌入式Linux系統從軟件角度通常可以分為以下4個層次:
引導加載程序 | Linux內核 | 文件系統 | 用戶應用程序
嵌入式Linux系統中典型分區結構:
正常啟動過程中,Bootloader首先運行,然后它將內核復制到內核中,並且在內存某個固定地址設置好要傳遞給內核的參數,最后運行內核。內核啟動之后,它會掛接根文件系統,啟動文件系統中的應用程序。
一、Bootloader的作用:
CPU上電后,會從某個地址開始執行,對於ARM結構的CPU是從0x00000000開始,Bootloader就存放在這個地址的開始處。這樣一上電就可以執行。Bootloader主要用於初始化最基本的硬件、准備好軟件環境和加載Linux操作系統。
在Linux內核啟動時,除了內核映像必須在主存的適當位置外,CPU還必須滿足以下條件:
CPU寄存器的設置
1、R0 = 0
2、R1 = Machine ID
3、R2 = 內核啟動參數在RAM中的起始基地址
U-boot最終是調用theKernel函數來跳轉執行linux內核的,uboot調用此函數(即linux內核)時會直接傳遞給linux內核3個參數,而這3個參數就是通過寄存器來實現傳參的。
其中第1個參數固定為0,就放在r0寄存器中,第二個參數為機器類型ID即機器碼,放在r1寄存器,第3個參數就是啟動參數標記列表在RAM中的首地址,就放在r2寄存器中。
CPU模式
1、必須禁止中斷(IRQs 和 FIQs,中斷請求和快速中斷請求)
2、CPU必須是SVC模式(特權模式)
為何要禁止中斷?---因為U-Boot只是完成硬件初始化、環境參數設置、代碼搬運等工作,用不到中斷,屏蔽中斷是為了避免因為意外中斷使得boot失敗,畢竟很多外設還
沒有初始化,對應中斷代碼也都沒有准備好。
為何特權模式?---從uboot方面考慮,其要做的事情是初始化系統相關硬件資源,需要獲取盡量多的權限,以方便操作硬件,初始化硬件。設置為svc模式,更有利於其工作。
Cache 和MMU 的設置
1、 MMU 必須關閉。
2、 指令Cache 可以打開也可以關閉。
3、數據Cache 必須關閉。
MMU在上電之初沒有任何作用,即U-boot第一階段的匯編代碼與第二階段的源代碼初始化相關外設時訪問的都是實際地址,為了啟動之初不影響對程序啟動常關MMU。
Cache是位於RAM和CPU內部寄存器之間的一個存儲設施,用來加速二者之間的數據傳輸速度,即用來加快CPU從內存中取出指令的速度。但是在上電后CPU的初始化要比內存RAM快一拍,當CPU初始化完成后需要讀取來自內存的數據,若內存還沒有准備好那勢必會造成異常,系統就掛掉了,因此需要關閉數據Cache,而指令Cache關與不關影響不大。
1、源代碼組織
對於ARM而言,主要的目錄如下:
board 平台依賴 存放電路板相關的目錄文件,每一套板子對 應一個目錄。如smdk2410(arm920t)
cpu 平台依賴 存放CPU相關的目錄文件,每一款CPU對應一個目錄,例如:arm920t、 xscale、i386等目錄
lib_arm 平台依賴 存放對ARM體系結構通用的文件,主要用於實現ARM平台通用的函數,如軟件浮點。
common 通用 通用的多功能函數實現,如環境,命令,控制台相關的函數實現。
include 通用 頭文件和開發板配置文件,所有開發板的配置文件都在configs目錄下
lib_generic 通用 通用庫函數的實現
net 通用 存放網絡協議的程序
drivers 通用 通用的設備驅動程序,主要有以太網接口的驅動,nand驅動。
.......
啟動相關的幾個重要文件:
1)cpu/arm920t 目錄下
Start.S文件是一個匯編代碼文件,是Bootloader運行的第一個文件,Bootloader的入口。
U-boot.lds 文件是一個連接器用的連接腳本文件,用來指定映像文件中各段的加載地址和運行地址。
......
2)Makefile簡要分析
所有這些目錄的編譯連接都是由頂層目錄的makefile來確定的。
在執行make之前,先要執行make $(board)_config 對工程進行配置,以確定特定於目標板的各個子目錄和頭文件。
$(board)_config:是makefile 中的一個偽目標,它傳入指定的CPU,ARCH,BOARD,SOC參數去執行mkconfig腳本。
這個腳本的主要功能在於連接目標板平台相關的頭文件夾,生成config.h文件包含板子的配置頭文件。
使得makefile能根據目標板的這些參數去編譯正確的平台相關的子目錄。
以smdk2410板為例,執行 make smdk2410_config,
主要完成三個功能:
1)在include文件夾下建立相應的文件(夾)軟連接,
#如果是ARM體系將執行以下操作:
#ln -s asm-arm asm
#ln -s arch-s3c24x0 asm-arm/arch
#ln -s proc-armv asm-arm/proc
2)生成Makefile包含文件include/config.mk,內容很簡單,定義了四個變量:
ARCH = arm
CPU = arm920t
BOARD = smdk2410
SOC = s3c24x0
3)生成include/config.h頭文件,只有一行:
/* Automatically generated - do not edit */
#include "config/smdk2410.h"
頂層makefile先調用各子目錄的makefile,生成目標文件或者目標文件庫。
然后再連接所有目標文件(庫)生成最終的u-boot.bin。
連接的跟平台相關的主要目標(庫)如下:
cpu/$(CPU)/start.o
board/$(BOARDDIR)/lib$(BOARD).a
cpu/$(CPU)/lib$(CPU).a
cpu/$(CPU)/$(SOC)/lib$(SOC).a
lib_$(ARCH)/lib$(ARCH).a
這里面的四個變量定義在include/config.mk(見上述)。
其余的均與平台無關。
所以考慮移植的時候也主要考慮這幾個目標文件(庫)對應的目錄。
U-boot啟動流程:
從文件層面上看主要流程是在兩個文件中:cpu/arm920t/start.s,lib_arm/board.c,下一節開始分析start.s
思考:
為什么引導代碼不全用C語言寫?
1、在初始化硬件時,需要設置特殊功能寄存器,比如CPU工作模式,C語言不能對這些寄存器操作
2、C程序運行需要提前設置棧空間。