1 /*
2  * FreeRTOS Kernel V11.1.0
3  * Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
4  *
5  * SPDX-License-Identifier: MIT
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a copy of
8  * this software and associated documentation files (the "Software"), to deal in
9  * the Software without restriction, including without limitation the rights to
10  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
11  * the Software, and to permit persons to whom the Software is furnished to do so,
12  * subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included in all
15  * copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
19  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
20  * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  *
24  * https://www.FreeRTOS.org
25  * https://github.com/FreeRTOS
26  *
27  */
28 
29 #include "FreeRTOSConfig.h"
30 
31 #define portCONTEXT_SIZE                160
32 #define portEPC_STACK_LOCATION          152
33 #define portSTATUS_STACK_LOCATION       156
34 #define portFPCSR_STACK_LOCATION        0
35 #define portTASK_HAS_FPU_STACK_LOCATION     0
36 #define portFPU_CONTEXT_SIZE            264
37 
38 /******************************************************************/
39 .macro  portSAVE_FPU_REGS    offset, base
40     /* Macro to assist with saving just the FPU registers to the
41      * specified address and base offset,
42      * offset is a constant, base is the base pointer register  */
43 
44     sdc1        $f31, \offset + 248(\base)
45     sdc1        $f30, \offset + 240(\base)
46     sdc1        $f29, \offset + 232(\base)
47     sdc1        $f28, \offset + 224(\base)
48     sdc1        $f27, \offset + 216(\base)
49     sdc1        $f26, \offset + 208(\base)
50     sdc1        $f25, \offset + 200(\base)
51     sdc1        $f24, \offset + 192(\base)
52     sdc1        $f23, \offset + 184(\base)
53     sdc1        $f22, \offset + 176(\base)
54     sdc1        $f21, \offset + 168(\base)
55     sdc1        $f20, \offset + 160(\base)
56     sdc1        $f19, \offset + 152(\base)
57     sdc1        $f18, \offset + 144(\base)
58     sdc1        $f17, \offset + 136(\base)
59     sdc1        $f16, \offset + 128(\base)
60     sdc1        $f15, \offset + 120(\base)
61     sdc1        $f14, \offset + 112(\base)
62     sdc1        $f13, \offset + 104(\base)
63     sdc1        $f12, \offset + 96(\base)
64     sdc1        $f11, \offset + 88(\base)
65     sdc1        $f10, \offset + 80(\base)
66     sdc1        $f9, \offset + 72(\base)
67     sdc1        $f8, \offset + 64(\base)
68     sdc1        $f7, \offset + 56(\base)
69     sdc1        $f6, \offset + 48(\base)
70     sdc1        $f5, \offset + 40(\base)
71     sdc1        $f4, \offset + 32(\base)
72     sdc1        $f3, \offset + 24(\base)
73     sdc1        $f2, \offset + 16(\base)
74     sdc1        $f1, \offset + 8(\base)
75     sdc1        $f0, \offset + 0(\base)
76 
77     .endm
78 
79 /******************************************************************/
80 .macro  portLOAD_FPU_REGS    offset, base
81     /* Macro to assist with loading just the FPU registers from the
82      * specified address and base offset, offset is a constant,
83      * base is the base pointer register  */
84 
85     ldc1        $f0, \offset + 0(\base)
86     ldc1        $f1, \offset + 8(\base)
87     ldc1        $f2, \offset + 16(\base)
88     ldc1        $f3, \offset + 24(\base)
89     ldc1        $f4, \offset + 32(\base)
90     ldc1        $f5, \offset + 40(\base)
91     ldc1        $f6, \offset + 48(\base)
92     ldc1        $f7, \offset + 56(\base)
93     ldc1        $f8, \offset + 64(\base)
94     ldc1        $f9, \offset + 72(\base)
95     ldc1        $f10, \offset + 80(\base)
96     ldc1        $f11, \offset + 88(\base)
97     ldc1        $f12, \offset + 96(\base)
98     ldc1        $f13, \offset + 104(\base)
99     ldc1        $f14, \offset + 112(\base)
100     ldc1        $f15, \offset + 120(\base)
101     ldc1        $f16, \offset + 128(\base)
102     ldc1        $f17, \offset + 136(\base)
103     ldc1        $f18, \offset + 144(\base)
104     ldc1        $f19, \offset + 152(\base)
105     ldc1        $f20, \offset + 160(\base)
106     ldc1        $f21, \offset + 168(\base)
107     ldc1        $f22, \offset + 176(\base)
108     ldc1        $f23, \offset + 184(\base)
109     ldc1        $f24, \offset + 192(\base)
110     ldc1        $f25, \offset + 200(\base)
111     ldc1        $f26, \offset + 208(\base)
112     ldc1        $f27, \offset + 216(\base)
113     ldc1        $f28, \offset + 224(\base)
114     ldc1        $f29, \offset + 232(\base)
115     ldc1        $f30, \offset + 240(\base)
116     ldc1        $f31, \offset + 248(\base)
117 
118     .endm
119 
120 /******************************************************************/
121 .macro  portSAVE_CONTEXT
122 
123     /* Make room for the context. First save the current status so it can be
124     manipulated, and the cause and EPC registers so their original values are
125     captured. */
126     mfc0        k0, _CP0_CAUSE
127     addiu       sp, sp, -portCONTEXT_SIZE
128 
129     #if ( __mips_hard_float == 1 ) && ( configUSE_TASK_FPU_SUPPORT == 1 )
130         /* Test if we are already using the system stack. Only tasks may use the
131         FPU so if we are already in a nested interrupt then the FPU context does
132         not require saving. */
133         la          k1, uxInterruptNesting
134         lw          k1, 0(k1)
135         bne         k1, zero, 2f
136         nop
137 
138         /* Test if the current task needs the FPU context saving. */
139         la          k1, ulTaskHasFPUContext
140         lw          k1, 0(k1)
141         beq         k1, zero, 1f
142         nop
143 
144         /* Adjust the stack to account for the additional FPU context.*/
145         addiu       sp, sp, -portFPU_CONTEXT_SIZE
146 
147     1:
148         /* Save the ulTaskHasFPUContext flag. */
149         sw          k1, portTASK_HAS_FPU_STACK_LOCATION(sp)
150 
151     2:
152     #endif
153 
154     mfc0        k1, _CP0_STATUS
155 
156     /* Also save s7, s6 and s5 so they can be used.  Any nesting interrupts
157     should maintain the values of these registers across the ISR. */
158     sw          s7, 48(sp)
159     sw          s6, 44(sp)
160     sw          s5, 40(sp)
161     sw          k1, portSTATUS_STACK_LOCATION(sp)
162 
163     /* Prepare to enable interrupts above the current priority. */
164     srl         k0, k0, 0xa
165     ins         k1, k0, 10, 7
166     srl         k0, k0, 0x7 /* This copies the MSB of the IPL, but it would be an error if it was set anyway. */
167     ins         k1, k0, 18, 1
168     ins         k1, zero, 1, 4
169 
170     /* s5 is used as the frame pointer. */
171     add         s5, zero, sp
172 
173     /* Check the nesting count value. */
174     la          k0, uxInterruptNesting
175     lw          s6, (k0)
176 
177     /* If the nesting count is 0 then swap to the the system stack, otherwise
178     the system stack is already being used. */
179     bne         s6, zero, 1f
180     nop
181 
182     /* Swap to the system stack. */
183     la          sp, xISRStackTop
184     lw          sp, (sp)
185 
186     /* Increment and save the nesting count. */
187 1:  addiu       s6, s6, 1
188     sw          s6, 0(k0)
189 
190     /* s6 holds the EPC value, this is saved after interrupts are re-enabled. */
191     mfc0        s6, _CP0_EPC
192 
193     /* Re-enable interrupts. */
194     mtc0        k1, _CP0_STATUS
195 
196     /* Save the context into the space just created.  s6 is saved again
197     here as it now contains the EPC value.  No other s registers need be
198     saved. */
199     sw          ra, 120(s5)
200     sw          s8, 116(s5)
201     sw          t9, 112(s5)
202     sw          t8, 108(s5)
203     sw          t7, 104(s5)
204     sw          t6, 100(s5)
205     sw          t5, 96(s5)
206     sw          t4, 92(s5)
207     sw          t3, 88(s5)
208     sw          t2, 84(s5)
209     sw          t1, 80(s5)
210     sw          t0, 76(s5)
211     sw          a3, 72(s5)
212     sw          a2, 68(s5)
213     sw          a1, 64(s5)
214     sw          a0, 60(s5)
215     sw          v1, 56(s5)
216     sw          v0, 52(s5)
217     sw          s6, portEPC_STACK_LOCATION(s5)
218     sw          $1, 16(s5)
219 
220     /* Save the AC0, AC1, AC2, AC3 registers from the DSP.  s6 is used as a
221     scratch register. */
222     mfhi        s6, $ac1
223     sw          s6, 128(s5)
224     mflo        s6, $ac1
225     sw          s6, 124(s5)
226 
227     mfhi        s6, $ac2
228     sw          s6, 136(s5)
229     mflo        s6, $ac2
230     sw          s6, 132(s5)
231 
232     mfhi        s6, $ac3
233     sw          s6, 144(s5)
234     mflo        s6, $ac3
235     sw          s6, 140(s5)
236 
237     /* Save the DSP Control register */
238     rddsp       s6
239     sw          s6, 148(s5)
240 
241     /* ac0 is done separately to match the MX port. */
242     mfhi        s6, $ac0
243     sw          s6, 12(s5)
244     mflo        s6, $ac0
245     sw          s6, 8(s5)
246 
247     /* Save the FPU context if the nesting count was zero. */
248     #if ( __mips_hard_float == 1 ) && ( configUSE_TASK_FPU_SUPPORT == 1 )
249         la          s6, uxInterruptNesting
250         lw          s6, 0(s6)
251         addiu       s6, s6, -1
252         bne         s6, zero, 1f
253         nop
254 
255         /* Test if the current task needs the FPU context saving. */
256         lw          s6, portTASK_HAS_FPU_STACK_LOCATION(s5)
257         beq         s6, zero, 1f
258         nop
259 
260         /* Save the FPU registers. */
261         portSAVE_FPU_REGS ( portCONTEXT_SIZE + 8 ), s5
262 
263         /* Save the FPU status register */
264         cfc1        s6, $f31
265         sw          s6, (portCONTEXT_SIZE + portFPCSR_STACK_LOCATION)(s5)
266 
267         1:
268     #endif
269 
270     /* Update the task stack pointer value if nesting is zero. */
271     la          s6, uxInterruptNesting
272     lw          s6, (s6)
273     addiu       s6, s6, -1
274     bne         s6, zero, 1f
275     nop
276 
277     /* Save the stack pointer. */
278     la          s6, uxSavedTaskStackPointer
279     sw          s5, (s6)
280 1:
281     .endm
282 
283 /******************************************************************/
284 .macro  portRESTORE_CONTEXT
285 
286     /* Restore the stack pointer from the TCB.  This is only done if the
287     nesting count is 1. */
288     la          s6, uxInterruptNesting
289     lw          s6, (s6)
290     addiu       s6, s6, -1
291     bne         s6, zero, 1f
292     nop
293     la          s6, uxSavedTaskStackPointer
294     lw          s5, (s6)
295 
296     #if ( __mips_hard_float == 1 ) && ( configUSE_TASK_FPU_SUPPORT == 1 )
297         /* Restore the FPU context if required. */
298         lw          s6, portTASK_HAS_FPU_STACK_LOCATION(s5)
299         beq         s6, zero, 1f
300         nop
301 
302         /* Restore the FPU registers. */
303         portLOAD_FPU_REGS   ( portCONTEXT_SIZE + 8 ), s5
304 
305         /* Restore the FPU status register. */
306         lw          s6, ( portCONTEXT_SIZE + portFPCSR_STACK_LOCATION )(s5)
307         ctc1        s6, $f31
308     #endif
309 
310 1:
311 
312     /* Restore the context. */
313     lw          s6, 128(s5)
314     mthi        s6, $ac1
315     lw          s6, 124(s5)
316     mtlo        s6, $ac1
317 
318     lw          s6, 136(s5)
319     mthi        s6, $ac2
320     lw          s6, 132(s5)
321     mtlo        s6, $ac2
322 
323     lw          s6, 144(s5)
324     mthi        s6, $ac3
325     lw          s6, 140(s5)
326     mtlo        s6, $ac3
327 
328     /* Restore DSPControl. */
329     lw          s6, 148(s5)
330     wrdsp       s6
331 
332     lw          s6, 8(s5)
333     mtlo        s6, $ac0
334     lw          s6, 12(s5)
335     mthi        s6, $ac0
336     lw          $1, 16(s5)
337 
338     /* s6 is loaded as it was used as a scratch register and therefore saved
339     as part of the interrupt context. */
340     lw          s7, 48(s5)
341     lw          s6, 44(s5)
342     lw          v0, 52(s5)
343     lw          v1, 56(s5)
344     lw          a0, 60(s5)
345     lw          a1, 64(s5)
346     lw          a2, 68(s5)
347     lw          a3, 72(s5)
348     lw          t0, 76(s5)
349     lw          t1, 80(s5)
350     lw          t2, 84(s5)
351     lw          t3, 88(s5)
352     lw          t4, 92(s5)
353     lw          t5, 96(s5)
354     lw          t6, 100(s5)
355     lw          t7, 104(s5)
356     lw          t8, 108(s5)
357     lw          t9, 112(s5)
358     lw          s8, 116(s5)
359     lw          ra, 120(s5)
360 
361     /* Protect access to the k registers, and others. */
362     di
363     ehb
364 
365     /* Decrement the nesting count. */
366     la          k0, uxInterruptNesting
367     lw          k1, (k0)
368     addiu       k1, k1, -1
369     sw          k1, 0(k0)
370 
371     #if ( __mips_hard_float == 1 ) && ( configUSE_TASK_FPU_SUPPORT == 1 )
372         /* If the nesting count is now zero then the FPU context may be restored. */
373         bne         k1, zero, 1f
374         nop
375 
376         /* Restore the value of ulTaskHasFPUContext */
377         la          k0, ulTaskHasFPUContext
378         lw          k1, 0(s5)
379         sw          k1, 0(k0)
380 
381         /* If the task does not have an FPU context then adjust the stack normally. */
382         beq         k1, zero, 1f
383         nop
384 
385         /* Restore the STATUS and EPC registers */
386         lw          k0, portSTATUS_STACK_LOCATION(s5)
387         lw          k1, portEPC_STACK_LOCATION(s5)
388 
389         /* Leave the stack in its original state.  First load sp from s5, then
390         restore s5 from the stack. */
391         add         sp, zero, s5
392         lw          s5, 40(sp)
393 
394         /* Adjust the stack pointer to remove the FPU context */
395         addiu       sp, sp, portFPU_CONTEXT_SIZE
396         beq         zero, zero, 2f
397         nop
398 
399         1:  /* Restore the STATUS and EPC registers */
400         lw          k0, portSTATUS_STACK_LOCATION(s5)
401         lw          k1, portEPC_STACK_LOCATION(s5)
402 
403         /* Leave the stack in its original state.  First load sp from s5, then
404         restore s5 from the stack. */
405         add         sp, zero, s5
406         lw          s5, 40(sp)
407 
408         2:  /* Adjust the stack pointer */
409         addiu       sp, sp, portCONTEXT_SIZE
410 
411     #else
412 
413         /* Restore the frame when there is no hardware FP support. */
414         lw          k0, portSTATUS_STACK_LOCATION(s5)
415         lw          k1, portEPC_STACK_LOCATION(s5)
416 
417         /* Leave the stack in its original state.  First load sp from s5, then
418         restore s5 from the stack. */
419         add         sp, zero, s5
420         lw          s5, 40(sp)
421 
422         addiu       sp, sp, portCONTEXT_SIZE
423 
424     #endif // ( __mips_hard_float == 1 ) && ( configUSE_TASK_FPU_SUPPORT == 1 )
425 
426     mtc0        k0, _CP0_STATUS
427     mtc0        k1, _CP0_EPC
428     ehb
429     eret
430     nop
431 
432     .endm
433