⚠️ 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.