以 TEE_OpenTASession 为例
TEE_OpenTASession // lib\libutee\tee_api.c
_utee_open_ta_session(destination, cancellationRequestTimeout,
&up, &s, returnOrigin);
UTEE_SYSCALL _utee_open_ta_session, TEE_SCN_OPEN_TA_SESSION, 5
在定义一个syscall的时候,在userspace层需要通过UTEE_SYSCALL宏来实现,在optee中所有的 utee_xxx类似的接口都使用该宏定义。在 lib\libutee\arch\arm\utee_syscalls_a64.S 中实现
.macro UTEE_SYSCALL name, scn, num_args
mov x8, #(\scn)
svc #0 //触发svc异常,陷入EL1
ret
该宏相当于实现吧了 utee_xxx 的函数,宏的参数 name 相当于 utee_xxx,scn是系统调用的index,numargs参数个数,小于或等于4则表示不需要传递参数给system call。
从el0的svc陷入到optee os内核,首先走到optee os的异常向量表
//core\arch\arm\kernel\thread_a64.S
el0_sync_a64
b el0_sync_a64_finish
b.eq el0_svc
bl thread_svc_handler
sess->handle_svc(regs)
user_ta_handle_svc //core\kernel\user_ta.c
// 根据 scn 从 tee_svc_syscall_table 中找到入口函数
scf = get_tee_syscall_func(scn)
set_svc_retval(regs, tee_svc_do_call(regs, scf))
tee_svc_do_call // 跳转到optee os 中的 syscall entry
/* Call the svc function */
ldr x16, [x19, #SC_REC_X1]
blr x16
根据 scn 从 tee_svc_syscall_table 中找到入口函数。tee_svc_syscall_table定义如下
// core\arch\arm\tee\arch_svc.c
static const struct syscall_entry tee_svc_syscall_table[] = {
///....
SYSCALL_ENTRY(syscall_open_ta_session)
///...
};
例子:
触发方式,linux终端输入以下命令
xtest 1011
TA_InvokeCommandEntryPoint //imx-optee-test\ta\rpc_test\ta_entry.c
switch (nCommandID) {
case TA_RPC_CMD_CRYPT_SHA256:
return rpc_sha256(false, nParamTypes, pParams);
// imx-optee-test\ta\rpc_test\ta_rpc.c
rpc_call_cryp(sec_mem, nParamTypes, pParams, TA_CRYPT_CMD_SHA256);
TEE_OpenTASession(&cryp_uuid, TEE_TIMEOUT_INFINITE, types,
params, &cryp_session, &origin);
以上,在uta中打开另外一个 CRYPT pta。
总结:
最总会调用 tee_svc_do_call
来执行 tee_svc_syscall_table[scn]
中定义的函数。在真正执行tee_svc_syscall_table[scn]
之前会保存相关寄存器以便执行完成之后恢复到 userspace
,而且还需要将userspcae
中带入的数据拷贝到kernel space
供tee_svc_syscall_table[scn]
中的函数使用。