diff options
| -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. |
