U-Boot 把内核镜像、设备树读到内存,做好 ARM64 启动约定(寄存器、内存、异常等级、参数),最后用 br 指令直接跳转到内核入口地址;U-Boot 不再运行,内核接管整个系统。
注意:ARM64 没有传统 32 位的 bootm 入口调用,ARM64 标准命令是 booti。
零、前置前提(ARM64 Linux 启动约定,必须全部满足)
Linux 内核文档 Documentation/arm64/booting.rst 规定的硬性要求,U-Boot 的 booti 就是用来满足这套约定:
1、CPU 处于 EL1(Exception Level 1)
- SOC平台带 ATF(BL31):上电 BL31 在 EL3,BL31 启动 U-Boot 时把 U-Boot 放在 EL1;U-Boot 跑 booti 时不碰 EL3,直接在 EL1 跳转内核。
- MMU:跳转前 MMU 必须关闭,内核自己会初始化 MMU、页表。
2、寄存器约定(最重要)
内核入口函数 _text 拿到这几个寄存器,就知道 dtb 在哪里。
- x0:0(必须是 0)
- x1:0
- x2:设备树 blob(dtb)在内存里的物理地址
- x3:0
3、内存:
内核镜像、dtb 都已经拷贝到物理内存指定位置,地址不能重叠;U-Boot 自身占用的内存区域,内核不能覆盖。
4、缓存:
跳转前必须做 cache 清理(dc civac),保证数据从 cache 刷到主存,避免 dtb / 镜像数据残缺。
5、中断:
DAIF 屏蔽(IRQ、FIQ 关闭),内核上来再自己开中断。
补充:ARM64 内核不支持旧的 ATAGS,只认 DTB,所以 x2 必须是合法 dtb 地址。

阶段 1:booti 命令入口(HUSH 命令层)
HUSH解析器 → run_list_real → run_pipe_real → cmd_call → do_booti()
do_booti 是 U-Boot 里 booti 命令的实现,文件:cmd/boot.c
booti [kernel_addr] [initrd_addr] [dtb_addr]
# 示例:booti 0x42000000 – 0x43000000
# “-”代表没有initramfs
do_booti 主要做:
阶段 2:boot_prep_linux —— 准备工作
核心工作:
1、把 dtb 地址合法性检查,复制 dtb(按需),保证 dtb 在 2MB 对齐、不被内核覆盖的安全区域
2、处理 initramfs(量产一般不用,填 – 就跳过)
3、准备 bootargs(内核命令行)
- 从环境变量 bootargs 拿字符串:console=ttyS0 root=/dev/mmcblk0p2 rw rootwait
- 把 bootargs 写到 dtb 的 chosen 节点 bootargs 属性
Linux 5.4 优先读 dtb /chosen/bootargs,这就是 U-Boot 传给内核命令行的方式!
4、内存预留:标记 U-Boot 本身、BL31、ATF、secure 区域,内核设备树里 reserved-memory 节点,内核不会去占用。
阶段 3:boot_jump_linux —— 跳转前最后一步(重点)
文件:arch/arm64/lib/boot.c 伪代码逻辑:
void boot_jump_linux(ulong image, ulong dtb, ulong initrd)
{
void (*kernel_entry)(void);
// 1. 关闭U-Boot自己的定时器、watchdog、外设
// 2. 刷新cache:dc civac,把cache数据全部回写到DDR
flush_dcache_all();
// 3. 关闭MMU(如果U-Boot开了MMU)
disable_mmu();
// 4. 关闭中断
disable_irq();
// 5. 填充约定寄存器
kernel_entry = (void (*)(void))image;
// 准备 x0,x1,x2,x3
// 汇编:mov x2, dtb_phys_addr
// 6. 跳转到内核入口
kernel_entry();
// 这里一旦跳转成功,后面代码永远跑不到!
panic("Kernel returned!");
}
关键点:kernel_entry 就是内核 Image 的起始地址,也就是内核的 _text 符号。
阶段 4:汇编跳转(最底层,ARM64)
U-Boot 汇编代码,设置好 x0~x3,然后一条跳转指令:
mov x0, #0
mov x1, #0
mov x2, dtb_phys
mov x3, #0
br x4 // x4里面放内核入口地址
br 是无条件跳转,跳转之后,CPU 取指地址直接切到内核 Image。
这一刻:U-Boot 彻底交出控制权,U-Boot 的代码不再执行,栈、全局变量全部作废。
内核不会返回,一旦返回,说明内核启动崩溃,触发 panic。
阶段 5:内核接管之后(内核侧启动,不属于 U-Boot)
内核 _text 入口开始执行:

两种镜像格式的区别(Image / FIT)
1、原始 Image(现在常用) booti 0x42000000 – 0x43000000
U-Boot 不解析镜像内部,直接把这个地址当成入口,寄存器带 dtb 地址跳转。
2、FIT 镜像(itb,把 kernel+dtb+initrd 打包在一起)
bootm 0x42000000
bootm 会解析 itb,自动提取 kernel、dtb,再调用底层和 booti 一样的跳转逻辑。
小结
一条极简时序串起来
U-Boot autoboot → run bootcmd → mmc read 加载Image到0x42000000
→ mmc read 加载dtb到0x43000000
→ booti 0x42000000 – 0x43000000
do_booti → boot_prep_linux(填充chosen/bootargs)
→ boot_jump_linux(刷cache、关MMU、关中断)
→ 汇编设置 x0/x1/x2/x3 → br 跳内核入口
↓
Linux _text 开始执行



