title: U-Boot启动流程详解 tags: [U-Boot, 启动流程, 嵌入式, Makefile, ARM] created: 2026-09-17 updated: 2026-09-17 pdf_ref:
关联知识: [[01-ARM架构与裸机编程/02-ARM汇编与启动流程]] | [[06-系统移植与启动/01-U-Boot基础与命令]]
mindmap
root((U-Boot源码))
arch/ 体系结构
board/ 板级支持
cmd/ 命令实现
common/ 通用代码
drivers/ 设备驱动
configs/ 配置文件
include/ 头文件
Makefile 顶层构建
| 目录 | 用途 | 关键子目录 |
|---|---|---|
arch/ |
体系结构相关代码 | arm/, arm64/, riscv/ |
board/ |
板级定制代码 | freescale/, ti/, rockchip/ |
cmd/ |
U-Boot命令实现 | bootm.c, md.c, tftp.c |
common/ |
通用启动代码 | main.c, bootm.c, cli.c |
drivers/ |
设备驱动程序 | serial/, mmc/, net/ |
fs/ |
文件系统支持 | ext4/, fat/, ubifs/ |
include/ |
头文件 | configs/, asm/ |
net/ |
网络协议栈 | tftp.c, nfs.c, bootp.c |
lib/ |
库函数 | string.c, printf.c |
tools/ |
构建工具 | mkimage, binman |
arch/arm/
+-- cpu/
| +-- armv7/
| | +-- start.S # 启动入口
| | +-- lowlevel_init.S
| | +-- u-boot.lds # 链接脚本模板
| +-- u-boot.lds
+-- imx-common/ # NXP i.MX系列通用代码
+-- mach-mx6/ # i.MX6系列SoC支持
+-- lib/
| +-- vectors.S # 中断向量表
| +-- crt0.S # C运行时初始化
| +-- board.c
+-- include/asm/arch-mx6/ # i.MX6寄存器定义
board/freescale/
+-- mx6ullevk/ # NXP官方开发板
| +-- mx6ullevk.c
| +-- imximage-ddr512.cfg.cfgtmp
| +-- plugin.S
+-- mx6ull_alientek_emmc/ # 正点原子EMMC版
| +-- mx6ull_alientek_emmc.c
| +-- imximage-ddr512.cfg.cfgtmp
| +-- panel.c
+-- mx6ull_alientek_nand/ # 正点原子NAND版
+-- common/
+-- etherboot.c
| 文件 | 说明 | 重要程度 |
|---|---|---|
arch/arm/cpu/armv7/start.S |
启动入口代码 | 最高 |
arch/arm/lib/crt0.S |
C运行时初始化 | 最高 |
arch/arm/lib/vectors.S |
中断向量表 | 高 |
arch/arm/cpu/u-boot.lds |
链接脚本模板 | 高 |
board/freescale/mx6ull_alientek_emmc/ |
板级支持包 | 高 |
include/configs/mx6ull_alientek_emmc.h |
板级配置 | 最高 |
configs/mx6ull_14x14_ddr512_emmc_defconfig |
默认配置 | 最高 |
| 文件 | 来源 | 用途 |
|---|---|---|
u-boot |
ELF格式镜像 | 调试用 |
u-boot.bin |
objcopy生成 | 二进制可执行镜像 |
u-boot-nodtb.bin |
无设备树版本 | u-boot.bin的源文件 |
u-boot.imx |
mkimage添加头部 | NXP专用,含IVT/DCD |
u-boot.lds |
链接脚本 | 定义内存布局 |
u-boot.map |
映射文件 | 查看函数链接地址 |
.config |
配置文件 | 编译选项 |
# .u-boot.bin.cmd - 生成u-boot.bin
cmd_u-boot.bin := cp u-boot-nodtb.bin u-boot.bin
# .u-boot-nodtb.bin.cmd - ELF转二进制
cmd_u-boot-nodtb.bin := arm-linux-gnueabihf-objcopy \
--gap-fill=0xff -j .text -j .rodata -j .data \
-j .got -j .got.plt -j .u_boot_list -j .rel.dyn \
-O binary u-boot u-boot-nodtb.bin
# .u-boot.imx.cmd - 生成NXP专用镜像
cmd_u-boot.imx := ./tools/mkimage \
-n board/freescale/mx6ull_alientek_emmc/imximage.cfg.cfgtmp \
-T imximage -e 0x87800000 -d u-boot.bin u-boot.imx
# 版本信息
VERSION = 2016
PATCHLEVEL = 03
SUBLEVEL = 00
EXTRAVERSION = -alientek
# 交叉编译工具链
CROSS_COMPILE ?= arm-linux-gnueabihf-
# 架构和CPU配置
ARCH ?= arm
CONFIG_SYS_ARCH="arm"
CONFIG_SYS_CPU="armv7"
CONFIG_SYS_SOC="mx6"
CONFIG_SYS_VENDOR="freescale"
CONFIG_SYS_BOARD="mx6ull_alientek_emmc"
CONFIG_SYS_CONFIG_NAME="mx6ull_alientek_emmc"
| 命令 | 说明 |
|---|---|
make xxx_defconfig |
配置U-Boot |
make menuconfig |
图形化配置 |
make all |
编译所有目标 |
make u-boot.bin |
生成二进制镜像 |
make u-boot.imx |
生成NXP专用镜像 |
make clean |
清理编译文件 |
make mrproper |
彻底清理 |
flowchart TD
A[make xxx_defconfig] --> B[silentoldconfig]
B --> C[生成 .config]
C --> D[生成 include/config/]
D --> E[生成 include/generated/]
E --> F[配置完成]
u-boot: $(OBJS) $(LIBS) u-boot.lds FORCE
$(LD) -pie --gc-sections -Bstatic \
-Ttext 0x87800000 \
-o u-boot -T u-boot.lds \
arch/arm/cpu/armv7/start.o \
--start-group $(OBJS) $(LIBS) --end-group
u-boot-nodtb.bin: u-boot FORCE
$(OBJCOPY) --gap-fill=0xff \
-j .text -j .rodata -j .data -j .got -j .u_boot_list \
-O binary u-boot u-boot-nodtb.bin
u-boot.bin: u-boot-nodtb.bin
cp u-boot-nodtb.bin u-boot.bin
OUTPUT_FORMAT("elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
. = 0x87800000; /* U-Boot加载地址 */
.text :
{
*(.__image_copy_start)
*(.vectors)
arch/arm/cpu/armv7/start.o (.text*)
*(.text*)
}
.rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }
.data : { *(.data*) }
.u_boot_list : { KEEP(*(SORT(.u_boot_list*))); }
.bss : { *(.bss*) }
}
关键地址变量:
| 变量 | 数值 | 描述 |
|---|---|---|
__image_copy_start |
0x87800000 | U-Boot拷贝首地址 |
__image_copy_end |
0x8785dd54 | U-Boot拷贝结束地址 |
__rel_dyn_start |
0x8785dd54 | .rel.dyn段起始 |
__bss_start |
0x8785dd54 | BSS段起始 |
__bss_end |
0x878a8e74 | BSS段结束 |
# 链接选项
-DCONFIG_SYS_TEXT_BASE=0x87800000
-pie
--gc-sections
# 编译选项
-O2
-fpie
# 安全选项
-fstack-protector-strong
-D_FORTIFY_SOURCE=2
flowchart TD
A[Kconfig] --> B[配置系统]
B --> C[.config]
C --> D[Kbuild]
D --> E[编译规则]
E --> F[OBJ文件]
F --> G[链接]
G --> H[U-Boot镜像]
$ make mx6ull_14x14_ddr512_emmc_defconfig
HOSTCC scripts/basic/fixdep
HOSTCC scripts/kconfig/conf.o
SHIPPED scripts/kconfig/conf
# configuration written to .config
$ make -j8
arm-linux-gnueabihf-gcc ... arch/arm/cpu/armv7/start.S
arm-linux-gnueabihf-gcc ... arch/arm/lib/crt0.S
...
arm-linux-gnueabihf-ld ... u-boot
arm-linux-gnueabihf-objcopy ... u-boot-nodtb.bin
cp u-boot-nodtb.bin u-boot.bin
./tools/mkimage ... u-boot.imx
flowchart TD
A[上电/复位] --> B[reset向量]
B --> C[save_boot_params]
C --> D[save_boot_params_ret]
D --> E[cpu_init_cp15]
E --> F[cpu_init_crit]
F --> G[lowlevel_init]
G --> H[_main入口]
H --> I[board_init_f_alloc_reserve]
I --> J[board_init_f]
J --> K[relocate_code重定位]
K --> L[relocate_vectors]
L --> M[c_runtime_cpu_setup]
M --> N[清零BSS段]
N --> O[board_init_r]
O --> P[main_loop主循环]
flowchart LR
subgraph SPL
A1[初始化DDR] --> A2[初始化串口]
A2 --> A3[加载U-Boot]
end
subgraph U-Boot
B1[初始化外设] --> B2[网络初始化]
B2 --> B3[命令初始化]
B3 --> B4[环境变量]
end
subgraph Linux
C1[加载内核] --> C2[加载设备树]
C2 --> C3[传递参数]
C3 --> C4[跳转执行]
end
A3 --> B1
B4 --> C1
三个阶段职责:
| 阶段 | 职责 | 关键函数 |
|---|---|---|
| SPL | 初始化DDR、串口,加载U-Boot | board_init_f(), board_init_r() |
| U-Boot | 初始化更多外设,网络,命令 | board_init_r(), main_loop() |
| Linux | 挂载rootfs,启动init | start_kernel() |
OCRAM (内部RAM):
0x00900000 +---------------------+
| OCRAM起始 |
0x0091FF00 +---------------------+ <- CONFIG_SYS_INIT_SP_ADDR
| 256B GD结构体 |
0x00920000 +---------------------+ OCRAM结束 (128KB)
DRAM (外部DDR):
0x80000000 +---------------------+
| DRAM起始 |
0x80800000 +---------------------+ <- Linux内核加载地址
| |
0x83000000 +---------------------+ <- 设备树加载地址
| |
0x84000000 +---------------------+ <- 根文件系统
| |
0x87800000 +---------------------+ <- U-Boot加载地址
| U-Boot代码 |
0x878a8e74 +---------------------+ <- BSS段结束
+---------------------+
SPL (Secondary Program Loader) 阶段职责:
+-- 1. 初始化CPU时钟
+-- 2. 初始化DDR控制器
+-- 3. 初始化串口控制台
+-- 4. 从存储介质加载U-Boot
| +-- MMC/SD卡
| +-- NAND Flash
| +-- SPI Flash
| +-- USB
+-- 5. 跳转到U-Boot执行
SPL启动时序:
sequenceDiagram
participant ROM as Boot ROM
participant SPL as SPL
participant DDR as DDR控制器
participant UART as 串口
participant MMC as MMC/SD卡
participant UBoot as U-Boot
ROM->>SPL: 加载SPL到OCRAM
SPL->>SPL: 初始化CPU时钟
SPL->>DDR: 初始化DDR控制器
DDR-->>SPL: DDR就绪
SPL->>UART: 初始化串口
SPL->>MMC: 从MMC读取U-Boot
MMC-->>SPL: U-Boot镜像
SPL->>UBoot: 跳转到U-Boot入口
U-Boot主阶段职责:
+-- 1. 初始化更多外设 (NAND/EMMC/USB/网络/显示)
+-- 2. 网络初始化 (网卡驱动/IP配置/TFTP/NFS)
+-- 3. 命令初始化 (注册命令/解析bootargs/启动脚本)
+-- 4. 环境变量处理 (读取/设置默认值/保存)
+-- 5. 启动Linux内核 (加载zImage/设备树/传递参数/跳转)
flowchart TD
A[board_init_f执行] --> B[计算重定位地址]
B --> C[复制U-Boot到新位置]
C --> D[修正重定位表]
D --> E[更新全局数据指针]
E --> F[跳转到新地址继续执行]
F --> G[board_init_r执行]
网络初始化流程:
+-- 1. 网卡驱动初始化 (读取MAC/配置PHY/初始化DMA)
+-- 2. 网络协议栈初始化 (ARP/IP/UDP/TFTP/NFS)
+-- 3. 网络参数配置
| +-- ipaddr - 开发板IP
| +-- serverip - 服务器IP
| +-- gatewayip - 网关
| +-- netmask - 子网掩码
+-- 4. 网络测试 (ping/TFTP下载)
// 常用启动命令
bootm [addr] [initrd] [fdt] // 启动内核镜像
bootz [addr] [initrd] [fdt] // 启动zImage内核
run <script> // 执行启动脚本
boot // 执行bootcmd
/ * arch/arm/cpu/armv7/start.S * /
_start:
b reset @ 0x00: 跳转到reset向量处理
ldr pc, _undefined_instruction @ 0x04: 未定义指令异常
ldr pc, _software_interrupt @ 0x08: 软件中断
ldr pc, _prefetch_abort @ 0x0c: 取指令中断
ldr pc, _data_abort @ 0x10: 数据中断
ldr pc, _not_used @ 0x14: 未使用
ldr pc, _irq @ 0x18: IRQ中断
ldr pc, _fiq @ 0x1c: FIQ中断
reset:
mrs r0, cpsr @ 读取CPSR寄存器
orr r0, r0, #0x1f @ 设置模式为SVC模式 (0x13 | 0x1c = 0x1f)
msr cpsr, r0 @ 禁止IRQ和FIQ中断
bl save_boot_params @ 保存启动参数
bl cpu_init_cp15 @ 初始化CP15协处理器
bl cpu_init_crit @ 初始化关键硬件
b _main @ 跳转到_main入口
cpu_init_cp15:
@ 禁用MMU
mcr p15, 0, r0, c8, c7, 0 @ 清零TLB表
mcr p15, 0, r0, c7, c5, 0 @ 清零指令缓存
mcr p15, 0, r0, c7, c5, 6 @ 清零BP表
mrc p15, 0, r0, c1, c0, 0 @ 读取SCTLR
bic r0, r0, #0x000d @ 关闭MMU和数据缓存
bic r0, r0, #0x1100 @ 关闭I-Cache和切换缓存
mcr p15, 0, r0, c1, c0, 0 @ 写入SCTLR
@ 启用I-Cache
mrc p15, 0, r0, c1, c0, 0 @ 读取SCTLR
orr r0, r0, #0x0004 @ 启用D-Cache作为普通内存
mcr p15, 0, r0, c1, c0, 0 @ 写入SCTLR
mov pc, lr @ 返回
lowlevel_init:
@ 检查是否在OCRAM中执行
mov r9, lr
bl is_in_ocram
cmp r0, #1
beq 1f
@ 不在OCRAM中,跳转到基杼地址执行
ldr sp, =CONFIG_SYS_INIT_SP_ADDR
push {r0-r3, r12, lr}
1:
ldr sp, =CONFIG_SYS_INIT_SP_ADDR
push {lr}
bl board_early_init_f @ 板级早期初始化
pop {pc}
CPSR模式位对照表:
| 位置 | 模式 | 值 | 说明 |
|---|---|---|---|
| [4:0] | USR | 10000 | 用户模式 |
| [4:0] | SVC | 10011 | 管理模式 (启动初始化) |
| [4:0] | IRQ | 10010 | IRQ中断 |
| [4:0] | FIQ | 10001 | FIQ中断 |
| [5] | T | 1 | 快速中断入口 |
| [6] | F | 1 | FIQ禁用 |
| [7] | I | 1 | IRQ禁用 |
_main:
ldr sp, =CONFIG_SYS_INIT_SP_ADDR
bl board_init_f_alloc_reserve @ 预留GD和栈空间
mov sp, r0 @ sp指向栈顶
bl board_init_f @ 初始化全局数据结构
@ relocate_code重定位完成后返回
bl board_init_r @ 主阶段初始化
board_init_f_alloc_reserve 内存布局:
CONFIG_SYS_INIT_SP_ADDR:
+---------------------+ <- sp初始值 (0x0091FF00)
| |
| 栈空间 |
| (512B) |
+---------------------+
| 256B GD结构 |
| (global_data_t) |
+---------------------+ <- sp更新后
| 512B GD->malloc |
| 缓冲区 |
+---------------------+
void board_init_f(ulong boot_flags)
{
gd_t *id;
gd = (gd_t *)_text; // 设置全局数据指针
// 清零GD结构
memset(gd, 0, sizeof(gd_t));
// 执行编程时初始化回调
init_sequence_f[] = {
setup_mon_len, // 设置监控器长度
init_fnc_t, // 初始化函数表
console_init_f, // 控制台早期初始化
serial_init, // 串口初始化
dram_init, // DRAM初始化
NULL
};
// 计算重定位后地址
fdt_register_boot_chart(gd->fdt_blob);
}
void board_init_r(gd_t *gd, ulong dest_addr)
{
// 重定位后的初始化
// 初始化回调函数表
initr_serial, // 串口初始化
initr_console, // 控制台初始化
initr_board, // 板级外设初始化
initr_ide, // IDE初始化
initr_nand, // NAND初始化
initr_mmc, // MMC初始化
initr_net, // 网络初始化
initr_env, // 环境变量初始化
initr_std, // 标准IO初始化
initr_boot, // 启动脚本初始化
NULL
}
void main_loop(void)
{
// 处理环境变量
process_fdt_options();
// 执行启动命令
if (bootdelay >= 0) {
autoboot_command(); // 执行bootcmd
}
// 启动脚本
run_command_list();
// 进入交互式命令行
cli_loop();
}
void boot_jump_linux(bootm_headers_t *images)
{
// 获取内核入口函数指针
void (*kernel_entry)(int zero, int machid, void *fdt);
kernel_entry = (void (*)(int, int, void *))images->ep;
// 打印启动信息
printf("Starting kernel ...\n");
// 调用内核入口函数
kernel_entry(0, machid, r2);
// r2指向设备树DTB
// machid = MACH_TYPE_MX6ULL
}
调用关系图:
flowchart TD
A[reset] --> B[cpu_init_cp15]
B --> C[cpu_init_crit]
C --> D[lowlevel_init]
D --> E[_main]
E --> F[board_init_f_alloc_reserve]
F --> G[board_init_f]
G --> H[relocate_code]
H --> I[board_init_r]
I --> J[main_loop]
J --> K[autoboot_command]
K --> L[bootm/bootz]
L --> M[boot_jump_linux]
M --> N[kernel_entry]
int bootz_setup(ulong image, ulong *initrd_start, ulong *initrd_end,
ulong *fdt_start)
{
struct zimage_headers *hdr = (struct zimage_headers *)image;
// 验证内核魔数
if (hdr->code0 != ZIMAGE_MAGIC) {
printf("Bad zImage magic number\n");
return -1;
}
// 获取内核起始地址
*initrd_start = hdr->start;
// 获取DTB地址
*fdt_start = hdr->start + hdr->offset;
return 0;
}
关键魔数:
| 魔数 | 值 | 说明 |
|---|---|---|
| ZIMAGE_MAGIC | 0x016f2818 | zImage标识 |
| MACH_TYPE_MX6ULL | 0xFFFFFFFF | i.MX6ULL机器类型 |
| DTB_MAGIC | 0xd00dfeed | 设备树魔数 |
flowchart LR
A[make xxx_defconfig] --> B[make menuconfig]
B --> C[make all]
C --> D[u-boot.bin]
C --> E[u-boot.imx]
configs/mx6ull_14x14_ddr512_emmc_defconfig
+-- 定义板级配置
+-- 包含必选组件
+-- 设置默认环境变量
Boot options ->
+-- Default device tree name: imx6ull-14x14-alientek-emmc
+-- Default boot command: bootm
General setup ->
+-- U-Boot version string: 2016.03-alientek
ARM architecture ->
+-- 选择i.MX6 SoC支持
+-- 选择开发板型号
# 1. 配置
make mx6ull_14x14_ddr512_emmc_defconfig
# 2. 编译
make all -j$(nproc)
# 3. 查看编译信息
make --just-print # 仅打印命令不执行
# 4. 查看配置
cat .config | grep CONFIG
# 5. 清理
make clean # 清理编译文件
make mrproper # 彻底清理(含.config)
flowchart LR
A[获取u-boot.imx] --> B[插入SD卡]
B --> C[sudo dd if=u-boot.imx of=/dev/sdb bs=1M seek=2]
C --> D[确认刷录完成]
D --> E[插入开发板启动]
dd命令参数说明:
| 参数 | 说明 |
|---|---|
if=u-boot.imx |
输入文件 |
of=/dev/sdb |
输出设备 |
bs=1M |
块大小 |
seek=2 |
跳过2个块 |
conv=notrunc |
不截断原文件 |
| 对比项 | i.MX6ULL | STM32MP1 | RK3568 |
|---|---|---|---|
| 内核加载地址 | 0x80800000 | 0xC2000000 | 0x00208000 |
| U-Boot加载地址 | 0x87800000 | 0xC4000000 | 0x00200000 |
| DTB加载地址 | 0x83000000 | 0xC4F00000 | 0x00F00000 |
| OCRAM | 128KB | 256KB | 无 |
| SPL存储 | eMMC/SD/USB | eMMC/SD | eMMC/SD/SPI |
| 烧录工具 | dd/mkimage | STM32CubeProgrammer | rkdevtool |
| 特殊处理 | IVT+DCD头 | FSBL+SSBL | TPL+SPL |
flowchart LR
subgraph i.MX6ULL
A1[Boot ROM] --> A2[SPL]
A2 --> A3[U-Boot]
A3 --> A4[Linux]
end
subgraph STM32MP1
B1[Boot ROM] --> B2[FSBL]
B2 --> B3[SSBL]
B3 --> B4[Linux]
end
subgraph RK3568
C1[Boot ROM] --> C2[TPL]
C2 --> C3[SPL]
C3 --> C4[U-Boot]
C4 --> C5[Linux]
end
这是基于i.MX6ULL SoC的设计约束:
- OCRAM范围是0x00900000-0x00920000(128KB),只能作为启动缓存
- DRAM起始于0x80000000,需要留出空间给内核和设备树
- 0x87800000位于DRAM中间位置,既不会冲突内核(0x80800000), 也不会冲突设备树(0x83000000)
作用:
- 将U-Boot从初始加载地址拷贝到最终运行地址
- 修正全局变量引用和函数指针
- 释放低地址内存给SPL使用
原因:
- SPL加载U-Boot到固定地址0x87800000
- 最终运行地址可能不同,需要重定位到未来运行地址
- 释放初始加载区域,避免内存浪费
OCRAM结束地址 = 0x00900000 + 128KB = 0x00920000 GD大小 = 248字节(对齐后256字节) 留出栈空间 = 512字节 SP = 0x00920000 - 256 - 512 = 0x0091FF00
- r0 = 0: 未使用
- r1 = machid: 机器类型ID,如MACH_TYPE_MX6ULL
- r2 = dtb地址: 设备树DTB在DRAM中的地址
- bootargs: 通过bootargs环境变量传递
- console=ttyS0,115200 串控台配置
- root=/dev/mmcblk0p2 根文件系统
- rdinit=/sbin/init 初始化程序
1. 串口无输出: 检查串口驱动、时钟配置 2. DDR初始化失败: 检查DDR参数配置 3. SPL加载失败: 检查存储驱动、分区表 4. U-Boot加载失败: 检查存储器、镜像格式 5. 内核启动失败: 检查内核地址、DTB地址 6. rootfs挂载失败: 检查rootfs路径、分区表
参考资料:
- U-Boot官方文档: https://docs.u-boot.org/
- 正点原子STM32MP157开发板教程
- 正点原子电子网: https://www.alientek.com/