diff options
Diffstat (limited to 'arch/arm/kernel/signal.c')
-rw-r--r-- | arch/arm/kernel/signal.c | 90 |
1 files changed, 53 insertions, 37 deletions
diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c index cb8398317644..0340224cf73c 100644 --- a/arch/arm/kernel/signal.c +++ b/arch/arm/kernel/signal.c | |||
@@ -597,19 +597,13 @@ setup_rt_frame(int usig, struct k_sigaction *ka, siginfo_t *info, | |||
597 | return err; | 597 | return err; |
598 | } | 598 | } |
599 | 599 | ||
600 | static inline void setup_syscall_restart(struct pt_regs *regs) | ||
601 | { | ||
602 | regs->ARM_r0 = regs->ARM_ORIG_r0; | ||
603 | regs->ARM_pc -= thumb_mode(regs) ? 2 : 4; | ||
604 | } | ||
605 | |||
606 | /* | 600 | /* |
607 | * OK, we're invoking a handler | 601 | * OK, we're invoking a handler |
608 | */ | 602 | */ |
609 | static int | 603 | static int |
610 | handle_signal(unsigned long sig, struct k_sigaction *ka, | 604 | handle_signal(unsigned long sig, struct k_sigaction *ka, |
611 | siginfo_t *info, sigset_t *oldset, | 605 | siginfo_t *info, sigset_t *oldset, |
612 | struct pt_regs * regs, int syscall) | 606 | struct pt_regs * regs) |
613 | { | 607 | { |
614 | struct thread_info *thread = current_thread_info(); | 608 | struct thread_info *thread = current_thread_info(); |
615 | struct task_struct *tsk = current; | 609 | struct task_struct *tsk = current; |
@@ -617,26 +611,6 @@ handle_signal(unsigned long sig, struct k_sigaction *ka, | |||
617 | int ret; | 611 | int ret; |
618 | 612 | ||
619 | /* | 613 | /* |
620 | * If we were from a system call, check for system call restarting... | ||
621 | */ | ||
622 | if (syscall) { | ||
623 | switch (regs->ARM_r0) { | ||
624 | case -ERESTART_RESTARTBLOCK: | ||
625 | case -ERESTARTNOHAND: | ||
626 | regs->ARM_r0 = -EINTR; | ||
627 | break; | ||
628 | case -ERESTARTSYS: | ||
629 | if (!(ka->sa.sa_flags & SA_RESTART)) { | ||
630 | regs->ARM_r0 = -EINTR; | ||
631 | break; | ||
632 | } | ||
633 | /* fallthrough */ | ||
634 | case -ERESTARTNOINTR: | ||
635 | setup_syscall_restart(regs); | ||
636 | } | ||
637 | } | ||
638 | |||
639 | /* | ||
640 | * translate the signal | 614 | * translate the signal |
641 | */ | 615 | */ |
642 | if (usig < 32 && thread->exec_domain && thread->exec_domain->signal_invmap) | 616 | if (usig < 32 && thread->exec_domain && thread->exec_domain->signal_invmap) |
@@ -685,6 +659,7 @@ handle_signal(unsigned long sig, struct k_sigaction *ka, | |||
685 | */ | 659 | */ |
686 | static void do_signal(struct pt_regs *regs, int syscall) | 660 | static void do_signal(struct pt_regs *regs, int syscall) |
687 | { | 661 | { |
662 | unsigned int retval = 0, continue_addr = 0, restart_addr = 0; | ||
688 | struct k_sigaction ka; | 663 | struct k_sigaction ka; |
689 | siginfo_t info; | 664 | siginfo_t info; |
690 | int signr; | 665 | int signr; |
@@ -698,18 +673,61 @@ static void do_signal(struct pt_regs *regs, int syscall) | |||
698 | if (!user_mode(regs)) | 673 | if (!user_mode(regs)) |
699 | return; | 674 | return; |
700 | 675 | ||
676 | /* | ||
677 | * If we were from a system call, check for system call restarting... | ||
678 | */ | ||
679 | if (syscall) { | ||
680 | continue_addr = regs->ARM_pc; | ||
681 | restart_addr = continue_addr - (thumb_mode(regs) ? 2 : 4); | ||
682 | retval = regs->ARM_r0; | ||
683 | |||
684 | /* | ||
685 | * Prepare for system call restart. We do this here so that a | ||
686 | * debugger will see the already changed PSW. | ||
687 | */ | ||
688 | switch (retval) { | ||
689 | case -ERESTARTNOHAND: | ||
690 | case -ERESTARTSYS: | ||
691 | case -ERESTARTNOINTR: | ||
692 | regs->ARM_r0 = regs->ARM_ORIG_r0; | ||
693 | regs->ARM_pc = restart_addr; | ||
694 | break; | ||
695 | case -ERESTART_RESTARTBLOCK: | ||
696 | regs->ARM_r0 = -EINTR; | ||
697 | break; | ||
698 | } | ||
699 | } | ||
700 | |||
701 | if (try_to_freeze()) | 701 | if (try_to_freeze()) |
702 | goto no_signal; | 702 | goto no_signal; |
703 | 703 | ||
704 | /* | ||
705 | * Get the signal to deliver. When running under ptrace, at this | ||
706 | * point the debugger may change all our registers ... | ||
707 | */ | ||
704 | signr = get_signal_to_deliver(&info, &ka, regs, NULL); | 708 | signr = get_signal_to_deliver(&info, &ka, regs, NULL); |
705 | if (signr > 0) { | 709 | if (signr > 0) { |
706 | sigset_t *oldset; | 710 | sigset_t *oldset; |
707 | 711 | ||
712 | /* | ||
713 | * Depending on the signal settings we may need to revert the | ||
714 | * decision to restart the system call. But skip this if a | ||
715 | * debugger has chosen to restart at a different PC. | ||
716 | */ | ||
717 | if (regs->ARM_pc == restart_addr) { | ||
718 | if (retval == -ERESTARTNOHAND | ||
719 | || (retval == -ERESTARTSYS | ||
720 | && !(ka.sa.sa_flags & SA_RESTART))) { | ||
721 | regs->ARM_r0 = -EINTR; | ||
722 | regs->ARM_pc = continue_addr; | ||
723 | } | ||
724 | } | ||
725 | |||
708 | if (test_thread_flag(TIF_RESTORE_SIGMASK)) | 726 | if (test_thread_flag(TIF_RESTORE_SIGMASK)) |
709 | oldset = ¤t->saved_sigmask; | 727 | oldset = ¤t->saved_sigmask; |
710 | else | 728 | else |
711 | oldset = ¤t->blocked; | 729 | oldset = ¤t->blocked; |
712 | if (handle_signal(signr, &ka, &info, oldset, regs, syscall) == 0) { | 730 | if (handle_signal(signr, &ka, &info, oldset, regs) == 0) { |
713 | /* | 731 | /* |
714 | * A signal was successfully delivered; the saved | 732 | * A signal was successfully delivered; the saved |
715 | * sigmask will have been stored in the signal frame, | 733 | * sigmask will have been stored in the signal frame, |
@@ -723,11 +741,14 @@ static void do_signal(struct pt_regs *regs, int syscall) | |||
723 | } | 741 | } |
724 | 742 | ||
725 | no_signal: | 743 | no_signal: |
726 | /* | ||
727 | * No signal to deliver to the process - restart the syscall. | ||
728 | */ | ||
729 | if (syscall) { | 744 | if (syscall) { |
730 | if (regs->ARM_r0 == -ERESTART_RESTARTBLOCK) { | 745 | /* |
746 | * Handle restarting a different system call. As above, | ||
747 | * if a debugger has chosen to restart at a different PC, | ||
748 | * ignore the restart. | ||
749 | */ | ||
750 | if (retval == -ERESTART_RESTARTBLOCK | ||
751 | && regs->ARM_pc == continue_addr) { | ||
731 | if (thumb_mode(regs)) { | 752 | if (thumb_mode(regs)) { |
732 | regs->ARM_r7 = __NR_restart_syscall - __NR_SYSCALL_BASE; | 753 | regs->ARM_r7 = __NR_restart_syscall - __NR_SYSCALL_BASE; |
733 | regs->ARM_pc -= 2; | 754 | regs->ARM_pc -= 2; |
@@ -750,11 +771,6 @@ static void do_signal(struct pt_regs *regs, int syscall) | |||
750 | #endif | 771 | #endif |
751 | } | 772 | } |
752 | } | 773 | } |
753 | if (regs->ARM_r0 == -ERESTARTNOHAND || | ||
754 | regs->ARM_r0 == -ERESTARTSYS || | ||
755 | regs->ARM_r0 == -ERESTARTNOINTR) { | ||
756 | setup_syscall_restart(regs); | ||
757 | } | ||
758 | 774 | ||
759 | /* If there's no signal to deliver, we just put the saved sigmask | 775 | /* If there's no signal to deliver, we just put the saved sigmask |
760 | * back. | 776 | * back. |