一、NS-EL1 调用 SMC 时,BL31 内部的异常入口与路由
1. 异常向量入口命中
.globl sync_exception_aarch64 .globl irq_aarch64 .globl fiq_aarch64 .globl serror_aarch64 vector_entry sync_exception_aarch64 // 0x400 ~ 0x600 区间:Lower EL using AArch64 的同步异常2. 向量入口前置处理
vector_entry sync_exception_aarch64 /* * This exception vector will be the entry point for SMCs and traps * that are unhandled at lower ELs most commonly. SP_EL3 should point * to a valid cpu context where the general purpose and system register * state can be saved. */ save_x30 // 1. 保存 LR(x30) 到 CPU 上下文,释放 x30 给后续逻辑使用 apply_at_speculative_wa // 2. 执行推测执行漏洞硬件缓解补丁 sync_and_handle_pending_serror // 3. 同步错误,检查待处理 SError,按 FFH/KFH 模式处理或反射 unmask_async_ea // 4. 取消异步外部中止屏蔽,允许 EL3 处理异步错误 handle_sync_exception // 进入同步异常分发核心逻辑 end_vector_entry sync_exception_aarch643. 同步异常类别(EC)判断
.macro handle_sync_exception #if ENABLE_RUNTIME_INSTRUMENTATION /* * Read the timestamp value and store it in per-cpu data. The value * will be extracted from per-cpu data by the C level SMC handler and * saved to the PMF timestamp region. */ mrs x30, cntpct_el0 str x29, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X29] mrs x29, tpidr_el3 str x30, [x29, #CPU_DATA_PMF_TS0_OFFSET] ldr x29, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X29] #endif mrs x30, esr_el3 ubfx x30, x30, #ESR_EC_SHIFT, #ESR_EC_LENGTH // 提取 EC 字段 /* Handle SMC exceptions separately from other synchronous exceptions */ cmp x30, #EC_AARCH32_SMC b.eq smc_handler32 // 32位SMC → smc_handler32 cmp x30, #EC_AARCH64_SMC b.eq sync_handler64 cmp x30, #EC_AARCH64_SYS b.eq sync_handler64 // 64位SMC → sync_handler64 cmp x30, #EC_IMP_DEF_EL3 b.eq imp_def_el3_handler4. SMC 运行时服务分发(核心路由)
sync_handler64: /* NOTE: The code below must preserve x0-x4 */ /* * Save general purpose and ARMv8.3-PAuth registers (if enabled). * Also save PMCR_EL0 and set the PSTATE to a known state. */ bl prepare_el3_entry #if ENABLE_PAUTH /* Load and program APIAKey firmware key */ bl pauth_load_bl31_apiakey #endif /* * Populate the parameters for the SMC handler. * We already have x0-x4 in place. x5 will point to a cookie (not used * now). x6 will point to the context structure (SP_EL3) and x7 will * contain flags we need to pass to the handler. */ mov x5, xzr mov x6, sp /* * Restore the saved C runtime stack value which will become the new * SP_EL0 i.e. EL3 runtime stack. It was saved in the 'cpu_context' * structure prior to the last ERET from EL3. */ ldr x12, [x6, #CTX_EL3STATE_OFFSET + CTX_RUNTIME_SP] /* Switch to SP_EL0 */ msr spsel, #MODE_SP_EL0 /* * Save the SPSR_EL3 and ELR_EL3 in case there is a world * switch during SMC handling. * TODO: Revisit if all system registers can be saved later. */ mrs x16, spsr_el3 mrs x17, elr_el3 stp x16, x17, [x6, #CTX_EL3STATE_OFFSET + CTX_SPSR_EL3] /* Load SCR_EL3 */ mrs x18, scr_el3 /* check for system register traps */ mrs x16, esr_el3 ubfx x17, x16, #ESR_EC_SHIFT, #ESR_EC_LENGTH cmp x17, #EC_AARCH64_SYS b.eq sysreg_handler64 /* Clear flag register */ mov x7, xzr #if ENABLE_RME /* Copy SCR_EL3.NSE bit to the flag to indicate caller's security */ ubfx x7, x18, #SCR_NSE_SHIFT, #1 /* * Shift copied SCR_EL3.NSE bit by 5 to create space for * SCR_EL3.NS bit. Bit 5 of the flag corresponds to * the SCR_EL3.NSE bit. */ lsl x7, x7, #5 #endif /* ENABLE_RME */ /* Copy SCR_EL3.NS bit to the flag to indicate caller's security */ bfi x7, x18, #0, #1 mov sp, x12 /* * Per SMCCC documentation, bits [23:17] must be zero for Fast * SMCs. Other values are reserved for future use. Ensure that * these bits are zeroes, if not report as unknown SMC. */ tbz x0, #FUNCID_TYPE_SHIFT, 2f /* Skip check if its a Yield Call*/ tst x0, #(FUNCID_FC_RESERVED_MASK << FUNCID_FC_RESERVED_SHIFT) b.ne smc_unknown /* * Per SMCCCv1.3 a caller can set the SVE hint bit in the SMC FID * passed through x0. Copy the SVE hint bit to flags and mask the * bit in smc_fid passed to the standard service dispatcher. * A service/dispatcher can retrieve the SVE hint bit state from * flags using the appropriate helper. */ 2: and x16, x0, #(FUNCID_SVE_HINT_MASK << FUNCID_SVE_HINT_SHIFT) orr x7, x7, x16 bic x0, x0, #(FUNCID_SVE_HINT_MASK << FUNCID_SVE_HINT_SHIFT) /* Get the unique owning entity number */ ubfx x16, x0, #FUNCID_OEN_SHIFT, #FUNCID_OEN_WIDTH ubfx x15, x0, #FUNCID_TYPE_SHIFT, #FUNCID_TYPE_WIDTH orr x16, x16, x15, lsl #FUNCID_OEN_WIDTH /* Load descriptor index from array of indices */ adrp x14, rt_svc_descs_indices add x14, x14, :lo12:rt_svc_descs_indices ldrb w15, [x14, x16] /* Any index greater than 127 is invalid. Check bit 7. */ tbnz w15, 7, smc_unknown /* * Get the descriptor using the index * x11 = (base + off), w15 = index * * handler = (base + off) + (index << log2(size)) */ adr x11, (__RT_SVC_DESCS_START__ + RT_SVC_DESC_HANDLE) lsl w10, w15, #RT_SVC_SIZE_LOG2 ldr x15, [x11, w10, uxtw] /* * Call the Secure Monitor Call handler and then drop directly into * el3_exit() which will program any remaining architectural state * prior to issuing the ERET to the desired lower EL. */ #if DEBUG cbz x15, rt_svc_fw_critical_error #endif blr x15 b el3_exit1.上下文保存:调用 prepare_el3_entry,将 NS 世界的通用寄存器、PAuth 寄存器完整保存到 CPU 上下文结构
2.栈切换:从 SP_EL3(异常专用栈)切换到 SP_EL0(EL3 C 语言运行时栈),为调用 C 函数做准备
2.参数封装:保留 x0~x4 的 SMC 入参,x6 指向 CPU 上下文,x7 携带安全状态、SVE 提示等标志位
4.服务索引计算:
ubfx x16, x0, #FUNCID_OEN_SHIFT, #FUNCID_OEN_WIDTH // 提取 SMC FID 的 OEN(所属服务组) ubfx x15, x0, #FUNCID_TYPE_SHIFT, #FUNCID_TYPE_WIDTH // 提取调用类型:Fast / Yielding orr x16, x16, x15, lsl #FUNCID_OEN_WIDTH // 组合成服务索引5.查表分发:
adrp x14, rt_svc_descs_indices ldrb w15, [x14, x16] // 从索引表查对应服务的描述符下标 adr x11, (__RT_SVC_DESCS_START__ + RT_SVC_DESC_HANDLE) lsl w10, w15, #RT_SVC_SIZE_LOG2 ldr x15, [x11, w10, uxtw] // 取出对应服务的 handler 函数指针 blr x15 // 调用对应运行时服务的处理函数6.异常返回:服务处理完成后,调用 el3_exit 恢复上下文,通过 ERET 指令返回 NS-EL1。
二、从 BL31 路由到 OP-TEE 运行实体的链路
1. opteed 的注册:预先绑定 SMC 范围
// 快速 SMC 服务:不可中断,用于简单控制指令 DECLARE_RT_SVC( optee_fast, OEN_TOS_START, OEN_TOS_END, // OEN 范围:50 ~ 63(SMCCC 规定的 Trusted OS 组) SMC_TYPE_FAST, opteed_setup, opteed_smc_handler ); // 标准 Yielding SMC 服务:可中断挂起,用于 TA 调用等复杂业务 DECLARE_RT_SVC( optee_std, OEN_TOS_START, OEN_TOS_END, SMC_TYPE_YIELD, NULL, opteed_smc_handler );2. NS → OP-TEE 的世界切换流程
(1)确认 NS 上下文已归档
(2)切换到 OP-TEE 安全上下文
(3)配置返回目标与安全状态
- SCR_EL3.NS = 0:标记 ERET 后进入安全世界
- ELR_EL3 = OP-TEE SMC 入口:指向 OP-TEE 内部的 SMC 向量表(如 vector_std_smc_entry / vector_fast_smc_entry)
- SPSR_EL3:配置为 S-EL1 特权级、EL1h 栈模式、开中断等运行状态
- x0~x4:原样保留 SMC 参数,透传给 OP-TEE
(4)el3_exit 完成最终跳转
- 从上下文恢复 S-EL1 的所有通用寄存器、系统寄存器
- 配置 SCR_EL3.NS = 0 切换安全状态
- 执行 ERET 指令,CPU 从 EL3 退出,跳转到 OP-TEE 的 S-EL1 异常向量入口
补充:两类 SMC 的差异
- Fast SMC:关中断执行,不支持挂起,用于简单操作(如版本查询、状态控制),全程不涉及 OP-TEE 线程调度
- Yielding SMC:开中断执行,支持线程挂起与 RPC 交互,是业务层 TA 调用、加密服务的主路径,也是最常用的类型