摘 要:Boot Loader 作為 ARM 嵌入式系統(tǒng)的一個(gè)重要部分。對(duì)于使用不相同版本的內(nèi)核的系統(tǒng)板,所對(duì)應(yīng)的Boot Loader 也是相同,因此對(duì)每個(gè)系統(tǒng)板都要對(duì)其運(yùn)行其所對(duì)應(yīng)的Boot Loader。在此簡(jiǎn)要介紹 S3C2410 及其兩種啟動(dòng)方式,著重分析S3C2410 從 NANDFLASH 啟動(dòng)的過(guò)程中,對(duì)各個(gè)內(nèi)部功能模塊進(jìn)行初始化過(guò)程,并設(shè)計(jì)出基于 S3C2410 嵌入式系統(tǒng)的Boot Loader。通過(guò)在線仿真及實(shí)際測(cè)試表明,該Boot Loader 具有良好的穩(wěn)定性、實(shí)時(shí)性和可移植性。關(guān)鍵詞:ARM; Boot Loader; 嵌入式系統(tǒng); 啟動(dòng)方式
中圖分類(lèi)號(hào):TN911-34; TP311 文獻(xiàn)標(biāo)識(shí)碼:A
文章編號(hào):1004-373X(2010)22-0071-03
Analysis and Design of Boot Loader Based on ARM-Linux Embedded System
WANG Shi-yang, YU Xue-cai, LIANG Xi-ning, CHEN Tao, ZHU Liang-xiao, SU Ke
(SOEI, University of Electronic Science Technology of China, Chengdu 610054, China)
Abstract: Boot Loader is an important part of ARM embedded system. For different kernel system board, its Boot Loader is also different. Each bare-board should program its own Boot Loader. Therefore, the development of specific Boot Loader is particularly important, because the superiority of Boo Lloader directly affects on the performance of embedded systems. The S3C2410 and its two start-up modes are introduced. The process of initialization that S3C2410 makes for each internal module is analyzed emphatically during the start-up of NANDFLASH. A Boot Loader based on S3C2410 embedded system is designed. The online simulation and practical tests show that the Boot Loader has good stability, real-time performance and portability.
Keywords: ARM; Boot Loader; embedded system; start-up mode
0 引 言
由Boot Loader和固化在固件(firmware)中的Boot代碼(可選)共同組成一個(gè)嵌入式系統(tǒng)的引導(dǎo)加載程序。它的作用和功能就像固化到計(jì)算機(jī)內(nèi)主板上的一個(gè)ROM芯片程序BIOS(basic input outputsystem)。但是它一般不配置像BIOS那樣的固件程序,這是因?yàn)橐紤]經(jīng)濟(jì)方面的原因,因此必須自己完成這方面的工作。Boot Loader可以初始化硬件設(shè)備,建立內(nèi)存空間的映射圖,從而將系統(tǒng)的軟硬件環(huán)境帶到一個(gè)合適的狀態(tài),以便為最終調(diào)用操作系統(tǒng)內(nèi)核準(zhǔn)備好正確的環(huán)境。它的實(shí)現(xiàn)嚴(yán)重地依賴于硬件,特別是在嵌入式系統(tǒng)中,即使基于同一個(gè)CPU的Boot Loader,對(duì)于不同的板子,也有很大的不同[1]。
1 Boot Loader分析
系統(tǒng)加電,然后復(fù)位后,基本上所有的CPU都是從復(fù)位地址上取得指令的。以微處理器為核心的嵌入式系統(tǒng)中,通常都有某種類(lèi)型的固態(tài)存儲(chǔ)設(shè)備(FLASH,E2PROM等),這個(gè)固態(tài)存儲(chǔ)設(shè)備被映射到一個(gè)預(yù)先設(shè)置好的地址上。在系統(tǒng)加電復(fù)位后,一開(kāi)始處理器就會(huì)去執(zhí)行存放在復(fù)位地址處的程序,而且通過(guò)開(kāi)發(fā)環(huán)境可以將 Boot Loader 定位在復(fù)位地址一開(kāi)始的存儲(chǔ)空間上,因此 Boot Loader 是系統(tǒng)加電后,在操作系統(tǒng)內(nèi)核或者一些應(yīng)用程序被運(yùn)行之前,首先會(huì)運(yùn)行的程序。對(duì)于嵌入式系統(tǒng)來(lái)說(shuō),比較復(fù)雜的或者為了方便后期開(kāi)發(fā)大的應(yīng)用程序,有的使用操作系統(tǒng),也有很多的情況下,因功能簡(jiǎn)單,或僅包括應(yīng)用程序的系統(tǒng)不使用操作系統(tǒng),但是不論有無(wú)操作系統(tǒng)在啟動(dòng)時(shí)都必須執(zhí)行Boot Loader,為的是準(zhǔn)備好軟硬件運(yùn)行環(huán)境。
以微處理器為核心的嵌入式系統(tǒng)中,一般都有某種類(lèi)型的固態(tài)存儲(chǔ)設(shè)備(FLASH,E2PROM等),這個(gè)固態(tài)存儲(chǔ)設(shè)備被映射到一個(gè)預(yù)先設(shè)置好的地址上。在系統(tǒng)加電復(fù)位后,一開(kāi)始處理器就會(huì)去執(zhí)行存放在復(fù)位地址處的程序。而且通過(guò)開(kāi)發(fā)環(huán)境可以將 Boot Loader 定位在復(fù)位地址一開(kāi)始的存儲(chǔ)空間上,因此 Boot Loader 是系統(tǒng)加電后,在操作系統(tǒng)內(nèi)核或者一些應(yīng)用程序被運(yùn)行之前,首先會(huì)運(yùn)行的程序。對(duì)于Linux系統(tǒng),它的主要任務(wù)有以下7個(gè)方面。
(1) 初始化處理器及外設(shè)的硬件資源配置。一般嵌入式系統(tǒng)的處理器在上電復(fù)位后,外部的I/O引腳都處于輸入狀態(tài),處理器的片內(nèi)和片外設(shè)備資源都需要配置。
(2) 建立內(nèi)存空間的映射圖,從而將系統(tǒng)的軟硬件環(huán)境帶到一個(gè)合適的環(huán)境,這樣就能為最終啟動(dòng)操作系統(tǒng)的內(nèi)核提供最好條件。
(3) 把操作裝載到映射的內(nèi)存中,這也是所有任務(wù)當(dāng)中最重要的一個(gè),只有完成這個(gè)任務(wù),操作系統(tǒng)才能被裝載到內(nèi)存當(dāng)中去,Boot Loader一般會(huì)提供串口和網(wǎng)絡(luò)裝載兩種方式。
(4) 為了把操作系統(tǒng)的映像保存在FLASH中,以便以后啟動(dòng),可以直接裝載FLASH的數(shù)據(jù),而不用重新下載程序,但需要對(duì)FLASH進(jìn)行編程。
(5) 運(yùn)行操作系統(tǒng)。設(shè)置相關(guān)的寄存器和資源,跳轉(zhuǎn)到操作系統(tǒng)的所在空間,進(jìn)行相關(guān)的引導(dǎo),這就是Boot Loader。
(6) 在Linux系統(tǒng)啟動(dòng)時(shí),傳遞系統(tǒng)的啟動(dòng)參數(shù),可以給內(nèi)核傳遞命令行等參數(shù),通過(guò)命令行可以選擇控制系統(tǒng)的啟動(dòng)模式。
(7) 命令行的解析和輸入/輸出控制。為了開(kāi)發(fā)的方便,多數(shù)的Boot Loader都采用串口作為終端的控制方式[2]。
Boot Loader的啟動(dòng)過(guò)程可分為兩個(gè)重要階段。第一階段:由于 Boot Loader的實(shí)現(xiàn)依賴于 CPU的體系結(jié)構(gòu),所以設(shè)備代碼的初始化等功能都在該階段完成。而且,為了達(dá)到縮短代碼的目的,通常用匯編語(yǔ)言來(lái)編寫(xiě)。在這一階段的執(zhí)行過(guò)程中,又可分為幾個(gè)方面。
① 硬件設(shè)備的初始化。
在該階段的執(zhí)行過(guò)程中,首先需要對(duì)硬件設(shè)備進(jìn)行初始化,其目的主要是為第二階段的執(zhí)行以及隨后Kernel的調(diào)用準(zhǔn)備基本的硬件環(huán)境。
② 為加載 Boot Loader的第二階段準(zhǔn)備RAM空間。
為了獲得更快的執(zhí)行速度,通常把第二階段加載到 RAM 空 間 中 來(lái) 執(zhí) 行。因 此, 必 須 為 加 載Boot Loader準(zhǔn)備好一段可用的 RAM空間范圍。
③ 設(shè)置堆棧指針。設(shè)置堆棧是為了執(zhí)行 C語(yǔ)言代碼作好準(zhǔn)備。
④ 跳轉(zhuǎn)到第二階段的C入口點(diǎn)。當(dāng)程序執(zhí)行到這個(gè)位置時(shí),可以通過(guò)修改 PC寄存器的值,使其跳轉(zhuǎn)到第二階段。
第二階段階段的啟動(dòng)流程分析:為了便于實(shí)現(xiàn)復(fù)雜的功能和獲得更好的代碼可讀性和可移植性,通常第二階段的代碼用C語(yǔ)言來(lái)實(shí)現(xiàn)。但是,與普通 C語(yǔ)言的不同之處是,這里使用了“ 彈簧床 ” 的概念,即先用匯編語(yǔ)言寫(xiě)一段小程序,并將這段小程序作為第二階段可執(zhí)行映像的執(zhí)行入口點(diǎn),然后在匯編程序中用 CPU跳轉(zhuǎn)指令跳入main ( )函數(shù)中去執(zhí)行,當(dāng) main ( )函數(shù)返回時(shí),CPU執(zhí)行路徑再次返回到匯編程序中第二階段,包括初始化本階段要使用的硬件設(shè)備,檢測(cè)系統(tǒng)內(nèi)存映射,會(huì)將Kernel映像和根文件系統(tǒng)映像從FLASH中讀到RAM空間中,為內(nèi)核設(shè)置啟動(dòng)參數(shù)調(diào)用內(nèi)核。
2 Boot Loader的設(shè)計(jì)
2.1 中斷向量表(二級(jí))的設(shè)計(jì)與建立
如果有中斷或者異常發(fā)生時(shí),處理器便會(huì)強(qiáng)制性地把PC指針指向向量表中它所對(duì)應(yīng)的中斷類(lèi)型地址值。為了提高中斷響應(yīng)速度,F(xiàn)LASH的0x0地址存放能跳轉(zhuǎn)到 0x33FFFF00地址處中斷向量的跳轉(zhuǎn)指令,也就是會(huì)在在RAM中建立一個(gè)二級(jí)中斷向量表,起始地址為0x33FFFF00。除了復(fù)位外,所有的異常入口地址都由FLASH跳轉(zhuǎn)得到,代碼如下:
#define _ISR_STARTADDRESS (SDRAM_END- 0x100) //0x33FFFF00
definepISR_RESET (* (unsigned *)(_ISR_STARTADDRESS+0x0))// x33FFFF00
#define pISR_UNDEF (* (unsigned *)(_ISR_STARTAD-DRESS+0x4))// x33FFFF04
2.2 第二階段拷貝到 RAM
把第二階段Stage2拷貝到 RAM地址的最頂大小為1 MB的開(kāi)始空間, RAM的起始地址為0x30000000。代碼如下所示[3]:
/* 計(jì)算在FLASH中的位置, 假設(shè)該映像不超過(guò)64K,可修改該值*/
Adr r0,_start
Add r2,r0,#(64*1024)
Add r0,ro,#0x1000
Ldr r1,BLOB_START
/* 開(kāi)始復(fù)制stage2 到 RAM,R0=源起始地址,R1=目的地址,R2源結(jié)束地址 */
copy_loop:
ldmia r0!,{r3- r10}
stmia r1!,{r3- r10}
cmp r0,r2
ble copy_loop
ldr r0,BLOB_START
2.3 堆棧指針的設(shè)置
用戶使用哪些中斷決定了系統(tǒng)堆棧的初始化, 以及系統(tǒng)需要處理的哪些錯(cuò)誤類(lèi)型。一般情況下,堆棧設(shè)置是必須, 而且是由管理者自己設(shè)置的。如果需要使用IRQ中斷, 那么IRQ堆棧的設(shè)置也是必須的,下面是IRQ堆棧的設(shè)置[4]:
IRQMode //堆棧
orr r1,r0,#IRQMODE|NOINT
msr cpsr_cxsf,r1; IRQMode
ldr sp,IRQStack
3 Stage2的設(shè)計(jì)
3.1 可執(zhí)行映像 Stage2 的入口
由于Glibc 庫(kù)支持的函數(shù)不能用于編譯和鏈接 Boot Loader 這樣用C語(yǔ)言編寫(xiě)的程序,因此把main()函數(shù)的起始地址作為第二階段的入口點(diǎn)是最直接的想法。可以用匯編編寫(xiě)一段Trampoline小程序,用 CPU跳轉(zhuǎn)指令跳到main()函數(shù)去執(zhí)行,當(dāng)函數(shù)返回時(shí)會(huì)再次回到Trampoline 程序[5],代碼如下:
ldr sp DW_STACK_START @setup stack pointer
mov fp,#0 @no previous frame,so fp=0
mov a2, #0 @set argv to NULL
bl main @call main
mov pc,#FLASH_BASE @otherwise,reboot
程序順利時(shí)就不會(huì)再回到開(kāi)始的Trampoline 程序,不然就會(huì)回到最后的語(yǔ)句,系統(tǒng)就會(huì)重新啟動(dòng)。
3.2 內(nèi)存影射
一般S3C2410上配置的SDRSAM大小為64 MB, 該SDRAM的物理地址范圍是0x30000000~0x33FFFFFF(屬于Bank 6)。由Section的大小可知,該物理空間可被分成64個(gè)物理段。因?yàn)锳RM體系結(jié)構(gòu)中數(shù)據(jù)緩沖必須通過(guò) MMU開(kāi)啟,因此Boot Loader效率不是很高,但是MMU可以通過(guò)平板映射(虛擬地址和物理地址相同)方式被開(kāi)啟,這樣使用內(nèi)存空間Dcache,從而使Boot Loader的運(yùn)行速度得到有效的提高[6]。映射關(guān)系代碼如下[7-8]:
void mem_mapping_linear(void)
{ unsigned long descriptor_index, section_base, sdram_base,
sdram_size;
sdram_base=0x30000000;
sdram_size=0x4000000;
for (section_base =sdram_base,descriptor_index =
sec-tion_base>>20;Ssection_base sdram_size; rdescrip-tor_index+=1;section_base +=0x100000) {* (mmu_tlb_base +(descriptor_index)) =(section_base >>20) | MMU_OTHER_SECDESC;} } 3.3 裝載內(nèi)核映像和根文件系統(tǒng)映像 像 ARM這樣的嵌入式 CPU 通常都是在統(tǒng)一的內(nèi)存地址空間中尋址FLASH等固態(tài)存儲(chǔ)設(shè)備的,因此從 Flash 上讀取數(shù)據(jù)與從 RAM單元中讀取數(shù)據(jù)一樣,用一個(gè)簡(jiǎn)單的循環(huán)就可以完成從FLASH設(shè)備上拷貝映像的工作:其中 count 為根文件系統(tǒng)映像的大小或內(nèi)核映像的大小[9]。 While(count) { *dest++=*src++;//src為FLASH中的地址, dest為 RAM中的地址 count-=4; } 3.4 內(nèi)核的啟動(dòng)參數(shù)的設(shè)置 內(nèi)核啟動(dòng)可以從 NAND FLASH(NOR FLASH)中啟動(dòng)運(yùn)行Linux,需要修改啟動(dòng)命令如下[10]: #ifdef CONFIG_S3C2410_NAND_BOOTChar Linux_ cmd[]=“ noinit root=/dev/bon/2 init=/Linuxrc console= tty0 console=ttys0” ; #else CharLinux_cmd[]=“CharLinux_cmd[]=” noinit root= /dev/bon/3init =/Linuxrc console=tty0 console=ttys0” ; LCD啟動(dòng)參數(shù)一般都包括root,init和console。noinitrd不使用ramdisk。root根文件系統(tǒng)在MTD分區(qū)。Init 內(nèi)核運(yùn)行入口命令文件。consol內(nèi)核信息控制臺(tái),ttys0表示串行口0[11];tty0表示虛擬終端。 4 結(jié) 語(yǔ) 通過(guò)對(duì)Boot Loader的分析可以看出,設(shè)計(jì)一個(gè)性能優(yōu)良的 Boot Loader 可以提高系統(tǒng)的穩(wěn)定性及實(shí)時(shí)性,它是嵌入式開(kāi)發(fā)中不可或缺的一部分。只有設(shè)計(jì)出一個(gè)穩(wěn)定的Boot Loader,才能進(jìn)行下一步的系統(tǒng)開(kāi)發(fā)工作,直至完成整個(gè)嵌入式系統(tǒng)的開(kāi)發(fā)。設(shè)計(jì)Boot Loader是一項(xiàng)很復(fù)雜的工作,需要對(duì)硬件資源和所用的操作系統(tǒng)有很深的理解。在實(shí)際開(kāi)發(fā)中可以根據(jù)需要簡(jiǎn)化設(shè)計(jì),去除不必要的系統(tǒng)功能,這樣可以大大提高程序執(zhí)行的效率和穩(wěn)定性。這里給出的 Boot Loader 已經(jīng)順利通過(guò)了調(diào)試,可以正常加載操作系統(tǒng)。 參考文獻(xiàn) [1]馬忠梅.ARM嵌入式處理器結(jié)構(gòu)與應(yīng)用基礎(chǔ) [M].北京:北京航空航天大學(xué)出版社,2003. [2]李駒光.ARM應(yīng)用系統(tǒng)開(kāi)發(fā)詳解:基于 S3C4510B的系統(tǒng)設(shè)計(jì)[M].北京:清華大學(xué)出版社,2003. [3]曹程遠(yuǎn).U-Boot在 S3C2410上的移植[J].微型電腦應(yīng)用,2005,21(7):48-50. [4]郭志,江秀臣,曾奕.一個(gè)嵌入式系統(tǒng)的啟動(dòng)分析[J].微計(jì)算機(jī)信息,2005,21(32):28-30. [5]陳海軍,申衛(wèi)昌,史穎.嵌入式系統(tǒng)引導(dǎo)程序詳探[J].計(jì)算機(jī)技術(shù)與發(fā)展,2006,16(1):123-125,128. [6]Microsoft Corporation. Microsoft extensible firmware initiative FAT32 file system specification [M ]. 1st ed. [S.l.]:[s.n.], 2000. [7]SanDisk Corporation. Sandisk secure digital card product manual[M ]. 2nd ed. [S.l.]: [s.n.], 2004. [8]Sumsung Electronics. S3C2410X 32 b risc microprocessor user′ s manual[M]. [S.l.]: [s.n.], 2003. [9]霍拉鮑夫.嵌入式Linux:硬件、軟件與接口[M].陳雷,譯.北京:電子工業(yè)出版社,2003. [10]徐睿,黃健,徐辰.基于 ARM的嵌入式系統(tǒng)開(kāi)發(fā)與應(yīng)用[M].北京:人民郵電出版社,2004. [11]李善平.Linux與嵌入式系統(tǒng)[M].北京:清華大學(xué)出版社,2002.