范蟠果,邢保毫,米曉亮,余書寶, 王 婷
(西北工業(yè)大學(xué) 自動(dòng)化學(xué)院,西安 710129)
?
基于嵌入式S3C2440系統(tǒng)Bootloader設(shè)計(jì)與實(shí)現(xiàn)
范蟠果,邢保毫,米曉亮,余書寶, 王 婷
(西北工業(yè)大學(xué) 自動(dòng)化學(xué)院,西安 710129)
Bootloader是嵌入式系統(tǒng)的一個(gè)重要環(huán)節(jié),對(duì)不同的硬件平臺(tái),其Bootloader都不盡相同,因此設(shè)計(jì)Bootloader是嵌入式系統(tǒng)開發(fā)的難點(diǎn);文中分析S3C2440嵌入式系統(tǒng)的硬件組成和u-boot源碼對(duì)linux內(nèi)核的啟動(dòng)流程,得出u-boot啟動(dòng)內(nèi)核兩個(gè)階段必備階段:第一個(gè)階段是用匯編初始與具體硬件平臺(tái)相關(guān)的操作等,第二階段是用C語(yǔ)言編寫復(fù)雜功能以及啟動(dòng)內(nèi)核;以加載linux-2.6.22.6內(nèi)核為例,根據(jù)u-boot啟動(dòng)內(nèi)核兩個(gè)階段所做的工作,設(shè)計(jì)出適用于S3C2440嵌入式系統(tǒng)的精簡(jiǎn)Bootloader;通過(guò)實(shí)驗(yàn)表明,該設(shè)計(jì)的Bootloader成功啟動(dòng)linux內(nèi)核,具有良好的穩(wěn)定性,可靠性和簡(jiǎn)潔性。
系統(tǒng)設(shè)計(jì);分析u-boot;實(shí)現(xiàn)Bootloader;啟動(dòng)內(nèi)核
Bootloader是嵌入式系統(tǒng)內(nèi)核運(yùn)行前的一段程序,這段程序初始化硬件設(shè)備,并且建立一個(gè)內(nèi)存空間映射圖,從而建立適當(dāng)?shù)南到y(tǒng)軟硬件環(huán)境,為最終啟動(dòng)內(nèi)核和加載文件系統(tǒng)做準(zhǔn)備[1]。由于Bootloader依賴于硬件,它與處理器架構(gòu),具體設(shè)計(jì)的硬件平臺(tái)資源相關(guān),因此設(shè)計(jì)一個(gè)適合某平臺(tái)的Bootloader是開發(fā)嵌入式系統(tǒng)重要工作。
在設(shè)計(jì)引導(dǎo)程序時(shí),一般會(huì)移植u-boot開源代碼,但是這樣代碼量大,占用較大存儲(chǔ)空間;本文通過(guò)分析u-boot啟動(dòng)流程,根據(jù)硬件平臺(tái)資源,設(shè)計(jì)一個(gè)精簡(jiǎn),穩(wěn)定的Bootloader。
1.1 u-boot啟動(dòng)流程分析
分析支持S3C2410的u-boot源代碼,具體啟動(dòng)流程分析如圖1所示。
圖1 u-boot啟動(dòng)流程
根據(jù)分析得出啟動(dòng)過(guò)程一般分為兩個(gè)階段。
stage1:主要通過(guò)匯編來(lái)實(shí)現(xiàn)和硬件相關(guān)代碼:硬件設(shè)備初始化,加載u-boot第二段代碼到RAM空間,設(shè)置好棧,跳轉(zhuǎn)到第二段代碼入口[2]。
stage2:主要用C語(yǔ)言來(lái)實(shí)現(xiàn)一些復(fù)雜的功能:初始化本階段使用的硬件設(shè)備,檢測(cè)系統(tǒng)內(nèi)存映射,將內(nèi)核從flash讀取到RAM中,為內(nèi)核設(shè)置啟動(dòng)參數(shù),調(diào)用內(nèi)核[2]。
1.2 u-boot編譯以及大小分析
u-boot在編譯之前,需要編寫好Makefile和連接文件u-boot.lds,然后通過(guò)make命令編譯生成u-boot.bin;Makefile規(guī)定u-boot所有函數(shù)的依賴關(guān)系;連接文件u-boot.lds指定u-boot編譯的連接地址,第一個(gè)被編譯的文件,存放的代碼段,數(shù)據(jù)段以及BSS段的位置。
如下面一個(gè)連接文件u-boot.lds:
SECTIONS
{
. = 33f00000;//指定的連接地址
.text ://代碼段
{
_start = .;//代碼段開始位置
arch/arm/cpu/arm920t/start.o (.text)//執(zhí)行編譯的第一個(gè)文件start.S
... }
.data ://數(shù)據(jù)段
.....
__bss_start = .;//BSS段開始位置,此處為u-boot.bin大小結(jié)束位置
......}
BSS段是由靜態(tài)和未初始化的全局變量組成,不會(huì)被編譯到u-boot.bin中。
由連接文件可知u-boot的大小為__bss_start 地址減去_start的地址 ,可以查看u-boot的反匯編得到具體的大小,執(zhí)行arm-linux-objdump -D u-boot > u-boot.dis命令來(lái)生成如下u-boot.dis反匯編文件,:{
Disassembly of section .text:
33f00000 <_start>:注釋:代碼開始位置,和連接文件中的連接地址一致
33f00000: ea000013 b 33f00054
......
33f00044 <_bss_start_ofs>:
33f00044: 00069c4c .word 0x00069c4c
.......} 注釋:在啟動(dòng)文件start.S中定義一個(gè)表示u-boot.bin大小的全局變:
.globl _bss_start_ofs
_bss_start_ofs:
.word __bss_start - _start
編譯的u-boot.bin大小為(0x00069c4c)423 KB,占有很大存儲(chǔ)空間。由于u-boot是一個(gè)支持多平臺(tái)的源代碼,所以它的結(jié)構(gòu)復(fù)雜,若要讓這個(gè)u-boot成功應(yīng)用到S3C2440系統(tǒng)上,需要對(duì)u-boot源代碼進(jìn)行裁剪,并且添加支持平臺(tái)外設(shè)的代碼,工作量比較大,而且不便于調(diào)試。所以很有必要設(shè)計(jì)適用S3C2440系統(tǒng)的Bootloader,具體設(shè)計(jì)如下。
2.1 硬件設(shè)計(jì)
圖2 是一個(gè)基于ARM嵌入式系統(tǒng)硬件框圖,S3C2440芯片是基于ARM920T的架構(gòu)處理器,片內(nèi)有4 k的SRAM,nandflash控制器,SDRAM控制器等資源。板級(jí)設(shè)備包含:全功能的串口,JTAG,Nandflash (256 M), SDRAM(64 M)等。
圖2 硬件平臺(tái)框圖
2.2 系統(tǒng)軟件內(nèi)存設(shè)計(jì)
1)根據(jù)S3C2440手冊(cè)可知SDRAM映射在BANK7,所以起始地址為0x30000000;結(jié)束地址為0x34000000。在調(diào)用C語(yǔ)言之前要設(shè)置棧指針,ARM棧是往下增長(zhǎng)的,設(shè)計(jì)棧指針為0x34000000。為給內(nèi)核以及文件系統(tǒng)留有足夠的內(nèi)存,設(shè)置啟動(dòng)代碼連接地址為0x33f80000。內(nèi)核編譯后大小不超過(guò)2 M,設(shè)計(jì)內(nèi)核連接地址為0x30008000。內(nèi)核把啟動(dòng)參數(shù)放在0x30000100位置處,具體內(nèi)存分布如圖3。
2)Nandflash是存儲(chǔ)程序和數(shù)據(jù),掉電后能保存數(shù)據(jù)和程序,所以啟動(dòng)代碼和內(nèi)核都存放nandflash中,為了保證兩者代碼不覆蓋,設(shè)計(jì)Bootloader存儲(chǔ)的起始地址為0x0,內(nèi)核存儲(chǔ)的起始地址為0x60000。
根據(jù)設(shè)計(jì)的存儲(chǔ)空間和內(nèi)存分配,兩者的映射關(guān)系如圖3所示。
圖3 映射空間圖
3)代碼重定位。
根據(jù)兩者映射關(guān)系,需要對(duì)代碼進(jìn)行重新定位,詳細(xì)分析如下:
由于cpu對(duì)nandflash讀取是以塊(2 048 KB)為單位的,所以不能在nandflash上運(yùn)行程序。當(dāng)系統(tǒng)上電或者重啟時(shí),硬件自動(dòng)把存儲(chǔ)器中前4 KB代碼拷貝到cpu內(nèi)部SRAM(映射地址為0);對(duì)于連接地址為0x33f80000的代碼若要成功運(yùn)行在地址為0的內(nèi)存中,在設(shè)計(jì)代碼時(shí)必須使用位置無(wú)關(guān)指令。通過(guò)運(yùn)行在SRAM程序把nandflash中啟動(dòng)代碼和內(nèi)核拷貝到SDRAM,然后通過(guò)一條跳轉(zhuǎn)指令,使PC指向SDRAM。
根據(jù)分析得到u-boot啟動(dòng)流程,以及分配好的內(nèi)存,下面針對(duì)S3C2440特定的嵌入式系統(tǒng),實(shí)現(xiàn)一個(gè)完整的Bootloader。
3.1 第一階段
Stage1主要是完成,建立向量表,初始化硬件,設(shè)置堆棧,把Bootloader拷貝到內(nèi)存中,具體流程如圖4所示。
圖4 Bootloader一階段流程
(1)設(shè)置ARM異常向量表。
根據(jù)ARM920T的架構(gòu)可知,異常向量入口地址從0x00000000開始到0x0000001c結(jié)束,分別設(shè)置對(duì)應(yīng)入口地址,當(dāng)一個(gè)異?;蛑袛喟l(fā)生時(shí),處理器會(huì)把pc指向?qū)?yīng)中斷向量的入口地址。
2)設(shè)置相關(guān)硬件。
設(shè)置時(shí)鐘,使CPU主頻為400 MHz;屏蔽中斷;初始化串口以便于調(diào)試。
3)初始化SDRAM。
SDRAM用來(lái)運(yùn)行啟動(dòng)代碼和內(nèi)核,必須對(duì)其進(jìn)行設(shè)置,SDRAM控制器的寄存器,包含處理器對(duì)SDRAM讀寫訪問(wèn)的時(shí)序,數(shù)據(jù)寬度;系統(tǒng)使用兩片16位數(shù)據(jù)接口的K4S561632N,其數(shù)據(jù)寬度為32位。
4)設(shè)置堆棧指針。
Bootloader在啟動(dòng)階段是運(yùn)行在管理模式下,只需設(shè)置管理模式下的堆棧指針(向下增長(zhǎng)),代碼為: ldr sp, =0x34000000;設(shè)置成功后就可以調(diào)用C語(yǔ)言代碼。
5)nandflash的初始化。
由于需要把存儲(chǔ)在nandflash的啟動(dòng)代碼和內(nèi)核讀入SDRAM,所以要設(shè)置nandflash控制器的寄存器,設(shè)置cpu對(duì)存儲(chǔ)器讀寫時(shí)序,以及數(shù)據(jù)寬度。
6)把啟動(dòng)代碼拷貝到SDRAM執(zhí)行。
SDRAM配置成功后,就可以從nandflash拷貝Bootloader啟動(dòng)代碼到內(nèi)存中運(yùn)行;具體代碼如下:
mov r0, 0 /*從nandflash 的0x0地址開始拷貝*/
ldr r1, =_start /*這個(gè)值是由連接腳本確定為0x33f80000*/
ldr r2, =__bss_start /*Bootlaoder代碼結(jié)束位置*/
sub r2, r2, r1 /*獲得拷貝的大小*/
bl copy_code_to_sdram / *從nandflash拷貝到SDRAM*/
ldr pc, =main /*重新給pc賦值,跳轉(zhuǎn)到SDRAM中,開始執(zhí)行第二階段代碼*/
3.2 第二階段
Stage2的主要工作設(shè)置內(nèi)核啟動(dòng)參數(shù),拷貝內(nèi)核到內(nèi)存中,然后啟動(dòng)內(nèi)核,具體流程如如圖5所示。
圖5 Bootloader二階段流程
1)設(shè)置內(nèi)核啟動(dòng)參數(shù)。
Bootloader和內(nèi)核之間參數(shù)傳遞是單向的,Bootloader將參數(shù)放在某個(gè)約定地址[3](0x30000100),再啟動(dòng)內(nèi)核,啟動(dòng)后會(huì)從0x30000100地址處取參數(shù)。
內(nèi)核啟動(dòng)前會(huì)檢查傳入的參數(shù),比如內(nèi)存的大小,命令等參數(shù)。因?yàn)?Linux2.6.22.6內(nèi)核檢查參數(shù)以標(biāo)記ATAG_CODE開始,以標(biāo)記ATAG_NONE結(jié)束。詳細(xì)結(jié)構(gòu)體可以參考linux內(nèi)核源碼include/asm/setup.h頭文件。
以設(shè)置參數(shù)開始ATAG_CODE標(biāo)記為例:
void setup_start_tag(void)
{
params = (struct tag *)0x30000100; /*約定的存放參數(shù)的起始地址*/
params->hdr.tag = ATAG_CODE; /*參數(shù)標(biāo)記*/
.......
params = tag_next (params); /*指向下一個(gè)參數(shù)*/
}
其他參數(shù)設(shè)置 如內(nèi)存標(biāo)記ATAG_MEM:告訴內(nèi)核開發(fā)平臺(tái)外設(shè)SDRAM內(nèi)存起始地址和大??;命令行標(biāo)記ATAG_CMDLINE:用來(lái)控制內(nèi)核一些行為,結(jié)束標(biāo)記ATAG_NONE用來(lái)標(biāo)記參數(shù)結(jié)束[3]。
最后配置的參數(shù)結(jié)束標(biāo)記:
void setup_end_tag(void)
{ params->hdr.tag = ATAG_NONE;
}
設(shè)置參數(shù)具體代碼:
puts("Set boot param "); /*打印提示信息*/
setup_start_tag(); /*設(shè)置起始參數(shù)*/
setup_memory_tags(); /*設(shè)置內(nèi)存參數(shù)*/
setup_commandline_tag("noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0"); /*設(shè)置命令參數(shù)*/
setup_end_tag(); /*參數(shù)設(shè)置結(jié)束*/
2)把內(nèi)核從flash中拷貝到內(nèi)存的0x30008000地址處。
編譯內(nèi)核是通過(guò)執(zhí)行:make uImage生成內(nèi)核影像uImage;用JTAG把內(nèi)核影像uImage 下載到nandflash的0x60000處,但是uImage開始的64字節(jié)表示內(nèi)核頭幀數(shù)據(jù),真正的內(nèi)核是從0x60000+64地址開始。具體拷貝代碼如下:
nand_read(0x60000+64, (unsigned char *)0x30008000, 0x200000);
第一個(gè)參數(shù): 0x60000+64代表的是內(nèi)核起始地址
第二個(gè)參數(shù):0x30008000代表是在SDRAM中內(nèi)核的運(yùn)行起始地址
第三個(gè)參數(shù):0x200000 代表從nandflash拷貝2M(內(nèi)核大小一般小于2M)大小程序到SDRAM中。
3)啟動(dòng)內(nèi)核。
根據(jù)分析u-boot源程序可知;啟動(dòng)內(nèi)核的過(guò)程就是把內(nèi)核在SDRAM的連接地址賦值給一個(gè)能啟動(dòng)內(nèi)核的函數(shù)指針;然后通過(guò)調(diào)用此函數(shù)指針把機(jī)器類型ID(S3C2440的ID為362)和啟動(dòng)參數(shù)在SDRAM中的起始基地址(0x30000100)傳遞給內(nèi)核[4]。
調(diào)用內(nèi)核具體代碼:
void (*theKernel)(int zero, int arch, unsigned int params);/*聲明啟動(dòng)內(nèi)核的函數(shù)指針*/
theKernel = (void (*)(int, int, unsigned int))0x30008000;/*把內(nèi)核在SDRAM中起始地址賦值給函數(shù)指針*/
puts("Boot kernel ");/*打印提示信息*/
theKernel(0, 362, 0x30000100); /*啟動(dòng)內(nèi)核*/
puts("error ");/*如果內(nèi)核啟動(dòng)成功,就不會(huì)執(zhí)行這一句,否則會(huì)打印出error:表示沒(méi)有成功啟動(dòng)內(nèi)核*/
為了測(cè)試設(shè)計(jì)的Bootloader是否具備穩(wěn)定和可靠特點(diǎn),需要在嵌入式S3C2440平臺(tái)多次測(cè)試是否能啟動(dòng)內(nèi)核,并分析Bootloader是否具備精簡(jiǎn)性。
4.1 Bootloader精簡(jiǎn)性分析
在編寫B(tài)ootloader的start.S文件中定義一個(gè)全局變量來(lái)計(jì)算Bootloader大小,具體代碼如下:
.globl _boot_size
_boot_size:
.word __bss_start - _start反匯編結(jié)果顯示如下:
33f80000 <_boot_size>:
33f80000: 00000730 andeq r0, r0, r0, lsr r7
則代碼大小為_boot_size地址處值:0x0000730=1.79 KB,所以遠(yuǎn)遠(yuǎn)小于u-boot.bin的432 KB, 節(jié)省大量的存儲(chǔ)空間,說(shuō)明設(shè)計(jì)的Bootloader具有精簡(jiǎn)性。
4.2 Bootloader穩(wěn)定和可靠性分析
把編譯后的Bootloader下載到nandflash中,經(jīng)過(guò)多次按下復(fù)位鍵后都能成功啟動(dòng)linux內(nèi)核。圖6顯示啟動(dòng)結(jié)果:
圖6 啟動(dòng)結(jié)果
通過(guò)實(shí)驗(yàn)可知,編寫的Bootloader比u-boot源碼簡(jiǎn)潔,代碼量小,并且成功加載linux內(nèi)核,通過(guò)借鑒u-boot源代碼的流程,以S3C2440為平臺(tái),設(shè)計(jì)出了精簡(jiǎn),穩(wěn)定的Bootloader。該設(shè)計(jì)方法具有一定通用性,對(duì)于其他不同平臺(tái),具有很強(qiáng)借鑒性。
[1]劉 坤,韓朝智. 淺析基于ARM嵌入式開發(fā)的BootLoader設(shè)計(jì)及其實(shí)現(xiàn)[J]. 電子技術(shù)與軟件工程,2016(2):203-204.
[2] 范展源,六 韜.深度實(shí)踐嵌入式Linux系統(tǒng)移植[M]北京:機(jī)械工業(yè)出版社,2015.
[3] 韋東山.嵌入式linux應(yīng)用開發(fā)[M],北京:人民郵電出版社,2008.
[4] 戚長(zhǎng)城,等.總線式ECU兩級(jí)Bootloader的設(shè)計(jì)與實(shí)現(xiàn)[J]. 計(jì)算機(jī)工程,2015(7):95-99.
Development and Implement of Bootloader Based on S3C2440 and Embedded Linux System
Fan Panguo, Xing Baohao , Mi Xiaoliang, Yu Shubao,Wang Ting
(College of Automation,Northwestern Polytechnical University,Xi’an 710129,China)
Bootloader is the important part of embedded system. For the different hardware platforms, Bootloader is different. So bootloader is the difficult for the developmemt of embedded system. This paper mainly analyzes the design of embedded S3C2440 system and the process of u-boot startup kernel, drawing two essential phases of u-boot startup kernel, the first phase is to use assembler language to write some functions of the specific hardware platforms ,The second phase is to use C language to write codes about some complicated functions and loading the kernel.For example,Loading the linux-2.6.22.6,according to the two stages work of u-boot startup kernel. Designing of streamlined Bootloader is suitable for the S3C2440 embedded system.Experiment shows that the design of Bootloader successfully starts the linux kernel ,and has a good stability,reliability and simplicity.
design system; analyze u-boot; realize Bootloader;load kernel
2016-03-30;
2016-04-18。
范蟠果(1960-),男,陜西西安人,碩士生導(dǎo)師,副教授,主要從事計(jì)算機(jī)測(cè)控方向的研究。
1671-4598(2016)09-0012-03
10.16526/j.cnki.11-4762/tp.2016.09.004
TP273
A