1/*
2 * Copyright (c) 2006-2020, RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date           Author       Notes
8 * 2018-12-10     Jesven       first version
9 * 2021-02-03     lizhirui     port to riscv64
10 * 2021-02-19     lizhirui     port to new version of rt-smart
11 * 2022-11-08     Wangxiaoyao  Cleanup codes;
12 *                             Support new context switch
13 * 2023-07-16     Shell        Move part of the codes to C from asm in signal handling
14 */
15
16#include "rtconfig.h"
17
18#ifndef __ASSEMBLY__
19#define __ASSEMBLY__
20#endif /* __ASSEMBLY__ */
21
22#include "cpuport.h"
23#include "encoding.h"
24#include "stackframe.h"
25#include "asm-generic.h"
26
27.section      .text.lwp
28
29/*
30 * void arch_start_umode(args, text, ustack, kstack);
31 */
32.global arch_start_umode
33.type arch_start_umode, % function
34arch_start_umode:
35    // load kstack for user process
36    csrw sscratch, a3
37    li t0, SSTATUS_SPP | SSTATUS_SIE    // set as user mode, close interrupt
38    csrc sstatus, t0
39    li t0, SSTATUS_SPIE // enable interrupt when return to user mode
40    csrs sstatus, t0
41
42    csrw sepc, a1
43    mv sp, a2
44    sret//enter user mode
45
46/*
47 * void arch_crt_start_umode(args, text, ustack, kstack);
48 */
49.global arch_crt_start_umode
50.type arch_crt_start_umode, % function
51arch_crt_start_umode:
52    li t0, SSTATUS_SPP | SSTATUS_SIE    // set as user mode, close interrupt
53    csrc sstatus, t0
54    li t0, SSTATUS_SPIE // enable interrupt when return to user mode
55    csrs sstatus, t0
56
57    csrw sepc, a1
58    mv s0, a0
59    mv s1, a1
60    mv s2, a2
61    mv s3, a3
62    mv a0, s2
63    call lwp_copy_return_code_to_user_stack
64    mv a0, s2
65    call lwp_fix_sp
66    mv sp, a0//user_sp
67    mv ra, a0//return address
68    mv a0, s0//args
69
70    csrw sscratch, s3
71    sret//enter user mode
72
73/**
74 * Unify exit point from kernel mode to enter user space
75 * we handle following things here:
76 * 1. restoring user mode debug state (not support yet)
77 * 2. handling thread's exit request
78 * 3. handling POSIX signal (skipped for signal quit path)
79 * 4. restoring user context
80 * 5. jump to user mode
81 */
82.global arch_ret_to_user
83arch_ret_to_user:
84    li s0, 1   // flag=1 (normal path)
85    j arch_ret_to_user_impl
86
87.global arch_signal_quit_ret_to_user
88arch_signal_quit_ret_to_user:
89    li s0, 0   // flag=0 (signal quit path)
90
91arch_ret_to_user_impl:
92    // TODO: we don't support kernel gdb server in risc-v yet
93    // so we don't check debug state here and handle debugging bussiness
94
95    call lwp_check_exit_request
96    beqz a0, 1f
97    mv a0, x0
98    call sys_exit
99
1001:
101    // Skip signal handling if coming from arch_signal_quit
102    beqz s0, ret_to_user_exit
103    mv a0, sp
104    call lwp_thread_signal_catch
105
106ret_to_user_exit:
107    RESTORE_ALL
108    // `RESTORE_ALL` also reset sp to user sp, and setup sscratch
109    sret
110
111/**
112 * Restore user context from exception frame stroraged in ustack
113 * And handle pending signals;
114 */
115arch_signal_quit:
116    LOAD a0, FRAME_OFF_SP(sp)
117    call arch_signal_ucontext_restore
118
119    /* reset kernel sp to the stack */
120    addi sp, sp, CTX_REG_NR * REGBYTES
121    STORE sp, FRAME_OFF_SP(a0)
122    /* return value is user sp */
123    mv sp, a0
124
125    /* restore user sp before enter trap */
126    addi a0, sp, CTX_REG_NR * REGBYTES
127    csrw sscratch, a0
128
129
130    RESTORE_ALL
131    SAVE_ALL
132    j arch_signal_quit_ret_to_user
133
134/**
135 * rt_noreturn
136 * void arch_thread_signal_enter(
137 *      int signo,                      -> a0
138 *      siginfo_t *psiginfo,            -> a1
139 *      void *exp_frame,                -> a2
140 *      void *entry_uaddr,              -> a3
141 *      lwp_sigset_t *save_sig_mask,    -> a4
142 *      )
143 */
144.global arch_thread_signal_enter
145arch_thread_signal_enter:
146    mv s3, a2
147    mv s2, a0
148    mv s1, a3
149
150    LOAD t0, FRAME_OFF_SP(a2)
151    mv a3, t0
152    call arch_signal_ucontext_save
153
154    /** restore kernel sp */
155    addi sp, s3, CTX_REG_NR * REGBYTES
156
157    /**
158     * set regiter RA to user signal handler
159     * set sp to user sp & save kernel sp in sscratch
160     */
161    mv ra, a0
162    csrw sscratch, sp
163    mv sp, a0
164
165    /**
166     * s1 is signal_handler,
167     * s1 = !s1 ? lwp_sigreturn : s1;
168     */
169    bnez s1, 1f
170    mv s1, ra
171
1721:
173    /* enter user mode and enable interrupt when return to user mode */
174    li t0, SSTATUS_SPP
175    csrc sstatus, t0
176    li t0, SSTATUS_SPIE
177    csrs sstatus, t0
178
179    /* sepc <- signal_handler */
180    csrw sepc, s1
181    /* a0 <- signal id */
182    mv a0, s2
183    /* a1 <- siginfo */
184    add a1, sp, 16
185    /* dummy a2 */
186    mv a2, a1
187
188    /* restore user GP */
189    LOAD gp, FRAME_OFF_GP(s3)
190
191    /* restore user TP */
192    LOAD tp, FRAME_OFF_TP(s3)
193
194    /**
195     * handler(signo, psi, ucontext);
196     */
197    sret
198
199.align 3
200lwp_debugreturn:
201    li a7, 0xff
202    ecall
203
204.align 3
205.global lwp_sigreturn
206lwp_sigreturn:
207    li a7, 0xfe
208    ecall
209
210.align 3
211lwp_sigreturn_end:
212
213.align 3
214.global lwp_thread_return
215lwp_thread_return:
216    li a0, 0
217    li a7, 1
218    ecall
219
220.align 3
221.global lwp_thread_return_end
222lwp_thread_return_end:
223
224.globl arch_get_tidr
225arch_get_tidr:
226    mv a0, tp
227    ret
228
229.global arch_set_thread_area
230arch_set_thread_area:
231.globl arch_set_tidr
232arch_set_tidr:
233    mv tp, a0
234    ret
235
236.global arch_clone_exit
237.global arch_fork_exit
238arch_fork_exit:
239arch_clone_exit:
240    j arch_syscall_exit
241
242START_POINT(syscall_entry)
243#ifndef ARCH_USING_NEW_CTX_SWITCH
244    //swap to thread kernel stack
245    csrr t0, sstatus
246    andi t0, t0, 0x100
247    beqz t0, __restore_sp_from_tcb
248
249__restore_sp_from_sscratch: // from kernel
250    csrr t0, sscratch
251    j __move_stack_context
252
253__restore_sp_from_tcb: // from user
254    jal rt_thread_self
255    jal get_thread_kernel_stack_top
256    mv t0, a0
257
258__move_stack_context:
259    mv t1, sp//src
260    mv sp, t0//switch stack
261    addi sp, sp, -CTX_REG_NR * REGBYTES
262    //copy context
263    li s0, CTX_REG_NR//cnt
264    mv t2, sp//dst
265
266copy_context_loop:
267    LOAD t0, 0(t1)
268    STORE t0, 0(t2)
269    addi s0, s0, -1
270    addi t1, t1, 8
271    addi t2, t2, 8
272    bnez s0, copy_context_loop
273#endif /* ARCH_USING_NEW_CTX_SWITCH */
274
275    /* fetch SYSCALL ID */
276    LOAD a7, 17 * REGBYTES(sp)
277    addi a7, a7, -0xfe
278    beqz a7, arch_signal_quit
279
280#ifdef ARCH_MM_MMU
281    /* save setting when syscall enter */
282    call  rt_thread_self
283    call  lwp_user_setting_save
284#endif
285
286    mv a0, sp
287    OPEN_INTERRUPT
288    call syscall_handler
289    j arch_syscall_exit
290START_POINT_END(syscall_entry)
291
292.global arch_syscall_exit
293arch_syscall_exit:
294    CLOSE_INTERRUPT
295
296    #if defined(ARCH_MM_MMU)
297        LOAD s0, FRAME_OFF_SSTATUS(sp)
298        andi s0, s0, 0x100
299        bnez s0, dont_ret_to_user
300        j arch_ret_to_user
301    #endif
302dont_ret_to_user:
303
304#ifdef ARCH_MM_MMU
305    /* restore setting when syscall exit */
306    call  rt_thread_self
307    call  lwp_user_setting_restore
308
309    /* after restore the reg `tp`, need modify context */
310    STORE tp, 4 * REGBYTES(sp)
311#endif
312
313    //restore context
314    RESTORE_ALL
315    csrw sscratch, zero
316    sret
317