xref: /optee_os/core/arch/riscv/kernel/thread_rv.S (revision 91d4649de98c6beeb8217d40f1fafa50720fe785)
19b1a3bbeSMarouene Boubakri/* SPDX-License-Identifier: BSD-2-Clause */
29b1a3bbeSMarouene Boubakri/*
39b1a3bbeSMarouene Boubakri * Copyright 2022-2023 NXP
45e26ef8fSAlvin Chang * Copyright 2024 Andes Technology Corporation
59b1a3bbeSMarouene Boubakri */
69b1a3bbeSMarouene Boubakri
79b1a3bbeSMarouene Boubakri#include <asm.S>
89b1a3bbeSMarouene Boubakri#include <generated/asm-defines.h>
99b1a3bbeSMarouene Boubakri#include <keep.h>
109b1a3bbeSMarouene Boubakri#include <kernel/thread.h>
119b1a3bbeSMarouene Boubakri#include <kernel/thread_private.h>
129b1a3bbeSMarouene Boubakri#include <mm/core_mmu.h>
139b1a3bbeSMarouene Boubakri#include <riscv.h>
149b1a3bbeSMarouene Boubakri#include <riscv_macros.S>
158d5bae1cSAlvin Chang#include <tee/optee_abi.h>
168d5bae1cSAlvin Chang#include <tee/teeabi_opteed.h>
178d5bae1cSAlvin Chang#include <tee/teeabi_opteed_macros.h>
189b1a3bbeSMarouene Boubakri
199b1a3bbeSMarouene Boubakri.macro get_thread_ctx res, tmp0
209b1a3bbeSMarouene Boubakri	lw	\tmp0, THREAD_CORE_LOCAL_CURR_THREAD(tp)
21*91d4649dSJens Wiklander.option push
22*91d4649dSJens Wiklander.option norelax
239b1a3bbeSMarouene Boubakri	la	\res, threads
24*91d4649dSJens Wiklander.option pop
25*91d4649dSJens Wiklander	LDR	\res, 0(\res)
269b1a3bbeSMarouene Boubakri1:
279b1a3bbeSMarouene Boubakri	beqz	\tmp0, 2f
289b1a3bbeSMarouene Boubakri	addi	\res, \res, THREAD_CTX_SIZE
299b1a3bbeSMarouene Boubakri	addi	\tmp0, \tmp0, -1
309b1a3bbeSMarouene Boubakri	bnez	\tmp0, 1b
319b1a3bbeSMarouene Boubakri2:
329b1a3bbeSMarouene Boubakri.endm
339b1a3bbeSMarouene Boubakri
3409653bcaSAlvin Chang.macro b_if_prev_priv_is_u reg, label
3509653bcaSAlvin Chang	andi	\reg, \reg, CSR_XSTATUS_SPP
3609653bcaSAlvin Chang	beqz	\reg, \label
3709653bcaSAlvin Chang.endm
3809653bcaSAlvin Chang
399b1a3bbeSMarouene Boubakri/* size_t __get_core_pos(void); */
409b1a3bbeSMarouene BoubakriFUNC __get_core_pos , : , .identity_map
412e27ec6cSYu-Chien Peter Lin	lw	a0, THREAD_CORE_LOCAL_HART_INDEX(tp)
429b1a3bbeSMarouene Boubakri	ret
439b1a3bbeSMarouene BoubakriEND_FUNC __get_core_pos
449b1a3bbeSMarouene Boubakri
459b1a3bbeSMarouene BoubakriFUNC thread_trap_vect , :
464fe3a3f7SAlvin Chang	csrrw	tp, CSR_XSCRATCH, tp
474fe3a3f7SAlvin Chang	bnez	tp, 0f
484fe3a3f7SAlvin Chang	/* Read tp back */
494fe3a3f7SAlvin Chang	csrrw	tp, CSR_XSCRATCH, tp
509b1a3bbeSMarouene Boubakri	j	trap_from_kernel
519b1a3bbeSMarouene Boubakri0:
524fe3a3f7SAlvin Chang	/* Now tp is thread_core_local */
539b1a3bbeSMarouene Boubakri	j	trap_from_user
549b1a3bbeSMarouene Boubakrithread_trap_vect_end:
559b1a3bbeSMarouene BoubakriEND_FUNC thread_trap_vect
569b1a3bbeSMarouene Boubakri
579b1a3bbeSMarouene BoubakriLOCAL_FUNC trap_from_kernel, :
585e26ef8fSAlvin Chang	/* Save sp, a0, a1 into temporary spaces of thread_core_local */
595e26ef8fSAlvin Chang	store_xregs tp, THREAD_CORE_LOCAL_X0, REG_SP
605e26ef8fSAlvin Chang	store_xregs tp, THREAD_CORE_LOCAL_X1, REG_A0, REG_A1
615e26ef8fSAlvin Chang
625e26ef8fSAlvin Chang	csrr	a0, CSR_XCAUSE
635e26ef8fSAlvin Chang	/* MSB of cause differentiates between interrupts and exceptions */
645e26ef8fSAlvin Chang	bge	a0, zero, exception_from_kernel
655e26ef8fSAlvin Chang
665e26ef8fSAlvin Changinterrupt_from_kernel:
675e26ef8fSAlvin Chang	/* Get thread context as sp */
685e26ef8fSAlvin Chang	get_thread_ctx sp, a0
695e26ef8fSAlvin Chang
705e26ef8fSAlvin Chang	/* Load and save kernel sp */
715e26ef8fSAlvin Chang	load_xregs tp, THREAD_CORE_LOCAL_X0, REG_A0
725e26ef8fSAlvin Chang	store_xregs sp, THREAD_CTX_REG_SP, REG_A0
735e26ef8fSAlvin Chang
745e26ef8fSAlvin Chang	/* Restore user a0, a1 which can be saved later */
755e26ef8fSAlvin Chang	load_xregs tp, THREAD_CORE_LOCAL_X1, REG_A0, REG_A1
765e26ef8fSAlvin Chang
775e26ef8fSAlvin Chang	/* Save all other GPRs */
785e26ef8fSAlvin Chang	store_xregs sp, THREAD_CTX_REG_RA, REG_RA
795e26ef8fSAlvin Chang	store_xregs sp, THREAD_CTX_REG_GP, REG_GP
805e26ef8fSAlvin Chang	store_xregs sp, THREAD_CTX_REG_T0, REG_T0, REG_T2
815e26ef8fSAlvin Chang	store_xregs sp, THREAD_CTX_REG_S0, REG_S0, REG_S1
825e26ef8fSAlvin Chang	store_xregs sp, THREAD_CTX_REG_A0, REG_A0, REG_A7
835e26ef8fSAlvin Chang	store_xregs sp, THREAD_CTX_REG_S2, REG_S2, REG_S11
845e26ef8fSAlvin Chang	store_xregs sp, THREAD_CTX_REG_T3, REG_T3, REG_T6
855e26ef8fSAlvin Chang	/* Save XIE */
865e26ef8fSAlvin Chang	csrr	t0, CSR_XIE
875e26ef8fSAlvin Chang	store_xregs sp, THREAD_CTX_REG_IE, REG_T0
885e26ef8fSAlvin Chang	/* Mask all interrupts */
895e26ef8fSAlvin Chang	csrw	CSR_XIE, x0
905e26ef8fSAlvin Chang	/* Save XSTATUS */
915e26ef8fSAlvin Chang	csrr	t0, CSR_XSTATUS
925e26ef8fSAlvin Chang	store_xregs sp, THREAD_CTX_REG_STATUS, REG_T0
935e26ef8fSAlvin Chang	/* Save XEPC */
945e26ef8fSAlvin Chang	csrr	t0, CSR_XEPC
955e26ef8fSAlvin Chang	store_xregs sp, THREAD_CTX_REG_EPC, REG_T0
965e26ef8fSAlvin Chang
975e26ef8fSAlvin Chang	/*
98ef00a923SAlvin Chang	 * a0 = struct thread_ctx_regs *regs
99ef00a923SAlvin Chang	 * a1 = cause
1005e26ef8fSAlvin Chang	 */
101ef00a923SAlvin Chang	mv	a0, sp
102ef00a923SAlvin Chang	csrr	a1, CSR_XCAUSE
1035e26ef8fSAlvin Chang	/* Load tmp_stack_va_end as current sp. */
1045e26ef8fSAlvin Chang	load_xregs tp, THREAD_CORE_LOCAL_TMP_STACK_VA_END, REG_SP
1055232a348SAlvin Chang
1065232a348SAlvin Chang	/*
1075232a348SAlvin Chang	 * Get interrupt code from XCAUSE and build XIP. For example, if the
1085232a348SAlvin Chang	 * value of XCAUSE is 0x8000000000000005 (supervisor timer interrupt),
1095232a348SAlvin Chang	 * we build 0x20, which is (1 << 5) and indicates the sip.STIP signal.
1105232a348SAlvin Chang	 */
1115232a348SAlvin Chang	li	a2, CSR_XCAUSE_INTR_FLAG
1125232a348SAlvin Chang	sub	a2, a1, a2
1135232a348SAlvin Chang	li	a3, 1
1145232a348SAlvin Chang	sll	a3, a3, a2
1155232a348SAlvin Chang	/*
1165232a348SAlvin Chang	 * Compare built XIP with THREAD_EXCP_FOREIGN_INTR. If XIP is one of
1175232a348SAlvin Chang	 * THREAD_EXCP_FOREIGN_INTR, we call thread_foreign_interrupt_handler().
1185232a348SAlvin Chang	 */
1195232a348SAlvin Chang	li	a2, THREAD_EXCP_FOREIGN_INTR
1205232a348SAlvin Chang	and	a2, a3, a2
1215232a348SAlvin Chang	beqz	a2, native_interrupt_from_kernel
1225232a348SAlvin Chang
1235232a348SAlvin Changforeign_interrupt_from_kernel:
1245232a348SAlvin Chang	/*
1255232a348SAlvin Chang	 * a0 = struct thread_ctx_regs *regs
1265232a348SAlvin Chang	 * Tail call thread_foreign_interrupt_handler(regs) since we will not
1275232a348SAlvin Chang	 * return to here.
1285232a348SAlvin Chang	 */
1295232a348SAlvin Chang	tail	thread_foreign_interrupt_handler
1305232a348SAlvin Chang
1315232a348SAlvin Changnative_interrupt_from_kernel:
132ce1f8a72SAlvin Chang	/* Update 32-bit core local flags */
133ce1f8a72SAlvin Chang	lw	a2, THREAD_CORE_LOCAL_FLAGS(tp)
134ce1f8a72SAlvin Chang	slli	a2, a2, THREAD_CLF_SAVED_SHIFT
135ce1f8a72SAlvin Chang	ori	a2, a2, (THREAD_CLF_TMP | THREAD_CLF_IRQ)
136ce1f8a72SAlvin Chang	sw	a2, THREAD_CORE_LOCAL_FLAGS(tp)
137ce1f8a72SAlvin Chang
1385232a348SAlvin Chang	/*
1395232a348SAlvin Chang	 * a0 = struct thread_ctx_regs *regs
1405232a348SAlvin Chang	 * a1 = cause
1415232a348SAlvin Chang	 * Call thread_native_interrupt_handler(regs, cause)
1425232a348SAlvin Chang	 */
1435232a348SAlvin Chang	call	thread_native_interrupt_handler
1445e26ef8fSAlvin Chang
145ce1f8a72SAlvin Chang	/* Update 32-bit core local flags */
146ce1f8a72SAlvin Chang	lw	a2, THREAD_CORE_LOCAL_FLAGS(tp)
147ce1f8a72SAlvin Chang	srli	a2, a2, THREAD_CLF_SAVED_SHIFT
148ce1f8a72SAlvin Chang	sw	a2, THREAD_CORE_LOCAL_FLAGS(tp)
149ce1f8a72SAlvin Chang
1505e26ef8fSAlvin Chang	/* Get thread context as sp */
1515e26ef8fSAlvin Chang	get_thread_ctx sp, t0
1525e26ef8fSAlvin Chang	/* Restore XEPC */
1535e26ef8fSAlvin Chang	load_xregs sp, THREAD_CTX_REG_EPC, REG_T0
1545e26ef8fSAlvin Chang	csrw	CSR_XEPC, t0
1555e26ef8fSAlvin Chang	/* Restore XSTATUS */
1565e26ef8fSAlvin Chang	load_xregs sp, THREAD_CTX_REG_STATUS, REG_T0
1575e26ef8fSAlvin Chang	csrw	CSR_XSTATUS, t0
1589f715794SAlvin Chang	/* Restore XIE */
1599f715794SAlvin Chang	load_xregs sp, THREAD_CTX_REG_IE, REG_T0
1609f715794SAlvin Chang	csrw	CSR_XIE, t0
1614a2528f8SAlvin Chang	/* We are going to XRET to kernel mode. Set XSCRATCH as 0 */
1624a2528f8SAlvin Chang	csrw	CSR_XSCRATCH, 0
1635e26ef8fSAlvin Chang	/* Restore all GPRs */
1645e26ef8fSAlvin Chang	load_xregs sp, THREAD_CTX_REG_RA, REG_RA
1655e26ef8fSAlvin Chang	load_xregs sp, THREAD_CTX_REG_GP, REG_GP
1665e26ef8fSAlvin Chang	load_xregs sp, THREAD_CTX_REG_T0, REG_T0, REG_T2
1675e26ef8fSAlvin Chang	load_xregs sp, THREAD_CTX_REG_S0, REG_S0, REG_S1
1685e26ef8fSAlvin Chang	load_xregs sp, THREAD_CTX_REG_A0, REG_A0, REG_A7
1695e26ef8fSAlvin Chang	load_xregs sp, THREAD_CTX_REG_S2, REG_S2, REG_S11
1705e26ef8fSAlvin Chang	load_xregs sp, THREAD_CTX_REG_T3, REG_T3, REG_T6
1715e26ef8fSAlvin Chang	load_xregs sp, THREAD_CTX_REG_SP, REG_SP
1725e26ef8fSAlvin Chang	XRET
1735e26ef8fSAlvin Chang
1745e26ef8fSAlvin Changexception_from_kernel:
1755e26ef8fSAlvin Chang	/*
1765e26ef8fSAlvin Chang	 * Update core local flags.
1775e26ef8fSAlvin Chang	 * flags = (flags << THREAD_CLF_SAVED_SHIFT) | THREAD_CLF_ABORT;
1785e26ef8fSAlvin Chang	 */
1795e26ef8fSAlvin Chang	lw	a0, THREAD_CORE_LOCAL_FLAGS(tp)
1805e26ef8fSAlvin Chang	slli	a0, a0, THREAD_CLF_SAVED_SHIFT
1815e26ef8fSAlvin Chang	ori	a0, a0, THREAD_CLF_ABORT
1825e26ef8fSAlvin Chang	li	a1, (THREAD_CLF_ABORT << THREAD_CLF_SAVED_SHIFT)
1835e26ef8fSAlvin Chang	and	a1, a0, a1
1845e26ef8fSAlvin Chang	bnez	a1, sel_tmp_sp
1855e26ef8fSAlvin Chang
1865e26ef8fSAlvin Chang	/* Select abort stack */
1875e26ef8fSAlvin Chang	load_xregs tp, THREAD_CORE_LOCAL_ABT_STACK_VA_END, REG_A1
1885e26ef8fSAlvin Chang	j	set_sp
1895e26ef8fSAlvin Chang
1905e26ef8fSAlvin Changsel_tmp_sp:
1915e26ef8fSAlvin Chang	/* We have an abort while using the abort stack, select tmp stack */
1925e26ef8fSAlvin Chang	load_xregs tp, THREAD_CORE_LOCAL_TMP_STACK_VA_END, REG_A1
1935e26ef8fSAlvin Chang	ori	a0, a0, THREAD_CLF_TMP	/* flags |= THREAD_CLF_TMP; */
1945e26ef8fSAlvin Chang
1955e26ef8fSAlvin Changset_sp:
1965e26ef8fSAlvin Chang	mv	sp, a1
1975e26ef8fSAlvin Chang	sw	a0, THREAD_CORE_LOCAL_FLAGS(tp)
1985e26ef8fSAlvin Chang
1995e26ef8fSAlvin Chang	/*
2005e26ef8fSAlvin Chang	 * Save state on stack
2015e26ef8fSAlvin Chang	 */
2025e26ef8fSAlvin Chang	addi	sp, sp, -THREAD_ABT_REGS_SIZE
2035e26ef8fSAlvin Chang
2045e26ef8fSAlvin Chang	/* Save kernel sp */
2055e26ef8fSAlvin Chang	load_xregs tp, THREAD_CORE_LOCAL_X0, REG_A0
2065e26ef8fSAlvin Chang	store_xregs sp, THREAD_ABT_REG_SP, REG_A0
2075e26ef8fSAlvin Chang
2085e26ef8fSAlvin Chang	/* Restore kernel a0, a1 which can be saved later */
2095e26ef8fSAlvin Chang	load_xregs tp, THREAD_CORE_LOCAL_X1, REG_A0, REG_A1
2105e26ef8fSAlvin Chang
2115e26ef8fSAlvin Chang	/* Save all other GPRs */
2125e26ef8fSAlvin Chang	store_xregs sp, THREAD_ABT_REG_RA, REG_RA
2135e26ef8fSAlvin Chang	store_xregs sp, THREAD_ABT_REG_GP, REG_GP
2145e26ef8fSAlvin Chang	store_xregs sp, THREAD_ABT_REG_TP, REG_TP
2155e26ef8fSAlvin Chang	store_xregs sp, THREAD_ABT_REG_T0, REG_T0, REG_T2
2165e26ef8fSAlvin Chang	store_xregs sp, THREAD_ABT_REG_S0, REG_S0, REG_S1
2175e26ef8fSAlvin Chang	store_xregs sp, THREAD_ABT_REG_A0, REG_A0, REG_A7
2185e26ef8fSAlvin Chang	store_xregs sp, THREAD_ABT_REG_S2, REG_S2, REG_S11
2195e26ef8fSAlvin Chang	store_xregs sp, THREAD_ABT_REG_T3, REG_T3, REG_T6
2205e26ef8fSAlvin Chang	/* Save XIE */
2215e26ef8fSAlvin Chang	csrr	t0, CSR_XIE
2225e26ef8fSAlvin Chang	store_xregs sp, THREAD_ABT_REG_IE, REG_T0
2235e26ef8fSAlvin Chang	/* Mask all interrupts */
2245e26ef8fSAlvin Chang	csrw	CSR_XIE, x0
2255e26ef8fSAlvin Chang	/* Save XSTATUS */
2265e26ef8fSAlvin Chang	csrr	t0, CSR_XSTATUS
2275e26ef8fSAlvin Chang	store_xregs sp, THREAD_ABT_REG_STATUS, REG_T0
2285e26ef8fSAlvin Chang	/* Save XEPC */
2295e26ef8fSAlvin Chang	csrr	t0, CSR_XEPC
2305e26ef8fSAlvin Chang	store_xregs sp, THREAD_ABT_REG_EPC, REG_T0
2315e26ef8fSAlvin Chang	/* Save XTVAL */
2325e26ef8fSAlvin Chang	csrr	t0, CSR_XTVAL
2335e26ef8fSAlvin Chang	store_xregs sp, THREAD_ABT_REG_TVAL, REG_T0
2345e26ef8fSAlvin Chang	/* Save XCAUSE */
2355e26ef8fSAlvin Chang	csrr	a0, CSR_XCAUSE
2365e26ef8fSAlvin Chang	store_xregs sp, THREAD_ABT_REG_CAUSE, REG_A0
2375e26ef8fSAlvin Chang
2385e26ef8fSAlvin Chang	/*
2395e26ef8fSAlvin Chang	 * a0 = cause
2405e26ef8fSAlvin Chang	 * a1 = sp (struct thread_abort_regs *regs)
2415e26ef8fSAlvin Chang	 * Call abort_handler(cause, regs)
2425e26ef8fSAlvin Chang	 */
2435e26ef8fSAlvin Chang	mv	a1, sp
2445e26ef8fSAlvin Chang	call	abort_handler
2455e26ef8fSAlvin Chang
2465e26ef8fSAlvin Chang	/*
2475e26ef8fSAlvin Chang	 * Restore state from stack
2485e26ef8fSAlvin Chang	 */
2495e26ef8fSAlvin Chang
2505e26ef8fSAlvin Chang	/* Restore XEPC */
2515e26ef8fSAlvin Chang	load_xregs sp, THREAD_ABT_REG_EPC, REG_T0
2525e26ef8fSAlvin Chang	csrw	CSR_XEPC, t0
2535e26ef8fSAlvin Chang	/* Restore XSTATUS */
2545e26ef8fSAlvin Chang	load_xregs sp, THREAD_ABT_REG_STATUS, REG_T0
2555e26ef8fSAlvin Chang	csrw	CSR_XSTATUS, t0
2569f715794SAlvin Chang	/* Restore XIE */
2579f715794SAlvin Chang	load_xregs sp, THREAD_ABT_REG_IE, REG_T0
2589f715794SAlvin Chang	csrw	CSR_XIE, t0
2594a2528f8SAlvin Chang	/* We are going to XRET to kernel mode. Set XSCRATCH as 0 */
2604a2528f8SAlvin Chang	csrw	CSR_XSCRATCH, 0
2615e26ef8fSAlvin Chang
2625e26ef8fSAlvin Chang	/* Update core local flags */
2635e26ef8fSAlvin Chang	lw	a0, THREAD_CORE_LOCAL_FLAGS(tp)
2645e26ef8fSAlvin Chang	srli	a0, a0, THREAD_CLF_SAVED_SHIFT
2655e26ef8fSAlvin Chang	sw	a0, THREAD_CORE_LOCAL_FLAGS(tp)
2665e26ef8fSAlvin Chang
2675e26ef8fSAlvin Chang	/* Restore all GPRs */
2685e26ef8fSAlvin Chang	load_xregs sp, THREAD_ABT_REG_RA, REG_RA
2695e26ef8fSAlvin Chang	load_xregs sp, THREAD_ABT_REG_GP, REG_GP
2705e26ef8fSAlvin Chang	load_xregs sp, THREAD_ABT_REG_TP, REG_TP
2715e26ef8fSAlvin Chang	load_xregs sp, THREAD_ABT_REG_T0, REG_T0, REG_T2
2725e26ef8fSAlvin Chang	load_xregs sp, THREAD_ABT_REG_S0, REG_S0, REG_S1
2735e26ef8fSAlvin Chang	load_xregs sp, THREAD_ABT_REG_A0, REG_A0, REG_A7
2745e26ef8fSAlvin Chang	load_xregs sp, THREAD_ABT_REG_S2, REG_S2, REG_S11
2755e26ef8fSAlvin Chang	load_xregs sp, THREAD_ABT_REG_T3, REG_T3, REG_T6
2765e26ef8fSAlvin Chang	load_xregs sp, THREAD_ABT_REG_SP, REG_SP
2779b1a3bbeSMarouene Boubakri	XRET
2789b1a3bbeSMarouene BoubakriEND_FUNC trap_from_kernel
2799b1a3bbeSMarouene Boubakri
2809b1a3bbeSMarouene BoubakriLOCAL_FUNC trap_from_user, :
2815e26ef8fSAlvin Chang	/* Save user sp, a0, a1 into temporary spaces of thread_core_local */
2825e26ef8fSAlvin Chang	store_xregs tp, THREAD_CORE_LOCAL_X0, REG_SP
2835e26ef8fSAlvin Chang	store_xregs tp, THREAD_CORE_LOCAL_X1, REG_A0, REG_A1
2845e26ef8fSAlvin Chang
2855e26ef8fSAlvin Chang	csrr	a0, CSR_XCAUSE
2865e26ef8fSAlvin Chang	/* MSB of cause differentiates between interrupts and exceptions */
2875e26ef8fSAlvin Chang	bge	a0, zero, exception_from_user
2885e26ef8fSAlvin Chang
2895e26ef8fSAlvin Changinterrupt_from_user:
2905e26ef8fSAlvin Chang	/* Get thread context as sp */
2915e26ef8fSAlvin Chang	get_thread_ctx sp, a0
2925e26ef8fSAlvin Chang
2935e26ef8fSAlvin Chang	/* Save user sp */
2945e26ef8fSAlvin Chang	load_xregs tp, THREAD_CORE_LOCAL_X0, REG_A0
2955e26ef8fSAlvin Chang	store_xregs sp, THREAD_CTX_REG_SP, REG_A0
2965e26ef8fSAlvin Chang
2975e26ef8fSAlvin Chang	/* Restore user a0, a1 which can be saved later */
2985e26ef8fSAlvin Chang	load_xregs tp, THREAD_CORE_LOCAL_X1, REG_A0, REG_A1
2995e26ef8fSAlvin Chang
3005e26ef8fSAlvin Chang	/* Save user gp */
3015e26ef8fSAlvin Chang	store_xregs sp, THREAD_CTX_REG_GP, REG_GP
3025e26ef8fSAlvin Chang
3035e26ef8fSAlvin Chang	/*
3045e26ef8fSAlvin Chang	 * Set the scratch register to 0 such in case of a recursive
3055e26ef8fSAlvin Chang	 * exception thread_trap_vect() knows that it is emitted from kernel.
3065e26ef8fSAlvin Chang	 */
3075e26ef8fSAlvin Chang	csrrw	gp, CSR_XSCRATCH, zero
3085e26ef8fSAlvin Chang	/* Save user tp we previously swapped into CSR_XSCRATCH */
3095e26ef8fSAlvin Chang	store_xregs sp, THREAD_CTX_REG_TP, REG_GP
3105e26ef8fSAlvin Chang	/* Set kernel gp */
3115e26ef8fSAlvin Chang.option push
3125e26ef8fSAlvin Chang.option norelax
3135e26ef8fSAlvin Chang	la	gp, __global_pointer$
3145e26ef8fSAlvin Chang.option pop
3155e26ef8fSAlvin Chang	/* Save all other GPRs */
3165e26ef8fSAlvin Chang	store_xregs sp, THREAD_CTX_REG_RA, REG_RA
3175e26ef8fSAlvin Chang	store_xregs sp, THREAD_CTX_REG_T0, REG_T0, REG_T2
3185e26ef8fSAlvin Chang	store_xregs sp, THREAD_CTX_REG_S0, REG_S0, REG_S1
3195e26ef8fSAlvin Chang	store_xregs sp, THREAD_CTX_REG_A0, REG_A0, REG_A7
3205e26ef8fSAlvin Chang	store_xregs sp, THREAD_CTX_REG_S2, REG_S2, REG_S11
3215e26ef8fSAlvin Chang	store_xregs sp, THREAD_CTX_REG_T3, REG_T3, REG_T6
3225e26ef8fSAlvin Chang	/* Save XIE */
3235e26ef8fSAlvin Chang	csrr	t0, CSR_XIE
3245e26ef8fSAlvin Chang	store_xregs sp, THREAD_CTX_REG_IE, REG_T0
3255e26ef8fSAlvin Chang	/* Mask all interrupts */
3265e26ef8fSAlvin Chang	csrw	CSR_XIE, x0
3275e26ef8fSAlvin Chang	/* Save XSTATUS */
3285e26ef8fSAlvin Chang	csrr	t0, CSR_XSTATUS
3295e26ef8fSAlvin Chang	store_xregs sp, THREAD_CTX_REG_STATUS, REG_T0
3305e26ef8fSAlvin Chang	/* Save XEPC */
3315e26ef8fSAlvin Chang	csrr	t0, CSR_XEPC
3325e26ef8fSAlvin Chang	store_xregs sp, THREAD_CTX_REG_EPC, REG_T0
3335e26ef8fSAlvin Chang
3345e26ef8fSAlvin Chang	/*
335ef00a923SAlvin Chang	 * a0 = struct thread_ctx_regs *regs
336ef00a923SAlvin Chang	 * a1 = cause
3375e26ef8fSAlvin Chang	 */
338ef00a923SAlvin Chang	mv	a0, sp
339ef00a923SAlvin Chang	csrr	a1, CSR_XCAUSE
3405e26ef8fSAlvin Chang	/* Load tmp_stack_va_end as current sp. */
3415e26ef8fSAlvin Chang	load_xregs tp, THREAD_CORE_LOCAL_TMP_STACK_VA_END, REG_SP
3425232a348SAlvin Chang
3435232a348SAlvin Chang	/*
3445232a348SAlvin Chang	 * Get interrupt code from XCAUSE and build XIP. For example, if the
3455232a348SAlvin Chang	 * value of XCAUSE is 0x8000000000000005 (supervisor timer interrupt),
3465232a348SAlvin Chang	 * we build 0x20, which is (1 << 5) and indicates the sip.STIP signal.
3475232a348SAlvin Chang	 */
3485232a348SAlvin Chang	li	a2, CSR_XCAUSE_INTR_FLAG
3495232a348SAlvin Chang	sub	a2, a1, a2
3505232a348SAlvin Chang	li	a3, 1
3515232a348SAlvin Chang	sll	a3, a3, a2
3525232a348SAlvin Chang	/*
3535232a348SAlvin Chang	 * Compare built XIP with THREAD_EXCP_FOREIGN_INTR. If XIP is one of
3545232a348SAlvin Chang	 * THREAD_EXCP_FOREIGN_INTR, call thread_foreign_interrupt_handler().
3555232a348SAlvin Chang	 */
3565232a348SAlvin Chang	li	a2, THREAD_EXCP_FOREIGN_INTR
3575232a348SAlvin Chang	and	a2, a3, a2
3585232a348SAlvin Chang	beqz	a2, native_interrupt_from_user
3595232a348SAlvin Chang
3605232a348SAlvin Changforeign_interrupt_from_user:
3615232a348SAlvin Chang	/*
3625232a348SAlvin Chang	 * a0 = struct thread_ctx_regs *regs
3635232a348SAlvin Chang	 * Tail call thread_foreign_interrupt_handler(regs) since we will not
3645232a348SAlvin Chang	 * return to here.
3655232a348SAlvin Chang	 */
3665232a348SAlvin Chang	tail	thread_foreign_interrupt_handler
3675232a348SAlvin Chang
3685232a348SAlvin Changnative_interrupt_from_user:
369ce1f8a72SAlvin Chang	/* Update 32-bit core local flags */
370ce1f8a72SAlvin Chang	lw	a2, THREAD_CORE_LOCAL_FLAGS(tp)
371ce1f8a72SAlvin Chang	slli	a2, a2, THREAD_CLF_SAVED_SHIFT
372ce1f8a72SAlvin Chang	ori	a2, a2, (THREAD_CLF_TMP | THREAD_CLF_IRQ)
373ce1f8a72SAlvin Chang	sw	a2, THREAD_CORE_LOCAL_FLAGS(tp)
374ce1f8a72SAlvin Chang
3755232a348SAlvin Chang	/*
3765232a348SAlvin Chang	 * a0 = struct thread_ctx_regs *regs
3775232a348SAlvin Chang	 * a1 = cause
3785232a348SAlvin Chang	 * Call thread_native_interrupt_handler(regs, cause)
3795232a348SAlvin Chang	 */
3805232a348SAlvin Chang	call	thread_native_interrupt_handler
3815e26ef8fSAlvin Chang
382ce1f8a72SAlvin Chang	/* Update 32-bit core local flags */
383ce1f8a72SAlvin Chang	lw	a2, THREAD_CORE_LOCAL_FLAGS(tp)
384ce1f8a72SAlvin Chang	srli	a2, a2, THREAD_CLF_SAVED_SHIFT
385ce1f8a72SAlvin Chang	sw	a2, THREAD_CORE_LOCAL_FLAGS(tp)
386ce1f8a72SAlvin Chang
3875e26ef8fSAlvin Chang	/* Get thread context as sp */
3885e26ef8fSAlvin Chang	get_thread_ctx sp, t0
3895e26ef8fSAlvin Chang	/* Restore XEPC */
3905e26ef8fSAlvin Chang	load_xregs sp, THREAD_CTX_REG_EPC, REG_T0
3915e26ef8fSAlvin Chang	csrw	CSR_XEPC, t0
3925e26ef8fSAlvin Chang	/* Restore XSTATUS */
3935e26ef8fSAlvin Chang	load_xregs sp, THREAD_CTX_REG_STATUS, REG_T0
3945e26ef8fSAlvin Chang	csrw	CSR_XSTATUS, t0
3959f715794SAlvin Chang	/* Restore XIE */
3969f715794SAlvin Chang	load_xregs sp, THREAD_CTX_REG_IE, REG_T0
3979f715794SAlvin Chang	csrw	CSR_XIE, t0
3985e26ef8fSAlvin Chang	/* Set scratch as thread_core_local */
3995e26ef8fSAlvin Chang	csrw	CSR_XSCRATCH, tp
4005e26ef8fSAlvin Chang	/* Restore all GPRs */
4015e26ef8fSAlvin Chang	load_xregs sp, THREAD_CTX_REG_RA, REG_RA
4025e26ef8fSAlvin Chang	load_xregs sp, THREAD_CTX_REG_GP, REG_GP
4035e26ef8fSAlvin Chang	load_xregs sp, THREAD_CTX_REG_TP, REG_TP
4045e26ef8fSAlvin Chang	load_xregs sp, THREAD_CTX_REG_T0, REG_T0, REG_T2
4055e26ef8fSAlvin Chang	load_xregs sp, THREAD_CTX_REG_S0, REG_S0, REG_S1
4065e26ef8fSAlvin Chang	load_xregs sp, THREAD_CTX_REG_A0, REG_A0, REG_A7
4075e26ef8fSAlvin Chang	load_xregs sp, THREAD_CTX_REG_S2, REG_S2, REG_S11
4085e26ef8fSAlvin Chang	load_xregs sp, THREAD_CTX_REG_T3, REG_T3, REG_T6
4095e26ef8fSAlvin Chang	load_xregs sp, THREAD_CTX_REG_SP, REG_SP
4105e26ef8fSAlvin Chang	XRET
4115e26ef8fSAlvin Chang
4125e26ef8fSAlvin Changexception_from_user:
4135e26ef8fSAlvin Chang	/* a0 is CSR_XCAUSE */
4145e26ef8fSAlvin Chang	li	a1, CAUSE_USER_ECALL
4155e26ef8fSAlvin Chang	bne	a0, a1, abort_from_user
4165e26ef8fSAlvin Changecall_from_user:
4175e26ef8fSAlvin Chang	/* Load and set kernel sp from thread context */
4185e26ef8fSAlvin Chang	get_thread_ctx a0, a1
4195e26ef8fSAlvin Chang	load_xregs a0, THREAD_CTX_KERN_SP, REG_SP
4205e26ef8fSAlvin Chang
4215e26ef8fSAlvin Chang	/* Now sp is kernel sp, create stack for struct thread_scall_regs */
4225e26ef8fSAlvin Chang	addi	sp, sp, -THREAD_SCALL_REGS_SIZE
4235e26ef8fSAlvin Chang	/* Save user sp */
4245e26ef8fSAlvin Chang	load_xregs tp, THREAD_CORE_LOCAL_X0, REG_A0
4255e26ef8fSAlvin Chang	store_xregs sp, THREAD_SCALL_REG_SP, REG_A0
4265e26ef8fSAlvin Chang
4275e26ef8fSAlvin Chang	/* Restore user a0, a1 which can be saved later */
4285e26ef8fSAlvin Chang	load_xregs tp, THREAD_CORE_LOCAL_X1, REG_A0, REG_A1
4295e26ef8fSAlvin Chang
4305e26ef8fSAlvin Chang	/* Save user gp */
4315e26ef8fSAlvin Chang	store_xregs sp, THREAD_SCALL_REG_GP, REG_GP
4325e26ef8fSAlvin Chang	/*
4335e26ef8fSAlvin Chang	 * Set the scratch register to 0 such in case of a recursive
4345e26ef8fSAlvin Chang	 * exception thread_trap_vect() knows that it is emitted from kernel.
4355e26ef8fSAlvin Chang	 */
4365e26ef8fSAlvin Chang	csrrw	gp, CSR_XSCRATCH, zero
4375e26ef8fSAlvin Chang	/* Save user tp we previously swapped into CSR_XSCRATCH */
4385e26ef8fSAlvin Chang	store_xregs sp, THREAD_SCALL_REG_TP, REG_GP
4395e26ef8fSAlvin Chang	/* Set kernel gp */
4405e26ef8fSAlvin Chang.option push
4415e26ef8fSAlvin Chang.option norelax
4425e26ef8fSAlvin Chang	la	gp, __global_pointer$
4435e26ef8fSAlvin Chang.option pop
4445e26ef8fSAlvin Chang
4455e26ef8fSAlvin Chang	/* Save other caller-saved registers */
4465e26ef8fSAlvin Chang	store_xregs sp, THREAD_SCALL_REG_RA, REG_RA
4475e26ef8fSAlvin Chang	store_xregs sp, THREAD_SCALL_REG_T0, REG_T0, REG_T2
4485e26ef8fSAlvin Chang	store_xregs sp, THREAD_SCALL_REG_A0, REG_A0, REG_A7
4495e26ef8fSAlvin Chang	store_xregs sp, THREAD_SCALL_REG_T3, REG_T3, REG_T6
4505e26ef8fSAlvin Chang	/* Save XIE */
4515e26ef8fSAlvin Chang	csrr	a0, CSR_XIE
4525e26ef8fSAlvin Chang	store_xregs sp, THREAD_SCALL_REG_IE, REG_A0
4535e26ef8fSAlvin Chang	/* Mask all interrupts */
4545e26ef8fSAlvin Chang	csrw	CSR_XIE, zero
4555e26ef8fSAlvin Chang	/* Save XSTATUS */
4565e26ef8fSAlvin Chang	csrr	a0, CSR_XSTATUS
4575e26ef8fSAlvin Chang	store_xregs sp, THREAD_SCALL_REG_STATUS, REG_A0
4585e26ef8fSAlvin Chang	/* Save XEPC */
4595e26ef8fSAlvin Chang	csrr	a0, CSR_XEPC
4605e26ef8fSAlvin Chang	store_xregs sp, THREAD_SCALL_REG_EPC, REG_A0
4615e26ef8fSAlvin Chang
4625e26ef8fSAlvin Chang	/*
4635e26ef8fSAlvin Chang	 * a0 = struct thread_scall_regs *regs
4645e26ef8fSAlvin Chang	 * Call thread_scall_handler(regs)
4655e26ef8fSAlvin Chang	 */
4665e26ef8fSAlvin Chang	mv	a0, sp
4675e26ef8fSAlvin Chang	call	thread_scall_handler
4685e26ef8fSAlvin Chang
4695e26ef8fSAlvin Chang	/*
4705e26ef8fSAlvin Chang	 * Save kernel sp we'll had at the beginning of this function.
4715e26ef8fSAlvin Chang	 * This is when this TA has called another TA because
4725e26ef8fSAlvin Chang	 * __thread_enter_user_mode() also saves the stack pointer in this
4735e26ef8fSAlvin Chang	 * field.
4745e26ef8fSAlvin Chang	 */
4755e26ef8fSAlvin Chang	get_thread_ctx a0, a1
4765e26ef8fSAlvin Chang	addi	t0, sp, THREAD_SCALL_REGS_SIZE
4775e26ef8fSAlvin Chang	store_xregs a0, THREAD_CTX_KERN_SP, REG_T0
4785e26ef8fSAlvin Chang
4795c718542SAlvin Chang	/* Restore XEPC */
4805e26ef8fSAlvin Chang	load_xregs sp, THREAD_SCALL_REG_EPC, REG_T0
4815e26ef8fSAlvin Chang	csrw	CSR_XEPC, t0
4825e26ef8fSAlvin Chang	/* Restore XSTATUS */
4835e26ef8fSAlvin Chang	load_xregs sp, THREAD_SCALL_REG_STATUS, REG_T0
4845e26ef8fSAlvin Chang	csrw	CSR_XSTATUS, t0
4859f715794SAlvin Chang	/* Restore XIE */
4869f715794SAlvin Chang	load_xregs sp, THREAD_SCALL_REG_IE, REG_T0
4879f715794SAlvin Chang	csrw	CSR_XIE, t0
4885c718542SAlvin Chang	/* Check previous privilege mode by status.SPP */
4899f715794SAlvin Chang	csrr	t0, CSR_XSTATUS
4905c718542SAlvin Chang	b_if_prev_priv_is_u t0, 1f
4915c718542SAlvin Chang	/*
4925c718542SAlvin Chang	 * We are going to XRET to kernel mode.
4935c718542SAlvin Chang	 * XSCRATCH is already zero to indicate that we are in kernel mode.
4945c718542SAlvin Chang	 * We must keep kernel gp & tp, so skip restoring user gp & tp.
4955c718542SAlvin Chang	 */
4965c718542SAlvin Chang	j	2f
4975c718542SAlvin Chang1:
4985c718542SAlvin Chang	/*
4995c718542SAlvin Chang	 * We are going to XRET to user mode.
5005c718542SAlvin Chang	 * XSCRATCH must be tp(thread_core_local) to be used in next trap.
5015c718542SAlvin Chang	 * We also need to restore user gp & tp
5025c718542SAlvin Chang	 */
5035e26ef8fSAlvin Chang	csrw	CSR_XSCRATCH, tp
5045e26ef8fSAlvin Chang	load_xregs sp, THREAD_SCALL_REG_GP, REG_GP
5055e26ef8fSAlvin Chang	load_xregs sp, THREAD_SCALL_REG_TP, REG_TP
5065c718542SAlvin Chang2:
5075c718542SAlvin Chang	/* Restore remaining caller-saved registers */
5085c718542SAlvin Chang	load_xregs sp, THREAD_SCALL_REG_RA, REG_RA
5095e26ef8fSAlvin Chang	load_xregs sp, THREAD_SCALL_REG_T0, REG_T0, REG_T2
5105e26ef8fSAlvin Chang	load_xregs sp, THREAD_SCALL_REG_A0, REG_A0, REG_A7
5115e26ef8fSAlvin Chang	load_xregs sp, THREAD_SCALL_REG_T3, REG_T3, REG_T6
5125e26ef8fSAlvin Chang	load_xregs sp, THREAD_SCALL_REG_SP, REG_SP
5135e26ef8fSAlvin Chang	XRET
5145e26ef8fSAlvin Chang
5155e26ef8fSAlvin Changabort_from_user:
5165e26ef8fSAlvin Chang	/*
5175e26ef8fSAlvin Chang	 * Update core local flags
5185e26ef8fSAlvin Chang	 */
5195e26ef8fSAlvin Chang	lw	a0, THREAD_CORE_LOCAL_FLAGS(tp)
5205e26ef8fSAlvin Chang	slli	a0, a0, THREAD_CLF_SAVED_SHIFT
5215e26ef8fSAlvin Chang	ori	a0, a0, THREAD_CLF_ABORT
5225e26ef8fSAlvin Chang	sw	a0, THREAD_CORE_LOCAL_FLAGS(tp)
5235e26ef8fSAlvin Chang
5245e26ef8fSAlvin Chang	/*
5255e26ef8fSAlvin Chang	 * Save state on stack
5265e26ef8fSAlvin Chang	 */
5275e26ef8fSAlvin Chang
5285e26ef8fSAlvin Chang	/* Load abt_stack_va_end and set it as sp */
5295e26ef8fSAlvin Chang	load_xregs tp, THREAD_CORE_LOCAL_ABT_STACK_VA_END, REG_SP
5305e26ef8fSAlvin Chang
5315e26ef8fSAlvin Chang	/* Now sp is abort sp, create stack for struct thread_abort_regs */
5325e26ef8fSAlvin Chang	addi	sp, sp, -THREAD_ABT_REGS_SIZE
5335e26ef8fSAlvin Chang
5345e26ef8fSAlvin Chang	/* Save user sp */
5355e26ef8fSAlvin Chang	load_xregs tp, THREAD_CORE_LOCAL_X0, REG_A0
5365e26ef8fSAlvin Chang	store_xregs sp, THREAD_ABT_REG_SP, REG_A0
5375e26ef8fSAlvin Chang
5385e26ef8fSAlvin Chang	/* Restore user a0, a1 which can be saved later */
5395e26ef8fSAlvin Chang	load_xregs tp, THREAD_CORE_LOCAL_X1, REG_A0, REG_A1
5405e26ef8fSAlvin Chang
5415e26ef8fSAlvin Chang	/* Save user gp */
5425e26ef8fSAlvin Chang	store_xregs sp, THREAD_ABT_REG_GP, REG_GP
5435e26ef8fSAlvin Chang
5445e26ef8fSAlvin Chang	/*
5455e26ef8fSAlvin Chang	 * Set the scratch register to 0 such in case of a recursive
5465e26ef8fSAlvin Chang	 * exception thread_trap_vect() knows that it is emitted from kernel.
5475e26ef8fSAlvin Chang	 */
5485e26ef8fSAlvin Chang	csrrw	gp, CSR_XSCRATCH, zero
5495e26ef8fSAlvin Chang	/* Save user tp we previously swapped into CSR_XSCRATCH */
5505e26ef8fSAlvin Chang	store_xregs sp, THREAD_ABT_REG_TP, REG_GP
5515e26ef8fSAlvin Chang	/* Set kernel gp */
5525e26ef8fSAlvin Chang.option push
5535e26ef8fSAlvin Chang.option norelax
5545e26ef8fSAlvin Chang	la	gp, __global_pointer$
5555e26ef8fSAlvin Chang.option pop
5565e26ef8fSAlvin Chang	/* Save all other GPRs */
5575e26ef8fSAlvin Chang	store_xregs sp, THREAD_ABT_REG_RA, REG_RA
5585e26ef8fSAlvin Chang	store_xregs sp, THREAD_ABT_REG_T0, REG_T0, REG_T2
5595e26ef8fSAlvin Chang	store_xregs sp, THREAD_ABT_REG_S0, REG_S0, REG_S1
5605e26ef8fSAlvin Chang	store_xregs sp, THREAD_ABT_REG_A0, REG_A0, REG_A7
5615e26ef8fSAlvin Chang	store_xregs sp, THREAD_ABT_REG_S2, REG_S2, REG_S11
5625e26ef8fSAlvin Chang	store_xregs sp, THREAD_ABT_REG_T3, REG_T3, REG_T6
5635e26ef8fSAlvin Chang	/* Save XIE */
5645e26ef8fSAlvin Chang	csrr	t0, CSR_XIE
5655e26ef8fSAlvin Chang	store_xregs sp, THREAD_ABT_REG_IE, REG_T0
5665e26ef8fSAlvin Chang	/* Mask all interrupts */
5675e26ef8fSAlvin Chang	csrw	CSR_XIE, x0
5685e26ef8fSAlvin Chang	/* Save XSTATUS */
5695e26ef8fSAlvin Chang	csrr	t0, CSR_XSTATUS
5705e26ef8fSAlvin Chang	store_xregs sp, THREAD_ABT_REG_STATUS, REG_T0
5715e26ef8fSAlvin Chang	/* Save XEPC */
5725e26ef8fSAlvin Chang	csrr	t0, CSR_XEPC
5735e26ef8fSAlvin Chang	store_xregs sp, THREAD_ABT_REG_EPC, REG_T0
5745e26ef8fSAlvin Chang	/* Save XTVAL */
5755e26ef8fSAlvin Chang	csrr	t0, CSR_XTVAL
5765e26ef8fSAlvin Chang	store_xregs sp, THREAD_ABT_REG_TVAL, REG_T0
5775e26ef8fSAlvin Chang	/* Save XCAUSE */
5785e26ef8fSAlvin Chang	csrr	a0, CSR_XCAUSE
5795e26ef8fSAlvin Chang	store_xregs sp, THREAD_ABT_REG_CAUSE, REG_A0
5805e26ef8fSAlvin Chang
5815e26ef8fSAlvin Chang	/*
5825e26ef8fSAlvin Chang	 * a0 = cause
5835e26ef8fSAlvin Chang	 * a1 = sp (struct thread_abort_regs *regs)
5845e26ef8fSAlvin Chang	 * Call abort_handler(cause, regs)
5855e26ef8fSAlvin Chang	 */
5865e26ef8fSAlvin Chang	mv	a1, sp
5875e26ef8fSAlvin Chang	call	abort_handler
5885e26ef8fSAlvin Chang
5895e26ef8fSAlvin Chang	/*
5905e26ef8fSAlvin Chang	 * Restore state from stack
5915e26ef8fSAlvin Chang	 */
5925e26ef8fSAlvin Chang
5935e26ef8fSAlvin Chang	/* Restore XEPC */
5945e26ef8fSAlvin Chang	load_xregs sp, THREAD_ABT_REG_EPC, REG_T0
5955e26ef8fSAlvin Chang	csrw	CSR_XEPC, t0
5965e26ef8fSAlvin Chang	/* Restore XSTATUS */
5975e26ef8fSAlvin Chang	load_xregs sp, THREAD_ABT_REG_STATUS, REG_T0
5985e26ef8fSAlvin Chang	csrw	CSR_XSTATUS, t0
5999f715794SAlvin Chang	/* Restore XIE */
6009f715794SAlvin Chang	load_xregs sp, THREAD_ABT_REG_IE, REG_T0
6019f715794SAlvin Chang	csrw	CSR_XIE, t0
6025e26ef8fSAlvin Chang
6035e26ef8fSAlvin Chang	/* Update core local flags */
6045e26ef8fSAlvin Chang	lw	a0, THREAD_CORE_LOCAL_FLAGS(tp)
6055e26ef8fSAlvin Chang	srli	a0, a0, THREAD_CLF_SAVED_SHIFT
6065e26ef8fSAlvin Chang	sw	a0, THREAD_CORE_LOCAL_FLAGS(tp)
6075e26ef8fSAlvin Chang
6085c718542SAlvin Chang	/* Check previous privilege mode by status.SPP */
6099f715794SAlvin Chang	csrr	t0, CSR_XSTATUS
6105c718542SAlvin Chang	b_if_prev_priv_is_u t0, 1f
6115c718542SAlvin Chang	/*
6125c718542SAlvin Chang	 * We are going to XRET to kernel mode.
6135c718542SAlvin Chang	 * XSCRATCH is already zero to indicate that we are in kernel mode.
6145c718542SAlvin Chang	 * We must keep kernel gp & tp, so skip restoring user gp & tp.
6155c718542SAlvin Chang	 */
6165c718542SAlvin Chang	j	2f
6175c718542SAlvin Chang1:
6185c718542SAlvin Chang	/*
6195c718542SAlvin Chang	 * We are going to XRET to user mode.
6205c718542SAlvin Chang	 * XSCRATCH must be tp(thread_core_local) to be used in next trap.
6215c718542SAlvin Chang	 * We also need to restore user gp & tp
6225c718542SAlvin Chang	 */
6235c718542SAlvin Chang	csrw	CSR_XSCRATCH, tp
6245e26ef8fSAlvin Chang	load_xregs sp, THREAD_ABT_REG_GP, REG_GP
6255e26ef8fSAlvin Chang	load_xregs sp, THREAD_ABT_REG_TP, REG_TP
6265c718542SAlvin Chang2:
6275c718542SAlvin Chang	/* Restore remaining GPRs */
6285c718542SAlvin Chang	load_xregs sp, THREAD_ABT_REG_RA, REG_RA
6295e26ef8fSAlvin Chang	load_xregs sp, THREAD_ABT_REG_T0, REG_T0, REG_T2
6305e26ef8fSAlvin Chang	load_xregs sp, THREAD_ABT_REG_S0, REG_S0, REG_S1
6315e26ef8fSAlvin Chang	load_xregs sp, THREAD_ABT_REG_A0, REG_A0, REG_A7
6325e26ef8fSAlvin Chang	load_xregs sp, THREAD_ABT_REG_S2, REG_S2, REG_S11
6335e26ef8fSAlvin Chang	load_xregs sp, THREAD_ABT_REG_T3, REG_T3, REG_T6
6345e26ef8fSAlvin Chang	load_xregs sp, THREAD_ABT_REG_SP, REG_SP
6359b1a3bbeSMarouene Boubakri	XRET
6369b1a3bbeSMarouene BoubakriEND_FUNC trap_from_user
6379b1a3bbeSMarouene Boubakri
6389b1a3bbeSMarouene Boubakri/*
6399b1a3bbeSMarouene Boubakri * void thread_unwind_user_mode(uint32_t ret, uint32_t exit_status0,
6409b1a3bbeSMarouene Boubakri * 				uint32_t exit_status1);
6419b1a3bbeSMarouene Boubakri * See description in thread.h
6429b1a3bbeSMarouene Boubakri */
6439b1a3bbeSMarouene BoubakriFUNC thread_unwind_user_mode , :
6449b1a3bbeSMarouene Boubakri	/* Store the exit status */
6459b1a3bbeSMarouene Boubakri	load_xregs sp, THREAD_USER_MODE_REC_CTX_REGS_PTR, REG_A3, REG_A5
6469b1a3bbeSMarouene Boubakri	sw	a1, (a4)
6479b1a3bbeSMarouene Boubakri	sw	a2, (a5)
6489df67cd4SAlvin Chang	/* Save user callee-saved regs */
6499b1a3bbeSMarouene Boubakri	store_xregs a3, THREAD_CTX_REG_S0, REG_S0, REG_S1
6509b1a3bbeSMarouene Boubakri	store_xregs a3, THREAD_CTX_REG_S2, REG_S2, REG_S11
6519df67cd4SAlvin Chang	/* Restore kernel ra(thread_enter_user_mode()) & callee-saved regs */
6529df67cd4SAlvin Chang	load_xregs sp, THREAD_USER_MODE_REC_RA, REG_RA
6539df67cd4SAlvin Chang	load_xregs sp, THREAD_USER_MODE_REC_S0, REG_S0, REG_S1
6549df67cd4SAlvin Chang	load_xregs sp, THREAD_USER_MODE_REC_S2, REG_S2, REG_S11
6559b1a3bbeSMarouene Boubakri	add	sp, sp, THREAD_USER_MODE_REC_SIZE
6569b1a3bbeSMarouene Boubakri	/* Return from the call of thread_enter_user_mode() */
6579b1a3bbeSMarouene Boubakri	ret
6589b1a3bbeSMarouene BoubakriEND_FUNC thread_unwind_user_mode
6599b1a3bbeSMarouene Boubakri
6609b1a3bbeSMarouene Boubakri/*
6619b1a3bbeSMarouene Boubakri * uint32_t __thread_enter_user_mode(struct thread_ctx_regs *regs,
6629b1a3bbeSMarouene Boubakri *				     uint32_t *exit_status0,
6639b1a3bbeSMarouene Boubakri *				     uint32_t *exit_status1);
6649b1a3bbeSMarouene Boubakri */
6659b1a3bbeSMarouene BoubakriFUNC __thread_enter_user_mode , :
6669b1a3bbeSMarouene Boubakri	/*
6679b1a3bbeSMarouene Boubakri	 * Create and fill in the struct thread_user_mode_rec
6689b1a3bbeSMarouene Boubakri	 */
6699b1a3bbeSMarouene Boubakri	addi	sp, sp, -THREAD_USER_MODE_REC_SIZE
6709b1a3bbeSMarouene Boubakri	store_xregs sp, THREAD_USER_MODE_REC_CTX_REGS_PTR, REG_A0, REG_A2
6719df67cd4SAlvin Chang	store_xregs sp, THREAD_USER_MODE_REC_RA, REG_RA
6729df67cd4SAlvin Chang	store_xregs sp, THREAD_USER_MODE_REC_S0, REG_S0, REG_S1
6739df67cd4SAlvin Chang	store_xregs sp, THREAD_USER_MODE_REC_S2, REG_S2, REG_S11
6749b1a3bbeSMarouene Boubakri
6759b1a3bbeSMarouene Boubakri	/*
6769b1a3bbeSMarouene Boubakri	 * Save the kernel stack pointer in the thread context
6779b1a3bbeSMarouene Boubakri	 */
6789b1a3bbeSMarouene Boubakri
6799b1a3bbeSMarouene Boubakri	/* Get pointer to current thread context */
6809b1a3bbeSMarouene Boubakri	get_thread_ctx s0, s1
6819b1a3bbeSMarouene Boubakri
6829b1a3bbeSMarouene Boubakri	/*
6839b1a3bbeSMarouene Boubakri	 * Save kernel stack pointer to ensure that
6845c718542SAlvin Chang	 * exception_from_user() uses correct stack pointer.
6859b1a3bbeSMarouene Boubakri	 */
6869b1a3bbeSMarouene Boubakri
6879b1a3bbeSMarouene Boubakri	store_xregs s0, THREAD_CTX_KERN_SP, REG_SP
6889b1a3bbeSMarouene Boubakri	/*
689b5bb30b3SAlvin Chang	 * Save thread_core_local in xSCRATCH to ensure that thread_trap_vect()
690b5bb30b3SAlvin Chang	 * uses correct core local structure.
6919b1a3bbeSMarouene Boubakri	 */
692b5bb30b3SAlvin Chang	csrw	CSR_XSCRATCH, tp
693b5bb30b3SAlvin Chang
6948a2c36cdSAlvin Chang	/* Move struct thread_ctx_regs *regs to sp to reduce code size */
6958a2c36cdSAlvin Chang	mv	sp, a0
6968a2c36cdSAlvin Chang
697dfa05b24SAlvin Chang	/* Set exception return PC */
6988a2c36cdSAlvin Chang	load_xregs sp, THREAD_CTX_REG_EPC, REG_S0
699dfa05b24SAlvin Chang	csrw	CSR_XEPC, s0
7009b1a3bbeSMarouene Boubakri	/* Set user status */
7018a2c36cdSAlvin Chang	load_xregs sp, THREAD_CTX_REG_STATUS, REG_S0
7029b1a3bbeSMarouene Boubakri	csrw	CSR_XSTATUS, s0
7039f715794SAlvin Chang	/* Set user ie */
7049f715794SAlvin Chang	load_xregs sp, THREAD_CTX_REG_IE, REG_S0
7059f715794SAlvin Chang	csrw	CSR_XIE, s0
7069b1a3bbeSMarouene Boubakri	/* Load the rest of the general purpose registers */
7078a2c36cdSAlvin Chang	load_xregs sp, THREAD_CTX_REG_RA, REG_RA
7088a2c36cdSAlvin Chang	load_xregs sp, THREAD_CTX_REG_GP, REG_GP
7098a2c36cdSAlvin Chang	load_xregs sp, THREAD_CTX_REG_TP, REG_TP
7108a2c36cdSAlvin Chang	load_xregs sp, THREAD_CTX_REG_T0, REG_T0, REG_T2
7118a2c36cdSAlvin Chang	load_xregs sp, THREAD_CTX_REG_S0, REG_S0, REG_S1
7128a2c36cdSAlvin Chang	load_xregs sp, THREAD_CTX_REG_A0, REG_A0, REG_A7
7138a2c36cdSAlvin Chang	load_xregs sp, THREAD_CTX_REG_S2, REG_S2, REG_S11
7148a2c36cdSAlvin Chang	load_xregs sp, THREAD_CTX_REG_T3, REG_T3, REG_T6
7158a2c36cdSAlvin Chang	load_xregs sp, THREAD_CTX_REG_SP, REG_SP /* sp must be last one */
7169b1a3bbeSMarouene Boubakri
7179b1a3bbeSMarouene Boubakri	/* Jump into user mode */
7189b1a3bbeSMarouene Boubakri	XRET
7199b1a3bbeSMarouene BoubakriEND_FUNC __thread_enter_user_mode
7209b1a3bbeSMarouene Boubakri
7219b1a3bbeSMarouene Boubakri/* void thread_resume(struct thread_ctx_regs *regs) */
7229b1a3bbeSMarouene BoubakriFUNC thread_resume , :
7238a2c36cdSAlvin Chang	/* Move struct thread_ctx_regs *regs to sp to reduce code size */
7248a2c36cdSAlvin Chang	mv	sp, a0
7258a2c36cdSAlvin Chang
72609653bcaSAlvin Chang	/* Restore epc */
7278a2c36cdSAlvin Chang	load_xregs sp, THREAD_CTX_REG_EPC, REG_T0
72809653bcaSAlvin Chang	csrw	CSR_XEPC, t0
72909653bcaSAlvin Chang	/* Restore status */
7308a2c36cdSAlvin Chang	load_xregs sp, THREAD_CTX_REG_STATUS, REG_T0
73109653bcaSAlvin Chang	csrw	CSR_XSTATUS, t0
7329f715794SAlvin Chang	/* Restore ie */
7339f715794SAlvin Chang	load_xregs sp, THREAD_CTX_REG_IE, REG_T0
7349f715794SAlvin Chang	csrw	CSR_XIE, t0
73509653bcaSAlvin Chang
73609653bcaSAlvin Chang	/* Check if previous privilege mode by status.SPP */
7379f715794SAlvin Chang	csrr	t0, CSR_XSTATUS
73809653bcaSAlvin Chang	b_if_prev_priv_is_u t0, 1f
73909653bcaSAlvin Chang	/* Set scratch as zero to indicate that we are in kernel mode */
74009653bcaSAlvin Chang	csrw	CSR_XSCRATCH, zero
74109653bcaSAlvin Chang	j	2f
74209653bcaSAlvin Chang1:
74309653bcaSAlvin Chang	/* Resume to U-mode, set scratch as tp to be used in the trap handler */
74409653bcaSAlvin Chang	csrw	CSR_XSCRATCH, tp
74509653bcaSAlvin Chang2:
74609653bcaSAlvin Chang	/* Restore all general-purpose registers */
7478a2c36cdSAlvin Chang	load_xregs sp, THREAD_CTX_REG_RA, REG_RA
7488a2c36cdSAlvin Chang	load_xregs sp, THREAD_CTX_REG_GP, REG_GP
7498a2c36cdSAlvin Chang	load_xregs sp, THREAD_CTX_REG_TP, REG_TP
7508a2c36cdSAlvin Chang	load_xregs sp, THREAD_CTX_REG_T0, REG_T0, REG_T2
7518a2c36cdSAlvin Chang	load_xregs sp, THREAD_CTX_REG_S0, REG_S0, REG_S1
7528a2c36cdSAlvin Chang	load_xregs sp, THREAD_CTX_REG_A0, REG_A0, REG_A7
7538a2c36cdSAlvin Chang	load_xregs sp, THREAD_CTX_REG_S2, REG_S2, REG_S11
7548a2c36cdSAlvin Chang	load_xregs sp, THREAD_CTX_REG_T3, REG_T3, REG_T6
7558a2c36cdSAlvin Chang	load_xregs sp, THREAD_CTX_REG_SP, REG_SP /* sp must be last one */
75609653bcaSAlvin Chang
75709653bcaSAlvin Chang	XRET
7589b1a3bbeSMarouene BoubakriEND_FUNC thread_resume
7598d5bae1cSAlvin Chang
7608d5bae1cSAlvin Chang/* void thread_foreign_interrupt_handler(struct thread_ctx_regs *regs) */
7618d5bae1cSAlvin ChangFUNC thread_foreign_interrupt_handler , :
7628d5bae1cSAlvin Chang	/* Update 32-bit core local flags */
7638d5bae1cSAlvin Chang	lw	s1, THREAD_CORE_LOCAL_FLAGS(tp)
7648d5bae1cSAlvin Chang	slli	s1, s1, THREAD_CLF_SAVED_SHIFT
7658d5bae1cSAlvin Chang	ori	s1, s1, (THREAD_CLF_TMP | THREAD_CLF_FIQ)
7668d5bae1cSAlvin Chang	sw	s1, THREAD_CORE_LOCAL_FLAGS(tp)
7678d5bae1cSAlvin Chang
7688d5bae1cSAlvin Chang	/*
7698d5bae1cSAlvin Chang	 * Mark current thread as suspended.
7708d5bae1cSAlvin Chang	 * a0 = THREAD_FLAGS_EXIT_ON_FOREIGN_INTR
7718d5bae1cSAlvin Chang	 * a1 = status
7728d5bae1cSAlvin Chang	 * a2 = epc
7738d5bae1cSAlvin Chang	 * thread_state_suspend(flags, status, pc)
7748d5bae1cSAlvin Chang	 */
7758d5bae1cSAlvin Chang	LDR	a1, THREAD_CTX_REG_STATUS(a0)
7768d5bae1cSAlvin Chang	LDR	a2, THREAD_CTX_REG_EPC(a0)
7778d5bae1cSAlvin Chang	li	a0, THREAD_FLAGS_EXIT_ON_FOREIGN_INTR
7788d5bae1cSAlvin Chang	call	thread_state_suspend
7798d5bae1cSAlvin Chang	/* Now return value a0 contains suspended thread ID. */
7808d5bae1cSAlvin Chang
7818d5bae1cSAlvin Chang	/* Update core local flags */
7828d5bae1cSAlvin Chang	lw	s1, THREAD_CORE_LOCAL_FLAGS(tp)
7838d5bae1cSAlvin Chang	srli	s1, s1, THREAD_CLF_SAVED_SHIFT
7848d5bae1cSAlvin Chang	ori	s1, s1, THREAD_CLF_TMP
7858d5bae1cSAlvin Chang	sw	s1, THREAD_CORE_LOCAL_FLAGS(tp)
7868d5bae1cSAlvin Chang
7878d5bae1cSAlvin Chang	/* Passing thread index in a0, and return to untrusted domain. */
7888d5bae1cSAlvin Chang	mv	a4, a0
7898d5bae1cSAlvin Chang	li	a0, TEEABI_OPTEED_RETURN_CALL_DONE
7908d5bae1cSAlvin Chang	li	a1, OPTEE_ABI_RETURN_RPC_FOREIGN_INTR
7918d5bae1cSAlvin Chang	li	a2, 0
7928d5bae1cSAlvin Chang	li	a3, 0
7938d5bae1cSAlvin Chang	li	a5, 0
7948d5bae1cSAlvin Chang	j	thread_return_to_udomain
7958d5bae1cSAlvin ChangEND_FUNC thread_foreign_interrupt_handler
796