| 
									
										
										
										
											2012-03-05 11:49:31 +00:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Based on arch/arm/kernel/signal.c | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright (C) 1995-2009 Russell King | 
					
						
							|  |  |  |  * Copyright (C) 2012 ARM Ltd. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is free software; you can redistribute it and/or modify | 
					
						
							|  |  |  |  * it under the terms of the GNU General Public License version 2 as | 
					
						
							|  |  |  |  * published by the Free Software Foundation. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is distributed in the hope that it will be useful, | 
					
						
							|  |  |  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
					
						
							|  |  |  |  * GNU General Public License for more details. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * You should have received a copy of the GNU General Public License | 
					
						
							|  |  |  |  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <linux/errno.h>
 | 
					
						
							|  |  |  | #include <linux/signal.h>
 | 
					
						
							|  |  |  | #include <linux/personality.h>
 | 
					
						
							|  |  |  | #include <linux/freezer.h>
 | 
					
						
							|  |  |  | #include <linux/uaccess.h>
 | 
					
						
							|  |  |  | #include <linux/tracehook.h>
 | 
					
						
							|  |  |  | #include <linux/ratelimit.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <asm/compat.h>
 | 
					
						
							|  |  |  | #include <asm/debug-monitors.h>
 | 
					
						
							|  |  |  | #include <asm/elf.h>
 | 
					
						
							|  |  |  | #include <asm/cacheflush.h>
 | 
					
						
							|  |  |  | #include <asm/ucontext.h>
 | 
					
						
							|  |  |  | #include <asm/unistd.h>
 | 
					
						
							|  |  |  | #include <asm/fpsimd.h>
 | 
					
						
							|  |  |  | #include <asm/signal32.h>
 | 
					
						
							|  |  |  | #include <asm/vdso.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Do a signal return; undo the signal stack. These are aligned to 128-bit. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | struct rt_sigframe { | 
					
						
							|  |  |  | 	struct siginfo info; | 
					
						
							|  |  |  | 	struct ucontext uc; | 
					
						
							| 
									
										
										
										
											2012-11-23 12:34:13 +00:00
										 |  |  | 	u64 fp; | 
					
						
							|  |  |  | 	u64 lr; | 
					
						
							| 
									
										
										
										
											2012-03-05 11:49:31 +00:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int preserve_fpsimd_context(struct fpsimd_context __user *ctx) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct fpsimd_state *fpsimd = ¤t->thread.fpsimd_state; | 
					
						
							|  |  |  | 	int err; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* dump the hardware registers to the fpsimd_state structure */ | 
					
						
							|  |  |  | 	fpsimd_save_state(fpsimd); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* copy the FP and status/control registers */ | 
					
						
							|  |  |  | 	err = __copy_to_user(ctx->vregs, fpsimd->vregs, sizeof(fpsimd->vregs)); | 
					
						
							|  |  |  | 	__put_user_error(fpsimd->fpsr, &ctx->fpsr, err); | 
					
						
							|  |  |  | 	__put_user_error(fpsimd->fpcr, &ctx->fpcr, err); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* copy the magic/size information */ | 
					
						
							|  |  |  | 	__put_user_error(FPSIMD_MAGIC, &ctx->head.magic, err); | 
					
						
							|  |  |  | 	__put_user_error(sizeof(struct fpsimd_context), &ctx->head.size, err); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return err ? -EFAULT : 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int restore_fpsimd_context(struct fpsimd_context __user *ctx) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct fpsimd_state fpsimd; | 
					
						
							|  |  |  | 	__u32 magic, size; | 
					
						
							|  |  |  | 	int err = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* check the magic/size information */ | 
					
						
							|  |  |  | 	__get_user_error(magic, &ctx->head.magic, err); | 
					
						
							|  |  |  | 	__get_user_error(size, &ctx->head.size, err); | 
					
						
							|  |  |  | 	if (err) | 
					
						
							|  |  |  | 		return -EFAULT; | 
					
						
							|  |  |  | 	if (magic != FPSIMD_MAGIC || size != sizeof(struct fpsimd_context)) | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* copy the FP and status/control registers */ | 
					
						
							|  |  |  | 	err = __copy_from_user(fpsimd.vregs, ctx->vregs, | 
					
						
							|  |  |  | 			       sizeof(fpsimd.vregs)); | 
					
						
							|  |  |  | 	__get_user_error(fpsimd.fpsr, &ctx->fpsr, err); | 
					
						
							|  |  |  | 	__get_user_error(fpsimd.fpcr, &ctx->fpcr, err); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* load the hardware registers from the fpsimd_state structure */ | 
					
						
							|  |  |  | 	if (!err) { | 
					
						
							|  |  |  | 		preempt_disable(); | 
					
						
							|  |  |  | 		fpsimd_load_state(&fpsimd); | 
					
						
							|  |  |  | 		preempt_enable(); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return err ? -EFAULT : 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int restore_sigframe(struct pt_regs *regs, | 
					
						
							|  |  |  | 			    struct rt_sigframe __user *sf) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	sigset_t set; | 
					
						
							|  |  |  | 	int i, err; | 
					
						
							|  |  |  | 	struct aux_context __user *aux = | 
					
						
							|  |  |  | 		(struct aux_context __user *)sf->uc.uc_mcontext.__reserved; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	err = __copy_from_user(&set, &sf->uc.uc_sigmask, sizeof(set)); | 
					
						
							|  |  |  | 	if (err == 0) | 
					
						
							|  |  |  | 		set_current_blocked(&set); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (i = 0; i < 31; i++) | 
					
						
							|  |  |  | 		__get_user_error(regs->regs[i], &sf->uc.uc_mcontext.regs[i], | 
					
						
							|  |  |  | 				 err); | 
					
						
							|  |  |  | 	__get_user_error(regs->sp, &sf->uc.uc_mcontext.sp, err); | 
					
						
							|  |  |  | 	__get_user_error(regs->pc, &sf->uc.uc_mcontext.pc, err); | 
					
						
							|  |  |  | 	__get_user_error(regs->pstate, &sf->uc.uc_mcontext.pstate, err); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Avoid sys_rt_sigreturn() restarting. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	regs->syscallno = ~0UL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	err |= !valid_user_regs(®s->user_regs); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (err == 0) | 
					
						
							|  |  |  | 		err |= restore_fpsimd_context(&aux->fpsimd); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return err; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | asmlinkage long sys_rt_sigreturn(struct pt_regs *regs) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct rt_sigframe __user *frame; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Always make any pending restarted system calls return -EINTR */ | 
					
						
							|  |  |  | 	current_thread_info()->restart_block.fn = do_no_restart_syscall; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Since we stacked the signal on a 128-bit boundary, then 'sp' should | 
					
						
							|  |  |  | 	 * be word aligned here. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	if (regs->sp & 15) | 
					
						
							|  |  |  | 		goto badframe; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	frame = (struct rt_sigframe __user *)regs->sp; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!access_ok(VERIFY_READ, frame, sizeof (*frame))) | 
					
						
							|  |  |  | 		goto badframe; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (restore_sigframe(regs, frame)) | 
					
						
							|  |  |  | 		goto badframe; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-23 01:56:45 -05:00
										 |  |  | 	if (restore_altstack(&frame->uc.uc_stack)) | 
					
						
							| 
									
										
										
										
											2012-03-05 11:49:31 +00:00
										 |  |  | 		goto badframe; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return regs->regs[0]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | badframe: | 
					
						
							|  |  |  | 	if (show_unhandled_signals) | 
					
						
							|  |  |  | 		pr_info_ratelimited("%s[%d]: bad frame in %s: pc=%08llx sp=%08llx\n", | 
					
						
							|  |  |  | 				    current->comm, task_pid_nr(current), __func__, | 
					
						
							|  |  |  | 				    regs->pc, regs->sp); | 
					
						
							|  |  |  | 	force_sig(SIGSEGV, current); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int setup_sigframe(struct rt_sigframe __user *sf, | 
					
						
							|  |  |  | 			  struct pt_regs *regs, sigset_t *set) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int i, err = 0; | 
					
						
							|  |  |  | 	struct aux_context __user *aux = | 
					
						
							|  |  |  | 		(struct aux_context __user *)sf->uc.uc_mcontext.__reserved; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-11-23 12:34:13 +00:00
										 |  |  | 	/* set up the stack frame for unwinding */ | 
					
						
							|  |  |  | 	__put_user_error(regs->regs[29], &sf->fp, err); | 
					
						
							|  |  |  | 	__put_user_error(regs->regs[30], &sf->lr, err); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-03-05 11:49:31 +00:00
										 |  |  | 	for (i = 0; i < 31; i++) | 
					
						
							|  |  |  | 		__put_user_error(regs->regs[i], &sf->uc.uc_mcontext.regs[i], | 
					
						
							|  |  |  | 				 err); | 
					
						
							|  |  |  | 	__put_user_error(regs->sp, &sf->uc.uc_mcontext.sp, err); | 
					
						
							|  |  |  | 	__put_user_error(regs->pc, &sf->uc.uc_mcontext.pc, err); | 
					
						
							|  |  |  | 	__put_user_error(regs->pstate, &sf->uc.uc_mcontext.pstate, err); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	__put_user_error(current->thread.fault_address, &sf->uc.uc_mcontext.fault_address, err); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	err |= __copy_to_user(&sf->uc.uc_sigmask, set, sizeof(*set)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (err == 0) | 
					
						
							|  |  |  | 		err |= preserve_fpsimd_context(&aux->fpsimd); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* set the "end" magic */ | 
					
						
							|  |  |  | 	__put_user_error(0, &aux->end.magic, err); | 
					
						
							|  |  |  | 	__put_user_error(0, &aux->end.size, err); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return err; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-11-23 11:01:04 +00:00
										 |  |  | static struct rt_sigframe __user *get_sigframe(struct k_sigaction *ka, | 
					
						
							|  |  |  | 					       struct pt_regs *regs) | 
					
						
							| 
									
										
										
										
											2012-03-05 11:49:31 +00:00
										 |  |  | { | 
					
						
							|  |  |  | 	unsigned long sp, sp_top; | 
					
						
							| 
									
										
										
										
											2012-11-23 11:01:04 +00:00
										 |  |  | 	struct rt_sigframe __user *frame; | 
					
						
							| 
									
										
										
										
											2012-03-05 11:49:31 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	sp = sp_top = regs->sp; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * This is the X/Open sanctioned signal stack switching. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	if ((ka->sa.sa_flags & SA_ONSTACK) && !sas_ss_flags(sp)) | 
					
						
							|  |  |  | 		sp = sp_top = current->sas_ss_sp + current->sas_ss_size; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-11-23 11:01:04 +00:00
										 |  |  | 	sp = (sp - sizeof(struct rt_sigframe)) & ~15; | 
					
						
							|  |  |  | 	frame = (struct rt_sigframe __user *)sp; | 
					
						
							| 
									
										
										
										
											2012-03-05 11:49:31 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Check that we can actually write to the signal frame. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	if (!access_ok(VERIFY_WRITE, frame, sp_top - sp)) | 
					
						
							|  |  |  | 		frame = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return frame; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-11-23 12:34:13 +00:00
										 |  |  | static void setup_return(struct pt_regs *regs, struct k_sigaction *ka, | 
					
						
							|  |  |  | 			 void __user *frame, int usig) | 
					
						
							| 
									
										
										
										
											2012-03-05 11:49:31 +00:00
										 |  |  | { | 
					
						
							|  |  |  | 	__sigrestore_t sigtramp; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	regs->regs[0] = usig; | 
					
						
							|  |  |  | 	regs->sp = (unsigned long)frame; | 
					
						
							| 
									
										
										
										
											2012-11-23 12:34:13 +00:00
										 |  |  | 	regs->regs[29] = regs->sp + offsetof(struct rt_sigframe, fp); | 
					
						
							| 
									
										
										
										
											2012-03-05 11:49:31 +00:00
										 |  |  | 	regs->pc = (unsigned long)ka->sa.sa_handler; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (ka->sa.sa_flags & SA_RESTORER) | 
					
						
							|  |  |  | 		sigtramp = ka->sa.sa_restorer; | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		sigtramp = VDSO_SYMBOL(current->mm->context.vdso, sigtramp); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	regs->regs[30] = (unsigned long)sigtramp; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int setup_rt_frame(int usig, struct k_sigaction *ka, siginfo_t *info, | 
					
						
							|  |  |  | 			  sigset_t *set, struct pt_regs *regs) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct rt_sigframe __user *frame; | 
					
						
							|  |  |  | 	int err = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-11-23 11:01:04 +00:00
										 |  |  | 	frame = get_sigframe(ka, regs); | 
					
						
							| 
									
										
										
										
											2012-03-05 11:49:31 +00:00
										 |  |  | 	if (!frame) | 
					
						
							|  |  |  | 		return 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	__put_user_error(0, &frame->uc.uc_flags, err); | 
					
						
							|  |  |  | 	__put_user_error(NULL, &frame->uc.uc_link, err); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-23 01:56:45 -05:00
										 |  |  | 	err |= __save_altstack(&frame->uc.uc_stack, regs->sp); | 
					
						
							| 
									
										
										
										
											2012-03-05 11:49:31 +00:00
										 |  |  | 	err |= setup_sigframe(frame, regs, set); | 
					
						
							| 
									
										
										
										
											2012-11-23 12:34:13 +00:00
										 |  |  | 	if (err == 0) { | 
					
						
							|  |  |  | 		setup_return(regs, ka, frame, usig); | 
					
						
							|  |  |  | 		if (ka->sa.sa_flags & SA_SIGINFO) { | 
					
						
							|  |  |  | 			err |= copy_siginfo_to_user(&frame->info, info); | 
					
						
							|  |  |  | 			regs->regs[1] = (unsigned long)&frame->info; | 
					
						
							|  |  |  | 			regs->regs[2] = (unsigned long)&frame->uc; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2012-03-05 11:49:31 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return err; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void setup_restart_syscall(struct pt_regs *regs) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if (is_compat_task()) | 
					
						
							|  |  |  | 		compat_setup_restart_syscall(regs); | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		regs->regs[8] = __NR_restart_syscall; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * OK, we're invoking a handler | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static void handle_signal(unsigned long sig, struct k_sigaction *ka, | 
					
						
							|  |  |  | 			  siginfo_t *info, struct pt_regs *regs) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct thread_info *thread = current_thread_info(); | 
					
						
							|  |  |  | 	struct task_struct *tsk = current; | 
					
						
							|  |  |  | 	sigset_t *oldset = sigmask_to_save(); | 
					
						
							|  |  |  | 	int usig = sig; | 
					
						
							|  |  |  | 	int ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * translate the signal | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	if (usig < 32 && thread->exec_domain && thread->exec_domain->signal_invmap) | 
					
						
							|  |  |  | 		usig = thread->exec_domain->signal_invmap[usig]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Set up the stack frame | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	if (is_compat_task()) { | 
					
						
							|  |  |  | 		if (ka->sa.sa_flags & SA_SIGINFO) | 
					
						
							|  |  |  | 			ret = compat_setup_rt_frame(usig, ka, info, oldset, | 
					
						
							|  |  |  | 						    regs); | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 			ret = compat_setup_frame(usig, ka, oldset, regs); | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		ret = setup_rt_frame(usig, ka, info, oldset, regs); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Check that the resulting registers are actually sane. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	ret |= !valid_user_regs(®s->user_regs); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (ret != 0) { | 
					
						
							|  |  |  | 		force_sigsegv(sig, tsk); | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Fast forward the stepping logic so we step into the signal | 
					
						
							|  |  |  | 	 * handler. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	user_fastforward_single_step(tsk); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	signal_delivered(sig, info, ka, regs, 0); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Note that 'init' is a special process: it doesn't get signals it doesn't | 
					
						
							|  |  |  |  * want to handle. Thus you cannot kill init even with a SIGKILL even by | 
					
						
							|  |  |  |  * mistake. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Note that we go through the signals twice: once to check the signals that | 
					
						
							|  |  |  |  * the kernel can handle, and then we build all the user-level signal handling | 
					
						
							|  |  |  |  * stack-frames in one go after that. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static void do_signal(struct pt_regs *regs) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	unsigned long continue_addr = 0, restart_addr = 0; | 
					
						
							|  |  |  | 	struct k_sigaction ka; | 
					
						
							|  |  |  | 	siginfo_t info; | 
					
						
							|  |  |  | 	int signr, retval = 0; | 
					
						
							|  |  |  | 	int syscall = (int)regs->syscallno; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * If we were from a system call, check for system call restarting... | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	if (syscall >= 0) { | 
					
						
							|  |  |  | 		continue_addr = regs->pc; | 
					
						
							|  |  |  | 		restart_addr = continue_addr - (compat_thumb_mode(regs) ? 2 : 4); | 
					
						
							|  |  |  | 		retval = regs->regs[0]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * Avoid additional syscall restarting via ret_to_user. | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		regs->syscallno = ~0UL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * Prepare for system call restart. We do this here so that a | 
					
						
							|  |  |  | 		 * debugger will see the already changed PC. | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		switch (retval) { | 
					
						
							|  |  |  | 		case -ERESTARTNOHAND: | 
					
						
							|  |  |  | 		case -ERESTARTSYS: | 
					
						
							|  |  |  | 		case -ERESTARTNOINTR: | 
					
						
							|  |  |  | 		case -ERESTART_RESTARTBLOCK: | 
					
						
							|  |  |  | 			regs->regs[0] = regs->orig_x0; | 
					
						
							|  |  |  | 			regs->pc = restart_addr; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Get the signal to deliver. When running under ptrace, at this point | 
					
						
							|  |  |  | 	 * the debugger may change all of our registers. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	signr = get_signal_to_deliver(&info, &ka, regs, NULL); | 
					
						
							|  |  |  | 	if (signr > 0) { | 
					
						
							|  |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * Depending on the signal settings, we may need to revert the | 
					
						
							|  |  |  | 		 * decision to restart the system call, but skip this if a | 
					
						
							|  |  |  | 		 * debugger has chosen to restart at a different PC. | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		if (regs->pc == restart_addr && | 
					
						
							|  |  |  | 		    (retval == -ERESTARTNOHAND || | 
					
						
							|  |  |  | 		     retval == -ERESTART_RESTARTBLOCK || | 
					
						
							|  |  |  | 		     (retval == -ERESTARTSYS && | 
					
						
							|  |  |  | 		      !(ka.sa.sa_flags & SA_RESTART)))) { | 
					
						
							|  |  |  | 			regs->regs[0] = -EINTR; | 
					
						
							|  |  |  | 			regs->pc = continue_addr; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		handle_signal(signr, &ka, &info, regs); | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Handle restarting a different system call. As above, if a debugger | 
					
						
							|  |  |  | 	 * has chosen to restart at a different PC, ignore the restart. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	if (syscall >= 0 && regs->pc == restart_addr) { | 
					
						
							|  |  |  | 		if (retval == -ERESTART_RESTARTBLOCK) | 
					
						
							|  |  |  | 			setup_restart_syscall(regs); | 
					
						
							|  |  |  | 		user_rewind_single_step(current); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	restore_saved_sigmask(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | asmlinkage void do_notify_resume(struct pt_regs *regs, | 
					
						
							|  |  |  | 				 unsigned int thread_flags) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if (thread_flags & _TIF_SIGPENDING) | 
					
						
							|  |  |  | 		do_signal(regs); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (thread_flags & _TIF_NOTIFY_RESUME) { | 
					
						
							|  |  |  | 		clear_thread_flag(TIF_NOTIFY_RESUME); | 
					
						
							|  |  |  | 		tracehook_notify_resume(regs); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } |