xtensa/asm2: Fix stack pointer during preemption spills

When returning into a different thread than we interrupted, we
obviously need to spill all the existing register windows to make sure
all their values are in the old thread's stack.  But the code to do
this forgot to reset the current stack pointer to the value it had at
interrupt time (it was still pointing to the saved context below
that), so the caller of the interrupted function was spilling to the
wrong spot.

This wouldn't show up as an instant failure, it would only happen when
switching BACK to the improperly-spilled thread.  And even then it
would be a noop if the original interrupt handler was deep enough to
have spilled that function naturally.

In practice, this happened only in some instances on ESP-32 (which has
more windowed registers than qemu) when interrupting the idle thread
(which is very shallow) with a (very simple) timer interrupt.  Trivial
to see, hard to find.

See https://github.com/zephyrproject-rtos/zephyr/issues/6346 for more
detail.

Signed-off-by: Andy Ross <andrew.j.ross@intel.com>
This commit is contained in:
Andy Ross 2018-03-05 13:22:18 -08:00 committed by Anas Nashif
commit bdac8d070f

View file

@ -297,12 +297,17 @@ _do_call_\@:
* to restore to in A6 (the call4'd function's A2). If this
* is not the same handle as we started with, we need to do a
* register spill before restoring, for obvious reasons.
* Remember to mask interrupts (which have been unmasked
* during the handler execution) while we muck with the
* windows. The restore will unmask them as needed.
* Remember to restore the A1 stack pointer as it existed at
* interrupt time so the caller of the interrupted function
* spills to the right place. Also mask interrupts (which
* have been unmasked during the handler execution) while we
* muck with the windows. The restore will unmask them
* correctly.
*/
beq a6, a1, _restore_\@
rsil a0, XCHAL_NMILEVEL
l32i a1, a1, 0
addi a1, a1, BASE_SAVE_AREA_SIZE
SPILL_ALL_WINDOWS
mov a1, a6