diff options
| -rw-r--r-- | arch/ppc/kernel/asm-offsets.c | 3 | ||||
| -rw-r--r-- | arch/ppc/kernel/head.S | 2 | ||||
| -rw-r--r-- | arch/ppc/kernel/misc.S | 2 | ||||
| -rw-r--r-- | arch/ppc/kernel/process.c | 1 | ||||
| -rw-r--r-- | arch/ppc/kernel/signal.c | 607 | ||||
| -rw-r--r-- | include/asm-ppc/processor.h | 1 | ||||
| -rw-r--r-- | include/asm-ppc/ucontext.h | 26 |
7 files changed, 379 insertions, 263 deletions
diff --git a/arch/ppc/kernel/asm-offsets.c b/arch/ppc/kernel/asm-offsets.c index 6e6f78e4e3a0..e41af4ff4dc6 100644 --- a/arch/ppc/kernel/asm-offsets.c +++ b/arch/ppc/kernel/asm-offsets.c @@ -52,6 +52,7 @@ main(void) DEFINE(THREAD_VR0, offsetof(struct thread_struct, vr[0])); DEFINE(THREAD_VRSAVE, offsetof(struct thread_struct, vrsave)); DEFINE(THREAD_VSCR, offsetof(struct thread_struct, vscr)); + DEFINE(THREAD_USED_VR, offsetof(struct thread_struct, used_vr)); #endif /* CONFIG_ALTIVEC */ /* Interrupt register frame */ DEFINE(STACK_FRAME_OVERHEAD, STACK_FRAME_OVERHEAD); @@ -101,7 +102,7 @@ main(void) DEFINE(_XER, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, xer)); DEFINE(_DAR, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, dar)); DEFINE(_DSISR, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, dsisr)); - /* The PowerPC 400-class processors have neither the DAR nor the DSISR + /* The PowerPC 400-class & Book-E processors have neither the DAR nor the DSISR * SPRs. Hence, we overload them to hold the similar DEAR and ESR SPRs * for such processors. For critical interrupts we use them to * hold SRR0 and SRR1. diff --git a/arch/ppc/kernel/head.S b/arch/ppc/kernel/head.S index 0311ae117dee..af68a4ee38d8 100644 --- a/arch/ppc/kernel/head.S +++ b/arch/ppc/kernel/head.S @@ -915,7 +915,9 @@ load_up_altivec: /* enable use of AltiVec after return */ oris r9,r9,MSR_VEC@h mfspr r5,SPRG3 /* current task's THREAD (phys) */ + li r4,1 li r10,THREAD_VSCR + stw r4,THREAD_USED_VR(r5) lvx vr0,r10,r5 mtvscr vr0 REST_32VR(0,r10,r5) diff --git a/arch/ppc/kernel/misc.S b/arch/ppc/kernel/misc.S index a41d10ee1614..b5f86a6d3267 100644 --- a/arch/ppc/kernel/misc.S +++ b/arch/ppc/kernel/misc.S @@ -1379,7 +1379,7 @@ _GLOBAL(sys_call_table) .long sys_clock_gettime .long sys_clock_getres .long sys_clock_nanosleep - .long sys_ni_syscall /* reserved for swapcontext */ + .long sys_swapcontext .long sys_tgkill /* 250 */ .long sys_utimes .long sys_statfs64 diff --git a/arch/ppc/kernel/process.c b/arch/ppc/kernel/process.c index f0a608eb1a6e..82e09cc776bc 100644 --- a/arch/ppc/kernel/process.c +++ b/arch/ppc/kernel/process.c @@ -415,6 +415,7 @@ void start_thread(struct pt_regs *regs, unsigned long nip, unsigned long sp) memset(current->thread.vr, 0, sizeof(current->thread.vr)); memset(¤t->thread.vscr, 0, sizeof(current->thread.vscr)); current->thread.vrsave = 0; + current->thread.used_vr = 0; #endif /* CONFIG_ALTIVEC */ } diff --git a/arch/ppc/kernel/signal.c b/arch/ppc/kernel/signal.c index 41efaef2153a..c737078db5b0 100644 --- a/arch/ppc/kernel/signal.c +++ b/arch/ppc/kernel/signal.c @@ -41,18 +41,6 @@ extern void sigreturn_exit(struct pt_regs *); #define GP_REGS_SIZE min(sizeof(elf_gregset_t), sizeof(struct pt_regs)) -/* - * These are the flags in the MSR that the user is allowed to change - * by modifying the saved value of the MSR on the stack. SE and BE - * should not be in this list since gdb may want to change these. I.e, - * you should be able to step out of a signal handler to see what - * instruction executes next after the signal handler completes. - * Alternately, if you stepped into a signal handler, you should be - * able to continue 'til the next breakpoint from within the signal - * handler, even if the handler returns. - */ -#define MSR_USERCHANGE (MSR_FE0 | MSR_FE1) - int do_signal(sigset_t *oldset, struct pt_regs *regs); /* @@ -72,8 +60,8 @@ sys_sigsuspend(old_sigset_t mask, int p2, int p3, int p4, int p6, int p7, spin_unlock_irq(¤t->sighand->siglock); regs->result = -EINTR; - regs->ccr |= 0x10000000; regs->gpr[3] = EINTR; + regs->ccr |= 0x10000000; while (1) { current->state = TASK_INTERRUPTIBLE; schedule(); @@ -103,8 +91,8 @@ sys_rt_sigsuspend(sigset_t __user *unewset, size_t sigsetsize, int p3, int p4, spin_unlock_irq(¤t->sighand->siglock); regs->result = -EINTR; - regs->ccr |= 0x10000000; regs->gpr[3] = EINTR; + regs->ccr |= 0x10000000; while (1) { current->state = TASK_INTERRUPTIBLE; schedule(); @@ -164,305 +152,389 @@ sys_sigaction(int sig, const struct old_sigaction __user *act, * */ struct sigregs { - elf_gregset_t gp_regs; - double fp_regs[ELF_NFPREG]; - unsigned long tramp[2]; + struct mcontext mctx; /* all the register values */ /* Programs using the rs6000/xcoff abi can save up to 19 gp regs and 18 fp regs below sp before decrementing it. */ int abigap[56]; }; -struct rt_sigframe -{ - unsigned long _unused[2]; - struct siginfo *pinfo; - void *puc; - struct siginfo info; - struct ucontext uc; -}; - +/* We use the mc_pad field for the signal return trampoline. */ +#define tramp mc_pad /* * When we have rt signals to deliver, we set up on the * user stack, going down from the original stack pointer: - * a sigregs struct - * one rt_sigframe struct (siginfo + ucontext) - * a gap of __SIGNAL_FRAMESIZE bytes + * one rt_sigframe struct (siginfo + ucontext + ABI gap) + * a gap of __SIGNAL_FRAMESIZE+16 bytes + * (the +16 is to get the siginfo and ucontext in the same + * positions as in older kernels). * * Each of these things must be a multiple of 16 bytes in size. * */ -int sys_rt_sigreturn(int r3, int r4, int r5, int r6, int r7, int r8, - struct pt_regs *regs) +struct rt_sigframe { - struct rt_sigframe __user *rt_sf; - struct sigcontext sigctx; - struct sigregs __user *sr; - elf_gregset_t saved_regs; /* an array of ELF_NGREG unsigned longs */ - sigset_t set; - stack_t st; + struct siginfo info; + struct ucontext uc; + /* Programs using the rs6000/xcoff abi can save up to 19 gp regs + and 18 fp regs below sp before decrementing it. */ + int abigap[56]; +}; - rt_sf = (struct rt_sigframe __user *)(regs->gpr[1] + __SIGNAL_FRAMESIZE); - if (copy_from_user(&sigctx, &rt_sf->uc.uc_mcontext, sizeof(sigctx)) - || copy_from_user(&set, &rt_sf->uc.uc_sigmask, sizeof(set)) - || copy_from_user(&st, &rt_sf->uc.uc_stack, sizeof(st))) - goto badframe; - sigdelsetmask(&set, ~_BLOCKABLE); - spin_lock_irq(¤t->sighand->siglock); - current->blocked = set; - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); +/* + * Save the current user registers on the user stack. + * We only save the altivec registers if the process has used + * altivec instructions at some point. + */ +static int +save_user_regs(struct pt_regs *regs, struct mcontext *frame, int sigret) +{ + /* save general and floating-point registers */ + CHECK_FULL_REGS(regs); if (regs->msr & MSR_FP) giveup_fpu(current); + if (__copy_to_user(&frame->mc_gregs, regs, GP_REGS_SIZE) + || __copy_to_user(&frame->mc_fregs, current->thread.fpr, + ELF_NFPREG * sizeof(double))) + return 1; - /* restore registers - - * sigctx is initialized to point to the - * preamble frame (where registers are stored) - * see handle_signal() + current->thread.fpscr = 0; /* turn off all fp exceptions */ + +#ifdef CONFIG_ALTIVEC + /* save altivec registers */ + if (current->thread.used_vr) { + if (regs->msr & MSR_VEC) + giveup_altivec(current); + if (__copy_to_user(&frame->mc_vregs, current->thread.vr, + ELF_NVRREG * sizeof(vector128))) + return 1; + /* set MSR_VEC in the saved MSR value to indicate that + frame->mc_vregs contains valid data */ + if (__put_user(regs->msr | MSR_VEC, &frame->mc_gregs[PT_MSR])) + return 1; + } + /* else assert((regs->msr & MSR_VEC) == 0) */ + + /* We always copy to/from vrsave, it's 0 if we don't have or don't + * use altivec. Since VSCR only contains 32 bits saved in the least + * significant bits of a vector, we "cheat" and stuff VRSAVE in the + * most significant bits of that same vector. --BenH */ - sr = (struct sigregs __user *) sigctx.regs; - if (copy_from_user(saved_regs, &sr->gp_regs, sizeof(sr->gp_regs))) - goto badframe; - saved_regs[PT_MSR] = (regs->msr & ~MSR_USERCHANGE) - | (saved_regs[PT_MSR] & MSR_USERCHANGE); - memcpy(regs, saved_regs, GP_REGS_SIZE); - if (copy_from_user(current->thread.fpr, &sr->fp_regs, - sizeof(sr->fp_regs))) - goto badframe; + if (__put_user(current->thread.vrsave, (u32 *)&frame->mc_vregs[32])) + return 1; +#endif /* CONFIG_ALTIVEC */ + + if (sigret) { + /* Set up the sigreturn trampoline: li r0,sigret; sc */ + if (__put_user(0x38000000UL + sigret, &frame->tramp[0]) + || __put_user(0x44000002UL, &frame->tramp[1])) + return 1; + flush_icache_range((unsigned long) &frame->tramp[0], + (unsigned long) &frame->tramp[2]); + } - sigreturn_exit(regs); /* doesn't return here */ return 0; +} -badframe: - do_exit(SIGSEGV); +/* + * Restore the current user register values from the user stack, + * (except for MSR). + */ +static int +restore_user_regs(struct pt_regs *regs, struct mcontext __user *sr) +{ +#ifdef CONFIG_ALTIVEC + unsigned long msr; +#endif + + /* copy up to but not including MSR */ + if (__copy_from_user(regs, &sr->mc_gregs, PT_MSR * sizeof(elf_greg_t))) + return 1; + /* copy from orig_r3 (the word after the MSR) up to the end */ + if (__copy_from_user(®s->orig_gpr3, &sr->mc_gregs[PT_ORIG_R3], + GP_REGS_SIZE - PT_ORIG_R3 * sizeof(elf_greg_t))) + return 1; + + /* force the process to reload the FP registers from + current->thread when it next does FP instructions */ + regs->msr &= ~MSR_FP; + if (__copy_from_user(current->thread.fpr, &sr->mc_fregs, + sizeof(sr->mc_fregs))) + return 1; + +#ifdef CONFIG_ALTIVEC + /* force the process to reload the altivec registers from + current->thread when it next does altivec instructions */ + regs->msr &= ~MSR_VEC; + if (!__get_user(msr, &sr->mc_gregs[PT_MSR]) && (msr & MSR_VEC) != 0) { + /* restore altivec registers from the stack */ + if (__copy_from_user(current->thread.vr, &sr->mc_vregs, + sizeof(sr->mc_vregs))) + return 1; + } else if (current->thread.used_vr) + memset(¤t->thread.vr, 0, sizeof(current->thread.vr)); + + /* Always get VRSAVE back */ + if (__get_user(current->thread.vrsave, (u32 *)&sr->mc_vregs[32])) + return 1; +#endif /* CONFIG_ALTIVEC */ + + return 0; } +/* + * Restore the user process's signal mask + */ static void -setup_rt_frame(struct pt_regs *regs, struct sigregs __user *frame, - signed long newsp) +restore_sigmask(sigset_t *set) { - struct rt_sigframe __user *rt_sf = (struct rt_sigframe __user *) newsp; + sigdelsetmask(set, ~_BLOCKABLE); + spin_lock_irq(¤t->sighand->siglock); + current->blocked = *set; + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); +} - /* Set up preamble frame */ - if (verify_area(VERIFY_WRITE, frame, sizeof(*frame))) +/* + * Set up a signal frame for a "real-time" signal handler + * (one which gets siginfo). + */ +static void +handle_rt_signal(unsigned long sig, struct k_sigaction *ka, + siginfo_t *info, sigset_t *oldset, struct pt_regs * regs, + unsigned long newsp) +{ + struct rt_sigframe __user *rt_sf; + struct mcontext __user *frame; + unsigned long origsp = newsp; + + /* Set up Signal Frame */ + /* Put a Real Time Context onto stack */ + newsp -= sizeof(*rt_sf); + rt_sf = (struct rt_sigframe __user *) newsp; + + /* create a stack frame for the caller of the handler */ + newsp -= __SIGNAL_FRAMESIZE + 16; + + if (verify_area(VERIFY_WRITE, (void __user *) newsp, origsp - newsp)) goto badframe; - CHECK_FULL_REGS(regs); - if (regs->msr & MSR_FP) - giveup_fpu(current); - if (__copy_to_user(&frame->gp_regs, regs, GP_REGS_SIZE) - || __copy_to_user(&frame->fp_regs, current->thread.fpr, - ELF_NFPREG * sizeof(double)) - /* Set up to return from user space. - It calls the sc exception at offset 0x9999 - for sys_rt_sigreturn(). - */ - || __put_user(0x38000000UL + __NR_rt_sigreturn, &frame->tramp[0]) - || __put_user(0x44000002UL, &frame->tramp[1])) /* sc */ + + /* Put the siginfo & fill in most of the ucontext */ + if (copy_siginfo_to_user(&rt_sf->info, info) + || __put_user(0, &rt_sf->uc.uc_flags) + || __put_user(0, &rt_sf->uc.uc_link) + || __put_user(current->sas_ss_sp, &rt_sf->uc.uc_stack.ss_sp) + || __put_user(sas_ss_flags(regs->gpr[1]), + &rt_sf->uc.uc_stack.ss_flags) + || __put_user(current->sas_ss_size, &rt_sf->uc.uc_stack.ss_size) + || __put_user(&rt_sf->uc.uc_mcontext, &rt_sf->uc.uc_regs) + || __copy_to_user(&rt_sf->uc.uc_oldsigmask, oldset, sizeof(*oldset)) + || __copy_to_user(&rt_sf->uc.uc_sigmask, oldset, sizeof(*oldset))) goto badframe; - flush_icache_range((unsigned long) &frame->tramp[0], - (unsigned long) &frame->tramp[2]); - current->thread.fpscr = 0; /* turn off all fp exceptions */ - /* Retrieve rt_sigframe from stack and - set up registers for signal handler - */ - newsp -= __SIGNAL_FRAMESIZE; - if (put_user(regs->gpr[1], (unsigned long __user *)newsp) - || get_user(regs->nip, &rt_sf->uc.uc_mcontext.handler) - || get_user(regs->gpr[3], &rt_sf->uc.uc_mcontext.signal) - || get_user(regs->gpr[4], (unsigned long __user *)&rt_sf->pinfo) - || get_user(regs->gpr[5], (unsigned long __user *)&rt_sf->puc)) + /* Save user registers on the stack */ + frame = &rt_sf->uc.uc_mcontext; + if (save_user_regs(regs, frame, __NR_rt_sigreturn)) goto badframe; + if (put_user(regs->gpr[1], (unsigned long __user *)newsp)) + goto badframe; regs->gpr[1] = newsp; + regs->gpr[3] = sig; + regs->gpr[4] = (unsigned long) &rt_sf->info; + regs->gpr[5] = (unsigned long) &rt_sf->uc; regs->gpr[6] = (unsigned long) rt_sf; + regs->nip = (unsigned long) ka->sa.sa_handler; regs->link = (unsigned long) frame->tramp; + regs->trap = 0; return; badframe: #if DEBUG_SIG - printk("badframe in setup_rt_frame, regs=%p frame=%p newsp=%lx\n", + printk("badframe in handle_rt_signal, regs=%p frame=%p newsp=%lx\n", regs, frame, newsp); #endif - do_exit(SIGSEGV); + if (sig == SIGSEGV) + ka->sa.sa_handler = SIG_DFL; + force_sig(SIGSEGV, current); } -/* - * Do a signal return; undo the signal stack. - */ -int sys_sigreturn(int r3, int r4, int r5, int r6, int r7, int r8, - struct pt_regs *regs) +static int do_setcontext(struct ucontext __user *ucp, struct pt_regs *regs) { - struct sigcontext __user *sc; - struct sigcontext sigctx; - struct sigregs __user *sr; - elf_gregset_t saved_regs; /* an array of ELF_NGREG unsigned longs */ sigset_t set; - sc = (struct sigcontext __user *)(regs->gpr[1] + __SIGNAL_FRAMESIZE); - if (copy_from_user(&sigctx, sc, sizeof(sigctx))) - goto badframe; + if (__copy_from_user(&set, &ucp->uc_sigmask, sizeof(set))) + return -EFAULT; + restore_sigmask(&set); - set.sig[0] = sigctx.oldmask; -#if _NSIG_WORDS > 1 - set.sig[1] = sigctx._unused[3]; -#endif - sigdelsetmask(&set, ~_BLOCKABLE); - spin_lock_irq(¤t->sighand->siglock); - current->blocked = set; - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); - if (regs->msr & MSR_FP ) - giveup_fpu(current); + if (restore_user_regs(regs, &ucp->uc_mcontext)) + return -EFAULT; - /* restore registers */ - sr = (struct sigregs __user *) sigctx.regs; - if (copy_from_user(saved_regs, &sr->gp_regs, sizeof(sr->gp_regs))) - goto badframe; - saved_regs[PT_MSR] = (regs->msr & ~MSR_USERCHANGE) - | (saved_regs[PT_MSR] & MSR_USERCHANGE); - memcpy(regs, saved_regs, GP_REGS_SIZE); + return 0; +} - if (copy_from_user(current->thread.fpr, &sr->fp_regs, - sizeof(sr->fp_regs))) - goto badframe; +int sys_swapcontext(struct ucontext __user *old_ctx, + struct ucontext __user *new_ctx, + int r5, int r6, int r7, int r8, struct pt_regs *regs) +{ + unsigned char tmp; + + if (old_ctx != NULL) { + if (verify_area(VERIFY_WRITE, old_ctx, sizeof(*old_ctx)) + || save_user_regs(regs, &old_ctx->uc_mcontext, 0) + || __copy_to_user(&old_ctx->uc_sigmask, + ¤t->blocked, sizeof(sigset_t)) + /* the next 2 things aren't strictly necessary */ + || __copy_to_user(&old_ctx->uc_oldsigmask, + ¤t->blocked, sizeof(sigset_t)) + || __put_user(&old_ctx->uc_mcontext, &old_ctx->uc_regs)) + return -EFAULT; + } + if (new_ctx == NULL) + return 0; + if (verify_area(VERIFY_READ, new_ctx, sizeof(*new_ctx)) + || __get_user(tmp, (u8 *) new_ctx) + || __get_user(tmp, (u8 *) (new_ctx + 1) - 1)) + return -EFAULT; + + /* + * If we get a fault copying the context into the kernel's + * image of the user's registers, we can't just return -EFAULT + * because the user's registers will be corrupted. For instance + * the NIP value may have been updated but not some of the + * other registers. Given that we have done the verify_area + * and successfully read the first and last bytes of the region + * above, this should only happen in an out-of-memory situation + * or if another thread unmaps the region containing the context. + * We kill the task with a SIGSEGV in this situation. + */ + if (do_setcontext(new_ctx, regs)) + do_exit(SIGSEGV); + sigreturn_exit(regs); + /* doesn't actually return back to here */ + return 0; +} + +int sys_rt_sigreturn(int r3, int r4, int r5, int r6, int r7, int r8, + struct pt_regs *regs) +{ + struct rt_sigframe __user *rt_sf; + + rt_sf = (struct rt_sigframe __user *) + (regs->gpr[1] + __SIGNAL_FRAMESIZE + 16); + if (verify_area(VERIFY_READ, rt_sf, sizeof(struct rt_sigframe))) + goto bad; + if (do_setcontext(&rt_sf->uc, regs)) + goto bad; + + /* + * It's not clear whether or why it is desirable to save the + * sigaltstack setting on signal delivery and restore it on + * signal return. But other architectures do this and we have + * always done it up until now so it is probably better not to + * change it. -- paulus + */ + do_sigaltstack(&rt_sf->uc.uc_stack, NULL, regs->gpr[1]); sigreturn_exit(regs); /* doesn't return here */ return 0; -badframe: - do_exit(SIGSEGV); -} + bad: + force_sig(SIGSEGV, current); + return 0; +} /* - * Set up a signal frame. + * OK, we're invoking a handler */ static void -setup_frame(struct pt_regs *regs, struct sigregs __user *frame, - unsigned long newsp) +handle_signal(unsigned long sig, struct k_sigaction *ka, + siginfo_t *info, sigset_t *oldset, struct pt_regs * regs, + unsigned long newsp) { - struct sigcontext __user *sc = (struct sigcontext __user *) newsp; + struct sigcontext __user *sc; + struct sigregs __user *frame; + unsigned long origsp = newsp; - if (verify_area(VERIFY_WRITE, frame, sizeof(*frame))) + /* Set up Signal Frame */ + newsp -= sizeof(struct sigregs); + frame = (struct sigregs __user *) newsp; + + /* Put a sigcontext on the stack */ + newsp -= sizeof(*sc); + sc = (struct sigcontext __user *) newsp; + + /* create a stack frame for the caller of the handler */ + newsp -= __SIGNAL_FRAMESIZE; + + if (verify_area(VERIFY_WRITE, (void *) newsp, origsp - newsp)) goto badframe; - CHECK_FULL_REGS(regs); - if (regs->msr & MSR_FP) - giveup_fpu(current); - if (__copy_to_user(&frame->gp_regs, regs, GP_REGS_SIZE) - || __copy_to_user(&frame->fp_regs, current->thread.fpr, - ELF_NFPREG * sizeof(double)) - || __put_user(0x38000000UL + __NR_sigreturn, &frame->tramp[0]) - || __put_user(0x44000002UL, &frame->tramp[1])) /* sc */ + +#if _NSIG != 64 +#error "Please adjust handle_signal()" +#endif + if (__put_user((unsigned long) ka->sa.sa_handler, &sc->handler) + || __put_user(oldset->sig[0], &sc->oldmask) + || __put_user(oldset->sig[1], &sc->_unused[3]) + || __put_user((struct pt_regs *)frame, &sc->regs) + || __put_user(sig, &sc->signal)) goto badframe; - flush_icache_range((unsigned long) &frame->tramp[0], - (unsigned long) &frame->tramp[2]); - current->thread.fpscr = 0; /* turn off all fp exceptions */ - newsp -= __SIGNAL_FRAMESIZE; - if (put_user(regs->gpr[1], (unsigned long __user *)newsp) - || get_user(regs->nip, &sc->handler) - || get_user(regs->gpr[3], &sc->signal)) + if (save_user_regs(regs, &frame->mctx, __NR_sigreturn)) + goto badframe; + + if (put_user(regs->gpr[1], (unsigned long *)newsp)) goto badframe; regs->gpr[1] = newsp; + regs->gpr[3] = sig; regs->gpr[4] = (unsigned long) sc; - regs->link = (unsigned long) frame->tramp; + regs->nip = (unsigned long) ka->sa.sa_handler; + regs->link = (unsigned long) frame->mctx.tramp; + regs->trap = 0; return; badframe: #if DEBUG_SIG - printk("badframe in setup_frame, regs=%p frame=%p newsp=%lx\n", - regs, frame, newsp); + printk("badframe in handle_signal, regs=%p frame=%lx newsp=%lx\n", + regs, frame, *newspp); #endif - do_exit(SIGSEGV); + if (sig == SIGSEGV) + ka->sa.sa_handler = SIG_DFL; + force_sig(SIGSEGV, current); } /* - * OK, we're invoking a handler + * Do a signal return; undo the signal stack. */ -static void -handle_signal(unsigned long sig, siginfo_t *info, sigset_t *oldset, - struct pt_regs * regs, unsigned long *newspp, unsigned long frame) +int sys_sigreturn(int r3, int r4, int r5, int r6, int r7, int r8, + struct pt_regs *regs) { struct sigcontext __user *sc; - struct rt_sigframe __user *rt_sf; - struct k_sigaction *ka = ¤t->sighand->action[sig-1]; - - if (TRAP(regs) == 0x0C00 /* System Call! */ - && ((int)regs->result == -ERESTARTNOHAND || - (int)regs->result == -ERESTART_RESTARTBLOCK || - ((int)regs->result == -ERESTARTSYS && - !(ka->sa.sa_flags & SA_RESTART)))) { - if ((int)regs->result == -ERESTART_RESTARTBLOCK) - current_thread_info()->restart_block.fn - = do_no_restart_syscall; - regs->result = -EINTR; - regs->gpr[3] = EINTR; - regs->ccr |= 0x10000000; - } + struct sigcontext sigctx; + struct mcontext __user *sr; + sigset_t set; - /* Set up Signal Frame */ - if (ka->sa.sa_flags & SA_SIGINFO) { - /* Put a Real Time Context onto stack */ - *newspp -= sizeof(*rt_sf); - rt_sf = (struct rt_sigframe __user *) *newspp; - if (verify_area(VERIFY_WRITE, rt_sf, sizeof(*rt_sf))) - goto badframe; - - if (__put_user((unsigned long) ka->sa.sa_handler, &rt_sf->uc.uc_mcontext.handler) - || __put_user(&rt_sf->info, &rt_sf->pinfo) - || __put_user(&rt_sf->uc, &rt_sf->puc) - /* Put the siginfo */ - || copy_siginfo_to_user(&rt_sf->info, info) - /* Create the ucontext */ - || __put_user(0, &rt_sf->uc.uc_flags) - || __put_user(0, &rt_sf->uc.uc_link) - || __put_user(current->sas_ss_sp, &rt_sf->uc.uc_stack.ss_sp) - || __put_user(sas_ss_flags(regs->gpr[1]), - &rt_sf->uc.uc_stack.ss_flags) - || __put_user(current->sas_ss_size, &rt_sf->uc.uc_stack.ss_size) - || __copy_to_user(&rt_sf->uc.uc_sigmask, oldset, sizeof(*oldset)) - /* mcontext.regs points to preamble register frame */ - || __put_user((struct pt_regs *)frame, &rt_sf->uc.uc_mcontext.regs) - || __put_user(sig, &rt_sf->uc.uc_mcontext.signal)) - goto badframe; - } else { - /* Put a sigcontext on the stack */ - *newspp -= sizeof(*sc); - sc = (struct sigcontext __user *) *newspp; - if (verify_area(VERIFY_WRITE, sc, sizeof(*sc))) - goto badframe; - - if (__put_user((unsigned long) ka->sa.sa_handler, &sc->handler) - || __put_user(oldset->sig[0], &sc->oldmask) -#if _NSIG_WORDS > 1 - || __put_user(oldset->sig[1], &sc->_unused[3]) -#endif - || __put_user((struct pt_regs *)frame, &sc->regs) - || __put_user(sig, &sc->signal)) - goto badframe; - } + sc = (struct sigcontext __user *)(regs->gpr[1] + __SIGNAL_FRAMESIZE); + if (copy_from_user(&sigctx, sc, sizeof(sigctx))) + goto badframe; - if (ka->sa.sa_flags & SA_ONESHOT) - ka->sa.sa_handler = SIG_DFL; + set.sig[0] = sigctx.oldmask; + set.sig[1] = sigctx._unused[3]; + restore_sigmask(&set); - if (!(ka->sa.sa_flags & SA_NODEFER)) { - spin_lock_irq(¤t->sighand->siglock); - sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); - sigaddset(¤t->blocked,sig); - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); - } - return; + sr = (struct mcontext *) sigctx.regs; + if (verify_area(VERIFY_READ, sr, sizeof(*sr)) + || restore_user_regs(regs, sr)) + goto badframe; + + sigreturn_exit(regs); /* doesn't return */ + return 0; badframe: -#if DEBUG_SIG - printk("badframe in handle_signal, regs=%p frame=%lx newsp=%lx\n", - regs, frame, *newspp); - printk("sc=%p sig=%d ka=%p info=%p oldset=%p\n", sc, sig, ka, info, oldset); -#endif - do_exit(SIGSEGV); + force_sig(SIGSEGV, current); + return 0; } /* @@ -475,7 +547,7 @@ int do_signal(sigset_t *oldset, struct pt_regs *regs) siginfo_t info; struct k_sigaction *ka; unsigned long frame, newsp; - int signr; + int signr, ret; if (!oldset) oldset = ¤t->blocked; @@ -483,40 +555,65 @@ int do_signal(sigset_t *oldset, struct pt_regs *regs) newsp = frame = 0; signr = get_signal_to_deliver(&info, regs, NULL); - if (signr > 0) { - ka = ¤t->sighand->action[signr-1]; - if ( (ka->sa.sa_flags & SA_ONSTACK) - && (! on_sig_stack(regs->gpr[1]))) - newsp = (current->sas_ss_sp + current->sas_ss_size); - else - newsp = regs->gpr[1]; - newsp = frame = newsp - sizeof(struct sigregs); - - /* Whee! Actually deliver the signal. */ - handle_signal(signr, &info, oldset, regs, &newsp, frame); - } - if (TRAP(regs) == 0x0C00) { /* System Call! */ - if ((int)regs->result == -ERESTARTNOHAND || - (int)regs->result == -ERESTARTSYS || - (int)regs->result == -ERESTARTNOINTR) { - regs->gpr[3] = regs->orig_gpr3; + ka = (signr == 0)? NULL: ¤t->sighand->action[signr-1]; + + if (TRAP(regs) == 0x0C00 /* System Call! */ + && regs->ccr & 0x10000000 /* error signalled */ + && ((ret = regs->gpr[3]) == ERESTARTSYS + || ret == ERESTARTNOHAND || ret == ERESTARTNOINTR + || ret == ERESTART_RESTARTBLOCK)) { + + if (signr > 0 + && (ret == ERESTARTNOHAND || ret == ERESTART_RESTARTBLOCK + || (ret == ERESTARTSYS + && !(ka->sa.sa_flags & SA_RESTART)))) { + /* make the system call return an EINTR error */ + regs->result = -EINTR; + regs->gpr[3] = EINTR; + /* note that the cr0.SO bit is already set */ + /* clear any restart function that was set */ + if (ret == ERESTART_RESTARTBLOCK) + current_thread_info()->restart_block.fn + = do_no_restart_syscall; + } else { regs->nip -= 4; /* Back up & retry system call */ regs->result = 0; - } else if ((int)regs->result == -ERESTART_RESTARTBLOCK) { - regs->gpr[0] = __NR_restart_syscall; - regs->nip -= 4; - regs->result = 0; + regs->trap = 0; + if (ret == ERESTART_RESTARTBLOCK) + regs->gpr[0] = __NR_restart_syscall; + else + regs->gpr[3] = regs->orig_gpr3; } } - if (newsp == frame) + if (signr == 0) return 0; /* no signals delivered */ + if ((ka->sa.sa_flags & SA_ONSTACK) && current->sas_ss_size + && !on_sig_stack(regs->gpr[1])) + newsp = current->sas_ss_sp + current->sas_ss_size; + else + newsp = regs->gpr[1]; + newsp &= ~0xfUL; + + /* Whee! Actually deliver the signal. */ if (ka->sa.sa_flags & SA_SIGINFO) - setup_rt_frame(regs, (struct sigregs __user *) frame, newsp); + handle_rt_signal(signr, ka, &info, oldset, regs, newsp); else - setup_frame(regs, (struct sigregs __user *) frame, newsp); + handle_signal(signr, ka, &info, oldset, regs, newsp); + + 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); + sigaddset(¤t->blocked, signr); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + } + return 1; } diff --git a/include/asm-ppc/processor.h b/include/asm-ppc/processor.h index e13d0a0b1f45..3a893e9d6b47 100644 --- a/include/asm-ppc/processor.h +++ b/include/asm-ppc/processor.h @@ -848,6 +848,7 @@ struct thread_struct { /* AltiVec status */ vector128 vscr __attribute((aligned(16))); unsigned long vrsave; + int used_vr; /* set if process has used altivec */ #endif /* CONFIG_ALTIVEC */ }; diff --git a/include/asm-ppc/ucontext.h b/include/asm-ppc/ucontext.h index 14f1e78c041d..1d6e70c4306d 100644 --- a/include/asm-ppc/ucontext.h +++ b/include/asm-ppc/ucontext.h @@ -1,14 +1,28 @@ #ifndef _ASMPPC_UCONTEXT_H #define _ASMPPC_UCONTEXT_H -/* Copied from i386. */ +#include <asm/elf.h> +#include <asm/signal.h> + +struct mcontext { + elf_gregset_t mc_gregs; + elf_fpregset_t mc_fregs; + unsigned long mc_pad[2]; + elf_vrregset_t mc_vregs __attribute__((__aligned__(16))); +}; struct ucontext { - unsigned long uc_flags; - struct ucontext *uc_link; - stack_t uc_stack; - struct sigcontext uc_mcontext; - sigset_t uc_sigmask; /* mask last for extensibility */ + unsigned long uc_flags; + struct ucontext *uc_link; + stack_t uc_stack; + int uc_pad[7]; + struct mcontext *uc_regs; /* backward compat */ + sigset_t uc_oldsigmask; /* backward compat */ + int uc_pad2; + sigset_t uc_sigmask; + /* glibc has 1024-bit signal masks, ours are 64-bit */ + int uc_maskext[30]; + struct mcontext uc_mcontext; }; #endif /* !_ASMPPC_UCONTEXT_H */ |
