| 
									
										
										
										
											2008-02-08 04:19:31 -08:00
										 |  |  | /* MN10300 Signal handling
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. | 
					
						
							|  |  |  |  * Written by David Howells (dhowells@redhat.com) | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is free software; you can redistribute it and/or | 
					
						
							|  |  |  |  * modify it under the terms of the GNU General Public Licence | 
					
						
							|  |  |  |  * as published by the Free Software Foundation; either version | 
					
						
							|  |  |  |  * 2 of the Licence, or (at your option) any later version. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <linux/sched.h>
 | 
					
						
							|  |  |  | #include <linux/mm.h>
 | 
					
						
							|  |  |  | #include <linux/smp.h>
 | 
					
						
							|  |  |  | #include <linux/kernel.h>
 | 
					
						
							|  |  |  | #include <linux/signal.h>
 | 
					
						
							|  |  |  | #include <linux/errno.h>
 | 
					
						
							|  |  |  | #include <linux/wait.h>
 | 
					
						
							|  |  |  | #include <linux/ptrace.h>
 | 
					
						
							|  |  |  | #include <linux/unistd.h>
 | 
					
						
							|  |  |  | #include <linux/stddef.h>
 | 
					
						
							|  |  |  | #include <linux/tty.h>
 | 
					
						
							|  |  |  | #include <linux/personality.h>
 | 
					
						
							|  |  |  | #include <linux/suspend.h>
 | 
					
						
							| 
									
										
										
										
											2009-06-11 13:08:37 +01:00
										 |  |  | #include <linux/tracehook.h>
 | 
					
						
							| 
									
										
										
										
											2008-02-08 04:19:31 -08:00
										 |  |  | #include <asm/cacheflush.h>
 | 
					
						
							|  |  |  | #include <asm/ucontext.h>
 | 
					
						
							|  |  |  | #include <asm/uaccess.h>
 | 
					
						
							|  |  |  | #include <asm/fpu.h>
 | 
					
						
							|  |  |  | #include "sigframe.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define DEBUG_SIG 0
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * do a signal return; undo the signal stack. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static int restore_sigcontext(struct pt_regs *regs, | 
					
						
							|  |  |  | 			      struct sigcontext __user *sc, long *_d0) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	unsigned int err = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-09-26 19:28:52 +01:00
										 |  |  | 	/* Always make any pending restarted system calls return -EINTR */ | 
					
						
							|  |  |  | 	current_thread_info()->restart_block.fn = do_no_restart_syscall; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-02-08 04:19:31 -08:00
										 |  |  | 	if (is_using_fpu(current)) | 
					
						
							|  |  |  | 		fpu_kill_state(current); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define COPY(x) err |= __get_user(regs->x, &sc->x)
 | 
					
						
							|  |  |  | 	COPY(d1); COPY(d2); COPY(d3); | 
					
						
							|  |  |  | 	COPY(a0); COPY(a1); COPY(a2); COPY(a3); | 
					
						
							|  |  |  | 	COPY(e0); COPY(e1); COPY(e2); COPY(e3); | 
					
						
							|  |  |  | 	COPY(e4); COPY(e5); COPY(e6); COPY(e7); | 
					
						
							|  |  |  | 	COPY(lar); COPY(lir); | 
					
						
							|  |  |  | 	COPY(mdr); COPY(mdrq); | 
					
						
							|  |  |  | 	COPY(mcvf); COPY(mcrl); COPY(mcrh); | 
					
						
							|  |  |  | 	COPY(sp); COPY(pc); | 
					
						
							|  |  |  | #undef COPY
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		unsigned int tmpflags; | 
					
						
							|  |  |  | #ifndef CONFIG_MN10300_USING_JTAG
 | 
					
						
							|  |  |  | #define USER_EPSW (EPSW_FLAG_Z | EPSW_FLAG_N | EPSW_FLAG_C | EPSW_FLAG_V | \
 | 
					
						
							|  |  |  | 		   EPSW_T | EPSW_nAR) | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  | #define USER_EPSW (EPSW_FLAG_Z | EPSW_FLAG_N | EPSW_FLAG_C | EPSW_FLAG_V | \
 | 
					
						
							|  |  |  | 		   EPSW_nAR) | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 		err |= __get_user(tmpflags, &sc->epsw); | 
					
						
							|  |  |  | 		regs->epsw = (regs->epsw & ~USER_EPSW) | | 
					
						
							|  |  |  | 		  (tmpflags & USER_EPSW); | 
					
						
							|  |  |  | 		regs->orig_d0 = -1;		/* disable syscall checks */ | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		struct fpucontext *buf; | 
					
						
							|  |  |  | 		err |= __get_user(buf, &sc->fpucontext); | 
					
						
							|  |  |  | 		if (buf) { | 
					
						
							|  |  |  | 			if (verify_area(VERIFY_READ, buf, sizeof(*buf))) | 
					
						
							|  |  |  | 				goto badframe; | 
					
						
							|  |  |  | 			err |= fpu_restore_sigcontext(buf); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	err |= __get_user(*_d0, &sc->d0); | 
					
						
							|  |  |  | 	return err; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | badframe: | 
					
						
							|  |  |  | 	return 1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * standard signal return syscall | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | asmlinkage long sys_sigreturn(void) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2010-10-27 17:29:01 +01:00
										 |  |  | 	struct sigframe __user *frame; | 
					
						
							| 
									
										
										
										
											2008-02-08 04:19:31 -08:00
										 |  |  | 	sigset_t set; | 
					
						
							|  |  |  | 	long d0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-10-27 17:29:01 +01:00
										 |  |  | 	frame = (struct sigframe __user *) current_frame()->sp; | 
					
						
							| 
									
										
										
										
											2008-02-08 04:19:31 -08:00
										 |  |  | 	if (verify_area(VERIFY_READ, frame, sizeof(*frame))) | 
					
						
							|  |  |  | 		goto badframe; | 
					
						
							|  |  |  | 	if (__get_user(set.sig[0], &frame->sc.oldmask)) | 
					
						
							|  |  |  | 		goto badframe; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (_NSIG_WORDS > 1 && | 
					
						
							|  |  |  | 	    __copy_from_user(&set.sig[1], &frame->extramask, | 
					
						
							|  |  |  | 			     sizeof(frame->extramask))) | 
					
						
							|  |  |  | 		goto badframe; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-11 10:57:59 +10:00
										 |  |  | 	set_current_blocked(&set); | 
					
						
							| 
									
										
										
										
											2008-02-08 04:19:31 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-10-27 17:29:01 +01:00
										 |  |  | 	if (restore_sigcontext(current_frame(), &frame->sc, &d0)) | 
					
						
							| 
									
										
										
										
											2008-02-08 04:19:31 -08:00
										 |  |  | 		goto badframe; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return d0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | badframe: | 
					
						
							|  |  |  | 	force_sig(SIGSEGV, current); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * realtime signal return syscall | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | asmlinkage long sys_rt_sigreturn(void) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2010-10-27 17:29:01 +01:00
										 |  |  | 	struct rt_sigframe __user *frame; | 
					
						
							| 
									
										
										
										
											2008-02-08 04:19:31 -08:00
										 |  |  | 	sigset_t set; | 
					
						
							| 
									
										
										
										
											2010-10-27 17:29:01 +01:00
										 |  |  | 	long d0; | 
					
						
							| 
									
										
										
										
											2008-02-08 04:19:31 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-10-27 17:29:01 +01:00
										 |  |  | 	frame = (struct rt_sigframe __user *) current_frame()->sp; | 
					
						
							| 
									
										
										
										
											2008-02-08 04:19:31 -08:00
										 |  |  | 	if (verify_area(VERIFY_READ, frame, sizeof(*frame))) | 
					
						
							|  |  |  | 		goto badframe; | 
					
						
							|  |  |  | 	if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) | 
					
						
							|  |  |  | 		goto badframe; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-11 10:57:59 +10:00
										 |  |  | 	set_current_blocked(&set); | 
					
						
							| 
									
										
										
										
											2008-02-08 04:19:31 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-10-27 17:29:01 +01:00
										 |  |  | 	if (restore_sigcontext(current_frame(), &frame->uc.uc_mcontext, &d0)) | 
					
						
							| 
									
										
										
										
											2008-02-08 04:19:31 -08:00
										 |  |  | 		goto badframe; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-23 03:17:06 -05:00
										 |  |  | 	if (restore_altstack(&frame->uc.uc_stack)) | 
					
						
							| 
									
										
										
										
											2008-02-08 04:19:31 -08:00
										 |  |  | 		goto badframe; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return d0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | badframe: | 
					
						
							|  |  |  | 	force_sig(SIGSEGV, current); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * store the userspace context into a signal frame | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static int setup_sigcontext(struct sigcontext __user *sc, | 
					
						
							|  |  |  | 			    struct fpucontext *fpuctx, | 
					
						
							|  |  |  | 			    struct pt_regs *regs, | 
					
						
							|  |  |  | 			    unsigned long mask) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int tmp, err = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define COPY(x) err |= __put_user(regs->x, &sc->x)
 | 
					
						
							|  |  |  | 	COPY(d0); COPY(d1); COPY(d2); COPY(d3); | 
					
						
							|  |  |  | 	COPY(a0); COPY(a1); COPY(a2); COPY(a3); | 
					
						
							|  |  |  | 	COPY(e0); COPY(e1); COPY(e2); COPY(e3); | 
					
						
							|  |  |  | 	COPY(e4); COPY(e5); COPY(e6); COPY(e7); | 
					
						
							|  |  |  | 	COPY(lar); COPY(lir); | 
					
						
							|  |  |  | 	COPY(mdr); COPY(mdrq); | 
					
						
							|  |  |  | 	COPY(mcvf); COPY(mcrl); COPY(mcrh); | 
					
						
							|  |  |  | 	COPY(sp); COPY(epsw); COPY(pc); | 
					
						
							|  |  |  | #undef COPY
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	tmp = fpu_setup_sigcontext(fpuctx); | 
					
						
							|  |  |  | 	if (tmp < 0) | 
					
						
							|  |  |  | 		err = 1; | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		err |= __put_user(tmp ? fpuctx : NULL, &sc->fpucontext); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* non-iBCS2 extensions.. */ | 
					
						
							|  |  |  | 	err |= __put_user(mask, &sc->oldmask); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return err; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * determine which stack to use.. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2014-03-05 15:36:49 +01:00
										 |  |  | static inline void __user *get_sigframe(struct ksignal *ksig, | 
					
						
							| 
									
										
										
										
											2008-02-08 04:19:31 -08:00
										 |  |  | 					struct pt_regs *regs, | 
					
						
							|  |  |  | 					size_t frame_size) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2014-03-05 15:36:49 +01:00
										 |  |  | 	unsigned long sp = sigsp(regs->sp, ksig); | 
					
						
							| 
									
										
										
										
											2008-02-08 04:19:31 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return (void __user *) ((sp - frame_size) & ~7UL); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * set up a normal signal frame | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2013-10-07 14:22:50 +02:00
										 |  |  | static int setup_frame(struct ksignal *ksig, sigset_t *set, | 
					
						
							| 
									
										
										
										
											2008-02-08 04:19:31 -08:00
										 |  |  | 		       struct pt_regs *regs) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct sigframe __user *frame; | 
					
						
							| 
									
										
										
										
											2013-10-07 14:22:50 +02:00
										 |  |  | 	int rsig, sig = ksig->sig; | 
					
						
							| 
									
										
										
										
											2008-02-08 04:19:31 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-05 15:36:49 +01:00
										 |  |  | 	frame = get_sigframe(ksig, regs, sizeof(*frame)); | 
					
						
							| 
									
										
										
										
											2008-02-08 04:19:31 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) | 
					
						
							| 
									
										
										
										
											2013-10-07 14:22:50 +02:00
										 |  |  | 		return -EFAULT; | 
					
						
							| 
									
										
										
										
											2008-02-08 04:19:31 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	rsig = sig; | 
					
						
							|  |  |  | 	if (sig < 32 && | 
					
						
							|  |  |  | 	    current_thread_info()->exec_domain && | 
					
						
							|  |  |  | 	    current_thread_info()->exec_domain->signal_invmap) | 
					
						
							|  |  |  | 		rsig = current_thread_info()->exec_domain->signal_invmap[sig]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (__put_user(rsig, &frame->sig) < 0 || | 
					
						
							|  |  |  | 	    __put_user(&frame->sc, &frame->psc) < 0) | 
					
						
							| 
									
										
										
										
											2013-10-07 14:22:50 +02:00
										 |  |  | 		return -EFAULT; | 
					
						
							| 
									
										
										
										
											2008-02-08 04:19:31 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (setup_sigcontext(&frame->sc, &frame->fpuctx, regs, set->sig[0])) | 
					
						
							| 
									
										
										
										
											2013-10-07 14:22:50 +02:00
										 |  |  | 		return -EFAULT; | 
					
						
							| 
									
										
										
										
											2008-02-08 04:19:31 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (_NSIG_WORDS > 1) { | 
					
						
							|  |  |  | 		if (__copy_to_user(frame->extramask, &set->sig[1], | 
					
						
							|  |  |  | 				   sizeof(frame->extramask))) | 
					
						
							| 
									
										
										
										
											2013-10-07 14:22:50 +02:00
										 |  |  | 			return -EFAULT; | 
					
						
							| 
									
										
										
										
											2008-02-08 04:19:31 -08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* set up to return from userspace.  If provided, use a stub already in
 | 
					
						
							|  |  |  | 	 * userspace */ | 
					
						
							| 
									
										
										
										
											2013-10-07 14:22:50 +02:00
										 |  |  | 	if (ksig->ka.sa.sa_flags & SA_RESTORER) { | 
					
						
							|  |  |  | 		if (__put_user(ksig->ka.sa.sa_restorer, &frame->pretcode)) | 
					
						
							|  |  |  | 			return -EFAULT; | 
					
						
							| 
									
										
										
										
											2008-02-08 04:19:31 -08:00
										 |  |  | 	} else { | 
					
						
							|  |  |  | 		if (__put_user((void (*)(void))frame->retcode, | 
					
						
							|  |  |  | 			       &frame->pretcode)) | 
					
						
							| 
									
										
										
										
											2013-10-07 14:22:50 +02:00
										 |  |  | 			return -EFAULT; | 
					
						
							| 
									
										
										
										
											2008-02-08 04:19:31 -08:00
										 |  |  | 		/* this is mov $,d0; syscall 0 */ | 
					
						
							|  |  |  | 		if (__put_user(0x2c, (char *)(frame->retcode + 0)) || | 
					
						
							|  |  |  | 		    __put_user(__NR_sigreturn, (char *)(frame->retcode + 1)) || | 
					
						
							|  |  |  | 		    __put_user(0x00, (char *)(frame->retcode + 2)) || | 
					
						
							|  |  |  | 		    __put_user(0xf0, (char *)(frame->retcode + 3)) || | 
					
						
							|  |  |  | 		    __put_user(0xe0, (char *)(frame->retcode + 4))) | 
					
						
							| 
									
										
										
										
											2013-10-07 14:22:50 +02:00
										 |  |  | 			return -EFAULT; | 
					
						
							| 
									
										
										
										
											2008-02-08 04:19:31 -08:00
										 |  |  | 		flush_icache_range((unsigned long) frame->retcode, | 
					
						
							|  |  |  | 				   (unsigned long) frame->retcode + 5); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* set up registers for signal handler */ | 
					
						
							|  |  |  | 	regs->sp = (unsigned long) frame; | 
					
						
							| 
									
										
										
										
											2013-10-07 14:22:50 +02:00
										 |  |  | 	regs->pc = (unsigned long) ksig->ka.sa.sa_handler; | 
					
						
							| 
									
										
										
										
											2008-02-08 04:19:31 -08:00
										 |  |  | 	regs->d0 = sig; | 
					
						
							|  |  |  | 	regs->d1 = (unsigned long) &frame->sc; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #if DEBUG_SIG
 | 
					
						
							|  |  |  | 	printk(KERN_DEBUG "SIG deliver %d (%s:%d): sp=%p pc=%lx ra=%p\n", | 
					
						
							|  |  |  | 	       sig, current->comm, current->pid, frame, regs->pc, | 
					
						
							|  |  |  | 	       frame->pretcode); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * set up a realtime signal frame | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2013-10-07 14:22:50 +02:00
										 |  |  | static int setup_rt_frame(struct ksignal *ksig, sigset_t *set, | 
					
						
							|  |  |  | 			  struct pt_regs *regs) | 
					
						
							| 
									
										
										
										
											2008-02-08 04:19:31 -08:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct rt_sigframe __user *frame; | 
					
						
							| 
									
										
										
										
											2013-10-07 14:22:50 +02:00
										 |  |  | 	int rsig, sig = ksig->sig; | 
					
						
							| 
									
										
										
										
											2008-02-08 04:19:31 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-05 15:36:49 +01:00
										 |  |  | 	frame = get_sigframe(ksig, regs, sizeof(*frame)); | 
					
						
							| 
									
										
										
										
											2008-02-08 04:19:31 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) | 
					
						
							| 
									
										
										
										
											2013-10-07 14:22:50 +02:00
										 |  |  | 		return -EFAULT; | 
					
						
							| 
									
										
										
										
											2008-02-08 04:19:31 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	rsig = sig; | 
					
						
							|  |  |  | 	if (sig < 32 && | 
					
						
							|  |  |  | 	    current_thread_info()->exec_domain && | 
					
						
							|  |  |  | 	    current_thread_info()->exec_domain->signal_invmap) | 
					
						
							|  |  |  | 		rsig = current_thread_info()->exec_domain->signal_invmap[sig]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (__put_user(rsig, &frame->sig) || | 
					
						
							|  |  |  | 	    __put_user(&frame->info, &frame->pinfo) || | 
					
						
							|  |  |  | 	    __put_user(&frame->uc, &frame->puc) || | 
					
						
							| 
									
										
										
										
											2013-10-07 14:22:50 +02:00
										 |  |  | 	    copy_siginfo_to_user(&frame->info, &ksig->info)) | 
					
						
							|  |  |  | 		return -EFAULT; | 
					
						
							| 
									
										
										
										
											2008-02-08 04:19:31 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* create the ucontext.  */ | 
					
						
							|  |  |  | 	if (__put_user(0, &frame->uc.uc_flags) || | 
					
						
							|  |  |  | 	    __put_user(0, &frame->uc.uc_link) || | 
					
						
							| 
									
										
										
										
											2012-12-23 03:17:06 -05:00
										 |  |  | 	    __save_altstack(&frame->uc.uc_stack, regs->sp) || | 
					
						
							| 
									
										
										
										
											2008-02-08 04:19:31 -08:00
										 |  |  | 	    setup_sigcontext(&frame->uc.uc_mcontext, | 
					
						
							|  |  |  | 			     &frame->fpuctx, regs, set->sig[0]) || | 
					
						
							|  |  |  | 	    __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set))) | 
					
						
							| 
									
										
										
										
											2013-10-07 14:22:50 +02:00
										 |  |  | 		return -EFAULT; | 
					
						
							| 
									
										
										
										
											2008-02-08 04:19:31 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* set up to return from userspace.  If provided, use a stub already in
 | 
					
						
							|  |  |  | 	 * userspace */ | 
					
						
							| 
									
										
										
										
											2013-10-07 14:22:50 +02:00
										 |  |  | 	if (ksig->ka.sa.sa_flags & SA_RESTORER) { | 
					
						
							|  |  |  | 		if (__put_user(ksig->ka.sa.sa_restorer, &frame->pretcode)) | 
					
						
							|  |  |  | 			return -EFAULT; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-02-08 04:19:31 -08:00
										 |  |  | 	} else { | 
					
						
							|  |  |  | 		if (__put_user((void(*)(void))frame->retcode, | 
					
						
							|  |  |  | 			       &frame->pretcode) || | 
					
						
							|  |  |  | 		    /* This is mov $,d0; syscall 0 */ | 
					
						
							|  |  |  | 		    __put_user(0x2c, (char *)(frame->retcode + 0)) || | 
					
						
							|  |  |  | 		    __put_user(__NR_rt_sigreturn, | 
					
						
							|  |  |  | 			       (char *)(frame->retcode + 1)) || | 
					
						
							|  |  |  | 		    __put_user(0x00, (char *)(frame->retcode + 2)) || | 
					
						
							|  |  |  | 		    __put_user(0xf0, (char *)(frame->retcode + 3)) || | 
					
						
							|  |  |  | 		    __put_user(0xe0, (char *)(frame->retcode + 4))) | 
					
						
							| 
									
										
										
										
											2013-10-07 14:22:50 +02:00
										 |  |  | 			return -EFAULT; | 
					
						
							| 
									
										
										
										
											2008-02-08 04:19:31 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		flush_icache_range((u_long) frame->retcode, | 
					
						
							|  |  |  | 				   (u_long) frame->retcode + 5); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Set up registers for signal handler */ | 
					
						
							|  |  |  | 	regs->sp = (unsigned long) frame; | 
					
						
							| 
									
										
										
										
											2013-10-07 14:22:50 +02:00
										 |  |  | 	regs->pc = (unsigned long) ksig->ka.sa.sa_handler; | 
					
						
							| 
									
										
										
										
											2008-02-08 04:19:31 -08:00
										 |  |  | 	regs->d0 = sig; | 
					
						
							|  |  |  | 	regs->d1 = (long) &frame->info; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #if DEBUG_SIG
 | 
					
						
							|  |  |  | 	printk(KERN_DEBUG "SIG deliver %d (%s:%d): sp=%p pc=%lx ra=%p\n", | 
					
						
							|  |  |  | 	       sig, current->comm, current->pid, frame, regs->pc, | 
					
						
							|  |  |  | 	       frame->pretcode); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-09-26 19:28:42 +01:00
										 |  |  | static inline void stepback(struct pt_regs *regs) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	regs->pc -= 2; | 
					
						
							|  |  |  | 	regs->orig_d0 = -1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-02-08 04:19:31 -08:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * handle the actual delivery of a signal to userspace | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2013-10-07 14:22:50 +02:00
										 |  |  | static int handle_signal(struct ksignal *ksig, struct pt_regs *regs) | 
					
						
							| 
									
										
										
										
											2008-02-08 04:19:31 -08:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2012-05-02 09:59:21 -04:00
										 |  |  | 	sigset_t *oldset = sigmask_to_save(); | 
					
						
							| 
									
										
										
										
											2008-02-08 04:19:31 -08:00
										 |  |  | 	int ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Are we from a system call? */ | 
					
						
							|  |  |  | 	if (regs->orig_d0 >= 0) { | 
					
						
							|  |  |  | 		/* If so, check system call restarting.. */ | 
					
						
							|  |  |  | 		switch (regs->d0) { | 
					
						
							|  |  |  | 		case -ERESTART_RESTARTBLOCK: | 
					
						
							|  |  |  | 		case -ERESTARTNOHAND: | 
					
						
							|  |  |  | 			regs->d0 = -EINTR; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		case -ERESTARTSYS: | 
					
						
							| 
									
										
										
										
											2013-10-07 14:22:50 +02:00
										 |  |  | 			if (!(ksig->ka.sa.sa_flags & SA_RESTART)) { | 
					
						
							| 
									
										
										
										
											2008-02-08 04:19:31 -08:00
										 |  |  | 				regs->d0 = -EINTR; | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			/* fallthrough */ | 
					
						
							|  |  |  | 		case -ERESTARTNOINTR: | 
					
						
							|  |  |  | 			regs->d0 = regs->orig_d0; | 
					
						
							| 
									
										
										
										
											2010-09-26 19:28:42 +01:00
										 |  |  | 			stepback(regs); | 
					
						
							| 
									
										
										
										
											2008-02-08 04:19:31 -08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Set up the stack frame */ | 
					
						
							| 
									
										
										
										
											2013-10-07 14:22:50 +02:00
										 |  |  | 	if (ksig->ka.sa.sa_flags & SA_SIGINFO) | 
					
						
							|  |  |  | 		ret = setup_rt_frame(ksig, oldset, regs); | 
					
						
							| 
									
										
										
										
											2008-02-08 04:19:31 -08:00
										 |  |  | 	else | 
					
						
							| 
									
										
										
										
											2013-10-07 14:22:50 +02:00
										 |  |  | 		ret = setup_frame(ksig, oldset, regs); | 
					
						
							| 
									
										
										
										
											2008-02-08 04:19:31 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-07 14:22:50 +02:00
										 |  |  | 	signal_setup_done(ret, ksig, test_thread_flag(TIF_SINGLESTEP)); | 
					
						
							| 
									
										
										
										
											2012-07-17 15:47:54 -07:00
										 |  |  | 	return 0; | 
					
						
							| 
									
										
										
										
											2008-02-08 04:19:31 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * handle a potential signal | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static void do_signal(struct pt_regs *regs) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-10-07 14:22:50 +02:00
										 |  |  | 	struct ksignal ksig; | 
					
						
							| 
									
										
										
										
											2008-02-08 04:19:31 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-07 14:22:50 +02:00
										 |  |  | 	if (get_signal(&ksig)) { | 
					
						
							|  |  |  | 		handle_signal(&ksig, regs); | 
					
						
							| 
									
										
										
										
											2008-02-08 04:19:31 -08:00
										 |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* did we come from a system call? */ | 
					
						
							|  |  |  | 	if (regs->orig_d0 >= 0) { | 
					
						
							|  |  |  | 		/* restart the system call - no handlers present */ | 
					
						
							|  |  |  | 		switch (regs->d0) { | 
					
						
							|  |  |  | 		case -ERESTARTNOHAND: | 
					
						
							|  |  |  | 		case -ERESTARTSYS: | 
					
						
							|  |  |  | 		case -ERESTARTNOINTR: | 
					
						
							|  |  |  | 			regs->d0 = regs->orig_d0; | 
					
						
							| 
									
										
										
										
											2010-09-26 19:28:42 +01:00
										 |  |  | 			stepback(regs); | 
					
						
							| 
									
										
										
										
											2008-02-08 04:19:31 -08:00
										 |  |  | 			break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		case -ERESTART_RESTARTBLOCK: | 
					
						
							|  |  |  | 			regs->d0 = __NR_restart_syscall; | 
					
						
							| 
									
										
										
										
											2010-09-26 19:28:42 +01:00
										 |  |  | 			stepback(regs); | 
					
						
							| 
									
										
										
										
											2008-02-08 04:19:31 -08:00
										 |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* if there's no signal to deliver, we just put the saved sigmask
 | 
					
						
							|  |  |  | 	 * back */ | 
					
						
							| 
									
										
										
										
											2012-05-21 23:33:55 -04:00
										 |  |  | 	restore_saved_sigmask(); | 
					
						
							| 
									
										
										
										
											2008-02-08 04:19:31 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * notification of userspace execution resumption | 
					
						
							|  |  |  |  * - triggered by current->work.notify_resume | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | asmlinkage void do_notify_resume(struct pt_regs *regs, u32 thread_info_flags) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	/* Pending single-step? */ | 
					
						
							|  |  |  | 	if (thread_info_flags & _TIF_SINGLESTEP) { | 
					
						
							|  |  |  | #ifndef CONFIG_MN10300_USING_JTAG
 | 
					
						
							|  |  |  | 		regs->epsw |= EPSW_T; | 
					
						
							|  |  |  | 		clear_thread_flag(TIF_SINGLESTEP); | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  | 		BUG(); /* no h/w single-step if using JTAG unit */ | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* deal with pending signal delivery */ | 
					
						
							| 
									
										
										
										
											2012-05-23 15:28:58 -04:00
										 |  |  | 	if (thread_info_flags & _TIF_SIGPENDING) | 
					
						
							| 
									
										
										
										
											2008-02-08 04:19:31 -08:00
										 |  |  | 		do_signal(regs); | 
					
						
							| 
									
										
										
										
											2009-06-11 13:08:37 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (thread_info_flags & _TIF_NOTIFY_RESUME) { | 
					
						
							|  |  |  | 		clear_thread_flag(TIF_NOTIFY_RESUME); | 
					
						
							| 
									
										
										
										
											2010-10-27 17:29:01 +01:00
										 |  |  | 		tracehook_notify_resume(current_frame()); | 
					
						
							| 
									
										
										
										
											2009-06-11 13:08:37 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2008-02-08 04:19:31 -08:00
										 |  |  | } |