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 132
32 #define portEPC_STACK_LOCATION  124
33 #define portSTATUS_STACK_LOCATION 128
34 
35 /******************************************************************/
36 .macro  portSAVE_CONTEXT
37 
38     /* Make room for the context. First save the current status so it can be
39     manipulated, and the cause and EPC registers so their original values are
40     captured. */
41     mfc0        k0, _CP0_CAUSE
42     addiu       sp, sp, -portCONTEXT_SIZE
43     mfc0        k1, _CP0_STATUS
44 
45     /* Also save s6 and s5 so they can be used.  Any nesting interrupts should
46     maintain the values of these registers across the ISR. */
47     sw          s6, 44(sp)
48     sw          s5, 40(sp)
49     sw          k1, portSTATUS_STACK_LOCATION(sp)
50 
51     /* Prepare to enable interrupts above the current priority. */
52     srl         k0, k0, 0xa
53     ins         k1, k0, 10, 6
54     ins         k1, zero, 1, 4
55 
56     /* s5 is used as the frame pointer. */
57     add         s5, zero, sp
58 
59     /* Check the nesting count value. */
60     la          k0, uxInterruptNesting
61     lw          s6, (k0)
62 
63     /* If the nesting count is 0 then swap to the the system stack, otherwise
64     the system stack is already being used. */
65     bne         s6, zero, 1f
66     nop
67 
68     /* Swap to the system stack. */
69     la          sp, xISRStackTop
70     lw          sp, (sp)
71 
72     /* Increment and save the nesting count. */
73 1:  addiu       s6, s6, 1
74     sw          s6, 0(k0)
75 
76     /* s6 holds the EPC value, this is saved after interrupts are re-enabled. */
77     mfc0        s6, _CP0_EPC
78 
79     /* Re-enable interrupts. */
80     mtc0        k1, _CP0_STATUS
81 
82     /* Save the context into the space just created.  s6 is saved again
83     here as it now contains the EPC value.  No other s registers need be
84     saved. */
85     sw          ra, 120(s5)
86     sw          s8, 116(s5)
87     sw          t9, 112(s5)
88     sw          t8, 108(s5)
89     sw          t7, 104(s5)
90     sw          t6, 100(s5)
91     sw          t5, 96(s5)
92     sw          t4, 92(s5)
93     sw          t3, 88(s5)
94     sw          t2, 84(s5)
95     sw          t1, 80(s5)
96     sw          t0, 76(s5)
97     sw          a3, 72(s5)
98     sw          a2, 68(s5)
99     sw          a1, 64(s5)
100     sw          a0, 60(s5)
101     sw          v1, 56(s5)
102     sw          v0, 52(s5)
103     sw          s6, portEPC_STACK_LOCATION(s5)
104     sw          $1, 16(s5)
105 
106     /* s6 is used as a scratch register. */
107     mfhi        s6
108     sw          s6, 12(s5)
109     mflo        s6
110     sw          s6, 8(s5)
111 
112     /* Update the task stack pointer value if nesting is zero. */
113     la          s6, uxInterruptNesting
114     lw          s6, (s6)
115     addiu       s6, s6, -1
116     bne         s6, zero, 1f
117     nop
118 
119     /* Save the stack pointer. */
120     la          s6, uxSavedTaskStackPointer
121     sw          s5, (s6)
122 1:
123     .endm
124 
125 /******************************************************************/
126 .macro  portRESTORE_CONTEXT
127 
128     /* Restore the stack pointer from the TCB.  This is only done if the
129     nesting count is 1. */
130     la          s6, uxInterruptNesting
131     lw          s6, (s6)
132     addiu       s6, s6, -1
133     bne         s6, zero, 1f
134     nop
135     la          s6, uxSavedTaskStackPointer
136     lw          s5, (s6)
137 
138     /* Restore the context. */
139 1:  lw          s6, 8(s5)
140     mtlo        s6
141     lw          s6, 12(s5)
142     mthi        s6
143     lw          $1, 16(s5)
144     /* s6 is loaded as it was used as a scratch register and therefore saved
145     as part of the interrupt context. */
146     lw          s6, 44(s5)
147     lw          v0, 52(s5)
148     lw          v1, 56(s5)
149     lw          a0, 60(s5)
150     lw          a1, 64(s5)
151     lw          a2, 68(s5)
152     lw          a3, 72(s5)
153     lw          t0, 76(s5)
154     lw          t1, 80(s5)
155     lw          t2, 84(s5)
156     lw          t3, 88(s5)
157     lw          t4, 92(s5)
158     lw          t5, 96(s5)
159     lw          t6, 100(s5)
160     lw          t7, 104(s5)
161     lw          t8, 108(s5)
162     lw          t9, 112(s5)
163     lw          s8, 116(s5)
164     lw          ra, 120(s5)
165 
166     /* Protect access to the k registers, and others. */
167     di
168     ehb
169 
170     /* Decrement the nesting count. */
171     la          k0, uxInterruptNesting
172     lw          k1, (k0)
173     addiu       k1, k1, -1
174     sw          k1, 0(k0)
175 
176     lw          k0, portSTATUS_STACK_LOCATION(s5)
177     lw          k1, portEPC_STACK_LOCATION(s5)
178 
179     /* Leave the stack in its original state.  First load sp from s5, then
180     restore s5 from the stack. */
181     add         sp, zero, s5
182     lw          s5, 40(sp)
183     addiu       sp, sp, portCONTEXT_SIZE
184 
185     mtc0        k0, _CP0_STATUS
186     mtc0        k1, _CP0_EPC
187     ehb
188     eret
189     nop
190 
191     .endm
192