Commit 895666a9 authored by Max Filippov's avatar Max Filippov Committed by Chris Zankel

xtensa: disable IRQs while IRQ handler is running

IRQ handlers are expected to run with IRQs disabled.
See e.g. http://lwn.net/Articles/380931/ for a longer story.

This was overlooked in the commit
  2d1c645c xtensa: dispatch medium-priority interrupts
Revert to old behavior and simplify interrupt entry and exit code.
Interrupt handler still honours IRQ priority.

do_notify_resume/schedule must be called with interrupts enabled, enable
interrupts if we return from user exception.
Signed-off-by: default avatarMax Filippov <jcmvbkbc@gmail.com>
Signed-off-by: default avatarChris Zankel <chris@zankel.net>
parent 8f371c75
...@@ -354,16 +354,16 @@ common_exception: ...@@ -354,16 +354,16 @@ common_exception:
* so we can allow exceptions and interrupts (*) again. * so we can allow exceptions and interrupts (*) again.
* Set PS(EXCM = 0, UM = 0, RING = 0, OWB = 0, WOE = 1, INTLEVEL = X) * Set PS(EXCM = 0, UM = 0, RING = 0, OWB = 0, WOE = 1, INTLEVEL = X)
* *
* (*) We only allow interrupts of higher priority than current IRQ * (*) We only allow interrupts if they were previously enabled and
* we're not handling an IRQ
*/ */
rsr a3, ps rsr a3, ps
addi a0, a0, -4 addi a0, a0, -EXCCAUSE_LEVEL1_INTERRUPT
movi a2, 1 movi a2, LOCKLEVEL
extui a3, a3, PS_INTLEVEL_SHIFT, PS_INTLEVEL_WIDTH extui a3, a3, PS_INTLEVEL_SHIFT, PS_INTLEVEL_WIDTH
# a3 = PS.INTLEVEL # a3 = PS.INTLEVEL
movnez a2, a3, a3 # a2 = 1: level-1, > 1: high priority moveqz a3, a2, a0 # a3 = LOCKLEVEL iff interrupt
moveqz a3, a2, a0 # a3 = IRQ level iff interrupt
movi a2, 1 << PS_WOE_BIT movi a2, 1 << PS_WOE_BIT
or a3, a3, a2 or a3, a3, a2
rsr a0, exccause rsr a0, exccause
...@@ -444,6 +444,8 @@ common_exception_return: ...@@ -444,6 +444,8 @@ common_exception_return:
1: l32i a3, a1, PT_PS 1: l32i a3, a1, PT_PS
_bbci.l a3, PS_UM_BIT, 4f _bbci.l a3, PS_UM_BIT, 4f
rsil a2, 0
/* Specific to a user exception exit: /* Specific to a user exception exit:
* We need to check some flags for signal handling and rescheduling, * We need to check some flags for signal handling and rescheduling,
* and have to restore WB and WS, extra states, and all registers * and have to restore WB and WS, extra states, and all registers
...@@ -684,51 +686,19 @@ common_exception_exit: ...@@ -684,51 +686,19 @@ common_exception_exit:
l32i a0, a1, PT_DEPC l32i a0, a1, PT_DEPC
l32i a3, a1, PT_AREG3 l32i a3, a1, PT_AREG3
_bltui a0, VALID_DOUBLE_EXCEPTION_ADDRESS, 1f
wsr a0, depc
l32i a2, a1, PT_AREG2 l32i a2, a1, PT_AREG2
l32i a0, a1, PT_AREG0 _bgeui a0, VALID_DOUBLE_EXCEPTION_ADDRESS, 1f
l32i a1, a1, PT_AREG1
rfde
1:
/* Restore a0...a3 and return */ /* Restore a0...a3 and return */
rsr a0, ps
extui a2, a0, PS_INTLEVEL_SHIFT, PS_INTLEVEL_WIDTH
movi a0, 2f
slli a2, a2, 4
add a0, a2, a0
l32i a2, a1, PT_AREG2
jx a0
.macro irq_exit_level level
.align 16
.if XCHAL_EXCM_LEVEL >= \level
l32i a0, a1, PT_PC
wsr a0, epc\level
l32i a0, a1, PT_AREG0 l32i a0, a1, PT_AREG0
l32i a1, a1, PT_AREG1 l32i a1, a1, PT_AREG1
rfi \level rfe
.endif
.endm
.align 16 1: wsr a0, depc
2:
l32i a0, a1, PT_AREG0 l32i a0, a1, PT_AREG0
l32i a1, a1, PT_AREG1 l32i a1, a1, PT_AREG1
rfe rfde
.align 16
/* no rfi for level-1 irq, handled by rfe above*/
nop
irq_exit_level 2
irq_exit_level 3
irq_exit_level 4
irq_exit_level 5
irq_exit_level 6
ENDPROC(kernel_exception) ENDPROC(kernel_exception)
......
...@@ -196,7 +196,6 @@ void do_multihit(struct pt_regs *regs, unsigned long exccause) ...@@ -196,7 +196,6 @@ void do_multihit(struct pt_regs *regs, unsigned long exccause)
/* /*
* IRQ handler. * IRQ handler.
* PS.INTLEVEL is the current IRQ priority level.
*/ */
extern void do_IRQ(int, struct pt_regs *); extern void do_IRQ(int, struct pt_regs *);
...@@ -213,18 +212,21 @@ void do_interrupt(struct pt_regs *regs) ...@@ -213,18 +212,21 @@ void do_interrupt(struct pt_regs *regs)
XCHAL_INTLEVEL6_MASK, XCHAL_INTLEVEL6_MASK,
XCHAL_INTLEVEL7_MASK, XCHAL_INTLEVEL7_MASK,
}; };
unsigned level = get_sr(ps) & PS_INTLEVEL_MASK;
if (WARN_ON_ONCE(level >= ARRAY_SIZE(int_level_mask)))
return;
for (;;) { for (;;) {
unsigned intread = get_sr(interrupt); unsigned intread = get_sr(interrupt);
unsigned intenable = get_sr(intenable); unsigned intenable = get_sr(intenable);
unsigned int_at_level = intread & intenable & unsigned int_at_level = intread & intenable;
int_level_mask[level]; unsigned level;
for (level = LOCKLEVEL; level > 0; --level) {
if (int_at_level & int_level_mask[level]) {
int_at_level &= int_level_mask[level];
break;
}
}
if (!int_at_level) if (level == 0)
return; return;
/* /*
......
...@@ -386,9 +386,12 @@ ENDPROC(_DebugInterruptVector) ...@@ -386,9 +386,12 @@ ENDPROC(_DebugInterruptVector)
.if XCHAL_EXCM_LEVEL >= \level .if XCHAL_EXCM_LEVEL >= \level
.section .Level\level\()InterruptVector.text, "ax" .section .Level\level\()InterruptVector.text, "ax"
ENTRY(_Level\level\()InterruptVector) ENTRY(_Level\level\()InterruptVector)
wsr a0, epc1 wsr a0, excsave2
rsr a0, epc\level rsr a0, epc\level
xsr a0, epc1 wsr a0, epc1
movi a0, EXCCAUSE_LEVEL1_INTERRUPT
wsr a0, exccause
rsr a0, eps\level
# branch to user or kernel vector # branch to user or kernel vector
j _SimulateUserKernelVectorException j _SimulateUserKernelVectorException
.endif .endif
...@@ -440,10 +443,8 @@ ENDPROC(_WindowOverflow4) ...@@ -440,10 +443,8 @@ ENDPROC(_WindowOverflow4)
*/ */
.align 4 .align 4
_SimulateUserKernelVectorException: _SimulateUserKernelVectorException:
wsr a0, excsave2 addi a0, a0, (1 << PS_EXCM_BIT)
movi a0, 4 # LEVEL1_INTERRUPT cause wsr a0, ps
wsr a0, exccause
rsr a0, ps
bbsi.l a0, PS_UM_BIT, 1f # branch if user mode bbsi.l a0, PS_UM_BIT, 1f # branch if user mode
rsr a0, excsave2 # restore a0 rsr a0, excsave2 # restore a0
j _KernelExceptionVector # simulate kernel vector exception j _KernelExceptionVector # simulate kernel vector exception
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment