1 // exc-c-wrapper-handler.S - General Exception Handler that Dispatches C Handlers
3 // Copyright (c) 2002-2004, 2006-2007, 2010 Tensilica Inc.
5 // Permission is hereby granted, free of charge, to any person obtaining
6 // a copy of this software and associated documentation files (the
7 // "Software"), to deal in the Software without restriction, including
8 // without limitation the rights to use, copy, modify, merge, publish,
9 // distribute, sublicense, and/or sell copies of the Software, and to
10 // permit persons to whom the Software is furnished to do so, subject to
11 // the following conditions:
13 // The above copyright notice and this permission notice shall be included
14 // in all copies or substantial portions of the Software.
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20 // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21 // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22 // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 #include <xtensa/coreasm.h>
25 #include <xtensa/corebits.h>
26 #include <xtensa/config/specreg.h>
27 #include "xtos-internal.h"
29 #include <xtensa/simcall.h>
32 #if XCHAL_HAVE_EXCEPTIONS
36 * This assembly-level handler causes the associated exception (usually causes 12-15)
37 * to be handled as if it were exception cause 3 (load/store error exception).
38 * This provides forward-compatibility with a possible future split of the
39 * load/store error cause into multiple more specific causes.
42 .global _xtos_cause3_handler
44 movi a2, EXCCAUSE_LOAD_STORE_ERROR
45 j _xtos_c_wrapper_handler
46 .size _xtos_cause3_handler, . - _xtos_cause3_handler
51 * This is the general exception assembly-level handler that dispatches C handlers.
54 .global _xtos_c_wrapper_handler
55 _xtos_c_wrapper_handler:
57 // HERE: a2, a3, a4 have been saved to exception stack frame allocated with a1 (sp).
58 // a2 contains EXCCAUSE.
59 s32i a5, a1, UEXC_a5 // a5 will get clobbered by ENTRY after the pseudo-CALL4
60 // (a4..a15 spilled as needed; save if modified)
62 //NOTA: Possible future improvement:
63 // keep interrupts disabled until we get into the handler, such that
64 // we don't have to save other critical state such as EXCVADDR here.
66 s32i a2, a1, UEXC_exccause
67 //s32i a3, a1, UEXC_excvaddr
70 # if XCHAL_HAVE_INTERRUPTS
71 rsilft a3, 1, XTOS_LOCKLEVEL // lockout
73 //movi a3, ~XCHAL_EXCM_MASK
74 movi a3, ~XTOS_LOCKOUT_MASK // mask out low and medium priority levels, and high priority levels covered by
75 // XTOS_LOCKLEVEL if any, so we can run at PS.INTLEVEL=0 while manipulating INTENABLE
76 s32i a2, a1, UEXC_sar // (temporary holding place for INTENABLE value to restore after pseudo-CALL4 below)
77 and a3, a2, a3 // mask out selected interrupts
78 wsr a3, INTENABLE // disable all interrupts up to and including XTOS_LOCKLEVEL
80 movi a3, PS_WOE|PS_CALLINC(1)|PS_UM // WOE=1, UM=1, INTLEVEL=0, CALLINC=1 (call4 emul), OWB=(dontcare)=0
82 // NOTE: could use XSR here if targeting T1040 or T1050 hardware (requiring slight sequence adjustment as for XEA2):
84 rsync //NOT-ISA-DEFINED // wait for WSR to INTENABLE to complete before clearing PS.INTLEVEL
85 wsr a3, PS // PS.INTLEVEL=0, effective INTLEVEL (via INTENABLE) is XTOS_LOCKLEVEL
87 // HERE: window overflows enabled, but NOT SAFE because we're not quite
88 // in a valid windowed context (haven't restored a1 yet...);
89 // so don't cause any (keep to a0..a3) until we've saved critical state and restored a1:
91 // NOTE: MUST SAVE EPC1 before causing any overflows, because overflows corrupt EPC1.
100 // WOE = __XTENSA_CALL0_ABI__ ? 0 : 1
102 // INTLEVEL = EXCM_LEVEL = 1
103 // CALLINC = __XTENSA_CALL0_ABI__ ? 0 : 1
104 // OWB = 0 (really, a dont care if !__XTENSA_CALL0_ABI__)
106 # ifdef __XTENSA_CALL0_ABI__
107 movi a2, PS_UM|PS_INTLEVEL(XCHAL_EXCM_LEVEL)
109 movi a2, PS_WOE|PS_CALLINC(1)|PS_UM|PS_INTLEVEL(XCHAL_EXCM_LEVEL) // CALL4 emulation
114 // HERE: window overflows enabled, but NOT SAFE because we're not quite
115 // in a valid windowed context (haven't restored a1 yet...);
116 // so don't cause any (keep to a0..a3) until we've saved critical state and restored a1:
118 // NOTE: MUST SAVE EPC1 before causing any overflows, because overflows corrupt EPC1.
123 #ifdef __XTENSA_CALL0_ABI__
125 s32i a0, a1, UEXC_a0 // save the rest of the registers
130 s32i a10, a1, UEXC_a10
131 s32i a11, a1, UEXC_a11
132 s32i a12, a1, UEXC_a12
133 s32i a13, a1, UEXC_a13
134 s32i a14, a1, UEXC_a14
135 s32i a15, a1, UEXC_a15
137 // TODO: setup return PC for call traceback through interrupt dispatch
140 rsync // wait for WSR to PS to complete
142 #else /* ! __XTENSA_CALL0_ABI__ */
145 l32i a2, a1, ESF_TOTALSIZE-20 // save nested-C-func call-chain ptr
147 addi a1, a1, ESF_TOTALSIZE // restore sp (dealloc ESF) for sane stack again
148 rsync // wait for WSR to PS to complete
150 /* HERE: we can SAFELY get window overflows.
152 * From here, registers a4..a15 automatically get spilled if needed.
153 * They become a0..a11 after the ENTRY instruction.
154 * Currently, we don't check whether or not these registers
155 * get spilled, so we must save and restore any that we
156 * modify. We've already saved a4 and a5
157 * which we modify as part of the pseudo-CALL.
159 * IMPLEMENTATION NOTE:
161 * The pseudo-CALL below effectively saves registers a2..a3 so
162 * that they are available again after the corresponding
163 * RETW when returning from the exception handling. We
164 * could choose to put something like EPC1 or PS in
165 * there, so they're available more quickly when
166 * restoring. HOWEVER, exception handlers may wish to
167 * change such values, or anything on the exception stack
168 * frame, and expect these to be restored as modified.
170 * NOTA: future: figure out what's the best thing to put
171 * in a2 and a3. (candidate: a4 and a5 below; but what
172 * if exception handler manipulates ARs, as in a syscall
173 * handler.... oh well)
176 * Now do the pseudo-CALL.
177 * Make it look as if the code that got the exception made a
178 * CALL4 to the exception handling code. (We call
179 * this the "pseudo-CALL".)
181 * This pseudo-CALL is important and done this way:
183 * 1. There are only three ways to safely update the stack pointer
184 * in the windowed ABI, such that window exceptions work correctly:
185 * (a) spill all live windows to stack then switch to a new stack
186 * (or, save the entire address register file and window
187 * registers, which is likely even more expensive)
188 * (b) use MOVSP (or equivalent)
190 * Doing (a) is excessively expensive, and doing (b) here requires
191 * copying 16 bytes back and forth which is also time-consuming;
192 * whereas (c) is very efficient, so that's what we do here.
194 * 2. Normally we cannot do a pseudo-CALL8 or CALL12 here.
196 * windowed ABI, a function must allocate enough space
197 * for the largest call that it makes. However, the
198 * pseudo-CALL is executed in the context of the
199 * function that happened to be executing at the time
200 * the interrupt was taken, and that function might or
201 * might not have allocated enough stack space for a
202 * CALL8 or a CALL12. If we try doing a pseudo-CALL8
203 * or -CALL12 here, we corrupt the stack if the
204 * interrupted function happened to not have allocated
205 * space for such a call.
207 * 3. We set the return PC, but it's not strictly
208 * necessary for proper operation. It does make
209 * debugging, ie. stack tracebacks, much nicer if it
210 * can point to the interrupted code (not always
211 * possible, eg. if interrupted code is in a different
212 * GB than the interrupt handling code, which is
213 * unlikely in a system without protection where
214 * interrupt handlers and general application code are
215 * typically linked together).
217 * IMPORTANT: Interrupts must stay disabled while doing the pseudo-CALL,
218 * or at least until after the ENTRY instruction, because SP has been
219 * restored to its original value that does not reflect the exception
220 * stack frame's allocation. An interrupt taken here would
221 * corrupt the exception stack frame (ie. allocate another over it).
222 * (High priority interrupts can remain enabled, they save and restore
223 * all of their state and use their own stack or save area.)
224 * For the same reason, we mustn't get any exceptions in this code
225 * (other than window exceptions where noted) until ENTRY is done.
228 // HERE: may get a single window overflow (caused by the following instruction).
231 movi a4, 0xC0000000 // [for debug] for return PC computation below
232 or a3, a4, a3 // [for debug] set upper two bits of return PC
233 addx2 a4, a4, a3 // [for debug] clear upper bit
235 movi a4, 0 // entry cannot cause overflow, cause it here
238 .global _GeneralException
239 _GeneralException: // this label makes tracebacks through exceptions look nicer
241 _entry a1, ESF_TOTALSIZE // as if after a CALL4 (PS.CALLINC set to 1 above)
244 * The above ENTRY instruction does a number of things:
246 * 1. Because we're emulating CALL4, the ENTRY rotates windows
247 * forward by 4 registers (as per 'ROTW +1'), so that
248 * a4-a15 became a0-a11. So now: a0-a11 are part of
249 * the interrupted context to be preserved. a0-a1
250 * were already saved above when they were a4-a5.
251 * a12-a15 are free to use as they're NOT part of the
252 * interrupted context. We don't need to save/restore
253 * them, and they will get spilled if needed.
255 * 2. Updates SP (new a1), allocating the exception stack
256 * frame in the new window, preserving the old a1 in
257 * the previous window.
259 * 3. The underscore prefix prevents the assembler from
260 * automatically aligning the ENTRY instruction on a
261 * 4-byte boundary, which could create a fatal gap in
262 * the instruction stream.
264 * At this point, ie. before we re-enable interrupts, we know the caller is
265 * always live so we can safely modify a1 without using MOVSP (we can use MOVSP
266 * but it will never cause an ALLOCA or underflow exception here).
267 * So this is a good point to modify the stack pointer if we want eg. to
268 * switch to an interrupt stack (if we do, we need to save the current SP
269 * because certain things have been saved to that exception stack frame).
270 * We couldn't do this easily before ENTRY, where the caller wasn't
273 * NOTE: We don't switch to an interrupt stack here, because exceptions
274 * are generally caused by executing code -- so we handle exceptions in
275 * the context of the thread that cause them, and thus remain on the same
276 * stack. This means a thread's stack must be large enough to handle
277 * the maximum level of nesting of exceptions that the thread can cause.
280 // NOTA: exception handlers for certain causes may need interrupts to be kept
281 // disabled through their dispatch, so they can turn them off themselves at
282 // the right point (if at all), eg. to save critical state unknown to this
283 // code here, or for some recovery action that must be atomic with respect
286 // Perhaps two versions of this assembly-level handler are needed, one that restores
287 // interrupts to what they were before the exception was taken (as here)
288 // and one that ensures at least low-priority interrupts are kept disabled?
289 // NOTA: For now, always enable interrupts here.
292 * Now we can enable interrupts.
293 * (Pseudo-CALL is complete, and SP reflects allocation of exception stack frame.)
296 #endif /* __XTENSA_CALL0_ABI__ */
299 #if XCHAL_HAVE_INTERRUPTS
301 //... recompute and set INTENABLE ...
302 l32i a13, a1, UEXC_sar // (temporary holding place for INTENABLE value saved before pseudo-CALL4 above)
304 wsr a13, INTENABLE // restore INTENABLE as it was on entry
313 movi a13, _xtos_c_handler_table // &table
314 l32i a15, a1, UEXC_exccause // arg2: exccause
316 s32i a12, a1, UEXC_sar
317 save_loops_mac16 a1, a12, a14 // save LOOP & MAC16 regs, if configured
319 addx4 a12, a15, a13 // a12 = table[exccause]
320 l32i a12, a12, 0 // ...
321 #ifdef __XTENSA_CALL0_ABI__
322 mov a2, a1 // arg1: exception parameters
323 mov a3, a15 // arg2: exccause
324 beqz a12, 1f // null handler => skip call
325 callx0 a12 // call C exception handler for this exception
327 mov a14, a1 // arg1: exception parameters
328 // mov a15, a15 // arg2: exccause, already in a15
329 beqz a12, 1f // null handler => skip call
330 callx12 a12 // call C exception handler for this exception
333 // Now exit the handler.
336 // Restore special registers
338 restore_loops_mac16 a1, a13, a14, a15 // restore LOOP & MAC16 regs, if configured
339 l32i a14, a1, UEXC_sar
342 * Disable interrupts while returning from the pseudo-CALL setup above,
343 * for the same reason they were disabled while doing the pseudo-CALL:
344 * this sequence restores SP such that it doesn't reflect the allocation
345 * of the exception stack frame, which we still need to return from
349 #if XCHAL_HAVE_INTERRUPTS
351 // Must disable interrupts via INTENABLE, because PS.INTLEVEL gets zeroed
352 // by any window exception exit, eg. the window underflow that may happen
353 // upon executing the RETW instruction.
354 // Also, must disable at XTOS_LOCKLEVEL, not just EXCM_LEVEL, because this
355 // code effectively manipulates virtual INTENABLE state up to the point
356 // INTENABLE is written in _xtos_return_from_exc.
358 rsilft a12, 1, XTOS_LOCKLEVEL // lockout
360 //movi a13, ~XCHAL_EXCM_MASK
361 movi a13, ~XTOS_LOCKOUT_MASK // mask out low and medium priority levels, and high priority levels covered by
362 // XTOS_LOCKLEVEL if any, so we can run at PS.INTLEVEL=0 while manipulating INTENABLE
363 s32i a12, a1, UEXC_sar // (temporary holding place for INTENABLE value to restore after pseudo-CALL4 below)
364 and a13, a12, a13 // mask out selected interrupts
365 wsr a13, INTENABLE // disable all interrupts up to and including XTOS_LOCKLEVEL
367 rsil a12, XCHAL_EXCM_LEVEL
372 movi a0, _xtos_return_from_exc
373 #ifdef __XTENSA_CALL0_ABI__
375 #else /* ! __XTENSA_CALL0_ABI__ */
376 /* Now return from the pseudo-CALL from the interrupted code, to rotate
377 * our windows back... */
382 # if XCHAL_HAVE_XEA1 && XCHAL_HAVE_INTERRUPTS
383 rsync //NOT-ISA-DEFINED // wait for WSR to INTENABLE to complete before doing RETW
384 // (ie. before underflow exception exit)
385 // (not needed, because underflow exception entry does implicit ISYNC ??
386 // but in case underflow not taken, WSR must complete before wsr to PS that lowers PS.INTLEVEL
387 // possibly below XTOS_LOCKLEVEL, in which RETW's jump is not sufficient sync, so a sync
388 // is needed but it can be placed just before WSR to PS -- but here is fine)
390 or a0, a0, a13 // set upper two bits
391 addx2 a0, a13, a0 // clear upper bit
393 #endif /* ! __XTENSA_CALL0_ABI__ */
395 /* FIXME: what about _GeneralException ? */
396 .size _xtos_c_wrapper_handler, . - _xtos_c_wrapper_handler
399 #endif /* XCHAL_HAVE_EXCEPTIONS */