summaryrefslogtreecommitdiff
path: root/libcpu/risc-v/common/context_gcc.S
blob: e0533c942c2abc6a8b20fc31ae364ad0ed9e2b3b (plain)
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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
/*
 * Copyright (c) 2006-2025, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2018-10-28     Bernard      The unify RISC-V porting implementation
 * 2018-12-27     Jesven       Add SMP support
 * 2021-02-02     lizhirui     Add userspace support
 * 2022-10-22     Shell        Support User mode RVV;
 *                             Trimming process switch context
 * 2025-04-20     GuEe-GUI     Support SMP
 */

#include "asm.h"
#include "csr.h"

#define REG_IDX(name)   RT_HW_SWITCH_CONTEXT_##name

.macro SAVE_REG reg, index
    REG_S   \reg, \index * SZREG(sp)
.endm

.macro LOAD_REG reg, index
    REG_L   \reg, \index * SZREG(sp)
.endm

.macro SAVE_CONTEXT
    addi        sp, sp, -(RT_HW_SWITCH_CONTEXT_SIZE * SZREG)
    SAVE_REG    tp, REG_IDX(TP)
    SAVE_REG    ra, REG_IDX(RA)
    SAVE_REG    s0, REG_IDX(S0)
    SAVE_REG    s1, REG_IDX(S1)
#ifndef ARCH_RISCV_32E
    SAVE_REG    s2, REG_IDX(S2)
    SAVE_REG    s3, REG_IDX(S3)
    SAVE_REG    s4, REG_IDX(S4)
    SAVE_REG    s5, REG_IDX(S5)
    SAVE_REG    s6, REG_IDX(S6)
    SAVE_REG    s7, REG_IDX(S7)
    SAVE_REG    s8, REG_IDX(S8)
    SAVE_REG    s9, REG_IDX(S9)
    SAVE_REG    s10, REG_IDX(S10)
    SAVE_REG    s11, REG_IDX(S11)
#endif
    csrr        s1, CSR_STATUS
    li          s0, (SR_PP)
    or          s1, s1, s0
    SAVE_REG    s1, REG_IDX(XSTATUS)
.endm

.macro RESTORE_CONTEXT
    LOAD_REG    s1, REG_IDX(XSTATUS)
    csrw        CSR_STATUS, s1
#ifndef ARCH_RISCV_32E
    LOAD_REG    s11, REG_IDX(S11)
    LOAD_REG    s10, REG_IDX(S10)
    LOAD_REG    s9, REG_IDX(S9)
    LOAD_REG    s8, REG_IDX(S8)
    LOAD_REG    s7, REG_IDX(S7)
    LOAD_REG    s6, REG_IDX(S6)
    LOAD_REG    s5, REG_IDX(S5)
    LOAD_REG    s4, REG_IDX(S4)
    LOAD_REG    s3, REG_IDX(S3)
    LOAD_REG    s2, REG_IDX(S2)
#endif
    LOAD_REG    s1, REG_IDX(S1)
    LOAD_REG    s0, REG_IDX(S0)
    LOAD_REG    ra, REG_IDX(RA)
    RESTORE_TP (REG_IDX(TP) * SZREG)
    addi        sp, sp, RT_HW_SWITCH_CONTEXT_SIZE * SZREG
    csrw        CSR_EPC, ra
.endm

    .section ".text","ax"

#ifdef RT_USING_SMP
/*
 * void rt_hw_context_switch_to(rt_ubase_t to, struct rt_thread *to_thread);
 * a0 --> to (thread stack)
 * a1 --> to_thread
 */
.globl rt_hw_context_switch_to
rt_hw_context_switch_to:
    REG_L   sp, (a0)

    /* Reserved to_thread */
    mv      s1, a1

    mv      a0, s1
    call    rt_cpus_lock_status_restore
#ifdef RT_USING_SMART
    mv      a0, s1
    call    lwp_user_setting_restore
#endif

    j       _context_switch_exit

/*
 * void rt_hw_context_switch(rt_ubase_t from, rt_ubase_t to, struct rt_thread *to_thread);
 * a0 --> from (from_thread stack)
 * a1 --> to (to_thread stack)
 * a2 --> to_thread
 */
.globl rt_hw_context_switch
rt_hw_context_switch:
    SAVE_CONTEXT
    mv      a3, sp
    REG_S   a3, (a0)            /* Store sp in preempted tasks TCB */
    REG_L   a0, (a1)            /* Get new task stack pointer */
    mv      sp, a0

    /* Backup thread self */
    mv      s1, a2

    mv      a0, s1
    call    rt_cpus_lock_status_restore
#ifdef RT_USING_SMART
    mv      a0, s1
    call    lwp_user_setting_restore
#endif
    j       _context_switch_exit

#define EXP_FRAME   t0
#define FROM_SPP    t1
#define TO_SPP      t2
#define TO_TCB      a4
#define TMP         a5

/*
 * void rt_hw_context_switch_interrupt(rt_ubase_t context, rt_ubase_t from, rt_ubase_t to, struct rt_thread *to_thread)
 * a0 --> interrupt context
 * a1 --> addr of from_thread's sp
 * a2 --> addr of to_thread's sp
 * a3 --> to_thread's tcb
 */
.globl rt_hw_context_switch_interrupt
rt_hw_context_switch_interrupt:
#ifdef RT_USING_DEBUG
    /* Debug frame for backtrace */
#if __riscv_xlen == 64
    addi    sp, sp, -(48)
    sd      ra, 40(sp)
    sd      s0, 32(sp)
#elif __riscv_xlen == 32
    addi    sp, sp, -(32)
    sw      ra, 28(sp)
    sw      s0, 24(sp)
#else
#error "Unexpected __riscv_xlen"
#endif
#endif /* RT_USING_DEBUG */

    /* We can discard all the previous ABI here */
    mv      EXP_FRAME, a0
    mv      FROM_SPP, a1
    mv      TO_SPP, a2
    mv      TO_TCB, a3

#ifdef RT_USING_SMART
    PUSH    EXP_FRAME
    PUSH    FROM_SPP
    PUSH    TO_SPP
    PUSH    TO_TCB

    call    rt_thread_self
    call    lwp_user_setting_save

    POP     TO_TCB
    POP     TO_SPP
    POP     FROM_SPP
    POP     EXP_FRAME
#endif /* RT_USING_SMART */

    /* Reset SP of from-thread */
    mv      sp, EXP_FRAME

    addi    sp, sp, -(RT_HW_SWITCH_CONTEXT_SIZE * SZREG)
    /* Resume according to the privilege mode saved in the exception frame. */
    la      ra, rt_hw_resume_interrupt
    /* Save the entire context */
    li      TMP, (K_XSTATUS_DEFAULT & ~SR_PIE)
    REG_S   TMP, REG_IDX(XSTATUS) * SZREG(sp)
    REG_S   ra, REG_IDX(RA) * SZREG(sp)

    /* Save SP of from-thread */
    mv      a0, sp
    REG_S   a0, (FROM_SPP)

    /* Setup SP to to-thread's */
    REG_L   a0, (TO_SPP)
    mv      sp, a0

#ifdef RT_USING_SMART
    PUSH    TO_TCB
#endif
    mv      a0, TO_TCB
    call    rt_cpus_lock_status_restore
#ifdef RT_USING_SMART
    POP     a0  /* TO_TCB */
    call    lwp_user_setting_restore
#endif
    j       _context_switch_exit

#else /* !RT_USING_SMP */
    .section ".data"

    .align RISCV_SZPTR
/**
 * @brief from thread used interrupt context switch
 */
.globl rt_interrupt_from_thread
rt_interrupt_from_thread:
    RISCV_PTR 0

/**
 * @brief to thread used interrupt context switch
 */
.globl rt_interrupt_to_thread
rt_interrupt_to_thread:
    RISCV_PTR 0

/**
 * @brief flag to indicate context switch in interrupt or not
 */
.globl rt_thread_switch_interrupt_flag
rt_thread_switch_interrupt_flag:
    RISCV_PTR 0

    .section ".text","ax"

/*
 * void rt_hw_context_switch_to(rt_ubase_t to);
 *
 * a0 --> to SP pointer
 */
.globl rt_hw_context_switch_to
rt_hw_context_switch_to:
    REG_L   sp, (a0)

    call    rt_thread_self
    mv      s1, a0

#ifdef RT_USING_SMART
    call    lwp_aspace_switch
#endif

    j       _context_switch_exit

/*
 * void rt_hw_context_switch(rt_ubase_t from, rt_ubase_t to);
 *
 * a0 --> from SP pointer
 * a1 --> to SP pointer
 *
 * It should only be used on local interrupt disable
 */
.globl rt_hw_context_switch
rt_hw_context_switch:
    SAVE_CONTEXT
    REG_S   sp, (a0)

    /* Restore to thread SP */
    REG_L   sp, (a1)

    /* Restore Address Space */
    call    rt_thread_self
    mv      s1, a0

#ifdef RT_USING_SMART
    call    lwp_aspace_switch
#endif

    j       _context_switch_exit

/*
 * void rt_hw_context_switch_interrupt(rt_ubase_t from, rt_ubase_t to, rt_thread_t from_thread, rt_thread_t to_thread)
 * a0 --> from_thread's sp
 * a1 --> to_thread's sp
 * a2 --> from_thread's tcb
 * a3 --> to_thread's tcb
 */
.globl rt_hw_context_switch_interrupt
rt_hw_context_switch_interrupt:
    la      t0, rt_thread_switch_interrupt_flag
    REG_L   t1, (t0)
    bne     t1, zero, _init_to_thread

    la      t1, rt_interrupt_from_thread
    REG_S   a0, (t1)

_init_to_thread:
    la      t1, rt_interrupt_to_thread
    REG_S   a1, (t1)

    li      t1, 1
    REG_S   t1, (t0)

    ret
#endif /* RT_USING_SMP */

.local _context_switch_exit
_context_switch_exit:
    RESTORE_CONTEXT
    XRET