summaryrefslogtreecommitdiffstats
path: root/arch/cris/arch-v32/kernel/entry.S
diff options
context:
space:
mode:
authorRabin Vincent <rabin@rab.in>2015-02-08 16:57:40 +0100
committerJesper Nilsson <jespern@axis.com>2015-03-25 10:49:31 +0100
commitdb4a35c651a10eddc6b48b69e1db1f46bea303fa (patch)
treefb86ccca5a5cfcd44be916b8016d5e1922d7eee8 /arch/cris/arch-v32/kernel/entry.S
parent1eb1390bb21b1aa3b303627eb254296f097988cb (diff)
downloadop-kernel-dev-db4a35c651a10eddc6b48b69e1db1f46bea303fa.zip
op-kernel-dev-db4a35c651a10eddc6b48b69e1db1f46bea303fa.tar.gz
CRISv32: don't attempt syscall restart on irq exit
r9 is used to determine whether syscall restarting must be performed or not. Unfortunately, r9 is never set to zero in the non-syscall path, and r9 is on top of that a callee-saved register which can be set to non-zero by the C functions that are called during IRQ handling. This means that if r10 (used for the syscall return value) is one of the -ERESTART* values when a hardware interrupt occurs which leads to a signal being delivered to the process, the kernel will "restart" a syscall which never occurred. This will lead to the PC being moved back by 2 on return to user space. Fix the problem by setting r9 to zero in the interrupt path. Test case (should loop forever but ends up executing the break 8 trap instruction): #include <signal.h> #include <stdlib.h> #include <sys/time.h> void f(int n) { register int r9 asm ("r9") = 1; register int r10 asm ("r10") = n; __asm__ __volatile__( "ba 1f \n" "nop \n" "break 8 \n" "1: ba . \n" "nop \n" : : "r" (r9), "r" (r10) : "memory"); } void handler1(int sig) { } int main(int argc, char *argv[]) { struct itimerval t1 = { .it_value = {1} }; signal(SIGALRM, handler1); setitimer(ITIMER_REAL, &t1, NULL); f(-513); /* -ERESTARTNOINTR */ return 0; } Signed-off-by: Rabin Vincent <rabin@rab.in> Signed-off-by: Jesper Nilsson <jespern@axis.com>
Diffstat (limited to 'arch/cris/arch-v32/kernel/entry.S')
-rw-r--r--arch/cris/arch-v32/kernel/entry.S2
1 files changed, 2 insertions, 0 deletions
diff --git a/arch/cris/arch-v32/kernel/entry.S b/arch/cris/arch-v32/kernel/entry.S
index 2f19ac6..d4c088b 100644
--- a/arch/cris/arch-v32/kernel/entry.S
+++ b/arch/cris/arch-v32/kernel/entry.S
@@ -99,6 +99,8 @@ ret_from_kernel_thread:
.type ret_from_intr,@function
ret_from_intr:
+ moveq 0, $r9 ; not a syscall
+
;; Check for resched if preemptive kernel, or if we're going back to
;; user-mode. This test matches the user_regs(regs) macro. Don't simply
;; test CCS since that doesn't necessarily reflect what mode we'll
OpenPOWER on IntegriCloud