(1)啟動加載模式:這種模式也稱為“自主”模式。也就是Bootloader從目標(biāo)機(jī)上的某個固態(tài)存儲設(shè)備上將操作系統(tǒng)加載到RAM中運(yùn)行,整個過程并沒有用戶的介入,這種模式是在嵌入式產(chǎn)品發(fā)布里的通用模式。
(2)下載模式:在這種模式下,目標(biāo)機(jī)上的Bootloader將通過串口連接或網(wǎng)絡(luò)連接等通信手段從主機(jī)下載文件,例如:下載內(nèi)核映像和根文件系統(tǒng)映像等。從主機(jī)下載的文件 通常首先被Bootloader保存到目標(biāo)機(jī)的RAM中,然后再被Bootloader寫到目標(biāo)上的Flash類的固態(tài)存儲設(shè)備中,Bootloader的這種模式是在在開發(fā)時使用的工作于這種模式的Bootloader通常都 會向它的終端用戶提供一個簡單的命令行接口。
在嵌入式Linux系統(tǒng)中從軟件的角度通??梢苑譃?個層次:
(1)引導(dǎo)加載程序,包括固化在固件中的boot代碼(可選)和Bootloader兩大部分。
有些CPU在運(yùn)行Bootloader之前運(yùn)行一段固化的程序 ,比如x86結(jié)構(gòu)的CPU就是先運(yùn)行BIOS中的固件,然后才運(yùn)行硬盤的第一個分區(qū)中的BootLoader。在大多數(shù)的嵌入式系統(tǒng)中并沒有固件,Bootloader是上電后第一個執(zhí)行的程序。
(2)Linux內(nèi)核
嵌入式定制的內(nèi)核以及啟動參數(shù),啟動參數(shù)可以是Bootloader傳遞給內(nèi)核的,也可以是內(nèi)核默認(rèn)的。
(3)文件系統(tǒng)
包括根文件系統(tǒng)和建立于Flash內(nèi)存設(shè)備之上的文件系統(tǒng)。里面包括了Linux系統(tǒng)能夠運(yùn)行所必要的應(yīng)用程序和庫文件等。比如可以給用戶提供操作Linux的控制shell程序。
(4)用戶應(yīng)用程序
特定于用戶的應(yīng)用程序,它們也存儲在文件系統(tǒng)中,有時在用戶應(yīng)用程序和內(nèi)核層之間可以還會包括一個嵌入式圖形用戶界面。
2. Bootloader啟動的兩個階段
從固態(tài)存儲設(shè)備上啟動的Bootloader大多都是兩階段的啟動過程,第一階段使用匯編來實(shí)現(xiàn)。它完成一些依賴于CPU體系結(jié)構(gòu)的初始化,并調(diào)用第二階段的代碼,第二階段則通常使用C語言來實(shí)現(xiàn),這樣可以實(shí)現(xiàn)更復(fù)雜的功能,而且代碼會有更好的可讀性和可移植性。
(1) Bootloader第一階段的功能
1)硬件設(shè)備初始化
2)為加載Bootloader的第二階段準(zhǔn)備RAM空間。
3)復(fù)制Bootloader的第二階段代碼到RAM空間中。
4)設(shè)置好棧
5)跳轉(zhuǎn)到第二階段代碼的C入口點(diǎn)。
在第一階段進(jìn)行的硬件初始化一般包括:關(guān)閉WATCHDOG,關(guān)中斷,設(shè)置 CPU的速度和時鐘頻率RAM初始化等。這些不都是必需的。
(2)Bootloader第二階段的功能
1)初始化本階段要使用的硬件設(shè)備
2)檢測系統(tǒng)內(nèi)存映射
3)將內(nèi)核映像和根文件系映象從Flash望到RAM空間中
4)為內(nèi)核設(shè)置啟動參數(shù)
5)調(diào)用內(nèi)核
將內(nèi)核存放在適當(dāng)?shù)奈恢煤?,直接跳到它的入口點(diǎn)即可調(diào)用內(nèi)核,調(diào)用內(nèi)核之前,下列條件要滿足
(1)CPU寄存器的設(shè)置
R0=0.
R1=機(jī)器類型ID;對于ARM結(jié)構(gòu)的CPU,其機(jī)器類型ID在linux/arch/arm/tools/mach-types
R2=啟動參數(shù)標(biāo)記列表在RAM中起始基地址
(2)CPU工作模式
必須禁止中斷(IRQs和FIQs)
CPU必須為SVC模式
(3)Cach和MMU的設(shè)置
MMU必須關(guān)閉
指令Cach可以打開也可以關(guān)閉
數(shù)據(jù)Cach必須關(guān)閉
U-boot.bin:二進(jìn)制可執(zhí)行文件,它就是可以直接燒入ROM,NORFlash的文件
u-Boot:ELF格式的可執(zhí)行文件,
U-Boot.srec:Motorla S-Record格式的可執(zhí)行文件
對于S3C2410的開發(fā)板,執(zhí)行”make smdk2410_config“."make all"后生成的U-Boot.bin可以燒入NOR Flash中運(yùn)行,啟動后可以看到串口輸出一些信息后進(jìn)行控制界面。
|
這是在根目錄下的MAKEFILE文件中的兩個語句,其中的MKCONFIG就是根目錄下的mkconfi文件。$(@:_config=)的結(jié)果就是將”smdk2410_config“中的_config去掉,結(jié)果為“smdk2410”.所以“make smdk2410_config”實(shí)際上就是執(zhí)行如下命令:
|
|
(1)確定開發(fā)板名稱BOARD_NAME,相關(guān)代碼如下:
|
|
(2)創(chuàng)建到平臺開發(fā)板相關(guān)折頭文件的鏈接
|
|
|
|
|
APPEND維持原值"NO",所以config.h被重新建立,也就是執(zhí)行echo "#include <configs/$1.h>" >>config.h
#include <configs/smdk2410.h>
總之,當(dāng)你執(zhí)行make smdk2410_config ,實(shí)際的作用就是執(zhí)行./mkconfig smdk2410 arm arm920t smdk2410 NULL s3c24x0,它將產(chǎn)生如下的
幾種作用
(1) 開發(fā)板的名稱 BOARD_NAME等于 $1
(2)創(chuàng)建到平臺,開發(fā)板相關(guān)的頭文件的鏈接,如下所示
ln -s asm-$2 asm
ln -s arch-$6 asm-S2/arch
ln - s proc-armv asmn-$2/proc 如果$2不是arm的話,此行沒有
(3)創(chuàng)建頂層Makefile包含的incldue /config.mk,如下所示
ARCH = $2
CPU = $3
BOARD = $4
VENDOR = $ $5 為空,或者NULL的話,些行沒有
SOC = $6
(4) 創(chuàng)建開發(fā)板相關(guān)的頭文件include/config.h,如下 所示
#include <config.h/$1.h>
從上面執(zhí)行完命令后的結(jié)果,可以看出來,如果要在board目錄下新建一個開發(fā)板<board_name>的目錄,則在 include/configs 目錄下也要建立一個文件<board_name>.h,里面存放的就是開發(fā)板<board_name>的配置信息。
3.U-Boot的編譯,連接過程
|
第一行中包含的config.mk文件,就是在第一開始配置過程中制作出來的include/conifg.mk文件,我們在一開始配置U-boot時執(zhí)行過mkconfig。mini2440 時生成的文件,其中定義了ARCH,CPU,BOARD,SOC等。4個變量的值為arm,arm920t,smdk2410,s3c24x0.我們在執(zhí)行mkconfig。mini2440時,其實(shí)執(zhí)行的是如下的命令:
|
|
繼續(xù)分析MAKEFIle文件:
|
|
|
|
總結(jié)一下U-Boot的編譯流程:
(1)首先編譯cpu/$(CPU)/start.s,對于不同的CPU,還可能編譯cpu/$(CPU)下面的其他文件。
(2)然后,對于平臺開發(fā)板相關(guān)的每個目錄,每個通用目錄都使用它們各自的MAKEFILE生成相應(yīng)和庫。
(3)將1,2步驟生成的.o.a文件按照$(BOARDDIR)/config.mk 文件中指定的代碼段起始地址。$(obj)u-boot.lds 連接腳本進(jìn)行連接。
(4)第3步得到的是ELF格式的U-Boot,后面MAKEFILE還會將它轉(zhuǎn)換為二進(jìn)制格式 S-Record格式。
(1)硬件設(shè)備初始化
依次完成如下設(shè)置:將CPU的工作模式設(shè)為管理模式(SVC),關(guān)閉WATCHDOG,設(shè)置FCLK,HCLK,PCLK的比例,關(guān)閉MMU,CACHE。代碼在cpu/arm920t/start.S中,
(2)為加載Bootloader的第二階段代碼準(zhǔn)備RAM空間。
所謂準(zhǔn)備RAM空間,就是初始化內(nèi)存芯片,使它可用,對于S3C24x0,通過在Start.S中調(diào)用lowlevel_init函數(shù)來設(shè)置存儲控制器,使得外接
|
這里將整個U-Boot代碼都復(fù)制到SDRAM中,這在cpu/arm920t/start.s中實(shí)現(xiàn)
|
(4)設(shè)置好棧
|
在跳轉(zhuǎn)之前,還要清除BSS段(初始值0,無初始值的全局變量,靜態(tài)變量放在BSS段。
|
|
第二階段從lib_arm/borad.c中的start_armboot函數(shù)開始,程序的流程如下 :
board_init函數(shù)設(shè)置MPLL,改變系統(tǒng)時鐘,它是開發(fā)板相關(guān)函數(shù)。board\samsung\smdk2410/smdk2410.c中實(shí)現(xiàn)。串口的初始化函數(shù)主要是serial_init,它設(shè)置UART控制器,是CPU相關(guān)的函數(shù),在cpu/arm920t/s3c24x0/serial.c中實(shí)現(xiàn)。
|
(3) 為內(nèi)核設(shè)置啟動參數(shù)
在start_armboot()函數(shù)的最后,調(diào)用main_loop()函數(shù),進(jìn)行一個無限循環(huán),該函數(shù)在common/main.c文件中定義。
|
|
|
|
|
|
image是bootm_headers結(jié)構(gòu)體的指針,可以在inlcude/image.h文件中看到這個結(jié)構(gòu)體的定義如下:
|
|
要知道哪個地址是啟動內(nèi)核,哪個地址啟動文件系統(tǒng),要分析common/cmd_bootm.c中的函數(shù) do_bootm,因?yàn)橐龑?dǎo)kernel就是bootm這條命令的工作,do_bootm是命令bootm的執(zhí)行函數(shù)現(xiàn)在我們來分析一下common/cmd_bootm.c中的函數(shù)do_bootm,這是bootm命令的處理函數(shù).do_bootm()函數(shù)中的很多功能都是分成了函數(shù)的形式,而在以前的版本中沒有這么有結(jié)構(gòu)層次,這里我們也只是分析對引導(dǎo)Linux內(nèi)核有作用的部分,因?yàn)檫@是一個在common文件夾下的文件,也就意味著,在引導(dǎo)別的操作系統(tǒng)時也會用到這個函數(shù),而不單單是Linux操作系統(tǒng).
|
|
uboot源代碼的tools/目錄下有mkimage工具,這個工具可以用來制作不壓縮或者壓縮的多種可啟動映象文件。
mkimage在制作映象文件的時候,是在原來的可執(zhí)行映象文件的前面加上一個0x40字節(jié)的頭,記錄參數(shù)所指定的信息,這樣uboot才能識別這個映象是針對哪個CPU體系結(jié)構(gòu)的,哪個OS的,哪種類型,加載內(nèi)存中的哪個位置, 入口點(diǎn)在內(nèi)存的那個位置以及映象名是什么?到這里整個U-Boot是如何啟動Linux內(nèi)核的,基本上也就清楚了,特別是如何向Linux內(nèi)核傳送的參數(shù)。
|