OP-TEE: libteec

⚠️ The code is based on: https://gitlab.com/riseproject/riscv-optee/optee_os/-/tree/dev-optee-mpxy Commit ID: 75df9ba41a404aec897399ead0ff0aebcbff48ca TEEC_InitializeContext() teec_open_dev() Open /dev/teeX device. TEEC_OpenSession() Call ioctl() with TEE_IOC_OPEN_SESSION command. This will eventually trap to Linux Kernel’s tee_ioctl(). TEEC_InvokeCommand() Call ioctl() with TEE_IOC_INVOKE command. The command ID for TA is passed through arg->func. TEEC_CloseSession() Call ioctl() with TEE_IOC_CLOSE_SESSION command. TEEC_FinalizeContext() Close /dev/teeX device.

2024/12/18 · 1 分鐘 · 54 字 · Frank Chang

OP-TEE: ABI

⚠️ The code is based on: https://gitlab.com/riseproject/riscv-optee/optee_os/-/tree/dev-optee-mpxy Commit ID: 75df9ba41a404aec897399ead0ff0aebcbff48ca std_abi_entry() If args->a0: OPTEE_ABI_CALL_WITH_ARG or OPTEE_ABI_CALL_WITH_RPC_ARG: std_entry_with_parg() call_entry_std() tee_entry_std() __tee_entry_std() OPTEE_ABI_CALL_WITH_REGD_ARG: std_entry_with_regd_arg() __tee_entry_std() Call thread_set_foreign_intr() to enable all foreign interrupts. If arg->cmd: OPTEE_MSG_CMD_OPEN_SESSION: entry_open_session() OPTEE_MSG_CMD_CLOSE_SESSION: entry_close_session() OPTEE_MSG_CMD_INVOKE_COMMAND: entry_invoke_command() OPTEE_MSG_CMD_CANCEL: entry_cancel() OPTEE_MSG_CMD_REGISTER_SHM: register_shm() OPTEE_MSG_CMD_UNREGISTER_SHM: unregister_shm() …

2024/12/15 · 1 分鐘 · 45 字 · Frank Chang

OP-TEE: RPC

⚠️ The code is based on: https://gitlab.com/riseproject/riscv-optee/optee_os/-/tree/dev-optee-mpxy Commit ID: 75df9ba41a404aec897399ead0ff0aebcbff48ca thread_rpc_cmd() Call thread_rpc() with rpc_args (rv[THREAD_RPC_NUM_ARGS]). rpc_args’s first element is set to OPTEE_ABI_RETURN_RPC_CMD function. thread_rpc() Call __thread_rpc() __thread_rpc() Call xstatus_for_xret() to get xstatus with xstatus.PIE set to 0, xstatus.PP set to S-mode. The returned xstatus is passed to thread_rpc_xstatus(). Call thread_rpc_xstatus(). 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 // core/arch/riscv/kernel/thread_optee_abi_rv.S /* * void thread_rpc_xstatus(uint32_t rv[THREAD_RPC_NUM_ARGS], * unsigned long status); */ FUNC thread_rpc_xstatus , : // Allocate stack space for 8 registers. /* Use stack for temporary storage */ addi sp, sp, -REGOFF(8) /* Read xSTATUS */ csrr a2, CSR_XSTATUS /* Mask all maskable exceptions before switching to temporary stack */ csrw CSR_XIE, x0 /* Save return address xSTATUS and pointer to rv */ // $a0: rv[THREAD_RPC_NUM_ARGS] // $a1: xstatus to restore // $a2: current xstatus STR a0, REGOFF(0)(sp) STR a1, REGOFF(1)(sp) STR s0, REGOFF(2)(sp) STR ra, REGOFF(3)(sp) STR a2, REGOFF(4)(sp) #ifdef CFG_UNWIND addi s0, sp, REGOFF(8) #endif /* Save thread state */ jal thread_get_ctx_regs // $a0 = current thread's struct thread_ctx. // Restore $ra. LDR ra, REGOFF(3)(sp) /* Save ra, sp, gp, tp, and s0~s11 */ store_xregs a0, THREAD_CTX_REG_RA, REG_RA, REG_TP store_xregs a0, THREAD_CTX_REG_S0, REG_S0, REG_S1 store_xregs a0, THREAD_CTX_REG_S2, REG_S2, REG_S11 /* Get to tmp stack */ jal thread_get_tmp_sp // $a0 = tmp stack. /* Get pointer to rv */ LDR s1, REGOFF(0)(sp) // $s1 = rv[THREAD_RPC_NUM_ARGS] /* xSTATUS to restore */ LDR a1, REGOFF(1)(sp) // $a1 = xstatus to restore /* Switch to tmp stack */ mv sp, a0 /* Early load rv[] into s2-s4 */ lw s2, 0(s1) lw s3, 4(s1) lw s4, 8(s1) li a0, THREAD_FLAGS_COPY_ARGS_ON_RETURN la a2, .thread_rpc_return // We are about to return to untrusted domain for RPC, // suspend the current thread. jal [thread_state_suspend](/posts/optee-threads/) // $a0 = thread index before suspend. mv a4, a0 /* thread index */ mv a1, s2 /* rv[0] */ mv a2, s3 /* rv[1] */ mv a3, s4 /* rv[2] */ li a0, TEEABI_OPTEED_RETURN_CALL_DONE mv a5, zero /* Return to untrusted domain */ // $a0: TEEABI_OPTEED_RETURN_CALL_DONE // $a1: rv[0], e.g. OPTEE_ABI_RETURN_RPC_CMD // $a2: rv[1] // $a3: rv[2] // $a4: thread index before suspend. jal [thread_return_to_udomain](/posts/optee-threads/) .thread_rpc_return: /* * Jumps here from thread_resume() above when RPC has returned. * At this point has the stack pointer been restored to the value * stored in THREAD_CTX above. */ /* Get pointer to rv[] */ LDR a4, REGOFF(0)(sp) /* Store a0-a3 into rv[] */ sw a0, 0(a4) sw a1, 4(a4) sw a2, 8(a4) sw a3, 12(a4) /* Pop saved XSTATUS from stack */ LDR s0, REGOFF(4)(sp) csrw CSR_XSTATUS, s0 /* Pop s0 from stack */ LDR s0, REGOFF(2)(sp) addi sp, sp, REGOFF(8) ret END_FUNC thread_rpc_xstatus DECLARE_KEEP_PAGER thread_rpc_xstatus

2024/12/09 · 3 分鐘 · 548 字 · Frank Chang

OP-TEE: ldelf

⚠️ The code is based on: https://gitlab.com/riseproject/riscv-optee/optee_os/-/tree/dev-optee-mpxy Commit ID: 75df9ba41a404aec897399ead0ff0aebcbff48ca ldelf_hex.c is generated by scripts/gen_ldelf_hex.py from the input file: ldelf.elf and is included by OP-TEE, i.e. ldelf_data[], ldelf_code_size, ldelf_data_size, and ldelf_entry. ldelf sources: <optee-src>/ldelf/ ldelf is running in U-mode. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 // core/kernel/ldelf_loader.c /* * This function may leave a few mappings behind on error, but that's taken * care of by tee_ta_init_user_ta_session() since the entire context is * removed then. */ TEE_Result ldelf_load_ldelf(struct user_mode_ctx *uctx) { TEE_Result res = TEE_SUCCESS; vaddr_t stack_addr = 0; vaddr_t code_addr = 0; vaddr_t rw_addr = 0; vaddr_t bb_addr = 0; uint32_t prot = 0; uctx->is_32bit = is_32bit; // Allocate memory for bounce buffer. res = alloc_and_map_fobj(uctx, BOUNCE_BUFFER_SIZE, TEE_MATTR_PRW, 0, &bb_addr); if (res) return res; uctx->bbuf = (void *)bb_addr; uctx->bbuf_size = BOUNCE_BUFFER_SIZE; // Allocate stack memory for ldelf. res = alloc_and_map_fobj(uctx, LDELF_STACK_SIZE, TEE_MATTR_URW | TEE_MATTR_PRW, VM_FLAG_LDELF, &stack_addr); if (res) return res; uctx->ldelf_stack_ptr = stack_addr + LDELF_STACK_SIZE; // Allocate code section memory for ldelf. res = alloc_and_map_fobj(uctx, ldelf_code_size, TEE_MATTR_PRW, VM_FLAG_LDELF, &code_addr); if (res) return res; // Assign uctx->entry_func to ldelf's entry point, i.e. [_ldelf_start()](/posts/optee-ldelf/). uctx->entry_func = code_addr + ldelf_entry; // Allocate data section memory for ldelf. rw_addr = ROUNDUP(code_addr + ldelf_code_size, SMALL_PAGE_SIZE); res = alloc_and_map_fobj(uctx, ldelf_data_size, TEE_MATTR_URW | TEE_MATTR_PRW, VM_FLAG_LDELF, &rw_addr); if (res) return res; vm_set_ctx(uctx->ts_ctx); // Copy ldelf codes. // ldelf_data[] includes both ldelf's codes and data. // ldelf_data[] is generated by script/gen_ldelf_hex.py. memcpy((void *)code_addr, ldelf_data, ldelf_code_size); // Copy ldelf data. res = copy_to_user((void *)rw_addr, ldelf_data + ldelf_code_size, ldelf_data_size); if (res) return res; prot = TEE_MATTR_URX; if (IS_ENABLED(CFG_CORE_BTI)) prot |= TEE_MATTR_GUARDED; res = vm_set_prot(uctx, code_addr, ROUNDUP(ldelf_code_size, SMALL_PAGE_SIZE), prot); if (res) return res; DMSG("ldelf load address %#"PRIxVA, code_addr); return TEE_SUCCESS; } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 // core/kernel/ldelf_loader.c TEE_Result ldelf_init_with_ldelf(struct ts_session *sess, struct user_mode_ctx *uctx) { TEE_Result res = TEE_SUCCESS; struct ldelf_arg *arg = NULL; uint32_t panic_code = 0; uint32_t panicked = 0; uaddr_t usr_stack = 0; struct ldelf_arg *arg_bbuf = NULL; // Set user stack. usr_stack = uctx->ldelf_stack_ptr; usr_stack -= ROUNDUP(sizeof(*arg), STACK_ALIGNMENT); arg = (struct ldelf_arg *)usr_stack; // Temporay overwrites handle_scall() with [scall_handle_ldelf()](/posts/optee-ldelf/). sess->handle_scall = scall_handle_ldelf; res = clear_user(arg, sizeof(*arg)); if (res) return res; // Copy UUID to &arg->uuid. res = PUT_USER_SCALAR(uctx->ts_ctx->uuid, &arg->uuid); if (res) return res; // uctx->entry_func is set to ldelf's entry point, i.e. [_ldelf_start()](/posts/optee-ldelf/), // in [ldelf_load_ldelf()](/posts/optee-ldelf/). // Switch to U-mode to execute ldelf. // ldelf will fill in arg parameter of the TA ELF. // E.g. arg->is_32bit, arg->entry_func, arg->load_addr, arg->stack_ptr... etc. res = [thread_enter_user_mode](/posts/optee-threads/)((vaddr_t)arg, 0, 0, 0, usr_stack, uctx->entry_func, is_32bit, &panicked, &panic_code); // Restore handle_scall(). sess->handle_scall = sess->ctx->ops->handle_scall; thread_user_clear_vfp(uctx); ldelf_sess_cleanup(sess); if (panicked) { abort_print_current_ts(); EMSG("ldelf panicked"); return TEE_ERROR_GENERIC; } if (res) { EMSG("ldelf failed with res: %#"PRIx32, res); return res; } res = BB_MEMDUP_USER(arg, sizeof(*arg), &arg_bbuf); if (res) return res; if (is_user_ta_ctx(uctx->ts_ctx)) { /* * This is already checked by the elf loader, but since it runs * in user mode we're not trusting it entirely. */ if (arg_bbuf->flags & ~TA_FLAGS_MASK) return TEE_ERROR_BAD_FORMAT; to_user_ta_ctx(uctx->ts_ctx)->ta_ctx.flags = arg_bbuf->flags; } // Copy TA ELF's information to struct user_mode_ctx. uctx->is_32bit = arg_bbuf->is_32bit; uctx->entry_func = arg_bbuf->entry_func; uctx->load_addr = arg_bbuf->load_addr; uctx->stack_ptr = arg_bbuf->stack_ptr; uctx->dump_entry_func = arg_bbuf->dump_entry; #ifdef CFG_FTRACE_SUPPORT uctx->ftrace_entry_func = arg_bbuf->ftrace_entry; sess->fbuf = arg_bbuf->fbuf; #endif uctx->dl_entry_func = arg_bbuf->dl_entry; bb_free(arg_bbuf, sizeof(*arg)); return TEE_SUCCESS; } scall_handle_ldelf() ...

2024/12/06 · 5 分鐘 · 1007 字 · Frank Chang

OP-TEE: REE filesystem TA

⚠️ The code is based on: https://gitlab.com/riseproject/riscv-optee/optee_os/-/tree/dev-optee-mpxy Commit ID: 75df9ba41a404aec897399ead0ff0aebcbff48ca Reference: Trusted Applications — OP-TEE documentation documentation ree_fs_ta_open() Call rpc_load() to request TA from tee-supplicant. Validate the loaded TA. rpc_load() Call thread_rpc_cmd() with OPTEE_RPC_CMD_LOAD_TA RPC command without struct thread_param_memref parameter to request the size of TA. OPTEE_RPC_CMD_LOAD_TA RPC command is saved to struct optee_msg_arg.cmd. struct optee_msg_arg is stored in the shared memory shared with the untrusted domain. Call thread_rpc_alloc_payload() to allocate data for TA. Call thread_rpc_alloc() to allocate shared memory for TA. Call thread_rpc() with rpc_args (rv[THREAD_RPC_NUM_ARGS]). rpc_args’s first element is set to OPTEE_ABI_RETURN_RPC_CMD function. The RPC command is set to OPTEE_RPC_CMD_SHM_ALLOC to allocate the shared memory for TA. Call thread_rpc_cmd() with OPTEE_RPC_CMD_LOAD_TA RPC command again with struct thread_param_memref parameter to load TA.

2024/12/03 · 1 分鐘 · 123 字 · Frank Chang

OP-TEE: User TAs

⚠️ The code is based on: https://gitlab.com/riseproject/riscv-optee/optee_os/-/tree/dev-optee-mpxy Commit ID: 75df9ba41a404aec897399ead0ff0aebcbff48ca tee_ta_init_user_ta_session() Initialize user TA for the UUID, assign ctx->ts_ctx.ops to user_ta_ops by calling set_ta_ctx_ops(). user_ta_ops will be the global callbacks for the user TAs; assign tee_ta_session->ts_sess.handle_scall to scall_handle_user_ta(). scall_handle_user_ta() will be the default callback to handle the syscall from user TA: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 // core/kernel/user_ta.c TEE_Result tee_ta_init_user_ta_session(const TEE_UUID *uuid, struct tee_ta_session *s) { TEE_Result res = TEE_SUCCESS; struct user_ta_ctx *utc = NULL; ..... // Initialize lists. TAILQ_INIT(&utc->open_sessions); TAILQ_INIT(&utc->cryp_states); TAILQ_INIT(&utc->objects); TAILQ_INIT(&utc->storage_enums); condvar_init(&utc->ta_ctx.busy_cv); utc->ta_ctx.ref_count = 1; /* * Set context TA operation structure. It is required by generic * implementation to identify userland TA versus pseudo TA contexts. */ // utc->ta_ctx->ts_ctx.ops = [user_ta_ops](/posts/optee-user-tas/). set_ta_ctx_ops(&utc->ta_ctx); utc->ta_ctx.ts_ctx.uuid = *uuid; // vm_info_init() will set: utc->uctx->ts_ctx = &utc->ta_ctx.ts_ctx. res = vm_info_init(&utc->uctx, &utc->ta_ctx.ts_ctx); if (res) { condvar_destroy(&utc->ta_ctx.busy_cv); free_utc(utc); return res; } ..... utc->ta_ctx.is_initializing = true; ..... s->ts_sess.ctx = &utc->ta_ctx.ts_ctx; s->ts_sess.handle_scall = s->ts_sess.ctx->ops->handle_scall; /* * Another thread trying to load this same TA may need to wait * until this context is fully initialized. This is needed to * handle single instance TAs. */ TAILQ_INSERT_TAIL(&tee_ctxes, &utc->ta_ctx, link); return TEE_SUCCESS; } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 // core/kernel/user_ta.c /* * Note: this variable is weak just to ease breaking its dependency chain * when added to the unpaged area. */ const struct ts_ops user_ta_ops __weak __relrodata_unpaged("user_ta_ops") = { .enter_open_session = user_ta_enter_open_session, .enter_invoke_cmd = user_ta_enter_invoke_cmd, .enter_close_session = user_ta_enter_close_session, #if defined(CFG_TA_STATS) .dump_mem_stats = user_ta_enter_dump_memstats, #endif .dump_state = user_ta_dump_state, #ifdef CFG_FTRACE_SUPPORT .dump_ftrace = user_ta_dump_ftrace, #endif .release_state = user_ta_release_state, .destroy = user_ta_ctx_destroy, .get_instance_id = user_ta_get_instance_id, .handle_scall = scall_handle_user_ta, #ifdef CFG_TA_GPROF_SUPPORT .gprof_set_status = user_ta_gprof_set_status, #endif }; tee_ta_complete_user_ta_session() Call ldelf_load_ldelf() to load ldelf program to the memory for this User TA. ldelf is responsible for loading the user TA ELF image residing in REE to the memory. If ldelf_load_ldelf() returns TEE_SUCCESS, call ldelf_init_with_ldelf() ldelf_load_ldelf() loads user TA ELF and fill in user TA ELF’s information to struct user_mode_ctx. user_ta_enter_open_session() Call user_ta_enter() with function ID: UTEE_ENTRY_FUNC_OPEN_SESSION. user_ta_enter_invoke_cmd() Call user_ta_enter() with function ID: UTEE_ENTRY_FUNC_INVOKE_COMMAND. user_ta_enter() Call thread_enter_user_mode() to switch to U-mode. utc->utcx.entry_func (user TA’s entry function address, filled by ldelf) will be called after switching to U-mode. E.g. For optee_example_hello_world, i.e. 8aaaf200-2450-11e4-abe2-0002a5d5c51b.elf, the entry_func is 0x400405f8 => __ta_entry(). __ta_entry() is the first user TA API called from TEE core (defined in ta/user_ta_header.c). It’s assigned in TA’s Makefile: ...

2024/11/30 · 4 分鐘 · 696 字 · Frank Chang

OP-TEE: Pseudo TAs

⚠️ The code is based on: https://gitlab.com/riseproject/riscv-optee/optee_os/-/tree/dev-optee-mpxy Commit ID: 75df9ba41a404aec897399ead0ff0aebcbff48ca tee_ta_init_pseudo_ta_session() Look up the pseudo TA based on UUID. Create pseudo TA context for the UUID, assign ctx->ts_ctx.ops to pseudo_ta_ops. pseudo_ta_ops will be the global callbacks for the pseudo TAs: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 // core/kernel/pseudo_ta.c TEE_Result tee_ta_init_pseudo_ta_session(const TEE_UUID *uuid, struct tee_ta_session *s) { ..... ctx->ref_count = 1; ctx->flags = ta->flags; stc->pseudo_ta = ta; ctx->ts_ctx.uuid = ta->uuid; ctx->ts_ctx.ops = &pseudo_ta_ops; ..... } 1 2 3 4 5 6 7 8 // core/kernel/pseudo_ta.c static const struct ts_ops pseudo_ta_ops = { .enter_open_session = pseudo_ta_enter_open_session, .enter_invoke_cmd = pseudo_ta_enter_invoke_cmd, .enter_close_session = pseudo_ta_enter_close_session, .destroy = pseudo_ta_destroy, }; pseudo_ta_enter_open_session() Call stc->pseudo_ta->open_session_entry_point() callback, if defined. E.g. If the opened session is for pseudo TA: rtc.pta, open_session_entry_point() callback is: open_session(): ...

2024/11/21 · 1 分鐘 · 211 字 · Frank Chang

OP-TEE: TAs

⚠️ The code is based on: https://gitlab.com/riseproject/riscv-optee/optee_os/-/tree/dev-optee-mpxy Commit ID: 75df9ba41a404aec897399ead0ff0aebcbff48ca entry_open_session() Open the session based on the UUID. tee_ta_open_session() tee_ta_open_session() tee_ta_init_session() Call ts_ctx->ops->enter_open_session() callback, For pseudo TAs, the callback is: pseudo_ta_enter_open_session() For user TAs, the callback is user_ta_enter_open_session() tee_ta_init_session() Look for already loaded TA tee_ta_init_session_with_context() If the TA for this UUID is not loaded yet: Look for secure partition stmm_init_session() Look for pseudo TA tee_ta_init_pseudo_ta_session() Look for user TA tee_ta_init_user_ta_session() If tee_ta_init_user_ta_session() returns TEE_SUCCESS, call tee_ta_complete_user_ta_session() entry_invoke_command() Call tee_ta_get_session() to get the opened session from arg->session. Call tee_ta_invoke_command() on the opened session. Call ts_ctx->ops->enter_invoke_cmd(): For pseudo TAs, the callback is: pseudo_ta_enter_invoke_cmd() For user TAs, the call back is: user_ta_enter_invoke_cmd()

2024/11/17 · 1 分鐘 · 109 字 · Frank Chang

OP-TEE: SBI MPXY

⚠️ The code is based on: https://gitlab.com/riseproject/riscv-optee/optee_os/-/tree/dev-optee-mpxy Commit ID: 75df9ba41a404aec897399ead0ff0aebcbff48ca mpxy_opteed_channel_init() Check if MPXY extension is supported by OpenSBI. Extract MPXY channel ID from DT: compatible = “riscv,sbi-mpxy-opteed"; riscv,sbi-mpxy-channel-id ← Defines MPXY channel ID. Save MPXY channel ID to mpxy_opteed_ctx.channel_id. opensbi-domain-instance ← Defines the OpenSBI domain used by OP-TEE (not used by OP-TEE). 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 chosen { opensbi-domains { trusted-domain { compatible = "opensbi,domain,instance"; regions = <0x04 0x3f>; possible-harts = <0x03 0x01>; next-addr = <0x00 0xf1000000>; next-mode = <0x01>; phandle = <0x02>; }; }; sbi-mpxy-opteed { opensbi-domain-instance = <0x02>; riscv,sbi-mpxy-channel-id = <0x02>; compatible = "riscv,sbi-mpxy-opteed"; }; sbi_mpxy_setup_shmem() ...

2024/10/18 · 2 分鐘 · 226 字 · Frank Chang

OP-TEE: Threads

⚠️ The code is based on: https://gitlab.com/riseproject/riscv-optee/optee_os/-/tree/dev-optee-mpxy Commit ID: 75df9ba41a404aec897399ead0ff0aebcbff48ca 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 // core/arch/riscv/include/kernel/thread_arch.h struct thread_core_local { unsigned long x[4]; uint32_t hart_id; vaddr_t tmp_stack_va_end; short int curr_thread; uint32_t flags; vaddr_t abt_stack_va_end; #ifdef CFG_TEE_CORE_DEBUG unsigned int locked_count; /* Number of spinlocks held */ #endif #ifdef CFG_CORE_DEBUG_CHECK_STACKS bool stackcheck_recursion; #endif #ifdef CFG_FAULT_MITIGATION struct ftmn_func_arg *ftmn_arg; #endif } THREAD_CORE_LOCAL_ALIGNED; 1 2 3 4 // core/kernel/thread.c // Per-core local threads. struct thread_core_local thread_core_local[CFG_TEE_CORE_NB_CORE] __nex_bss; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 // core/arch/riscv/kernel/entry.S /* * Implement based on the transport method used to communicate between * untrusted domain and trusted domain. It could be an SBI/ECALL-based to * a security monitor running in M-Mode and panic or messaging-based across * domains where we return to a messaging callback which parses and handles * messages. * * void thread_return_to_udomain(unsigned long arg0, unsigned long arg1, * unsigned long arg2, unsigned long arg3, * unsigned long arg4, unsigned long arg5); */ FUNC thread_return_to_udomain , : /* Caller should provide arguments in a0~a5 */ // E.g. when booting: // $a0: If boot core: TEEABI_OPTEED_RETURN_ENTRY_DONE. // Otherwise: TEEABI_OPTEED_RETURN_ON_DONE. // $a1: If boot core: thread_vector_table. // Otherwise: 0x0 (OPTEE_ABI_RETURN_OK) on success // or anything else to indicate error condition. // $a2 ~ $a5: 0x0 #if defined(CFG_RISCV_WITH_M_MODE_SM) jal [thread_return_to_udomain_by_mpxy](/posts/optee-sbi-mpxy/) #else /* Other protocol */ #endif /* ABI to REE should not return */ panic_at_abi_return END_FUNC thread_return_to_udomain 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 // core/arch/riscv/kernel/thread_optee_abi_rv.S /* * Vector table supplied to M-mode secure monitor (e.g., openSBI) at * initialization. * * Note that M-mode secure monitor depends on the layout of this vector table, * any change in layout has to be synced with M-mode secure monitor. */ FUNC thread_vector_table , : , .identity_map, , nobti .option push .option norvc j [vector_std_abi_entry](/posts/optee-threads/) j [vector_fast_abi_entry](/posts/optee-threads/) j . j . j . j . j vector_fiq_entry j . j . .option pop END_FUNC thread_vector_table DECLARE_KEEP_PAGER thread_vector_table 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 // core/arch/riscv/kernel/thread_optee_abi_rv.S LOCAL_FUNC vector_std_abi_entry, : , .identity_map // sbi_mpxy_get_shmem() returns VA of the shared memory. jal sbi_mpxy_get_shmem // Shared memory contents: struct thread_abi_args *arg jal [thread_handle_std_abi](/posts/optee-threads/) /* * Normally thread_handle_std_abi() should return via * thread_exit(), thread_rpc(), but if thread_handle_std_abi() * hasn't switched stack (error detected) it will do a normal "C" * return. */ /* Restore thread_handle_std_abi() return value */ mv a1, a0 li a2, 0 li a3, 0 li a4, 0 // Function ID: This will be placed as the first item in the shared memory. li a0, TEEABI_OPTEED_RETURN_CALL_DONE mv a5, zero /* Return to untrusted domain */ j [thread_return_to_udomain](/posts/optee-threads/) END_FUNC vector_std_abi_entry 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 // core/arch/riscv/kernel/thread_optee_abi_rv.S LOCAL_FUNC vector_fast_abi_entry , : , .identity_map // sbi_mpxy_get_shmem() returns VA of the shared memory. jal sbi_mpxy_get_shmem // Save the shared memory contents to $a0 ~ $a7 mv t0, a0 ld a0, 0(t0) ld a1, 8(t0) ld a2, 16(t0) ld a3, 24(t0) ld a4, 32(t0) ld a5, 40(t0) ld a6, 48(t0) ld a7, 56(t0) // Allocate space on stack to save $a0 ~ $a7. addi sp, sp, -THREAD_ABI_ARGS_SIZE // Store $a0 ~ $a7 to stack so that we can pass // the pointer to thread_handle_fast_abi() store_xregs sp, THREAD_ABI_ARGS_A0, REG_A0, REG_A7 mv a0, sp jal thread_handle_fast_abi // Restore $a0 ~ $a7 from the stack. load_xregs sp, THREAD_ABI_ARGS_A0, REG_A1, REG_A7 // Release the allocated space. addi sp, sp, THREAD_ABI_ARGS_SIZE // Function ID: This will be placed as the first item in the shared memory. li a0, TEEABI_OPTEED_RETURN_CALL_DONE /* Return to untrusted domain */ j [thread_return_to_udomain](/posts/optee-threads/) END_FUNC vector_fast_abi_entry thread_handle_std_abi() If args->a0 == OPTEE_ABI_CALL_RETURN_FROM_RPC: thread_resume_from_rpc() Otherwise: thread_alloc_and_run() thread_alloc_and_run() Call __thread_alloc_and_run() with pc parameter set to thread_std_abi_entry(). So when thread is resumed, thread_std_abi_entry() will be executed. __thread_alloc_and_run() Find the free thread whose state is THREAD_STATE_FREE. If found, set thread’s state to THREAD_STATE_ACTIVE. Set the current thread ID (l->curr_thread) to the founded thread ID. Call init_regs() to initialize the registers to be restored of the thread. thread->regs.epc is set to pc. Call thread_resume() to resume the thread. thread_resume_from_rpc() Check if the state of the thread to be resumed (indicated by thread_id) is THREAD_STATE_SUSPENDED. If yes, set thread’s state to THREAD_STATE_ACTIVE. Set the current thread ID (l->curr_thread) to thread_id. Call thread_resume() to resume the thread. Otherwise, return and do nothing. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 // core/arch/riscv/kernel/thread_rv.S /* void thread_resume(struct thread_ctx_regs *regs) */ FUNC thread_resume , : /* Disable global interrupts first */ csrc CSR_XSTATUS, CSR_XSTATUS_IE /* Restore epc */ load_xregs a0, THREAD_CTX_REG_EPC, REG_T0 csrw CSR_XEPC, t0 /* Restore ie */ load_xregs a0, THREAD_CTX_REG_IE, REG_T0 csrw CSR_XIE, t0 /* Restore status */ load_xregs a0, THREAD_CTX_REG_STATUS, REG_T0 csrw CSR_XSTATUS, t0 /* Check if previous privilege mode by status.SPP */ b_if_prev_priv_is_u t0, 1f /* Set scratch as zero to indicate that we are in kernel mode */ csrw CSR_XSCRATCH, zero j 2f 1: /* Resume to U-mode, set scratch as tp to be used in the trap handler */ csrw CSR_XSCRATCH, tp 2: /* Restore all general-purpose registers */ load_xregs a0, THREAD_CTX_REG_RA, REG_RA, REG_TP load_xregs a0, THREAD_CTX_REG_T0, REG_T0, REG_T2 load_xregs a0, THREAD_CTX_REG_S0, REG_S0, REG_S1 load_xregs a0, THREAD_CTX_REG_S2, REG_S2, REG_S11 load_xregs a0, THREAD_CTX_REG_T3, REG_T3, REG_T6 load_xregs a0, THREAD_CTX_REG_A0, REG_A0, REG_A7 XRET END_FUNC thread_resume 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 // core/arch/riscv/kernel/thread_optee_abi_rv.S FUNC thread_std_abi_entry , : jal [__thread_std_abi_entry](/posts/optee-threads/) /* Save return value */ mv s0, a0 /* Disable all interrupts */ csrw CSR_XIE, x0 /* Switch to temporary stack */ jal thread_get_tmp_sp mv sp, a0 /* * We are returning from thread_alloc_and_run() * set thread state as free */ // Set: // current thread's state to THREAD_STATE_FREE. // l->curr_thread to THREAD_ID_INVALID. jal thread_state_free /* Restore __thread_std_abi_entry() return value */ mv a1, s0 li a2, 0 li a3, 0 li a4, 0 li a0, TEEABI_OPTEED_RETURN_CALL_DONE mv a5, zero /* Return to untrusted domain */ jal thread_return_to_udomain END_FUNC thread_std_abi_entry __thread_std_abi_entry() Call std_abi_entry() thread_handle_fast_abi() tee_entry_fast() tee_entry_fast() __tee_entry_fast() __tee_entry_fast() If args->a0: OPTEE_ABI_CALLS_COUNT: tee_entry_get_api_call_count() OPTEE_ABI_CALLS_UID: tee_entry_get_api_uuid() … thread_enter_user_mode() Disable all interrupts. Call xstatus_for_xret() to get xstatus with xstatus.PIE set to 1, xstatus.PP set to U-mode. The returned xstatus will be saved along with $a0, $a1, $a2, $a3, user_sp, entry_func, and $xie to current thread’s reg context (struct thread_ctx_regs). Call __thread_enter_user_mode() to switch to U-mode. entry_func is set to thread_ctx_regs.ra and then set to $xepc so that when xret is called to return to U-mode, entry_func will be executed. Re-enable original interrupts. thread_state_suspend() Current thread’s context (struct thread_ctx) will be updated with: flags |= THREAD_FLAGS_COPY_ARGS_ON_RETURN regs.status = xstatus to return regs.epc = [.thread_rpc_return](/posts/optee-rpc/#hl-0-84) (defined within thread_rpc_xstatus()) So when the thread is returned from RPC by thread_resume_from_rpc() , .thread_rpc_return will be called. state = THREAD_STATE_SUSPENDED Current thread ID (l->curr_thread) is set to THREAD_ID_INVALID to indicate no active current thread. thread_mask_exceptions() Mask the interrupts. thread_unmask_exceptions() Unmask the interrupts.

2024/10/14 · 6 分鐘 · 1263 字 · Frank Chang