aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/arm/kernel/entry-common.S24
-rw-r--r--arch/arm/kernel/ptrace.c1
-rw-r--r--arch/arm/kernel/signal.c114
-rw-r--r--arch/arm/kernel/signal.h2
-rw-r--r--arch/arm/kernel/traps.c2
5 files changed, 58 insertions, 85 deletions
diff --git a/arch/arm/kernel/entry-common.S b/arch/arm/kernel/entry-common.S
index 49d9f930524..978eac57e04 100644
--- a/arch/arm/kernel/entry-common.S
+++ b/arch/arm/kernel/entry-common.S
@@ -51,23 +51,15 @@ ret_fast_syscall:
51fast_work_pending: 51fast_work_pending:
52 str r0, [sp, #S_R0+S_OFF]! @ returned r0 52 str r0, [sp, #S_R0+S_OFF]! @ returned r0
53work_pending: 53work_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
69work_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
404local_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 dab711e6e1c..3e0fc5f7ed4 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 536c5d6b340..f27789e4e38 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 */
57const 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 */
65asmlinkage int sys_sigsuspend(int restart, unsigned long oldmask, old_sigset_t mask) 52asmlinkage 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 */
585static void do_signal(struct pt_regs *regs, int syscall) 572static 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
676asmlinkage void 638asmlinkage int
677do_notify_resume(struct pt_regs *regs, unsigned int thread_flags, int syscall) 639do_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 6fcfe8398aa..5ff067b7c75 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
13extern const unsigned long sigreturn_codes[7]; 12extern const unsigned long sigreturn_codes[7];
14extern const unsigned long syscall_restart_code[2];
diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c
index 7978d4f0f3a..f7945218b8c 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);