我們知道樟氢,bootloader是系統(tǒng)上電后最初加載運(yùn)行的代碼火鼻。它提供了處理器上電復(fù)位后最開(kāi)始需要執(zhí)行的初始化代碼催享。 在PC機(jī)上引導(dǎo)程序一般由BIOS開(kāi)始執(zhí)行豌蟋,然后讀取硬盤(pán)中位于MBR(Main Boot Record塘淑,主引導(dǎo)記錄)中的Bootloader(例如LILO或GRUB),并進(jìn)一步引導(dǎo)操作系統(tǒng)的啟動(dòng)呜笑。
然而在嵌入式系統(tǒng)中通常沒(méi)有像BIOS那樣的固件程序撤蟆,因此整個(gè)系統(tǒng)的加載啟動(dòng)就完全由bootloader來(lái)完成垒迂。它主要的功能是加載與引導(dǎo)內(nèi)核映像
一個(gè)嵌入式的存儲(chǔ)設(shè)備通過(guò)通常包括四個(gè)分區(qū):
第一分區(qū):存放的當(dāng)然是u-boot
第二個(gè)分區(qū):存放著u-boot要傳給系統(tǒng)內(nèi)核的參數(shù)
第三個(gè)分區(qū):是系統(tǒng)內(nèi)核(kernel)
第四個(gè)分區(qū):則是根文件系統(tǒng)
如下圖所示:
四大分區(qū)
u-boot是一種普遍用于嵌入式系統(tǒng)中的Bootloader牵寺。
Bootloader介紹
Bootloader是進(jìn)行嵌入式開(kāi)發(fā)必然會(huì)接觸的一個(gè)概念悍引,本篇文章主要講解Bootloader的基本概念以及內(nèi)部原理,這部分內(nèi)容的掌握將對(duì)嵌入式linux系統(tǒng)開(kāi)發(fā)的學(xué)習(xí)非常有幫助帽氓!
Bootloader的定義:Bootloader是在操作系統(tǒng)運(yùn)行之前執(zhí)行的一小段程序趣斤,通過(guò)這一小段程序,我們可以初始化硬件設(shè)備黎休、建立內(nèi)存空間的映射表浓领,從而建立適當(dāng)?shù)南到y(tǒng)軟硬件環(huán)境,為最終調(diào)用操作系統(tǒng)內(nèi)核做好準(zhǔn)備势腮。意思就是說(shuō)如果我們要想讓一個(gè)操作系統(tǒng)在我們的板子上運(yùn)轉(zhuǎn)起來(lái)联贩,我們就必須首先對(duì)我們的板子進(jìn)行一些基本配置和初始化,然后才可以將操作系統(tǒng)引導(dǎo)進(jìn)來(lái)運(yùn)行捎拯。具體在Bootloader中完成了哪些操作我們會(huì)在后面分析到撑蒜,這里我們先來(lái)回憶一下PC的體系結(jié)構(gòu):PC機(jī)中的引導(dǎo)加載程序是由BIOS和位于硬盤(pán)MBR中的OS Boot Loader(比如LILO和GRUB等)一起組成的,BIOS在完成硬件檢測(cè)和資源分配后玄渗,將硬盤(pán)MBR中的Boot Loader讀到系統(tǒng)的RAM中座菠,然后將控制權(quán)交給OS Boot Loader。Boot Loader的主要運(yùn)行任務(wù)就是將內(nèi)核映象從硬盤(pán)上讀到RAM中藤树,然后跳轉(zhuǎn)到內(nèi)核的入口點(diǎn)去運(yùn)行浴滴,即開(kāi)始啟動(dòng)操作系統(tǒng)。在嵌入式系統(tǒng)中岁钓,通常并沒(méi)有像BIOS那樣的固件程序(注:有的嵌入式cpu也會(huì)內(nèi)嵌一段短小的啟動(dòng)程序)升略,因此整個(gè)系統(tǒng)的加載啟動(dòng)任務(wù)就完全由Boot Loader來(lái)完成微王。比如在一個(gè)基于ARM7TDMI core的嵌入式系統(tǒng)中,系統(tǒng)在上電或復(fù)位時(shí)通常都從地址0x00000000處開(kāi)始執(zhí)行品嚣,而在這個(gè)地址處安排的通常就是系統(tǒng)的Boot Loader程序炕倘。(先想一下,通用PC和嵌入式系統(tǒng)為何會(huì)在此處存在如此的差異呢翰撑?)
Bootloader是基于特定硬件平臺(tái)來(lái)實(shí)現(xiàn)的罩旋,因此幾乎不可能為所有的嵌入式系統(tǒng)建立一個(gè)通用的Bootloader,不同的處理器架構(gòu)都有不同的Bootloader眶诈,Bootloader不但依賴(lài)于cpu的體系結(jié)構(gòu)涨醋,還依賴(lài)于嵌入式系統(tǒng)板級(jí)設(shè)備的配置。對(duì)于2塊不同的板子而言逝撬,即使他們使用的是相同的處理器浴骂,要想讓運(yùn)行在一塊板子上的Bootloader程序也能運(yùn)行在另一塊板子上,一般也需要修改Bootloader的源程序宪潮。
Bootloader的啟動(dòng)方式
Bootloader的啟動(dòng)方式主要有網(wǎng)絡(luò)啟動(dòng)方式溯警、磁盤(pán)啟動(dòng)方式和Flash啟動(dòng)方式。
1狡相、網(wǎng)絡(luò)啟動(dòng)方式

如圖1所示愧膀,里面主機(jī)和目標(biāo)板,他們中間通過(guò)網(wǎng)絡(luò)來(lái)連接谣光,首先目標(biāo)板的DHCP/BIOS通過(guò)BOOTP服務(wù)來(lái)為Bootloader分配IP地址檩淋,配置網(wǎng)絡(luò)參數(shù),這樣才能支持網(wǎng)絡(luò)傳輸功能萄金。我們使用的u-boot可以直接設(shè)置網(wǎng)絡(luò)參數(shù)蟀悦,因此這里就不用使用DHCP的方式動(dòng)態(tài)分配IP了。接下來(lái)目標(biāo)板的Bootloader通過(guò)TFTP服務(wù)將內(nèi)核映像下載到目標(biāo)板上氧敢,然后通過(guò)網(wǎng)絡(luò)文件系統(tǒng)來(lái)建立主機(jī)與目標(biāo)板之間的文件通信過(guò)程日戈,之后的系統(tǒng)更新通常也是使用Boot Loader的這種工作模式。工作于這種模式下的Boot Loader通常都會(huì)向它的終端用戶提供一個(gè)簡(jiǎn)單的命令行接口孙乖。
2浙炼、磁盤(pán)啟動(dòng)方式
這種方式主要是用在臺(tái)式機(jī)和服務(wù)器上的,這些計(jì)算機(jī)都使用BIOS引導(dǎo)唯袄,并且使用磁盤(pán)作為存儲(chǔ)介質(zhì)弯屈,這里面兩個(gè)重要的用來(lái)啟動(dòng)linux的有LILO和GRUB,這里就不再具體說(shuō)明了恋拷。
3资厉、Flash啟動(dòng)方式
這是我們最常用的方式。Flash有NOR Flash和NAND Flash兩種蔬顾。NOR Flash可以支持隨機(jī)訪問(wèn)宴偿,所以代碼可以直接在Flash上執(zhí)行湘捎,Bootloader一般是存儲(chǔ)在Flash芯片上的。另外Flash上還存儲(chǔ)著參數(shù)窄刘、內(nèi)核映像和文件系統(tǒng)窥妇。這種啟動(dòng)方式與網(wǎng)絡(luò)啟動(dòng)方式之間的不同之處就在于,在網(wǎng)絡(luò)啟動(dòng)方式中娩践,內(nèi)核映像和文件系統(tǒng)首先是放在主機(jī)上的活翩,然后經(jīng)過(guò)網(wǎng)絡(luò)傳輸下載進(jìn)目標(biāo)板的,而這種啟動(dòng)方式中內(nèi)核映像和文件系統(tǒng)則直接是放在Flash中的欺矫,這兩點(diǎn)在我們u-boot的使用過(guò)程中都用到了。
U-boot的定義
U-boot展氓,全稱(chēng)Universal Boot Loader穆趴,是由DENX小組的開(kāi)發(fā)的遵循GPL條款的開(kāi)放源碼項(xiàng)目,它的主要功能是完成硬件設(shè)備初始化遇汞、操作系統(tǒng)代碼搬運(yùn)未妹,并提供一個(gè)控制臺(tái)及一個(gè)指令集在操作系統(tǒng)運(yùn)行前操控硬件設(shè)備。U-boot之所以這么通用空入,原因是他具有很多特點(diǎn):開(kāi)放源代碼络它、支持多種嵌入式操作系統(tǒng)內(nèi)核、支持多種處理器系列化戳、較高的穩(wěn)定性埋凯、高度靈活的功能設(shè)置、豐富的設(shè)備驅(qū)動(dòng)源碼以及較為豐富的開(kāi)發(fā)調(diào)試文檔與強(qiáng)大的網(wǎng)絡(luò)技術(shù)支持白对。另外u-boot對(duì)操作系統(tǒng)和產(chǎn)品研發(fā)提供了靈活豐富的支持掠廓,主要表現(xiàn)在:可以引導(dǎo)壓縮或非壓縮系統(tǒng)內(nèi)核,可以靈活設(shè)置/傳遞多個(gè)關(guān)鍵參數(shù)給操作系統(tǒng)蟀瞧,適合系統(tǒng)在不同開(kāi)發(fā)階段的調(diào)試要求與產(chǎn)品發(fā)布条摸,支持多種文件系統(tǒng)悦污,支持多種目標(biāo)板環(huán)境參數(shù)存儲(chǔ)介質(zhì),采用CRC32校驗(yàn)钉蒲,可校驗(yàn)內(nèi)核及鏡像文件是否完好塞关,提供多種控制臺(tái)接口子巾,使用戶可以在不需要ICE的情況下通過(guò)串口/以太網(wǎng)/USB等接口下載數(shù)據(jù)并燒錄到存儲(chǔ)設(shè)備中去(這個(gè)功能在實(shí)際的產(chǎn)品中是很實(shí)用的,尤其是在軟件現(xiàn)場(chǎng)升級(jí)的時(shí)候)椰于,以及提供豐富的設(shè)備驅(qū)動(dòng)等瘾婿。
u-boot源代碼的目錄結(jié)構(gòu)
1蜻牢、board中存放于開(kāi)發(fā)板相關(guān)的配置文件抢呆,每一個(gè)開(kāi)發(fā)板都以子文件夾的形式出現(xiàn)笛谦。
2、Commom文件夾實(shí)現(xiàn)u-boot行下支持的命令恳邀,每一個(gè)命令對(duì)應(yīng)一個(gè)文件灶轰。
3、cpu中存放特定cpu架構(gòu)相關(guān)的目錄乳附,每一款cpu架構(gòu)都對(duì)應(yīng)了一個(gè)子目錄伴澄。
4、Doc是文檔目錄秉版,有u-boot非常完善的文檔。
5并蝗、Drivers中是u-boot支持的各種設(shè)備的驅(qū)動(dòng)程序秸妥。
6、Fs是支持的文件系統(tǒng)键畴,其中最常用的是JFFS2文件系統(tǒng)。
7涡贱、Include文件夾是u-boot使用的頭文件惹想,還有各種硬件平臺(tái)支持的匯編文件,系統(tǒng)配置文件和文件系統(tǒng)支持的文件激挪。
8锋叨、Net是與網(wǎng)絡(luò)協(xié)議相關(guān)的代碼娃磺,bootp協(xié)議、TFTP協(xié)議嘿般、NFS文件系統(tǒng)得實(shí)現(xiàn)涯冠。
9逼庞、Tooles是生成U-boot的工具赛糟。
對(duì)u-boot的目錄有了一些了解后,分析啟動(dòng)代碼的過(guò)程就方便多了掌逛,其中比較重要的目錄就是/board司倚、/cpu、/drivers和/include目錄皿伺,如果想實(shí)現(xiàn)u-boot在一個(gè)平臺(tái)上的移植盒粮,就要對(duì)這些目錄進(jìn)行深入的分析。
什么是《編譯地址》妒穴?什么是《運(yùn)行地址》?
(一)編譯地址: 32位的處理器弃甥,它的每一條指令是4個(gè)字節(jié)淆攻,以4個(gè)字節(jié)存儲(chǔ)順序嘿架,進(jìn)行順序執(zhí)行,CPU是順序執(zhí)行的伞芹,只要沒(méi)發(fā)生什么跳轉(zhuǎn)蝉娜,它會(huì)順序進(jìn)行執(zhí)行行召川, 編譯器會(huì)對(duì)每一條指令分配一個(gè)編譯地址,這是編譯器分配的汉形,在編譯過(guò)程中分配的地址倍阐,我們稱(chēng)之為編譯地址。(二)運(yùn)行地址:是指程序指令真正運(yùn)行的地址岔冀,是由用戶指定的概耻,用戶將運(yùn)行地址燒錄到哪里咐蚯,哪里就是運(yùn)行的地址。
比如有一個(gè)指令的編譯地址是0x5矫膨,實(shí)際運(yùn)行的地址是0x200,如果用戶將指令燒到0x200上危尿,那么這條指令的運(yùn)行地址就是0x200馁痴,
當(dāng)編譯地址和運(yùn)行地址不同的時(shí)候會(huì)出現(xiàn)什么結(jié)果罗晕?結(jié)果是不能跳轉(zhuǎn),編譯后會(huì)產(chǎn)生跳轉(zhuǎn)地址法褥,如果實(shí)際地址和編譯后產(chǎn)生的地址不相等酬屉,那么就不能跳轉(zhuǎn)。
C語(yǔ)言編譯地址:都希望把編譯地址和實(shí)際運(yùn)行地址放在一起的杀饵,但是匯編代碼因?yàn)椴恍枰?a target="_blank" rel="nofollow">c語(yǔ)言到匯編的轉(zhuǎn)換切距,可以認(rèn)為的去寫(xiě)地址怯屉,所以直接寫(xiě)的就是他的運(yùn)行地址這就是為什么任何bootloader剛開(kāi)始會(huì)有一段匯編代碼饵沧,因?yàn)槠鹗即a編譯地址和實(shí)際地址不相等狼牺,這段代碼和匯編無(wú)關(guān),跳轉(zhuǎn)用的運(yùn)行地址掠归。** **
** 編譯地址和運(yùn)行地址如何來(lái)算呢悄泥?**
- 假如有兩個(gè)編譯地址a=0x10弹囚,b=0x7,b的運(yùn)行地址是0x300蛮穿,那么a的運(yùn)行地址就是b的運(yùn)行地址加上兩者編譯地址的差值,a-b=0x10-0x7=0x3单刁,
a的運(yùn)行地址就是0x300+0x3=0x303府适。 2. 假設(shè)uboot上兩條指令的編譯地址為a=0x33000007和b=0x33000001檐春,這兩條指令都落在bank6上,現(xiàn)在要計(jì)算出他們對(duì)應(yīng)的運(yùn)行地址恍风,要找出運(yùn)行地址的始地址誓篱,這個(gè)是由用戶燒錄進(jìn)去的,假設(shè)運(yùn)行地址的首地址是0x0锦募,則a的運(yùn)行地址為0x7糠亩,b為0x1准验,就是這樣算出來(lái)的。** 為什么要分配編譯地址垂寥?這樣做有什么好處滞项,有什么作用夭坪?** 比如在函數(shù)a中定義了函數(shù)b,當(dāng)執(zhí)行到函數(shù)b時(shí)要進(jìn)行指令跳轉(zhuǎn)戏仓,要跳轉(zhuǎn)到b函數(shù)所對(duì)應(yīng)的起始地址上去柜去,編譯時(shí),編譯器給每條指令都分配了編譯地址讼撒,如果編譯器已經(jīng)給分配了地址就可以直接進(jìn)行跳轉(zhuǎn)股耽,查找b函數(shù)跳轉(zhuǎn)指令所對(duì)應(yīng)的表物蝙,進(jìn)行直接跳轉(zhuǎn),因?yàn)橛袀€(gè)編譯地址和指令對(duì)應(yīng)的一個(gè)表册赛,如果沒(méi)有分配震嫉,編譯器就查找不到這個(gè)跳轉(zhuǎn)地址票堵,要進(jìn)行計(jì)算,非常麻煩窗宇。** 什么是《相對(duì)地址》特纤?** 以NOR Flash為例叫潦,NOR Falsh是映射到bank0上面官硝,SDRAM是映射到bank6上面氢架,uboot和內(nèi)核最終是在SDRAM上面運(yùn)行,最開(kāi)始我們是從Nor Flash的零地址開(kāi)始往后燒錄卿操,uboot中至少有一段代碼編譯地址和運(yùn)行地址是不一樣的,編譯uboot或內(nèi)核時(shí)扇雕,都會(huì)將編譯地址放入到SDRAM中窥摄,他們最終都會(huì)在SDRAM中執(zhí)行崭放,剛開(kāi)始uboot在Nor Flash中運(yùn)行,運(yùn)行地址是一個(gè)低端地址建峭,是bank0中的一個(gè)地址亿蒸,但編譯地址是bank6中的地址掌桩,這樣就會(huì)導(dǎo)致絕對(duì)跳轉(zhuǎn)指令執(zhí)行的失敗,所以就引出了相對(duì)地址的概念砚蓬。
** 那么什么是相對(duì)地址呢灰蛙?**
至少在bank0中uboot這段代碼要知道不能用b+編譯地址這樣的方法去跳轉(zhuǎn)指令隔躲,因?yàn)檫@段代碼的編譯地址和運(yùn)行地址不一樣宣旱,那如何去做呢?
要去計(jì)算這個(gè)指令運(yùn)行的真實(shí)地址笙纤,計(jì)算出來(lái)后再做跳轉(zhuǎn)省容,應(yīng)該是b+運(yùn)行地址燎字,不能出現(xiàn)b+編譯地址阿宅,而是b+運(yùn)行地址洒放,而運(yùn)行地址是算出來(lái)的滨砍。 _TEXT_BASE: .word TEXT_BASE //0x33F80000,在board/config.mk中這段話表示惨好,用戶告訴編譯器編譯地址的起始地址
** U-Boot****工作過(guò)程**
大多數(shù) Boot Loader 都包含兩種不同的操作模式:"啟動(dòng)加載"模式和"下載"模式,這種區(qū)別僅對(duì)于開(kāi)發(fā)人員才有意義日川。
但從最終用戶的角度看,Boot Loader 的作用就是:用來(lái)加載操作系統(tǒng),而并不存在所謂的啟動(dòng)加載模式與下載工作模式的區(qū)別。 (一)啟動(dòng)加載(Boot loading)模式:這種模式也稱(chēng)為"自主"(Autonomous)模式回论。
也即 Boot Loader 從目標(biāo)機(jī)上的某個(gè)固態(tài)存儲(chǔ)設(shè)備上將操作系統(tǒng)加載到 RAM 中運(yùn)行,整個(gè)過(guò)程并沒(méi)有用戶的介入傀蓉。
這種模式是 Boot Loader 的正常工作模式,因此在嵌入式產(chǎn)品發(fā)布的時(shí)侯,Boot Loader 顯然必須工作在這種模式下职抡。
(二)下載(Downloading)模式:在這種模式下,目標(biāo)機(jī)上的 Boot Loader 將通過(guò)串口連接或網(wǎng)絡(luò)連接等通信手段從主機(jī)(Host)下載文件,比如:下載內(nèi)核映像和根文件系統(tǒng)映像等缚甩。從主機(jī)下載的文件通常首先被 Boot Loader保存到目標(biāo)機(jī)的RAM 中,然后再被 BootLoader寫(xiě)到目標(biāo)機(jī)上的FLASH類(lèi)固態(tài)存儲(chǔ)設(shè)備中。Boot Loader 的這種模式通常在第一次安裝內(nèi)核與根文件系統(tǒng)時(shí)被使用;此外,以后的系統(tǒng)更新也會(huì)使用 Boot Loader 的這種工作模式壕探。工作于這種模式下的 Boot Loader 通常都會(huì)向它的終端用戶提供一個(gè)簡(jiǎn)單的命令行接口李请。這種工作模式通常在第一次安裝內(nèi)核與跟文件系統(tǒng)時(shí)使用厉熟∏烀ǎ或者在系統(tǒng)更新時(shí)使用。進(jìn)行嵌入式系統(tǒng)調(diào)試時(shí)一般也讓bootloader工作在這一模式下嘁字。 U-Boot 這樣功能強(qiáng)大的 Boot Loader 同時(shí)支持這兩種工作模式,而且允許用戶在這兩種工作模式之間進(jìn)行切換纪蜒。
大多數(shù) bootloader 都分為階段 1(stage1)和階段 2(stage2)兩大部分,u-boot 也不例外此叠。依賴(lài)于 CPU 體系結(jié)構(gòu)的代碼(如 CPU 初始化代碼等)通常都放在階段 1 中且通常用匯編語(yǔ)言實(shí)現(xiàn),而階段 2 則通常用 C 語(yǔ)言來(lái)實(shí)現(xiàn),這樣可以實(shí)現(xiàn)復(fù)雜的功能,而且有更好的可讀性和移植性灭袁。