U-Boot 之三 U-Boot 源码文件解析及移植过程详解
??在之前的博文 Linux 之八 完整嵌入式 Linux 環(huán)境介紹及搭建說(shuō)明 中我們說(shuō)了要一步步搭建整個(gè)嵌入式 Linux 運(yùn)行環(huán)境。我所使用的硬件平臺(tái)及整個(gè)要搭建的嵌入式 Linux 環(huán)境見博文 Linux 之八 完整嵌入式 Linux 環(huán)境介紹及搭建說(shuō)明,這里的編譯都是基于以上環(huán)境的,就不過(guò)多說(shuō)明了。
??這篇博文我們僅僅關(guān)注 U-Boot 源碼及移植過(guò)程本身,想要吃透 U-Boot,有太多東西需要學(xué)習(xí)!最開始我想放到一篇文章中,寫著寫著內(nèi)容越來(lái)越多,最終超過(guò)了 CSDN 編輯器的限制。。。最終決定把內(nèi)容拆分成多篇文章。你可能需要:
U-Boot
??說(shuō)白了 U-Boot 其實(shí)就是一裸板程序,這個(gè)程序最主要的一個(gè)功能就是傳遞內(nèi)核參數(shù),跳轉(zhuǎn)內(nèi)核。當(dāng)然除了跳轉(zhuǎn)到內(nèi)核,U-Boot 本身還實(shí)現(xiàn)了其他一些功能(U-Boot 命令),以方便大家進(jìn)行各種操作。
??看過(guò)我之前的博文,或者使用過(guò) STM32 實(shí)現(xiàn)過(guò)在線升級(jí)的人應(yīng)該都知道,我們通常的在線升級(jí)是 IAP + APP 這個(gè)模式,其中的 IAP 一個(gè)主要功能就是跳轉(zhuǎn)到 APP,這就和 U-Boot 功能是一樣的。
??注意,本身無(wú)論是 U-Boot 還是 Linux Kernel,他們都支持多種架構(gòu)的多種 CPU,也因此,代碼中會(huì)有各種架構(gòu)各種 CPU 相關(guān)的代碼,我后續(xù)的內(nèi)容主要以 STM32F769I 為例來(lái)進(jìn)行說(shuō)明。STM32F769I 采用的是 ARM Cortex-M7 的核心,指令集架構(gòu)是 ARMv7m。
??在眾多支持中,ARM 是最麻煩的一個(gè)。因?yàn)?ARM 賣 IP 且市場(chǎng)占有率相當(dāng)高,導(dǎo)致產(chǎn)生了非常多的 ARM 核心的廠商,這些廠商會(huì)有自己的改動(dòng),進(jìn)一步導(dǎo)致了 U-Boot 的 ARM 架構(gòu)文件夾(./arch/arm)下有非常多的 mach-xxx 文件夾。
??通過(guò)上面的圖我們可以知道,U-Boot 對(duì)于眾多架構(gòu)的支持已經(jīng)到達(dá)了開發(fā)板級(jí)別。對(duì)于一些常用的開發(fā)板,U-Boot 直接實(shí)現(xiàn)了對(duì)他們的支持,也就意味著 U-Boot 可以直接在這些開發(fā)板上運(yùn)行。具體到 ST 系列則有以下這些被支持:
正好 U-Boot 沒有提供對(duì)于我使用的 STM32F769i-EVAL 板子的支持,后續(xù)的移植章節(jié)我就添加對(duì)于 STM32F769i-EVAL 板子的支持。以此來(lái)介紹 U-Boot 的具體移植步驟。
SPL/TPL
??SPL 即 Secondary Program Loader 的縮寫,中文就是第二段程序加載器。這里的第二段程序其實(shí)就是指的 U-Boot,也就是,SPL 是第一段程序,優(yōu)先執(zhí)行,然后他再去加載 U-Boot。那么 U-Boot 本身已經(jīng)是一個(gè)bootloader了,為啥要有 SPL 這個(gè)東西的存在呢?
??這個(gè)主要原因是對(duì)于一些 MCU 來(lái)說(shuō),它的內(nèi)部 SRAM 可能會(huì)比較小,小到無(wú)法裝載下一個(gè)完整的 U-Boot 鏡像,那么就需要SPL,它主要負(fù)責(zé)初始化外部 RAM 運(yùn)行環(huán)境,并加載真正的 U-Boot 鏡像到外部 RAM 中來(lái)執(zhí)行。這里其實(shí)還有個(gè)問(wèn)題需要注意,U-Boot 在設(shè)計(jì)上需要將自身(并不一定是所有代碼)復(fù)制到 RAM 執(zhí)行的!
??SPL 并不一定需要。具體需不需要 SPL 這個(gè)和芯片的設(shè)計(jì)有關(guān)系(或者說(shuō)和使用的開發(fā)板有關(guān)系)。同樣以我這里使用 STM32F769i-EVAL 板子來(lái)說(shuō),就是需要 SPL 的。具體參看 使用章節(jié)的說(shuō)明。
??TPL 即 Tertiary Program Loader 的縮寫,中文就是第三段程序加載器。根據(jù)官方文檔,TPL 本身屬于 SPL 的精簡(jiǎn),代碼就在 SPL 代碼中,通過(guò)宏 CONFIG_TPL_BUILD 來(lái)區(qū)分,而且,現(xiàn)在只有 powerpc 的 mpc85xx 有這個(gè)要求并將實(shí)現(xiàn)它。TPL 我之前也沒接觸過(guò),不是很了解,有了解的歡迎評(píng)論區(qū)給出指導(dǎo)。
源碼目錄說(shuō)明
??U-Boot 源碼的的文檔全部位于源碼根目錄的 doc 目錄下。官方網(wǎng)站上也有非常詳細(xì)的文檔 http://www.denx.de/wiki/U-Boot/Documentation。不過(guò),源碼根目錄下的 README 應(yīng)該算是一個(gè)最詳細(xì)的介紹文檔了。源碼中各文件的層級(jí)結(jié)構(gòu)可以參考下圖:
下面是對(duì) U-Boot 源代碼中各個(gè)目錄的一個(gè)簡(jiǎn)介:
編譯產(chǎn)生的最終文件
??成功編譯之后,就會(huì)在 U-Boot 源碼的根目錄下產(chǎn)生多個(gè)可執(zhí)行二進(jìn)制文件以及編譯過(guò)程文件,這些文件都是 u-boot.xxx 的命名方式。這些文件由一些列名為 .xxx.cmd 的文件生成,.xxx.cmd 這些文件都是由編譯系統(tǒng)產(chǎn)生的用于處理最終的可執(zhí)行程序的。注意,這里寫文件有沒有與 make menuconfig 中的配置有關(guān)系。
- u-boot: 這個(gè)文件是編譯后產(chǎn)生的 ELF 格式的 U-Boot 鏡像文件,后續(xù)的文件都是由它產(chǎn)生的!由 .u-boot.cmd 這個(gè)命令腳本產(chǎn)生。
- u-boot-nodtb.bin: 這文件是使用編譯工具鏈的 objcopy 工具從 u-boot 這個(gè)文件中提取來(lái)的,它只包含可執(zhí)行的二進(jìn)制代碼。就是把 u-boot 這個(gè)文件中對(duì)于執(zhí)行不需要的節(jié)區(qū)刪除后剩余的僅執(zhí)行需要的部分。由 .u-boot-nodtb.bin.cmd 這個(gè)命令腳本產(chǎn)生。
- u-boot.bin: 就是把 u-boot-nodtb.bin 重命名得到的。由 .u-boot.bin.cmd 這個(gè)命令腳本產(chǎn)生。
- u-boot-dtb.bin: 在 u-boot-nodtb.bin 后面拼接上設(shè)備樹后形成的文件。由 .u-boot-dtb.bin.cmd 這個(gè)命令腳本產(chǎn)生。
- u-boot.img: 在 u-boot-nodtb.bin 后面拼接上設(shè)備樹后形成的文件。由 .u-boot.img.cmd 這個(gè)命令腳本產(chǎn)生。
- u-boot-nodtb.img: 由 .u-boot-nodtb.bin.cmd 這個(gè)命令腳本產(chǎn)生。
- u-boot-dtb.img: 由 .u-boot.img.cmd 這個(gè)命令腳本產(chǎn)生。
- u-boot.srec: S-Record 格式的鏡像文件。由 .u-boot.srec.cmd 這個(gè)命令腳本產(chǎn)生。
- u-boot.sym: 編譯過(guò)程中的符號(hào)文件。由 .u-boot.sym.cmd 這個(gè)命令腳本產(chǎn)生。
- u-boot.lds: 編譯使用的鏈接腳本文件。由 .u-boot.lds.cmd 這個(gè)命令腳本產(chǎn)生。
- u-boot.map: 編譯的內(nèi)存映射文件。
Kconfig 及 Makefile 文件
??Kconfig 及 Makefile 文件幾乎在每個(gè)目錄下都會(huì)有,關(guān)于這些文件在博文 U-Boot 之四 配置構(gòu)建過(guò)程(Kconfig、Kuild)詳解 中單獨(dú)進(jìn)行說(shuō)明。
移植過(guò)程
??要使用 U-Boot,首先要確定 U-Boot 是否支持我們的使用芯片(開發(fā)板)。這就需要查看 ./config 目錄下有沒有對(duì)應(yīng)的配置文件,或者說(shuō)有沒有類似的配置文件。如果直接有(對(duì)于一些通用的平臺(tái),U-Boot 已經(jīng)添加好了一些默認(rèn)配置),那么恭喜可以省事很多;如果沒有(如果是自己畫的板子,指定是沒有),后續(xù)就牽扯到自己移植修改代碼。
??正好 U-Boot 沒有提供對(duì)于我使用的 STM32F769i-EVAL 板子的支持,我這里就添加對(duì)于 STM32F769i-EVAL 板子的支持。以此來(lái)介紹移植過(guò)程。移植后的 U-Boot 代碼放到了Github 上:https://github.com/ZCShou/U-Boot-STM32。具體步驟如下:
??具體到我這里使用的 STM32F769i-EVAL 板子,使用的 STM32F769 設(shè)備樹沒有,但是有個(gè)類似的 STM32F769i-disco 的設(shè)備樹,所以我這里就依據(jù) STM32F769i-disco 添加 STM32F769-eval 相關(guān)設(shè)備樹。具體更改如下:
其中 stm32f769.dtsi 就是 stm32f746.dtsi 改名字,下面是各設(shè)備數(shù)文件的包含關(guān)系示意圖:
這里有個(gè)重點(diǎn)注意事項(xiàng)就是 其中的 compatible 的內(nèi)容,驅(qū)動(dòng)使用該項(xiàng)的內(nèi)容來(lái)進(jìn)行匹配,隨意更改可能導(dǎo)致驅(qū)動(dòng)無(wú)法識(shí)別!!舉例如下:
??具體到我這里使用的 STM32F769i-EVAL 板子,原來(lái)已經(jīng)存在 arch/arm/mach-stm32/stm32f7/Kconfig 了,我只是在其中添加自己的更改,具體如下:
這里可以看到,他又引用了 board/st/stm32f769-eval/Kconfig 這個(gè)后面步驟我們會(huì)建立它。
??至于 arch/arm/mach-xxx/xxx 和 arch\arm\include\asm\xxx 下的代碼文件都需要增加哪些,這個(gè)就需要參考其他 U-Boot 已支持的 CPU 來(lái)決定(主要是我們找到相關(guān)資料介紹需要增加哪些)。我這里不需要更改。
??這里也有一個(gè)注意事項(xiàng),在我們添加了新的配置之后,原來(lái) U-Boot 的配置系統(tǒng)中的配置項(xiàng)可能有依賴關(guān)系,要確保自己新增的配置也添加到依賴項(xiàng)里面,舉例如下:
??具體到我這里使用的 STM32F769i-EVAL 板子,原來(lái)已經(jīng)存在 arch/arm/mach-stm32/Kconfig 了,這里也已經(jīng)被添加到了 arch\arm\Kconfig 文件中,如下圖所示:
至于 arch/arm/Makefile 由于我這里沒有任何新增文件,因此不需要改動(dòng)。
??具體到我這里使用的 STM32F769i-EVAL 板子,原來(lái)已經(jīng)存在 arch/arm/Kconfig 了,這里我就不需要改動(dòng)了。至此架構(gòu)中的移植就完成了,接下來(lái)就開始具體添加一些板子相關(guān)的文件。
??具體到我這里使用的 STM32F769i-EVAL 板子,我參考 stm32f746-disco 新建了 stm32f769-eval,然后修改了其中各文件的內(nèi)容,具體如下:
??這里需要注意,在 CPU 的 Kconfig 中有可能已經(jīng)直接引用了開發(fā)板的 Kconfig,而 CPU 的 Kconfig 又被包含到了架構(gòu)一級(jí)的 Kconfig 文件,因此,如果有這個(gè)包含關(guān)系,這里就不用再次引入開發(fā)板的 Kconfig 了。例如 STM32F769-disco 的包含關(guān)系如下:
??具體到我這里使用的 STM32F769i-EVAL 板子,我這里新增的 stm32f769-eval 也是以上這種情況。
??具體到我這里使用的 STM32F769i-EVAL 板子,我直接依據(jù) stm32f746-disco.h 新建了 stm32f769-eval.h,并修改了其中的內(nèi)容:
這個(gè)文件會(huì)在編譯過(guò)程中被引入到我們上面添加的板子相關(guān)文件(如果你直接搜索,會(huì)發(fā)現(xiàn)根本沒有引用它的地方)。
??具體到我這里使用的 STM32F769i-EVAL 板子,我直接依據(jù) stm32f769-disco_defconfig 新建了 stm32f769-eval_defconfig,然后更改了其中的內(nèi)容:
??以上步驟基本把移植過(guò)程介紹了差不多,如果想要完整的移植到新板子,要修改的內(nèi)容還有很多(例如,新增驅(qū)動(dòng),設(shè)備樹修改等等)。通常情況的移植往往是參考一個(gè)近似來(lái)對(duì)比添加更改。
整理
??由于 U-Boot 源碼文件眾多,而具體到某一平臺(tái)(開發(fā)板)之后,其中的大多數(shù)文件我們根本不需要。為了學(xué)習(xí)的方便,剔除無(wú)用文件,僅僅保留我們需要的文件對(duì)于我們學(xué)習(xí)將有很大幫助。如果可以正常整理出需要的源代碼,那基本對(duì)于 U-Boot 的文件結(jié)構(gòu)掌握差不多了。
??由于 U-Boot 很多文件是編譯過(guò)程中產(chǎn)生的,如何有效提取成了個(gè)問(wèn)題。我在網(wǎng)上看到有個(gè)網(wǎng)友搞了一個(gè)可以根據(jù)編譯過(guò)程提取源代碼的腳本:https://github.com/tonyho/Generate_Kernel_Uboot_Project_forIDE,但是經(jīng)過(guò)我嘗試,發(fā)現(xiàn)并不是很準(zhǔn)確,但基本可以用。
??此外,如果使用的是 VSCode 來(lái)查看代碼,可以直接在 .vsoce/settings.json 中使用以下配置以使 VSCode 不顯示相關(guān)目錄及文件
{"files.exclude": {"**/.git": true,"**/.svn": true,"**/.hg": true,"**/CVS": true,"**/.DS_Store": true,"**/.gitignore": true,"**/*.o": true,"**/*.su": true,"**/*.dtb": true,"**/*.cmd": true,"**/*mips*": true,"**/*powerpc*": true,"**/*riscv*": true,"Licenses": true,".git*": true,".stamp*": true,"*.yml": true,// 排除不使用的架構(gòu)"arch/{arc,m68k,microblaze,mips,nds32,nios2,powerpc,riscv,sandbox,sh,x86,xtensa}": true,// 排除 arch/arm/ 中無(wú)關(guān)目錄及文件"arch/arm/mach-[^s]*": true,"arch/arm/mach-s[^t]*": true,"arch/arm/mach-st[^m]*": true,"arch/arm/mach-stm32[$^m]*": true,"arch/arm/mach-stm32/stm32[^f]*": true,"arch/arm/mach-stm32/stm32f[^7]*": true,// 排除 arch/arm/cpu/* 無(wú)關(guān)目錄及文件"arch/arm/cpu/{arm11,arm720t,arm920t,arm946es,arm1136,arm1176,armv7,arm926ejs,armv8,pxa,sa1100}": true,// 排除 arch/arm/dts/* 無(wú)關(guān)目錄及文件"arch/arm/dts/[^s|^M|^i]*": true,"arch/arm/dts/i[^n]*": true,"arch/arm/dts/s[^t]*": true,"arch/arm/dts/st[^m]*": true,"arch/arm/dts/stm32[^f]*": true,"arch/arm/dts/stm32f[^7]*": true,"arch/arm/dts/stm32f7[^6|^4|^-]*": true,"arch/arm/dts/stm32f746[^.]*": true,"arch/arm/dts/stm32f769-[^e]*": true,// 排除 arch/arm/include/asm 無(wú)關(guān)目錄及文件"arch/arm/include/asm/{arch-[t-z]*,arch-[b-r]*,*-common,xen,mach-imx,arch-sunxi,arch-stv0991,arch-stm32h7,arch-stm32f4,arch-stih410,arch-sa1100,arch-aspeed,arch-am33xx,arch-armada8k,arch-armada100,armv8}": true,// 排除 board/* 無(wú)關(guān)目錄及文件"board/[^s]*": true,"board/s[^t]*": true,"board/ste": true,"board/sto*": true,"board/st/st[^m]*": true,"board/st/stm32[^f]*": true,"board/st/stm32f[^7]*": true,"board/st/stm32f7[^6]*": true,// 排除 include/configs/* 無(wú)關(guān)目錄及文件"include/configs/[^s]*": true,"include/configs/s[^t]*": true,"include/configs/st[^m]*": true,"include/configs/stm[^3]*": true,"include/configs/stm32[^f]*": true,"include/configs/stm32f[^7]*": true,"include/configs/stm32f7[^6]*": true,// 排除 configs/* 中無(wú)關(guān)目錄及文件"configs/[^s]*": true,"configs/s[^t]*": true,"configs/st[^m]*": true,"configs/stm[^3]*": true,"configs/stm32[^f]*": true,"configs/stm32f[^7]*": true,"configs/stm32f7[^6]*": true,"configs/stm32f769-[^e]*": true,} }參考
總結(jié)
以上是生活随笔為你收集整理的U-Boot 之三 U-Boot 源码文件解析及移植过程详解的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: U-Boot 之二 详解使用 eclip
- 下一篇: Linux 之十二 Makefile 从