X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=arch%2Farm%2Fkernel%2Fsignal.c;h=ef7fa2c71284daee95f2a970d1e99d6fcf2df18b;hb=6a77f38946aaee1cd85eeec6cf4229b204c15071;hp=d214e8d494f60354b1fe9571c4decbafc2da38f1;hpb=9bf4aaab3e101692164d49b7ca357651eb691cb6;p=linux-2.6.git diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c index d214e8d49..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,27 +125,145 @@ sys_sigaction(int sig, const struct old_sigaction __user *act, return ret; } +#ifdef CONFIG_IWMMXT + +/* iwmmxt_area is 0x98 bytes long, preceeded by 8 bytes of signature */ +#define IWMMXT_STORAGE_SIZE (0x98 + 8) +#define IWMMXT_MAGIC0 0x12ef842a +#define IWMMXT_MAGIC1 0x1c07ca71 + +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); + if (pmd_present(*pmd)) { + pte_t *pte = pte_offset_map(pmd, addr); + return (pte_present(*pte) && (!wr || pte_write(*pte))); + } + } + return 0; +} + +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 */ + WARN_ON((unsigned long)frame & 7); + + __put_user_error(IWMMXT_MAGIC0, &frame->magic0, err); + __put_user_error(IWMMXT_MAGIC1, &frame->magic1, 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. + */ + err |= copy_locked(&frame->storage, current_thread_info(), + sizeof(frame->storage), 1, iwmmxt_task_copy); + return err; +} + +static int restore_iwmmxt_context(struct iwmmxt_sigframe *frame) +{ + unsigned long magic0, magic1; + int err = 0; + + /* the iWMMXt context is 64 bit aligned */ + WARN_ON((unsigned long)frame & 7); + + /* + * Validate iWMMXt context signature. + * Also, iwmmxt_task_restore() 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. + */ + __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 + /* - * Do a signal return; undo the signal stack. + * Auxiliary signal frame. This saves stuff like FP state. + * The layout of this structure is not part of the user ABI. */ -struct sigframe -{ +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 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; @@ -170,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; } @@ -205,7 +331,7 @@ asmlinkage int sys_sigreturn(struct pt_regs *regs) recalc_sigpending(); spin_unlock_irq(¤t->sighand->siglock); - if (restore_sigcontext(regs, &frame->sc)) + if (restore_sigcontext(regs, &frame->sc, &frame->aux)) goto badframe; /* Send SIGTRAP if we're single-stepping */ @@ -250,7 +376,7 @@ 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) @@ -270,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; @@ -298,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; } @@ -305,6 +440,7 @@ static inline void __user * get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, int framesize) { unsigned long sp = regs->ARM_sp; + void __user *frame; /* * This is the X/Open sanctioned signal stack switching. @@ -315,7 +451,15 @@ get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, int framesize) /* * ATPCS B01 mandates 8-byte alignment */ - return (void __user *)((sp - framesize) & ~7); + frame = (void __user *)((sp - framesize) & ~7); + + /* + * Check that we can actually write to the signal frame. + */ + if (!access_ok(VERIFY_WRITE, frame, framesize)) + frame = NULL; + + return frame; } static int @@ -384,10 +528,10 @@ setup_frame(int usig, struct k_sigaction *ka, sigset_t *set, struct pt_regs *reg struct sigframe __user *frame = get_sigframe(ka, regs, sizeof(*frame)); int err = 0; - if (!access_ok(VERIFY_WRITE, frame, sizeof (*frame))) + 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], @@ -408,7 +552,7 @@ setup_rt_frame(int usig, struct k_sigaction *ka, siginfo_t *info, stack_t stack; int err = 0; - if (!access_ok(VERIFY_WRITE, frame, sizeof (*frame))) + if (!frame) return 1; __put_user_error(&frame->info, &frame->pinfo, err); @@ -424,7 +568,7 @@ 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)); @@ -454,12 +598,12 @@ static inline void restart_syscall(struct pt_regs *regs) * OK, we're invoking a handler */ static void -handle_signal(unsigned long sig, siginfo_t *info, sigset_t *oldset, +handle_signal(unsigned long sig, struct k_sigaction *ka, + siginfo_t *info, sigset_t *oldset, struct pt_regs * regs, int syscall) { struct thread_info *thread = current_thread_info(); struct task_struct *tsk = current; - struct k_sigaction *ka = &tsk->sighand->action[sig-1]; int usig = sig; int ret; @@ -514,15 +658,10 @@ handle_signal(unsigned long sig, siginfo_t *info, sigset_t *oldset, spin_unlock_irq(&tsk->sighand->siglock); } - if (ret == 0) { - if (ka->sa.sa_flags & SA_ONESHOT) - ka->sa.sa_handler = SIG_DFL; + if (ret == 0) return; - } - if (sig == SIGSEGV) - ka->sa.sa_handler = SIG_DFL; - force_sig(SIGSEGV, tsk); + force_sigsegv(sig, tsk); } /* @@ -536,6 +675,7 @@ handle_signal(unsigned long sig, siginfo_t *info, sigset_t *oldset, */ static int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall) { + struct k_sigaction ka; siginfo_t info; int signr; @@ -548,17 +688,15 @@ 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); - signr = get_signal_to_deliver(&info, regs, NULL); + signr = get_signal_to_deliver(&info, &ka, regs, NULL); if (signr > 0) { - handle_signal(signr, &info, oldset, regs, syscall); + handle_signal(signr, &ka, &info, oldset, regs, syscall); if (current->ptrace & PT_SINGLESTEP) ptrace_set_bpt(current); return 1;