diff options
author | Max Filippov <jcmvbkbc@gmail.com> | 2015-06-25 16:03:29 -0400 |
---|---|---|
committer | Max Filippov <jcmvbkbc@gmail.com> | 2015-08-17 00:32:48 -0400 |
commit | b6569439f1cec0ce15f647e2ba814431b5930b82 (patch) | |
tree | b821980f450559146b93175f5ff381996d27c4f0 | |
parent | de7c1c7862c4072bc93155da2cd6921137cb87ae (diff) |
xtensa: keep exception/interrupt stack continuous
Restore original a0 in the kernel exception stack frame. This way it
looks like the frame that got interrupt/exception did alloca (copy a0 and
a1 spilled under old stack to the new location as well) to save registers
and then did a call to handler.
The point where interrupt/exception was taken is not in the stack chain,
only in pt_regs (call4 from that address can be simulated to keep it in
the stack trace).
Signed-off-by: Max Filippov <jcmvbkbc@gmail.com>
-rw-r--r-- | arch/xtensa/kernel/entry.S | 43 |
1 files changed, 28 insertions, 15 deletions
diff --git a/arch/xtensa/kernel/entry.S b/arch/xtensa/kernel/entry.S index 51e9877ced06..e955f6098f64 100644 --- a/arch/xtensa/kernel/entry.S +++ b/arch/xtensa/kernel/entry.S | |||
@@ -122,6 +122,7 @@ _user_exception: | |||
122 | /* Save SAR and turn off single stepping */ | 122 | /* Save SAR and turn off single stepping */ |
123 | 123 | ||
124 | movi a2, 0 | 124 | movi a2, 0 |
125 | wsr a2, depc # terminate user stack trace with 0 | ||
125 | rsr a3, sar | 126 | rsr a3, sar |
126 | xsr a2, icountlevel | 127 | xsr a2, icountlevel |
127 | s32i a3, a1, PT_SAR | 128 | s32i a3, a1, PT_SAR |
@@ -301,7 +302,18 @@ _kernel_exception: | |||
301 | s32i a14, a1, PT_AREG14 | 302 | s32i a14, a1, PT_AREG14 |
302 | s32i a15, a1, PT_AREG15 | 303 | s32i a15, a1, PT_AREG15 |
303 | 304 | ||
305 | _bnei a2, 1, 1f | ||
306 | |||
307 | /* Copy spill slots of a0 and a1 to imitate movsp | ||
308 | * in order to keep exception stack continuous | ||
309 | */ | ||
310 | l32i a3, a1, PT_SIZE | ||
311 | l32i a0, a1, PT_SIZE + 4 | ||
312 | s32e a3, a1, -16 | ||
313 | s32e a0, a1, -12 | ||
304 | 1: | 314 | 1: |
315 | l32i a0, a1, PT_AREG0 # restore saved a0 | ||
316 | wsr a0, depc | ||
305 | 317 | ||
306 | #ifdef KERNEL_STACK_OVERFLOW_CHECK | 318 | #ifdef KERNEL_STACK_OVERFLOW_CHECK |
307 | 319 | ||
@@ -346,12 +358,12 @@ common_exception: | |||
346 | s32i a0, a1, PT_EXCCAUSE | 358 | s32i a0, a1, PT_EXCCAUSE |
347 | s32i a3, a2, EXC_TABLE_FIXUP | 359 | s32i a3, a2, EXC_TABLE_FIXUP |
348 | 360 | ||
349 | /* All unrecoverable states are saved on stack, now, and a1 is valid, | 361 | /* All unrecoverable states are saved on stack, now, and a1 is valid. |
350 | * so we can allow exceptions and interrupts (*) again. | 362 | * Now we can allow exceptions again. In case we've got an interrupt |
351 | * Set PS(EXCM = 0, UM = 0, RING = 0, OWB = 0, WOE = 1, INTLEVEL = X) | 363 | * PS.INTLEVEL is set to LOCKLEVEL disabling furhter interrupts, |
364 | * otherwise it's left unchanged. | ||
352 | * | 365 | * |
353 | * (*) We only allow interrupts if they were previously enabled and | 366 | * Set PS(EXCM = 0, UM = 0, RING = 0, OWB = 0, WOE = 1, INTLEVEL = X) |
354 | * we're not handling an IRQ | ||
355 | */ | 367 | */ |
356 | 368 | ||
357 | rsr a3, ps | 369 | rsr a3, ps |
@@ -362,28 +374,30 @@ common_exception: | |||
362 | moveqz a3, a2, a0 # a3 = LOCKLEVEL iff interrupt | 374 | moveqz a3, a2, a0 # a3 = LOCKLEVEL iff interrupt |
363 | movi a2, 1 << PS_WOE_BIT | 375 | movi a2, 1 << PS_WOE_BIT |
364 | or a3, a3, a2 | 376 | or a3, a3, a2 |
365 | rsr a0, exccause | 377 | rsr a2, exccause |
378 | /* restore return address (or 0 if return to userspace) */ | ||
379 | rsr a0, depc | ||
366 | xsr a3, ps | 380 | xsr a3, ps |
367 | 381 | ||
368 | s32i a3, a1, PT_PS # save ps | 382 | s32i a3, a1, PT_PS # save ps |
369 | 383 | ||
370 | /* Save lbeg, lend */ | 384 | /* Save lbeg, lend */ |
371 | 385 | ||
372 | rsr a2, lbeg | 386 | rsr a4, lbeg |
373 | rsr a3, lend | 387 | rsr a3, lend |
374 | s32i a2, a1, PT_LBEG | 388 | s32i a4, a1, PT_LBEG |
375 | s32i a3, a1, PT_LEND | 389 | s32i a3, a1, PT_LEND |
376 | 390 | ||
377 | /* Save SCOMPARE1 */ | 391 | /* Save SCOMPARE1 */ |
378 | 392 | ||
379 | #if XCHAL_HAVE_S32C1I | 393 | #if XCHAL_HAVE_S32C1I |
380 | rsr a2, scompare1 | 394 | rsr a3, scompare1 |
381 | s32i a2, a1, PT_SCOMPARE1 | 395 | s32i a3, a1, PT_SCOMPARE1 |
382 | #endif | 396 | #endif |
383 | 397 | ||
384 | /* Save optional registers. */ | 398 | /* Save optional registers. */ |
385 | 399 | ||
386 | save_xtregs_opt a1 a2 a4 a5 a6 a7 PT_XTREGS_OPT | 400 | save_xtregs_opt a1 a3 a4 a5 a6 a7 PT_XTREGS_OPT |
387 | 401 | ||
388 | #ifdef CONFIG_TRACE_IRQFLAGS | 402 | #ifdef CONFIG_TRACE_IRQFLAGS |
389 | l32i a4, a1, PT_DEPC | 403 | l32i a4, a1, PT_DEPC |
@@ -391,8 +405,7 @@ common_exception: | |||
391 | * while PS.EXCM was set, i.e. interrupts disabled. | 405 | * while PS.EXCM was set, i.e. interrupts disabled. |
392 | */ | 406 | */ |
393 | bgeui a4, VALID_DOUBLE_EXCEPTION_ADDRESS, 1f | 407 | bgeui a4, VALID_DOUBLE_EXCEPTION_ADDRESS, 1f |
394 | l32i a4, a1, PT_EXCCAUSE | 408 | bnei a2, EXCCAUSE_LEVEL1_INTERRUPT, 1f |
395 | bnei a4, EXCCAUSE_LEVEL1_INTERRUPT, 1f | ||
396 | /* We came here with an interrupt means interrupts were enabled | 409 | /* We came here with an interrupt means interrupts were enabled |
397 | * and we've just disabled them. | 410 | * and we've just disabled them. |
398 | */ | 411 | */ |
@@ -407,8 +420,8 @@ common_exception: | |||
407 | 420 | ||
408 | rsr a4, excsave1 | 421 | rsr a4, excsave1 |
409 | mov a6, a1 # pass stack frame | 422 | mov a6, a1 # pass stack frame |
410 | mov a7, a0 # pass EXCCAUSE | 423 | mov a7, a2 # pass EXCCAUSE |
411 | addx4 a4, a0, a4 | 424 | addx4 a4, a2, a4 |
412 | l32i a4, a4, EXC_TABLE_DEFAULT # load handler | 425 | l32i a4, a4, EXC_TABLE_DEFAULT # load handler |
413 | 426 | ||
414 | /* Call the second-level handler */ | 427 | /* Call the second-level handler */ |