diff options
Diffstat (limited to 'arch/m68k/kernel/signal.c')
| -rw-r--r-- | arch/m68k/kernel/signal.c | 199 | 
1 files changed, 83 insertions, 116 deletions
| diff --git a/arch/m68k/kernel/signal.c b/arch/m68k/kernel/signal.c index 8f215e79e70e..338817d0cb3f 100644 --- a/arch/m68k/kernel/signal.c +++ b/arch/m68k/kernel/signal.c @@ -447,7 +447,7 @@ static inline void save_fpu_state(struct sigcontext *sc, struct pt_regs *regs)  	if (CPU_IS_060 ? sc->sc_fpstate[2] : sc->sc_fpstate[0]) {  		fpu_version = sc->sc_fpstate[0]; -		if (CPU_IS_020_OR_030 && +		if (CPU_IS_020_OR_030 && !regs->stkadj &&  		    regs->vector >= (VEC_FPBRUC * 4) &&  		    regs->vector <= (VEC_FPNAN * 4)) {  			/* Clear pending exception in 68882 idle frame */ @@ -510,7 +510,7 @@ static inline int rt_save_fpu_state(struct ucontext __user *uc, struct pt_regs *  		if (!(CPU_IS_060 || CPU_IS_COLDFIRE))  			context_size = fpstate[1];  		fpu_version = fpstate[0]; -		if (CPU_IS_020_OR_030 && +		if (CPU_IS_020_OR_030 && !regs->stkadj &&  		    regs->vector >= (VEC_FPBRUC * 4) &&  		    regs->vector <= (VEC_FPNAN * 4)) {  			/* Clear pending exception in 68882 idle frame */ @@ -641,56 +641,35 @@ static inline void siginfo_build_tests(void)  static int mangle_kernel_stack(struct pt_regs *regs, int formatvec,  			       void __user *fp)  { -	int fsize = frame_extra_sizes(formatvec >> 12); -	if (fsize < 0) { +	int extra = frame_extra_sizes(formatvec >> 12); +	char buf[sizeof_field(struct frame, un)]; + +	if (extra < 0) {  		/*  		 * user process trying to return with weird frame format  		 */  		pr_debug("user process returning with weird frame format\n"); -		return 1; +		return -1;  	} -	if (!fsize) { -		regs->format = formatvec >> 12; -		regs->vector = formatvec & 0xfff; -	} else { -		struct switch_stack *sw = (struct switch_stack *)regs - 1; -		/* yes, twice as much as max(sizeof(frame.un.fmt<x>)) */ -		unsigned long buf[sizeof_field(struct frame, un) / 2]; - -		/* that'll make sure that expansion won't crap over data */ -		if (copy_from_user(buf + fsize / 4, fp, fsize)) -			return 1; - -		/* point of no return */ -		regs->format = formatvec >> 12; -		regs->vector = formatvec & 0xfff; -#define frame_offset (sizeof(struct pt_regs)+sizeof(struct switch_stack)) -		__asm__ __volatile__ ( -#ifdef CONFIG_COLDFIRE -			 "   movel %0,%/sp\n\t" -			 "   bra ret_from_signal\n" -#else -			 "   movel %0,%/a0\n\t" -			 "   subl %1,%/a0\n\t"     /* make room on stack */ -			 "   movel %/a0,%/sp\n\t"  /* set stack pointer */ -			 /* move switch_stack and pt_regs */ -			 "1: movel %0@+,%/a0@+\n\t" -			 "   dbra %2,1b\n\t" -			 "   lea %/sp@(%c3),%/a0\n\t" /* add offset of fmt */ -			 "   lsrl  #2,%1\n\t" -			 "   subql #1,%1\n\t" -			 /* copy to the gap we'd made */ -			 "2: movel %4@+,%/a0@+\n\t" -			 "   dbra %1,2b\n\t" -			 "   bral ret_from_signal\n" +	if (extra && copy_from_user(buf, fp, extra)) +		return -1; +	regs->format = formatvec >> 12; +	regs->vector = formatvec & 0xfff; +	if (extra) { +		void *p = (struct switch_stack *)regs - 1; +		struct frame *new = (void *)regs - extra; +		int size = sizeof(struct pt_regs)+sizeof(struct switch_stack); + +		memmove(p - extra, p, size); +		memcpy(p - extra + size, buf, extra); +		current->thread.esp0 = (unsigned long)&new->ptregs; +#ifdef CONFIG_M68040 +		/* on 68040 complete pending writebacks if any */ +		if (new->ptregs.format == 7) // bus error frame +			berr_040cleanup(new);  #endif -			 : /* no outputs, it doesn't ever return */ -			 : "a" (sw), "d" (fsize), "d" (frame_offset/4-1), -			   "n" (frame_offset), "a" (buf + fsize/4) -			 : "a0"); -#undef frame_offset  	} -	return 0; +	return extra;  }  static inline int @@ -698,7 +677,6 @@ restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *usc, void __u  {  	int formatvec;  	struct sigcontext context; -	int err = 0;  	siginfo_build_tests(); @@ -707,7 +685,7 @@ restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *usc, void __u  	/* get previous context */  	if (copy_from_user(&context, usc, sizeof(context))) -		goto badframe; +		return -1;  	/* restore passed registers */  	regs->d0 = context.sc_d0; @@ -720,15 +698,10 @@ restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *usc, void __u  	wrusp(context.sc_usp);  	formatvec = context.sc_formatvec; -	err = restore_fpu_state(&context); +	if (restore_fpu_state(&context)) +		return -1; -	if (err || mangle_kernel_stack(regs, formatvec, fp)) -		goto badframe; - -	return 0; - -badframe: -	return 1; +	return mangle_kernel_stack(regs, formatvec, fp);  }  static inline int @@ -745,7 +718,7 @@ rt_restore_ucontext(struct pt_regs *regs, struct switch_stack *sw,  	err = __get_user(temp, &uc->uc_mcontext.version);  	if (temp != MCONTEXT_VERSION) -		goto badframe; +		return -1;  	/* restore passed registers */  	err |= __get_user(regs->d0, &gregs[0]);  	err |= __get_user(regs->d1, &gregs[1]); @@ -774,22 +747,17 @@ rt_restore_ucontext(struct pt_regs *regs, struct switch_stack *sw,  	err |= restore_altstack(&uc->uc_stack);  	if (err) -		goto badframe; +		return -1; -	if (mangle_kernel_stack(regs, temp, &uc->uc_extra)) -		goto badframe; - -	return 0; - -badframe: -	return 1; +	return mangle_kernel_stack(regs, temp, &uc->uc_extra);  } -asmlinkage int do_sigreturn(struct pt_regs *regs, struct switch_stack *sw) +asmlinkage void *do_sigreturn(struct pt_regs *regs, struct switch_stack *sw)  {  	unsigned long usp = rdusp();  	struct sigframe __user *frame = (struct sigframe __user *)(usp - 4);  	sigset_t set; +	int size;  	if (!access_ok(frame, sizeof(*frame)))  		goto badframe; @@ -801,20 +769,22 @@ asmlinkage int do_sigreturn(struct pt_regs *regs, struct switch_stack *sw)  	set_current_blocked(&set); -	if (restore_sigcontext(regs, &frame->sc, frame + 1)) +	size = restore_sigcontext(regs, &frame->sc, frame + 1); +	if (size < 0)  		goto badframe; -	return regs->d0; +	return (void *)sw - size;  badframe:  	force_sig(SIGSEGV); -	return 0; +	return sw;  } -asmlinkage int do_rt_sigreturn(struct pt_regs *regs, struct switch_stack *sw) +asmlinkage void *do_rt_sigreturn(struct pt_regs *regs, struct switch_stack *sw)  {  	unsigned long usp = rdusp();  	struct rt_sigframe __user *frame = (struct rt_sigframe __user *)(usp - 4);  	sigset_t set; +	int size;  	if (!access_ok(frame, sizeof(*frame)))  		goto badframe; @@ -823,27 +793,34 @@ asmlinkage int do_rt_sigreturn(struct pt_regs *regs, struct switch_stack *sw)  	set_current_blocked(&set); -	if (rt_restore_ucontext(regs, sw, &frame->uc)) +	size = rt_restore_ucontext(regs, sw, &frame->uc); +	if (size < 0)  		goto badframe; -	return regs->d0; +	return (void *)sw - size;  badframe:  	force_sig(SIGSEGV); -	return 0; +	return sw; +} + +static inline struct pt_regs *rte_regs(struct pt_regs *regs) +{ +	return (void *)regs + regs->stkadj;  }  static void setup_sigcontext(struct sigcontext *sc, struct pt_regs *regs,  			     unsigned long mask)  { +	struct pt_regs *tregs = rte_regs(regs);  	sc->sc_mask = mask;  	sc->sc_usp = rdusp();  	sc->sc_d0 = regs->d0;  	sc->sc_d1 = regs->d1;  	sc->sc_a0 = regs->a0;  	sc->sc_a1 = regs->a1; -	sc->sc_sr = regs->sr; -	sc->sc_pc = regs->pc; -	sc->sc_formatvec = regs->format << 12 | regs->vector; +	sc->sc_sr = tregs->sr; +	sc->sc_pc = tregs->pc; +	sc->sc_formatvec = tregs->format << 12 | tregs->vector;  	save_a5_state(sc, regs);  	save_fpu_state(sc, regs);  } @@ -851,6 +828,7 @@ static void setup_sigcontext(struct sigcontext *sc, struct pt_regs *regs,  static inline int rt_setup_ucontext(struct ucontext __user *uc, struct pt_regs *regs)  {  	struct switch_stack *sw = (struct switch_stack *)regs - 1; +	struct pt_regs *tregs = rte_regs(regs);  	greg_t __user *gregs = uc->uc_mcontext.gregs;  	int err = 0; @@ -871,9 +849,9 @@ static inline int rt_setup_ucontext(struct ucontext __user *uc, struct pt_regs *  	err |= __put_user(sw->a5, &gregs[13]);  	err |= __put_user(sw->a6, &gregs[14]);  	err |= __put_user(rdusp(), &gregs[15]); -	err |= __put_user(regs->pc, &gregs[16]); -	err |= __put_user(regs->sr, &gregs[17]); -	err |= __put_user((regs->format << 12) | regs->vector, &uc->uc_formatvec); +	err |= __put_user(tregs->pc, &gregs[16]); +	err |= __put_user(tregs->sr, &gregs[17]); +	err |= __put_user((tregs->format << 12) | tregs->vector, &uc->uc_formatvec);  	err |= rt_save_fpu_state(uc, regs);  	return err;  } @@ -890,13 +868,14 @@ static int setup_frame(struct ksignal *ksig, sigset_t *set,  			struct pt_regs *regs)  {  	struct sigframe __user *frame; -	int fsize = frame_extra_sizes(regs->format); +	struct pt_regs *tregs = rte_regs(regs); +	int fsize = frame_extra_sizes(tregs->format);  	struct sigcontext context;  	int err = 0, sig = ksig->sig;  	if (fsize < 0) {  		pr_debug("setup_frame: Unknown frame format %#x\n", -			 regs->format); +			 tregs->format);  		return -EFAULT;  	} @@ -907,7 +886,7 @@ static int setup_frame(struct ksignal *ksig, sigset_t *set,  	err |= __put_user(sig, &frame->sig); -	err |= __put_user(regs->vector, &frame->code); +	err |= __put_user(tregs->vector, &frame->code);  	err |= __put_user(&frame->sc, &frame->psc);  	if (_NSIG_WORDS > 1) @@ -934,33 +913,27 @@ static int setup_frame(struct ksignal *ksig, sigset_t *set,  	push_cache ((unsigned long) &frame->retcode);  	/* -	 * Set up registers for signal handler.  All the state we are about -	 * to destroy is successfully copied to sigframe. -	 */ -	wrusp ((unsigned long) frame); -	regs->pc = (unsigned long) ksig->ka.sa.sa_handler; -	adjustformat(regs); - -	/*  	 * This is subtle; if we build more than one sigframe, all but the  	 * first one will see frame format 0 and have fsize == 0, so we won't  	 * screw stkadj.  	 */ -	if (fsize) +	if (fsize) {  		regs->stkadj = fsize; - -	/* Prepare to skip over the extra stuff in the exception frame.  */ -	if (regs->stkadj) { -		struct pt_regs *tregs = -			(struct pt_regs *)((ulong)regs + regs->stkadj); +		tregs = rte_regs(regs);  		pr_debug("Performing stackadjust=%04lx\n", regs->stkadj); -		/* This must be copied with decreasing addresses to -                   handle overlaps.  */  		tregs->vector = 0;  		tregs->format = 0; -		tregs->pc = regs->pc;  		tregs->sr = regs->sr;  	} + +	/* +	 * Set up registers for signal handler.  All the state we are about +	 * to destroy is successfully copied to sigframe. +	 */ +	wrusp ((unsigned long) frame); +	tregs->pc = (unsigned long) ksig->ka.sa.sa_handler; +	adjustformat(regs); +  	return 0;  } @@ -968,7 +941,8 @@ static int setup_rt_frame(struct ksignal *ksig, sigset_t *set,  			   struct pt_regs *regs)  {  	struct rt_sigframe __user *frame; -	int fsize = frame_extra_sizes(regs->format); +	struct pt_regs *tregs = rte_regs(regs); +	int fsize = frame_extra_sizes(tregs->format);  	int err = 0, sig = ksig->sig;  	if (fsize < 0) { @@ -1019,33 +993,26 @@ static int setup_rt_frame(struct ksignal *ksig, sigset_t *set,  	push_cache ((unsigned long) &frame->retcode);  	/* -	 * Set up registers for signal handler.  All the state we are about -	 * to destroy is successfully copied to sigframe. -	 */ -	wrusp ((unsigned long) frame); -	regs->pc = (unsigned long) ksig->ka.sa.sa_handler; -	adjustformat(regs); - -	/*  	 * This is subtle; if we build more than one sigframe, all but the  	 * first one will see frame format 0 and have fsize == 0, so we won't  	 * screw stkadj.  	 */ -	if (fsize) +	if (fsize) {  		regs->stkadj = fsize; - -	/* Prepare to skip over the extra stuff in the exception frame.  */ -	if (regs->stkadj) { -		struct pt_regs *tregs = -			(struct pt_regs *)((ulong)regs + regs->stkadj); +		tregs = rte_regs(regs);  		pr_debug("Performing stackadjust=%04lx\n", regs->stkadj); -		/* This must be copied with decreasing addresses to -                   handle overlaps.  */  		tregs->vector = 0;  		tregs->format = 0; -		tregs->pc = regs->pc;  		tregs->sr = regs->sr;  	} + +	/* +	 * Set up registers for signal handler.  All the state we are about +	 * to destroy is successfully copied to sigframe. +	 */ +	wrusp ((unsigned long) frame); +	tregs->pc = (unsigned long) ksig->ka.sa.sa_handler; +	adjustformat(regs);  	return 0;  } |