嵌入式开发论坛

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

31. secure world对smc请求的处理------OP-TEE对标准smc(std smc)请求...

[复制链接]

59

主题

64

帖子

255

积分

版主

Rank: 7Rank: 7Rank: 7

积分
255
发表于 2018-11-25 14:25:47 | 显示全部楼层 |阅读模式
当在驱动中触发的是标准smc时(std smc),经过monitor模式下程序的处理后最终会调用到OP-TEE中的vector_std_smc_entry来对请求进行处理,该过程与上一文判定是fast smc请求一样。都是进入monitor模式的smc中断处理函数,然后根据a0参数中的bit31来判定是快速smc还是标准smc。如果bit31的值是0,则会进入到标准smc的处理逻辑。进入到正式操作之前的流程如如下:

  用于处理标准smc的向量处理函数如下:
  1. LOCAL_FUNC vector_std_smc_entry , :
  2. UNWIND(        .fnstart)
  3. UNWIND(        .cantunwind)
  4.         push        {r0-r7}        //将参数入栈
  5.         mov        r0, sp        //将栈指针赋值给r0寄存器
  6.         bl        thread_handle_std_smc        //调用处理函数,参数的地址存放在r0寄存器中
  7.         /*
  8.          * Normally thread_handle_std_smc() should return via
  9.          * thread_exit(), thread_rpc(), but if thread_handle_std_smc()
  10.          * hasn't switched stack (error detected) it will do a normal "C"
  11.          * return.
  12.          */
  13.         pop        {r1-r8}        //出栈操作
  14.         ldr        r0, =TEESMC_OPTEED_RETURN_CALL_DONE        //标记OP-TEE处理完成
  15.         smc        #0        //调用smc切回到normal world
  16.         b        .        /* SMC should not return */
  17. UNWIND(        .fnend)
  18. END_FUNC vector_std_smc_entry
复制代码

函数thread_handle_std_smc的内容如下:
  1. void thread_handle_std_smc(struct thread_smc_args *args)
  2. {
  3. /* 检查堆栈 */
  4.         thread_check_canaries();

  5.         if (args->a0 == OPTEE_SMC_CALL_RETURN_FROM_RPC)
  6. //处理由tee_supplican回复的RPC请求处理结果
  7.                 thread_resume_from_rpc(args);
  8.         else
  9. //处理来自Libteec的请求,主要包括open session, close session, invoke等
  10.                 thread_alloc_and_run(args);       
  11. }
复制代码
  只有在libteec中触发的smc后,需要OP-TEE作出相应的操作后才可能产生来自RPC请求,故先介绍OP-TEE对来自libteec请求部分,主要是对打开session, 关闭close, 调用特定TA的command,取消command等操作。

1. OP-TEE中处理来自libteec的smc请求操作  在《21. OP-TEE中TA与CA执行流程-------libteec介绍》一文中介绍了libteec提供给上层使用的所有接口,这些接口调用之后就会就有可能需要OP-TEE进行对应的操作。在monitor模式对这些请求处理之后会进入到OP-TEE中,然后调用thread_alloc_and_run创建一个线程来对请求做专门的处理。而且在处理过成中还有可能TEE与REE侧之间的RPC请求等。thread_alloc_and_run函数的内容如下:
  1. static void thread_alloc_and_run(struct thread_smc_args *args)
  2. {
  3.         size_t n;
  4. /* 获取当前CPU的ID,并返回该CPU core的对应结构体 */
  5.         struct thread_core_local *l = thread_get_core_local();
  6.         bool found_thread = false;

  7. /* 判定是否有thread正在占用CPU */
  8.         assert(l->curr_thread == -1);

  9. /* 锁定线程状态 */
  10.         lock_global();

  11. /* 查找系统中那个线程空间当前可用 */
  12.         for (n = 0; n < CFG_NUM_THREADS; n++) {
  13.                 if (threads[n].state == THREAD_STATE_FREE) {
  14.                         threads[n].state = THREAD_STATE_ACTIVE;
  15.                         found_thread = true;
  16.                         break;
  17.                 }
  18.         }

  19. /* 解锁 */
  20.         unlock_global();

  21. /* 初步设定返回给REE侧驱动的结果为OPTEE_SMC_RETURN_ETHREAD_LIMIT,返回的
  22. 数据在后续处理中会被更改 */
  23.         if (!found_thread) {
  24.                 args->a0 = OPTEE_SMC_RETURN_ETHREAD_LIMIT;
  25.                 return;
  26.         }

  27. /* 记录当前cortex使用了那个thread空间来执行操作 */
  28.         l->curr_thread = n;

  29. /* 设置选中的thread空间的flag为0*/
  30.         threads[n].flags = 0;

  31. /*并对该thread中使用的pc,cpsr等相关寄存器进行设置,并且将参数传递到thread context的
  32. reg.ro~reg.r7中 */
  33.         init_regs(threads + n, args);

  34.         /* Save Hypervisor Client ID */
  35. /* 保存Hypervisor客户端的ID值 */
  36.         threads[n].hyp_clnt_id = args->a7;

  37. /* 保存vfp相关数据 */
  38.         thread_lazy_save_ns_vfp();

  39. /* 调用thread_resume函数,开始执行被初始化好的thread */
  40.         thread_resume(&threads[n].regs);
  41. }
复制代码

  thread_alloc_and_run会建立一个thread,并通过init_regs函数初始化该thread的运行上下文,指定该thread的入口函数以及运行时的参数,初始化完成之后,调用thread_resume启动该线程。thread的运行上下文的配置和初始化在init_regs函数中实现,内容如下:
  1. static void init_regs(struct thread_ctx *thread,
  2.                 struct thread_smc_args *args)
  3. {       
  4. /* 指定该线程上下文中PC指针的地址,当该thread resume回来之后就会开始执行regs.pc执
  5. 行的函数 */
  6.         thread->regs.pc = (uint32_t)thread_std_smc_entry;

  7.         /*
  8.          * Stdcalls starts in SVC mode with masked foreign interrupts, masked
  9.          * Asynchronous abort and unmasked native interrupts.
  10.          */
  11. /* 设定cpsr寄存器的值,屏蔽外部中断,进入SVC模式 */
  12.         thread->regs.cpsr = read_cpsr() & ARM32_CPSR_E;
  13.         thread->regs.cpsr |= CPSR_MODE_SVC | CPSR_A |
  14.                         (THREAD_EXCP_FOREIGN_INTR << ARM32_CPSR_F_SHIFT);
  15.         /* Enable thumb mode if it's a thumb instruction */
  16.         if (thread->regs.pc & 1)
  17.                 thread->regs.cpsr |= CPSR_T;
  18.         /* Reinitialize stack pointer */
  19.         thread->regs.svc_sp = thread->stack_va_end;         //重新定位栈地址

  20.         /*
  21.          * Copy arguments into context. This will make the
  22.          * arguments appear in r0-r7 when thread is started.
  23.          */
  24. /* 配置运行时传入的参数 */
  25.         thread->regs.r0 = args->a0;
  26.         thread->regs.r1 = args->a1;
  27.         thread->regs.r2 = args->a2;
  28.         thread->regs.r3 = args->a3;
  29.         thread->regs.r4 = args->a4;
  30.         thread->regs.r5 = args->a5;
  31.         thread->regs.r6 = args->a6;
  32.         thread->regs.r7 = args->a7;
  33. }
复制代码

1.1 thread的resum操作
  通过init_regs配置完thread的运行上下文之后,通过调用thread_resume函数来唤醒该线程,让其进入到执行状态。resume函数使用汇编来实现,主要是保存一些寄存器状态,指定thread运行在什么模式。
  1. FUNC thread_resume , :
  2. UNWIND(        .fnstart)
  3. UNWIND(        .cantunwind)
  4.         add        r12, r0, #(13 * 4)        /* Restore registers r0-r12 later */

  5.         cps        #CPSR_MODE_SYS        //进入sys模式
  6.         ldm        r12!, {sp, lr}       

  7.         cps        #CPSR_MODE_SVC        //进入到svc模式
  8.         ldm        r12!, {r1, sp, lr}               
  9.         msr        spsr_fsxc, r1

  10.         cps        #CPSR_MODE_SVC        //进入到svc模式
  11.         ldm        r12, {r1, r2}
  12.         push        {r1, r2}        //出栈操作

  13.         ldm        r0, {r0-r12}        //将参数存放到r0~r12中

  14.         /* Restore CPSR and jump to the instruction to resume at */
  15.         rfefd        sp!        //跳转到thread的pc指针出执行并返回
  16. UNWIND(        .fnend)
  17. END_FUNC thread_resume
复制代码

1.2 用于处理标准smc的thread的入口函数
  在init_regs中的regs.pc中已经制定了该thread被resume回来之后的pc指针为thread_std_smc_entry,当thread被resume之后就会去执行该函数
  1. FUNC thread_std_smc_entry , :
  2. UNWIND(        .fnstart)
  3. UNWIND(        .cantunwind)
  4.         /* Pass r0-r7 in a struct thread_smc_args */
  5.         push        {r0-r7}        //入栈操作,将r0~r7的数据入栈
  6.         mov        r0, sp        //将r0执行栈地址作为参数传递給__thread_std_smc_entry
  7.         bl        __thread_std_smc_entry                //正式对标准smc进行处理
  8.         /*
  9.          * Load the returned r0-r3 into preserved registers and skip the
  10.          * "returned" r4-r7 since they will not be returned to normal
  11.          * world.
  12.          */
  13.         pop        {r4-r7}
  14.         add        sp, #(4 * 4)

  15.         /* Disable interrupts before switching to temporary stack */
  16.         cpsid        aif        //关闭中断
  17.         bl        thread_get_tmp_sp        //获取堆栈
  18.         mov        sp, r0        //将r0的值存放到sp中

  19.         bl        thread_state_free        //释放thread

  20.         ldr        r0, =TEESMC_OPTEED_RETURN_CALL_DONE//设置返回到normal的r0寄存器的值
  21.         mov        r1, r4
  22.         mov        r2, r5
  23.         mov        r3, r6
  24.         mov        r4, r7
  25.         smc        #0        //调用smc,切回到normal world
  26.         b        .        /* SMC should not return */
  27. UNWIND(        .fnend)
  28. END_FUNC thread_std_smc_entry
复制代码

1.3 标准smc请求的handle
  在__thread_std_smc_entry函数中最终会调用thread_std_smc_handler_ptr来对请求做正式的处理,而thread_std_smc_handler_ptr是在OP-TEE启动的过程中执行init_handlers函数时被初始化成了handlers->std_smc。而handlers->std_smc根据不同的板级可能有所不同,在vexpress板级中std_smc的值为tee_entry_std。
  1. void tee_entry_std(struct thread_smc_args *smc_args)
  2. {
  3.         paddr_t parg;
  4.         struct optee_msg_arg *arg = NULL;        /* fix gcc warning */
  5.         uint32_t num_params;

  6. /* 判定a0是否合法 */
  7.         if (smc_args->a0 != OPTEE_SMC_CALL_WITH_ARG) {
  8.                 EMSG("Unknown SMC 0x%" PRIx64, (uint64_t)smc_args->a0);
  9.                 DMSG("Expected 0x%x\n", OPTEE_SMC_CALL_WITH_ARG);
  10.                 smc_args->a0 = OPTEE_SMC_RETURN_EBADCMD;
  11.                 return;
  12.         }
  13. /* 判定传入参数起始地址否存属于non-secure memory中,因为驱动与OP-TEE之间使用共
  14. 享内存来共享数据,而共享内存属于非安全内存 */
  15.         parg = (uint64_t)smc_args->a1 << 32 | smc_args->a2;
  16.         if (!tee_pbuf_is_non_sec(parg, sizeof(struct optee_msg_arg)) ||
  17.             !ALIGNMENT_IS_OK(parg, struct optee_msg_arg) ||
  18.             !(arg = phys_to_virt(parg, MEM_AREA_NSEC_SHM))) {
  19.                 EMSG("Bad arg address 0x%" PRIxPA, parg);
  20.                 smc_args->a0 = OPTEE_SMC_RETURN_EBADADDR;
  21.                 return;
  22.         }

  23. /* 检查所有参数是否存放在non-secure memory中 */
  24.         num_params = arg->num_params;
  25.         if (!tee_pbuf_is_non_sec(parg, OPTEE_MSG_GET_ARG_SIZE(num_params))) {
  26.                 EMSG("Bad arg address 0x%" PRIxPA, parg);
  27.                 smc_args->a0 = OPTEE_SMC_RETURN_EBADADDR;
  28.                 return;
  29.         }

  30.         /* Enable foreign interrupts for STD calls */
  31.         thread_set_foreign_intr(true);        //使能中断

  32. /* 根据参数的cmd成员来判定来自libteec的请求是要求OP-TEE做什么操作 */
  33.         switch (arg->cmd) {
  34. /* 执行打开session操作 */
  35.         case OPTEE_MSG_CMD_OPEN_SESSION:
  36.                 entry_open_session(smc_args, arg, num_params);
  37.                 break;
  38. /* 执行关闭session的操作 */
  39.         case OPTEE_MSG_CMD_CLOSE_SESSION:
  40.                 entry_close_session(smc_args, arg, num_params);
  41.                 break;
  42. /* 请求特定TA执行特定的command */
  43.         case OPTEE_MSG_CMD_INVOKE_COMMAND:
  44.                 entry_invoke_command(smc_args, arg, num_params);
  45.                 break;
  46. /* 请求cancel掉某个session的command */
  47.         case OPTEE_MSG_CMD_CANCEL:
  48.                 entry_cancel(smc_args, arg, num_params);
  49.                 break;
  50.         default:
  51.                 EMSG("Unknown cmd 0x%x\n", arg->cmd);
  52.                 smc_args->a0 = OPTEE_SMC_RETURN_EBADCMD;
  53.         }
  54. }
复制代码

  当进入到tee_entry_std函数之后,根据不同的cmd值来判定是执行session还是执行调用TA的command的操作。尤其是在执行打开session的操作时,根据TA image的不同类型可以能会触发RPC请求,当TA image存放在文件系统中时,在打开session的时候OP-TEE就会触发RPC请求,请求tee_supplicant从文件系统中读取TA image的内容,并将内容传递給OP-TEE,然后经过对image的校验判定完成TA image的加载操作,并将该session添加到session的队列中。



回复

使用道具 举报

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

本版积分规则

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

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

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

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