CRISv32: don't attempt syscall restart on irq exit
authorRabin Vincent <rabin@rab.in>
Sun, 8 Feb 2015 15:57:40 +0000 (16:57 +0100)
committerJesper Nilsson <jespern@axis.com>
Wed, 25 Mar 2015 09:49:31 +0000 (10:49 +0100)
commitdb4a35c651a10eddc6b48b69e1db1f46bea303fa
treefb86ccca5a5cfcd44be916b8016d5e1922d7eee8
parent1eb1390bb21b1aa3b303627eb254296f097988cb
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>
arch/cris/arch-v32/kernel/entry.S