diff options
Diffstat (limited to 'arch/sparc/kernel/signal32.c')
| -rw-r--r-- | arch/sparc/kernel/signal32.c | 161 |
1 files changed, 97 insertions, 64 deletions
diff --git a/arch/sparc/kernel/signal32.c b/arch/sparc/kernel/signal32.c index ea22cd373c64..75fad425e249 100644 --- a/arch/sparc/kernel/signal32.c +++ b/arch/sparc/kernel/signal32.c | |||
| @@ -453,8 +453,66 @@ static int save_fpu_state32(struct pt_regs *regs, __siginfo_fpu_t __user *fpu) | |||
| 453 | return err; | 453 | return err; |
| 454 | } | 454 | } |
| 455 | 455 | ||
| 456 | static void setup_frame32(struct k_sigaction *ka, struct pt_regs *regs, | 456 | /* The I-cache flush instruction only works in the primary ASI, which |
| 457 | int signo, sigset_t *oldset) | 457 | * right now is the nucleus, aka. kernel space. |
| 458 | * | ||
| 459 | * Therefore we have to kick the instructions out using the kernel | ||
| 460 | * side linear mapping of the physical address backing the user | ||
| 461 | * instructions. | ||
| 462 | */ | ||
| 463 | static void flush_signal_insns(unsigned long address) | ||
| 464 | { | ||
| 465 | unsigned long pstate, paddr; | ||
| 466 | pte_t *ptep, pte; | ||
| 467 | pgd_t *pgdp; | ||
| 468 | pud_t *pudp; | ||
| 469 | pmd_t *pmdp; | ||
| 470 | |||
| 471 | /* Commit all stores of the instructions we are about to flush. */ | ||
| 472 | wmb(); | ||
| 473 | |||
| 474 | /* Disable cross-call reception. In this way even a very wide | ||
| 475 | * munmap() on another cpu can't tear down the page table | ||
| 476 | * hierarchy from underneath us, since that can't complete | ||
| 477 | * until the IPI tlb flush returns. | ||
| 478 | */ | ||
| 479 | |||
| 480 | __asm__ __volatile__("rdpr %%pstate, %0" : "=r" (pstate)); | ||
| 481 | __asm__ __volatile__("wrpr %0, %1, %%pstate" | ||
| 482 | : : "r" (pstate), "i" (PSTATE_IE)); | ||
| 483 | |||
| 484 | pgdp = pgd_offset(current->mm, address); | ||
| 485 | if (pgd_none(*pgdp)) | ||
| 486 | goto out_irqs_on; | ||
| 487 | pudp = pud_offset(pgdp, address); | ||
| 488 | if (pud_none(*pudp)) | ||
| 489 | goto out_irqs_on; | ||
| 490 | pmdp = pmd_offset(pudp, address); | ||
| 491 | if (pmd_none(*pmdp)) | ||
| 492 | goto out_irqs_on; | ||
| 493 | |||
| 494 | ptep = pte_offset_map(pmdp, address); | ||
| 495 | pte = *ptep; | ||
| 496 | if (!pte_present(pte)) | ||
| 497 | goto out_unmap; | ||
| 498 | |||
| 499 | paddr = (unsigned long) page_address(pte_page(pte)); | ||
| 500 | |||
| 501 | __asm__ __volatile__("flush %0 + %1" | ||
| 502 | : /* no outputs */ | ||
| 503 | : "r" (paddr), | ||
| 504 | "r" (address & (PAGE_SIZE - 1)) | ||
| 505 | : "memory"); | ||
| 506 | |||
| 507 | out_unmap: | ||
| 508 | pte_unmap(ptep); | ||
| 509 | out_irqs_on: | ||
| 510 | __asm__ __volatile__("wrpr %0, 0x0, %%pstate" : : "r" (pstate)); | ||
| 511 | |||
| 512 | } | ||
| 513 | |||
| 514 | static int setup_frame32(struct k_sigaction *ka, struct pt_regs *regs, | ||
| 515 | int signo, sigset_t *oldset) | ||
| 458 | { | 516 | { |
| 459 | struct signal_frame32 __user *sf; | 517 | struct signal_frame32 __user *sf; |
| 460 | int sigframe_size; | 518 | int sigframe_size; |
| @@ -547,13 +605,7 @@ static void setup_frame32(struct k_sigaction *ka, struct pt_regs *regs, | |||
| 547 | if (ka->ka_restorer) { | 605 | if (ka->ka_restorer) { |
| 548 | regs->u_regs[UREG_I7] = (unsigned long)ka->ka_restorer; | 606 | regs->u_regs[UREG_I7] = (unsigned long)ka->ka_restorer; |
| 549 | } else { | 607 | } else { |
| 550 | /* Flush instruction space. */ | ||
| 551 | unsigned long address = ((unsigned long)&(sf->insns[0])); | 608 | unsigned long address = ((unsigned long)&(sf->insns[0])); |
| 552 | pgd_t *pgdp = pgd_offset(current->mm, address); | ||
| 553 | pud_t *pudp = pud_offset(pgdp, address); | ||
| 554 | pmd_t *pmdp = pmd_offset(pudp, address); | ||
| 555 | pte_t *ptep; | ||
| 556 | pte_t pte; | ||
| 557 | 609 | ||
| 558 | regs->u_regs[UREG_I7] = (unsigned long) (&(sf->insns[0]) - 2); | 610 | regs->u_regs[UREG_I7] = (unsigned long) (&(sf->insns[0]) - 2); |
| 559 | 611 | ||
| @@ -562,34 +614,22 @@ static void setup_frame32(struct k_sigaction *ka, struct pt_regs *regs, | |||
| 562 | if (err) | 614 | if (err) |
| 563 | goto sigsegv; | 615 | goto sigsegv; |
| 564 | 616 | ||
| 565 | preempt_disable(); | 617 | flush_signal_insns(address); |
| 566 | ptep = pte_offset_map(pmdp, address); | ||
| 567 | pte = *ptep; | ||
| 568 | if (pte_present(pte)) { | ||
| 569 | unsigned long page = (unsigned long) | ||
| 570 | page_address(pte_page(pte)); | ||
| 571 | |||
| 572 | wmb(); | ||
| 573 | __asm__ __volatile__("flush %0 + %1" | ||
| 574 | : /* no outputs */ | ||
| 575 | : "r" (page), | ||
| 576 | "r" (address & (PAGE_SIZE - 1)) | ||
| 577 | : "memory"); | ||
| 578 | } | ||
| 579 | pte_unmap(ptep); | ||
| 580 | preempt_enable(); | ||
| 581 | } | 618 | } |
| 582 | return; | 619 | return 0; |
| 583 | 620 | ||
| 584 | sigill: | 621 | sigill: |
| 585 | do_exit(SIGILL); | 622 | do_exit(SIGILL); |
| 623 | return -EINVAL; | ||
| 624 | |||
| 586 | sigsegv: | 625 | sigsegv: |
| 587 | force_sigsegv(signo, current); | 626 | force_sigsegv(signo, current); |
| 627 | return -EFAULT; | ||
| 588 | } | 628 | } |
| 589 | 629 | ||
| 590 | static void setup_rt_frame32(struct k_sigaction *ka, struct pt_regs *regs, | 630 | static int setup_rt_frame32(struct k_sigaction *ka, struct pt_regs *regs, |
| 591 | unsigned long signr, sigset_t *oldset, | 631 | unsigned long signr, sigset_t *oldset, |
| 592 | siginfo_t *info) | 632 | siginfo_t *info) |
| 593 | { | 633 | { |
| 594 | struct rt_signal_frame32 __user *sf; | 634 | struct rt_signal_frame32 __user *sf; |
| 595 | int sigframe_size; | 635 | int sigframe_size; |
| @@ -687,12 +727,7 @@ static void setup_rt_frame32(struct k_sigaction *ka, struct pt_regs *regs, | |||
| 687 | if (ka->ka_restorer) | 727 | if (ka->ka_restorer) |
| 688 | regs->u_regs[UREG_I7] = (unsigned long)ka->ka_restorer; | 728 | regs->u_regs[UREG_I7] = (unsigned long)ka->ka_restorer; |
| 689 | else { | 729 | else { |
| 690 | /* Flush instruction space. */ | ||
| 691 | unsigned long address = ((unsigned long)&(sf->insns[0])); | 730 | unsigned long address = ((unsigned long)&(sf->insns[0])); |
| 692 | pgd_t *pgdp = pgd_offset(current->mm, address); | ||
| 693 | pud_t *pudp = pud_offset(pgdp, address); | ||
| 694 | pmd_t *pmdp = pmd_offset(pudp, address); | ||
| 695 | pte_t *ptep; | ||
| 696 | 731 | ||
| 697 | regs->u_regs[UREG_I7] = (unsigned long) (&(sf->insns[0]) - 2); | 732 | regs->u_regs[UREG_I7] = (unsigned long) (&(sf->insns[0]) - 2); |
| 698 | 733 | ||
| @@ -704,38 +739,32 @@ static void setup_rt_frame32(struct k_sigaction *ka, struct pt_regs *regs, | |||
| 704 | if (err) | 739 | if (err) |
| 705 | goto sigsegv; | 740 | goto sigsegv; |
| 706 | 741 | ||
| 707 | preempt_disable(); | 742 | flush_signal_insns(address); |
| 708 | ptep = pte_offset_map(pmdp, address); | ||
| 709 | if (pte_present(*ptep)) { | ||
| 710 | unsigned long page = (unsigned long) | ||
| 711 | page_address(pte_page(*ptep)); | ||
| 712 | |||
| 713 | wmb(); | ||
| 714 | __asm__ __volatile__("flush %0 + %1" | ||
| 715 | : /* no outputs */ | ||
| 716 | : "r" (page), | ||
| 717 | "r" (address & (PAGE_SIZE - 1)) | ||
| 718 | : "memory"); | ||
| 719 | } | ||
| 720 | pte_unmap(ptep); | ||
| 721 | preempt_enable(); | ||
| 722 | } | 743 | } |
| 723 | return; | 744 | return 0; |
| 724 | 745 | ||
| 725 | sigill: | 746 | sigill: |
| 726 | do_exit(SIGILL); | 747 | do_exit(SIGILL); |
| 748 | return -EINVAL; | ||
| 749 | |||
| 727 | sigsegv: | 750 | sigsegv: |
| 728 | force_sigsegv(signr, current); | 751 | force_sigsegv(signr, current); |
| 752 | return -EFAULT; | ||
| 729 | } | 753 | } |
| 730 | 754 | ||
| 731 | static inline void handle_signal32(unsigned long signr, struct k_sigaction *ka, | 755 | static inline int handle_signal32(unsigned long signr, struct k_sigaction *ka, |
| 732 | siginfo_t *info, | 756 | siginfo_t *info, |
| 733 | sigset_t *oldset, struct pt_regs *regs) | 757 | sigset_t *oldset, struct pt_regs *regs) |
| 734 | { | 758 | { |
| 759 | int err; | ||
| 760 | |||
| 735 | if (ka->sa.sa_flags & SA_SIGINFO) | 761 | if (ka->sa.sa_flags & SA_SIGINFO) |
| 736 | setup_rt_frame32(ka, regs, signr, oldset, info); | 762 | err = setup_rt_frame32(ka, regs, signr, oldset, info); |
| 737 | else | 763 | else |
| 738 | setup_frame32(ka, regs, signr, oldset); | 764 | err = setup_frame32(ka, regs, signr, oldset); |
| 765 | |||
| 766 | if (err) | ||
| 767 | return err; | ||
| 739 | 768 | ||
| 740 | spin_lock_irq(¤t->sighand->siglock); | 769 | spin_lock_irq(¤t->sighand->siglock); |
| 741 | sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); | 770 | sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); |
| @@ -743,6 +772,10 @@ static inline void handle_signal32(unsigned long signr, struct k_sigaction *ka, | |||
| 743 | sigaddset(¤t->blocked,signr); | 772 | sigaddset(¤t->blocked,signr); |
| 744 | recalc_sigpending(); | 773 | recalc_sigpending(); |
| 745 | spin_unlock_irq(¤t->sighand->siglock); | 774 | spin_unlock_irq(¤t->sighand->siglock); |
| 775 | |||
| 776 | tracehook_signal_handler(signr, info, ka, regs, 0); | ||
| 777 | |||
| 778 | return 0; | ||
| 746 | } | 779 | } |
| 747 | 780 | ||
| 748 | static inline void syscall_restart32(unsigned long orig_i0, struct pt_regs *regs, | 781 | static inline void syscall_restart32(unsigned long orig_i0, struct pt_regs *regs, |
| @@ -789,16 +822,14 @@ void do_signal32(sigset_t *oldset, struct pt_regs * regs, | |||
| 789 | if (signr > 0) { | 822 | if (signr > 0) { |
| 790 | if (restart_syscall) | 823 | if (restart_syscall) |
| 791 | syscall_restart32(orig_i0, regs, &ka.sa); | 824 | syscall_restart32(orig_i0, regs, &ka.sa); |
| 792 | handle_signal32(signr, &ka, &info, oldset, regs); | 825 | if (handle_signal32(signr, &ka, &info, oldset, regs) == 0) { |
| 793 | 826 | /* A signal was successfully delivered; the saved | |
| 794 | /* A signal was successfully delivered; the saved | 827 | * sigmask will have been stored in the signal frame, |
| 795 | * sigmask will have been stored in the signal frame, | 828 | * and will be restored by sigreturn, so we can simply |
| 796 | * and will be restored by sigreturn, so we can simply | 829 | * clear the TS_RESTORE_SIGMASK flag. |
| 797 | * clear the TS_RESTORE_SIGMASK flag. | 830 | */ |
| 798 | */ | 831 | current_thread_info()->status &= ~TS_RESTORE_SIGMASK; |
| 799 | current_thread_info()->status &= ~TS_RESTORE_SIGMASK; | 832 | } |
| 800 | |||
| 801 | tracehook_signal_handler(signr, &info, &ka, regs, 0); | ||
| 802 | return; | 833 | return; |
| 803 | } | 834 | } |
| 804 | if (restart_syscall && | 835 | if (restart_syscall && |
| @@ -809,12 +840,14 @@ void do_signal32(sigset_t *oldset, struct pt_regs * regs, | |||
| 809 | regs->u_regs[UREG_I0] = orig_i0; | 840 | regs->u_regs[UREG_I0] = orig_i0; |
| 810 | regs->tpc -= 4; | 841 | regs->tpc -= 4; |
| 811 | regs->tnpc -= 4; | 842 | regs->tnpc -= 4; |
| 843 | pt_regs_clear_syscall(regs); | ||
| 812 | } | 844 | } |
| 813 | if (restart_syscall && | 845 | if (restart_syscall && |
| 814 | regs->u_regs[UREG_I0] == ERESTART_RESTARTBLOCK) { | 846 | regs->u_regs[UREG_I0] == ERESTART_RESTARTBLOCK) { |
| 815 | regs->u_regs[UREG_G1] = __NR_restart_syscall; | 847 | regs->u_regs[UREG_G1] = __NR_restart_syscall; |
| 816 | regs->tpc -= 4; | 848 | regs->tpc -= 4; |
| 817 | regs->tnpc -= 4; | 849 | regs->tnpc -= 4; |
| 850 | pt_regs_clear_syscall(regs); | ||
| 818 | } | 851 | } |
| 819 | 852 | ||
| 820 | /* If there's no signal to deliver, we just put the saved sigmask | 853 | /* If there's no signal to deliver, we just put the saved sigmask |
