X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=arch%2Fparisc%2Fkernel%2Fsignal.c;h=4a0a8adbc79d7ca68ee0e8146c221ac65de8f73a;hb=6a77f38946aaee1cd85eeec6cf4229b204c15071;hp=1aed89de6d85f420b66d75794603a02d3f88ed86;hpb=5273a3df6485dc2ad6aa7ddd441b9a21970f003b;p=linux-2.6.git diff --git a/arch/parisc/kernel/signal.c b/arch/parisc/kernel/signal.c index 1aed89de6..4a0a8adbc 100644 --- a/arch/parisc/kernel/signal.c +++ b/arch/parisc/kernel/signal.c @@ -353,12 +353,17 @@ setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, goto give_sigsegv; /* Set up to return from userspace. If provided, use a stub - already in userspace. */ + already in userspace. The first words of tramp are used to + save the previous sigrestartblock trampoline that might be + on the stack. We start the sigreturn trampoline at + SIGRESTARTBLOCK_TRAMP+X. */ err |= __put_user(in_syscall ? INSN_LDI_R25_1 : INSN_LDI_R25_0, - &frame->tramp[SIGRETURN_TRAMP+0]); - err |= __put_user(INSN_LDI_R20, &frame->tramp[SIGRETURN_TRAMP+1]); - err |= __put_user(INSN_BLE_SR2_R0, &frame->tramp[SIGRETURN_TRAMP+2]); - err |= __put_user(INSN_NOP, &frame->tramp[SIGRETURN_TRAMP+3]); + &frame->tramp[SIGRESTARTBLOCK_TRAMP+0]); + err |= __put_user(INSN_LDI_R20, + &frame->tramp[SIGRESTARTBLOCK_TRAMP+1]); + err |= __put_user(INSN_BLE_SR2_R0, + &frame->tramp[SIGRESTARTBLOCK_TRAMP+2]); + err |= __put_user(INSN_NOP, &frame->tramp[SIGRESTARTBLOCK_TRAMP+3]); #if DEBUG_SIG /* Assert that we're flushing in the correct space... */ @@ -370,12 +375,16 @@ setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, } #endif - flush_user_dcache_range((unsigned long) &frame->tramp[SIGRETURN_TRAMP], + flush_user_dcache_range((unsigned long) &frame->tramp[0], (unsigned long) &frame->tramp[TRAMP_SIZE]); - flush_user_icache_range((unsigned long) &frame->tramp[SIGRETURN_TRAMP], + flush_user_icache_range((unsigned long) &frame->tramp[0], (unsigned long) &frame->tramp[TRAMP_SIZE]); - rp = (unsigned long) &frame->tramp[SIGRETURN_TRAMP]; + /* TRAMP Words 0-4, Lenght 5 = SIGRESTARTBLOCK_TRAMP + * TRAMP Words 5-9, Length 4 = SIGRETURN_TRAMP + * So the SIGRETURN_TRAMP is at the end of SIGRESTARTBLOCK_TRAMP + */ + rp = (unsigned long) &frame->tramp[SIGRESTARTBLOCK_TRAMP]; if (err) goto give_sigsegv; @@ -485,11 +494,9 @@ give_sigsegv: */ static long -handle_signal(unsigned long sig, siginfo_t *info, sigset_t *oldset, - struct pt_regs *regs, int in_syscall) +handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka, + sigset_t *oldset, struct pt_regs *regs, int in_syscall) { - struct k_sigaction *ka = ¤t->sighand->action[sig-1]; - DBG(1,"handle_signal: sig=%ld, ka=%p, info=%p, oldset=%p, regs=%p\n", sig, ka, info, oldset, regs); @@ -497,9 +504,6 @@ handle_signal(unsigned long sig, siginfo_t *info, sigset_t *oldset, if (!setup_rt_frame(sig, ka, info, oldset, regs, in_syscall)) return 0; - if (ka->sa.sa_flags & SA_ONESHOT) - ka->sa.sa_handler = SIG_DFL; - if (!(ka->sa.sa_flags & SA_NODEFER)) { spin_lock_irq(¤t->sighand->siglock); sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); @@ -526,7 +530,7 @@ asmlinkage int do_signal(sigset_t *oldset, struct pt_regs *regs, int in_syscall) { siginfo_t info; - struct k_sigaction *ka; + struct k_sigaction ka; int signr; DBG(1,"\ndo_signal: oldset=0x%p, regs=0x%p, sr7 %#lx, in_syscall=%d\n", @@ -544,10 +548,15 @@ do_signal(sigset_t *oldset, struct pt_regs *regs, int in_syscall) oldset->sig[0], oldset->sig[1]); - signr = get_signal_to_deliver(&info, regs, NULL); - DBG(3,"do_signal: signr = %d, regs->gr[28] = %ld\n", signr, regs->gr[28]); + /* May need to force signal if handle_signal failed to deliver */ + while (1) { + + signr = get_signal_to_deliver(&info, &ka, regs, NULL); + DBG(3,"do_signal: signr = %d, regs->gr[28] = %ld\n", signr, regs->gr[28]); - if (signr > 0) { + if (signr <= 0) + break; + /* Restart a system call if necessary. */ if (in_syscall) { /* Check the return code */ @@ -560,8 +569,7 @@ do_signal(sigset_t *oldset, struct pt_regs *regs, int in_syscall) break; case -ERESTARTSYS: - ka = ¤t->sighand->action[signr-1]; - if (!(ka->sa.sa_flags & SA_RESTART)) { + if (!(ka.sa.sa_flags & SA_RESTART)) { DBG(1,"ERESTARTSYS: putting -EINTR\n"); regs->gr[28] = -EINTR; break; @@ -569,8 +577,7 @@ do_signal(sigset_t *oldset, struct pt_regs *regs, int in_syscall) /* fallthrough */ case -ERESTARTNOINTR: /* A syscall is just a branch, so all - we have to do is fiddle the return - pointer. */ + we have to do is fiddle the return pointer. */ regs->gr[31] -= 8; /* delayed branching */ /* Preserve original r28. */ regs->gr[28] = regs->orig_r28; @@ -580,12 +587,13 @@ do_signal(sigset_t *oldset, struct pt_regs *regs, int in_syscall) /* 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, 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]); return 1; } } + /* end of while(1) looping forever if we can't force a signal */ /* Did we come from a system call? */ if (in_syscall) {