嵌入式Linux之U-Boot
1. 前言
1.1 为什么嵌入式离不开U-Boot?
嵌入式Linux无法直接启动内核,必须由Bootloader 完成硬件初始化、内存映射、内核加载、参数传递等前置工作。U-Boot(Universal Boot Loader)是当前嵌入式Linux最主流的开源Bootloader ,支持ARM/x86/RISC-V等多架构,兼容NAND/eMMC/SD/Flash等存储,解决了硬件初始化复杂、内核启动繁琐、多存储适配、量产调试四大核心痛点。
| 嵌入式启动痛点 | U-Boot的解决思路 |
|---|---|
| 硬件未初始化,内核无法运行 | 初始化CPU/时钟/DDR/串口/存储,搭建内核运行环境 |
| 存储介质多样(SD/eMMC/NAND) | 统一驱动接口,支持多介质加载内核与设备树 |
| 启动参数需灵活配置(串口/根文件系统/IP) | 提供环境变量,动态修改启动参数无需重编译 |
| 量产烧录与现场调试 | 命令行交互,支持烧写、读取、校验镜像 |

1.2 U-Boot核心定位
U-Boot是硬件与Linux内核之间的桥梁,是嵌入式系统上电后第一个运行的软件,核心职责:
- 初始化核心硬件,让系统从"裸机"进入"可运行内核"状态
- 提供交互式命令行,用于调试、烧录、配置启动策略
- 从存储加载Linux内核(zImage/uImage)与设备树(dtb)到内存
- 传递启动参数(bootargs)给内核,完成内核启动

1.3 嵌入式Linux标准启动流程
Boot ROM → U-Boot → Linux Kernel → Root Filesystem
| 启动阶段 | 运行载体 | 核心工作 |
|---|---|---|
| Boot ROM | 芯片内置ROM | 上电自动运行,检测启动介质,加载U-Boot前导程序 |
| U-Boot | DDR | 初始化硬件、加载内核、传递参数、启动内核 |
| Linux Kernel | DDR | 内核初始化、驱动加载、挂载根文件系统 |
| Rootfs | 存储/内存 | 启动init进程,运行应用程序 |

2. U-Boot核心基础
2.1 核心功能
- 硬件初始化:CPU、PLL、DDR、GPIO、串口、以太网、存储控制器
- 交互控制:串口命令行,支持在线调试与配置
- 镜像管理:读取/烧写/校验内核、设备树、文件系统镜像
- 启动引导:支持从SD/eMMC/NAND/网络启动内核
- 环境变量 :动态配置启动参数、设备路径、网络信息

2.2 关键特性
- 开源免费,社区活跃,支持绝大多数嵌入式处理器
- 轻量级,体积小,适配嵌入式资源受限场景
- 可裁剪,通过配置关闭无用功能,减小体积
- 跨平台,同一套源码支持多架构、多开发板
3. U-Boot完整启动流程
U-Boot启动分为汇编阶段 与C语言阶段 ,先底层初始化,再高层功能初始化,最后启动内核。

3.1 第一阶段:汇编初始化(arch/arm/cpu/start.S)
核心目标:关中断、关MMU、初始化栈、最小硬件初始化、跳转到C语言
-
关闭CPU中断与MMU,进入裸机运行模式
-
初始化CPU时钟与栈指针(SP),为C语言运行准备环境
-
初始化DDR控制器(最关键,没有DDR无法运行C语言)
-
复制U-Boot自身到DDR(部分芯片需要)

-
跳转到C语言入口函数
board_init_f
3.2 第二阶段:C语言初始化(board_init_r)
核心目标:初始化外设、命令系统、环境变量、准备启动内核
- 初始化串口、以太网、存储(SD/eMMC/NAND)等外设
- 初始化命令系统,支持串口交互
- 加载环境变量(从Flash/SD/eMMC)
- 自动检测启动介质,执行默认启动命令
- 等待用户输入命令,或自动启动内核
3.3 启动内核流程
- 读取环境变量
bootcmd,执行启动命令 - 从存储加载
zImage(内核)与dtb(设备树)到DDR指定地址 - 设置
bootargs启动参数,告诉内核串口、根文件系统路径 - 调用
bootz/bootm命令,跳转到内核入口,移交控制权
4. U-Boot源码编译与配置
4.1 源码获取
bash
# 官方源码(推荐稳定版)
git clone https://github.com/u-boot/u-boot.git
cd u-boot
# 切换到稳定版本
git checkout v2023.10
4.2 配置命令
bash
# 清理旧配置
make distclean
# 加载开发板默认配置(以IMX6ULL为例)
make mx6ull_14x14_evk_defconfig
# 图形化配置(可选)
make menuconfig
4.3 编译命令
bash
# 交叉编译(指定ARM交叉编译器)
make CROSS_COMPILE=arm-linux-gnueabihf- -j4
4.4 编译产物说明
| 产物文件 | 作用 | 烧录位置 |
|---|---|---|
| u-boot.bin | 二进制镜像,直接烧录到存储 | 启动介质起始位置 |
| u-boot.img | 带头部信息的镜像,部分芯片专用 | 芯片指定偏移地址 |
| MLO | 部分芯片(TI AM335x)的前导程序 | 存储起始位置 |
| u-boot.dtb | U-Boot自身设备树(非内核dtb) | 无需烧录,调试用 |
5. U-Boot高频实用命令
5.1 信息查询类
| 命令 | 作用 |
|---|---|
| version | 查看U-Boot版本 |
| bdinfo | 查看板级信息(DDR大小、MAC地址) |
| printenv | 查看所有环境变量 |
5.2 环境变量类
| 命令 | 作用 | 示例 |
|---|---|---|
| setenv | 设置环境变量 | setenv bootdelay 3 |
| saveenv | 保存环境变量到存储 | saveenv |
| editenv | 编辑环境变量 | editenv bootargs |
5.3 存储操作类
| 命令 | 作用 | 示例 |
|---|---|---|
| mmc list | 列出MMC/SD设备 | mmc list |
| mmc dev 0 | 切换到SD卡0 | mmc dev 0 |
| fatload | 从FAT分区读取文件到DDR | fatload mmc 0:1 0x80800000 zImage |
| nand write | 烧写镜像到NAND Flash | nand write 0x80800000 kernel 0x500000 |
5.4 启动控制类
| 命令 | 作用 | 适用场景 |
|---|---|---|
| bootz | 启动zImage格式内核 | ARM Linux主流 |
| bootm | 启动uImage格式内核 | 旧版内核 |
| boot | 执行bootcmd自动启动 | 量产默认启动 |
6. U-Boot设备树(DTB)
设备树是描述硬件信息的文件,U-Boot负责加载dtb到内存并传递给内核,内核无需重编译即可适配不同硬件。
6.1 核心作用
- 分离硬件描述与内核代码,提升内核可移植性
- 描述CPU、DDR、外设、中断、GPIO等硬件信息
- U-Boot传递dtb地址给内核,内核解析后初始化硬件
6.2 加载设备树命令
bash
# 从SD卡加载dtb到DDR 0x83000000
fatload mmc 0:1 0x83000000 imx6ull-14x14-evk.dtb
7. 启动Linux内核
以SD卡启动ARM Linux为例,完整命令流程:
bash
# 1. 设置启动参数(串口、根文件系统)
setenv bootargs 'console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw'
# 2. 加载内核到DDR 0x80800000
fatload mmc 0:1 0x80800000 zImage
# 3. 加载设备树到DDR 0x83000000
fatload mmc 0:1 0x83000000 imx6ull-14x14-evk.dtb
# 4. 启动内核(内核地址 + dtb地址)
bootz 0x80800000 - 0x83000000
# 5. 保存配置,下次自动启动
setenv bootcmd 'fatload mmc 0:1 0x80800000 zImage; fatload mmc 0:1 0x83000000 imx6ull-14x14-evk.dtb; bootz 0x80800000 - 0x83000000'
saveenv
8. U-Boot移植核心要点
U-Boot移植是适配新开发板的核心工作,重点修改4个部分:
- DDR初始化
修改板级配置文件(board/freescale/mx6ullevk/),配置DDR参数(时序、位宽、容量),这是移植成败关键。 - 时钟配置
修改CPU PLL时钟参数,保证系统稳定运行。 - 存储驱动
适配SD/eMMC/NAND控制器,确保能读写存储介质。 - 环境变量存储
指定环境变量保存位置(SD/eMMC/NAND),确保配置断电不丢失。
9. 常见问题与排查
9.1 无串口输出
- 排查:串口参数(115200 8N1)、串口线连接、U-Boot串口初始化配置
- 解决:检查
board_config.h中串口宏定义,确保串口时钟配置正确
9.2 DDR初始化失败
- 现象:U-Boot运行到DDR初始化就死机
- 解决:重新校准DDR时序参数,核对硬件原理图与配置参数
9.3 无法加载内核
- 排查:存储设备选择错误、镜像路径错误、DDR地址冲突
- 解决:用
mmc list确认存储设备,核对fatload地址与内核大小
9.4 内核启动后死机
- 排查:bootargs参数错误、dtb与内核不匹配、根文件系统异常
- 解决:核对
root=/dev/xxx路径,确保dtb与内核版本一致
10. 总结
- 核心定位 :U-Boot是嵌入式Linux的启动基石,负责硬件初始化与内核引导
- 启动流程:汇编底层初始化 → C语言高层初始化 → 加载内核 → 启动内核
- 实战核心:环境变量配置、存储操作、内核+dtb加载、启动命令执行
- 移植关键:DDR初始化、时钟配置、存储驱动、环境变量存储
- 设计原则:轻量级、可裁剪、适配硬件、稳定可靠