目录
一、什么是 Bootloader
Bootloader(引导加载程序)是嵌入式系统上电后运行的第一段代码,负责初始化最小硬件环境、加载操作系统内核并将控制权交给内核。它在硬件与 OS 之间扮演"桥梁"角色。
类比 PC 上的 BIOS/UEFI:PC 启动时 BIOS 完成硬件自检(POST)、初始化外设、读取磁盘引导扇区,然后跳转到 MBR/VBR 加载 GRUB/Windows Boot Manager。嵌入式系统没有统一的 BIOS,Bootloader 集成了 BIOS + GRUB 两层职责。
1.1 为什么需要 Bootloader
嵌入式系统上电后 CPU 处于初始状态:Cache 未初始化、内存控制器未配置、外设未启动,无法直接运行 OS。Bootloader 解决三大核心问题:
- 硬件最小初始化:配置 CPU 时钟、内存控制器(DDR)、UART、Flash 控制器,让系统能运行复杂代码
- 内核加载:从 Flash/SD/eMMC/网络读取 OS 内核镜像到 RAM,准备启动参数
- 权力移交:关闭中断、清空 Cache、跳转到内核入口,让 OS 接管系统
几乎所有 SoC(如 i.MX、STM32MP1、RK3399)都在芯片内部固化了Boot ROM(不可修改的启动代码)。上电后 CPU 首先执行 Boot ROM,它根据 boot cfg 引脚状态从 SD/NAND/NOR/QSPI/USB 加载下一段代码(通常是 SPL)。这是芯片厂商写死的,用户不可改。
二、嵌入式系统启动全流程
以 i.MX6 / RK3399 / STM32MP1 为典型,嵌入式系统从上电到 Linux 用户登录共经历五个阶段:
2.1 Boot ROM 的工作
Boot ROM 是芯片出厂时烧录在芯片内部 ROM 中的代码,无法修改。它在 CPU 复位向量地址执行,主要工作:
- 读取 boot cfg 引脚:用户通过拨码开关/电阻配置启动介质(SD/eMMC/NAND/NOR/USB/UART)
- 加载 SPL:从指定介质读取固定大小的 SPL 镜像(通常 < 32KB,可放入 SRAM)
- 校验头部:检查 SPL 镜像头的 magic number、校验和、加载地址
- 跳转执行:将 PC 指针指向 SPL 入口地址
2.2 为什么需要 SPL
现代 SoC 上电后 DDR 还未初始化,只有芯片内部 SRAM 可用(通常 32~128KB)。完整 U-Boot 有几百 KB,无法塞进 SRAM。所以SPL(Second Program Loader)作为"过渡阶段"出现:
- SPL 体积小:< 32KB,能放进 SRAM
- SPL 任务单一:仅初始化 DDR、UART、Timer,然后加载完整 U-Boot 到 DDR
- SPL 是 U-Boot 的"精简版":复用 U-Boot 的代码框架,但只编译最少驱动
有人认为 Bootloader = U-Boot,实际上 U-Boot 只是 Bootloader 的一种实现。完整启动链是 Boot ROM → SPL → U-Boot,其中 SPL 是 U-Boot 项目的一部分(在 build 目录里叫 u-boot-spl.bin)。
三、Bootloader 的两大职责
无论使用哪种 Bootloader(U-Boot、Barebox、CoreBoot),它们都有两个核心职责:硬件初始化和内核加载。
3.1 硬件初始化的具体内容
| 类别 | 初始化内容 | 典型代码位置 |
|---|---|---|
| CPU 初始化 | 关闭中断、关闭 MMU、清 Cache、设置 SVC 模式 | start.S / cpu_init.c |
| 时钟初始化 | 配置 PLL、CPU/AXI/AHB/APB 分频 | clk/clock.c |
| DDR 初始化 | 配置 DDR 控制器、PHY 训练、时序参数 | ddr/ddr_init.c |
| 管脚复用 | IOMUX 配置引脚功能(UART/SDC/GPIO) | pinctrl.c |
| 外设初始化 | UART 调试口、Timer、Flash/SD 控制器 | drivers/ |
3.2 内核加载的三种方式
| 加载方式 | 介质 | 命令 | 典型场景 |
|---|---|---|---|
| 本地加载 | SD/eMMC/NAND/NOR | fatload / ext4load / nand read | 量产产品 |
| 网络加载 | TFTP / NFS | tftpboot / nfs | 开发调试 |
| RAM 加载 | USB/UART 下载到 RAM | loadb / loads / usbboot | 烧写空板 |
四、主流 Bootloader 对比
嵌入式领域有多种 Bootloader,U-Boot 是最广泛使用的,但并非唯一选择。
| Bootloader | 支持架构 | 典型场景 | 开放性 |
|---|---|---|---|
| U-Boot | ARM/MIPS/PPC/x86/RISC-V | 嵌入式 Linux 通用 | GPLv2 完全开放 |
| Barebox | ARM/MIPS/x86 | 工业嵌入式 | GPLv2 完全开放 |
| CoreBoot | x86/ARM/RISC-V | 服务器/Chromebook | GPLv2 完全开放 |
| Little Kernel (LK) | ARM/ARM64 | Android Boot | MIT 开放 |
| RedBoot | ARM/MIPS/x86 | eCos 系统遗留 | GPLv2 / eCos license |
• 通用嵌入式 Linux 产品 → U-Boot(生态最成熟,厂商支持最好)
• 工业级设备、追求代码质量 → Barebox(更干净、命令行更友好)
• x86 服务器/Chromebook → CoreBoot(替代 BIOS)
• Android 设备 → Little Kernel + ABL(高通/MTK 默认)
五、U-Boot 简介:历史与地位
U-Boot(Das U-Boot,全称"the U-Boot")由德国 DENX 软件工程公司 Wolfgang Denk 主导开发,2000 年开源发布,基于 PowerPC 时代的 8xxrom 演化而来,逐渐成为 ARM/MIPS 嵌入式 Linux 的事实标准 Bootloader。
5.1 U-Boot 的核心特点
- 架构覆盖广:ARM/MIPS/PPC/x86/RISC-V,几乎全平台
- 命令行交互:提供类似 shell 的命令行,开发期友好
- 环境变量持久化:bootargs、bootcmd 等存储在 Flash/eMMC
- 多阶段启动:SPL + U-Boot Main,支持 SRAM 启动
- Driver Model:类 Linux 的设备/驱动/总线三层架构
- 设备树原生支持:DTB 解析、FDT 操作、Overlay
- 网络协议栈:TFTP、NFS、DHCP、Ping、HTTP
- 文件系统支持:FAT/ext2/ext4/UBIFS/JFFS2/SquashFS
- 镜像格式:legacy uImage / FIT image / raw
- EFI 支持:可启动 EFI 应用,做 BIOS 替代
U-Boot 生态分两层:主线(mainline)由社区维护,质量高但厂商支持滞后;厂商 BSP(Board Support Package)由芯片厂商基于主线 fork,加入自家驱动但常滞后。开发流程:BSP 启动 → 推动改动回流主线 → 长期靠主线维护。
六、U-Boot 架构与目录结构
U-Boot 源码采用类似 Linux 内核的目录组织,让有 Linux 经验的开发者快速上手。
6.1 三层代码分层
| 层级 | 目录 | 作用 |
|---|---|---|
| 架构层 | arch/arm/cpu、arch/arm/lib | CPU 启动汇编、MMU、Cache、中断 |
| 开发板层 | board/<vendor>/<board>/ | 板级初始化、DDR 配置、外设 pinmux |
| 通用层 | common/、drivers/、lib/ | 通用命令、设备驱动、库函数 |
6.2 Kconfig 配置系统
U-Boot 借鉴 Linux 的 Kconfig + Kbuild 系统,每个开发板对应一个 configs/<board>_defconfig 文件:
# configs/rpi_4_defconfig(树莓派 4B 默认配置) CONFIG_ARM=y CONFIG_TARGET_RPI_4=y CONFIG_SYS_ARCH="arm" CONFIG_SYS_CPU="armv8" CONFIG_USE_ARCH=64 CONFIG_ARCH_BCM283X=y CONFIG_NR_DRAM_BANKS=1 CONFIG_ENV_SIZE=0x4000 CONFIG_ENV_OFFSET=0x3c0000 # 启用 SPL(可选) CONFIG_SPL=y CONFIG_SPL_STACK_R=y # 启用 Driver Model CONFIG_DM=y CONFIG_DM_GPIO=y CONFIG_DM_MMC=y CONFIG_DM_SERIAL=y # 启用网络 CONFIG_CMD_NET=y CONFIG_CMD_DHCP=y CONFIG_CMD_TFTPPUT=y # 启用文件系统 CONFIG_CMD_FAT=y CONFIG_CMD_EXT4=y CONFIG_CMD_FS_GENERIC=y # 启用 bootm / bootz CONFIG_CMD_BOOTM=y CONFIG_CMD_BOOTZ=y CONFIG_FIT=y # FIT 镜像支持
七、SPL 与多阶段启动
SPL(Second Program Loader)是 U-Boot 的"精简版",专门为 SRAM 启动场景设计。本节详解 SPL/TPL 的多阶段启动机制。
7.1 何时需要 SPL
| 场景 | 需要 SPL? | 原因 |
|---|---|---|
| SRAM < 32KB,DDR 未初始化 | 必须 | U-Boot 太大,放不进 SRAM |
| Boot ROM 直接初始化 DDR | 不需要 | Boot ROM 已准备好 RAM,直接加载 U-Boot |
| DDR 需软件训练(DDR4/LPDDR4) | 需要 TPL | 训练代码需运行在 SRAM,比 SPL 更早 |
| x86 平台 | 不需要 | BIOS/UEFI 已初始化内存 |
make 后在 build 目录生成:•
u-boot-spl.bin:SPL 镜像(精简版,送进 SRAM)•
u-boot.bin:U-Boot Main 镜像(完整版,运行在 DDR)•
u-boot.img:加了 mkimage 头部的 U-Boot Main(Boot ROM 通过头部识别)•
u-boot.itb:FIT 格式镜像(包含 SPL + Main + 多个 DTB)
八、U-Boot 启动流程详解
U-Boot 启动过程从汇编入口到 main 循环,经历多个阶段。下图以 ARM 平台为例展示完整流程:
8.1 启动关键函数
| 函数 | 位置 | 职责 |
|---|---|---|
| _start | arch/arm/cpu/armv8/start.S | 汇编入口,设置异常向量、SVC 模式 |
| cpu_init_cp15 | arch/arm/cpu/armv8/cpu.c | 关闭 MMU、清 Cache、关闭中断 |
| board_init_f | common/board_f.c | 早期初始化(在 SRAM 中运行):GPIO、UART、DDR 准备 |
| relocate_code | arch/arm/lib/relocate.c | 自搬运 U-Boot 到 RAM 高地址 |
| board_init_r | common/board_r.c | 后期初始化(在 RAM 中):DM、env、网络、MMC |
| main_loop | common/main.c | 命令行循环,等待用户输入或自动 boot |
U-Boot 默认启动后会等待 2~3 秒(由
bootdelay 环境变量控制),期间按任意键进入命令行;否则自动执行 bootcmd 环境变量指定的启动命令。这是为了兼顾"量产自动启动"和"开发期调试"两种场景。
九、U-Boot 命令体系与环境变量
U-Boot 命令行是开发期最重要的交互界面,提供类似 Linux shell 的命令体系。每条命令由 U_BOOT_CMD 宏注册。
9.1 环境变量
U-Boot 通过环境变量持久化启动配置,类似 Linux 的 /etc/fstab。常见变量如下:
| 变量名 | 含义 | 典型值 |
|---|---|---|
| bootdelay | 启动延迟(秒) | 2(按任意键进入命令行) |
| bootcmd | 自动启动命令 | run boot_sd |
| bootargs | 传给内核的参数 | console=ttyS0,115200 root=/dev/mmcblk0p2 |
| bootfile | 网络加载文件名 | zImage |
| serverip | TFTP 服务器 IP | 192.168.1.100 |
| ipaddr | 本机 IP | 192.168.1.10 |
| ethaddr | MAC 地址 | 00:1c:42:00:00:01 |
9.2 常用命令一览
| 命令 | 用途 | 示例 |
|---|---|---|
| printenv | 打印环境变量 | printenv bootcmd |
| setenv | 设置环境变量 | setenv bootdelay 5 |
| saveenv | 保存到 Flash | saveenv |
| fatload | 从 FAT 分区加载 | fatload mmc 0:1 0x80008000 zImage |
| ext4load | 从 ext4 分区加载 | ext4load mmc 0:2 0x88000000 /boot/dtb |
| tftpboot | TFTP 网络加载 | tftpboot 0x80008000 zImage |
| bootm | 启动 uImage | bootm 0x80008000 - 0x88000000 |
| bootz | 启动 zImage | bootz 0x80008000 - 0x88000000 |
| booti | 启动 ARM64 Image | booti 0x80080000 - 0x88000000 |
| md | 显示内存 | md.b 0x80008000 0x40 |
| mw | 写内存 | mw.l 0x80008000 0xdeadbeef 0x10 |
| go | 跳转执行 | go 0x80008000 |
| reset | 软复位 | reset |
| bcb | Android Boot Control | bcb boot_slot a |
9.3 启动命令组合
# 典型 SD 卡启动命令组合 # 1. 加载内核到 0x80008000 U-Boot> fatload mmc 0:1 0x80008000 zImage # 2. 加载设备树到 0x88000000 U-Boot> fatload mmc 0:1 0x88000000 bcm2837-rpi-3-b.dtb # 3. 设置内核命令行 U-Boot> setenv bootargs console=ttyAMA0,115200 root=/dev/mmcblk0p2 rootwait # 4. 启动 U-Boot> bootz 0x80008000 - 0x88000000 # 一键启动组合(保存到环境变量) U-Boot> setenv boot_sd "fatload mmc 0:1 0x80008000 zImage; \ fatload mmc 0:1 0x88000000 rpi-3-b.dtb; \ bootz 0x80008000 - 0x88000000" U-Boot> setenv bootcmd "run boot_sd" U-Boot> saveenv # 网络启动(开发期常用) U-Boot> setenv serverip 192.168.1.100 U-Boot> setenv ipaddr 192.168.1.10 U-Boot> tftpboot 0x80008000 zImage U-Boot> tftpboot 0x88000000 rpi-3-b.dtb U-Boot> bootz 0x80008000 - 0x88000000
•
printenv 查看当前配置,saveenv 持久化•
run <cmd> 执行环境变量中的命令组合•
editenv <name> 在线编辑长命令•
env default -a 恢复默认配置(救命稻草)
十、设备树:DTB 与 FDT
设备树(Device Tree,DT)是从 PowerPC 时代引入的硬件描述语言,Linux 内核通过它知道板卡有哪些外设、寄存器地址、中断号。U-Boot 在启动时将 DTB(DT Blob 二进制)传给内核。
10.1 设备树源码示例
/dts-v1/; / { # 根节点 model = "Raspberry Pi 4 Model B"; compatible = "raspberrypi,4-model-b", "brcm,bcm2711"; # CPU 节点 cpus { cpu@0 { device_type = "cpu"; compatible = "arm,cortex-a72"; reg = <0>; }; }; # 内存节点 memory@0 { device_type = "memory"; reg = <0 0x80000000>; # 起始地址 + 大小 }; # UART 节点 uart@7e215040 { compatible = "arm,pl011", "arm,primecell"; reg = <0x7e215040 0x40>; interrupts = <0 121 4>; clock-names = "uartclk", "apb_pclk"; status = "okay"; }; # SD 控制器节点 sdhci@7e340000 { compatible = "brcm,bcm2711-emmc2"; reg = <0x7e340000 0x100>; interrupts = <0 126 4>; bus-width = <8>; status = "okay"; }; };
10.2 U-Boot 中的 fdt 命令
U-Boot 提供 fdt 命令在启动前修改设备树(如动态修改 bootargs、调整内存大小):
# U-Boot 命令行修改设备树 U-Boot> fdt addr 0x88000000 # 设置 DTB 地址 U-Boot> fdt print /memory # 打印 memory 节点 U-Boot> fdt set /memory reg <0 0x40000000> # 改内存大小为 1GB U-Boot> fdt set /chosen bootargs "console=ttyAMA0,115200" U-Boot> fdt print /chosen
对于"模块化板卡"(如 BeagleBone Cape、Raspberry Pi HAT),U-Boot 支持在运行时叠加 DTBO(Overlay 文件),动态扩展基础 DTB。这在工业模块化设计中非常有用。
十一、Driver Model 驱动框架
U-Boot 在 2014 年引入 Driver Model(DM),借鉴 Linux 的 device/driver/bus 三层架构,让驱动代码模块化、可复用。在此之前,U-Boot 驱动是"散装"的,每个驱动都自己写初始化逻辑。
11.1 DM 驱动示例
// drivers/serial/serial_pl011.c —— ARM PL011 UART 驱动 #include <dm/device.h> #include <serial.h> #include "pl011.h" // 1. 实现 uclass 接口(serial_ops) static int pl011_setbrg(struct udevice *dev, int baudrate) { struct pl011_uart *plat = dev_get_plat(dev); unsigned int divisor = UART_PL011_CLOCK / (16 * baudrate); writel(divisor & 0xFFFF, plat->base + UART_PL011_LDIV); return 0; } static int pl011_getc(struct udevice *dev) { struct pl011_uart *plat = dev_get_plat(dev); return readl(plat->base + UART_PL011_DR); } static const struct dm_serial_ops pl011_serial_ops = { .putc = pl011_putc, .getc = pl011_getc, .setbrg = pl011_setbrg, }; // 2. probe 函数(设备绑定后调用) static int pl011_probe(struct udevice *dev) { struct pl011_uart *plat = dev_get_plat(dev); plat->base = dev_read_addr(dev); writel(0x70, plat->base + UART_PL011_CR); // 使能 UART return 0; } // 3. 设备匹配(compatible 字符串) static const udevice_id pl011_ids[] = { { .compatible = "arm,pl011" }, { .compatible = "arm,primecell" }, { } }; // 4. 注册到 DM 框架 U_BOOT_DRIVER(pl011_serial) = { .name = "pl011_serial", .id = UCLASS_SERIAL, .of_match = pl011_ids, .probe = pl011_probe, .ops = &pl011_serial_ops, .plat_auto = sizeof(struct pl011_uart), };
• 代码复用:同一驱动可服务多个 udevice(如多个 UART)
• 解耦:上层 uclass 接口稳定,下层 driver 可替换
• 设备树绑定:通过
compatible 字符串自动匹配驱动• 测试友好:uclass 提供测试桩,便于单元测试
十二、烧写方式:SD/NAND/NOR/网络
U-Boot 镜像如何烧写到目标板?常见方式有 4 类:
每种 SoC 都有固定的 Boot ROM 加载偏移(offset),错了就启动不起来:
• i.MX6/7 SD 卡:offset 0x400(1KB) 开始放 SPL
• 树莓派 SD 卡:FAT 分区根目录放 bootcode.bin/start.elf/kernel8.img
• Rockchip SD 卡:offset 0x40(64 个扇区)放 idbloader(SPL)
• i.MX NAND:按 mtd 分区表,u-boot 在 mtd1
12.1 SD 卡烧写完整命令
# 在 Linux 主机烧写 U-Boot 到 SD 卡 sudo dd if=u-boot-spl.bin of=/dev/sdX seek=2 bs=512 # SPL 从第 2 扇区开始 sudo dd if=u-boot.bin of=/dev/sdX seek=64 bs=512 # U-Boot Main 从第 64 扇区开始 sync # 树莓派用单独的脚本 sudo dd if=u-boot.bin of=/dev/sdX seek=8 # 树莓派从扇区 8 开始 sync # 使用 u-boot 自身的 ums 命令把 SD 卡当 U 盘 U-Boot> ums 0 mmc 0 # 把 SD 卡 (mmc 0) 通过 USB 暴露给主机
十三、引导内核:bootm / bootz / booti
U-Boot 引导 Linux 内核有三种命令,对应不同内核镜像格式:
| 命令 | 镜像格式 | 典型用途 |
|---|---|---|
| bootm | uImage(mkimage 打包) | 传统 ARM 32 位 |
| bootz | zImage(gzip 压缩) | ARM 32 位压缩内核 |
| booti | Image(未压缩原始格式) | ARM64 / RISC-V |
13.1 bootm 命令详解
# bootm 命令格式 bootm <kernel_addr> <ramdisk_addr> <fdt_addr> # 实际使用示例 U-Boot> fatload mmc 0:1 0x80008000 uImage U-Boot> fatload mmc 0:1 0x83000000 initramfs.cpio.gz U-Boot> fatload mmc 0:1 0x88000000 board.dtb U-Boot> bootm 0x80008000 0x83000000 0x88000000 # 无 initramfs 时用 - 占位 U-Boot> bootm 0x80008000 - 0x88000000
13.2 FIT 镜像(Flattened Image Tree)
对于多内核 + 多 DTB + 多 initramfs 的场景,U-Boot 引入了 FIT 镜像——一个 itb 文件包含多个组件,类似设备树的层级结构。
// image.its —— FIT 镜像描述文件(类似 DTS) /dts-v1/; / { description = "Multi image for board X"; #images = "kernel", "fdt"; kernel { description = "Linux kernel"; data = /incbin/("zImage"); type = "kernel"; arch = "arm"; os = "linux"; compression = "gzip"; load = <0x80008000>; entry = <0x80008000>; }; fdt { description = "Board device tree"; data = /incbin/("board.dtb"); type = "flat_dt"; arch = "arm"; compression = "none"; }; configurations { default = "conf"; conf { description = "Default configuration"; kernel = "kernel"; fdt = "fdt"; }; }; }; # 编译命令 mkimage -f image.its image.itb # U-Boot 中启动 U-Boot> fatload mmc 0:1 0x80008000 image.itb U-Boot> bootm 0x80008000 # 自动选择 default configuration U-Boot> bootm 0x80008000#conf # 指定配置
bootz/booti 不需要 mkimage 打包头部,直接用原始 zImage/Image 即可。U-Boot 内部自动检测镜像 magic,跳到入口。
现代开发优先用 FIT,因为它支持多镜像、签名验证(Secure Boot)。
十四、实战:编译 U-Boot for Raspberry Pi
本节以树莓派 4B 为例,演示完整的 U-Boot 编译流程。树莓派是学习 U-Boot 的最佳平台——无需 JTAG、SD 卡可启动、社区文档丰富。
14.1 环境准备
# Ubuntu 主机安装依赖 sudo apt install build-essential gcc-aarch64-linux-gnu \ bison flex libssl-dev bc python3 python3-pyelftools # 拉取 U-Boot 源码(主线 v2024.04 LTS) git clone https://github.com/u-boot/u-boot.git cd u-boot git checkout v2024.04
14.2 配置与编译
# 1. 配置树莓派 4B(64 位) make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- rpi_4_defconfig # 2. 可选:菜单配置(启用/禁用特性) make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- menuconfig # 3. 编译 make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j$(nproc) # 产物 ls -lh u-boot.bin ls -lh u-boot-nodtb.bin # 不含 DTB 的版本(树莓派专用)
14.3 烧写到 SD 卡
# 假设 SD 卡为 /dev/sdX(请按实际替换) sudo dd if=u-boot.bin of=/dev/sdX seek=8 bs=512 conv=fsync sync # 在 SD 卡第一个分区(FAT32)放入内核与设备树 sudo mount /dev/sdX1 /mnt sudo cp zImage /mnt/ sudo cp bcm2711-rpi-4-b.dtb /mnt/ # config.txt 告诉树莓派固件跳到 U-Boot echo "kernel=u-boot.bin" | sudo tee /mnt/config.txt sudo umount /mnt
14.4 启动验证
将 SD 卡插入树莓派 4B,连接 UART-USB 转串口到 PC,波特率 115200,上电后应看到:
U-Boot 2024.04 (Sep 09 2026 - 12:34:56 +0800) DRAM: 4 GiB RPI 4 Model B (0xb03114) Core: 105 devices, 18 uclasses, devicetree: separate MMC: mmcnr@7e202000: 0, emmc2@7e340000: 1 Loading Environment from FAT... OK In: serial,usbkbd Out: vidconsole,serial Err: vidconsole,serial Net: eth0: ethernet@7e580000 Hit any key to stop autoboot: 2 # 倒计时 Hit any key to stop autoboot: 1 Hit any key to stop autoboot: 0 U-Boot> # 进入命令行
14.5 配置自动启动
# 在 U-Boot 命令行配置 U-Boot> setenv bootargs "console=serial0,115200 console=tty1 root=/dev/mmcblk0p2 rootwait" U-Boot> setenv bootcmd "fatload mmc 0:1 0x80080000 Image; \ fatload mmc 0:1 0x88000000 bcm2711-rpi-4-b.dtb; \ booti 0x80080000 - 0x88000000" U-Boot> saveenv U-Boot> reset
• 用
dmesg 在内核启动后看是否识别到 U-Boot 传参• 用
md.b 0x80080000 0x100 看内核镜像头部•
printenv 查看当前环境变量,saveenv 持久化• 用
dm tree 查看所有 udevice(DM 调试利器)• 用
bdinfo 看 U-Boot 自身信息(编译时间、DDR 大小)
十五、调试技巧与常见问题
15.1 调试工具链
| 工具 | 用途 | 典型场景 |
|---|---|---|
| UART 串口 | 查看 U-Boot 启动日志 | 必备,调试期主输出 |
| JTAG (OpenOCD) | 断点、单步、内存查看 | SPL 阶段死机时 |
| QEMU | 虚拟机模拟启动 | 无需硬件快速验证 |
| Wireshark | 抓 TFTP/网络包 | 网络加载失败 |
| hexdump + strings | 分析 u-boot.bin | 镜像检查 |
15.2 常见故障排查
| 故障现象 | 可能原因 | 排查方法 |
|---|---|---|
| 上电无任何输出 | UART 接线错/波特率错/电源不足 | 用示波器看 UART TX;确认 3.3V |
| 启动到 DDR 初始化挂死 | DDR 时序参数错/电压错 | 用 JTAG 单步;查 DDR 配置 |
| SPL 卡住不再继续 | SPL 大小超限/链接地址错 | 看 u-boot-spl.bin 是否 < 32KB |
| U-Boot 提示 ROM 加载失败 | 烧写位置错/介质未识别 | 读 SoC 手册确认 boot cfg 与 offset |
| 找不到 SD 卡 | 未启用 DM_MMC/pinmux 错 | 用 mmc dev 测试;查 defconfig |
| 网络不通 | ethaddr 未设/IP 配置错 | setenv ethaddr;ping serverip |
| 内核启动后无输出 | bootargs console 错 | 确认 console=ttyXXX 与 UART 一致 |
| bootm 报 "Bad Magic" | 加载的不是 uImage 镜像 | 用 bootz/booti 或重新 mkimage |
| 设备树不生效 | DTB 没加载到 fdt_addr | 确认 bootm 第三个参数 |
① UART 串口是调试主输出,没有串口等于"盲飞"
② SPL 卡住常见原因是 DDR 配置错误——用 JTAG 看 DDR PHY 训练日志
③ bootcmd 改错导致启不来?按任意键进 U-Boot 命令行,
env default -a 恢复默认④ 烧错位置是 Boot ROM 不识别 SD 卡的常见原因——查 SoC datasheet 的 boot cfg
十六、总结
U-Boot 是嵌入式 Linux 启动的"第一段代码"——从 Boot ROM 接棒,到交给内核,承担了硬件初始化、镜像加载、参数传递三大职责。它如同桥梁,连接了芯片固化的 Boot ROM 与可扩展的 Linux 内核。
核心要点速记
- Bootloader 是嵌入式系统上电第一段代码,承担硬件初始化与内核加载
- 启动五阶段:Boot ROM → SPL → U-Boot Main → Linux Kernel → 用户态
- SPL 是 U-Boot 的精简版,运行在 SRAM,仅初始化 DDR,加载 U-Boot Main 到 RAM
- 三大职责:硬件初始化(CPU/DDR/外设)+ 镜像加载(SD/NAND/网络)+ 权力移交(bootm)
- 主流 Bootloader:U-Boot(80% 嵌入式)、Barebox、CoreBoot、Little Kernel
- U-Boot 架构:arch(架构层)+ board(板级层)+ common/drivers/lib(通用层)
- Kconfig 配置:每个开发板一个 defconfig 文件
- 环境变量:bootcmd/bootargs/bootdelay 持久化在 Flash
- 设备树:DTS → DTB,描述硬件拓扑,U-Boot 可在线修改后传给内核
- Driver Model:uclass(类别)+ udevice(设备)+ driver(驱动)三层架构
- 引导命令:bootm(uImage)/ bootz(zImage)/ booti(ARM64 Image)
- FIT 镜像:多组件打包,支持多内核多 DTB + 签名验证
- 烧写四法:SD(dd)/ NAND(nand 命令)/ NOR(sf 命令)/ 网络(tftpboot)
- 烧写位置:每个 SoC 有固定的 Boot ROM offset,错了启动不起来
- 定位:U-Boot 是"嵌入式 Linux 启动的桥梁"——从硬件接棒,到内核交棒
嵌入式工业通信与系统启动系列至此涵盖 串口/RS-485/CAN/EtherCAT/Modbus/PROFINET + Bootloader/U-Boot 七大主题。前六篇覆盖"运行期通信",本篇覆盖"启动期引导"——两者结合,构成了嵌入式 Linux 从上电到运行的全栈知识。