U-Boot 之三 U-Boot 源码文件解析及移植过程详解

  在之前的博文 Linux 之八 完整嵌入式 Linux 环境介绍及搭建说明 中我们说了要一步步搭建整个嵌入式 Linux 运行环境。我所使用的硬件平台及整个要搭建的嵌入式 Linux 环境见博文 Linux 之八 完整嵌入式 Linux 环境介绍及搭建说明,这里的编译都是基于以上环境的,就不过多说明了。

  这篇博文我们仅仅关注 U-Boot 源码及移植过程本身,想要吃透 U-Boot,有太多东西需要学习!最开始我想放到一篇文章中,写着写着内容越来越多,最终超过了 CSDN 编辑器的限制。。。最终决定把内容拆分成多篇文章。你可能需要:

  1. U-Boot 之一 零基础编译 U-Boot 过程详解 及 编译后的使用说明
  2. U-Boot 之二 详解使用 eclipse + J-Link 进行编译及在线调试
  3. U-Boot 之四 配置构建过程(Kconfig、Kuild)详解
  4. U-Boot 之五 详解 U-Boot 及 SPL 的启动流程

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 源代码中各个目录的一个简介:

/arch			特定于架构的文件.实现了不同体系结构的 CPU,指令集、设备树底层抽象,利用链接绑定实现了符号入口相对位置保持不变,故才能实现将内核镜像拷贝到内存然后进行引导的功能/arc			通用的架构文件/arm			ARM 架构/lib		实现了初始化C运行时环境(栈/堆指针等的初始化)/dts		实现了设备树的底层体系架构依赖的具体抽象剥离/cpu		不同的 ARM 指令集的 CPU 分开处理/mach-xxx	由于同样的内核相同,各家芯片外设都不尽相同,所以将各自个性实现剥离实现于此,这主要体系在ARM体系的芯片,由于ARM公司售卖IP,各家芯片厂商在内核的基础上延伸出各自不同的芯片,所以需要将差异性剥离实现/m68k			m68k 架构/microblaze	microblaze 架构/mips			MIPS 架构/nds32		NDS32 架构/nios2		Altera NIOS2 架构/powerpc		PowerPC 架构/riscv		RISC-V 架构/sandbox		独立于硬件的 "sandbox" 模式/sh			SH 架构/x86			x86 架构/xtensa		Xtensa 架构
/api			供外部应用程序使用的与架构或设备无关的 API.如标准化输入输出,显示,网络API、存储API等,为cmd提供支持
/board			开发板依赖文件.实现了产业链下游,设备厂商的差异性,对于产品设计而言,需要将各自在boot阶段需要严格初始化的实现放在这里,比如IO口的初始化,产品中大部分IO口必须显式设置其初始状态
/boot			images and booting 文件
/cmd			U-Boot 命令相关接口
/common			与架构无关的一些通用文件. 是 U-Boot 主体,如系统停留在U-Boot阶段,CPU始终在执行一个死循环,run_main_loop().
/configs		开发板默认的配置文件。格式均为:开发板名_defconfig
/disk			磁盘驱动器分区处理的代码.实现了轻量级磁盘管理
/doc			文档 (a mix of ReST and READMEs)
/drivers		设备驱动.这里实现了boot阶段必要的设备驱动,如网口、显示等
/dts			实现了设备树.用于构建 内部 U-Boot fdt 的 Makefile
/env			环境支持
/examples		示例代码
/fs				文件系统代码 (cramfs, ext2, jffs2, etc.)
/include		头文件
/lib			通用于所有架构的库例程.比如CRC算法,加密算法,压缩算法,字符串操作等
/Licenses		各种许可证文件
/net			网络代码.实现网络协议层
/post			上电自检
/scripts		各种构建脚本和 Makefile 文件。跟 make menuconfig 配置界面的图形绘制相关的文件,我们作为使用者无需关心这个文件夹的内容
/test			各种单元测试文件
/tools			里面包含一系列构建 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。具体步骤如下:

  1. 新增 CPU 架构相关的文件:arch/架构/cpu/xxxx。这个一般不需要添加,一般人也搞不了。唯一一种可能就是你用的芯片使用了比较新的架构,而 U-Boot 还不支持。例如,如果芯片使用了最新的 ARMv9 架构,U-Boot 目前还没有支持!
  2. 新增芯片(或许应该说是开发板)的设备树文件:arch\arm\dts\xxxxx.dts,然后将新增的设备树文件添加到 arch/arm/dts/Makefile 中。一般比较常见的 MCU,U-Boot 都是支持的,无需我们关心。通常,我们往往是将一个与我们芯片类似的做一些对应的更改以适用于自己。
      具体到我这里使用的 STM32F769i-EVAL 板子,使用的 STM32F769 设备树没有,但是有个类似的 STM32F769i-disco 的设备树,所以我这里就依据 STM32F769i-disco 添加 STM32F769-eval 相关设备树。具体更改如下:
    在这里插入图片描述
    其中 stm32f769.dtsi 就是 stm32f746.dtsi 改名字,下面是各设备数文件的包含关系示意图:
    在这里插入图片描述
    这里有个重点注意事项就是 其中的 compatible 的内容,驱动使用该项的内容来进行匹配,随意更改可能导致驱动无法识别!!举例如下:
    在这里插入图片描述
  3. 定义目标 CPU 的 Kconfig 文件:arch/arm/mach-xxx/xxx/Kconfig,并将其添加到 arch/arm/mach-stm32/Kconfig 中。然后添加 CPU 相关的代码文件 arch/arm/mach-imx/xxxarch\arm\include\asm\xxx。同样,对于常见的 MCU,U-Boot 都是支持的,无需我们关心,通常只需要根据我们的板子做一些对应的更改即可。
      具体到我这里使用的 STM32F769i-EVAL 板子,原来已经存在 arch/arm/mach-stm32/stm32f7/Kconfig 了,我只是在其中添加自己的更改,具体如下:
    在这里插入图片描述
    这里可以看到,他又引用了 board/st/stm32f769-eval/Kconfig 这个后面步骤我们会建立它。
      至于 arch/arm/mach-xxx/xxxarch\arm\include\asm\xxx 下的代码文件都需要增加哪些,这个就需要参考其他 U-Boot 已支持的 CPU 来决定(主要是我们找到相关资料介绍需要增加哪些)。我这里不需要更改。
      这里也有一个注意事项,在我们添加了新的配置之后,原来 U-Boot 的配置系统中的配置项可能有依赖关系,要确保自己新增的配置也添加到依赖项里面,举例如下:
    在这里插入图片描述
  4. 将新增的 CPU 的 Kconfig 文件添加他的到上一级:架构的 Kconfig 文件 arch\arm\Kconfig 中并且根据需要修改这架构级别的 Makefile 文件:arch/arm/Makefile
      具体到我这里使用的 STM32F769i-EVAL 板子,原来已经存在 arch/arm/mach-stm32/Kconfig 了,这里也已经被添加到了 arch\arm\Kconfig 文件中,如下图所示:
    在这里插入图片描述
    至于 arch/arm/Makefile 由于我这里没有任何新增文件,因此不需要改动。
  5. 将架构的 Kconfig 文件 arch/arm/Kconfig 添加到它的上一级架构总的 Kconfig 文件 arch/Kconfig 中。
    在这里插入图片描述
      具体到我这里使用的 STM32F769i-EVAL 板子,原来已经存在 arch/arm/Kconfig 了,这里我就不需要改动了。至此架构中的移植就完成了,接下来就开始具体添加一些板子相关的文件。
  6. 新建 board 文件:board/my_vendor/my_board/my_board.cboard/my_vendor/my_board/Kconfigboard/my_vendor/my_board/Makefile 这三个问你件。其中,board/my_vendor/my_board/my_board.c 中的具体内容 U-Boot 都有规定,这个可以参考已经存在的类似的板子的相关文件。board/my_vendor/my_board/Makefile 用于编译 board/my_vendor/my_board/my_board.cboard/my_vendor/my_board/Kconfig 用于 Kconfig 配置系统
      具体到我这里使用的 STM32F769i-EVAL 板子,我参考 stm32f746-disco 新建了 stm32f769-eval,然后修改了其中各文件的内容,具体如下:
    在这里插入图片描述
  7. 将我们新增的开发板的的 Kconfig 添加到架构一级的 Kconfig 文件 arch/arm/Kconfig 中:如下图是 U-Boot 支持的开发板的 Kconfig 情况:
    在这里插入图片描述
      这里需要注意,在 CPU 的 Kconfig 中有可能已经直接引用了开发板的 Kconfig,而 CPU 的 Kconfig 又被包含到了架构一级的 Kconfig 文件,因此,如果有这个包含关系,这里就不用再次引入开发板的 Kconfig 了。例如 STM32F769-disco 的包含关系如下:
    在这里插入图片描述
      具体到我这里使用的 STM32F769i-EVAL 板子,我这里新增的 stm32f769-eval 也是以上这种情况。
  8. 新增开发板默认的一些参数的头文件:include/configs/xxxx.h。这里面存放了一些默认的配置项。
      具体到我这里使用的 STM32F769i-EVAL 板子,我直接依据 stm32f746-disco.h 新建了 stm32f769-eval.h,并修改了其中的内容:
    在这里插入图片描述
    这个文件会在编译过程中被引入到我们上面添加的板子相关文件(如果你直接搜索,会发现根本没有引用它的地方)。
  9. 新建 board defconfig 文件:configs/my_board_defconfig。具体内容同样参考一个近似的,然后修改内容即可。
      具体到我这里使用的 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,}
}

参考

  1. https://qingmu.blog.csdn.net/article/details/109199081
  2. https://blog.csdn.net/csdnxmj/article/details/95507165


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部