diff options
| -rw-r--r-- | arch/arm/kernel/entry-common.S | 24 | ||||
| -rw-r--r-- | arch/arm/kernel/ptrace.c | 1 | ||||
| -rw-r--r-- | arch/arm/kernel/signal.c | 114 | ||||
| -rw-r--r-- | arch/arm/kernel/signal.h | 2 | ||||
| -rw-r--r-- | arch/arm/kernel/traps.c | 2 |
5 files changed, 58 insertions, 85 deletions
diff --git a/arch/arm/kernel/entry-common.S b/arch/arm/kernel/entry-common.S index 49d9f9305247..978eac57e04a 100644 --- a/arch/arm/kernel/entry-common.S +++ b/arch/arm/kernel/entry-common.S | |||
| @@ -51,23 +51,15 @@ ret_fast_syscall: | |||
| 51 | fast_work_pending: | 51 | fast_work_pending: |
| 52 | str r0, [sp, #S_R0+S_OFF]! @ returned r0 | 52 | str r0, [sp, #S_R0+S_OFF]! @ returned r0 |
| 53 | work_pending: | 53 | work_pending: |
| 54 | tst r1, #_TIF_NEED_RESCHED | ||
| 55 | bne work_resched | ||
| 56 | /* | ||
| 57 | * TIF_SIGPENDING or TIF_NOTIFY_RESUME must've been set if we got here | ||
| 58 | */ | ||
| 59 | ldr r2, [sp, #S_PSR] | ||
| 60 | mov r0, sp @ 'regs' | 54 | mov r0, sp @ 'regs' |
| 61 | tst r2, #15 @ are we returning to user mode? | ||
| 62 | bne no_work_pending @ no? just leave, then... | ||
| 63 | mov r2, why @ 'syscall' | 55 | mov r2, why @ 'syscall' |
| 64 | tst r1, #_TIF_SIGPENDING @ delivering a signal? | 56 | bl do_work_pending |
| 65 | movne why, #0 @ prevent further restarts | 57 | cmp r0, #0 |
| 66 | bl do_notify_resume | 58 | beq no_work_pending |
| 67 | b ret_slow_syscall @ Check work again | 59 | movlt scno, #(__NR_restart_syscall - __NR_SYSCALL_BASE) |
| 60 | ldmia sp, {r0 - r6} @ have to reload r0 - r6 | ||
| 61 | b local_restart @ ... and off we go | ||
| 68 | 62 | ||
| 69 | work_resched: | ||
| 70 | bl schedule | ||
| 71 | /* | 63 | /* |
| 72 | * "slow" syscall return path. "why" tells us if this was a real syscall. | 64 | * "slow" syscall return path. "why" tells us if this was a real syscall. |
| 73 | */ | 65 | */ |
| @@ -409,6 +401,7 @@ ENTRY(vector_swi) | |||
| 409 | eor scno, scno, #__NR_SYSCALL_BASE @ check OS number | 401 | eor scno, scno, #__NR_SYSCALL_BASE @ check OS number |
| 410 | #endif | 402 | #endif |
| 411 | 403 | ||
| 404 | local_restart: | ||
| 412 | ldr r10, [tsk, #TI_FLAGS] @ check for syscall tracing | 405 | ldr r10, [tsk, #TI_FLAGS] @ check for syscall tracing |
| 413 | stmdb sp!, {r4, r5} @ push fifth and sixth args | 406 | stmdb sp!, {r4, r5} @ push fifth and sixth args |
| 414 | 407 | ||
| @@ -450,7 +443,8 @@ __sys_trace: | |||
| 450 | mov scno, r0 @ syscall number (possibly new) | 443 | mov scno, r0 @ syscall number (possibly new) |
| 451 | add r1, sp, #S_R0 + S_OFF @ pointer to regs | 444 | add r1, sp, #S_R0 + S_OFF @ pointer to regs |
| 452 | cmp scno, #NR_syscalls @ check upper syscall limit | 445 | cmp scno, #NR_syscalls @ check upper syscall limit |
| 453 | ldmccia r1, {r0 - r3} @ have to reload r0 - r3 | 446 | ldmccia r1, {r0 - r6} @ have to reload r0 - r6 |
| 447 | stmccia sp, {r4, r5} @ and update the stack args | ||
| 454 | ldrcc pc, [tbl, scno, lsl #2] @ call sys_* routine | 448 | ldrcc pc, [tbl, scno, lsl #2] @ call sys_* routine |
| 455 | b 2b | 449 | b 2b |
| 456 | 450 | ||
diff --git a/arch/arm/kernel/ptrace.c b/arch/arm/kernel/ptrace.c index dab711e6e1ca..3e0fc5f7ed4b 100644 --- a/arch/arm/kernel/ptrace.c +++ b/arch/arm/kernel/ptrace.c | |||
| @@ -25,6 +25,7 @@ | |||
| 25 | #include <linux/regset.h> | 25 | #include <linux/regset.h> |
| 26 | #include <linux/audit.h> | 26 | #include <linux/audit.h> |
| 27 | #include <linux/tracehook.h> | 27 | #include <linux/tracehook.h> |
| 28 | #include <linux/unistd.h> | ||
| 28 | 29 | ||
| 29 | #include <asm/pgtable.h> | 30 | #include <asm/pgtable.h> |
| 30 | #include <asm/traps.h> | 31 | #include <asm/traps.h> |
diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c index 536c5d6b340b..f27789e4e38a 100644 --- a/arch/arm/kernel/signal.c +++ b/arch/arm/kernel/signal.c | |||
| @@ -27,7 +27,6 @@ | |||
| 27 | */ | 27 | */ |
| 28 | #define SWI_SYS_SIGRETURN (0xef000000|(__NR_sigreturn)|(__NR_OABI_SYSCALL_BASE)) | 28 | #define SWI_SYS_SIGRETURN (0xef000000|(__NR_sigreturn)|(__NR_OABI_SYSCALL_BASE)) |
| 29 | #define SWI_SYS_RT_SIGRETURN (0xef000000|(__NR_rt_sigreturn)|(__NR_OABI_SYSCALL_BASE)) | 29 | #define SWI_SYS_RT_SIGRETURN (0xef000000|(__NR_rt_sigreturn)|(__NR_OABI_SYSCALL_BASE)) |
| 30 | #define SWI_SYS_RESTART (0xef000000|__NR_restart_syscall|__NR_OABI_SYSCALL_BASE) | ||
| 31 | 30 | ||
| 32 | /* | 31 | /* |
| 33 | * With EABI, the syscall number has to be loaded into r7. | 32 | * With EABI, the syscall number has to be loaded into r7. |
| @@ -48,18 +47,6 @@ const unsigned long sigreturn_codes[7] = { | |||
| 48 | }; | 47 | }; |
| 49 | 48 | ||
| 50 | /* | 49 | /* |
| 51 | * Either we support OABI only, or we have EABI with the OABI | ||
| 52 | * compat layer enabled. In the later case we don't know if | ||
| 53 | * user space is EABI or not, and if not we must not clobber r7. | ||
| 54 | * Always using the OABI syscall solves that issue and works for | ||
| 55 | * all those cases. | ||
| 56 | */ | ||
| 57 | const unsigned long syscall_restart_code[2] = { | ||
| 58 | SWI_SYS_RESTART, /* swi __NR_restart_syscall */ | ||
| 59 | 0xe49df004, /* ldr pc, [sp], #4 */ | ||
| 60 | }; | ||
| 61 | |||
| 62 | /* | ||
| 63 | * atomically swap in the new signal mask, and wait for a signal. | 50 | * atomically swap in the new signal mask, and wait for a signal. |
| 64 | */ | 51 | */ |
| 65 | asmlinkage int sys_sigsuspend(int restart, unsigned long oldmask, old_sigset_t mask) | 52 | asmlinkage int sys_sigsuspend(int restart, unsigned long oldmask, old_sigset_t mask) |
| @@ -582,12 +569,13 @@ handle_signal(unsigned long sig, struct k_sigaction *ka, | |||
| 582 | * the kernel can handle, and then we build all the user-level signal handling | 569 | * the kernel can handle, and then we build all the user-level signal handling |
| 583 | * stack-frames in one go after that. | 570 | * stack-frames in one go after that. |
| 584 | */ | 571 | */ |
| 585 | static void do_signal(struct pt_regs *regs, int syscall) | 572 | static int do_signal(struct pt_regs *regs, int syscall) |
| 586 | { | 573 | { |
| 587 | unsigned int retval = 0, continue_addr = 0, restart_addr = 0; | 574 | unsigned int retval = 0, continue_addr = 0, restart_addr = 0; |
| 588 | struct k_sigaction ka; | 575 | struct k_sigaction ka; |
| 589 | siginfo_t info; | 576 | siginfo_t info; |
| 590 | int signr; | 577 | int signr; |
| 578 | int restart = 0; | ||
| 591 | 579 | ||
| 592 | /* | 580 | /* |
| 593 | * If we were from a system call, check for system call restarting... | 581 | * If we were from a system call, check for system call restarting... |
| @@ -602,15 +590,15 @@ static void do_signal(struct pt_regs *regs, int syscall) | |||
| 602 | * debugger will see the already changed PSW. | 590 | * debugger will see the already changed PSW. |
| 603 | */ | 591 | */ |
| 604 | switch (retval) { | 592 | switch (retval) { |
| 593 | case -ERESTART_RESTARTBLOCK: | ||
| 594 | restart -= 2; | ||
| 605 | case -ERESTARTNOHAND: | 595 | case -ERESTARTNOHAND: |
| 606 | case -ERESTARTSYS: | 596 | case -ERESTARTSYS: |
| 607 | case -ERESTARTNOINTR: | 597 | case -ERESTARTNOINTR: |
| 598 | restart++; | ||
| 608 | regs->ARM_r0 = regs->ARM_ORIG_r0; | 599 | regs->ARM_r0 = regs->ARM_ORIG_r0; |
| 609 | regs->ARM_pc = restart_addr; | 600 | regs->ARM_pc = restart_addr; |
| 610 | break; | 601 | break; |
| 611 | case -ERESTART_RESTARTBLOCK: | ||
| 612 | regs->ARM_r0 = -EINTR; | ||
| 613 | break; | ||
| 614 | } | 602 | } |
| 615 | } | 603 | } |
| 616 | 604 | ||
| @@ -619,14 +607,17 @@ static void do_signal(struct pt_regs *regs, int syscall) | |||
| 619 | * point the debugger may change all our registers ... | 607 | * point the debugger may change all our registers ... |
| 620 | */ | 608 | */ |
| 621 | signr = get_signal_to_deliver(&info, &ka, regs, NULL); | 609 | signr = get_signal_to_deliver(&info, &ka, regs, NULL); |
| 610 | /* | ||
| 611 | * Depending on the signal settings we may need to revert the | ||
| 612 | * decision to restart the system call. But skip this if a | ||
| 613 | * debugger has chosen to restart at a different PC. | ||
| 614 | */ | ||
| 615 | if (regs->ARM_pc != restart_addr) | ||
| 616 | restart = 0; | ||
| 622 | if (signr > 0) { | 617 | if (signr > 0) { |
| 623 | /* | 618 | if (unlikely(restart)) { |
| 624 | * Depending on the signal settings we may need to revert the | 619 | if (retval == -ERESTARTNOHAND || |
| 625 | * decision to restart the system call. But skip this if a | 620 | retval == -ERESTART_RESTARTBLOCK |
| 626 | * debugger has chosen to restart at a different PC. | ||
| 627 | */ | ||
| 628 | if (regs->ARM_pc == restart_addr) { | ||
| 629 | if (retval == -ERESTARTNOHAND | ||
| 630 | || (retval == -ERESTARTSYS | 621 | || (retval == -ERESTARTSYS |
| 631 | && !(ka.sa.sa_flags & SA_RESTART))) { | 622 | && !(ka.sa.sa_flags & SA_RESTART))) { |
| 632 | regs->ARM_r0 = -EINTR; | 623 | regs->ARM_r0 = -EINTR; |
| @@ -635,52 +626,43 @@ static void do_signal(struct pt_regs *regs, int syscall) | |||
| 635 | } | 626 | } |
| 636 | 627 | ||
| 637 | handle_signal(signr, &ka, &info, regs); | 628 | handle_signal(signr, &ka, &info, regs); |
| 638 | return; | 629 | return 0; |
| 639 | } | ||
| 640 | |||
| 641 | if (syscall) { | ||
| 642 | /* | ||
| 643 | * Handle restarting a different system call. As above, | ||
| 644 | * if a debugger has chosen to restart at a different PC, | ||
| 645 | * ignore the restart. | ||
| 646 | */ | ||
| 647 | if (retval == -ERESTART_RESTARTBLOCK | ||
| 648 | && regs->ARM_pc == continue_addr) { | ||
| 649 | if (thumb_mode(regs)) { | ||
| 650 | regs->ARM_r7 = __NR_restart_syscall - __NR_SYSCALL_BASE; | ||
| 651 | regs->ARM_pc -= 2; | ||
| 652 | } else { | ||
| 653 | #if defined(CONFIG_AEABI) && !defined(CONFIG_OABI_COMPAT) | ||
| 654 | regs->ARM_r7 = __NR_restart_syscall; | ||
| 655 | regs->ARM_pc -= 4; | ||
| 656 | #else | ||
| 657 | u32 __user *usp; | ||
| 658 | |||
| 659 | regs->ARM_sp -= 4; | ||
| 660 | usp = (u32 __user *)regs->ARM_sp; | ||
| 661 | |||
| 662 | if (put_user(regs->ARM_pc, usp) == 0) { | ||
| 663 | regs->ARM_pc = KERN_RESTART_CODE; | ||
| 664 | } else { | ||
| 665 | regs->ARM_sp += 4; | ||
| 666 | force_sigsegv(0, current); | ||
| 667 | } | ||
| 668 | #endif | ||
| 669 | } | ||
| 670 | } | ||
| 671 | } | 630 | } |
| 672 | 631 | ||
| 673 | restore_saved_sigmask(); | 632 | restore_saved_sigmask(); |
| 633 | if (unlikely(restart)) | ||
| 634 | regs->ARM_pc = continue_addr; | ||
| 635 | return restart; | ||
| 674 | } | 636 | } |
| 675 | 637 | ||
| 676 | asmlinkage void | 638 | asmlinkage int |
| 677 | do_notify_resume(struct pt_regs *regs, unsigned int thread_flags, int syscall) | 639 | do_work_pending(struct pt_regs *regs, unsigned int thread_flags, int syscall) |
| 678 | { | 640 | { |
| 679 | if (thread_flags & _TIF_SIGPENDING) | 641 | do { |
| 680 | do_signal(regs, syscall); | 642 | if (likely(thread_flags & _TIF_NEED_RESCHED)) { |
| 681 | 643 | schedule(); | |
| 682 | if (thread_flags & _TIF_NOTIFY_RESUME) { | 644 | } else { |
| 683 | clear_thread_flag(TIF_NOTIFY_RESUME); | 645 | if (unlikely(!user_mode(regs))) |
| 684 | tracehook_notify_resume(regs); | 646 | return 0; |
| 685 | } | 647 | local_irq_enable(); |
| 648 | if (thread_flags & _TIF_SIGPENDING) { | ||
| 649 | int restart = do_signal(regs, syscall); | ||
| 650 | if (unlikely(restart)) { | ||
| 651 | /* | ||
| 652 | * Restart without handlers. | ||
| 653 | * Deal with it without leaving | ||
| 654 | * the kernel space. | ||
| 655 | */ | ||
| 656 | return restart; | ||
| 657 | } | ||
| 658 | syscall = 0; | ||
| 659 | } else { | ||
| 660 | clear_thread_flag(TIF_NOTIFY_RESUME); | ||
| 661 | tracehook_notify_resume(regs); | ||
| 662 | } | ||
| 663 | } | ||
| 664 | local_irq_disable(); | ||
| 665 | thread_flags = current_thread_info()->flags; | ||
| 666 | } while (thread_flags & _TIF_WORK_MASK); | ||
| 667 | return 0; | ||
| 686 | } | 668 | } |
diff --git a/arch/arm/kernel/signal.h b/arch/arm/kernel/signal.h index 6fcfe8398aa4..5ff067b7c752 100644 --- a/arch/arm/kernel/signal.h +++ b/arch/arm/kernel/signal.h | |||
| @@ -8,7 +8,5 @@ | |||
| 8 | * published by the Free Software Foundation. | 8 | * published by the Free Software Foundation. |
| 9 | */ | 9 | */ |
| 10 | #define KERN_SIGRETURN_CODE (CONFIG_VECTORS_BASE + 0x00000500) | 10 | #define KERN_SIGRETURN_CODE (CONFIG_VECTORS_BASE + 0x00000500) |
| 11 | #define KERN_RESTART_CODE (KERN_SIGRETURN_CODE + sizeof(sigreturn_codes)) | ||
| 12 | 11 | ||
| 13 | extern const unsigned long sigreturn_codes[7]; | 12 | extern const unsigned long sigreturn_codes[7]; |
| 14 | extern const unsigned long syscall_restart_code[2]; | ||
diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c index 7978d4f0f3ae..f7945218b8c6 100644 --- a/arch/arm/kernel/traps.c +++ b/arch/arm/kernel/traps.c | |||
| @@ -844,8 +844,6 @@ void __init early_trap_init(void *vectors_base) | |||
| 844 | */ | 844 | */ |
| 845 | memcpy((void *)(vectors + KERN_SIGRETURN_CODE - CONFIG_VECTORS_BASE), | 845 | memcpy((void *)(vectors + KERN_SIGRETURN_CODE - CONFIG_VECTORS_BASE), |
| 846 | sigreturn_codes, sizeof(sigreturn_codes)); | 846 | sigreturn_codes, sizeof(sigreturn_codes)); |
| 847 | memcpy((void *)(vectors + KERN_RESTART_CODE - CONFIG_VECTORS_BASE), | ||
| 848 | syscall_restart_code, sizeof(syscall_restart_code)); | ||
| 849 | 847 | ||
| 850 | flush_icache_range(vectors, vectors + PAGE_SIZE); | 848 | flush_icache_range(vectors, vectors + PAGE_SIZE); |
| 851 | modify_domain(DOMAIN_USER, DOMAIN_CLIENT); | 849 | modify_domain(DOMAIN_USER, DOMAIN_CLIENT); |
