嵌入式开发论坛

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 134|回复: 0

33. secure world对smc请求的处理------invoke command操作在OP-TEE中...

[复制链接]

59

主题

64

帖子

255

积分

版主

Rank: 7Rank: 7Rank: 7

积分
255
发表于 2018-11-25 14:30:31 | 显示全部楼层 |阅读模式
在REE侧的CA执行open session成功之后,CA就可以使用获取到的session和command ID调用TEEC_InvokeCommand接口来实现让TA指定特定的command的操作。在REE侧调用TEEC_InvokeCommand接口之后,该函数会将调用时带入的session变量,command ID,以及需要传递給TA的参数信息通过ioctl的系统调用发送到OP-TEE的驱动中,驱动最终会调用optee_invoke_func函数会将需要传递給TA的参数信息保存在共享内存中,并触发smc操作,切换到monitor模式进行secure world端的处理,invoke command操作的smc请求最终会被作为标准smc(std smc)进行解析并建立一个专门的thread进入thread_std_smc_entry函数执行,线程运行到tee_entry_std函数时会对smc请求做出判定进入invoke command分支。出发smc之后在secure world端的完成执行流程如下图所示:

1. 切换到userspace运行  线程会调用session中注册的ops成员中的enter_invoke_cmd指定的函数来处理invoke command操作,而enter_invoke_cmd指向user_ta_enter_invoke_cmd函数,该函数会调用user_ta_enter执行,在user_ta_enter函数中加载userspace的上下文然后调用__thread_enter_user_mode函数将该线程切换到userspace继续执行,该函数是以汇编的方式实现,其内容如下:
  1. FUNC __thread_enter_user_mode , :
  2. UNWIND(        .fnstart)
  3. UNWIND(        .cantunwind)
  4.         /*
  5.          * Save all registers to allow syscall_return() to resume execution
  6.          * as if this function would have returned. This is also used in
  7.          * syscall_panic().
  8.          *
  9.          * If stack usage of this function is changed
  10.          * thread_unwind_user_mode() has to be updated.
  11.          */
  12.         push    {r4-r12,lr}        //保存r4~r12和lr寄存器中的值

  13. /* 将用户栈地址保存到r4寄存去中 */
  14.         ldr     r4, [sp, #(10 * 0x4)]   /* user stack pointer */

  15. /* 将user的入口功能函数的地址保存到r5寄存器中 */
  16.         ldr     r5, [sp, #(11 * 0x4)]   /* user function */

  17. /* 将spsr的数据存放到r6寄存器中 */
  18.         ldr     r6, [sp, #(12 * 0x4)]   /* spsr */

  19.         /*
  20.          * Set the saved Processors Status Register to user mode to allow
  21.          * entry of user mode through movs below.
  22.          */
  23.         msr     spsr_cxsf, r6        //设置状态保存寄存器,允许记录用户模式
  24.        
  25.         /*
  26.          * Save old user sp and set new user sp.
  27.          */
  28.         cps        #CPSR_MODE_SYS        //进入SYS模式
  29.         mov        r6, sp        //保存sys模式的sp值到r6
  30.         mov     sp, r4        //将user的sp指针保存到sp中
  31.         cps        #CPSR_MODE_SVC        //切换到SVC模式
  32.         push        {r6,r7}       

  33.         /*
  34.         * Don't allow return from this function, return is done through
  35.         * thread_unwind_user_mode() below.
  36.         */
  37.         mov     lr, #0
  38.         /* Call the user function with its arguments */
  39.         movs    pc, r5        //将跳转地址存放到pc中下一指令就会跳转到指定的user functon执行
  40. UNWIND(        .fnend)
复制代码

  movs pc, r5是将参数中指定的user function的地址赋值给pc,下一条指令就会跳转到pc执行的地址继续运行,sp寄存器也被赋值成了userspace的栈指针,且spsr也被设置成了传入的r6寄存器的数据,r6存放的是spsr数据,该数据在调用__thread_enter_user_mode 之前通过执行get_spsr函数被置成user模式的spsr。所以完成了pc指针的赋值之后就能够直接进入到userspace中运行指定的user function.

2. user function的值
  user function的值就是在调用thread_enter_user_mode传入的utc->entry_func指定的函数地址。该地址在Open session的操作是调用ta_load的时候被赋值成ta_head->entry.ptr64。代码如下
utc->entry_func = ta_head->entry.ptr64;
  而ta_head即指向与uuid想对应的TA image中的ta_head段中的内容,而ta_head段中存放的内容可从user_ta_head.c文件中查看到,如下:
  1. const struct ta_head ta_head __section(".ta_head") = {
  2.         /* UUID, unique to each TA */
  3.         .uuid = TA_UUID,        //该TA的UUID值
  4.         /*
  5.          * According to GP Internal API, TA_FRAMEWORK_STACK_SIZE corresponds to
  6.          * the stack size used by the TA code itself and does not include stack
  7.          * space possibly used by the Trusted Core Framework.
  8.          * Hence, stack_size which is the size of the stack to use,
  9.          * must be enlarged
  10.          */
  11. /* 设定该TA在userspace栈的大小 */
  12.         .stack_size = TA_STACK_SIZE + TA_FRAMEWORK_STACK_SIZE,
  13.         .flags = TA_FLAG_USER_MODE | TA_FLAGS,         //设定flag
  14. #ifdef __ILP32__
  15.         /*
  16.          * This workaround is neded on 32-bit because it seems we can't
  17.          * initialize a 64-bit integer from the address of a function.
  18.          */
  19.         .entry.ptr32 = { .lo = (uint32_t)__utee_entry },                //指定TA的entry fuctiong函数指针
  20. #else
  21.         .entry.ptr64 = (uint64_t)__utee_entry,
  22. #endif
  23. };
复制代码

  为兼容64位系统,在传入user function的时候使用的是ptr64。也就是说在thread_enter_user_mode函数中传入到__thread_enter_user_mode 函数中的entry_func的值是__utee_entry函数的地址。由此可见调用完__thread_enter_user_mode之后程序是进入到__utee_entry函数继续执行。__utee_entry函数的内容如下:
  1. void __noreturn __utee_entry(unsigned long func, unsigned long session_id,
  2.                         struct utee_params *up, unsigned long cmd_id)
  3. {
  4.         TEE_Result res;

  5. /* 根据传入的func值来确定进入那个分支 */
  6.         switch (func) {
  7. /* 在user space中处理open session操作 */
  8.         case UTEE_ENTRY_FUNC_OPEN_SESSION:
  9.                 res = entry_open_session(session_id, up);
  10.                 break;
  11. /* 在user space中处理close session操作 */
  12.         case UTEE_ENTRY_FUNC_CLOSE_SESSION:
  13.                 res = entry_close_session(session_id);
  14.                 break;
  15. /* 在user space中处理invoke command操作 */
  16.         case UTEE_ENTRY_FUNC_INVOKE_COMMAND:
  17.                 res = entry_invoke_command(session_id, up, cmd_id);
  18.                 break;
  19.         default:
  20.                 res = 0xffffffff;
  21.                 TEE_Panic(0);
  22.                 break;
  23.         }
  24.         ta_header_save_params(0, NULL);
  25.         utee_return(res);
  26. }
复制代码

3. user space中的entry_invoke_command
  在OP-TEE中定义了两个entry_invoke_command函数,一个是kernel space的函数,一个是user space的函数。当程序运行到user space后则会调用user space的entry_invoke_command函数执行invoke command的操作。user space的entry_invoke_command函数定义在optee_os/lib/libutee/arch/arm/user_ta_entry.c文件中,该函数内容如下:
  1. static TEE_Result entry_invoke_command(unsigned long session_id,
  2.                         struct utee_params *up, unsigned long cmd_id)
  3. {
  4.         TEE_Result res;
  5.         uint32_t param_types;
  6.         TEE_Param params[TEE_NUM_PARAMS];
  7.         struct ta_session *session = ta_header_get_session(session_id);

  8.         if (!session)
  9.                 return TEE_ERROR_BAD_STATE;

  10. /* 检查传入到user space的参数是否合法 */
  11.         __utee_to_param(params, ¶m_types, up);
  12.         ta_header_save_params(param_types, params);

  13. /* 调用TA的TA_InvokeCommandEntryPoint函数 */
  14.         res = TA_InvokeCommandEntryPoint(session->session_ctx, cmd_id,
  15.                                          param_types, params);

  16.         __utee_from_param(up, param_types, params);
  17.         return res;
  18. }
复制代码

  调用到user space的entry_invoke_command函数的时候,其实线程已经进入到了TA image上下文中运行了,所以当调用TA_InvokeCommandEntryPoint函数的时候就会去执行TA image中定义的TA_InvokeCommandEntryPoint函数,而该函数具体会执行什么操作就由特定的TA决定了,一般做法是根据command id的值去执行特定的操作



回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|TEE and Virtualization

GMT+8, 2019-5-25 19:05 , Processed in 0.088964 second(s), 19 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表