ATF-源码分析el3_exit或者bl1_run_bl2_in_el3分析
2026/6/3 1:33:04 网站建设 项目流程

EL3 确实要退出了,但并不是真正意义上面的退出,而是通过 ERET 指令降级到更低的异常级别(EL2 / EL1 / EL0)去执行下一个镜像。

函数名里的 "exit" 指的是 "退出 EL3 异常级别" 跳转到低权限级,而不是 EL3 固件关机或结束。

一、el3_exit 的完整执行流程

el3_exit 入口 │ ├─ ① 断言:当前必须在 SP_EL0 模式 (spsel == 0) │ ├─ ② 保存运行时栈指针 │ mov x17, sp ← 当前 SP_EL0 (运行时栈) │ msr spsel, #MODE_SP_ELX ← 切回 SP_EL3 │ str x17, [sp, ...CTX_RUNTIME_SP] ← 存回 context,下次 SMC 进入时可恢复 │ ├─ ③ 恢复 Per-World 上下文寄存器 │ ├─ get_per_world_context → 根据 SCR_EL3.NS 位获取当前安全状态的上下文 │ ├─ CPTR_EL3 ← 浮点/SIMD/PAuth 追踪控制 │ ├─ MPAM3_EL3 ← 内存分区和监控 (可选) │ └─ CVE-2018-3639 缓解 ← SSB 漏洞缓解状态恢复 │ ├─ ④ 同步错误 (BL31 only) │ synchronize_errors ← 确保之前的错误被同步 │ ├─ ⑤ 恢复关键 EL3 系统寄存器 ← ★ 决定"去哪、什么权限"的核心步骤 │ ldp x16, x17, [sp, CTX_SPSR_EL3] ← x16=SPSR_EL3, x17=ELR_EL3 │ ldr x18, [sp, CTX_SCR_EL3] ← SCR_EL3 (安全/非安全状态) │ ldr x19, [sp, CTX_MDCR_EL3] ← 调试控制 │ msr spsr_el3, x16 ← ★ 设置目标 PSTATE (决定目标 EL、SP、字节序等) │ msr elr_el3, x17 ← ★ 设置返回地址 (下一个镜像的入口点) │ msr scr_el3, x18 ← ★ 设置安全状态 (Secure/Non-Secure/Realm) │ msr mdcr_el3, x19 │ ├─ ⑥ restore_ptw_el1_sys_regs ← ERRATA_SPECULATIVE_AT 修复 │ ├─ ⑦ 恢复通用寄存器和 PAuth 密钥 │ bl restore_el3_runtime_regs ← 从 context 中恢复 x0-x29, SP_EL0, PAuth keys │ ldr x30, [sp, ...LR] ← 恢复 LR (返回地址寄存器) │ ├─ ⑧ 清除嵌套异常标志 (BL31 only) │ str xzr, [sp, CTX_NESTED_EA_FLAG] │ └─ ⑨ exception_return (ERET) ★ 关键指令 └── 原子操作: ├─ PC = ELR_EL3 ← 跳转到下一个镜像入口 ├─ PSTATE = SPSR_EL3 ← 降级到目标异常级别 └─ 安全状态 = SCR_EL3 ← 进入 Secure/Non-Secure

二、ERET 到底做了什么?

exception_return 是 AArch64 的 ERET 指令,它的效果是不可分割的原子操作:

操作

说明

PC ← ELR_EL3

从ELR_EL3加载目标地址,即下一阶段 BL 的入口点

PSTATE ← SPSR_EL3

加载目标 PSTATE,决定

EL、SP 选择、字节序、A32/A64 模式

安全状态切换

SCR_EL3.NS

/SCR_EL3.NSE

决定 (Secure/Non-Secure/Realm)

三、结合BL1 场景——完整链路回顾

回顾 bl1_entrypoint.S

bl bl1_main ← C 初始化 ; ← bl1_main 返回后执行这里 #if BL2_RUNS_AT_EL3 b bl1_run_bl2_in_el3 ← 直接跳转 (不调用 el3_exit) #else b el3_exit ← 通过 ERET 切换到 EL2/EL1 #endif

两个分支的区别:

分支 A:BL2_RUNS_AT_EL3(BL2 在 EL3 运行)
bl1_run_bl2_in_el3: ← bl1_entrypoint.S disable_mmu_icache_el3 ← 关 MMU 和 ICache (BL2 要重建页表) tlbi alle3 ← 刷 TLB ldp x0, x1, [x20, PC_OFFSET] ← ELR_EL3 = BL2 入口 msr elr_el3, x0 msr spsr_el3, x1 ← SPSR_EL3 = EL3 模式 ldp x0-x7, [x20, ARGS] ← 加载 BL2 参数 exception_return ← ERET → 但 SPSR 设的是 EL3 级别

这种情况下 EL3 并没有真正降级——SPSR_EL3 的目标异常级别就是 EL3,所以 ERET 后 CPU 仍在 EL3,只是从 BL1 的代码跳到了 BL2 的入口。

分支 B:!BL2_RUNS_AT_EL3(BL2 在 Secure-EL1 或 EL2 运行)
b el3_exit ← 调用 el3_exit

bl1_prepare_next_image() 已经把 BL2 的入口地址填入了 context 结构的 ELR_EL3,目标异常级别(EL2 或 S-EL1)填入了 SPSR_EL3。

el3_exit 执行 ERET → CPU 真正退出 EL3,降级到 EL2/S-EL1 运行 BL2。

四、"EL3 要是否退出"——两种情况对比

场景

调用路径

SPSR_EL3 的目标 EL

ERET 后 CPU 级别

BL2 所在位置

BL1 → BL2 (EL3模式)

bl1_run_bl2_in_el3

EL3

仍在 EL3

Trusted SRAM,EL3

BL1 → BL2 (非EL3模式)

el3_exit

EL2 或 S-EL1

降级到 EL2/EL1

Secure RAM,非 EL3

BL31 → 内核 (正常启动)

el3_exit

Non-Secure EL2/EL1

降级到 NS-EL2/EL1

内核空间

所以:

el3_exit 这个名字描述的是机制(从 EL3 异常级别退出到更低级),但 SPSR_EL3 的值决定了它在实际使用中是"降级"还是"同级跳转"

在 bl1_run_bl2_in_el3 中,虽然也用了 exception_return,但因为 SPSR_EL3 指向 EL3,所以效果是 在同一 EL 内跳转,并非真正"退出"

五、el3_exit 的两个关键设计意图

设计点

原因

保存 SP_EL0 → context

当前 EL3 的运行时栈指针保存到 context,下次通过 SMC 进入 EL3 时可以

prepare_el3_entry恢复,形成完整的进入/退出闭环

恢复 CPTR_EL3 / SCR_EL3 / PAuth 等

不同安全世界(Secure/Non-Secure/Realm)可能有不同的寄存器设置,每次切换时必须恢复对应世界的配置,否则安全隔离会被破坏

总结:el3_exit 是一个"世界切换器"——它从 context 中恢复目标世界的全部寄存器状态,然后通过 ERET 原子地完成异常级别降级和安全状态切换,让下一个镜像感觉自己一直在运行,从未被中断过。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询