X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=arch%2Farm%2Fkernel%2Fsignal.c;h=a0cd0a90a10d06f4bd266db044937796337c1eb3;hb=43bc926fffd92024b46cafaf7350d669ba9ca884;hp=c0f84aacb0bad4dfc036cbdac2ddb3c231035796;hpb=5273a3df6485dc2ad6aa7ddd441b9a21970f003b;p=linux-2.6.git diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c index c0f84aacb..a0cd0a90a 100644 --- a/arch/arm/kernel/signal.c +++ b/arch/arm/kernel/signal.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include @@ -20,6 +19,7 @@ #include #include "ptrace.h" +#include "signal.h" #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) @@ -29,6 +29,12 @@ #define SWI_SYS_SIGRETURN (0xef000000|(__NR_sigreturn)) #define SWI_SYS_RT_SIGRETURN (0xef000000|(__NR_rt_sigreturn)) +/* + * With EABI, the syscall number has to be loaded into r7. + */ +#define MOV_R7_NR_SIGRETURN (0xe3a07000 | (__NR_sigreturn - __NR_SYSCALL_BASE)) +#define MOV_R7_NR_RT_SIGRETURN (0xe3a07000 | (__NR_rt_sigreturn - __NR_SYSCALL_BASE)) + /* * For Thumb syscalls, we pass the syscall number via r7. We therefore * need two 16-bit instructions. @@ -36,9 +42,9 @@ #define SWI_THUMB_SIGRETURN (0xdf00 << 16 | 0x2700 | (__NR_sigreturn - __NR_SYSCALL_BASE)) #define SWI_THUMB_RT_SIGRETURN (0xdf00 << 16 | 0x2700 | (__NR_rt_sigreturn - __NR_SYSCALL_BASE)) -static const unsigned long retcodes[4] = { - SWI_SYS_SIGRETURN, SWI_THUMB_SIGRETURN, - SWI_SYS_RT_SIGRETURN, SWI_THUMB_RT_SIGRETURN +const unsigned long sigreturn_codes[7] = { + MOV_R7_NR_SIGRETURN, SWI_SYS_SIGRETURN, SWI_THUMB_SIGRETURN, + MOV_R7_NR_RT_SIGRETURN, SWI_SYS_RT_SIGRETURN, SWI_THUMB_RT_SIGRETURN, }; static int do_signal(sigset_t *oldset, struct pt_regs * regs, int syscall); @@ -103,7 +109,7 @@ sys_sigaction(int sig, const struct old_sigaction __user *act, if (act) { old_sigset_t mask; - if (verify_area(VERIFY_READ, act, sizeof(*act)) || + if (!access_ok(VERIFY_READ, act, sizeof(*act)) || __get_user(new_ka.sa.sa_handler, &act->sa_handler) || __get_user(new_ka.sa.sa_restorer, &act->sa_restorer)) return -EFAULT; @@ -115,7 +121,7 @@ sys_sigaction(int sig, const struct old_sigaction __user *act, ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); if (!ret && oact) { - if (verify_area(VERIFY_WRITE, oact, sizeof(*oact)) || + if (!access_ok(VERIFY_WRITE, oact, sizeof(*oact)) || __put_user(old_ka.sa.sa_handler, &oact->sa_handler) || __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer)) return -EFAULT; @@ -126,27 +132,85 @@ 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 preserve_iwmmxt_context(struct iwmmxt_sigframe *frame) +{ + char kbuf[sizeof(*frame) + 8]; + struct iwmmxt_sigframe *kframe; + + /* the iWMMXt context must be 64 bit aligned */ + kframe = (struct iwmmxt_sigframe *)((unsigned long)(kbuf + 8) & ~7); + kframe->magic0 = IWMMXT_MAGIC0; + kframe->magic1 = IWMMXT_MAGIC1; + iwmmxt_task_copy(current_thread_info(), &kframe->storage); + return __copy_to_user(frame, kframe, sizeof(*frame)); +} + +static int restore_iwmmxt_context(struct iwmmxt_sigframe *frame) +{ + char kbuf[sizeof(*frame) + 8]; + struct iwmmxt_sigframe *kframe; + + /* the iWMMXt context must be 64 bit aligned */ + kframe = (struct iwmmxt_sigframe *)((unsigned long)(kbuf + 8) & ~7); + if (__copy_from_user(kframe, frame, sizeof(*frame))) + return -1; + if (kframe->magic0 != IWMMXT_MAGIC0 || + kframe->magic1 != IWMMXT_MAGIC1) + return -1; + iwmmxt_task_restore(current_thread_info(), &kframe->storage); + return 0; +} + +#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; + unsigned long retcode[2]; + 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; + unsigned long retcode[2]; + 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 +234,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; } @@ -191,7 +264,7 @@ asmlinkage int sys_sigreturn(struct pt_regs *regs) frame = (struct sigframe __user *)regs->ARM_sp; - if (verify_area(VERIFY_READ, frame, sizeof (*frame))) + if (!access_ok(VERIFY_READ, frame, sizeof (*frame))) goto badframe; if (__get_user(set.sig[0], &frame->sc.oldmask) || (_NSIG_WORDS > 1 @@ -205,7 +278,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 */ @@ -239,7 +312,7 @@ asmlinkage int sys_rt_sigreturn(struct pt_regs *regs) frame = (struct rt_sigframe __user *)regs->ARM_sp; - if (verify_area(VERIFY_READ, frame, sizeof (*frame))) + if (!access_ok(VERIFY_READ, frame, sizeof (*frame))) goto badframe; if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) goto badframe; @@ -250,7 +323,10 @@ 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; /* Send SIGTRAP if we're single-stepping */ @@ -267,7 +343,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; @@ -295,6 +371,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; } @@ -302,6 +387,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. @@ -312,7 +398,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 @@ -348,22 +442,31 @@ setup_return(struct pt_regs *regs, struct k_sigaction *ka, if (ka->sa.sa_flags & SA_RESTORER) { retcode = (unsigned long)ka->sa.sa_restorer; } else { - unsigned int idx = thumb; + unsigned int idx = thumb << 1; if (ka->sa.sa_flags & SA_SIGINFO) - idx += 2; + idx += 3; - if (__put_user(retcodes[idx], rc)) + if (__put_user(sigreturn_codes[idx], rc) || + __put_user(sigreturn_codes[idx+1], rc+1)) return 1; - /* - * Ensure that the instruction cache sees - * the return code written onto the stack. - */ - flush_icache_range((unsigned long)rc, - (unsigned long)(rc + 1)); - - retcode = ((unsigned long)rc) + thumb; + if (cpsr & MODE32_BIT) { + /* + * 32-bit code can use the new high-page + * signal return code support. + */ + retcode = KERN_SIGRETURN_CODE + (idx << 2) + thumb; + } else { + /* + * Ensure that the instruction cache sees + * the return code written onto the stack. + */ + flush_icache_range((unsigned long)rc, + (unsigned long)(rc + 2)); + + retcode = ((unsigned long)rc) + thumb; + } } regs->ARM_r0 = usig; @@ -381,10 +484,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], @@ -392,7 +495,7 @@ setup_frame(int usig, struct k_sigaction *ka, sigset_t *set, struct pt_regs *reg } if (err == 0) - err = setup_return(regs, ka, &frame->retcode, frame, usig); + err = setup_return(regs, ka, frame->retcode, frame, usig); return err; } @@ -402,24 +505,31 @@ setup_rt_frame(int usig, struct k_sigaction *ka, siginfo_t *info, sigset_t *set, struct pt_regs *regs) { struct rt_sigframe __user *frame = get_sigframe(ka, regs, sizeof(*frame)); + 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); __put_user_error(&frame->uc, &frame->puc, err); err |= copy_siginfo_to_user(&frame->info, info); - /* Clear all the bits of the ucontext we don't use. */ - err |= __clear_user(&frame->uc, offsetof(struct ucontext, uc_mcontext)); + __put_user_error(0, &frame->uc.uc_flags, err); + __put_user_error(NULL, &frame->uc.uc_link, err); + + memset(&stack, 0, sizeof(stack)); + stack.ss_sp = (void __user *)current->sas_ss_sp; + stack.ss_flags = sas_ss_flags(regs->ARM_sp); + 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)); if (err == 0) - err = setup_return(regs, ka, &frame->retcode, frame, usig); + err = setup_return(regs, ka, frame->retcode, frame, usig); if (err == 0) { /* @@ -427,8 +537,8 @@ setup_rt_frame(int usig, struct k_sigaction *ka, siginfo_t *info, * arguments for the signal handler. * -- Peter Maydell 2000-12-06 */ - regs->ARM_r1 = (unsigned long)frame->pinfo; - regs->ARM_r2 = (unsigned long)frame->puc; + regs->ARM_r1 = (unsigned long)&frame->info; + regs->ARM_r2 = (unsigned long)&frame->uc; } return err; @@ -444,12 +554,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; @@ -492,27 +602,22 @@ handle_signal(unsigned long sig, siginfo_t *info, sigset_t *oldset, */ ret |= !valid_user_regs(regs); + if (ret != 0) { + force_sigsegv(sig, tsk); + return; + } + /* - * Block the signal if we were unsuccessful. + * Block the signal if we were successful. */ - if (ret != 0 || !(ka->sa.sa_flags & SA_NODEFER)) { - spin_lock_irq(&tsk->sighand->siglock); - sigorsets(&tsk->blocked, &tsk->blocked, - &ka->sa.sa_mask); + spin_lock_irq(&tsk->sighand->siglock); + sigorsets(&tsk->blocked, &tsk->blocked, + &ka->sa.sa_mask); + if (!(ka->sa.sa_flags & SA_NODEFER)) sigaddset(&tsk->blocked, sig); - recalc_sigpending(); - spin_unlock_irq(&tsk->sighand->siglock); - } - - if (ret == 0) { - if (ka->sa.sa_flags & SA_ONESHOT) - ka->sa.sa_handler = SIG_DFL; - return; - } + recalc_sigpending(); + spin_unlock_irq(&tsk->sighand->siglock); - if (sig == SIGSEGV) - ka->sa.sa_handler = SIG_DFL; - force_sig(SIGSEGV, tsk); } /* @@ -526,6 +631,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; @@ -538,17 +644,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()) 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; @@ -564,10 +668,10 @@ static int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall) regs->ARM_r7 = __NR_restart_syscall; regs->ARM_pc -= 2; } else { - u32 *usp; + u32 __user *usp; regs->ARM_sp -= 12; - usp = (u32 *)regs->ARM_sp; + usp = (u32 __user *)regs->ARM_sp; put_user(regs->ARM_pc, &usp[0]); /* swi __NR_restart_syscall */