|
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源代碼的部分。現(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時(shí)使用的。 第二步:檢查當(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)目都有這些記錄的。可以直接忽略,主要是給維護(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的,暫時(shí)用不到,先不管。 (8)MAINTAINERS。維護(hù)者,就是當(dāng)前在參與維護(hù)uboot源碼的社區(qū)工作者。 (9)MAKEALL。一個腳本,應(yīng)該是幫助編譯uboot的。 (10)Makefile。這個很重要,是uboot源代碼的主Makefile,將來整個uboot被編譯時(shí)就是用這個Makefile管理編譯的,所以我們在下個課程中研究uboot配置編譯過程時(shí)就要分析這個Makefile。 (11)mk?焖倬幾g的腳本,其實(shí)就是先清理然后配置然后編譯而已。 (12)mkconfig。這個很重要,是uboot配置階段的主要配置腳本。uboot的可移植性很大程度就是靠這個配置腳本在維護(hù)的。我們在下個課程中研究uboot配置編譯過程時(shí)就要分析這個配置腳本。 (13)mkmovi。暫時(shí)不去管他,一個腳本,和iNand/SD卡啟動有關(guān) (14)README。所有的軟件都有README,一般拿到一個東西要先讀README,這個東西其實(shí)就是個簡單的使用說明書。 (15)rules.mk。這個文件是我們uboot的Makefile使用的規(guī)則,本身非常重要,但是我們不去分析他,不去看他。
文件夾介紹:
(1)api. 硬件無關(guān)的功能函數(shù)的API。uboot移植時(shí)基本不用管,這些函數(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系列。 注意:這個問價(jià)是嚴(yán)格和硬件相關(guān)的,因此移植時(shí)也是要注意的。但是因?yàn)檫@個文件夾內(nèi)都是SoC有關(guān)的,我們自己的開發(fā)板和三星的開發(fā)板雖然板子設(shè)計(jì)不同但是SoC都是同一個,因此實(shí)際移植時(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在管理頭文件時(shí)都采用了同一個思路,就是把所有的頭文件全部集中存放在include目錄下,而不是頭文件跟著自己對應(yīng)的c文件。所以在uboot中頭文件包含時(shí)路徑結(jié)構(gòu)要在這里去找。 (12)lib_開頭的一坨。(典型的lib_arm和lib_generic)架構(gòu)相關(guān)的庫文件。譬如lib_arm里面就是arm架構(gòu)使用的一些庫文件。lib_generic里是所有架構(gòu)通用的庫文件。這類文件夾中的內(nèi)容移植時(shí)基本不用管。 (13)libfdt。設(shè)備樹有關(guān)的。linux內(nèi)核在3.4左右的版本的時(shí)候更改了啟動傳參的機(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卡啟動,然后去看啟動時(shí)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)平時(shí)默認(rèn)編譯時(shí)命令行會打印出來很多編譯信息。但是有時(shí)候我們不希望看到這些編譯信息,就后臺編譯即可。這就叫靜默編譯。 (2)使用方法就是編譯時(shí)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)行處理,無法同時(shí)維護(hù)2個或2個以上的配置編譯方式。 (3)為了解決以上2種缺陷,uboot支持單獨(dú)輸出文件夾方式的編譯(linux kernel也支持,而且uboot的這種技術(shù)就是從linux kernel學(xué)習(xí)來的)。基本思路就是在編譯時(shí)另外指定一個輸出目錄,將來所有的編譯生成的.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í)的參數(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)上的交叉編譯工具鏈,只是前綴不一樣,后綴都是一樣的。因此定義時(shí)把前綴和后綴分開,只需要在定義前綴時(shí)區(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)用時(shí),我們可以在Makefile中去更改設(shè)置CROSS_COMPILE的值,也可以在編譯時(shí)用make CROSS_COMPILE=xxxx來設(shè)置,而且編譯時(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的編譯鏈接過程時(shí)就要考慮這個鏈接腳本。
11、TEXT_BASE(config.mk 156-158行) (1)Makefile中在配置X210開發(fā)板時(shí),在board/samsung/x210目錄下生成了一個文件config.mk,其中的內(nèi)容就是:TEXT_BASE = 0xc3e00000相當(dāng)于定義了一個變量。 (2)TEXT_BASE是將來我們整個uboot鏈接時(shí)指定的鏈接地址。因?yàn)閡boot中啟用了虛擬地址映射,因此這個C3E00000地址就等于0x23E00000(也可能是33E00000具體地址要取決于uboot中做的虛擬地址映射關(guān)系)。 (3)回顧裸機(jī)中講的鏈接地址的問題,再想想dnw方式先下載x210_usb.bin然后再下載uboot.bin時(shí)為什么第二個地址是23E00000.
12、自動推導(dǎo)規(guī)則(config.mk 239-256行) 我們在講Makefile時(shí)提到過自動推導(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ā)板后再次去配置時(shí)還可以配置。 (4)我們配置開發(fā)板時(shí)使用: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ā)板的編譯時(shí)用符號連接的方式提供一個具體的名字的文件夾供編譯時(shí)使用。這樣就可以在配置的過程中通過不同的配置使用不同的文件,就可以正確的包含正確的文件。 (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個符號鏈接將來在寫代碼過程中,頭文件包含時(shí)非常有用。譬如一個頭文件包含可能是:#include <asm/xx.h> (7)創(chuàng)建include/config.mk文件(mkconfig文件123-129行) (8)創(chuàng)建include/config.mk文件是為了讓主Makefile在第133行去包含的。 (9)思考:uboot的配置和編譯過程的配合。編譯的時(shí)候需要ARCH=arm、CPU=xx等這些變量來指導(dǎo)編譯,配置的時(shí)候就是為編譯階段提供這些變量。那為什么不在Makefile中直接定義這些變量去使用,而要在mkconfig腳本中創(chuàng)建config.mk文件然后又在Makefile中include這些文件呢? 個人理解是為了uboot的可移植性。 (10)理解這些腳本時(shí),時(shí)刻要注意自己當(dāng)前所處的路徑。 (11)創(chuàng)建(默認(rèn)情況)/追加(make -a時(shí)追加)include/config.h文件(mkconfig文件的134-141行)。 (12)這個文件里面的內(nèi)容就一行#include <configs/x210_sd.h>,這個頭文件是我們移植x210開發(fā)板時(shí),對開發(fā)板的宏定義配置文件。這個文件是我們移植x210時(shí)最主要的文件。 (13)x210_sd.h文件會被用來生成一個autoconfig.mk文件,這個文件會被主Makefile引入,指導(dǎo)整個編譯過程。這里面的這些宏定義會影響我們對uboot中大部分.c文件中一些條件編譯的選擇。從而實(shí)現(xiàn)最終的可移植性。 注意:uboot的整個配置過程,很多文件之間是有關(guān)聯(lián)的(有時(shí)候這個文件是在那個文件中創(chuàng)建出來的;有時(shí)候這個文件被那個文件包含進(jìn)去;有時(shí)候這個文件是由那個文件的內(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移植時(shí)的配置文件。這里面是好多宏。 因此這個頭文件包含將include/configs/x210_sd.h文件和start.S文件關(guān)聯(lián)了起來。因此之后在分析start.S文件時(shí),主要要考慮的就是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目錄是配置時(shí)創(chuàng)建的一個符號鏈接,實(shí)際指向的是就是asm-arm(詳解上一章節(jié)分析mkconfig腳本時(shí)). (5)經(jīng)過分析后發(fā)現(xiàn),實(shí)際文件是:include/asm-arm/proc-armv/domain.h (6)從這里可以看出之前配置時(shí)創(chuàng)建的符號鏈接的作用,如果沒有這些符號鏈接則編譯時(shí)根本通不過,因?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中源代碼不用改,只需要在具體的硬件移植時(shí)配置不同,創(chuàng)建的符號鏈接指向的不同,則可以具有可移植性。
3、uboot匯編部分
1、啟動代碼的16字節(jié)頭部
(1)裸機(jī)中講過,在SD卡啟動/Nand啟動等整個鏡像開頭需要16字節(jié)的校驗(yàn)頭。(mkv210image.c中就是為了計(jì)算這個校驗(yàn)頭)。我們以前做裸機(jī)程序時(shí)根本沒考慮這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)為什么要對齊訪問?有時(shí)候是效率的要求,有時(shí)候是硬件的特殊要求。
4、TEXT_BASE等 (1)第100行這個TEXT_BASE就是上個課程中分析Makefile時(shí)講到的那個配置階段的TEXT_BASE,其實(shí)就是我們鏈接時(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ù)位時(shí)默認(rèn)就會進(jìn)入SVC模式,但是這里還是使用軟件將其置為SVC模式。整個uboot工作時(shí)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ù)字等于某個特定值時(shí)就表示SD啟動,等于另一個特定值時(shí)表示從Nand啟動 |
|