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