1/*
2 * Copyright (c) 2018 Synopsys.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7#include <offsets_short.h>
8#include <zephyr/toolchain.h>
9#include <zephyr/linker/sections.h>
10#include <zephyr/kernel_structs.h>
11#include <zephyr/arch/cpu.h>
12#include <zephyr/syscall.h>
13#include <swap_macros.h>
14#include <v2/irq.h>
15
16.macro clear_scratch_regs
17	mov_s r1, 0
18	mov_s r2, 0
19	mov_s r3, 0
20	mov_s r4, 0
21	mov_s r5, 0
22	mov_s r6, 0
23	mov_s r7, 0
24	mov_s r8, 0
25	mov_s r9, 0
26	mov_s r10, 0
27	mov_s r11, 0
28	mov_s r12, 0
29.endm
30
31.macro clear_callee_regs
32	mov_s r25, 0
33	mov_s r24, 0
34	mov_s r23, 0
35	mov_s r22, 0
36	mov_s r21, 0
37	mov_s r20, 0
38	mov_s r19, 0
39	mov_s r18, 0
40	mov_s r17, 0
41	mov_s r16, 0
42
43	mov_s r15, 0
44	mov_s r14, 0
45	mov_s r13, 0
46.endm
47
48GTEXT(z_arc_userspace_enter)
49GTEXT(_arc_do_syscall)
50GTEXT(z_user_thread_entry_wrapper)
51GTEXT(arch_user_string_nlen)
52GTEXT(z_arc_user_string_nlen_fault_start)
53GTEXT(z_arc_user_string_nlen_fault_end)
54GTEXT(z_arc_user_string_nlen_fixup)
55
56/**
57 * @brief Wrapper for z_thread_entry in the case of user thread
58 *
59 * The init parameters are in privileged stack
60 */
61SECTION_FUNC(TEXT, z_user_thread_entry_wrapper)
62	seti _ARC_V2_INIT_IRQ_LOCK_KEY
63	pop_s r3
64	pop_s r2
65	pop_s r1
66	pop_s r0
67/* the start of user sp is in r5 */
68	pop r5
69/* start of privilege stack in blink */
70	mov_s blink, sp
71
72	st.aw r0, [r5, -4]
73	st.aw r1, [r5, -4]
74	st.aw r2, [r5, -4]
75	st.aw r3, [r5, -4]
76
77/*
78 * when CONFIG_INIT_STACKS is enable, stack will be initialized
79 * in z_new_thread_init.
80 */
81	j _arc_go_to_user_space
82
83/**
84 *
85 * User space entry function
86 *
87 * This function is the entry point to user mode from privileged execution.
88 * The conversion is one way, and threads which transition to user mode do
89 * not transition back later, unless they are doing system calls.
90 *
91 */
92SECTION_FUNC(TEXT, z_arc_userspace_enter)
93	/*
94	 * In ARCv2, the U bit can only be set through exception return
95	 */
96	/* disable stack checking as the stack should be initialized */
97	_disable_stack_checking blink
98
99	/* the end of user stack in r5 */
100	add r5, r4, r5
101	/* get start of privilege stack, r6 points to current thread */
102	ld blink, [r6, _thread_offset_to_priv_stack_start]
103	add blink, blink, CONFIG_PRIVILEGED_STACK_SIZE
104
105	mov_s sp, r5
106
107	push_s r0
108	push_s r1
109	push_s r2
110	push_s r3
111
112	mov r5, sp /* skip r0, r1, r2, r3 */
113
114/* to avoid the leakage of kernel info, the thread stack needs to be
115 * re-initialized
116 */
117#ifdef CONFIG_INIT_STACKS
118	mov_s r0, 0xaaaaaaaa
119#else
120	mov_s r0, 0x0
121#endif
122_clear_user_stack:
123	st.ab r0, [r4, 4]
124	cmp r4, r5
125	jlt _clear_user_stack
126
127/* reload the stack checking regs as the original kernel stack
128 * becomes user stack
129 */
130#ifdef CONFIG_ARC_STACK_CHECKING
131/* current thread in r6, SMP case is also considered */
132	mov r2, r6
133
134	_load_stack_check_regs
135
136	_enable_stack_checking r0
137#endif
138
139/* the following codes are used to switch from kernel mode
140 * to user mode by fake exception, because U bit can only be set
141 * by exception
142 */
143_arc_go_to_user_space:
144	lr r0, [_ARC_V2_STATUS32]
145	bset r0, r0, _ARC_V2_STATUS32_U_BIT
146
147	mov_s r1, z_thread_entry_wrapper1
148
149	sr r0, [_ARC_V2_ERSTATUS]
150	sr r1, [_ARC_V2_ERET]
151
152	/* fake exception return */
153	lr r0, [_ARC_V2_STATUS32]
154	bset r0, r0, _ARC_V2_STATUS32_AE_BIT
155	kflag r0
156
157/* when exception returns from kernel to user, sp and _ARC_V2_USER_SP
158 * /_ARC_V2_SECU_SP will be switched
159 */
160#if defined(CONFIG_ARC_HAS_SECURE) && defined(CONFIG_ARC_SECURE_FIRMWARE)
161	lr r0, [_ARC_V2_SEC_STAT]
162	/* the mode returns from exception return is secure mode */
163	bset r0, r0, 31
164	sr r0, [_ARC_V2_ERSEC_STAT]
165	sr r5, [_ARC_V2_SEC_U_SP]
166#else
167	sr r5, [_ARC_V2_USER_SP]
168#endif
169	mov_s sp, blink
170
171	mov_s r0, 0
172
173	clear_callee_regs
174
175	clear_scratch_regs
176
177	mov fp, 0
178	mov r29, 0
179	mov r30, 0
180	mov blink, 0
181
182	rtie
183
184/**
185 *
186 * Userspace system call function
187 *
188 * This function is used to do system calls from unprivileged code.  This
189 * function is responsible for the following:
190 * 1) Dispatching the system call
191 * 2) Restoring stack and calling back to the caller of the system call
192 *
193 */
194SECTION_FUNC(TEXT, _arc_do_syscall)
195	/*
196	 * r0-r5: arg1-arg6, r6 is call id which is already checked in
197	 * trap_s handler, r7 is the system call stack frame pointer
198	 * need to recover r0, r1, r2 because they will be modified in
199	 * _create_irq_stack_frame. If a specific syscall frame (different
200	 * with irq stack frame) is defined, the cover of r0, r1, r2 can be
201	 * optimized.
202	 */
203	ld_s r0, [sp, ___isf_t_r0_OFFSET]
204	ld_s r1, [sp, ___isf_t_r1_OFFSET]
205	ld_s r2, [sp, ___isf_t_r2_OFFSET]
206
207	mov r7, sp
208
209	mov_s blink, _k_syscall_table
210	ld.as r6, [blink, r6]
211
212	jl [r6]
213
214	/* save return value */
215	st_s r0, [sp, ___isf_t_r0_OFFSET]
216
217	mov r29, 0
218	mov r30, 0
219
220	/* through fake exception return, go back to the caller */
221	lr r0, [_ARC_V2_STATUS32]
222	bset r0, r0, _ARC_V2_STATUS32_AE_BIT
223	kflag r0
224
225
226#ifdef CONFIG_ARC_SECURE_FIRMWARE
227	ld_s r0, [sp, ___isf_t_sec_stat_OFFSET]
228	sr r0,[_ARC_V2_ERSEC_STAT]
229#endif
230	ld_s r0, [sp, ___isf_t_status32_OFFSET]
231	sr r0,[_ARC_V2_ERSTATUS]
232
233	ld_s r0, [sp, ___isf_t_pc_OFFSET] /* eret into pc */
234	sr r0,[_ARC_V2_ERET]
235
236	_pop_irq_stack_frame
237
238	rtie
239
240/*
241 * size_t arch_user_string_nlen(const char *s, size_t maxsize, int *err_arg)
242 */
243SECTION_FUNC(TEXT, arch_user_string_nlen)
244	/* int err; */
245	sub_s sp,sp,0x4
246
247	/* Initial error value (-1 failure), store at [sp,0] */
248	mov_s r3, -1
249	st_s r3, [sp, 0]
250
251	/* Loop setup.
252	 * r12 (position locator) = s - 1
253	 * r0 (length counter return value)) = 0
254	 * lp_count = maxsize + 1
255	 * */
256	sub r12, r0, 0x1
257	mov_s r0, 0
258	add_s r1, r1, 1
259	mov lp_count, r1
260
261strlen_loop:
262z_arc_user_string_nlen_fault_start:
263	/* is the byte at ++r12 a NULL? if so, we're done. Might fault! */
264	ldb.aw r1, [r12, 1]
265
266z_arc_user_string_nlen_fault_end:
267	brne_s r1, 0, not_null
268
269strlen_done:
270	/* Success, set err to 0 */
271	mov_s r1, 0
272	st_s r1, [sp, 0]
273
274z_arc_user_string_nlen_fixup:
275	/* *err_arg = err; Pop stack and return */
276	ld_s r1, [sp, 0]
277	add_s sp, sp, 4
278	j_s.d [blink]
279	st_s r1, [r2, 0]
280
281not_null:
282	/* check if we've hit the maximum, if so we're done. */
283	brne.d.nt lp_count, 0x1, inc_len
284	sub lp_count, lp_count, 0x1
285	b_s strlen_done
286
287inc_len:
288	/* increment length measurement, loop again */
289	add_s r0, r0, 1
290	b_s strlen_loop
291