uboot编译启动流程

Tutorial: 卓越工程师 Category: 嵌入式 Published: 2026-10-23 11:05:29 Views: 1 Likes: 0 Comments: 0

330.3.3uboot编译启动流程

本节使用课程配套的 NXP 下游 U-Boot 2016.03 源码,在 Ubuntu 中为 iTOP-i.MX6ULL 开发板构建 u-boot.imx,并从整体上说明它怎样完成早期初始化、重定位以及 Linux 引导。先建立一条准确的主线:

上电/复位
  → i.MX6ULL 片内 Boot ROM
  → 读取 u-boot.imx 的启动头并按 DCD 配置 DDR
  → 装载并跳入 U-Boot
  → U-Boot 早期初始化、建立 C 运行环境并重定位
  → U-Boot 后期初始化、执行 bootcmd
  → 把 zImage 与一个 DTB 读入 DDR
  → 跳入 Linux 内核
  → Linux 挂载 eMMC 上的根文件系统并启动用户空间

这里必须区分三种存储资源:Boot ROM 是 SoC 内部固化的启动代码;eMMC 是断电后仍保存 U-Boot、内核、设备树和根文件系统的非易失存储;DDR 是掉电即失、但适合运行代码和保存工作数据的主存。DDR 控制器和相关引脚尚未配置好以前,处理器不能像使用普通内存那样直接使用板外 DDR。

一、优步特编译流程

标题中的“优步特”是 U-Boot 的音译,正文统一写作 U-Boot。U-Boot 是开源引导加载程序,不只是笼统的“逻辑程序”。它位于片内 Boot ROM 与 Linux 之间,负责完成启动所需的板级初始化、选择启动来源、准备启动参数、装载操作系统镜像并移交控制权。

1.1 综述

本节涉及的各层职责如下:

层级所在位置本节中的主要职责
i.MX6ULL Boot ROMSoC 片内 ROM复位后最先执行;采样启动配置、寻找可启动镜像、解释 i.MX 启动头并装载下一阶段
U-Boot构建后写入 eMMC,运行时位于片内 RAM/DDR初始化启动所需硬件,提供环境变量和命令,读取内核与 DTB,最后跳入内核
Linux 内核eMMC 上的 zImage,启动前读入 DDR接管 CPU,完成内存、调度、驱动、文件系统等内核初始化
根文件系统本课程中位于 eMMC 分区提供 /sbin/init、Shell、动态库、配置和应用等用户空间内容

正常启动路径中,U-Boot 跳入 Linux 后不会再作为一个并行程序继续运行,也通常不会从内核返回;此后由 Linux 接管机器。不过“内核就是整个系统”同样不准确:一个可用的 Linux 系统还需要根文件系统中的用户空间程序和配置。

课程所说的“第一阶段、第二阶段”适合用来建立“早期低级初始化 → 建立 C 环境并完成主体功能”的直觉,但不能机械地等同为“所有汇编都是第一阶段、所有 C 都是第二阶段”。现代 U-Boot 还可能启用真正独立的 SPL/TPL/VPL;SPL 的含义是一个体积较小的 Secondary Program Loader,常用来初始化 SDRAM 并装载完整 U-Boot。本课程默认 eMMC 配置没有启用 SPL,因此不要把这里的汇编/C 教学划分误认为 SPL/U-Boot 两个镜像。U-Boot SPL 官方说明

1.2 编译优步特

视频使用的课程压缩包是:

uboot-imx-rel_imx_4.1.15_2.1.0_ga_20200512.tar.gz

项目内该文件的 SHA-256 为:

4A4D3375AD9152ED28FCB1B01D9E0318C5DA79C67A5936C7CCA97FC7D5EAC381

解压后的源码顶层 Makefile 标明 U-Boot 版本为 2016.03,目录名中的 rel_imx_4.1.15_2.1.0_ga 则表明它来自对应的 NXP i.MX Linux BSP 发布系列。这是一套为课程板卡修改过的旧源码,复现实验时应优先使用与它匹配的交叉编译器、板级配置和镜像,不要直接拿最新上游 U-Boot 替换后仍期待命令、配置和生成文件完全相同。

1.2.1 安装编译必用工具

视频中的准确命令是:

sudo apt-get install lzop libncurses5-dev u-boot-tools

这不是一个名为“narcos”的工具,而是三个独立软件包:

软件包本节相关作用
lzop提供 LZO 压缩工具,供部分旧式镜像或构建流程使用
libncurses5-dev提供 ncurses 开发文件,常用于 menuconfig 一类终端图形配置界面
u-boot-tools提供 mkimage、dumpimage 等主机端 U-Boot 镜像工具

视频在安装确认提示处输入 y。如果 APT 软件包索引过旧,应先执行 sudo apt-get update;如果较新的发行版已不再提供 libncurses5-dev,应根据发行版改用兼容的 libncurses-dev,但复现课程的 Ubuntu 20.04 环境时应先保持课程版本。U-Boot 当前官方构建文档同样把主机 GCC、交叉编译器、make、ncurses 开发包等列为按目标需要安装的依赖,并明确指出实际依赖随板级目标而变化。U-Boot:Building with GCC

画面中的 192.168.31.230 只是讲师虚拟机当时的地址,不是固定参数。应在自己的 Ubuntu 中查询地址:

ip addr
# 或只快速查看已分配地址
hostname -I

随后让 SSH 客户端连接本机实际 IP。只有主机与虚拟机网络互通、SSH 服务已启动且账户凭据正确时,连接才会成功。

1.2.2 拷⻉迅为Uboot源代码

原标题中的“拷⻉”应为“拷贝”。视频把工作目录建在用户家目录的 Linux/uboot 下,注意 Linux 路径区分大小写:

cd ~
mkdir Linux
cd Linux
mkdir uboot
cd uboot
pwd

讲师第一次把 uboot 建错了位置,于是删除后重新创建。若确认该误建目录为空,优先使用只删除空目录的命令:

rmdir ~/uboot

视频演示的 rm -R uboot 与常见的 rm -r uboot 都会递归删除目录内容,操作不可由命令本身撤销;执行前必须先用 pwd、ls -la -- uboot 核对目标,不能照抄到路径不明的目录。

随后在 FileZilla 左侧找到课程资料的“代码&源代码/uboot&内核源码”,右侧进入 Ubuntu 的 ~/Linux/uboot,把上述 .tar.gz 压缩包传过去。传统 FTP 默认不加密用户名、密码和文件内容,只适合课程的受控局域网;非隔离环境应优先使用 SFTP/SCP,并核对服务器主机密钥。传输完成后还应核对大小或哈希,而不能只凭队列消失判断文件一定完整:

cd ~/Linux/uboot
sha256sum uboot-imx-rel_imx_4.1.15_2.1.0_ga_20200512.tar.gz
1.2.3 解压Uboot源码

视频先用 ls 确认上传文件,再执行:

cd ~/Linux/uboot
ls
tar -xvf uboot-imx-rel_imx_4.1.15_2.1.0_ga_20200512.tar.gz
cd uboot-imx-rel_imx_4.1.15_2.1.0_ga
ls

-x 表示提取,-v 表示逐项显示文件名,-f 表示后一个参数是归档文件。视频环境中的 GNU tar 能依据文件内容识别 gzip;想显式指定 gzip,可写成:

tar -xzvf uboot-imx-rel_imx_4.1.15_2.1.0_ga_20200512.tar.gz

输入文件名前几个字符后按的是 Tab 键补全,不是转录中的 “type”。解压所得目录名也不是简单的 uboot,必须以 ls 的实际结果为准。

1.2.4 编译Uboot源码

编译前先确认上一节安装的 ARM 交叉编译器和本机 make 都能被 Shell 找到:

command -v arm-linux-gnueabihf-gcc
arm-linux-gnueabihf-gcc --version
command -v make
make --version

视频第一次运行:

./create.sh

终端在脚本调用 make 的位置报告 make: command not found。这表示 Ubuntu 中缺少构建工具 make,不是“编译器里没有 make”。视频随后执行:

sudo apt install make
./create.sh

本项目课程源码中的 create.sh 已核实会设置 ARCH=arm 和交叉编译器前缀 arm-linux-gnueabihf-,先执行 make distclean,再选择板级配置并构建:

  • 不带参数的 ./create.sh 选择 mx6ul_topeet_emmc_defconfig;
  • ./create.sh nand 才选择 mx6ul_topeet_nand_defconfig;
  • make distclean 会删除旧构建产物和 .config,因此每次运行都相当于重新配置,不会保留此前的本地构建状态。

脚本正文将在 332 节专门分析;本节只需知道视频运行的是默认 eMMC 配置。U-Boot 的标准构建关系也是先用 <board>_defconfig 生成 .config,再通过 CROSS_COMPILE 指定交叉工具链并运行 make。U-Boot 官方构建文档

构建输出中的 CC xxx.o 表示把 C 源文件编译为可重定位目标文件 .o,AS 表示汇编,LD 表示链接,OBJCOPY 表示从已链接映像转换出原始二进制,MKIMAGE 则表示封装目标平台需要的启动镜像。因此 .o 不是“点文件”,u-boot.bin 也不是可在 Ubuntu 中直接双击或运行的普通主机程序。

编译结束后应同时检查命令退出状态和目标文件,而不是只看滚动输出:

echo "$?"
ls -lh u-boot u-boot.bin u-boot.imx

退出码 0 表示脚本最后一个命令成功。若提示 arm-linux-gnueabihf-gcc: command not found,应回到上一节检查交叉工具链的 bin 目录是否真的在当前会话的 PATH 中;反复安装 make 不能解决交叉编译器路径问题。

1.2.5 烧写测试

本节视频没有重新执行烧写,只回顾上一节的烧写文件。对于课程的 i.MX6ULL eMMC 板和这套 MfgTool2 固件包,应放入烧写目录的是构建生成的:

u-boot.imx

不能仅因 u-boot.bin 也含有机器码就把它替换进去;Boot ROM 和烧写脚本需要的是与板卡、启动介质及预期偏移匹配的镜像格式。烧写会覆盖 eMMC 内容,实际操作仍须遵守 329 节的板卡版本、拨码、供电、USB OTG、文件集合和备份要求。

烧写前可做只读核验:

ls -lh u-boot.bin u-boot.imx
file u-boot u-boot.bin u-boot.imx
./tools/mkimage -l u-boot.imx
sha256sum u-boot.imx

mkimage -l 能否列出全部字段取决于这套旧版工具的实现;即使能成功解析,也只能证明镜像结构可识别,不能代替上板启动测试。

1.2.6 Uboot文件介绍

原笔记中的 uboot.rmx 是识别错误,画面和课程烧写包中的准确文件名都是 u-boot.imx。主要构建产物的边界如下:

文件类型用途
u-boot带 ELF 头、段信息和符号的 ARM 链接结果调试、反汇编和生成后续映像;不是课程直接烧入 eMMC 的文件
u-boot.bin从 ELF 中提取的扁平原始二进制负载作为生成平台启动镜像的输入;本身不含 i.MX Boot ROM 所需的完整启动头
u-boot.imxFreescale/NXP i.MX Boot Image本课程 eMMC 板的烧写目标,含 i.MX ROM 所需结构和 U-Boot 负载
System.map、u-boot.map、u-boot.sym符号/链接映射资料定位符号地址、分析映像布局和调试
各目录中的 .o可重定位目标文件由编译器/汇编器产生,最后参与链接,不是独立烧写镜像

扩展名不能单独决定文件能否执行。u-boot 是 ARM ELF,但当前 x86-64 Ubuntu 不能把它当本机程序运行;u-boot.bin、u-boot.imx 是供目标启动链解释或装载的数据镜像,不能笼统称为两个“名称不同的可执行文件”。

1.2.7 uboot.bin和uboot.imx的区别

这套源码的 arch/arm/imx-common/Makefile 使用类似下面的逻辑生成镜像:以 u-boot.bin 为数据负载,以板级 imximage.cfg 为配置,调用 mkimage -T imximage 生成 u-boot.imx。课程板文件 board/freescale/mx6ul_topeet/imximage.cfg 采用 i.MX image version 2,并包含大量 DATA 4 地址 值 项,用来配置时钟、IOMUX 和 MMDC/DDR3L 相关寄存器。

所以“u-boot.imx 比 u-boot.bin 多了 DDR 信息”抓住了本板的关键,却不够完整。u-boot.imx 前部还需要组织 i.MX Boot ROM 可识别的启动结构,例如 Image Vector Table(IVT)、Boot Data、DCD 指针与数据;安全启动配置下还可能涉及 CSF 指针。NXP 的移植指南明确说明,i.MX 6 的 DDR 初始化配置位于 U-Boot 镜像启动头内的 DCD 表,由 ROM 代码读取,并要求这些参数与具体 DDR 器件、I/O 和 MMDC 配置匹配。NXP i.MX BSP Porting Guide

板上电后的关系可简化为:

eMMC 中的 u-boot.imx
  ├─ i.MX 启动头:IVT、Boot Data、DCD 等
  │                 └─ Boot ROM 据此写寄存器,使板外 DDR 可用
  └─ U-Boot 负载:来源于 u-boot.bin
                    └─ Boot ROM 装载后跳入其入口地址

二进制对比工具在文件开头标红,说明 .imx 相比 .bin 插入了启动头区域。后面的主要负载确实来自 u-boot.bin,但还可能受头部尺寸、偏移和填充影响,不应据此表述成“除开头外每一个字节和位置都完全相同”。i.MX6ULL 的具体启动设备、启动镜像搜索和头部解释规则应以芯片参考手册的 System Boot 章节为准。NXP i.MX6ULL 产品与文档页、i.MX6ULL Reference Manual

DCD 参数具有明确的硬件依赖。把另一块板、另一种 DDR 型号或另一种布线拓扑的 u-boot.imx 直接烧入,可能在串口初始化以前就因 DDR 不可用而无任何输出;这时不能仅从“黑屏/无日志”判断 U-Boot C 代码本身有错误。

1.3 Uboot的启动流程

视频把流程概括为“汇编部分 + C 语言部分”,适合作为第一次接触时的索引。对本课程这套 U-Boot 2016.03,更准确的源码主线是:

i.MX6ULL Boot ROM
  → arch/arm/lib/vectors.S:异常向量与复位入口
  → arch/arm/cpu/armv7/start.S:reset、CPU 早期状态处理
  → arch/arm/lib/crt0.S:_main,建立栈和全局数据
  → common/board_f.c:board_init_f(),重定位前初始化
  → 重定位代码、向量、栈和 global data,清零 BSS
  → common/board_r.c:board_init_r(),重定位后初始化
  → common/main.c:main_loop(),自动启动或进入命令行
  → bootcmd:装载 zImage 与 DTB,执行 bootz
  → Linux 内核

U-Boot 官方初始化说明也把典型流程概括为架构相关入口、board_init_f()、重定位、board_init_r(),最终进入 main_loop();同时指出不同架构和板卡可能存在差异。U-Boot:Board Initialisation Flow

视频借用 Keil 工程中的 startup.s 说明“C 的 main() 以前还有启动代码”,这个类比是有效的,但 U-Boot 并非直接跳到普通应用程序的 main()。本源码使用大写 .S 文件;.s 和 .S 都表示汇编源文件,但 GNU 工具链通常约定大写 .S 先经过 C 预处理器,可使用 #include、#ifdef 等预处理功能。

1.3.1 汇编部分

从上电到完成重定位前,可以按以下顺序理解:

  1. **Boot ROM 先执行。**CPU 复位后最先运行的不是 vectors.S,而是 i.MX6ULL 片内 ROM。ROM 根据启动配置选择 eMMC 等介质,查找并解释 u-boot.imx,执行 DCD 寄存器写入,使 DDR 具备基本工作条件,再装载并跳转到 U-Boot。
  2. **进入已知的 CPU 状态。**U-Boot 的复位入口会建立合适的异常向量和 ARMv7-A 特权状态,屏蔽 IRQ/FIQ,并让 CP15、MMU、Cache 等处于代码预期的早期状态。SVC32 是 32 位 ARM 的 Supervisor 特权模式,允许执行普通用户态不能执行的系统控制操作。
  3. **不要把“关”理解成统一清单。**是否禁用、重新配置或喂看门狗取决于 SoC、板级代码和构建选项;关闭看门狗也不等于天然提高健壮性,量产系统常依赖看门狗从死机中恢复。早期暂时关闭 MMU/Cache 的主要目的是在页表、内存属性和缓存一致性尚未建立时取得确定状态,而不是“防止程序员碰到物理地址”。MMU 能做地址转换、权限与内存属性控制;进程隔离还需要操作系统共同完成。
  4. **先建立 C 代码可依赖的环境。**启动代码要准备临时栈和 global_data 等基本状态,随后才能安全调用 board_init_f()。串口、时钟、DRAM 探测等很多初始化实际由 C 函数完成,不能全部归到汇编指令名下。
  5. **计算并执行重定位。**U-Boot 在可用 DDR 顶部为自身、堆、全局数据和栈安排位置,再复制代码、修正相关地址/向量、切换栈并清零 BSS,进入重定位后的 board_init_r()。重定位的意义不只是“DDR 比 Flash 快”,还包括为完整运行期数据结构、堆栈和后续镜像腾出有规划的内存布局。

“U-Boot 自己从 Flash 搬到内存”只是概念化说法。在本板的 eMMC 启动中,Boot ROM 已先按 .imx 结构把 U-Boot 装入可执行位置,随后 U-Boot 再把自身重定位到最终 DDR 地址;不是所有字节都必然由 U-Boot 直接从 eMMC 再读一遍。其他平台还可能使用 SPL、片内 SRAM、XIP 或不同的装载地址,因此不能把某一条搬运路径推广为所有 U-Boot。U-Boot 初始化流程、SPL 启动说明

1.3.2 C语言部分

重定位完成后,board_init_r() 所在的主体阶段可以正常使用 DDR、BSS、堆和全局变量,并按配置继续初始化环境变量、控制台、MMC、网络、USB、显示等子系统。这里的“初始化网卡、USB、屏幕”不表示每块板一定启用所有设备,更不表示 U-Boot 会完成 Linux 驱动级别的全部功能;实际执行项由 .config、板级代码和初始化序列决定。

随后 main_loop() 处理启动延时和命令:用户在倒计时内按键,可停在 U-Boot 命令行;无人干预时则执行 bootcmd。本课程 eMMC 配置的典型动作是从 MMC 文件系统把 zImage 和选定的 .dtb 读到各自的 DDR 地址,设置内核命令行,然后通过 bootz 把内核地址和 FDT 地址交给 Linux。U-Boot bootz 命令文档

这里要纠正原笔记最重要的一点:

  • 内核与 DTB:本课程启动日志明确显示由 U-Boot 从 eMMC 读入 DDR;
  • initramfs/initrd:若启动配置使用,U-Boot也会把它读入 DDR并把地址传给内核;
  • 普通 eMMC 根文件系统:通常不会由 U-Boot 整体“搬到内存”。本课程内核启动后直接挂载 mmcblk1p2 上的 EXT3 根文件系统,文件按需从 eMMC 读取;
  • 控制权移交:U-Boot 跳入内核后,内核解压/初始化、挂载根文件系统并执行 init,U-Boot 的正常引导任务至此结束。Linux 官方文档也明确区分了内存中的 initramfs 与后来挂载的真实根设备。Linux rootfs、ramfs 与 initramfs 文档

把两阶段压缩成一句话,可记为:**重定位前阶段建立“U-Boot 自己能够可靠运行”的条件;重定位后阶段建立“能够选择并启动操作系统”的条件。**下一节再沿 vectors.S → start.S → crt0.S → board_init_f() → board_init_r() → main_loop() 逐个文件核对源码。

二、完整操作速查

以下命令保持课程的默认 eMMC 构建路径;已经完成传输或解压时,应从对应步骤继续,不要无故重复删除目录:

# 1. 安装视频明确列出的三个依赖,以及视频随后补装的 make。
sudo apt-get install lzop libncurses5-dev u-boot-tools
sudo apt install make

# 2. 创建课程工作目录。Linux 路径区分大小写。
cd ~
mkdir -p Linux/uboot
cd Linux/uboot

# 3. 此处通过 FileZilla/SFTP 上传课程压缩包,然后核对哈希。
sha256sum uboot-imx-rel_imx_4.1.15_2.1.0_ga_20200512.tar.gz

# 4. 解压并进入源码根目录。
tar -xvf uboot-imx-rel_imx_4.1.15_2.1.0_ga_20200512.tar.gz
cd uboot-imx-rel_imx_4.1.15_2.1.0_ga

# 5. 核对工具链。前缀应与 create.sh 一致。
command -v arm-linux-gnueabihf-gcc
arm-linux-gnueabihf-gcc --version
make --version

# 6. 不带参数时构建课程的 eMMC 配置。
./create.sh

# 7. 核对结果;课程实际烧写的是 u-boot.imx。
ls -lh u-boot u-boot.bin u-boot.imx
file u-boot u-boot.bin u-boot.imx
./tools/mkimage -l u-boot.imx
sha256sum u-boot.imx

预期至少满足:脚本返回码为 0;u-boot 是 ARM ELF;u-boot.bin 与 u-boot.imx 均存在且 .imx 更大;mkimage 能识别 i.MX 镜像;准备烧写的文件名确为 u-boot.imx。这些是主机侧验收,最终仍须通过开发板串口日志确认 Boot ROM、DDR、U-Boot 和 Linux 启动链均正常。

三、本节关键纠正

  • U-Boot 是引导加载程序,不是含义不明的“逻辑程序”;Linux 内核也不等于完整 Linux 用户空间。
  • 安装命令中的包名是 lzop、libncurses5-dev、u-boot-tools,不存在原笔记所写的 narcos。
  • 视频源码文件名是 create.sh,不是 crate.sh;它的默认分支构建 eMMC 配置,传入 nand 才构建 NAND 配置。
  • make: command not found 表示主机缺少 make,不是交叉编译器内部少了一个组件。
  • .o 是可重定位目标文件;u-boot 是 ARM ELF,u-boot.bin 是扁平负载,u-boot.imx 是 i.MX ROM 启动镜像,三者不能都笼统称为“可执行文件”。
  • uboot.rmx 是转录错误,正确文件名是 u-boot.imx。
  • .imx 不只“多一点 DDR 信息”,还含 IVT、Boot Data、DCD 等 i.MX Boot ROM 所需结构;本课程 DCD 的确包含 DDR、IOMUX、时钟/MMDC 寄存器配置。
  • CPU 上电后先运行片内 Boot ROM,再进入 U-Boot;U-Boot 也不是直接从汇编跳到普通应用式 main()。
  • 关 MMU/Cache 是为了取得可控的早期 CPU 状态;是否关闭看门狗取决于平台,不能把“全部关闭”当成通用安全准则。
  • U-Boot 重定位不是在所有平台上都等于“自己直接从 Flash 复制到 DDR”;装载位置和是否使用 SPL/XIP 取决于 SoC 与配置。
  • U-Boot 通常把内核和 DTB 装入 DDR,但 eMMC 根文件系统一般由 Linux 直接挂载,不会整套先搬入内存。

核对依据