想省掉U-Boot Proper让SPL直接进内核?——Falcon模式配置与限制 电子说
问题场景
从按下电源键到显示开机画面,正常启动链要走BootROM→TPL→SPL→U-Boot→Kernel,光U-Boot Proper就要跑300ms。如果能让SPL加载完ATF后直接跳内核,把U-Boot Proper整个跳过,就可以大幅缩短启动时间。Falcon模式(CONFIG_SPL_KERNEL_BOOT)就是干这个的。本文对照RK3576源码,厘清Falcon模式的触发条件、跳转实现和必须注意的限制。
勘误说明:本文已对照RK3576 SDK实际代码审核修正。主要修正:spl_next_stage() 的实际实现与文章此前版本有较大差异——使用 goto out 模式而非 force_uboot 变量,使用 spl_rockchip_dnl_key_pressed() 而非 spl_key_pressed();spl_invoke_atf() 的Falcon分支在 common/spl/spl_atf.c 中而非 mach-rockchip/spl.c;Falcon模式下FIT的 firmware=atf 其实与非Falcon模式相同,差异在于loadables列表。
一、正常模式 vs Falcon模式
两种启动模式的完整对比:

时间节省的主要来源:
| 阶段 | 正常模式 | Falcon模式 | 节省时间 |
|---|---|---|---|
| BootROM | ~100ms | ~100ms | — |
| TPL | ~200ms | ~200ms | — |
| SPL加载FIT | ~250ms | ~250ms | — |
| U-Boot Proper | ~300ms | 跳过 | 300ms |
| Linux内核 | ~2s | ~2s | — |
| 合计 | ~2850ms | ~2550ms | ~300ms |
对于用户感知而言,300ms是从"按下电源键"到"显示logo/闪灯"的体验分水岭。Falcon模式抢出的这300ms恰好覆盖了这个临界点。
二、Falcon模式的触发逻辑
SPL在 spl_next_stage() 中决定是否启用Falcon模式:
// arch/arm/mach-rockchip/spl.c — RK3576实际实现// 注意:CONFIG_SPL_KERNEL_BOOT 的保护范围从398行开始void spl_next_stage(struct spl_image_info *spl){ const char *reason[] = { "Recovery key", "Ctrl+c", "LowPwr", "Other" }; uint32_t reg_boot_mode; int i = 0;
// ── 条件1: 音量键(Recovery按键) ── if (spl_rockchip_dnl_key_pressed()) { i = 0; spl->next_stage = SPL_NEXT_STAGE_UBOOT; goto out; }
// ── 条件2: Ctrl+C 中断 ── if (gd->console_evt == 0x03) { i = 1; spl->next_stage = SPL_NEXT_STAGE_UBOOT; goto out; }
// ── 条件3: 低电量(CONFIG_SPL_DM_FUEL_GAUGE) ── if (spl_is_low_power()) { i = 2; spl->next_stage = SPL_NEXT_STAGE_UBOOT; goto out; }
// ── 条件4: boot_mode寄存器特殊值 ── reg_boot_mode = readl((void *)CONFIG_ROCKCHIP_BOOT_MODE_REG); switch (reg_boot_mode) { case BOOT_LOADER: case BOOT_FASTBOOT: case BOOT_CHARGING: case BOOT_UMS: case BOOT_DFU: i = 3; spl->next_stage = SPL_NEXT_STAGE_UBOOT; break; default: // ★ 无任何条件命中 → Falcon模式启用 spl->next_stage = SPL_NEXT_STAGE_KERNEL; }
out: if (spl->next_stage == SPL_NEXT_STAGE_UBOOT) printf("Enter uboot reason: %sn", reason[i]);}
关键点对比:
实际代码中没有 force_uboot 布尔变量,而是使用 goto out 模式
实际代码中没有 spl_adc_key_pressed() 独立调用——ADC按键通过 spl_rockchip_dnl_key_pressed() 判定(对应 KEY_VOLUMEUP)
实际代码中没有 CONFIG_SPL_AB 分支——AB系统不修改 next_stage
三、Falcon模式启动决策树

四、Falcon模式下的FIT加载差异
在Falcon模式下,SPL对FIT的加载行为有所不同:

Falcon模式下的FIT差异:
需要注意的是,firmware = "atf" 在正常模式和Falcon模式下是一样的——RK3576的FIT中firmware始终指向ATF。Falcon模式的真正差异在于:
loadables 列表中用 kernel 替换了 uboot
SPL在遍历loadables时遇到 os_type == IH_OS_U_BOOT 会 continue 跳过(spl_fit.c line 779-781)
内核的entry point设置为 0x40400000(与 kernel_addr_r 一致)
// Falcon模式的FIT:用kernel替换ubootimages { kernel { description = "Linux Kernel"; data = /incbin/("Image.lz4"); type = "kernel"; arch = "arm64"; compression = "lz4"; load = <0x40400000>; entry = <0x40400000>; };
atf { /* 不变,仍为firmware */ }; tee { /* 不变 */ }; fdt { /* 不变 */ };};
configurations { default = "config-1"; config-1 { description = "RK3576 Falcon"; firmware = "atf"; // ★ 与正常模式相同 loadables = "kernel", "tee"; // kernel替换了uboot fdt = "fdt"; };};
五、Falcon模式跳转实现
跳转到内核通过ATF间接完成:SPL先跳转ATF(BL31),ATF通过 entry_point_bl33 决定跳转到U-Boot还是Kernel:
// common/spl/spl.c — board_init_r 中的跳转决策switch (spl_image.os) {case IH_OS_ARM_TRUSTED_FIRMWARE: // ★ spl_image.next_stage 决定了BL33的入口 printf("Jumping to %s via ATFn", spl_image.next_stage == SPL_NEXT_STAGE_UBOOT ? "U-Boot" : spl_image.next_stage == SPL_NEXT_STAGE_KERNEL ? "Kernel" : "Unknown"); spl_invoke_atf(&spl_image); break;}
// common/spl/spl_atf.c — spl_invoke_atf通用实现// 设置bl33_entry为spl_image->entry_point_bl33// 在Falcon模式下,entry_point_bl33被设为内核入口0x40400000// ATF通过smc返回后直接跳转到bl33_entry
核心机制:
spl_fit.c 中遍历loadables时,U-Boot的 os_type == IH_OS_U_BOOT 被跳过(spl_fit.c:779-781)
剩下的kernel(os_type == IH_OS_LINUX 或未指定)被正常加载
kernel的load地址赋值给 spl_image->entry_point_bl33
ATF启动后检查 entry_point_bl33,发现不是U-Boot地址,跳转到内核入口
六、Falcon模式的应用场景

七、Falcon模式配置
# RK3576 Falcon模式配置CONFIG_SPL_KERNEL_BOOT=y # 启用Falcon模式CONFIG_SPL_INPUT=y # 输入检测(Ctrl+C等)CONFIG_SPL_ADC_KEY=y # ADC物理按键CONFIG_SPL_DM_FUEL_GAUGE=y # 电量检测(仅平板等设备)CONFIG_SPL_AB=y # AB系统(与Falcon共存) # 内核镜像相关CONFIG_SPL_LOAD_FIT=y # FIT加载CONFIG_SPL_LOAD_FIT_ADDRESS=0x42000000 # FIT缓冲地址CONFIG_SYS_SPL_MALLOC_SIZE=0x800000 # SPL堆大小(8MB)
审核编辑 黄宇
全部0条评论
快来发表一下你的评论吧 !