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

相关文章

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

OpenHarmony鸿蒙开发( Beta5.0)无感配网详解

1、简介 无感配网是指在设备联网过程中无需输入热点相关账号信息,即可快速实现设备配网,是一种兼顾高效性、可靠性和安全性的配网方式。 2、配网原理 2.1 通信原理 手机和智能设备之间的信息传递,利用特有的NAN协议实现。利用手机和智能设备之间的WiFi 感知订阅、发布能力,实现了数字管家应用和设备之间的发现。在完成设备间的认证和响应后,即可发送相关配网数据。同时还支持与常规Sof

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

6.1.数据结构-c/c++堆详解下篇(堆排序,TopK问题)

上篇:6.1.数据结构-c/c++模拟实现堆上篇(向下,上调整算法,建堆,增删数据)-CSDN博客 本章重点 1.使用堆来完成堆排序 2.使用堆解决TopK问题 目录 一.堆排序 1.1 思路 1.2 代码 1.3 简单测试 二.TopK问题 2.1 思路(求最小): 2.2 C语言代码(手写堆) 2.3 C++代码(使用优先级队列 priority_queue)

K8S(Kubernetes)开源的容器编排平台安装步骤详解

K8S(Kubernetes)是一个开源的容器编排平台,用于自动化部署、扩展和管理容器化应用程序。以下是K8S容器编排平台的安装步骤、使用方式及特点的概述: 安装步骤: 安装Docker:K8S需要基于Docker来运行容器化应用程序。首先要在所有节点上安装Docker引擎。 安装Kubernetes Master:在集群中选择一台主机作为Master节点,安装K8S的控制平面组件,如AP

【Linux 从基础到进阶】Ansible自动化运维工具使用

Ansible自动化运维工具使用 Ansible 是一款开源的自动化运维工具,采用无代理架构(agentless),基于 SSH 连接进行管理,具有简单易用、灵活强大、可扩展性高等特点。它广泛用于服务器管理、应用部署、配置管理等任务。本文将介绍 Ansible 的安装、基本使用方法及一些实际运维场景中的应用,旨在帮助运维人员快速上手并熟练运用 Ansible。 1. Ansible的核心概念

maven 编译构建可以执行的jar包

💝💝💝欢迎莅临我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」👈,「stormsha的知识库」👈持续学习,不断总结,共同进步,为了踏实,做好当下事儿~ 专栏导航 Python系列: Python面试题合集,剑指大厂Git系列: Git操作技巧GO

AI基础 L9 Local Search II 局部搜索

Local Beam search 对于当前的所有k个状态,生成它们的所有可能后继状态。 检查生成的后继状态中是否有任何状态是解决方案。 如果所有后继状态都不是解决方案,则从所有后继状态中选择k个最佳状态。 当达到预设的迭代次数或满足某个终止条件时,算法停止。 — Choose k successors randomly, biased towards good ones — Close

如何编写Linux PCIe设备驱动器 之二

如何编写Linux PCIe设备驱动器 之二 功能(capability)集功能(capability)APIs通过pci_bus_read_config完成功能存取功能APIs参数pos常量值PCI功能结构 PCI功能IDMSI功能电源功率管理功能 功能(capability)集 功能(capability)APIs int pcie_capability_read_wo

嵌入式Openharmony系统构建与启动详解

大家好,今天主要给大家分享一下,如何构建Openharmony子系统以及系统的启动过程分解。 第一:OpenHarmony系统构建      首先熟悉一下,构建系统是一种自动化处理工具的集合,通过将源代码文件进行一系列处理,最终生成和用户可以使用的目标文件。这里的目标文件包括静态链接库文件、动态链接库文件、可执行文件、脚本文件、配置文件等。      我们在编写hellowor