X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;ds=sidebyside;f=arch%2Farm%2Fkernel%2Fsignal.c;h=ef7fa2c71284daee95f2a970d1e99d6fcf2df18b;hb=6a77f38946aaee1cd85eeec6cf4229b204c15071;hp=d433d47842b6cec73827ff1741c067cb51c4dbc0;hpb=87fc8d1bb10cd459024a742c6a10961fefcef18f;p=linux-2.6.git diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c index d433d4784..ef7fa2c71 100644 --- a/arch/arm/kernel/signal.c +++ b/arch/arm/kernel/signal.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include @@ -126,25 +125,6 @@ sys_sigaction(int sig, const struct old_sigaction __user *act, return ret; } -/* - * Do a signal return; undo the signal stack. - */ -struct sigframe -{ - struct sigcontext sc; - unsigned long extramask[_NSIG_WORDS-1]; - unsigned long retcode; -}; - -struct rt_sigframe -{ - struct siginfo __user *pinfo; - void __user *puc; - struct siginfo info; - struct ucontext uc; - unsigned long retcode; -}; - #ifdef CONFIG_IWMMXT /* iwmmxt_area is 0x98 bytes long, preceeded by 8 bytes of signature */ @@ -152,8 +132,15 @@ struct rt_sigframe #define IWMMXT_MAGIC0 0x12ef842a #define IWMMXT_MAGIC1 0x1c07ca71 -static int page_present(struct mm_struct *mm, unsigned long addr, int wr) +struct iwmmxt_sigframe { + unsigned long magic0; + unsigned long magic1; + unsigned long storage[0x98/4]; +}; + +static int page_present(struct mm_struct *mm, void __user *uptr, int wr) { + unsigned long addr = (unsigned long)uptr; pgd_t *pgd = pgd_offset(mm, addr); if (pgd_present(*pgd)) { pmd_t *pmd = pmd_offset(pgd, addr); @@ -165,49 +152,66 @@ static int page_present(struct mm_struct *mm, unsigned long addr, int wr) return 0; } -static int -preserve_iwmmxt_context(void *iwmmxt_save_area) +static int copy_locked(void __user *uptr, void *kptr, size_t size, int write, + void (*copyfn)(void *, void __user *)) +{ + unsigned char v, __user *userptr = uptr; + int err = 0; + + do { + struct mm_struct *mm; + + if (write) { + __put_user_error(0, userptr, err); + __put_user_error(0, userptr + size - 1, err); + } else { + __get_user_error(v, userptr, err); + __get_user_error(v, userptr + size - 1, err); + } + + if (err) + break; + + mm = current->mm; + spin_lock(&mm->page_table_lock); + if (page_present(mm, userptr, write) && + page_present(mm, userptr + size - 1, write)) { + copyfn(kptr, uptr); + } else + err = 1; + spin_unlock(&mm->page_table_lock); + } while (err); + + return err; +} + +static int preserve_iwmmxt_context(struct iwmmxt_sigframe *frame) { int err = 0; /* the iWMMXt context must be 64 bit aligned */ - long *iwmmxt_storage = (long *)(((long)iwmmxt_save_area + 4) & ~7); + WARN_ON((unsigned long)frame & 7); + + __put_user_error(IWMMXT_MAGIC0, &frame->magic0, err); + __put_user_error(IWMMXT_MAGIC1, &frame->magic1, err); -again: - __put_user_error(IWMMXT_MAGIC0, iwmmxt_storage+0, err); - __put_user_error(IWMMXT_MAGIC1, iwmmxt_storage+1, err); /* * iwmmxt_task_copy() doesn't check user permissions. * Let's do a dummy write on the upper boundary to ensure * access to user mem is OK all way up. */ - __put_user_error(0, iwmmxt_storage+IWMMXT_STORAGE_SIZE/4-1, err); - if (!err) { - /* Let's make sure the user mapping won't disappear under us */ - struct mm_struct *mm = current->mm; - unsigned long addr = (unsigned long)iwmmxt_storage; - spin_lock(&mm->page_table_lock); - if ( !page_present(mm, addr, 1) || - !page_present(mm, addr+IWMMXT_STORAGE_SIZE-1, 1) ) { - /* our user area has gone before grabbing the lock */ - spin_unlock(&mm->page_table_lock); - goto again; - } - iwmmxt_task_copy(current_thread_info(), iwmmxt_storage+2); - spin_unlock(&mm->page_table_lock); - return 0; - } + err |= copy_locked(&frame->storage, current_thread_info(), + sizeof(frame->storage), 1, iwmmxt_task_copy); return err; } -static int -restore_iwmmxt_context(void *iwmmxt_save_area) +static int restore_iwmmxt_context(struct iwmmxt_sigframe *frame) { + unsigned long magic0, magic1; int err = 0; - long *iwmmxt_storage, magic0, magic1, dummy; /* the iWMMXt context is 64 bit aligned */ - iwmmxt_storage = (long *)(((long)iwmmxt_save_area + 4) & ~7); + WARN_ON((unsigned long)frame & 7); /* * Validate iWMMXt context signature. @@ -215,32 +219,51 @@ restore_iwmmxt_context(void *iwmmxt_save_area) * Let's do a dummy write on the upper boundary to ensure * access to user mem is OK all way up. */ -again: - __get_user_error(magic0, iwmmxt_storage+0, err); - __get_user_error(magic1, iwmmxt_storage+1, err); - if (!err && magic0 == IWMMXT_MAGIC0 && magic1 == IWMMXT_MAGIC1 && - !__get_user(dummy, iwmmxt_storage+IWMMXT_STORAGE_SIZE/4-1)) { - /* Let's make sure the user mapping won't disappear under us */ - struct mm_struct *mm = current->mm; - unsigned long addr = (unsigned long)iwmmxt_storage; - spin_lock(&mm->page_table_lock); - if ( !page_present(mm, addr, 0) || - !page_present(mm, addr+IWMMXT_STORAGE_SIZE-1, 0) ) { - /* our user area has gone before grabbing the lock */ - spin_unlock(&mm->page_table_lock); - goto again; - } - iwmmxt_task_restore(current_thread_info(), iwmmxt_storage+2); - spin_unlock(&mm->page_table_lock); - return 0; - } - return -1; + __get_user_error(magic0, &frame->magic0, err); + __get_user_error(magic1, &frame->magic1, err); + if (!err && magic0 == IWMMXT_MAGIC0 && magic1 == IWMMXT_MAGIC1) + err = copy_locked(&frame->storage, current_thread_info(), + sizeof(frame->storage), 0, iwmmxt_task_restore); + return err; } #endif +/* + * Auxiliary signal frame. This saves stuff like FP state. + * The layout of this structure is not part of the user ABI. + */ +struct aux_sigframe { +#ifdef CONFIG_IWMMXT + struct iwmmxt_sigframe iwmmxt; +#endif +#ifdef CONFIG_VFP + union vfp_state vfp; +#endif +}; + +/* + * Do a signal return; undo the signal stack. These are aligned to 64-bit. + */ +struct sigframe { + struct sigcontext sc; + unsigned long extramask[_NSIG_WORDS-1]; + unsigned long retcode; + struct aux_sigframe aux __attribute__((aligned(8))); +}; + +struct rt_sigframe { + struct siginfo __user *pinfo; + void __user *puc; + struct siginfo info; + struct ucontext uc; + unsigned long retcode; + struct aux_sigframe aux __attribute__((aligned(8))); +}; + static int -restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc) +restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc, + struct aux_sigframe __user *aux) { int err = 0; @@ -264,6 +287,15 @@ restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc) err |= !valid_user_regs(regs); +#ifdef CONFIG_IWMMXT + if (err == 0 && test_thread_flag(TIF_USING_IWMMXT)) + err |= restore_iwmmxt_context(&aux->iwmmxt); +#endif +#ifdef CONFIG_VFP +// if (err == 0) +// err |= vfp_restore_state(&aux->vfp); +#endif + return err; } @@ -299,13 +331,8 @@ asmlinkage int sys_sigreturn(struct pt_regs *regs) recalc_sigpending(); spin_unlock_irq(¤t->sighand->siglock); - if (restore_sigcontext(regs, &frame->sc)) - goto badframe; - -#ifdef CONFIG_IWMMXT - if (test_thread_flag(TIF_USING_IWMMXT) && restore_iwmmxt_context(frame+1)) + if (restore_sigcontext(regs, &frame->sc, &frame->aux)) goto badframe; -#endif /* Send SIGTRAP if we're single-stepping */ if (current->ptrace & PT_SINGLESTEP) { @@ -349,17 +376,12 @@ asmlinkage int sys_rt_sigreturn(struct pt_regs *regs) recalc_sigpending(); spin_unlock_irq(¤t->sighand->siglock); - if (restore_sigcontext(regs, &frame->uc.uc_mcontext)) + if (restore_sigcontext(regs, &frame->uc.uc_mcontext, &frame->aux)) goto badframe; if (do_sigaltstack(&frame->uc.uc_stack, NULL, regs->ARM_sp) == -EFAULT) goto badframe; -#ifdef CONFIG_IWMMXT - if (test_thread_flag(TIF_USING_IWMMXT) && restore_iwmmxt_context(frame+1)) - goto badframe; -#endif - /* Send SIGTRAP if we're single-stepping */ if (current->ptrace & PT_SINGLESTEP) { ptrace_cancel_bpt(current); @@ -374,7 +396,7 @@ badframe: } static int -setup_sigcontext(struct sigcontext __user *sc, /*struct _fpstate *fpstate,*/ +setup_sigcontext(struct sigcontext __user *sc, struct aux_sigframe __user *aux, struct pt_regs *regs, unsigned long mask) { int err = 0; @@ -402,6 +424,15 @@ setup_sigcontext(struct sigcontext __user *sc, /*struct _fpstate *fpstate,*/ __put_user_error(current->thread.address, &sc->fault_address, err); __put_user_error(mask, &sc->oldmask, err); +#ifdef CONFIG_IWMMXT + if (err == 0 && test_thread_flag(TIF_USING_IWMMXT)) + err |= preserve_iwmmxt_context(&aux->iwmmxt); +#endif +#ifdef CONFIG_VFP +// if (err == 0) +// err |= vfp_save_state(&aux->vfp); +#endif + return err; } @@ -411,11 +442,6 @@ get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, int framesize) unsigned long sp = regs->ARM_sp; void __user *frame; -#ifdef CONFIG_IWMMXT - if (test_thread_flag(TIF_USING_IWMMXT)) - framesize = (framesize + 4 + IWMMXT_STORAGE_SIZE) & ~7; -#endif - /* * This is the X/Open sanctioned signal stack switching. */ @@ -505,18 +531,13 @@ setup_frame(int usig, struct k_sigaction *ka, sigset_t *set, struct pt_regs *reg if (!frame) return 1; - err |= setup_sigcontext(&frame->sc, /*&frame->fpstate,*/ regs, set->sig[0]); + err |= setup_sigcontext(&frame->sc, &frame->aux, regs, set->sig[0]); if (_NSIG_WORDS > 1) { err |= __copy_to_user(frame->extramask, &set->sig[1], sizeof(frame->extramask)); } -#ifdef CONFIG_IWMMXT - if (test_thread_flag(TIF_USING_IWMMXT)) - err |= preserve_iwmmxt_context(frame+1); -#endif - if (err == 0) err = setup_return(regs, ka, &frame->retcode, frame, usig); @@ -547,15 +568,10 @@ setup_rt_frame(int usig, struct k_sigaction *ka, siginfo_t *info, stack.ss_size = current->sas_ss_size; err |= __copy_to_user(&frame->uc.uc_stack, &stack, sizeof(stack)); - err |= setup_sigcontext(&frame->uc.uc_mcontext, /*&frame->fpstate,*/ + err |= setup_sigcontext(&frame->uc.uc_mcontext, &frame->aux, regs, set->sig[0]); err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); -#ifdef CONFIG_IWMMXT - if (test_thread_flag(TIF_USING_IWMMXT)) - err |= preserve_iwmmxt_context(frame+1); -#endif - if (err == 0) err = setup_return(regs, ka, &frame->retcode, frame, usig); @@ -672,10 +688,8 @@ static int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall) if (!user_mode(regs)) return 0; - if (current->flags & PF_FREEZE) { - refrigerator(0); + if (try_to_freeze(0)) goto no_signal; - } if (current->ptrace & PT_SINGLESTEP) ptrace_cancel_bpt(current);