打印
[STM32MP1]

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

[复制链接]
382|4
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
tpgf|  楼主 | 2022-3-3 20:05 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
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 的具体移植步骤。


使用特权

评论回复
沙发
tpgf|  楼主 | 2022-3-3 20:07 | 只看该作者
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 使用的工具的源代码


使用特权

评论回复
板凳
tpgf|  楼主 | 2022-3-3 20:12 | 只看该作者
编译产生的最终文件
  成功编译之后,就会在 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: 编译的内存映射文件。



移植过程
要使用 U-Boot,首先要确定 U-Boot 是否支持我们的使用芯片(开发板)。这就需要查看 ./config 目录下有没有对应的配置文件,或者说有没有类似的配置文件。如果直接有(对于一些通用的平台,U-Boot 已经添加好了一些默认配置),那么恭喜可以省事很多;如果没有(如果是自己画的板子,指定是没有),后续就牵扯到自己移植修改代码。

  正好 U-Boot 没有提供对于我使用的 STM32F769i-EVAL 板子的支持,我这里就添加对于 STM32F769i-EVAL 板子的支持。以此来介绍移植过程。移植后的 U-Boot 代码放到了Github 上:https://github.com/ZCShou/U-Boot-STM32。具体步骤如下:

新增 CPU 架构相关的文件:arch/架构/cpu/xxxx。这个一般不需要添加,一般人也搞不了。唯一一种可能就是你用的芯片使用了比较新的架构,而 U-Boot 还不支持。例如,如果芯片使用了最新的 ARMv9 架构,U-Boot 目前还没有支持!
新增芯片(或许应该说是开发板)的设备树文件:arch\arm\dts\xxxxx.dts,然后将新增的设备树文件添加到 arch/arm/dts/Makefile 中。一般比较常见的 MCU,U-Boot 都是支持的,无需我们关心。通常,我们往往是将一个与我们芯片类似的做一些对应的更改以适用于自己。
  具体到我这里使用的 STM32F769i-EVAL 板子,使用的 STM32F769 设备树没有,但是有个类似的 STM32F769i-disco 的设备树,所以我这里就依据 STM32F769i-disco 添加 STM32F769-eval 相关设备树。具体更改如下:




[color=rgba(0, 0, 0, 0.75)]其中 stm32f769.dtsi[color=rgba(0, 0, 0, 0.75)] 就是 stm32f746.dtsi[color=rgba(0, 0, 0, 0.75)] 改名字,下面是各设备数文件的包含关系示意图:


[color=rgba(0, 0, 0, 0.75)]这里有个重点注意事项就是 其中的 compatible[color=rgba(0, 0, 0, 0.75)] 的内容,驱动使用该项的内容来进行匹配,随意更改可能导致驱动无法识别!!举例如下:

定义目标 CPU 的 Kconfig 文件:arch/arm/mach-xxx/xxx/Kconfig,并将其添加到 arch/arm/mach-stm32/Kconfig 中。然后添加 CPU 相关的代码文件 arch/arm/mach-imx/xxx 和 arch\arm\include\asm\xxx。同样,对于常见的 MCU,U-Boot 都是支持的,无需我们关心,通常只需要根据我们的板子做一些对应的更改即可。
  具体到我这里使用的 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 的配置系统中的配置项可能有依赖关系,要确保自己新增的配置也添加到依赖项里面,举例如下:

将新增的 CPU 的 Kconfig 文件添加他的到上一级:架构的 Kconfig 文件 arch\arm\Kconfig 中并且根据需要修改这架构级别的 Makefile 文件:arch/arm/Makefile。
  具体到我这里使用的 STM32F769i-EVAL 板子,原来已经存在 arch/arm/mach-stm32/Kconfig 了,这里也已经被添加到了 arch\arm\Kconfig 文件中,如下图所示:

至于 arch/arm/Makefile 由于我这里没有任何新增文件,因此不需要改动。
将架构的 Kconfig 文件 arch/arm/Kconfig 添加到它的上一级架构总的 Kconfig 文件 arch/Kconfig 中。

  具体到我这里使用的 STM32F769i-EVAL 板子,原来已经存在 arch/arm/Kconfig 了,这里我就不需要改动了。至此架构中的移植就完成了,接下来就开始具体添加一些板子相关的文件。
新建 board 文件:board/my_vendor/my_board/my_board.c、board/my_vendor/my_board/Kconfig、board/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.c;board/my_vendor/my_board/Kconfig 用于 Kconfig 配置系统
  具体到我这里使用的 STM32F769i-EVAL 板子,我参考 stm32f746-disco 新建了 stm32f769-eval,然后修改了其中各文件的内容,具体如下:

将我们新增的开发板的的 Kconfig 添加到架构一级的 Kconfig 文件 arch/arm/Kconfig 中:如下图是 U-Boot 支持的开发板的 Kconfig 情况:

  这里需要注意,在 CPU 的 Kconfig 中有可能已经直接引用了开发板的 Kconfig,而 CPU 的 Kconfig 又被包含到了架构一级的 Kconfig 文件,因此,如果有这个包含关系,这里就不用再次引入开发板的 Kconfig 了。例如 STM32F769-disco 的包含关系如下:

  具体到我这里使用的 STM32F769i-EVAL 板子,我这里新增的 stm32f769-eval 也是以上这种情况。
新增开发板默认的一些参数的头文件:include/configs/xxxx.h。这里面存放了一些默认的配置项。
  具体到我这里使用的 STM32F769i-EVAL 板子,我直接依据 stm32f746-disco.h 新建了 stm32f769-eval.h,并修改了其中的内容:

这个文件会在编译过程中被引入到我们上面添加的板子相关文件(如果你直接搜索,会发现根本没有引用它的地方)。
新建 board defconfig 文件:configs/my_board_defconfig。具体内容同样参考一个近似的,然后修改内容即可。
  具体到我这里使用的 STM32F769i-EVAL 板子,我直接依据 stm32f769-disco_defconfig 新建了 stm32f769-eval_defconfig,然后更改了其中的内容:

  以上步骤基本把移植过程介绍了差不多,如果想要完整的移植到新板子,要修改的内容还有很多(例如,新增驱动,设备树修改等等)。通常情况的移植往往是参考一个近似来对比添加更改。






168476220afc407048.png (39.23 KB )

168476220afc407048.png

使用特权

评论回复
地板
tpgf|  楼主 | 2022-3-3 20:15 | 只看该作者
整理

  由于 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,

    }

}



使用特权

评论回复
5
weifeng90| | 2022-3-3 20:28 | 只看该作者
uboot是Linux里面的吧

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

1354

主题

13901

帖子

8

粉丝