快速连接
👉👉👉【精选】ARMv8/ARMv9架构入门到精通-目录 👈👈👈
std smc跳转到optee的场景有两种:
- REE主动发起std smc的调用,基本就算Globalplatform Client
API(libteec.so)的调用,也就是openssion\invoke\closession等函数的调用; - Optee中发起了RPC反向调用REE后,从REE再切回来是,也是走std call流程
1、ATF跳转到optee的std_smc_entry
当REE发起std smc调用后,跳转到ATF后,ATF根据smc_fid的type判定该smc属于STD,则设置ELR寄存器的值为std_smc_entry(在ATF中保存着一份和optee中同样的线程向量表),那么当ATF ERET返回时,程序跳转到ELR中的地址处,也就optee中所定义的线程向量表中的std_smc_entry
uint64_t opteed_smc_handler(uint32_t smc_fid,
uint64_t x1,
uint64_t x2,
uint64_t x3,
uint64_t x4,
void *cookie,
void *handle,
uint64_t flags)
{
......
if (GET_SMC_TYPE(smc_fid) == SMC_TYPE_FAST) {
cm_set_elr_el3(SECURE, (uint64_t)
&optee_vectors->fast_smc_entry);
} else {
cm_set_elr_el3(SECURE, (uint64_t)
&optee_vectors->std_smc_entry); //------------------设置ELR寄存器
}
......
SMC_RET4(&optee_ctx->cpu_ctx, smc_fid, x1, x2, x3); //-------------ERET返回,程序跳转到ELR中的地址
}
2、optee中的std_smc_entry流程
LOCAL_FUNC vector_std_smc_entry , :
sub sp, sp, #THREAD_SMC_ARGS_SIZE
store_xregs sp, THREAD_SMC_ARGS_X0, 0, 7
mov x0, sp
bl thread_handle_std_smc //------------------------------std业务逻辑的处理
/*
* Normally thread_handle_std_smc() should return via
* thread_exit(), thread_rpc(), but if thread_handle_std_smc()
* hasn't switched stack (error detected) it will do a normal "C"
* return.
*/
load_xregs sp, THREAD_SMC_ARGS_X0, 1, 8
add sp, sp, #THREAD_SMC_ARGS_SIZE
ldr x0, =TEESMC_OPTEED_RETURN_CALL_DONE
smc #0 //--------------------------------std业务处理完成后,再次将cpu切回REE
b . /* SMC should not return */
如果是std call则调用thread_alloc_and_run,如果是RPC回来的(RPC回来也是走的std smc)则走thread_resume_from_rpc(args)
void thread_handle_std_smc(struct thread_smc_args *args)
{
thread_check_canaries();
if (args->a0 == OPTEE_SMC_CALL_RETURN_FROM_RPC)
thread_resume_from_rpc(args);
else
thread_alloc_and_run(args);
}
(1)、RPC回来的std call的处理
thread_resume_from_rpc中,直接调用了thread_resume
static void thread_resume_from_rpc(struct thread_smc_args *args)
{
size_t n = args->a3; /* thread id */
struct thread_core_local *l = thread_get_core_local();
uint32_t rv = 0;
assert(l->curr_thread == -1);
lock_global();
if (n < CFG_NUM_THREADS &&
threads[n].state == THREAD_STATE_SUSPENDED &&
args->a7 == threads[n].hyp_clnt_id)
threads[n].state = THREAD_STATE_ACTIVE;
else
rv = OPTEE_SMC_RETURN_ERESUME;
unlock_global();
if (rv) {
args->a0 = rv;
return;
}
l->curr_thread = n;
if (is_user_mode(&threads[n].regs))
tee_ta_update_session_utime_resume();
if (threads[n].have_user_map)
core_mmu_set_user_map(&threads[n].user_map);
/*
* Return from RPC to request service of a foreign interrupt must not
* get parameters from non-secure world.
*/
if (threads[n].flags & THREAD_FLAGS_COPY_ARGS_ON_RETURN) {
copy_a0_to_a5(&threads[n].regs, args);
threads[n].flags &= ~THREAD_FLAGS_COPY_ARGS_ON_RETURN;
}
thread_lazy_save_ns_vfp();
thread_resume(&threads[n].regs);
}
而在thread_resume中,程序会通过eret返回,也就是跳转到ELR中的地址. 那么ELR中的值来在来自x2,x2又来自threads[n].regs结构体中的PC值
/* void thread_resume(struct thread_ctx_regs *regs) */
FUNC thread_resume , :
load_xregs x0, THREAD_CTX_REGS_SP, 1, 3 //-------------------------x2等于struct thread_ctx_regs结构体中的pc值
load_xregs x0, THREAD_CTX_REGS_X4, 4, 30
mov sp, x1
msr elr_el1, x2 //-------------------------将x2赋值到elr寄存器
msr spsr_el1, x3
b_if_spsr_is_el0 w3, 1f
load_xregs x0, THREAD_CTX_REGS_X1, 1, 3
ldr x0, [x0, THREAD_CTX_REGS_X0]
eret //------------------------------------eret返回,则跳转到elr中的地址
1: load_xregs x0, THREAD_CTX_REGS_X1, 1, 3
ldr x0, [x0, THREAD_CTX_REGS_X0]
msr spsel, #1
store_xregs sp, THREAD_CORE_LOCAL_X0, 0, 1
b eret_to_el0
END_FUNC thread_resume
那么threads[n].regs结构体中的PC值又来自哪里呢?
在optee发起RPC调用时,将RPC调用从REE回来后,需要接着运行的地址,写入到了x2中,然后又保存到了struct thread_ctx_regs结构体中
/* void thread_rpc(uint32_t rv[THREAD_RPC_NUM_ARGS]) */
FUNC thread_rpc , :
/* Read daif and create an SPSR */
mrs x1, daif
orr x1, x1, #(SPSR_64_MODE_EL1 << SPSR_64_MODE_EL_SHIFT)
/* Mask all maskable exceptions before switching to temporary stack */
msr daifset, #DAIFBIT_ALL
push x0, xzr
push x1, x30
bl thread_get_ctx_regs
ldr x30, [sp, #8]
store_xregs x0, THREAD_CTX_REGS_X19, 19, 30
mov x19, x0
bl thread_get_tmp_sp
pop x1, xzr /* Match "push x1, x30" above */
mov x2, sp
str x2, [x19, #THREAD_CTX_REGS_SP]
ldr x20, [sp] /* Get pointer to rv[] */
mov sp, x0 /* Switch to tmp stack */
adr x2, .thread_rpc_return //------------------------等RPC调用从REE再回来时,接着执行thread_rpc_return
mov w0, #THREAD_FLAGS_COPY_ARGS_ON_RETURN
bl thread_state_suspend
mov x4, x0 /* Supply thread index */
ldr w0, =TEESMC_OPTEED_RETURN_CALL_DONE
load_wregs x20, 0, 1, 3 /* Load rv[] into w0-w2 */
smc #0 //-----------------------------------------------RPC调用,这里要切到REE了
b . /* SMC should not return */
.thread_rpc_return: //---------------------------------------------等RPC调用从REE再回来时,接着执行thread_rpc_return
/*
* At this point has the stack pointer been restored to the value
* stored in THREAD_CTX above.
*
* Jumps here from thread_resume above when RPC has returned. The
* IRQ and FIQ bits are restored to what they where when this
* function was originally entered.
*/
pop x16, xzr /* Get pointer to rv[] */
store_wregs x16, 0, 0, 5 /* Store w0-w5 into rv[] */
ret
END_FUNC thread_rpc
KEEP_PAGER thread_rpc
(2)、REE调用的std call的处理 : thread_std_smc_entry
thread_alloc_and_run函数中,先调用init_regs(threads + n, args), 然后再调用thread_resume(&threads[n].regs)
static void thread_alloc_and_run(struct thread_smc_args *args)
{
size_t n;
struct thread_core_local *l = thread_get_core_local();
bool found_thread = false;
assert(l->curr_thread == -1);
lock_global();
for (n = 0; n < CFG_NUM_THREADS; n++) {
if (threads[n].state == THREAD_STATE_FREE) {
threads[n].state = THREAD_STATE_ACTIVE;
found_thread = true;
break;
}
}
unlock_global();
if (!found_thread) {
args->a0 = OPTEE_SMC_RETURN_ETHREAD_LIMIT;
return;
}
l->curr_thread = n;
threads[n].flags = 0;
init_regs(threads + n, args);
/* Save Hypervisor Client ID */
threads[n].hyp_clnt_id = args->a7;
thread_lazy_save_ns_vfp();
thread_resume(&threads[n].regs);
}
而在init_regs中,将thread_std_smc_entry函数的地址,写入到了struct thread_ctx_regs结构体中的pc变量中
thread->regs.pc = (uint64_t)thread_std_smc_entry;
#ifdef ARM64
static void init_regs(struct thread_ctx *thread,
struct thread_smc_args *args)
{
thread->regs.pc = (uint64_t)thread_std_smc_entry;
/*
* Stdcalls starts in SVC mode with masked foreign interrupts, masked
* Asynchronous abort and unmasked native interrupts.
*/
thread->regs.cpsr = SPSR_64(SPSR_64_MODE_EL1, SPSR_64_MODE_SP_EL0,
THREAD_EXCP_FOREIGN_INTR | DAIFBIT_ABT);
/* Reinitialize stack pointer */
thread->regs.sp = thread->stack_va_end;
/*
* Copy arguments into context. This will make the
* arguments appear in x0-x7 when thread is started.
*/
thread->regs.x[0] = args->a0;
thread->regs.x[1] = args->a1;
thread->regs.x[2] = args->a2;
thread->regs.x[3] = args->a3;
thread->regs.x[4] = args->a4;
thread->regs.x[5] = args->a5;
thread->regs.x[6] = args->a6;
thread->regs.x[7] = args->a7;
/* Set up frame pointer as per the Aarch64 AAPCS */
thread->regs.x[29] = 0;
}
#endif /*ARM64*/
而在thread_resume中,将struct thread_ctx_regs结构体中的pc值赋给了x2, x2又赋给了ELR.
thread_resume程序返回后,就将执行ELR执行的地址,也就是对应着thread_std_smc_entry函数
/* void thread_resume(struct thread_ctx_regs *regs) */
FUNC thread_resume , :
load_xregs x0, THREAD_CTX_REGS_SP, 1, 3 //-------------------------x2等于struct thread_ctx_regs结构体中的pc值
load_xregs x0, THREAD_CTX_REGS_X4, 4, 30
mov sp, x1
msr elr_el1, x2 //-------------------------将x2赋值到elr寄存器
msr spsr_el1, x3
b_if_spsr_is_el0 w3, 1f
load_xregs x0, THREAD_CTX_REGS_X1, 1, 3
ldr x0, [x0, THREAD_CTX_REGS_X0]
eret //------------------------------------eret返回,则跳转到elr中的地址
1: load_xregs x0, THREAD_CTX_REGS_X1, 1, 3
ldr x0, [x0, THREAD_CTX_REGS_X0]
msr spsel, #1
store_xregs sp, THREAD_CORE_LOCAL_X0, 0, 1
b eret_to_el0
END_FUNC thread_resume
3、线程thread_std_smc_entry
当REE调用标准的std smc时,将进入optee中的thread_std_smc_entry程序,那么该程序都做了什么事情呢?
thread_std_smc_entry调用__thread_std_smc_entry,而__thread_std_smc_entry又会调用平台的std_smc_hander
void __weak __thread_std_smc_entry(struct thread_smc_args *args)
{
thread_std_smc_handler_ptr(args); //-------------------------------这里调用vendor厂商实现的std_smc_hander,在main.c中
if (args->a0 == OPTEE_SMC_RETURN_OK) {
struct thread_ctx *thr = threads + thread_get_id();
tee_fs_rpc_cache_clear(&thr->tsd);
if (!thread_prealloc_rpc_cache) {
thread_rpc_free_arg(thr->rpc_carg);
mobj_free(thr->rpc_mobj);
thr->rpc_carg = 0;
thr->rpc_arg = 0;
thr->rpc_mobj = NULL;
}
}
}
而平台实现的std_smc_hander,其实都是指向entry_std.c中的std_smc_hander(意思就是平台可以自行实现,也可以用标准的entry_std.c中的)
从std_smc_hander的实现来看,std call也就是支持一些标准的命令,如open close invoke等
/*
* Note: this function is weak just to make it possible to exclude it from
* the unpaged area.
*/
void __weak tee_entry_std(struct thread_smc_args *smc_args)
{
paddr_t parg;
struct optee_msg_arg *arg = NULL; /* fix gcc warning */
uint32_t num_params = 0; /* fix gcc warning */
struct mobj *mobj;
if (smc_args->a0 != OPTEE_SMC_CALL_WITH_ARG) {
EMSG("Unknown SMC 0x%" PRIx64, (uint64_t)smc_args->a0);
DMSG("Expected 0x%x\n", OPTEE_SMC_CALL_WITH_ARG);
smc_args->a0 = OPTEE_SMC_RETURN_EBADCMD;
return;
}
parg = (uint64_t)smc_args->a1 << 32 | smc_args->a2;
/* Check if this region is in static shared space */
if (core_pbuf_is(CORE_MEM_NSEC_SHM, parg,
sizeof(struct optee_msg_arg))) {
mobj = get_cmd_buffer(parg, &num_params);
} else {
if (parg & SMALL_PAGE_MASK) {
smc_args->a0 = OPTEE_SMC_RETURN_EBADADDR;
return;
}
mobj = map_cmd_buffer(parg, &num_params);
}
if (!mobj || !ALIGNMENT_IS_OK(parg, struct optee_msg_arg)) {
EMSG("Bad arg address 0x%" PRIxPA, parg);
smc_args->a0 = OPTEE_SMC_RETURN_EBADADDR;
mobj_free(mobj);
return;
}
arg = mobj_get_va(mobj, 0);
assert(arg && mobj_is_nonsec(mobj));
/* Enable foreign interrupts for STD calls */
thread_set_foreign_intr(true);
switch (arg->cmd) {
case OPTEE_MSG_CMD_OPEN_SESSION:
entry_open_session(smc_args, arg, num_params);
break;
case OPTEE_MSG_CMD_CLOSE_SESSION:
entry_close_session(smc_args, arg, num_params);
break;
case OPTEE_MSG_CMD_INVOKE_COMMAND:
entry_invoke_command(smc_args, arg, num_params);
break;
case OPTEE_MSG_CMD_CANCEL:
entry_cancel(smc_args, arg, num_params);
break;
case OPTEE_MSG_CMD_REGISTER_SHM:
register_shm(smc_args, arg, num_params);
break;
case OPTEE_MSG_CMD_UNREGISTER_SHM:
unregister_shm(smc_args, arg, num_params);
break;
default:
EMSG("Unknown cmd 0x%x\n", arg->cmd);
smc_args->a0 = OPTEE_SMC_RETURN_EBADCMD;
}
mobj_free(mobj);
}
关注"Arm精选"公众号,备注进ARM交流讨论区。