uboot编译启动流程
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 ROM | SoC 片内 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.imx | Freescale/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 汇编部分
从上电到完成重定位前,可以按以下顺序理解:
- **Boot ROM 先执行。**CPU 复位后最先运行的不是
vectors.S,而是 i.MX6ULL 片内 ROM。ROM 根据启动配置选择 eMMC 等介质,查找并解释u-boot.imx,执行 DCD 寄存器写入,使 DDR 具备基本工作条件,再装载并跳转到 U-Boot。 - **进入已知的 CPU 状态。**U-Boot 的复位入口会建立合适的异常向量和 ARMv7-A 特权状态,屏蔽 IRQ/FIQ,并让 CP15、MMU、Cache 等处于代码预期的早期状态。SVC32 是 32 位 ARM 的 Supervisor 特权模式,允许执行普通用户态不能执行的系统控制操作。
- **不要把“关”理解成统一清单。**是否禁用、重新配置或喂看门狗取决于 SoC、板级代码和构建选项;关闭看门狗也不等于天然提高健壮性,量产系统常依赖看门狗从死机中恢复。早期暂时关闭 MMU/Cache 的主要目的是在页表、内存属性和缓存一致性尚未建立时取得确定状态,而不是“防止程序员碰到物理地址”。MMU 能做地址转换、权限与内存属性控制;进程隔离还需要操作系统共同完成。
- **先建立 C 代码可依赖的环境。**启动代码要准备临时栈和
global_data等基本状态,随后才能安全调用board_init_f()。串口、时钟、DRAM 探测等很多初始化实际由 C 函数完成,不能全部归到汇编指令名下。 - **计算并执行重定位。**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 直接挂载,不会整套先搬入内存。
核对依据
- 视频:
videos/330.3.3uboot编译&启动流程.mp4 - 转录:
transcripts/330.3.3uboot编译&启动流程.txt、transcripts/330.3.3uboot编译&启动流程.srt - 课程源码:
源码/5、ARM-Linux完全入门/代码&源代码/uboot&内核源码/uboot-imx-rel_imx_4.1.15_2.1.0_ga_20200512.tar.gz - 课程源码内核对文件:
create.sh、Makefile、arch/arm/config.mk、arch/arm/imx-common/Makefile、board/freescale/mx6ul_topeet/imximage.cfg、doc/README.imximage、configs/mx6ul_topeet_emmc_defconfig - U-Boot:Building with GCC
- U-Boot:Board Initialisation Flow
- U-Boot:Booting from TPL/SPL
- NXP i.MX BSP Porting Guide
- NXP i.MX6ULL Reference Manual
- Linux:rootfs、ramfs 与 initramfs