xref: /optee_os/core/arch/riscv/kernel/thread_arch.c (revision b9807372c2314a7a52993f6db52df545ca20ad3a)
1fdb66914SMarouene Boubakri // SPDX-License-Identifier: BSD-2-Clause
2fdb66914SMarouene Boubakri /*
3fdb66914SMarouene Boubakri  * Copyright 2022-2023 NXP
4fdb66914SMarouene Boubakri  * Copyright (c) 2016-2022, Linaro Limited
5fdb66914SMarouene Boubakri  * Copyright (c) 2014, STMicroelectronics International N.V.
6fdb66914SMarouene Boubakri  * Copyright (c) 2020-2021, Arm Limited
7fdb66914SMarouene Boubakri  */
8fdb66914SMarouene Boubakri 
9fdb66914SMarouene Boubakri #include <platform_config.h>
10fdb66914SMarouene Boubakri 
11fdb66914SMarouene Boubakri #include <assert.h>
12fdb66914SMarouene Boubakri #include <config.h>
13fdb66914SMarouene Boubakri #include <io.h>
14fdb66914SMarouene Boubakri #include <keep.h>
15fdb66914SMarouene Boubakri #include <kernel/asan.h>
16fdb66914SMarouene Boubakri #include <kernel/boot.h>
17a441cdcfSAlvin Chang #include <kernel/interrupt.h>
18fdb66914SMarouene Boubakri #include <kernel/linker.h>
19fdb66914SMarouene Boubakri #include <kernel/lockdep.h>
20fdb66914SMarouene Boubakri #include <kernel/misc.h>
21fdb66914SMarouene Boubakri #include <kernel/panic.h>
22fdb66914SMarouene Boubakri #include <kernel/spinlock.h>
23fdb66914SMarouene Boubakri #include <kernel/tee_ta_manager.h>
24fdb66914SMarouene Boubakri #include <kernel/thread.h>
25fdb66914SMarouene Boubakri #include <kernel/thread_private.h>
26fdb66914SMarouene Boubakri #include <kernel/user_mode_ctx_struct.h>
27fdb66914SMarouene Boubakri #include <kernel/virtualization.h>
28fdb66914SMarouene Boubakri #include <mm/core_memprot.h>
29fdb66914SMarouene Boubakri #include <mm/mobj.h>
30fdb66914SMarouene Boubakri #include <mm/tee_mm.h>
31fdb66914SMarouene Boubakri #include <mm/vm.h>
32fdb66914SMarouene Boubakri #include <riscv.h>
33fdb66914SMarouene Boubakri #include <trace.h>
34fdb66914SMarouene Boubakri #include <util.h>
35fdb66914SMarouene Boubakri 
36d7b20c1eSAlvin Chang /*
37d7b20c1eSAlvin Chang  * This function is called as a guard after each ABI call which is not
38d7b20c1eSAlvin Chang  * supposed to return.
39d7b20c1eSAlvin Chang  */
__panic_at_abi_return(void)40d7b20c1eSAlvin Chang void __noreturn __panic_at_abi_return(void)
41d7b20c1eSAlvin Chang {
42d7b20c1eSAlvin Chang 	panic();
43d7b20c1eSAlvin Chang }
44d7b20c1eSAlvin Chang 
45ed89e939SAlvin Chang /* This function returns current masked exception bits. */
thread_get_exceptions(void)46fdb66914SMarouene Boubakri uint32_t __nostackcheck thread_get_exceptions(void)
47fdb66914SMarouene Boubakri {
48ed89e939SAlvin Chang 	uint32_t xie = read_csr(CSR_XIE) & THREAD_EXCP_ALL;
49ed89e939SAlvin Chang 
50ed89e939SAlvin Chang 	return xie ^ THREAD_EXCP_ALL;
51fdb66914SMarouene Boubakri }
52fdb66914SMarouene Boubakri 
thread_set_exceptions(uint32_t exceptions)53fdb66914SMarouene Boubakri void __nostackcheck thread_set_exceptions(uint32_t exceptions)
54fdb66914SMarouene Boubakri {
55fdb66914SMarouene Boubakri 	/* Foreign interrupts must not be unmasked while holding a spinlock */
56fdb66914SMarouene Boubakri 	if (!(exceptions & THREAD_EXCP_FOREIGN_INTR))
57fdb66914SMarouene Boubakri 		assert_have_no_spinlock();
58fdb66914SMarouene Boubakri 
59ed89e939SAlvin Chang 	/*
60ed89e939SAlvin Chang 	 * In ARM, the bits in DAIF register are used to mask the exceptions.
61ed89e939SAlvin Chang 	 * While in RISC-V, the bits in CSR XIE are used to enable(unmask)
62ed89e939SAlvin Chang 	 * corresponding interrupt sources. To not modify the function of
63ed89e939SAlvin Chang 	 * thread_set_exceptions(), we should "invert" the bits in "exceptions".
64ed89e939SAlvin Chang 	 * The corresponding bits in "exceptions" will be inverted so they will
65ed89e939SAlvin Chang 	 * be cleared when we write the final value into CSR XIE. So that we
66ed89e939SAlvin Chang 	 * can mask those exceptions.
67ed89e939SAlvin Chang 	 */
68ed89e939SAlvin Chang 	exceptions &= THREAD_EXCP_ALL;
69ed89e939SAlvin Chang 	exceptions ^= THREAD_EXCP_ALL;
70ed89e939SAlvin Chang 
71fdb66914SMarouene Boubakri 	barrier();
72fdb66914SMarouene Boubakri 	write_csr(CSR_XIE, exceptions);
73fdb66914SMarouene Boubakri 	barrier();
74fdb66914SMarouene Boubakri }
75fdb66914SMarouene Boubakri 
thread_mask_exceptions(uint32_t exceptions)76fdb66914SMarouene Boubakri uint32_t __nostackcheck thread_mask_exceptions(uint32_t exceptions)
77fdb66914SMarouene Boubakri {
78fdb66914SMarouene Boubakri 	uint32_t state = thread_get_exceptions();
79fdb66914SMarouene Boubakri 
80fdb66914SMarouene Boubakri 	thread_set_exceptions(state | (exceptions & THREAD_EXCP_ALL));
81fdb66914SMarouene Boubakri 	return state;
82fdb66914SMarouene Boubakri }
83fdb66914SMarouene Boubakri 
thread_unmask_exceptions(uint32_t state)84fdb66914SMarouene Boubakri void __nostackcheck thread_unmask_exceptions(uint32_t state)
85fdb66914SMarouene Boubakri {
86fdb66914SMarouene Boubakri 	thread_set_exceptions(state & THREAD_EXCP_ALL);
87fdb66914SMarouene Boubakri }
88fdb66914SMarouene Boubakri 
thread_lazy_save_ns_vfp(void)89fdb66914SMarouene Boubakri static void thread_lazy_save_ns_vfp(void)
90fdb66914SMarouene Boubakri {
91fdb66914SMarouene Boubakri 	static_assert(!IS_ENABLED(CFG_WITH_VFP));
92fdb66914SMarouene Boubakri }
93fdb66914SMarouene Boubakri 
thread_lazy_restore_ns_vfp(void)94fdb66914SMarouene Boubakri static void thread_lazy_restore_ns_vfp(void)
95fdb66914SMarouene Boubakri {
96fdb66914SMarouene Boubakri 	static_assert(!IS_ENABLED(CFG_WITH_VFP));
97fdb66914SMarouene Boubakri }
98fdb66914SMarouene Boubakri 
setup_unwind_user_mode(struct thread_scall_regs * regs)99fdb66914SMarouene Boubakri static void setup_unwind_user_mode(struct thread_scall_regs *regs)
100fdb66914SMarouene Boubakri {
1015c718542SAlvin Chang 	regs->epc = (uintptr_t)thread_unwind_user_mode;
102b5bb30b3SAlvin Chang 	regs->status = xstatus_for_xret(true, PRV_S);
1035c718542SAlvin Chang 	regs->ie = 0;
104cd7384a0SAlvin Chang 	/*
105cd7384a0SAlvin Chang 	 * We are going to exit user mode. The stack pointer must be set as the
106cd7384a0SAlvin Chang 	 * original value it had before allocating space of scall "regs" and
107cd7384a0SAlvin Chang 	 * calling thread_scall_handler(). Thus, we can simply set stack pointer
108cd7384a0SAlvin Chang 	 * as (regs + 1) value.
109cd7384a0SAlvin Chang 	 */
110cd7384a0SAlvin Chang 	regs->sp = (uintptr_t)(regs + 1);
111fdb66914SMarouene Boubakri }
112fdb66914SMarouene Boubakri 
thread_unhandled_trap(struct thread_ctx_regs * regs __unused,unsigned long cause __unused)113ef00a923SAlvin Chang static void thread_unhandled_trap(struct thread_ctx_regs *regs __unused,
114ef00a923SAlvin Chang 				  unsigned long cause __unused)
115fdb66914SMarouene Boubakri {
116fdb66914SMarouene Boubakri 	DMSG("Unhandled trap xepc:0x%016lx xcause:0x%016lx xtval:0x%016lx",
117fdb66914SMarouene Boubakri 	     read_csr(CSR_XEPC), read_csr(CSR_XCAUSE), read_csr(CSR_XTVAL));
118fdb66914SMarouene Boubakri 	panic();
119fdb66914SMarouene Boubakri }
120fdb66914SMarouene Boubakri 
thread_scall_handler(struct thread_scall_regs * regs)121fdb66914SMarouene Boubakri void thread_scall_handler(struct thread_scall_regs *regs)
122fdb66914SMarouene Boubakri {
123fdb66914SMarouene Boubakri 	struct ts_session *sess = NULL;
124fdb66914SMarouene Boubakri 	uint32_t state = 0;
125fdb66914SMarouene Boubakri 
126fdb66914SMarouene Boubakri 	/* Enable native interrupts */
127fdb66914SMarouene Boubakri 	state = thread_get_exceptions();
128fdb66914SMarouene Boubakri 	thread_unmask_exceptions(state & ~THREAD_EXCP_NATIVE_INTR);
129fdb66914SMarouene Boubakri 
130fdb66914SMarouene Boubakri 	thread_user_save_vfp();
131fdb66914SMarouene Boubakri 
132fdb66914SMarouene Boubakri 	sess = ts_get_current_session();
133fdb66914SMarouene Boubakri 
134fdb66914SMarouene Boubakri 	/* Restore foreign interrupts which are disabled on exception entry */
135fdb66914SMarouene Boubakri 	thread_restore_foreign_intr();
136fdb66914SMarouene Boubakri 
137fdb66914SMarouene Boubakri 	assert(sess && sess->handle_scall);
138fdb66914SMarouene Boubakri 
1395c718542SAlvin Chang 	if (sess->handle_scall(regs)) {
1405c718542SAlvin Chang 		/*
1415c718542SAlvin Chang 		 * We're about to switch back to next instruction of ecall in
1425c718542SAlvin Chang 		 * user-mode
1435c718542SAlvin Chang 		 */
1445c718542SAlvin Chang 		regs->epc += 4;
1455c718542SAlvin Chang 	} else {
1465c718542SAlvin Chang 		/* We're returning from __thread_enter_user_mode() */
147fdb66914SMarouene Boubakri 		setup_unwind_user_mode(regs);
148fdb66914SMarouene Boubakri 	}
149fdb66914SMarouene Boubakri }
150fdb66914SMarouene Boubakri 
thread_irq_handler(void)151fdb66914SMarouene Boubakri static void thread_irq_handler(void)
152fdb66914SMarouene Boubakri {
153e1aad7e9SEtienne Carriere 	interrupt_main_handler();
154fdb66914SMarouene Boubakri }
155fdb66914SMarouene Boubakri 
thread_native_interrupt_handler(struct thread_ctx_regs * regs,unsigned long cause)156ef00a923SAlvin Chang void thread_native_interrupt_handler(struct thread_ctx_regs *regs,
157ef00a923SAlvin Chang 				     unsigned long cause)
158fdb66914SMarouene Boubakri {
159fdb66914SMarouene Boubakri 	switch (cause & LONG_MAX) {
160fdb66914SMarouene Boubakri 	case IRQ_XTIMER:
161fdb66914SMarouene Boubakri 		clear_csr(CSR_XIE, CSR_XIE_TIE);
162fdb66914SMarouene Boubakri 		break;
163fdb66914SMarouene Boubakri 	case IRQ_XSOFT:
164ef00a923SAlvin Chang 		thread_unhandled_trap(regs, cause);
165fdb66914SMarouene Boubakri 		break;
166fdb66914SMarouene Boubakri 	case IRQ_XEXT:
167fdb66914SMarouene Boubakri 		thread_irq_handler();
168fdb66914SMarouene Boubakri 		break;
169fdb66914SMarouene Boubakri 	default:
170ef00a923SAlvin Chang 		thread_unhandled_trap(regs, cause);
171fdb66914SMarouene Boubakri 	}
172fdb66914SMarouene Boubakri }
173fdb66914SMarouene Boubakri 
xstatus_for_xret(uint8_t pie,uint8_t pp)17409653bcaSAlvin Chang unsigned long xstatus_for_xret(uint8_t pie, uint8_t pp)
17509653bcaSAlvin Chang {
17609653bcaSAlvin Chang 	unsigned long xstatus = read_csr(CSR_XSTATUS);
17709653bcaSAlvin Chang 
17809653bcaSAlvin Chang 	assert(pp == PRV_M || pp == PRV_S || pp == PRV_U);
17909653bcaSAlvin Chang 
18009653bcaSAlvin Chang #ifdef RV32
18109653bcaSAlvin Chang 	xstatus = set_field_u32(xstatus, CSR_XSTATUS_IE, 0);
18209653bcaSAlvin Chang 	xstatus = set_field_u32(xstatus, CSR_XSTATUS_PIE, pie);
18309653bcaSAlvin Chang 	xstatus = set_field_u32(xstatus, CSR_XSTATUS_SPP, pp);
18409653bcaSAlvin Chang #else	/* RV64 */
18509653bcaSAlvin Chang 	xstatus = set_field_u64(xstatus, CSR_XSTATUS_IE, 0);
18609653bcaSAlvin Chang 	xstatus = set_field_u64(xstatus, CSR_XSTATUS_PIE, pie);
18709653bcaSAlvin Chang 	xstatus = set_field_u64(xstatus, CSR_XSTATUS_SPP, pp);
18809653bcaSAlvin Chang #endif
18909653bcaSAlvin Chang 
19009653bcaSAlvin Chang 	return xstatus;
19109653bcaSAlvin Chang }
19209653bcaSAlvin Chang 
init_regs(struct thread_ctx * thread,uint32_t a0,uint32_t a1,uint32_t a2,uint32_t a3,uint32_t a4,uint32_t a5,uint32_t a6,uint32_t a7,void * pc)193fdb66914SMarouene Boubakri static void init_regs(struct thread_ctx *thread, uint32_t a0, uint32_t a1,
194fdb66914SMarouene Boubakri 		      uint32_t a2, uint32_t a3, uint32_t a4, uint32_t a5,
195fdb66914SMarouene Boubakri 		      uint32_t a6, uint32_t a7, void *pc)
196fdb66914SMarouene Boubakri {
19709653bcaSAlvin Chang 	memset(&thread->regs, 0, sizeof(thread->regs));
19809653bcaSAlvin Chang 
19909653bcaSAlvin Chang 	thread->regs.epc = (uintptr_t)pc;
200fdb66914SMarouene Boubakri 
201fdb66914SMarouene Boubakri 	/* Set up xstatus */
20209653bcaSAlvin Chang 	thread->regs.status = xstatus_for_xret(true, PRV_S);
20309653bcaSAlvin Chang 
20409653bcaSAlvin Chang 	/* Enable native interrupt */
20509653bcaSAlvin Chang 	thread->regs.ie = THREAD_EXCP_NATIVE_INTR;
206fdb66914SMarouene Boubakri 
207fdb66914SMarouene Boubakri 	/* Reinitialize stack pointer */
208fdb66914SMarouene Boubakri 	thread->regs.sp = thread->stack_va_end;
209fdb66914SMarouene Boubakri 
21009653bcaSAlvin Chang 	/* Set up GP and TP */
21109653bcaSAlvin Chang 	thread->regs.gp = read_gp();
21209653bcaSAlvin Chang 	thread->regs.tp = read_tp();
21309653bcaSAlvin Chang 
214fdb66914SMarouene Boubakri 	/*
215fdb66914SMarouene Boubakri 	 * Copy arguments into context. This will make the
216fdb66914SMarouene Boubakri 	 * arguments appear in a0-a7 when thread is started.
217fdb66914SMarouene Boubakri 	 */
218fdb66914SMarouene Boubakri 	thread->regs.a0 = a0;
219fdb66914SMarouene Boubakri 	thread->regs.a1 = a1;
220fdb66914SMarouene Boubakri 	thread->regs.a2 = a2;
221fdb66914SMarouene Boubakri 	thread->regs.a3 = a3;
222fdb66914SMarouene Boubakri 	thread->regs.a4 = a4;
223fdb66914SMarouene Boubakri 	thread->regs.a5 = a5;
224fdb66914SMarouene Boubakri 	thread->regs.a6 = a6;
225fdb66914SMarouene Boubakri 	thread->regs.a7 = a7;
226fdb66914SMarouene Boubakri }
227fdb66914SMarouene Boubakri 
__thread_alloc_and_run(uint32_t a0,uint32_t a1,uint32_t a2,uint32_t a3,uint32_t a4,uint32_t a5,uint32_t a6,uint32_t a7,void * pc)228fdb66914SMarouene Boubakri static void __thread_alloc_and_run(uint32_t a0, uint32_t a1, uint32_t a2,
229fdb66914SMarouene Boubakri 				   uint32_t a3, uint32_t a4, uint32_t a5,
230fdb66914SMarouene Boubakri 				   uint32_t a6, uint32_t a7,
231fdb66914SMarouene Boubakri 				   void *pc)
232fdb66914SMarouene Boubakri {
233fdb66914SMarouene Boubakri 	struct thread_core_local *l = thread_get_core_local();
234fdb66914SMarouene Boubakri 	bool found_thread = false;
235fdb66914SMarouene Boubakri 	size_t n = 0;
236fdb66914SMarouene Boubakri 
237fdb66914SMarouene Boubakri 	assert(l->curr_thread == THREAD_ID_INVALID);
238fdb66914SMarouene Boubakri 
239fdb66914SMarouene Boubakri 	thread_lock_global();
240fdb66914SMarouene Boubakri 
241fdb66914SMarouene Boubakri 	for (n = 0; n < CFG_NUM_THREADS; n++) {
242fdb66914SMarouene Boubakri 		if (threads[n].state == THREAD_STATE_FREE) {
243fdb66914SMarouene Boubakri 			threads[n].state = THREAD_STATE_ACTIVE;
244fdb66914SMarouene Boubakri 			found_thread = true;
245fdb66914SMarouene Boubakri 			break;
246fdb66914SMarouene Boubakri 		}
247fdb66914SMarouene Boubakri 	}
248fdb66914SMarouene Boubakri 
249fdb66914SMarouene Boubakri 	thread_unlock_global();
250fdb66914SMarouene Boubakri 
251fdb66914SMarouene Boubakri 	if (!found_thread)
252fdb66914SMarouene Boubakri 		return;
253fdb66914SMarouene Boubakri 
254fdb66914SMarouene Boubakri 	l->curr_thread = n;
255fdb66914SMarouene Boubakri 
256fdb66914SMarouene Boubakri 	threads[n].flags = 0;
257fdb66914SMarouene Boubakri 	init_regs(threads + n, a0, a1, a2, a3, a4, a5, a6, a7, pc);
258fdb66914SMarouene Boubakri 
259fdb66914SMarouene Boubakri 	thread_lazy_save_ns_vfp();
260fdb66914SMarouene Boubakri 
261fdb66914SMarouene Boubakri 	l->flags &= ~THREAD_CLF_TMP;
262fdb66914SMarouene Boubakri 
263fdb66914SMarouene Boubakri 	thread_resume(&threads[n].regs);
264fdb66914SMarouene Boubakri 	/*NOTREACHED*/
265fdb66914SMarouene Boubakri 	panic();
266fdb66914SMarouene Boubakri }
267fdb66914SMarouene Boubakri 
thread_alloc_and_run(uint32_t a0,uint32_t a1,uint32_t a2,uint32_t a3,uint32_t a4,uint32_t a5)268fdb66914SMarouene Boubakri void thread_alloc_and_run(uint32_t a0, uint32_t a1, uint32_t a2, uint32_t a3,
269fdb66914SMarouene Boubakri 			  uint32_t a4, uint32_t a5)
270fdb66914SMarouene Boubakri {
271fdb66914SMarouene Boubakri 	__thread_alloc_and_run(a0, a1, a2, a3, a4, a5, 0, 0,
2723f1a58ffSMarouene Boubakri 			       thread_std_abi_entry);
273fdb66914SMarouene Boubakri }
274fdb66914SMarouene Boubakri 
copy_a0_to_a3(struct thread_ctx_regs * regs,uint32_t a0,uint32_t a1,uint32_t a2,uint32_t a3)275fdb66914SMarouene Boubakri static void copy_a0_to_a3(struct thread_ctx_regs *regs, uint32_t a0,
276fdb66914SMarouene Boubakri 			  uint32_t a1, uint32_t a2, uint32_t a3)
277fdb66914SMarouene Boubakri {
278fdb66914SMarouene Boubakri 	regs->a0 = a0;
279fdb66914SMarouene Boubakri 	regs->a1 = a1;
280fdb66914SMarouene Boubakri 	regs->a2 = a2;
281fdb66914SMarouene Boubakri 	regs->a3 = a3;
282fdb66914SMarouene Boubakri }
283fdb66914SMarouene Boubakri 
is_from_user(unsigned long status)284fdb66914SMarouene Boubakri static bool is_from_user(unsigned long status)
285fdb66914SMarouene Boubakri {
28669a443d0SAlvin Chang 	return (status & CSR_XSTATUS_SPP) == 0;
287fdb66914SMarouene Boubakri }
288fdb66914SMarouene Boubakri 
289fdb66914SMarouene Boubakri #ifdef CFG_SYSCALL_FTRACE
ftrace_suspend(void)290fdb66914SMarouene Boubakri static void __noprof ftrace_suspend(void)
291fdb66914SMarouene Boubakri {
292fdb66914SMarouene Boubakri 	struct ts_session *s = TAILQ_FIRST(&thread_get_tsd()->sess_stack);
293fdb66914SMarouene Boubakri 
294fdb66914SMarouene Boubakri 	if (s && s->fbuf)
295fdb66914SMarouene Boubakri 		s->fbuf->syscall_trace_suspended = true;
296fdb66914SMarouene Boubakri }
297fdb66914SMarouene Boubakri 
ftrace_resume(void)298fdb66914SMarouene Boubakri static void __noprof ftrace_resume(void)
299fdb66914SMarouene Boubakri {
300fdb66914SMarouene Boubakri 	struct ts_session *s = TAILQ_FIRST(&thread_get_tsd()->sess_stack);
301fdb66914SMarouene Boubakri 
302fdb66914SMarouene Boubakri 	if (s && s->fbuf)
303fdb66914SMarouene Boubakri 		s->fbuf->syscall_trace_suspended = false;
304fdb66914SMarouene Boubakri }
305fdb66914SMarouene Boubakri #else
ftrace_suspend(void)306fdb66914SMarouene Boubakri static void __maybe_unused __noprof ftrace_suspend(void)
307fdb66914SMarouene Boubakri {
308fdb66914SMarouene Boubakri }
309fdb66914SMarouene Boubakri 
ftrace_resume(void)310fdb66914SMarouene Boubakri static void __noprof ftrace_resume(void)
311fdb66914SMarouene Boubakri {
312fdb66914SMarouene Boubakri }
313fdb66914SMarouene Boubakri #endif
314fdb66914SMarouene Boubakri 
is_user_mode(struct thread_ctx_regs * regs)315fdb66914SMarouene Boubakri static bool is_user_mode(struct thread_ctx_regs *regs)
316fdb66914SMarouene Boubakri {
317fdb66914SMarouene Boubakri 	return is_from_user((uint32_t)regs->status);
318fdb66914SMarouene Boubakri }
319fdb66914SMarouene Boubakri 
thread_get_saved_thread_sp(void)320fdb66914SMarouene Boubakri vaddr_t thread_get_saved_thread_sp(void)
321fdb66914SMarouene Boubakri {
322fdb66914SMarouene Boubakri 	struct thread_core_local *l = thread_get_core_local();
323fdb66914SMarouene Boubakri 	int ct = l->curr_thread;
324fdb66914SMarouene Boubakri 
325fdb66914SMarouene Boubakri 	assert(ct != THREAD_ID_INVALID);
326fdb66914SMarouene Boubakri 	return threads[ct].kern_sp;
327fdb66914SMarouene Boubakri }
328fdb66914SMarouene Boubakri 
thread_get_hartid_by_hartindex(uint32_t hartidx)329*2e27ec6cSYu-Chien Peter Lin uint32_t thread_get_hartid_by_hartindex(uint32_t hartidx)
330*2e27ec6cSYu-Chien Peter Lin {
331*2e27ec6cSYu-Chien Peter Lin 	assert(hartidx < CFG_TEE_CORE_NB_CORE);
332*2e27ec6cSYu-Chien Peter Lin 
333*2e27ec6cSYu-Chien Peter Lin 	return thread_core_local[hartidx].hart_id;
334*2e27ec6cSYu-Chien Peter Lin }
335*2e27ec6cSYu-Chien Peter Lin 
thread_resume_from_rpc(uint32_t thread_id,uint32_t a0,uint32_t a1,uint32_t a2,uint32_t a3)336fdb66914SMarouene Boubakri void thread_resume_from_rpc(uint32_t thread_id, uint32_t a0, uint32_t a1,
337fdb66914SMarouene Boubakri 			    uint32_t a2, uint32_t a3)
338fdb66914SMarouene Boubakri {
339fdb66914SMarouene Boubakri 	size_t n = thread_id;
340fdb66914SMarouene Boubakri 	struct thread_core_local *l = thread_get_core_local();
341fdb66914SMarouene Boubakri 	bool found_thread = false;
342fdb66914SMarouene Boubakri 
343fdb66914SMarouene Boubakri 	assert(l->curr_thread == THREAD_ID_INVALID);
344fdb66914SMarouene Boubakri 
345fdb66914SMarouene Boubakri 	thread_lock_global();
346fdb66914SMarouene Boubakri 
347fdb66914SMarouene Boubakri 	if (n < CFG_NUM_THREADS && threads[n].state == THREAD_STATE_SUSPENDED) {
348fdb66914SMarouene Boubakri 		threads[n].state = THREAD_STATE_ACTIVE;
349fdb66914SMarouene Boubakri 		found_thread = true;
350fdb66914SMarouene Boubakri 	}
351fdb66914SMarouene Boubakri 
352fdb66914SMarouene Boubakri 	thread_unlock_global();
353fdb66914SMarouene Boubakri 
354fdb66914SMarouene Boubakri 	if (!found_thread)
355fdb66914SMarouene Boubakri 		return;
356fdb66914SMarouene Boubakri 
357fdb66914SMarouene Boubakri 	l->curr_thread = n;
358fdb66914SMarouene Boubakri 
359fdb66914SMarouene Boubakri 	if (threads[n].have_user_map) {
360fdb66914SMarouene Boubakri 		core_mmu_set_user_map(&threads[n].user_map);
361fdb66914SMarouene Boubakri 		if (threads[n].flags & THREAD_FLAGS_EXIT_ON_FOREIGN_INTR)
362fdb66914SMarouene Boubakri 			tee_ta_ftrace_update_times_resume();
363fdb66914SMarouene Boubakri 	}
364fdb66914SMarouene Boubakri 
365fdb66914SMarouene Boubakri 	if (is_user_mode(&threads[n].regs))
366fdb66914SMarouene Boubakri 		tee_ta_update_session_utime_resume();
367fdb66914SMarouene Boubakri 
368fdb66914SMarouene Boubakri 	/*
36909653bcaSAlvin Chang 	 * We may resume thread at another hart, so we need to re-assign value
37009653bcaSAlvin Chang 	 * of tp to be current hart's thread_core_local.
37109653bcaSAlvin Chang 	 */
37209653bcaSAlvin Chang 	if (!is_user_mode(&threads[n].regs))
37309653bcaSAlvin Chang 		threads[n].regs.tp = read_tp();
37409653bcaSAlvin Chang 
37509653bcaSAlvin Chang 	/*
376fdb66914SMarouene Boubakri 	 * Return from RPC to request service of a foreign interrupt must not
377fdb66914SMarouene Boubakri 	 * get parameters from non-secure world.
378fdb66914SMarouene Boubakri 	 */
379fdb66914SMarouene Boubakri 	if (threads[n].flags & THREAD_FLAGS_COPY_ARGS_ON_RETURN) {
380fdb66914SMarouene Boubakri 		copy_a0_to_a3(&threads[n].regs, a0, a1, a2, a3);
381fdb66914SMarouene Boubakri 		threads[n].flags &= ~THREAD_FLAGS_COPY_ARGS_ON_RETURN;
382fdb66914SMarouene Boubakri 	}
383fdb66914SMarouene Boubakri 
384fdb66914SMarouene Boubakri 	thread_lazy_save_ns_vfp();
385fdb66914SMarouene Boubakri 
386fdb66914SMarouene Boubakri 	if (threads[n].have_user_map)
387fdb66914SMarouene Boubakri 		ftrace_resume();
388fdb66914SMarouene Boubakri 
389fdb66914SMarouene Boubakri 	l->flags &= ~THREAD_CLF_TMP;
390fdb66914SMarouene Boubakri 	thread_resume(&threads[n].regs);
391fdb66914SMarouene Boubakri 	/*NOTREACHED*/
392fdb66914SMarouene Boubakri 	panic();
393fdb66914SMarouene Boubakri }
394fdb66914SMarouene Boubakri 
thread_state_free(void)395fdb66914SMarouene Boubakri void thread_state_free(void)
396fdb66914SMarouene Boubakri {
397fdb66914SMarouene Boubakri 	struct thread_core_local *l = thread_get_core_local();
398fdb66914SMarouene Boubakri 	int ct = l->curr_thread;
399fdb66914SMarouene Boubakri 
400fdb66914SMarouene Boubakri 	assert(ct != THREAD_ID_INVALID);
401fdb66914SMarouene Boubakri 
402fdb66914SMarouene Boubakri 	thread_lazy_restore_ns_vfp();
403fdb66914SMarouene Boubakri 
404fdb66914SMarouene Boubakri 	thread_lock_global();
405fdb66914SMarouene Boubakri 
406fdb66914SMarouene Boubakri 	assert(threads[ct].state == THREAD_STATE_ACTIVE);
407fdb66914SMarouene Boubakri 	threads[ct].state = THREAD_STATE_FREE;
408fdb66914SMarouene Boubakri 	threads[ct].flags = 0;
409fdb66914SMarouene Boubakri 	l->curr_thread = THREAD_ID_INVALID;
410fdb66914SMarouene Boubakri 
411b76b2296SJerome Forissier 	if (IS_ENABLED(CFG_NS_VIRTUALIZATION))
412fdb66914SMarouene Boubakri 		virt_unset_guest();
413fdb66914SMarouene Boubakri 	thread_unlock_global();
414fdb66914SMarouene Boubakri }
415fdb66914SMarouene Boubakri 
thread_state_suspend(uint32_t flags,unsigned long status,vaddr_t pc)4160aa98cd2SAlvin Chang int thread_state_suspend(uint32_t flags, unsigned long status, vaddr_t pc)
417fdb66914SMarouene Boubakri {
418fdb66914SMarouene Boubakri 	struct thread_core_local *l = thread_get_core_local();
419fdb66914SMarouene Boubakri 	int ct = l->curr_thread;
420fdb66914SMarouene Boubakri 
421fdb66914SMarouene Boubakri 	assert(ct != THREAD_ID_INVALID);
422fdb66914SMarouene Boubakri 
423fdb66914SMarouene Boubakri 	if (core_mmu_user_mapping_is_active())
424fdb66914SMarouene Boubakri 		ftrace_suspend();
425fdb66914SMarouene Boubakri 
426fdb66914SMarouene Boubakri 	thread_check_canaries();
427fdb66914SMarouene Boubakri 
428fdb66914SMarouene Boubakri 	if (is_from_user(status)) {
429fdb66914SMarouene Boubakri 		thread_user_save_vfp();
430fdb66914SMarouene Boubakri 		tee_ta_update_session_utime_suspend();
431fdb66914SMarouene Boubakri 		tee_ta_gprof_sample_pc(pc);
432fdb66914SMarouene Boubakri 	}
433fdb66914SMarouene Boubakri 	thread_lazy_restore_ns_vfp();
434fdb66914SMarouene Boubakri 
435fdb66914SMarouene Boubakri 	thread_lock_global();
436fdb66914SMarouene Boubakri 
437fdb66914SMarouene Boubakri 	assert(threads[ct].state == THREAD_STATE_ACTIVE);
438fdb66914SMarouene Boubakri 	threads[ct].flags |= flags;
439fdb66914SMarouene Boubakri 	threads[ct].regs.status = status;
44009653bcaSAlvin Chang 	threads[ct].regs.epc = pc;
441fdb66914SMarouene Boubakri 	threads[ct].state = THREAD_STATE_SUSPENDED;
442fdb66914SMarouene Boubakri 
443fdb66914SMarouene Boubakri 	threads[ct].have_user_map = core_mmu_user_mapping_is_active();
444fdb66914SMarouene Boubakri 	if (threads[ct].have_user_map) {
445fdb66914SMarouene Boubakri 		if (threads[ct].flags & THREAD_FLAGS_EXIT_ON_FOREIGN_INTR)
446fdb66914SMarouene Boubakri 			tee_ta_ftrace_update_times_suspend();
447fdb66914SMarouene Boubakri 		core_mmu_get_user_map(&threads[ct].user_map);
448fdb66914SMarouene Boubakri 		core_mmu_set_user_map(NULL);
449fdb66914SMarouene Boubakri 	}
450fdb66914SMarouene Boubakri 
451fdb66914SMarouene Boubakri 	l->curr_thread = THREAD_ID_INVALID;
452fdb66914SMarouene Boubakri 
453b76b2296SJerome Forissier 	if (IS_ENABLED(CFG_NS_VIRTUALIZATION))
454fdb66914SMarouene Boubakri 		virt_unset_guest();
455fdb66914SMarouene Boubakri 
456fdb66914SMarouene Boubakri 	thread_unlock_global();
457fdb66914SMarouene Boubakri 
458fdb66914SMarouene Boubakri 	return ct;
459fdb66914SMarouene Boubakri }
460fdb66914SMarouene Boubakri 
init_user_kcode(void)461fdb66914SMarouene Boubakri static void init_user_kcode(void)
462fdb66914SMarouene Boubakri {
463fdb66914SMarouene Boubakri }
464fdb66914SMarouene Boubakri 
thread_init_primary(void)465fdb66914SMarouene Boubakri void thread_init_primary(void)
466fdb66914SMarouene Boubakri {
467fdb66914SMarouene Boubakri 	init_user_kcode();
468fdb66914SMarouene Boubakri }
469fdb66914SMarouene Boubakri 
get_trap_vect(void)470fdb66914SMarouene Boubakri static vaddr_t get_trap_vect(void)
471fdb66914SMarouene Boubakri {
472fdb66914SMarouene Boubakri 	return (vaddr_t)thread_trap_vect;
473fdb66914SMarouene Boubakri }
474fdb66914SMarouene Boubakri 
thread_init_tvec(void)475fdb66914SMarouene Boubakri void thread_init_tvec(void)
476fdb66914SMarouene Boubakri {
477fdb66914SMarouene Boubakri 	unsigned long tvec = (unsigned long)get_trap_vect();
478fdb66914SMarouene Boubakri 
479fdb66914SMarouene Boubakri 	write_csr(CSR_XTVEC, tvec);
480fdb66914SMarouene Boubakri 	assert(read_csr(CSR_XTVEC) == tvec);
481fdb66914SMarouene Boubakri }
482fdb66914SMarouene Boubakri 
thread_init_per_cpu(void)483fdb66914SMarouene Boubakri void thread_init_per_cpu(void)
484fdb66914SMarouene Boubakri {
485fdb66914SMarouene Boubakri 	thread_init_tvec();
486fdb66914SMarouene Boubakri 	/*
487fdb66914SMarouene Boubakri 	 * We may receive traps from now, therefore, zeroize xSCRATCH such
488fdb66914SMarouene Boubakri 	 * that thread_trap_vect() can distinguish between user traps
489fdb66914SMarouene Boubakri 	 * and kernel traps.
490fdb66914SMarouene Boubakri 	 */
491fdb66914SMarouene Boubakri 	write_csr(CSR_XSCRATCH, 0);
4923db1b3e3SAlvin Chang #ifndef CFG_PAN
4933db1b3e3SAlvin Chang 	/*
4943db1b3e3SAlvin Chang 	 * Allow access to user pages. When CFG_PAN is enabled, the SUM bit will
4953db1b3e3SAlvin Chang 	 * be set and clear at runtime when necessary.
4963db1b3e3SAlvin Chang 	 */
497fdb66914SMarouene Boubakri 	set_csr(CSR_XSTATUS, CSR_XSTATUS_SUM);
4983db1b3e3SAlvin Chang #endif
499fdb66914SMarouene Boubakri }
500fdb66914SMarouene Boubakri 
set_ctx_regs(struct thread_ctx_regs * regs,unsigned long a0,unsigned long a1,unsigned long a2,unsigned long a3,unsigned long user_sp,unsigned long entry_func,unsigned long status,unsigned long ie,struct thread_pauth_keys * keys __unused)501fdb66914SMarouene Boubakri static void set_ctx_regs(struct thread_ctx_regs *regs, unsigned long a0,
502fdb66914SMarouene Boubakri 			 unsigned long a1, unsigned long a2, unsigned long a3,
503fdb66914SMarouene Boubakri 			 unsigned long user_sp, unsigned long entry_func,
504b5bb30b3SAlvin Chang 			 unsigned long status, unsigned long ie,
505fdb66914SMarouene Boubakri 			 struct thread_pauth_keys *keys __unused)
506fdb66914SMarouene Boubakri {
507fdb66914SMarouene Boubakri 	*regs = (struct thread_ctx_regs){
508fdb66914SMarouene Boubakri 		.a0 = a0,
509fdb66914SMarouene Boubakri 		.a1 = a1,
510fdb66914SMarouene Boubakri 		.a2 = a2,
511fdb66914SMarouene Boubakri 		.a3 = a3,
512b5bb30b3SAlvin Chang 		.s0 = 0,
513fdb66914SMarouene Boubakri 		.sp = user_sp,
514dfa05b24SAlvin Chang 		.epc = entry_func,
515b5bb30b3SAlvin Chang 		.status = status,
516b5bb30b3SAlvin Chang 		.ie = ie,
517fdb66914SMarouene Boubakri 	};
518fdb66914SMarouene Boubakri }
519fdb66914SMarouene Boubakri 
thread_enter_user_mode(unsigned long a0,unsigned long a1,unsigned long a2,unsigned long a3,unsigned long user_sp,unsigned long entry_func,bool is_32bit __unused,uint32_t * exit_status0,uint32_t * exit_status1)520fdb66914SMarouene Boubakri uint32_t thread_enter_user_mode(unsigned long a0, unsigned long a1,
521fdb66914SMarouene Boubakri 				unsigned long a2, unsigned long a3,
522fdb66914SMarouene Boubakri 				unsigned long user_sp,
523fdb66914SMarouene Boubakri 				unsigned long entry_func,
524fdb66914SMarouene Boubakri 				bool is_32bit __unused,
525fdb66914SMarouene Boubakri 				uint32_t *exit_status0,
526fdb66914SMarouene Boubakri 				uint32_t *exit_status1)
527fdb66914SMarouene Boubakri {
5280aa98cd2SAlvin Chang 	unsigned long status = 0;
529b5bb30b3SAlvin Chang 	unsigned long ie = 0;
530fdb66914SMarouene Boubakri 	uint32_t exceptions = 0;
531fdb66914SMarouene Boubakri 	uint32_t rc = 0;
532fdb66914SMarouene Boubakri 	struct thread_ctx_regs *regs = NULL;
533fdb66914SMarouene Boubakri 
534fdb66914SMarouene Boubakri 	tee_ta_update_session_utime_resume();
535fdb66914SMarouene Boubakri 
536b5bb30b3SAlvin Chang 	/* Read current interrupt masks */
537b5bb30b3SAlvin Chang 	ie = read_csr(CSR_XIE);
538b5bb30b3SAlvin Chang 
539b5bb30b3SAlvin Chang 	/*
540b5bb30b3SAlvin Chang 	 * Mask all exceptions, the CSR_XSTATUS.IE will be set from
541b5bb30b3SAlvin Chang 	 * setup_unwind_user_mode() after exiting.
542b5bb30b3SAlvin Chang 	 */
543fdb66914SMarouene Boubakri 	exceptions = thread_mask_exceptions(THREAD_EXCP_ALL);
544fdb66914SMarouene Boubakri 	regs = thread_get_ctx_regs();
545b5bb30b3SAlvin Chang 	status = xstatus_for_xret(true, PRV_U);
546b5bb30b3SAlvin Chang 	set_ctx_regs(regs, a0, a1, a2, a3, user_sp, entry_func, status, ie,
547b5bb30b3SAlvin Chang 		     NULL);
548fdb66914SMarouene Boubakri 	rc = __thread_enter_user_mode(regs, exit_status0, exit_status1);
549fdb66914SMarouene Boubakri 	thread_unmask_exceptions(exceptions);
550fdb66914SMarouene Boubakri 
551fdb66914SMarouene Boubakri 	return rc;
552fdb66914SMarouene Boubakri }
553d1d1ca23SAlvin Chang 
__thread_rpc(uint32_t rv[THREAD_RPC_NUM_ARGS])554d1d1ca23SAlvin Chang void __thread_rpc(uint32_t rv[THREAD_RPC_NUM_ARGS])
555d1d1ca23SAlvin Chang {
556d1d1ca23SAlvin Chang 	thread_rpc_xstatus(rv, xstatus_for_xret(false, PRV_S));
557d1d1ca23SAlvin Chang }
558