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 # reset、save_boot_params、cpu_init_cp15、cpu_init_crit
| | +-- lowlevel_init.S# 低级初始化(设置 SP、调用 s_init)
| +-- u-boot.lds # 链接脚本模板(编译后生成根目录 u-boot.lds)
+-- imx-common/ # NXP i.MX系列通用代码
+-- mach-mx6/ # i.MX6系列SoC支持
+-- lib/
| +-- vectors.S # _start 与中断向量表
| +-- crt0.S # _main 与 C 运行时初始化
| +-- relocate.S # relocate_code / relocate_vectors
+-- 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 .secure_text -j .rodata -j .hash \
-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
# 版本信息(顶层 Makefile 第 5~9 行)
VERSION = 2016
PATCHLEVEL = 03
SUBLEVEL =
EXTRAVERSION =
NAME =
# VERSION/PATCHLEVEL/SUBLEVEL 共同构成 2016.03;EXTRAVERSION、NAME 一般不使用
# 交叉编译工具链(第 245~246 行:主机架构==目标架构时 CROSS_COMPILE 为空)
ifeq ($(HOSTARCH),$(ARCH))
CROSS_COMPILE ?=
endif
# 实际编译时通过 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- 传入,
# 也可直接在顶层 Makefile 中写死 ARCH=arm / CROSS_COMPILE=arm-linux-gnueabihf-
以下CONFIG_SYS_*来自板级配置头文件(经make xxx_defconfig写入.config),而非顶层 Makefile:
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"
CONFIG_SYS_TEXT_BASE=0x87800000
| 命令 | 说明 |
|---|---|
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[配置完成]
# 目标依赖关系(顶层 Makefile)
u-boot: $(u-boot-init) $(u-boot-main) u-boot.lds FORCE
$(call if_changed,u-boot__)
u-boot-nodtb.bin: u-boot FORCE
$(call if_changed,objcopy)
$(call DO_STATIC_RELA,$<,$@,$(CONFIG_SYS_TEXT_BASE))
u-boot.bin: u-boot-nodtb.bin FORCE
$(call if_changed,copy) # 即 cp u-boot-nodtb.bin u-boot.bin
# u-boot-init = $(head-y) = arch/arm/cpu/armv7/start.o
# u-boot-main = $(libs-y) = 所有子目录的 built-in.o
展开后实际执行的链接命令(来自 .u-boot.cmd):
arm-linux-gnueabihf-ld.bfd -pie --gc-sections -Bstatic -Ttext 0x87800000 \
-o u-boot -T u-boot.lds arch/arm/cpu/armv7/start.o \
--start-group arch/arm/cpu/built-in.o arch/arm/cpu/armv7/built-in.o \
arch/arm/imx-common/built-in.o arch/arm/lib/built-in.o \
board/freescale/common/built-in.o \
board/freescale/mx6ull_alientek_emmc/built-in.o \
cmd/built-in.o common/built-in.o disk/built-in.o drivers/built-in.o \
... fs/built-in.o lib/built-in.o net/built-in.o ... --end-group \
arch/arm/lib/eabi_compat.o -lgcc -Map u-boot.map
u-boot.lds 的生成规则:u-boot.lds: $(LDSCRIPT) prepare FORCE($(LDSCRIPT) 即 arch/arm/cpu/u-boot.lds)。
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
. = 0x00000000; /* 注意:链接脚本里是 0x00000000,不是 0x87800000 */
. = ALIGN(4);
.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*))); }
.image_copy_end : { *(.__image_copy_end) }
.rel_dyn_start : { *(.__rel_dyn_start) }
.rel.dyn : { *(.rel*) }
.rel_dyn_end : { *(.__rel_dyn_end) }
.end : { *(.__end) }
_image_binary_end = .;
.bss_start __rel_dyn_start (OVERLAY) : { KEEP(*(.__bss_start)); __bss_base = .; }
.bss __bss_base (OVERLAY) : { *(.bss*) . = ALIGN(4); __bss_limit = .; }
.bss_end __bss_limit (OVERLAY) : { KEEP(*(.__bss_end)); }
...
}
真正的加载地址由链接时的
-Ttext 0x87800000决定,链接脚本本身从0x00000000开始。入口点是_start,定义在arch/arm/lib/vectors.S中,其后紧跟中断向量表(.vectors段)。
关键地址变量(示例编译值,以实际编译为准):
| 变量 | 数值 | 描述 |
|---|---|---|
__image_copy_start |
0x87800000 | U-Boot拷贝首地址 |
__image_copy_end |
0x8785dd54 | U-Boot拷贝结束地址 |
__rel_dyn_start |
0x8785dd54 | .rel.dyn段起始 |
__rel_dyn_end |
0x878668f4 | .rel.dyn段结束 |
_image_binary_end |
0x878668f4 | 镜像结束地址 |
__bss_start |
0x8785dd54 | BSS段起始 |
__bss_end |
0x878a8e74 | BSS段结束 |
# 链接选项(.u-boot.cmd)
-pie # 位置无关可执行文件
--gc-sections # 回收未使用的段
-Bstatic # 静态链接
-Ttext 0x87800000 # U-Boot 链接/加载地址
# 生成 u-boot-nodtb.bin 时(.u-boot-nodtb.bin.cmd)
--gap-fill=0xff
$(call DO_STATIC_RELA,$<,$@,$(CONFIG_SYS_TEXT_BASE)) # 修正 .rel.dyn 重定位表
# 交叉工具变量(顶层 Makefile 第 333~347 行)
CC = $(CROSS_COMPILE)gcc
LD = $(CROSS_COMPILE)ld.bfd(若 ld.bfd 不可用则回退为 $(CROSS_COMPILE)ld)
OBJCOPY = $(CROSS_COMPILE)objcopy
AR = $(CROSS_COMPILE)ar
NM = $(CROSS_COMPILE)nm
OBJDUMP = $(CROSS_COMPILE)objdump
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主循环]
⚠️ 来源说明:本节不属于《I.MX6U嵌入式Linux驱动开发指南》内容,为扩展知识(书中未讲 SPL;此处用于说明 I.MX6U 与常见 SPL 架构的区别)。
I.MX6U 的 U-Boot 不是 SPL 架构(默认直接使用完整的u-boot.imx)。上电后由芯片内部的 Boot ROM 把u-boot.imx加载到 DDR 的0x87800000处执行,之后 U-Boot 自身再经历“重定位前 → 重定位 → 重定位后”三个阶段:
flowchart LR
subgraph ROM[Boot ROM]
A1[解析 u-boot.imx 头部] --> A2[加载到 DDR 0x87800000]
end
subgraph F[board_init_f(重定位前)]
B1[初始化串口/定时器/DDR] --> B2[规划内存并计算重定位地址]
end
subgraph R[board_init_r(重定位后)]
C1[初始化外设/MMC/网络] --> C2[环境变量/控制台] --> C3[main_loop 命令行]
end
subgraph Linux
D1[加载内核与设备树] --> D2[传递参数] --> D3[跳转执行]
end
A2 --> B1
B2 --> R
C3 --> D1
各阶段职责:
| 阶段 | 职责 | 关键函数 |
|---|---|---|
| Boot ROM | 从启动设备加载u-boot.imx到 DDR |
芯片固化代码 |
| board_init_f(重定位前) | 初始化串口/定时器/DDR,init_sequence_f[],计算重定位目的地址 |
board_init_f() |
| 重定位 | 将 U-Boot 拷贝到 DDR 高地址,修正.rel.dyn,重定位向量表 |
relocate_code()、relocate_vectors() |
| board_init_r(重定位后) | 初始化 MMC/NAND/网络/环境变量,进入命令行 | board_init_r()、main_loop() |
| Linux | 挂载rootfs,启动init | boot_jump_linux()、kernel_entry() |
OCRAM (内部RAM, 128KB):
0x00900000 +---------------------+ <- IRAM_BASE_ADDR
| OCRAM |
0x0091FE08 +---------------------+ <- lowlevel_init 中 sp -= GD_SIZE(248)
0x0091FF00 +---------------------+ <- CONFIG_SYS_INIT_SP_ADDR (sp 初始值)
0x00920000 +---------------------+ OCRAM结束 (0x20000, 128KB)
DRAM (外部DDR, 512MB):
0x80000000 +---------------------+ <- DRAM起始 (gd->ram_size=0x20000000)
0x80800000 +---------------------+ <- Linux内核(zImage)加载地址
0x83000000 +---------------------+ <- 设备树(dtb)加载地址
0x87800000 +---------------------+ <- U-Boot链接/初始加载地址
| U-Boot代码 |
0x878a8e74 +---------------------+ <- BSS段结束 (__bss_end)
| ... |
0x9EF44E90 +---------------------+ <- 重定位后最终的 sp (gd->start_addr_sp)
0x9FF47000 +---------------------+ <- U-Boot重定位后的运行地址 (gd->relocaddr)
| ... |
0xA0000000 +---------------------+ <- DRAM最高地址 (gd->ram_top)
_main中先调用board_init_f_alloc_reserve预留早期 malloc 与 gd 区域,再调用board_init_f运行init_sequence_f[],主要工作是初始化串口、定时器、DDR 等,并“自顶向下”规划内存,最终算出重定位目的地址。
board_init_f(重定位前)职责:
+-- 1. setup_mon_len:设置 gd->mon_len = __bss_end - _start
+-- 2. arch_cpu_init / board_early_init_f:CPU 与板级早期初始化(串口 IO)
+-- 3. timer_init / get_clocks:定时器与时钟
+-- 4. env_init / init_baud_rate / serial_init / console_init_f:环境变量、波特率、串口、控制台
+-- 5. dram_init:设置 gd->ram_size(正点原子 EMMC 版为 512MB)
+-- 6. setup_dest_addr:设置 gd->ram_size、gd->ram_top=0xA0000000、gd->relocaddr=0xA0000000
+-- 7. reserve_*:从高地址向下依次预留 MMU TLB、U-Boot、malloc、bd、gd、栈等
+-- 8. setup_reloc:把旧 gd 拷贝到 gd->new_gd
关键中间结果(示例):gd->relocaddr = 0xA0000000 → reserve_mmu后 0x9FFF0000 → reserve_uboot后 0x9FF47000 → reserve_malloc(16MB+8KB)后 0x9EF45000 → reserve_board(80B)→ reserve_global_data(248B)→ reserve_stacks 后最终 gd->start_addr_sp = 0x9EF44E90。
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
入口_start定义在arch/arm/lib/vectors.S,其后就是中断向量表(.vectors 段),第一个向量b reset跳到arch/arm/cpu/armv7/start.S中的reset:
/ * arch/arm/lib/vectors.S * /
_start:
b reset @ 0x00: 复位向量
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
ldr pc, _fiq @ 0x1c
/ * arch/arm/cpu/armv7/start.S * /
reset:
b save_boot_params @ 允许板子保存重要寄存器
ENTRY(save_boot_params)
b save_boot_params_ret @ back to my caller
save_boot_params_ret:
mrs r0, cpsr
and r1, r0, #0x1f @ mask mode bits
teq r1, #0x1a @ test for HYP mode
bicne r0, r0, #0x1f @ clear all mode bits
orrne r0, r0, #0x13 @ set SVC mode
orr r0, r0, #0xc0 @ disable FIQ and IRQ
msr cpsr, r0
@ 设置向量表重定位:清 SCTLR.V,把 _start 写入 VBAR
mrc p15, 0, r0, c1, c0, 0 @ Read CP15 SCTLR
bic r0, #CR_V @ CR_V = (1 << 13)
mcr p15, 0, r0, c1, c0, 0
ldr r0, =_start
mcr p15, 0, r0, c12, c0, 0 @ Set VBAR
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
bl cpu_init_cp15
bl cpu_init_crit
#endif
bl _main
ENTRY(cpu_init_cp15)
/* Invalidate L1 I/D */
mov r0, #0
mcr p15, 0, r0, c8, c7, 0 @ invalidate TLBs
mcr p15, 0, r0, c7, c5, 0 @ invalidate icache
mcr p15, 0, r0, c7, c5, 6 @ invalidate BP array
mcr p15, 0, r0, c7, c10, 4 @ DSB
mcr p15, 0, r0, c7, c5, 4 @ ISB
/* disable MMU stuff and caches */
mrc p15, 0, r0, c1, c0, 0
bic r0, r0, #0x00002000 @ clear bit 13 (--V-)
bic r0, r0, #0x00000007 @ clear bits 2:0 (-CAM)
orr r0, r0, #0x00000002 @ set bit 1 (--A-) Align
orr r0, r0, #0x00000800 @ set bit 11 (Z---) BTB
#ifdef CONFIG_SYS_ICACHE_OFF
bic r0, r0, #0x00001000 @ clear bit 12 (I) I-cache
#else
orr r0, r0, #0x00001000 @ set bit 12 (I) I-cache
#endif
mcr p15, 0, r0, c1, c0, 0
...
mov pc, r5 @ back to my caller
ENDPROC(cpu_init_cp15)
说明:I.MX6U 的 U-Boot 默认打开 I-Cache(未定义
CONFIG_SYS_ICACHE_OFF),并关闭 MMU、D-Cache。
/ * arch/arm/cpu/armv7/lowlevel_init.S * /
ENTRY(lowlevel_init)
ldr sp, =CONFIG_SYS_INIT_SP_ADDR
bic sp, sp, #7 @ 8 字节对齐
#ifdef CONFIG_SPL_BUILD
ldr r9, =gdata
#else
sub sp, sp, #GD_SIZE @ 为 gd 预留空间(GD_SIZE=248)
bic sp, sp, #7
mov r9, sp @ r9 保存 gd 指针
#endif
push {ip, lr}
bl s_init @ 早期初始化(I.MX6ULL 为空函数)
pop {ip, pc}
ENDPROC(lowlevel_init)
CONFIG_SYS_INIT_SP_ADDR = CONFIG_SYS_INIT_RAM_ADDR + (CONFIG_SYS_INIT_RAM_SIZE - GENERATED_GBL_DATA_SIZE) = 0x00900000 + (0x20000 - 256) = 0x0091FF00,再减去 GD_SIZE(248) 后为 0x0091FE08。
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禁用 |
/ * arch/arm/lib/crt0.S * /
ENTRY(_main)
ldr sp, =(CONFIG_SYS_INIT_SP_ADDR) @ sp = 0x0091FF00
bic sp, sp, #7 @ 8 字节对齐
mov r0, sp
bl board_init_f_alloc_reserve @ 预留 malloc 与 gd 区域,返回新 top
mov sp, r0
mov r9, r0 @ r9 = gd 指针
bl board_init_f_init_reserve @ 清零 gd 区域
mov r0, #0
bl board_init_f @ 重定位前初始化
ldr sp, [r9, #GD_START_ADDR_SP] @ sp = gd->start_addr_sp
bic sp, sp, #7
ldr r9, [r9, #GD_BD] @ r9 = gd->bd
sub r9, r9, #GD_SIZE @ 新 gd 位于 bd 之下
adr lr, here
ldr r0, [r9, #GD_RELOC_OFF] @ r0 = gd->reloc_off
add lr, lr, r0
ldr r0, [r9, #GD_RELOCADDR] @ r0 = gd->relocaddr
b relocate_code @ 代码重定位
here:
bl relocate_vectors @ 向量表重定位
bl c_runtime_cpu_setup
ldr r0, =__bss_start
... @ 清零 BSS(memset 或 clbss_l 循环)
mov r0, r9 @ r0 = gd_t
ldr r1, [r9, #GD_RELOCADDR] @ r1 = dest_addr
ldr pc, =board_init_r @ 跳转 board_init_r(不返回)
ENDPROC(_main)
board_init_f_alloc_reserve自栈顶向下预留早期 malloc 区(CONFIG_SYS_MALLOC_F_LEN)与 gd 区并返回新的栈顶,board_init_f_init_reserve再把该区域清零并把 gd 指针存入 r9。
/* common/board_f.c */
void board_init_f(ulong boot_flags)
{
gd_t data;
gd = &data;
gd->flags = boot_flags;
gd->have_console = 0;
/* 通过 initcall_run_list 依次执行 init_sequence_f[] */
if (initcall_run_list(init_sequence_f))
hang();
}
init_sequence_f[](去掉条件编译后的主要函数,按执行顺序):
static init_fnc_t init_sequence_f[] = {
setup_mon_len, /* gd->mon_len = __bss_end - _start */
initf_malloc, /* gd->malloc_limit = CONFIG_SYS_MALLOC_F_LEN = 0x400 */
initf_console_record, /* I.MX6ULL 未定义 CONFIG_CONSOLE_RECORD,直接返回 */
arch_cpu_init,
initf_dm,
arch_cpu_init_dm, /* 未实现 */
mark_bootstage,
board_early_init_f, /* 板级早期初始化(I.MX6ULL 初始化串口 IO) */
timer_init, /* 初始化 Cortex-A 内核定时器 */
board_postclk_init, /* I.MX6ULL:设置 VDDSOC 电压 */
get_clocks, /* I.MX6ULL:获取 sdhc_clk */
env_init, /* 设置 gd->env_addr */
init_baud_rate, /* 根据 baudrate 环境变量设置 gd->baudrate */
serial_init,
console_init_f, /* gd->have_console = 1 */
display_options,
display_text_info,
print_cpuinfo,
show_board_info,
INIT_FUNC_WATCHDOG_INIT,
INIT_FUNC_WATCHDOG_RESET,
init_func_i2c,
announce_dram_init, /* 输出 "DRAM:" */
dram_init, /* 设置 gd->ram_size,EMMC 版为 512MB */
post_init_f,
INIT_FUNC_WATCHDOG_RESET,
testdram, /* 空函数 */
INIT_FUNC_WATCHDOG_RESET,
INIT_FUNC_WATCHDOG_RESET,
setup_dest_addr, /* gd->ram_size/ram_top/relocaddr */
reserve_round_4k,
reserve_mmu, /* TLB 表,64K 对齐 */
reserve_trace,
reserve_uboot, /* 预留 U-Boot 空间(gd->mon_len),4K 对齐 */
reserve_malloc, /* TOTAL_MALLOC_LEN = 16MB + 8KB */
reserve_board, /* bd_t 大小 80B */
setup_machine, /* I.MX6ULL 走设备树,此函数无效 */
reserve_global_data, /* gd_t 大小 248B */
reserve_fdt, /* I.MX6ULL 未用到 */
reserve_arch, /* 空函数 */
reserve_stacks, /* 预留栈空间,16 字节对齐 */
setup_dram_config, /* 设置 gd->bd->bi_dram[0].start/size */
show_dram_config,
display_new_sp,
INIT_FUNC_WATCHDOG_RESET,
reloc_fdt, /* 未用到 */
setup_reloc, /* 拷贝 gd 到 gd->new_gd */
NULL,
};
执行完毕后关键值(示例):gd->ram_size=0x20000000、gd->ram_top=0xA0000000、gd->relocaddr=0x9FF47000、gd->start_addr_sp=0x9EF44E90、gd->reloc_off=0x18747000、gd->new_gd=0x9EF44EB8。
/* common/board_r.c */
void board_init_r(gd_t *new_gd, ulong dest_addr)
{
#ifdef CONFIG_NEEDS_MANUAL_RELOC
for (i = 0; i < ARRAY_SIZE(init_sequence_r); i++)
init_sequence_r[i] += gd->reloc_off;
#endif
if (initcall_run_list(init_sequence_r))
hang();
/* NOTREACHED - run_main_loop() does not return */
hang();
}
init_sequence_r[](去掉条件编译后的主要函数):
init_fnc_t init_sequence_r[] = {
initr_trace,
initr_reloc, /* 设置 gd->flags,标记重定位完成 */
initr_caches, /* 使能 cache */
initr_reloc_global_data,
initr_barrier, /* I.MX6ULL 未用到 */
initr_malloc,
initr_console_record, /* 空函数 */
bootstage_relocate,
initr_bootstage,
board_init, /* 板级初始化:I2C/FEC/USB/QSPI 等 */
stdio_init_tables,
initr_serial,
initr_announce,
INIT_FUNC_WATCHDOG_RESET,
INIT_FUNC_WATCHDOG_RESET,
INIT_FUNC_WATCHDOG_RESET,
power_init_board, /* 正点原子板未用到 */
initr_flash, /* 定义了 CONFIG_SYS_NO_FLASH,无效 */
INIT_FUNC_WATCHDOG_RESET,
initr_nand, /* NAND 版本核心板初始化 NAND */
initr_mmc, /* EMMC 版本核心板初始化 EMMC */
initr_env, /* 初始化环境变量 */
INIT_FUNC_WATCHDOG_RESET,
initr_secondary_cpu, /* 单核,未用 */
INIT_FUNC_WATCHDOG_RESET,
stdio_add_devices, /* 初始化 LCD 等设备 */
initr_jumptable,
console_init_r, /* 控制台初始化 */
INIT_FUNC_WATCHDOG_RESET,
interrupt_init,
initr_enable_interrupts,
initr_ethaddr, /* 读取环境变量 ethaddr 作为 MAC */
board_late_init, /* 定义在 mx6ull_alientek_emmc.c,切换 MMC 设备 */
INIT_FUNC_WATCHDOG_RESET,
INIT_FUNC_WATCHDOG_RESET,
INIT_FUNC_WATCHDOG_RESET,
initr_net, /* initr_net -> eth_initialize -> board_eth_init */
INIT_FUNC_WATCHDOG_RESET,
run_main_loop, /* 进入主循环 */
};
/* common/board_r.c */
static int run_main_loop(void)
{
/* main_loop() can return to retry autoboot, if so just run it again */
for (;;)
main_loop();
return 0;
}
/* common/main.c */
void main_loop(void)
{
const char *s;
bootstage_mark_name(BOOTSTAGE_ID_MAIN_LOOP, "main_loop");
#ifdef CONFIG_VERSION_VARIABLE
setenv("ver", version_string); /* 设置 version 环境变量 */
#endif
cli_init(); /* 初始化 hush shell 相关变量 */
run_preboot_environment_command(); /* 执行 preboot 环境变量(一般不用) */
s = bootdelay_process(); /* 读取 bootdelay 和 bootcmd,返回 bootcmd */
if (cli_process_fdt(&s))
cli_secure_boot_cmd(s);
autoboot_command(s); /* 倒计时;未打断则运行 bootcmd */
cli_loop(); /* 进入命令行 */
}
autoboot_command核心:stored_bootdelay != -1 && s && !abortboot(stored_bootdelay)三条都成立时执行run_command_list(s, -1, 0);否则(按键打断)走cli_loop()。
/* arch/arm/lib/bootm.c */
static void boot_jump_linux(bootm_headers_t *images, int flag)
{
unsigned long machid = gd->bd->bi_arch_number; /* 机器 ID */
const char *s;
void (*kernel_entry)(int zero, int arch, uint params);
unsigned long r2;
kernel_entry = (void (*)(int, int, uint))images->ep; /* 内核入口 = images->ep */
s = getenv("machid");
if (s) {
if (strict_strtoul(s, 16, &machid) < 0)
return;
}
if (images->ft_len)
r2 = (unsigned long)images->ft_addr; /* 使用设备树:r2 = DTB 地址 */
else
r2 = gd->bd->bi_boot_params; /* 不使用设备树:r2 = 参数地址 */
kernel_entry(0, machid, r2); /* 进入 Linux 内核,一去不复返 */
}
使用设备树后,内核靠 DTB 的 "compatible" 属性匹配机器,
machid已不再关键;I.MX6U 实际就使用设备树方式启动。
调用关系图:
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]
/* arch/arm/lib/bootm.c */
#define LINUX_ARM_ZIMAGE_MAGIC 0x016f2818
int bootz_setup(ulong image, ulong *start, ulong *end)
{
struct zimage_header *zi;
zi = (struct zimage_header *)map_sysmem(image, 0);
if (zi->zi_magic != LINUX_ARM_ZIMAGE_MAGIC) {
puts("Bad Linux ARM zImage magic!\n");
return 1;
}
*start = zi->zi_start;
*end = zi->zi_end;
printf("Kernel image @ %#08lx [ %#08lx - %#08lx ]\n",
image, *start, *end);
return 0;
}
关键魔数:
| 魔数 | 值 | 说明 |
|---|---|---|
LINUX_ARM_ZIMAGE_MAGIC |
0x016f2818 | ARM Linux zImage 标识 |
ZIMAGE_MAGIC(旧书稿) |
0x016f2818 | 同一魔数,命名不同 |
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)
⚠️ 来源说明:本节不属于《I.MX6U嵌入式Linux驱动开发指南》内容,为扩展知识(书中第三十章使用
imxdownload烧写u-boot.bin;此处为通用 dd 方式)。
u-boot.imx需烧写到 SD 卡偏移 1024 字节处(即跳过前 2 个 512 字节扇区):
flowchart LR
A[获取u-boot.imx] --> B[插入SD卡]
B --> C[sudo dd if=u-boot.imx of=/dev/sdb bs=512 seek=2 conv=fsync]
C --> D[确认烧录完成]
D --> E[插入开发板启动]
dd命令参数说明:
| 参数 | 说明 |
|---|---|
if=u-boot.imx |
输入文件 |
of=/dev/sdb |
输出设备(切勿写系统盘) |
bs=512 |
块大小 = 一个扇区 |
seek=2 |
跳过 2 个扇区 = 1024 字节偏移 |
conv=fsync |
同步写入,确保数据落盘(可选 notrunc) |
⚠️ 来源说明:本节不属于《I.MX6U嵌入式Linux驱动开发指南》内容,为扩展知识。
| 对比项 | 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.MX6U嵌入式Linux驱动开发指南》内容,为扩展知识(答案基于书中事实整理)。
0x87800000是 NXP 为 i.MX6 系列定义的CONFIG_SYS_TEXT_BASE(写在板级头文件 mx6ullevk.h / mx6ull_alientek_emmc.h 中),编译时通过-Ttext 0x87800000链接,也是上电后 Boot ROM 把 U-Boot 加载到 DDR 的位置。正点原子裸机例程的链接起始地址同样选0x87800000,目的是与 U-Boot 保持一致。注意:这是 U-Boot 的初始加载/链接地址;重定位后 U-Boot 实际运行在 DDR 高地址(示例为
0x9FF47000)。
作用:
- 将U-Boot从初始加载地址拷贝到最终运行地址
- 修正全局变量引用和函数指针
- 释放低地址内存给SPL使用
原因:
- SPL加载U-Boot到固定地址0x87800000
- 最终运行地址可能不同,需要重定位到未来运行地址
- 释放初始加载区域,避免内存浪费
CONFIG_SYS_INIT_RAM_ADDR = IRAM_BASE_ADDR = 0x00900000 CONFIG_SYS_INIT_RAM_SIZE = IRAM_SIZE = 0x00020000 (128KB) GENERATED_GBL_DATA_SIZE = (sizeof(struct global_data) + 15) & ~15 = 256 CONFIG_SYS_INIT_SP_OFFSET = 0x20000 - 256 = 0x1FF00 CONFIG_SYS_INIT_SP_ADDR = 0x00900000 + 0x1FF00 = 0x0091FF00随后在
lowlevel_init中执行sub sp, sp, #GD_SIZE(GD_SIZE=248)并 8 字节对齐,得到0x0091FE08。
- r0 = 0: 未使用
- r1 = machid: 机器类型ID(
gd->bd->bi_arch_number);使用设备树后内核靠 DTB 的 compatible 匹配,machid 已不再关键- r2 = dtb地址: 使用设备树时为
images->ft_addr(DTB 在 DRAM 中的地址),否则为gd->bd->bi_boot_params- bootargs: 通过 bootargs 环境变量传递,I.MX6ULL 上典型值:
console=ttymxc0,115200串口控制台配置root=/dev/mmcblk1p2根文件系统分区rootwait、rw、rootfstype=ext4等
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/
内容来源: 《I.MX6U嵌入式Linux驱动开发指南》第31章 U-Boot顶层Makefile详解、第32章 U-Boot启动流程详解