|
Uboot簡單介紹, 1、認(rèn)識uboot
U-Boot,全稱 Universal Boot Loader,是遵循GPL條款是一個開源項(xiàng)目,用于啟動操作系統(tǒng)內(nèi)核,操作系統(tǒng)并不是一開機(jī)就會自動啟動,是要有引導(dǎo)程序的。uboot就是一個這樣的開源的引導(dǎo)程序。uboot的官方網(wǎng)站:http://www.denx.de/wiki/U-Boot/SourceCode uboot早期的版本號是這樣的 uboot1.4.3,后來是是這樣的uboot-2018.11.
2、使用uboot
我們可以在uboot啟動過程中進(jìn)入uboot的控制臺底下,使用uboot。所謂的使用uboot也就是使用uboot的環(huán)境變量和命令。ping
3、配置編譯下載uboot
這里說一下如何配置編譯下載uboot,后面的部分為詳細(xì)講解uboot源代碼的部分,F(xiàn)在最新版本的uboot的配置方法和Linux內(nèi)核的配置方法是一樣的,后面我會有Linux內(nèi)核配置的文章。
配置
uboot和linux kernel等復(fù)雜項(xiàng)目,都不能直接編譯,都要先配置才能編譯。uboot也要先配置,配置方法是:首先cd進(jìn)入uboot源碼的根目錄,然后在根目錄下執(zhí)行:make x210_sd_config(就看Makefile中的那個目標(biāo)來確定,其實(shí)也就是include/configs/目錄下的配置文件后面加上_config,比如我這里include/configs/x210_sd.h,所以這里就是make x210_sd_config)。執(zhí)行配置命令后,如果出現(xiàn):Configuring for x210_sd board…說明配置好了,如果不是這個是別的說明配置出錯了。
編譯
編譯之前一定要注意檢查arm-linux-gcc對不對,檢查份2步: 第一步:檢查當(dāng)前編譯環(huán)境中有沒有安裝合適的arm-linux-gcc。我們裝的是arm-2009q3,因?yàn)檫@個是三星官方、九鼎官方開發(fā)uboot時使用的。 第二步:檢查當(dāng)前目錄下(uboot根目錄)的Makefile中編譯器的設(shè)置是否正確。在工程的總Makefile中會設(shè)置交叉編譯工具鏈的路徑和名字,必須確保這個路徑和名字和我們自己裝的一致,否則編譯會出錯。 確保了以上2點(diǎn),即可進(jìn)行編譯。編譯很簡單,直接make即可;蛘呖梢詍ake -j4 (多線程編譯,主機(jī)如果是多核心電腦,可以嘗試多線程編譯,會快一些)
下載
uboot根目錄下有一個sd_fusing文件夾,進(jìn)去之后先檢查sd_fusing.sh的執(zhí)行權(quán)限有沒有,文件中的bin文件與根目錄中生成的bin文件是否一致,文件中的reader_type1=“/dev/sdc“ 中是否是對應(yīng)的自己的SD卡,make clean,然后make,然后插入SD卡。執(zhí)行./sd_fusing.sh /dev/sdc,即可
uboot中各個文件夾的介紹
文件介紹
(1).gitignore。git工具的文件,git是一個版本管理工具(類似的還有個svn),這個文件和git有關(guān),和uboot本身無關(guān)的,不用去管。 (2)arm_config.mk。后綴是.mk,是一個Makefile文件,將來在某個Makefile中會去調(diào)用它。 (3)三個Changelog文件,修改記錄文件,該文件記錄了這個uboot項(xiàng)目的版本變遷以及每個版本較上個版本修改的記錄。正式的項(xiàng)目都有這些記錄的?梢灾苯雍雎裕饕墙o維護(hù)uboot的人用的。 (4)config.mk。和arm_config.mk差不多性質(zhì)。 (5)COPYING。版權(quán)聲明,uboot本身是GPL許可證的。 (6)CREDITS。鳴謝,里面記錄了對uboot有貢獻(xiàn)的人,感謝目錄。 (7)image_split。一個腳本,看說明是用來分割uboot.bin到BL1的,暫時用不到,先不管。 (8)MAINTAINERS。維護(hù)者,就是當(dāng)前在參與維護(hù)uboot源碼的社區(qū)工作者。 (9)MAKEALL。一個腳本,應(yīng)該是幫助編譯uboot的。 (10)Makefile。這個很重要,是uboot源代碼的主Makefile,將來整個uboot被編譯時就是用這個Makefile管理編譯的,所以我們在下個課程中研究uboot配置編譯過程時就要分析這個Makefile。 (11)mk?焖倬幾g的腳本,其實(shí)就是先清理然后配置然后編譯而已。 (12)mkconfig。這個很重要,是uboot配置階段的主要配置腳本。uboot的可移植性很大程度就是靠這個配置腳本在維護(hù)的。我們在下個課程中研究uboot配置編譯過程時就要分析這個配置腳本。 (13)mkmovi。暫時不去管他,一個腳本,和iNand/SD卡啟動有關(guān) (14)README。所有的軟件都有README,一般拿到一個東西要先讀README,這個東西其實(shí)就是個簡單的使用說明書。 (15)rules.mk。這個文件是我們uboot的Makefile使用的規(guī)則,本身非常重要,但是我們不去分析他,不去看他。
文件夾介紹:
(1)api. 硬件無關(guān)的功能函數(shù)的API。uboot移植時基本不用管,這些函數(shù)是uboot本身使用的。 (2)api_examples. API相關(guān)的測試事例代碼。 (3)board。board是板的意思,板就是開發(fā)板。board文件夾下每一個文件都代表一個開發(fā)板,這個文件夾下面放的文件就是用來描述這一個開發(fā)板的信息的。board目錄下有多少個文件夾,就表示當(dāng)前這個uboot已經(jīng)被移植到多少個開發(fā)板上了(當(dāng)前的uboot支持多少個開發(fā)板)。 (4)common。common是普遍的普通的,這個文件夾下放的是一些與具體硬件無關(guān)的普遍適用的一些代碼。譬如控制臺實(shí)現(xiàn)、crc校驗(yàn)的。但是更多的主要是兩類: 一類是cmd開頭的,是用來實(shí)現(xiàn)uboot的命令系統(tǒng)的;另一類是env開頭的,是用來實(shí)現(xiàn)環(huán)境變量的。 (5)cpu。這個目錄是SoC相關(guān)的,里面存放的代碼都是SoC相關(guān)初始化和控制代碼(譬如CPU的、中斷的、串口等SoC內(nèi)部外設(shè)的,包括起始代碼start.S也在這里)。里面很多子文件夾,每一個子文件夾就是一個SoC系列。 注意:這個問價是嚴(yán)格和硬件相關(guān)的,因此移植時也是要注意的。但是因?yàn)檫@個文件夾內(nèi)都是SoC有關(guān)的,我們自己的開發(fā)板和三星的開發(fā)板雖然板子設(shè)計(jì)不同但是SoC都是同一個,因此實(shí)際移植時這個目錄幾乎不用動。 (6)disk。磁盤有關(guān)的,沒研究過,沒用過。 (7)doc。文檔目錄,里面存放了很多uboot相關(guān)文檔,這些文檔可以幫助我們理解uboot代碼。但是因?yàn)槭羌冇⑽牡模液茈s亂,所以幾乎沒用。 (8)drivers。顧名思義,驅(qū)動。這里面放的就是從linux源代碼中扣出來的原封不動的linux設(shè)備驅(qū)動,主要是開發(fā)板上必須用到的一些驅(qū)動,如網(wǎng)卡驅(qū)動、Inand/SD卡、NandFlash等的驅(qū)動。要知道:uboot中的驅(qū)動其實(shí)就是linux中的驅(qū)動,uboot在一定程度上移植了linux的驅(qū)動給自己用。但是linux是操作系統(tǒng)而uboot只是個裸機(jī)程序,因此這種移植會有不同,讓我說:uboot中的驅(qū)動其實(shí)是linux中的驅(qū)動的一部分。 (9)examples。示例代碼,沒用過。 (10)fs。filesystem,文件系統(tǒng)。這個也是從linux源代碼中移植過來的,用來管理Flash等資源。 (11)include。頭文件目錄。uboot和linux kernel在管理頭文件時都采用了同一個思路,就是把所有的頭文件全部集中存放在include目錄下,而不是頭文件跟著自己對應(yīng)的c文件。所以在uboot中頭文件包含時路徑結(jié)構(gòu)要在這里去找。 (12)lib_開頭的一坨。(典型的lib_arm和lib_generic)架構(gòu)相關(guān)的庫文件。譬如lib_arm里面就是arm架構(gòu)使用的一些庫文件。lib_generic里是所有架構(gòu)通用的庫文件。這類文件夾中的內(nèi)容移植時基本不用管。 (13)libfdt。設(shè)備樹有關(guān)的。linux內(nèi)核在3.4左右的版本的時候更改了啟動傳參的機(jī)制,改用設(shè)備樹來進(jìn)行啟動傳參,進(jìn)行硬件信息的描述了。 (14)nand_spl。nand相關(guān)的,不講。 (15)net。網(wǎng)絡(luò)相關(guān)的代碼,譬如uboot中的tftp nfs ping命令 都是在這里實(shí)現(xiàn)的。 (16)onenand開頭的,是onenand相關(guān)的代碼,是三星加的,標(biāo)準(zhǔn)uboot中應(yīng)該是沒有的。 (17)post。沒關(guān)注過,不知道干嘛的。 (18)sd_fusing。這里面代碼實(shí)現(xiàn)了燒錄uboot鏡像到SD卡的代碼。后面要仔細(xì)研究的。 (19)tools。里面是一些工具類的代碼。譬如mkimage。
uboot主Makefile分析
1、uboot version確定(Makefile的24-29行) (1)uboot的版本號分3個級別: VERSION:主板本號 PATCHLEVEL:次版本號 SUBLEVEL:再次版本號 EXTRAVERSION:另外附加的版本信息 這4個用.分隔開共同構(gòu)成了最終的版本號。 (2)Makefile中版本號最終生成了一個變量U_BOOT_VERSION,這個變量記錄了Makefile中配置的版本號。 (3)include/version_autogenerated.h文件是編譯過程中自動生成的一個文件,所以源目錄中沒有,但是編譯過后的uboot中就有了。它里面的內(nèi)容是一個宏定義,宏定義的值內(nèi)容就是我們在Makefile中配置的uboot的版本號。 (4)驗(yàn)證方法:自己修改主Makefile中幾個Version有關(guān)的變量,然后重新編譯uboot,然后燒錄到SD卡中,從SD卡啟動,然后去看啟動時uboot打印出來的版本信息,看看變化是不是和自己的分析一致。
2、HOSTARCH和HOSTOS (1)直接在shell中執(zhí)行uname -m得到i686,得到的值其實(shí)你當(dāng)前執(zhí)行這個命令的電腦的CPU的版本號。 (2)shell中的 叫做管道,管道的作用就是把管道前面一個運(yùn)算式的輸出作為后面一個的輸入再去做處理,最終的輸出才是我們整個式子的輸出。 (3)HOSTARCH這個名字:HOST是主機(jī),就是當(dāng)前在做開發(fā)用的這臺電腦就叫主機(jī);ARCH是architecture(架構(gòu))的縮寫,表示CPU的架構(gòu)。所以HOSTARCH就表示主機(jī)的CPU的架構(gòu)。 (4)這兩個環(huán)境變量是主機(jī)的操作系統(tǒng)和主機(jī)的CPU架構(gòu),得出后保存?zhèn)溆,后面自然會用到?
3、靜默編譯(50-54行) (1)平時默認(rèn)編譯時命令行會打印出來很多編譯信息。但是有時候我們不希望看到這些編譯信息,就后臺編譯即可。這就叫靜默編譯。 (2)使用方法就是編譯時make -s,-s會作為MAKEFLAGS傳給Makefile,在50-54行這段代碼作用下XECHO變量就會被變成空(默認(rèn)等于echo),于是實(shí)現(xiàn)了靜默編譯。
4、2種編譯方法(原地編譯和單獨(dú)輸出文件夾編譯) (1)編譯復(fù)雜項(xiàng)目,Makefile提供2種編譯管理方法。默認(rèn)情況下是當(dāng)前文件夾中的.c文件,編譯出來的.o文件會放在同一文件夾下。這種方式叫原地編譯。原地編譯的好處就是處理起來簡單。 (2)原地編譯有一些壞處:第一,污染了源文件目錄。第二的缺陷就是一套源代碼只能按照一種配置和編譯方法進(jìn)行處理,無法同時維護(hù)2個或2個以上的配置編譯方式。 (3)為了解決以上2種缺陷,uboot支持單獨(dú)輸出文件夾方式的編譯(linux kernel也支持,而且uboot的這種技術(shù)就是從linux kernel學(xué)習(xí)來的);舅悸肪褪窃诰幾g時另外指定一個輸出目錄,將來所有的編譯生成的.o文件或生成的其他文件全部丟到那個輸出目錄下去。源代碼目錄不做任何污染,這樣輸出目錄就承載了本次配置編譯的所有結(jié)果。 (4)具體用法:默認(rèn)的就是原地編譯。如果需要指定具體的輸出目錄編譯則有2種方式來指定輸出目錄。(具體參考Makefile 56-76行注釋內(nèi)容) 第一種:make O=輸出目錄 第二種:export BUILD_DIR=輸出目錄 然后再make ,如果兩個都指定了(既有BUILD_DIR環(huán)境變量存在,又有O=xx),則O=xx具有更高優(yōu)先級,聽他的。 (5)兩種編譯的實(shí)現(xiàn)代碼在Makefile的78-123行。
5、OBJTREE、SRCTREE、TOPDIR (1)OBJTREE:編譯出的.o文件存放的目錄的根目錄。在默認(rèn)編譯下,OBJTREE等于當(dāng)前目錄;在O=xx編譯下,OBJTREE就等于我們設(shè)置的那個輸出目錄。 (2)SRCTREE: 源碼目錄,其實(shí)就是源代碼的根目錄,也就是當(dāng)前目錄。 總結(jié):在默認(rèn)編譯下,OBJTREE和SRCTREE相等;在O=xx這種編譯下OBJTREE和SRCTREE不相等。Makefile中定義這兩個變量,其實(shí)就是為了記錄編譯后的.o文件往哪里放,就是為了實(shí)現(xiàn)O=xx的這種編譯方式的。
6、MKCONFIG(Makefile的101行) (1)Makefile中定義的一個變量(在這里定義,在后面使用),它的值就是我們源碼根目錄下面的mkconfig。 這個mkconfig是一個腳本,這個腳本就是uboot配置階段的配置腳本。后面仔細(xì)說
7、include $(obj)include/config.mk(133行) (1)include/config.mk不是源碼自帶的(你在沒有編譯過的源碼目錄下是找不到這個文件的),要在配置過程(make x210_sd_config)中才會生成這個文件。因此這個文件的值和我們配置過程有關(guān),是由配置過程根據(jù)我們的配置自動生成的。 (2)我們X210在iNand情況下配置生成的config.mk內(nèi)容為: ARCH = arm CPU = s5pc11x BOARD = x210 VENDOR = samsung SOC = s5pc110 (3)我們在下一行(134行)export導(dǎo)出了這5個變量作為環(huán)境變量。所以著兩行加起來其實(shí)就是為當(dāng)前makefile定義了5個環(huán)境變量而已。之所以不直接給出這5個環(huán)境變量的值,是因?yàn)槲覀兿M@5個值是可以被人很容易的、集中的配置的。 (4)這里的配置值來自于2589行那里的配置項(xiàng)。如果我們要更改這里的某個配置值要到2589行那里調(diào)用MKCONFIG腳本傳參時的參數(shù)。
8、ARCH CROSS_COMPILE (1)接下來有2個很重要的環(huán)境變量。一個是ARCH,上面導(dǎo)出的,值來自于我們的配置過程,它的值會影響后面的CROSS_COMPILE環(huán)境變量的值。ARCH的意義是定義當(dāng)前編譯的目標(biāo)CPU的架構(gòu)。 (2)CROSS_COMPILE是定義交叉編譯工具鏈的前綴的。定義這些前綴是為了在后面用(用前綴加上后綴來定義編譯過程中用到的各種工具鏈中的工具)。我們把前綴和后綴分開還有一個原因就是:在不同CPU架構(gòu)上的交叉編譯工具鏈,只是前綴不一樣,后綴都是一樣的。因此定義時把前綴和后綴分開,只需要在定義前綴時區(qū)分各種架構(gòu)即可實(shí)現(xiàn)可移植性。 (3)CROSS_COMPILE在136-182行來確定。CROSS_COMPILE是被ARCH所確定的,只要配置了ARCH=arm,那么我們就只能在ARM的那個分支去設(shè)置CROSS_COMPILE的值。這個設(shè)置值只要能保證找到那個交叉編譯工具鏈即可,不一定非得是全路徑的,相對路徑也可以。(如果已經(jīng)將工具鏈導(dǎo)出到環(huán)境變量,并且設(shè)置了符號鏈接,這樣CROSS_COMPILE = arm-linux-就可以) (4)實(shí)際運(yùn)用時,我們可以在Makefile中去更改設(shè)置CROSS_COMPILE的值,也可以在編譯時用make CROSS_COMPILE=xxxx來設(shè)置,而且編譯時傳參的方法可以覆蓋Makefile里面的設(shè)置。
9、$(TOPDIR)/config.mk(主Makefile的185行) 編譯工具定義(config.mk 94-107行) 包含開發(fā)板配置項(xiàng)目(config.mk, 112行) (1)autoconfig.mk文件不是源碼提供的,是配置過程自動生成的。 (2)這個文件的作用就是用來指導(dǎo)整個uboot的編譯過程。這個文件的內(nèi)容其實(shí)就是很多CONFIG_開頭的宏(可以理解為變量),這些宏/變量會影響我們uboot編譯過程的走向(原理就是條件編譯)。在uboot代碼中有很多地方使用條件編譯進(jìn)行編寫,這個條件編譯是用來實(shí)現(xiàn)可移植性的。(可以說uboot的源代碼在很大程度來說是拼湊起來的,同一個代碼包含了各種不同開發(fā)板的適用代碼,用條件編譯進(jìn)行區(qū)別。) (3)這個文件不是憑空產(chǎn)生的,配置過程也是需要原材料來產(chǎn)生這個文件的。原材料在源碼目錄的inlcude/configs/xxx.h頭文件。(X210開發(fā)板中為include/configs/x210_sd.h)。這個h頭文件里面全都是宏定義,這些宏定義就是我們對當(dāng)前開發(fā)板的移植。每一個開發(fā)板的移植都對應(yīng)這個目錄下的一個頭文件,這個頭文件里每一個宏定義都很重要,這些配置的宏定義就是我們移植uboot的關(guān)鍵所在。
10、鏈接腳本(config.mk 142-149行) (1)如果定義了CONFIG_NAND_U_BOOT宏,則鏈接腳本叫u-boot-nand.lds,如果未定義這個宏則鏈接腳本叫u-boot.lds。 (2)從字面意思分析,即可知:CONFIG_NAND_U_BOOT是在Nand版本情況下才使用的,我們使用的X210都是iNand版本的,因此這個宏沒有的。 (3)實(shí)際在boardsamsungx210目錄下有u-boot.lds,這個就是鏈接腳本。我們在分析uboot的編譯鏈接過程時就要考慮這個鏈接腳本。
11、TEXT_BASE(config.mk 156-158行) (1)Makefile中在配置X210開發(fā)板時,在board/samsung/x210目錄下生成了一個文件config.mk,其中的內(nèi)容就是:TEXT_BASE = 0xc3e00000相當(dāng)于定義了一個變量。 (2)TEXT_BASE是將來我們整個uboot鏈接時指定的鏈接地址。因?yàn)閡boot中啟用了虛擬地址映射,因此這個C3E00000地址就等于0x23E00000(也可能是33E00000具體地址要取決于uboot中做的虛擬地址映射關(guān)系)。 (3)回顧裸機(jī)中講的鏈接地址的問題,再想想dnw方式先下載x210_usb.bin然后再下載uboot.bin時為什么第二個地址是23E00000.
12、自動推導(dǎo)規(guī)則(config.mk 239-256行) 我們在講Makefile時提到過自動推導(dǎo)規(guī)則
13、Makefile的目標(biāo) (1)291行出現(xiàn)了整個主Makefile中第一個目標(biāo)all(也就是默認(rèn)目標(biāo),我們直接在uboot根目錄下make其實(shí)就等于make all,就等于make這個目標(biāo)) (2)目標(biāo)中有一些比較重要的。譬如:u-boot是最終編譯鏈接生成的elf格式的可執(zhí)行文件, (3)unconfig字面意思來理解就是未配置。這個符號用來做為我們各個開發(fā)板配置目標(biāo)的依賴。目標(biāo)是當(dāng)我們已經(jīng)配置過一個開發(fā)板后再次去配置時還可以配置。 (4)我們配置開發(fā)板時使用:make x210_sd_config,因此分析x210_sd_config肯定是主Makefile中的一個目標(biāo)。
uboot配置過程詳解
(1)mkconfig腳本的6個參數(shù) $(@:_config=) arm s5pc11x x210 samsung s5pc110(Makefile中2590行) x210_sd_config里的_config部分用空替換,得到:x210_sd,這就是第一個參數(shù),所以: $1: x210_sd $2: arm $3: s5pc11x $4: x210 $5: samsumg $6: s5pc110 所以,$# = 6 (2)第23行:其實(shí)就是看BOARD_NAME變量是否有值,如果有值就維持不變;如果無值就給他賦值為$1,實(shí)際分析結(jié)果:BOARD_NAME=x210_sd (3)第25行:如果$#小于4,則exit 1(mkconfig腳本返回1) (4)第26行:如果$#大于6,則也返回1. 所以:mkconfig腳本傳參只能是4、5、6,如果大于6或者小于4都不行。 (5)從第33行到第118行,都是在創(chuàng)建符號鏈接。為什么要創(chuàng)建符號鏈接?這些符號鏈接文件的存在就是整個配置過程的核心,這些符號鏈接文件(文件夾)的主要作用是給頭文件包含等過程提供指向性連接。根本目的是讓uboot具有可移植性。uboot可移植性的實(shí)現(xiàn)原理:在uboot中有很多彼此平行的代碼,各自屬于各自不同的架構(gòu)/CPU/開發(fā)板,我們在具體到一個開發(fā)板的編譯時用符號連接的方式提供一個具體的名字的文件夾供編譯時使用。這樣就可以在配置的過程中通過不同的配置使用不同的文件,就可以正確的包含正確的文件。 (6)創(chuàng)建的符號鏈接: 第一個:在include目錄下創(chuàng)建asm文件,指向asm-arm。(46-48行) 第二個:在inlcude/asm-arm下創(chuàng)建一個arch文件,指向include/asm-arm/arch-s5pc110 第三個:在include目錄下創(chuàng)建regs.h文件,指向include/s5pc110.h 刪除第二個。 第四個:在inlcude/asm-arm下創(chuàng)建一個arch文件,指向include/asm-arm/arch-s5pc11x 第五個:在include/asm-arm下創(chuàng)建一個proc文件,指向include/asm-arm/proc-armv 總結(jié):一共創(chuàng)建了4個符號鏈接。這4個符號鏈接將來在寫代碼過程中,頭文件包含時非常有用。譬如一個頭文件包含可能是:#include <asm/xx.h> (7)創(chuàng)建include/config.mk文件(mkconfig文件123-129行) (8)創(chuàng)建include/config.mk文件是為了讓主Makefile在第133行去包含的。 (9)思考:uboot的配置和編譯過程的配合。編譯的時候需要ARCH=arm、CPU=xx等這些變量來指導(dǎo)編譯,配置的時候就是為編譯階段提供這些變量。那為什么不在Makefile中直接定義這些變量去使用,而要在mkconfig腳本中創(chuàng)建config.mk文件然后又在Makefile中include這些文件呢? 個人理解是為了uboot的可移植性。 (10)理解這些腳本時,時刻要注意自己當(dāng)前所處的路徑。 (11)創(chuàng)建(默認(rèn)情況)/追加(make -a時追加)include/config.h文件(mkconfig文件的134-141行)。 (12)這個文件里面的內(nèi)容就一行#include <configs/x210_sd.h>,這個頭文件是我們移植x210開發(fā)板時,對開發(fā)板的宏定義配置文件。這個文件是我們移植x210時最主要的文件。 (13)x210_sd.h文件會被用來生成一個autoconfig.mk文件,這個文件會被主Makefile引入,指導(dǎo)整個編譯過程。這里面的這些宏定義會影響我們對uboot中大部分.c文件中一些條件編譯的選擇。從而實(shí)現(xiàn)最終的可移植性。 注意:uboot的整個配置過程,很多文件之間是有關(guān)聯(lián)的(有時候這個文件是在那個文件中創(chuàng)建出來的;有時候這個文件被那個文件包含進(jìn)去;有時候這個文件是由那個文件的內(nèi)容生成的決定的) 注意:uboot中配置和編譯過程,所有的文件或者全局變量都是字符串形式的(不是指的C語言字符串的概念,指的是都是字符組成的序列)。這意味著我們整個uboot的配置過程都是字符串匹配的,所以一定要細(xì)節(jié),注意大小寫,要注意不要輸錯字符,因?yàn)橐坏╁e一個最后會出現(xiàn)一些莫名其妙的錯誤,很難排查,這個是uboot移植過程中新手來說最難的地方。
uboot的鏈接腳本
(1)uboot的鏈接腳本和我們之前裸機(jī)中的鏈接腳本并沒有本質(zhì)區(qū)別,只是復(fù)雜度高一些,文件多一些,使用到的技巧多一些。
(2)ENTRY(_start)用來指定整個程序的入口地址。所謂入口地址就是整個程序的開頭地址,可以認(rèn)為就是整個程序的第一句指令。有點(diǎn)像C語言中的main。
(3)之前在裸機(jī)中指定程序的鏈接地址有2種方法:一種是在Makefile中l(wèi)d的flags用-Ttext 0x20000000來指定;第二種是在鏈接腳本的SECTIONS開頭用.=0x20000000來指定。兩種都可以實(shí)現(xiàn)相同效果。其實(shí),這兩種技巧是可以共同配合使用的,也就是說既在鏈接腳本中指定也在ld flags中用-Ttext來指定。兩個都指定以后以-Ttext指定的為準(zhǔn)。
4、以啟動內(nèi)核為主線分析代碼
1、start.S引入
u-boot.lds中找到start.S入口 (1)在C語言中整個項(xiàng)目的入口就是main函數(shù)(這是C語言規(guī)定的),所以譬如說一個有10000個.c文件的項(xiàng)目,第一個要分析的文件就是包含了main函數(shù)的那個文件。 (2)在uboot中因?yàn)橛袇R編階段參與,因此不能直接找main.c。整個程序的入口取決于鏈接腳本中ENTRY聲明的地方。ENTRY(_start)因此_start符號所在的文件就是整個程序的起始文件,_start所在處的代碼就是整個程序的起始代碼。
2、不簡單的頭文件包含 (1)#include <config.h>。 config.h是在include目錄下的,這個文件不是源碼中本身存在的文件,而是配置過程中自動生成的文件。(詳見mkconfig腳本)。 這個文件的內(nèi)容其實(shí)是包含了一個頭文件:#include <configs/x210_sd.h>“. (2)經(jīng)過分析后,發(fā)現(xiàn)start.S中包含的第一個頭文件就是:include/configs/x210_sd.h,這個文件是整個uboot移植時的配置文件。這里面是好多宏。 因此這個頭文件包含將include/configs/x210_sd.h文件和start.S文件關(guān)聯(lián)了起來。因此之后在分析start.S文件時,主要要考慮的就是x210_sd.h文件。 (3)#include <version.h>。include/version.h中包含了include/version_autogenerated.h,這個頭文件就是配置過程中自動生成的。里面就一行內(nèi)容: #define U_BOOT_VERSION “U-Boot 1.3.4“。這里面定義的宏U_BOOT_VERSION的值是一個字符串,字符串中的版本號信息來自于Makefile中的配置值。這個宏在程序中會被調(diào)用,在uboot啟動過程中會串口打印出uboot的版本號,那個版本號信息就是從這來的。 (4)#include <asm/proc/domain.h>。asm目錄不是uboot中的原生目錄,uboot中本來是沒有這個目錄的。asm目錄是配置時創(chuàng)建的一個符號鏈接,實(shí)際指向的是就是asm-arm(詳解上一章節(jié)分析mkconfig腳本時). (5)經(jīng)過分析后發(fā)現(xiàn),實(shí)際文件是:include/asm-arm/proc-armv/domain.h (6)從這里可以看出之前配置時創(chuàng)建的符號鏈接的作用,如果沒有這些符號鏈接則編譯時根本通不過,因?yàn)檎也坏筋^文件。(所以uboot不能在windows的共享文件夾下配置編譯,因?yàn)閣indows中沒有符號鏈接) 思考:為什么start.S不直接包含asm-arm/proc-armv/domain.h,而要用asm/proc/domain.h。這樣的設(shè)計(jì)主要是為了可移植性。因?yàn)槿绻苯影,則start.S文件和CPU架構(gòu)(和硬件)有關(guān)了,可移植性就差了。譬如我要把uboot移植到mips架構(gòu)下,則start.S源代碼中所有的頭文件包含全部要修改。我們用了符號鏈接之后,則start.S中源代碼不用改,只需要在具體的硬件移植時配置不同,創(chuàng)建的符號鏈接指向的不同,則可以具有可移植性。
3、uboot匯編部分
1、啟動代碼的16字節(jié)頭部
(1)裸機(jī)中講過,在SD卡啟動/Nand啟動等整個鏡像開頭需要16字節(jié)的校驗(yàn)頭。(mkv210image.c中就是為了計(jì)算這個校驗(yàn)頭)。我們以前做裸機(jī)程序時根本沒考慮這16字節(jié)校驗(yàn)頭,因?yàn)椋?、如果我們是usb啟動直接下載的方式啟動的則不需要16字節(jié)校驗(yàn)頭(irom application note);2、如果是SD卡啟動mkv210image.c中會給原鏡像前加16字節(jié)的校驗(yàn)頭。 (2)uboot這里start.S中在開頭位置放了16字節(jié)的填充占位,這個占位的16字節(jié)只是保證正式的image的頭部確實(shí)有16字節(jié),但是這16字節(jié)的內(nèi)容是不對的,還是需要后面去計(jì)算校驗(yàn)和然后重新填充的。
2、異常向量表的構(gòu)建 (1)異常向量表是硬件決定的,軟件只是參照硬件的設(shè)計(jì)來實(shí)現(xiàn)它。 (2)異常向量表中每種異常都應(yīng)該被處理,否則真遇到了這種異常就跑飛了。但是我們在uboot中并未非常細(xì)致的處理各種異常。 (3)復(fù)位異常處的代碼是:b reset,因此在CPU復(fù)位后真正去執(zhí)行的有效代碼是reset處的代碼,因此reset符號處才是真正的有意義的代碼開始的地方。
3、有點(diǎn)意思的deadbeef (1).balignl 16,0xdeadbeef. 這一句指令是讓當(dāng)前地址對齊排布,如果當(dāng)前地址不對齊則自動向后走地址直到對齊,并且向后走的那些內(nèi)存要用0xdeadbeef來填充。 (2)0xdeadbeef這是一個十六進(jìn)制的數(shù)字,這個數(shù)字很有意思,組成這個數(shù)字的十六進(jìn)制數(shù)全是abcdef之中的字母,而且這8個字母剛好組成了英文的dead beef這兩個單詞,字面意思是壞牛肉 (3)為什么要對齊訪問?有時候是效率的要求,有時候是硬件的特殊要求。
4、TEXT_BASE等 (1)第100行這個TEXT_BASE就是上個課程中分析Makefile時講到的那個配置階段的TEXT_BASE,其實(shí)就是我們鏈接時指定的uboot的鏈接地址。(值就是c3e00000) (2)源代碼中和配置Makefile中很多變量是可以互相運(yùn)送的。簡單來說有些符號的值可以從Makefile中傳遞到源代碼中。 (1)CFG_PHY_UBOOT_BASE 33e00000 uboot在DDR中的物理地址
5、設(shè)置CPU為SVC模式 (1)msr cpsr_c, #0xd3 將CPU設(shè)置為禁止FIQ IRQ,ARM狀態(tài),SVC模式。 (2)其實(shí)ARM CPU在復(fù)位時默認(rèn)就會進(jìn)入SVC模式,但是這里還是使用軟件將其置為SVC模式。整個uboot工作時CPU一直處于SVC模式。
6、設(shè)置L2、L1cache和MMU (1)bl disable_l2cache // 禁止L2 cache (2)bl set_l2cache_auxctrl_cycle // l2 cache相關(guān)初始化 (3)bl enable_l2cache // 使能l2 cache (4)刷新L1 cache的icache和dcache。 (5)關(guān)閉MMU 總結(jié):上面這5步都是和CPU的cache和mmu有關(guān)的,不用去細(xì)看,大概知道即可。
7、識別并暫存啟動介質(zhì)選擇 (1)從哪里啟動是由SoC的OM5:OM0這6個引腳的高低電平?jīng)Q定的。 (2)實(shí)際上在210內(nèi)部有一個寄存器(地址是0xE0000004),這個寄存器中的值是硬件根據(jù)OM引腳的設(shè)置而自動設(shè)置值的。這個值反映的就是OM引腳的接法(電平高低),也就是真正的啟動介質(zhì)是誰。 (3)我們代碼中可以通過讀取這個寄存器的值然后判斷其值來確定當(dāng)前選中的啟動介質(zhì)是Nand還是SD還是其他的。 (4)start.S的225-227行執(zhí)行完后,在r2寄存器中存儲了一個數(shù)字,這個數(shù)字等于某個特定值時就表示SD啟動,等于另一個特定值時表示從Nand啟動 |
|