ATF-BL1启动流程详解:从复位到BL2的完美一跳
上电复位后,其实是直接跳转到这条指令bl1/aarch64/bl1_entrypoint.S的 bl bl1_main,这条指令是整个 BL1 (Boot Loader 第一阶段) 从汇编跳入 C 世界的关键一跳。下面从全局启动流程到每一阶段的职责做详细分析。
一、总体架构:BL1 在整个启动链中的位置
BL1 是 ARM Trusted Firmware 的第一个启动阶段,运行在最高权限级 EL3。它在上电/复位后最先获得控制权,完成最底层的硬件初始化,验证并加载 BL2,最后将执行权移交给下一阶段。
复位 → BL1 (EL3) → BL2 (EL3/SP) → BL31 (EL3) / BL32 (SP) → 内核 (EL1)二、bl1_entrypoint 入口汇编分析(执行 bl bl1_main 之前)
文件 bl1/aarch64/bl1_entrypoint.S :
func bl1_entrypoint el3_entrypoint_common \ _init_sctlr=1 \ _warm_boot_mailbox=!PROGRAMMABLE_RESET_ADDRESS \ _secondary_cold_boot=!COLD_BOOT_SINGLE_CPU \ _init_memory=1 \ _init_c_runtime=1 \ _exception_vectors=bl1_exceptions \ _pie_fixup_size=0这个宏 el3_common_macros.S 展开后做了 6 件关键事情:
阶段 | 宏参数 | 实际动作 |
① SCTLR 初始化 | _init_sctlr=1 | 清零 SCTLR_EL3中的 EE(大小端)、WXN、SA、A、DSSBS 等位,设置成已知状态 |
② 冷/热启动检测 | _warm_boot_mailbox= !PROGRAMMABLE_RESET_ADDRESS | 调用plat_get_my_entrypoint() ,若非零则视为热启动,直接跳转过去 |
③ PIE 重定位 | _pie_fixup_size=0 | 禁用(BL1 不需要位置无关可执行文件修复) |
④ 异常向量表 | _exception_vectors=bl1_exceptions | 设置VBAR_EL3→bl1_exceptions |
⑤ 主/次 CPU 判断 | _secondary_cold_boot=!COLD_BOOT_SINGLE_CPU | 主 CPU 继续,次 CPU 进plat_secondary_cold_boot_setup等待 |
⑥ 内存+C运行时 | _init_memory=1 , _init_c_runtime=1 | platform_mem_init→ 清零 BSS/COHERENT_RAM → 搬运 data 段 → plat_set_my_stack设栈 |
- MMU/缓存:已初始化(BSS 已清零,数据段已就位)
- 栈指针 SP_EL0:已指向当前 CPU 的专用栈
- 异常向量:已注册
- x30(LR):指向 bl1_entrypoint 中 bl bl1_main 的下一条指令
三、bl bl1_main 之后的执行流
bl bl1_main 调用回来后,控制权进入 C 函数 bl1_main()。执行完毕后返回,根据编译选项
#if BL2_RUNS_AT_EL3 b bl1_run_bl2_in_el3 ; ← 直接跳转(不返回) #else b el3_exit ; ← 通过 ERET 切换到下一个 EL #endif如果定义了BL2_RUNS_AT_EL3,就直接专跳了
bl1_run_bl2_in_el3 :从 bl2_ep_info 中加载入口地址和参数到 ELR_EL3/SPSR_EL3 和 x0-x7,执行 exception_return 直接跳入 BL2
el3_exit:标准的 ERET 序列,切换到 Secure-EL1 或 EL2 执行 BL2
四、bl1_main C 函数——核心实现需求
文件 bl1/bl1_main.c 中,整个函数实现了 8 个阶段的职责:
阶段 1:早期平台初始化
plat_setup_early_console(); // 提前开启调试串口(如 EARLY_CONSOLE) bl1_early_platform_setup(); // 平台早期 setup:UART、计时器、GIC 基础需求: 在 MMU 开启前完成最基本的外设初始化,确保可以输出调试信息。
阶段 2:架构/内存初始化
bl1_plat_arch_setup(); // 开启 MMU、设置页表、启用cache cm_manage_extensions_el3(...); // EL3 扩展寄存器初始化(PAuth、MPAM、SVE 等)阶段 3:运行时检测与公告
PMF_CAPTURE_TIMESTAMP(...); // 记录性能时间戳 NOTICE(FIRMWARE_WELCOME_STR); // 打印固件欢迎信息 print_errata_status(); // 打印当前 CPU 的勘误状态阶段 4:断言验证(调试模式)
- 验证 MMU 已使能(SCTLR_M_BIT)
- 验证数据/指令缓存已使能(SCTLR_C_BIT / SCTLR_I_BIT)
- 验证 CACHE_WRITEBACK_GRANULE 与硬件 CTR_EL0.CWG 一致 需求: 调试模式下确保关键硬件配置正确。
阶段 5:架构和认证初始化
bl1_arch_setup(); // 通用 EL3 架构设置 crypto_mod_init(); // 加密模块初始化 auth_mod_init(); // 认证模块初始化(验证镜像签名) bl1_plat_mboot_init(); // 可信启动测量初始化 bl1_platform_setup(); // 平台后期 setup(GPIO、看门狗等)阶段 6:决定下一阶段镜像
image_id = bl1_plat_get_next_image_id(); if (image_id == BL2_IMAGE_ID) { bl1_load_bl2(); // 加载并认证 BL2 } else { NOTICE("BL1-FWU: FWU Process Started\n"); // 固件更新模式 }阶段 7:加载 BL2(bl1_load_bl2)
desc = bl1_plat_get_image_desc(BL2_IMAGE_ID); bl1_plat_handle_pre_image_load(BL2_IMAGE_ID); load_auth_image(BL2_IMAGE_ID, info); // 加载 + 认证 bl1_plat_handle_post_image_load(BL2_IMAGE_ID);- 获取 BL2 镜像的描述符
- 平台预处理钩子(如擦除目标存储区域)
- load_auth_image() — 从非易失存储加载 BL2 镜像,同时对镜像进行签名验证和哈希校验(TBB 可信启动)
- 平台后处理钩子(如填充入口参数结构体 bl2_ep_info)
阶段 8:清理并移交
bl1_plat_mboot_finish(); // 测量启动清理 crypto_mod_finish(); // 加密模块清理 bl1_prepare_next_image(image_id); // 准备下一阶段上下文 pauth_disable_el3(); // 若支持 PAuth,禁用指针认证 console_flush(); // 刷新串口缓冲五、整体数据流图
┌─────────────────────────────────────────────────────────┐ │ bl1_entrypoint (汇编) │ │ ├─ SCTLR 初始化 │ │ ├─ 冷/热启动判断 │ │ ├─ 异常向量表设置 (VBAR_EL3 = bl1_exceptions) │ │ ├─ 主/次CPU识别 │ │ ├─ platform_mem_init │ │ ├─ BSS清零 / data段搬运 │ │ ├─ 栈初始化 (plat_set_my_stack) │ │ └─ bl bl1_main ────────────────────────────────┐ │ │ │ │ ├─────────────────────────────────────────────────────┤ │ │ bl1_main (C) │ │ │ ├─ plat_setup_early_console() ◄──────┘ │ │ ├─ bl1_early_platform_setup() │ │ ├─ bl1_plat_arch_setup() ← MMU+Cache on │ │ ├─ cm_manage_extensions_el3() │ │ ├─ NOTICE/print_errata_status() │ │ ├─ bl1_arch_setup() │ │ ├─ crypto_mod_init / auth_mod_init │ │ ├─ bl1_platform_setup() │ │ ├─ bl1_plat_get_next_image_id() │ │ ├─ bl1_load_bl2() │ │ │ ├─ bl1_plat_get_image_desc(BL2_IMAGE_ID) │ │ │ ├─ load_auth_image() ← 签名验证 │ │ │ └─ bl1_plat_handle_post_image_load() │ │ ├─ bl1_prepare_next_image() ← 设置 boot 参数 │ │ └─ 返回汇编 (console_flush + pauth_disable) │ │ │ │ ├─────────────────────────────────────────────────────┤ │ │ 返回 bl1_entrypoint (汇编) │ │ │ ├─ BL2_RUNS_AT_EL3? → bl1_run_bl2_in_el3 │ │ │ │ └─ disable_mmu_icache_el3 │ │ │ │ └─ 加载 bl2_ep_info 到 ELR/SPSR/参数寄存器 │ │ │ │ └─ exception_return (ERET) │ │ │ └─ !BL2_RUNS_AT_EL3? → el3_exit │ │ │ └─ ERET 到 Secure-EL1/EL2 │ │ └─────────────────────────────────────────────────────────┘六、关键设计需求总结
- 冷热启动兼容:通过 mailbox 机制区分冷启动(完整初始化)和热启动(快速跳转)
- 多核支持:主 CPU 做全局初始化,次 CPU 进入等待
- 安全启动 (TBB):加载 BL2 前必须完成镜像认证(auth_mod + crypto_mod)
- 平台可移植性:通过 plat系列钩子函数抽象平台差异(内存布局、存储介质、外设)
- 运行时测量:通过 PMF 框架记录启动阶段时间戳,便于性能分析
- 固件更新 (FWU):bl1_plat_get_next_image_id() 返回非 BL2 镜像时,进入固件更新流程
- EL3 安全退出:跳转前禁用 EL3 特有的 PAuth、刷新 console,防止信息泄露至低权限级
