U-Boot 之二 零基础编译、详解 Image 镜像及 DTB 文件

2023-10-28 21:20

本文主要是介绍U-Boot 之二 零基础编译、详解 Image 镜像及 DTB 文件,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

  在上一篇博文 U-Boot 之一 源码文件、启动阶段(TPL、SPL)、FALCON、设备树 中我们已经对 U-Boot 的源码进行了整体的一个认识,本篇博文就来看看如何对 U-Boot 进行编译,不过这里仅仅关注构建过程本身,不涉及 U-Boot 中任何技术细节。

基本环境

  我们需要一台 Linux PC(可以使用虚拟机) 及一个块可运行 Linux 的开发板。尽管部分源码支持在 Windows 下的 Linux 环境中编译(例如 WSL),但是强烈不推荐,因为难免出现一些奇怪问题。

开发环境

  我这里使用的基本开发环境是全新安装的 Ubuntu 22.04.1 LTS,为了避免一开始就安装一堆不是道干啥的软件问题,我们采用在后续开发中缺少补啥的方式(这就是我所谓的零基础编译)。使用 sudo apt install lsb-core 安装并查看发行版基本信息如下:
在这里插入图片描述

  1. 由于 Ubuntu 22.04 LTS 默认是标配 OpenSSL 3.x,而旧版 U-Boot 使用的是 OpenSSL 1.x,所以,该环境编译旧版 U-Boot(从 commit e927e21c 开始添加了相关处理) 将出现一堆警告,因此,后续使用 u-boot-v2022.10 这个版本为主:
    在这里插入图片描述
  2. Arm GNU Toolchain 10.2-2022.02 存在 BUG,导致编译 U-Boot 报错,不要使用这个版本!
    在这里插入图片描述
  3. 新版(Arm GNU Toolchain 10.3 之后的版本)的 Arm GNU Toolchain 在 Linux 上 GDB 需要 Python3.8 支持。然而,Ubuntu 22.04 默认的 Python 是 3.10。直接运行 arm-none-eabi-gdb 报错如下:
    在这里插入图片描述
    解决方法就是直接手动安装 Python3.8 即可。 旧版的 Arm GNU Toolchain 10.3 -2021.10 不需要 Python 支持
    sudo add-apt-repository ppa:deadsnakes/ppa -y
    sudo apt install python3.8
    

运行环境

  我使用的嵌入式 Linux 运行环境是 STM32F769I-EVAL 板子。STM32F769I-EVAL 板子使用的 STM32F769NI 这个 MCU,STM32F769NI 这款 MCU 采用的是 ARM Cortex-M7 的核心,指令集架构是 ARMv7m。此外,还需要注意,这个板子上的的串口的 RX 默认是断开,需要用短路帽连接起来。
在这里插入图片描述
  U-Boot 本身没有提供对于 STM32F769I-EVAL 板子的支持,但是它支持的 STM32F769-Disco 板子。STM32F769-Disco 开发板与 STM32F769I-EVAL 评估板的 MCU 都使用的是 STM32F769NI,因此,本文我们就直接编译 STM32F769-Disco 板子的固件。如下是整个嵌入式 Linux 环境的内存布局。
在这里插入图片描述
  不过两者的板载资源不同(例如,DRAM 大小),因此,直接下载到开发板运行则 U-Boot 识别的某些外设信息是不对的。本篇博文仅仅关注如何进行零基础编译,在博文 U-Boot 之三 移植过程详解、 STM32F769I-EVAL 开发板适配 中我详细介绍了如何将 U-Boot 移植到 STM32F769I-EVAL 板子。

示例代码

  嵌入式 Linux 运行环境搭建系列博文涉及的所有源代码均放到了我个人的 Gitee 上:https://gitee.com/itexp/BOARD-STM32F769I-EVAL这个仓库中包含了的所有源代码会根据后续博文一步步进行各种适配,如果你是纯学习则可以直接 clone 该仓库来学习!
在这里插入图片描述
  其中,为了在适配 Buildroot、Yocto 等工具时方便,该仓库采用了 git submodule 来进行基本的组织。本篇博文我们重点是学习 U-Boot,因此,也可以直接使用 https://gitee.com/itexp/u-boot-v2021.10 这个子仓库来学习。

编译过程

  由于我这里采用的是全新安装的 Ubuntu 22.04 LTS,各种软件包都没有安装,因此,肯定会出现各种错误,我们就采用构建中缺啥补啥,解决其中出现的各种错误。

  1. 第一步肯定是获取 U-Boot 的源代码,我这里直接使用了当前最新存档版:u-boot-2021.10.tar.bz2。这里需要重点注意,我最开始直接使用 Git 获取了最新的源代码,结果编译之后运行直接 HardFault,分析好久没找到原因,最后决定使用一个稳定发布版试试,结果没有问题。。。
    在这里插入图片描述
      成功下载并解压源代码之后(注意我这里将解压后的文件名命名为了 u-boot),我们需要进入 u-boot 目录下,使用命令:cd u-boot。此后就在 u-boot 目录下进行各种操作。

  2. 第二步就是生成配置,直接使用命令:ARCH=arm CROSS_COMPILE=arm-none-eabi- make O=build_stm32 stm32f769-disco_defconfig其中,参数 O=xx 用于指定编译的输出路径,这样,所有编译输出都放到指定目录下,方便查看。 不出意外的话会出现以下错误:

    1. /bin/sh: 1: bison: not found
      在这里插入图片描述
        这个错误是由于我们没有安装 bison 这个工具。Bison 是一个通用解析器生成器。GUN 软件之一,官网 https://www.gnu.org/software/bison/。Ubuntu 下直接使用命令:sudo apt install bison 即可(这个不是最新版,如果需要最新版需要自己从源码安装)。

    2. /bin/sh: 1: flex: not found
      在这里插入图片描述
        这个错误是由于我们没有安装 flex 这个工具。flex 是一个词法分析器。用来将一个 .l 文件生成一个 .c 程序文件。源代码托管于 Github:https://github.com/westes/flex。Ubuntu 下直接使用命令:sudo apt install flex 即可。

    3. 正常完成如下所示:
      在这里插入图片描述

  3. 第三步修改配置(裁剪)。直接使用命令:ARCH=arm CROSS_COMPILE=arm-none-eabi- make O=build_stm32 menuconfig其中,参数 O=xx 用于指定编译的输出路径,这样,所有编译输出都放到指定目录下,方便查看。 不出意外的话会出现以下错误:

    1. Unable to find the ncurses package
      在这里插入图片描述
        这个错误是由于我们没有安装 ncurses 这个工具。ncurses(new curses)是一套编程库,它提供了一系列的函数以便使用者调用它们去生成基于文本的用户界面。GUN 软件之一,官网:https://invisible-island.net/ncurses/。Ubuntu 下直接使用命令:sudo apt install libncurses-dev 即可。安装成功之后,重新 make menuconfig,就会进入下面的界面:
      在这里插入图片描述
      我们需要做的就是选择其中的 SPL / TPL 菜单项,然后回车,在其中下翻页找到 Activate Falcon Mode 项(SPL / TPL ---> Activate Falcon Mode),按 n 将选择去掉,然后保存退出。
      在这里插入图片描述
        其中还有很多项,这个就需要根据自己需要来具体进行裁剪了。当然有个前提是,修改了配置很大可能还需要配套修改对应的源代码,因为之所以修改肯定是为了适应自己开发板。
    2. Your display is too small to run Menuconfig!
      在这里插入图片描述
      这个错误提示很明显,就是终端界面太小,把终端拖大一些就好了。
  4. 第四步就是真正的编译了,直接使用命令:ARCH=arm CROSS_COMPILE=arm-none-eabi- make O=build_stm32 -j$(nproc)其中,参数 O=xx 用于指定编译的输出路径,这样,所有编译输出都放到指定目录下,方便查看。 不出意外的话会出现以下错误:

    1. /bin/sh: 1: arm-none-eabi-gcc: not found
      在这里插入图片描述
        这个错误是由于我们没有安装 GCC for ARM 导致的。也就是没有编译 U-Boot 使用的编译器。解决方法也非常简单,就是安装 GCC for ARM 即可。

        这里有个需要注意的点,如果大家搜索 Ubuntu 下安装 GCC for ARM,很多文章都过推荐使用 apt 命令来安装,类似于:sudo apt-get install gcc-arm-none-eabi,这个版本并不是最新的(貌似使用这个旧版本也可以,我选择了使用最新版)。更重要的是,ARM 之前已经宣布不再更新 Launchpad 上的 GCC for ARM 了(具体见 Launchpad 上的说明)。 其只在 ARM 的官网提供编译好的压缩包及源代码的压缩包。

      这里简单来讲解一下直接从 ARM 官网下载压缩包的安装方法:

      1. 下载最新版的 Linux x86_64 Tarball。目前最新的是 gcc-arm-none-eabi-10.3-2021.10-x86_64-linux.tar.bz2 。GCC for ARM 下载地址:https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm/downloads

      2. 将压缩包解压。我这里将其解压到了 /opt/ 目录下。直接使用命令:tar -xjvf gcc-arm-none-eabi-10.3-2021.10-x86_64-linux.tar.bz2 -C /opt。等待解压完成,/opt/gcc-arm-none-eabi-10.3-2021.10 目录下就是最新的 GCC for ARM 的各种可执行程序、库等文件了。

      3. 此时我们需要将 /opt/gcc-arm-none-eabi-10.3-2021.10 添加到系统环境变量这样才能正常在终端中使用各种命令。具体有两种方法(我采用了第二种):

        1. 第一种是创建符号连接
          sudo ln -s /opt/gcc-arm-none-eabi-your-version/bin/arm-none-eabi-gcc /usr/bin/arm-none-eabi-gcc 
          sudo ln -s /opt/gcc-arm-none-eabi-your-version/bin/arm-none-eabi-g++ /usr/bin/arm-none-eabi-g++
          sudo ln -s /opt/gcc-arm-none-eabi-your-version/bin/arm-none-eabi-gdb /usr/bin/arm-none-eabi-gdb
          sudo ln -s /opt/gcc-arm-none-eabi-your-version/bin/arm-none-eabi-size /usr/bin/arm-none-eabi-size
          
        2. 第二种是在 .bashrc 文件(在 Linux 系统普通用户目录(cd /home/xxx)或 root 用户目录(cd /root)下,用指令 ls -al 可以看到该隐藏文件,一般使用用户目录下的即可)最后面增加以下内容:
          # GCC for ARM
          export PATH="$PATH:/opt/gcc-arm-none-eabi-10.3-2021.10/bin"
          
      4. 可能需要重启我们之前已经打开的终端以上配置才会生效。

      5. 这里有一点需要注意,通过压缩包安装不能解决依赖关系,需要我们自己运行尝试,看看少啥安啥。目前已知的依赖是 ncures5(会报错:error while loading shared libraries: libncurses.so.5)和 libncursesw5(报错:error while loading shared libraries: libncursesw.so.5: cannot open shared object file: No such file or directory),其需要安装 sudo apt install libncurses5 libncursesw5(貌似应该是需要 32 位的,我们上面安装的 libncurses-dev 应该是 64 位的)。 在这里插入图片描述
        至于需不需要其他的依赖,大家自行尝试,我这里是没有提示需要其他任何组件。

    2. fatal error: openssl/evp.h:
      在这里插入图片描述
      这个错误主要是由于 U-Boot 代码使用了 openssl 中的相代码,而我们的环境中没有安装 openssl。Ubuntu 下直接使用命令:sudo apt install libssl-dev 即可。

  5. 正常编译完成之后。我们需要的文件就有了。我们真正需要的是根目录下的 u-boot.bin 和 spl 目录下的 u-boot-spl.bin
    在这里插入图片描述

  6. 其他一些问题。我在试用 make CHANGELOG 命令时出现错误 unrecognized command line option ‘-mno-unaligned-access’,不知道为啥,也没找到如何解决。

    最新的 U-Boot-v2022.07 版本已经没有该错误了
    在这里插入图片描述

Image 镜像

  成功编译之后,就会在 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-dtb.bin:u-boot-nodtb.bin 尾部拼接上设备树后形成的文件。由 .u-boot-dtb.bin.cmd 这个命令脚本产生。
    在这里插入图片描述
  • u-boot.bin: 就是把 u-boot-dtb.bin 重命名得到的。由 .u-boot.bin.cmd 这个命令脚本产生。
    在这里插入图片描述
  • u-boot.img:u-boot.bin 开头拼接一些信息后形成的文件。由 .u-boot.img.cmd 这个命令脚本产生。
  • u-boot-dtb.img:u-boot.bin 开头拼接一些信息后形成的文件。由 .u-boot-dtb.img.cmd 这个命令脚本产生。
  • u-boot.srec: S-Record 格式的镜像文件。由 .u-boot.srec.cmd 这个命令脚本产生。
  • u-boot.sym: 这个是从 u-boot 中导出的符号表文件。由 .u-boot.sym.cmd 这个命令脚本产生。
  • u-boot.lds: 编译使用的链接脚本文件。由 .u-boot.lds.cmd 这个命令脚本产生。
  • u-boot.map: 编译的内存映射文件。该文件是有编译工具链的连接器输出的!
  • System.map: 记录 U-Boot 中各个符号在内核中位置,但是这个文件是使用了 nmgrep工具来手动生成的
    在这里插入图片描述
  • u-boot.dtb: 这个是编译好的设备树二进制文件。就是 ./dts/dt.dtb 重命名得到的。./dts/dt.dtb 来自于 arch/arm/dts/stm32f769-eval.dtb 重命名。

  默认是开启了 SPL 的,因此,在编译 U-Boot 时会额外单独编译 SPL,编译产生的镜像文件就存放在 ./SPL 目录下。这下面的镜像生成方式与 U-Boot 基本是一模一样的。

  • u-boot-spl: 这个文件是编译后产生的 ELF 格式的 SPL 镜像文件,后续的文件都是由它产生的!.u-boot-spl.cmd 这个命令脚本描述了如何产生。
  • u-boot-spl-nodtb.bin: 这文件是使用编译工具链的 objcopy 工具从 u-boot-spl 这个文件中提取来的,它只包含可执行的二进制代码。就是把 u-boot-spl 这个文件中对于执行不需要的节区删除后剩余的仅执行需要的部分。由 .u-boot-spl-nodtb.bin.cmd 这个命令脚本产生。
  • u-boot-spl-dtb.bin:u-boot-nodtb.bin 尾部依次拼接上 u-boot-spl-pad.binu-boot-spl.dtb 后形成的文件。由 .u-boot-spl-dtb.bin.cmd 这个命令脚本产生。
  • u-boot-spl.bin: 就是把 u-boot-dtb.bin 重命名得到的。由 .u-boot-spl.bin.cmd 这个命令脚本产生。
  • u-boot-spl.sym: 这个是从 u-boot 中导出的符号表文件。由 .u-boot-spl.sym.cmd 这个命令脚本产生。
  • u-boot-spl.lds: 编译使用的链接脚本文件。由 .u-boot-spl.lds.cmd 这个命令脚本产生。
  • u-boot-spl.map: 编译 SPL 的内存映射文件。
  • u-boot-spl.dtb: 这个是编译好的设备树二进制文件。就是 ./dts/dt.dtb 重命名得到的。./dts/dt.dtb 来自于 arch/arm/dts/stm32f769-eval.dtb 重命名。
  • u-boot-spl-pad.bin: 应该是对齐使用的数据,具体为啥需要这个还没找到。

Image 使用

  编译之后,源码的根目录下会生成一堆二进制的文件(.bin),其中,在默认的 U-Boot 配置下,我们实际需要的 U-Boot 实际包含两部分:spl/u-boot-spl.binu-boot.bin。而且,默认情况下,编译的这俩文件是可以直接在 MCU 内部的 Nor Flash 中运行的。
在这里插入图片描述
  因此,我们可以直接使用 J-link 等调试器将这俩文件烧写到 Flash 中。至于烧写地址,在 U-Boot 源码中(具体见于 ./config/stm32f769-disco_defconfig./include/configs/stm32f746-disco.h)都是配置好了的。

设备树

  设备树源文件被最终编译为二进制的 DTB 文件,原始的 DTB 文件位于 arch/arm/dts/xxx.dtb 下面,构建系统会复制到 ./dts/dt.dtb,进一步重命名为 ./u-boot.dtb。u-boot 支持两种形式将 dtb 编译到 u-boot 的镜像中:

  • dtb 和 u-boot 的 bin文件分离

    • 需要打开 CONFIG_OF_SEPARATE 宏来使能。
    • 在这种方式下,u-boot 的编译和 dtb 的编译是分开的,先生成 u-boot 的 bin 文件,然后再另外生成dtb 文件。
    • dtb 最终会自动追加到 u-boot 的 bin 文件的最后面。因此,可以通过 u-boot 的结束地址符号,也就是 _end 符号来获取 dtb 的地址。
  • dtb 集成到 u-boot 的 bin 文件内部

    • 需要打开 CONFIG_OF_EMBED 宏来使能。
    • 在这种方式下,在编译 u-boot 的过程中,也会编译 dtb。
    • 最终 dtb 是包含到了u-boot 的 bin 文件内部的。dtb 会位于 u-boot 的 .dtb.init.rodata 段中,并且在代码中可以通过 __dtb_dt_begin 符号获取其符号。

    官方不推荐这种方式,建议仅用于调试

  • 另外,也可以通过 fdtcontroladdr 环境变量来指定 dtb 的地址。可以通过直接把 dtb 加载到内存的某个位置,并在环境变量中设置 fdtcontroladdr 为这个地址,达到动态指定 dtb 的目的。

参考

  1. https://askubuntu.com/questions/1243252/how-to-install-arm-none-eabi-gdb-on-ubuntu-20-04-lts-focal-fossa
  2. https://james-hui.com/2021/07/02/building-a-small-uboot-linux-and-rootfs-for-arm-cortex-m7/
  3. opdenacker-understanding-u-boot-falcon-mode.pdf
  4. https://adrianalin.gitlab.io/popsblog.me/posts/build-linux-for-stm32f769i-disco-using-buildroot/

这篇关于U-Boot 之二 零基础编译、详解 Image 镜像及 DTB 文件的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/296022

相关文章

java图像识别工具类(ImageRecognitionUtils)使用实例详解

《java图像识别工具类(ImageRecognitionUtils)使用实例详解》:本文主要介绍如何在Java中使用OpenCV进行图像识别,包括图像加载、预处理、分类、人脸检测和特征提取等步骤... 目录前言1. 图像识别的背景与作用2. 设计目标3. 项目依赖4. 设计与实现 ImageRecogni

Java访问修饰符public、private、protected及默认访问权限详解

《Java访问修饰符public、private、protected及默认访问权限详解》:本文主要介绍Java访问修饰符public、private、protected及默认访问权限的相关资料,每... 目录前言1. public 访问修饰符特点:示例:适用场景:2. private 访问修饰符特点:示例:

python管理工具之conda安装部署及使用详解

《python管理工具之conda安装部署及使用详解》这篇文章详细介绍了如何安装和使用conda来管理Python环境,它涵盖了从安装部署、镜像源配置到具体的conda使用方法,包括创建、激活、安装包... 目录pytpshheraerUhon管理工具:conda部署+使用一、安装部署1、 下载2、 安装3

详解Java如何向http/https接口发出请求

《详解Java如何向http/https接口发出请求》这篇文章主要为大家详细介绍了Java如何实现向http/https接口发出请求,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 用Java发送web请求所用到的包都在java.net下,在具体使用时可以用如下代码,你可以把它封装成一

JAVA系统中Spring Boot应用程序的配置文件application.yml使用详解

《JAVA系统中SpringBoot应用程序的配置文件application.yml使用详解》:本文主要介绍JAVA系统中SpringBoot应用程序的配置文件application.yml的... 目录文件路径文件内容解释1. Server 配置2. Spring 配置3. Logging 配置4. Ma

mac中资源库在哪? macOS资源库文件夹详解

《mac中资源库在哪?macOS资源库文件夹详解》经常使用Mac电脑的用户会发现,找不到Mac电脑的资源库,我们怎么打开资源库并使用呢?下面我们就来看看macOS资源库文件夹详解... 在 MACOS 系统中,「资源库」文件夹是用来存放操作系统和 App 设置的核心位置。虽然平时我们很少直接跟它打交道,但了

关于Maven中pom.xml文件配置详解

《关于Maven中pom.xml文件配置详解》pom.xml是Maven项目的核心配置文件,它描述了项目的结构、依赖关系、构建配置等信息,通过合理配置pom.xml,可以提高项目的可维护性和构建效率... 目录1. POM文件的基本结构1.1 项目基本信息2. 项目属性2.1 引用属性3. 项目依赖4. 构

Rust 数据类型详解

《Rust数据类型详解》本文介绍了Rust编程语言中的标量类型和复合类型,标量类型包括整数、浮点数、布尔和字符,而复合类型则包括元组和数组,标量类型用于表示单个值,具有不同的表示和范围,本文介绍的非... 目录一、标量类型(Scalar Types)1. 整数类型(Integer Types)1.1 整数字

Java操作ElasticSearch的实例详解

《Java操作ElasticSearch的实例详解》Elasticsearch是一个分布式的搜索和分析引擎,广泛用于全文搜索、日志分析等场景,本文将介绍如何在Java应用中使用Elastics... 目录简介环境准备1. 安装 Elasticsearch2. 添加依赖连接 Elasticsearch1. 创

Redis缓存问题与缓存更新机制详解

《Redis缓存问题与缓存更新机制详解》本文主要介绍了缓存问题及其解决方案,包括缓存穿透、缓存击穿、缓存雪崩等问题的成因以及相应的预防和解决方法,同时,还详细探讨了缓存更新机制,包括不同情况下的缓存更... 目录一、缓存问题1.1 缓存穿透1.1.1 问题来源1.1.2 解决方案1.2 缓存击穿1.2.1