arc: fix stack corruption caused by firq handling

There are a few problems with the code being repaired here.
1. A seti was used to re-enable all interrupts, even though the
thread being switched to may have had certain interrupt priorities masked.
2. saved status32 already has SC bit if thats wanted, so its ok to just
restore status32 as-is w/o needing to and off anything.
3. the code is difficult to write using kflag and seti because as you
restore registers, there aren't any to use. But we can exploit a
trick where we pretend an interrupt has occured by setting a bit in
AUX_IRQ_ACT, and then use RTIE instruction to restore status32
atomically with branching to return address. Something about the way
this code was written was causing stack corruptings and crashes in an
application that uses a high rate of both FIRQ and Regular interrupts.

Change-Id: Ia7166d51f0e750c07832ab115b7151ce37ee0278
Signed-off-by: Chuck Jordan <cjordan@synopsys.com>
This commit is contained in:
Chuck Jordan 2016-05-30 13:26:11 -07:00 committed by Anas Nashif
parent b3a485147f
commit 6c86ed85fe

View file

@ -151,15 +151,8 @@ _swap_return_from_coop:
st 0, [r2, __tTCS_intlock_key_OFFSET]
ld_s r0, [r2, __tTCS_return_value_OFFSET]
/*
* Adjust the stack here in case we go to _return_from_exc: this allows
* keeping handling both coop and irq cases in _return_from_exc without
* adding extra logic.
*/
add_s sp, sp, 8
lr ilink, [_ARC_V2_STATUS32]
bbit1 ilink, _ARC_V2_STATUS32_AE_BIT, _return_from_exc
sub_s sp, sp, 8
pop_s blink /* pc into blink */
pop_s r3 /* status32 into r3 */
@ -173,39 +166,32 @@ _swap_return_from_coop:
_swap_return_from_rirq:
_swap_return_from_firq:
_pop_irq_stack_frame
lr ilink, [_ARC_V2_STATUS32]
bbit1 ilink, _ARC_V2_STATUS32_AE_BIT, _return_from_exc
ld ilink, [sp, -4] /* status32 into ilink */
#ifdef CONFIG_ARC_STACK_CHECKING
and ilink, ilink, 0x7fffbffe // keep SC off till we stop using under SP
#else
and ilink, ilink, 0x7ffffffe // keep interrupts disabled until seti
#endif
kflag ilink
ld ilink, [sp, -8] /* pc into ilink */
#ifdef CONFIG_ARC_STACK_CHECKING
/* Enable stack checking now it is safe */
push_s r3
lr r3, [_ARC_V2_STATUS32]
bset r3, r3, _ARC_V2_STATUS32_SC_BIT
flag r3
pop_s r3
#endif
j.d [ilink]
seti (_ARC_V2_DEF_IRQ_LEVEL | (1 << 4))
bbit1 r3, _ARC_V2_STATUS32_AE_BIT, _return_from_exc_irq
/* pretend interrupt happened to use rtie instruction */
lr r3, [_ARC_V2_AUX_IRQ_ACT]
brne r3,0,_swap_already_in_irq
or r3,r3,(1<<(CONFIG_NUM_IRQ_PRIO_LEVELS-1)) /* use lowest */
sr r3, [_ARC_V2_AUX_IRQ_ACT]
_swap_already_in_irq:
rtie
.balign 4
_return_from_exc_irq:
_pop_irq_stack_frame
sub_s sp, sp, 8
_return_from_exc:
/* put the return address to eret */
ld ilink, [sp, -8] /* pc into ilink */
ld ilink, [sp] /* pc into ilink */
sr ilink, [_ARC_V2_ERET]
/* put status32 into estatus */
ld ilink, [sp, -4] /* status32 into ilink */
ld ilink, [sp, 4] /* status32 into ilink */
sr ilink, [_ARC_V2_ERSTATUS]
add_s sp, sp, 8
rtie