diff options
Diffstat (limited to 'arch/parisc')
-rw-r--r-- | arch/parisc/kernel/signal.c | 177 |
1 files changed, 98 insertions, 79 deletions
diff --git a/arch/parisc/kernel/signal.c b/arch/parisc/kernel/signal.c index 8d781b0e668..64169ab82de 100644 --- a/arch/parisc/kernel/signal.c +++ b/arch/parisc/kernel/signal.c @@ -471,6 +471,97 @@ handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka, return 1; } +static inline void +syscall_restart(struct pt_regs *regs, struct k_sigaction *ka) +{ + /* Check the return code */ + switch (regs->gr[28]) { + case -ERESTART_RESTARTBLOCK: + current_thread_info()->restart_block.fn = + do_no_restart_syscall; + case -ERESTARTNOHAND: + DBG(1,"ERESTARTNOHAND: returning -EINTR\n"); + regs->gr[28] = -EINTR; + break; + + case -ERESTARTSYS: + if (!(ka->sa.sa_flags & SA_RESTART)) { + DBG(1,"ERESTARTSYS: putting -EINTR\n"); + regs->gr[28] = -EINTR; + break; + } + /* fallthrough */ + case -ERESTARTNOINTR: + /* A syscall is just a branch, so all + * we have to do is fiddle the return pointer. + */ + regs->gr[31] -= 8; /* delayed branching */ + /* Preserve original r28. */ + regs->gr[28] = regs->orig_r28; + break; + } +} + +static inline void +insert_restart_trampoline(struct pt_regs *regs) +{ + switch(regs->gr[28]) { + case -ERESTART_RESTARTBLOCK: { + /* Restart the system call - no handlers present */ + unsigned int *usp = (unsigned int *)regs->gr[30]; + + /* Setup a trampoline to restart the syscall + * with __NR_restart_syscall + * + * 0: <return address (orig r31)> + * 4: <2nd half for 64-bit> + * 8: ldw 0(%sp), %r31 + * 12: be 0x100(%sr2, %r0) + * 16: ldi __NR_restart_syscall, %r20 + */ +#ifdef CONFIG_64BIT + put_user(regs->gr[31] >> 32, &usp[0]); + put_user(regs->gr[31] & 0xffffffff, &usp[1]); + put_user(0x0fc010df, &usp[2]); +#else + put_user(regs->gr[31], &usp[0]); + put_user(0x0fc0109f, &usp[2]); +#endif + put_user(0xe0008200, &usp[3]); + put_user(0x34140000, &usp[4]); + + /* Stack is 64-byte aligned, and we only need + * to flush 1 cache line. + * Flushing one cacheline is cheap. + * "sync" on bigger (> 4 way) boxes is not. + */ + flush_icache_range(regs->gr[30], regs->gr[30] + 4); + + regs->gr[31] = regs->gr[30] + 8; + /* Preserve original r28. */ + regs->gr[28] = regs->orig_r28; + + return; + } + case -ERESTARTNOHAND: + case -ERESTARTSYS: + case -ERESTARTNOINTR: { + /* Hooray for delayed branching. We don't + * have to restore %r20 (the system call + * number) because it gets loaded in the delay + * slot of the branch external instruction. + */ + regs->gr[31] -= 8; + /* Preserve original r28. */ + regs->gr[28] = regs->orig_r28; + + return; + } + default: + break; + } +} + /* * Note that 'init' is a special process: it doesn't get signals it doesn't * want to handle. Thus you cannot kill init even with a SIGKILL even by @@ -482,7 +573,6 @@ handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka, * registers). As noted below, the syscall number gets restored for * us due to the magic of delayed branching. */ - asmlinkage void do_signal(struct pt_regs *regs, long in_syscall) { @@ -518,36 +608,14 @@ do_signal(struct pt_regs *regs, long in_syscall) break; /* Restart a system call if necessary. */ - if (in_syscall) { - /* Check the return code */ - switch (regs->gr[28]) { - case -ERESTART_RESTARTBLOCK: - current_thread_info()->restart_block.fn = do_no_restart_syscall; - case -ERESTARTNOHAND: - DBG(1,"ERESTARTNOHAND: returning -EINTR\n"); - regs->gr[28] = -EINTR; - break; - - case -ERESTARTSYS: - if (!(ka.sa.sa_flags & SA_RESTART)) { - DBG(1,"ERESTARTSYS: putting -EINTR\n"); - regs->gr[28] = -EINTR; - break; - } - /* fallthrough */ - case -ERESTARTNOINTR: - /* A syscall is just a branch, so all - we have to do is fiddle the return pointer. */ - regs->gr[31] -= 8; /* delayed branching */ - /* Preserve original r28. */ - regs->gr[28] = regs->orig_r28; - break; - } - } + if (in_syscall) + syscall_restart(regs, &ka); + /* Whee! Actually deliver the signal. If the delivery failed, we need to continue to iterate in this loop so we can deliver the SIGSEGV... */ - if (handle_signal(signr, &info, &ka, oldset, regs, in_syscall)) { + if (handle_signal(signr, &info, &ka, oldset, + regs, in_syscall)) { DBG(1,KERN_DEBUG "do_signal: Exit (success), regs->gr[28] = %ld\n", regs->gr[28]); if (test_thread_flag(TIF_RESTORE_SIGMASK)) @@ -558,57 +626,8 @@ do_signal(struct pt_regs *regs, long in_syscall) /* end of while(1) looping forever if we can't force a signal */ /* Did we come from a system call? */ - if (in_syscall) { - /* Restart the system call - no handlers present */ - if (regs->gr[28] == -ERESTART_RESTARTBLOCK) { - unsigned int *usp = (unsigned int *)regs->gr[30]; - - /* Setup a trampoline to restart the syscall - * with __NR_restart_syscall - * - * 0: <return address (orig r31)> - * 4: <2nd half for 64-bit> - * 8: ldw 0(%sp), %r31 - * 12: be 0x100(%sr2, %r0) - * 16: ldi __NR_restart_syscall, %r20 - */ -#ifndef __LP64__ - put_user(regs->gr[31], &usp[0]); - put_user(0x0fc0109f, &usp[2]); -#else - put_user(regs->gr[31] >> 32, &usp[0]); - put_user(regs->gr[31] & 0xffffffff, &usp[1]); - put_user(0x0fc010df, &usp[2]); -#endif - put_user(0xe0008200, &usp[3]); - put_user(0x34140000, &usp[4]); - - /* Stack is 64-byte aligned, and we only need - * to flush 1 cache line. - * Flushing one cacheline is cheap. - * "sync" on bigger (> 4 way) boxes is not. - */ - asm("fdc %%r0(%%sr3, %0)\n" - "sync\n" - "fic %%r0(%%sr3, %0)\n" - "sync\n" - : : "r"(regs->gr[30])); - - regs->gr[31] = regs->gr[30] + 8; - /* Preserve original r28. */ - regs->gr[28] = regs->orig_r28; - } else if (regs->gr[28] == -ERESTARTNOHAND || - regs->gr[28] == -ERESTARTSYS || - regs->gr[28] == -ERESTARTNOINTR) { - /* Hooray for delayed branching. We don't - have to restore %r20 (the system call - number) because it gets loaded in the delay - slot of the branch external instruction. */ - regs->gr[31] -= 8; - /* Preserve original r28. */ - regs->gr[28] = regs->orig_r28; - } - } + if (in_syscall) + insert_restart_trampoline(regs); DBG(1,"do_signal: Exit (not delivered), regs->gr[28] = %ld\n", regs->gr[28]); |