1 | /* |
---|
2 | * (c) 1999, Eric Valette valette@crf.canon.fr |
---|
3 | * |
---|
4 | * Modified and partially rewritten by Till Straumann, 2007 |
---|
5 | * |
---|
6 | * Low-level assembly code for PPC exceptions (macros). |
---|
7 | * |
---|
8 | * This file was written with the goal to eliminate |
---|
9 | * ALL #ifdef <cpu_flavor> conditionals -- please do not |
---|
10 | * reintroduce such statements. |
---|
11 | */ |
---|
12 | |
---|
13 | #include <rtems/score/cpu.h> |
---|
14 | #include <bsp/vectors.h> |
---|
15 | #include <libcpu/raw_exception.h> |
---|
16 | |
---|
17 | #define EXC_MIN_GPR1 0 |
---|
18 | #define FRAME_LINK_SPACE 8 |
---|
19 | |
---|
20 | #define r0 0 |
---|
21 | #define r1 1 |
---|
22 | #define r2 2 |
---|
23 | #define r3 3 |
---|
24 | #define r4 4 |
---|
25 | #define r5 5 |
---|
26 | #define r6 6 |
---|
27 | #define r7 7 |
---|
28 | #define r8 8 |
---|
29 | #define r9 9 |
---|
30 | #define r10 10 |
---|
31 | #define r11 11 |
---|
32 | #define r12 12 |
---|
33 | #define r13 13 |
---|
34 | #define r14 14 |
---|
35 | #define r15 15 |
---|
36 | #define r16 16 |
---|
37 | #define r17 17 |
---|
38 | #define r18 18 |
---|
39 | #define r19 19 |
---|
40 | #define r20 20 |
---|
41 | #define r21 21 |
---|
42 | #define r22 22 |
---|
43 | #define r23 23 |
---|
44 | #define r24 24 |
---|
45 | #define r25 25 |
---|
46 | #define r26 26 |
---|
47 | #define r27 27 |
---|
48 | #define r28 28 |
---|
49 | #define r29 29 |
---|
50 | #define r30 30 |
---|
51 | #define r31 31 |
---|
52 | |
---|
53 | #define cr0 0 |
---|
54 | #define cr1 1 |
---|
55 | #define cr4 4 |
---|
56 | |
---|
57 | #define LT(cr) ((cr)*4+0) |
---|
58 | #define GT(cr) ((cr)*4+1) |
---|
59 | #define EQ(cr) ((cr)*4+2) |
---|
60 | |
---|
61 | #define NOFRAME 0xffff8000 |
---|
62 | |
---|
63 | /* Switch r1 to interrupt stack if not already there. |
---|
64 | * |
---|
65 | * USES: RA, RB |
---|
66 | * ON EXIT: RA, RB available, r1 points into interrupt |
---|
67 | * stack. |
---|
68 | * |
---|
69 | * NOTES: |
---|
70 | * - NEVER store stuff in a frame before |
---|
71 | * reserving it (stwu r1) - otherwise |
---|
72 | * higher-priority exception may overwrite. |
---|
73 | * - algorithm should allow nesting of higher |
---|
74 | * priority exceptions (HPE) (by disabling |
---|
75 | * them while the stack is switched). |
---|
76 | */ |
---|
77 | #if 0 |
---|
78 | .macro SWITCH_STACK RA RB FLVR |
---|
79 | mfspr \RB, SPRG1 |
---|
80 | cmplw cr0, r1, \RB |
---|
81 | bgt do_r1_reload_\FLVR |
---|
82 | lwz \RA, ppc_exc_intr_stack_size@sdarel(r13) |
---|
83 | subf \RB, \RB, \RA |
---|
84 | cmplw cr0, r1, \RB |
---|
85 | bge no_r1_reload_\FLVR |
---|
86 | do_r1_reload_\FLVR: |
---|
87 | mfspr r1, SPRG1 |
---|
88 | no_r1_reload_\FLVR: |
---|
89 | .endm |
---|
90 | #else |
---|
91 | .macro SWITCH_STACK RA RB FLVR |
---|
92 | /* disable interrupts */ |
---|
93 | lwz \RA, ppc_exc_msr_irq_mask@sdarel(r13) |
---|
94 | mfmsr \RB |
---|
95 | and \RA, \RB, \RA |
---|
96 | mtmsr \RA |
---|
97 | /* increment nest level */ |
---|
98 | lwz \RA, _ISR_Nest_level@sdarel(r13) |
---|
99 | cmplwi cr0, \RA, 0 |
---|
100 | bne no_r1_reload_\FLVR |
---|
101 | /* reload r1 */ |
---|
102 | mfspr r1, SPRG1 |
---|
103 | no_r1_reload_\FLVR: |
---|
104 | addi \RA, \RA, 1 |
---|
105 | stw \RA, _ISR_Nest_level@sdarel(r13) |
---|
106 | /* restore IRQ mask */ |
---|
107 | mtmsr \RB |
---|
108 | .endm |
---|
109 | #endif |
---|
110 | |
---|
111 | /* |
---|
112 | * Minimal prologue snippets: |
---|
113 | * |
---|
114 | * Rationale: on some PPCs the vector offsets are spaced |
---|
115 | * as closely as 16 bytes. |
---|
116 | * |
---|
117 | * If we deal with asynchronous exceptions ('interrupts') |
---|
118 | * then we can use 4 instructions to |
---|
119 | * 1. atomically write lock to indicate ISR is in progress |
---|
120 | * (we cannot atomically increase the Thread_Dispatch_disable_level, |
---|
121 | * see README) |
---|
122 | * 2. save a register in special area |
---|
123 | * 3. load register with vector info |
---|
124 | * 4. branch |
---|
125 | * |
---|
126 | * If we deal with a synchronous exception (no stack switch |
---|
127 | * nor dispatch-disabling necessary) then it's easier: |
---|
128 | * 1. push stack frame |
---|
129 | * 2. save register on stack |
---|
130 | * 3. load register with vector info |
---|
131 | * 4. branch |
---|
132 | * |
---|
133 | */ |
---|
134 | .macro PPC_EXC_MIN_PROLOG_ASYNC _NAME _VEC _PRI _FLVR |
---|
135 | .global ppc_exc_min_prolog_async_\_NAME |
---|
136 | ppc_exc_min_prolog_async_\_NAME: |
---|
137 | /* Atomically write lock variable in 1st instruction with non-zero value |
---|
138 | * (r1 is always nonzero; r13 could also be used) |
---|
139 | */ |
---|
140 | stw r1, ppc_exc_lock_\_PRI@sdarel(r13) |
---|
141 | /* We have no stack frame yet; store r3 in special area; |
---|
142 | * a higher-priority (critical) interrupt uses a different area |
---|
143 | * (hence the different prologue snippets) (\PRI) |
---|
144 | */ |
---|
145 | stw r3, ppc_exc_gpr3_\_PRI@sdarel(r13) |
---|
146 | /* Load vector. |
---|
147 | */ |
---|
148 | li r3, ( \_VEC | 0xffff8000 ) |
---|
149 | /* Branch (must be within 32MB) |
---|
150 | */ |
---|
151 | ba wrap_\_FLVR |
---|
152 | .endm |
---|
153 | |
---|
154 | .macro PPC_EXC_MIN_PROLOG_SYNC _NAME _VEC _PRI _FLVR |
---|
155 | .global ppc_exc_min_prolog_sync_\_NAME |
---|
156 | ppc_exc_min_prolog_sync_\_NAME: |
---|
157 | stwu r1, -EXCEPTION_FRAME_END(r1) |
---|
158 | stw r3, GPR3_OFFSET(r1) |
---|
159 | li r3, \_VEC |
---|
160 | ba wrap_nopush_\_FLVR |
---|
161 | .endm |
---|
162 | |
---|
163 | .macro TEST_LOCK_std |
---|
164 | /* 'std' is lowest level, i.e., can not be locked -> EQ(cr4) = 1 */ |
---|
165 | creqv EQ(cr4), EQ(cr4), EQ(cr4) |
---|
166 | .endm |
---|
167 | |
---|
168 | /* critical-exception wrapper has to check 'std' lock: */ |
---|
169 | .macro TEST_LOCK_crit |
---|
170 | lwz r5, ppc_exc_lock_std@sdarel(r13) |
---|
171 | cmpli cr4, r5, 0 |
---|
172 | .endm |
---|
173 | |
---|
174 | /* machine-check wrapper has to check 'std' and 'crit' locks */ |
---|
175 | .macro TEST_LOCK_mchk |
---|
176 | lwz r5, ppc_exc_lock_std@sdarel(r13) |
---|
177 | cmpli cr4, r5, 0 |
---|
178 | lwz r5, ppc_exc_lock_crit@sdarel(r13) |
---|
179 | cmpli cr0, r5, 0 |
---|
180 | cror EQ(cr4), EQ(cr4), EQ(cr0) |
---|
181 | .endm |
---|
182 | |
---|
183 | /* Minimal prologue snippets jump into WRAP |
---|
184 | * which prepares calling code common to all |
---|
185 | * flavors of exceptions. |
---|
186 | * We must have this macro instantiated for |
---|
187 | * each possible flavor of exception so that |
---|
188 | * we use the proper lock variable, SRR register pair and |
---|
189 | * RFI instruction. |
---|
190 | */ |
---|
191 | .macro WRAP _FLVR _PRI _SRR0 _SRR1 _RFI |
---|
192 | wrap_\_FLVR: |
---|
193 | stwu r1, -EXCEPTION_FRAME_END(r1) |
---|
194 | wrap_nopush_\_FLVR: |
---|
195 | stw r14, GPR14_OFFSET(r1) |
---|
196 | wrap_no_save_r14_\_FLVR: |
---|
197 | |
---|
198 | /* Save r4 r5 and CR; we want CR soon */ |
---|
199 | mfcr r14 |
---|
200 | stw r4, GPR4_OFFSET(r1) |
---|
201 | stw r5, GPR5_OFFSET(r1) |
---|
202 | stw r14, EXC_CR_OFFSET(r1) |
---|
203 | |
---|
204 | /* Check if this is an 'interrupt-type' exception |
---|
205 | * (MSB vector is set). |
---|
206 | * 'interrupt-type' exceptions disable thread dispatching |
---|
207 | * and switch to a private stack. |
---|
208 | * The type of exception is kept in (non-volatile) cr2 |
---|
209 | * < 0 -> interrupt-type |
---|
210 | * > 0 -> 'normal' exception; always on task stack, |
---|
211 | * may switch context at any time. |
---|
212 | */ |
---|
213 | cmpwi cr2, r3, 0 |
---|
214 | |
---|
215 | /* |
---|
216 | * Save frame address in r14 |
---|
217 | */ |
---|
218 | mr r14, r1 |
---|
219 | |
---|
220 | bge cr2, no_thread_dispatch_disable_\_FLVR |
---|
221 | |
---|
222 | /* first thing we need to |
---|
223 | * increment the thread-dispatch disable level |
---|
224 | * in case a higher priority exception occurs |
---|
225 | * we don't want it to run the scheduler. |
---|
226 | */ |
---|
227 | lwz r5, _Thread_Dispatch_disable_level@sdarel(r13) |
---|
228 | addi r5, r5, 1 |
---|
229 | stw r5, _Thread_Dispatch_disable_level@sdarel(r13) |
---|
230 | |
---|
231 | /* clear lock; no higher-priority interrupt occurring after |
---|
232 | * this point can cause a context switch. |
---|
233 | */ |
---|
234 | li r5, 0 |
---|
235 | stw r5, ppc_exc_lock_\_PRI@sdarel(r13) |
---|
236 | |
---|
237 | /* test lower-priority locks; result in (non-volatile) cr4 */ |
---|
238 | TEST_LOCK_\_PRI |
---|
239 | |
---|
240 | /* Peform stack switch if necessary */ |
---|
241 | SWITCH_STACK RA=r4 RB=r5 FLVR=\_FLVR |
---|
242 | |
---|
243 | /* save r3, in exception frame */ |
---|
244 | lwz r5, ppc_exc_gpr3_\_PRI@sdarel(r13) |
---|
245 | stw r5, GPR3_OFFSET(r14) |
---|
246 | |
---|
247 | no_thread_dispatch_disable_\_FLVR: |
---|
248 | |
---|
249 | /* save lr into exception frame */ |
---|
250 | mflr r4 |
---|
251 | stw r4, EXC_LR_OFFSET(r14) |
---|
252 | |
---|
253 | /* we now have r4,r5,lr,cr available; |
---|
254 | * r3 still holds the vector, |
---|
255 | * r14 a pointer to the exception frame (always on |
---|
256 | * task stack) |
---|
257 | * r1 is the stack pointer, either on the task stack |
---|
258 | * or on the IRQ stack |
---|
259 | */ |
---|
260 | |
---|
261 | /* retrieve SRR0/SRR1 */ |
---|
262 | mf\_SRR0 r4 |
---|
263 | mf\_SRR1 r5 |
---|
264 | |
---|
265 | /* branch to common routine */ |
---|
266 | bl wrap_common |
---|
267 | |
---|
268 | /* restore SRR, r4, r5, r1 (stack pointer) and lr */ |
---|
269 | mt\_SRR0 r4 |
---|
270 | mt\_SRR1 r5 |
---|
271 | /* restore lr */ |
---|
272 | lwz r5, EXC_LR_OFFSET(r1) |
---|
273 | lwz r4, GPR4_OFFSET(r1) |
---|
274 | mtlr r5 |
---|
275 | lwz r5, GPR5_OFFSET(r1) |
---|
276 | lwz r1, EXC_MIN_GPR1(r1) |
---|
277 | \_RFI |
---|
278 | .endm |
---|