diff options
Diffstat (limited to 'arch/tile/kernel/signal.c')
| -rw-r--r-- | arch/tile/kernel/signal.c | 222 | 
1 files changed, 143 insertions, 79 deletions
diff --git a/arch/tile/kernel/signal.c b/arch/tile/kernel/signal.c index 687719d4abd..d1d026f0126 100644 --- a/arch/tile/kernel/signal.c +++ b/arch/tile/kernel/signal.c @@ -16,7 +16,6 @@  #include <linux/sched.h>  #include <linux/mm.h>  #include <linux/smp.h> -#include <linux/smp_lock.h>  #include <linux/kernel.h>  #include <linux/signal.h>  #include <linux/errno.h> @@ -34,26 +33,17 @@  #include <asm/ucontext.h>  #include <asm/sigframe.h>  #include <asm/syscalls.h> +#include <asm/vdso.h>  #include <arch/interrupts.h>  #define DEBUG_SIG 0 -#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) - - -SYSCALL_DEFINE3(sigaltstack, const stack_t __user *, uss, -		stack_t __user *, uoss, struct pt_regs *, regs) -{ -	return do_sigaltstack(uss, uoss, regs->sp); -} - -  /*   * Do a signal return; undo the signal stack.   */  int restore_sigcontext(struct pt_regs *regs, -		       struct sigcontext __user *sc, long *pr0) +		       struct sigcontext __user *sc)  {  	int err = 0;  	int i; @@ -76,39 +66,41 @@ int restore_sigcontext(struct pt_regs *regs,  	regs->faultnum = INT_SWINT_1_SIGRETURN; -	err |= __get_user(*pr0, &sc->gregs[0]);  	return err;  } -/* sigreturn() returns long since it restores r0 in the interrupted code. */ -SYSCALL_DEFINE1(rt_sigreturn, struct pt_regs *, regs) +void signal_fault(const char *type, struct pt_regs *regs, +		  void __user *frame, int sig)  { +	trace_unhandled_signal(type, regs, (unsigned long)frame, SIGSEGV); +	force_sigsegv(sig, current); +} + +/* The assembly shim for this function arranges to ignore the return value. */ +SYSCALL_DEFINE0(rt_sigreturn) +{ +	struct pt_regs *regs = current_pt_regs();  	struct rt_sigframe __user *frame =  		(struct rt_sigframe __user *)(regs->sp);  	sigset_t set; -	long r0;  	if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))  		goto badframe;  	if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))  		goto badframe; -	sigdelsetmask(&set, ~_BLOCKABLE); -	spin_lock_irq(¤t->sighand->siglock); -	current->blocked = set; -	recalc_sigpending(); -	spin_unlock_irq(¤t->sighand->siglock); +	set_current_blocked(&set); -	if (restore_sigcontext(regs, &frame->uc.uc_mcontext, &r0)) +	if (restore_sigcontext(regs, &frame->uc.uc_mcontext))  		goto badframe; -	if (do_sigaltstack(&frame->uc.uc_stack, NULL, regs->sp) == -EFAULT) +	if (restore_altstack(&frame->uc.uc_stack))  		goto badframe; -	return r0; +	return 0;  badframe: -	force_sig(SIGSEGV, current); +	signal_fault("bad sigreturn frame", regs, frame, 0);  	return 0;  } @@ -193,17 +185,13 @@ static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,  	err |= __clear_user(&frame->save_area, sizeof(frame->save_area));  	err |= __put_user(0, &frame->uc.uc_flags);  	err |= __put_user(NULL, &frame->uc.uc_link); -	err |= __put_user((void __user *)(current->sas_ss_sp), -			  &frame->uc.uc_stack.ss_sp); -	err |= __put_user(sas_ss_flags(regs->sp), -			  &frame->uc.uc_stack.ss_flags); -	err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size); +	err |= __save_altstack(&frame->uc.uc_stack, regs->sp);  	err |= setup_sigcontext(&frame->uc.uc_mcontext, regs);  	err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));  	if (err)  		goto give_sigsegv; -	restorer = VDSO_BASE; +	restorer = VDSO_SYM(&__vdso_rt_sigreturn);  	if (ka->sa.sa_flags & SA_RESTORER)  		restorer = (unsigned long) ka->sa.sa_restorer; @@ -222,19 +210,10 @@ static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,  	regs->regs[1] = (unsigned long) &frame->info;  	regs->regs[2] = (unsigned long) &frame->uc;  	regs->flags |= PT_FLAGS_CALLER_SAVES; - -	/* -	 * Notify any tracer that was single-stepping it. -	 * The tracer may want to single-step inside the -	 * handler too. -	 */ -	if (test_thread_flag(TIF_SINGLESTEP)) -		ptrace_notify(SIGTRAP); -  	return 0;  give_sigsegv: -	force_sigsegv(sig, current); +	signal_fault("bad setup frame", regs, frame, sig);  	return -EFAULT;  } @@ -242,13 +221,13 @@ give_sigsegv:   * OK, we're invoking a handler   */ -static int handle_signal(unsigned long sig, siginfo_t *info, -			 struct k_sigaction *ka, sigset_t *oldset, +static void handle_signal(unsigned long sig, siginfo_t *info, +			 struct k_sigaction *ka,  			 struct pt_regs *regs)  { +	sigset_t *oldset = sigmask_to_save();  	int ret; -  	/* Are we from a system call? */  	if (regs->faultnum == INT_SWINT_1) {  		/* If so, check system call restarting.. */ @@ -279,21 +258,10 @@ static int handle_signal(unsigned long sig, siginfo_t *info,  	else  #endif  		ret = setup_rt_frame(sig, ka, info, oldset, regs); -	if (ret == 0) { -		/* This code is only called from system calls or from -		 * the work_pending path in the return-to-user code, and -		 * either way we can re-enable interrupts unconditionally. -		 */ -		spin_lock_irq(¤t->sighand->siglock); -		sigorsets(¤t->blocked, -			  ¤t->blocked, &ka->sa.sa_mask); -		if (!(ka->sa.sa_flags & SA_NODEFER)) -			sigaddset(¤t->blocked, sig); -		recalc_sigpending(); -		spin_unlock_irq(¤t->sighand->siglock); -	} - -	return ret; +	if (ret) +		return; +	signal_delivered(sig, info, ka, regs, +			test_thread_flag(TIF_SINGLESTEP));  }  /* @@ -306,7 +274,6 @@ void do_signal(struct pt_regs *regs)  	siginfo_t info;  	int signr;  	struct k_sigaction ka; -	sigset_t *oldset;  	/*  	 * i386 will check if we're coming from kernel mode and bail out @@ -315,24 +282,10 @@ void do_signal(struct pt_regs *regs)  	 * helpful, we can reinstate the check on "!user_mode(regs)".  	 */ -	if (current_thread_info()->status & TS_RESTORE_SIGMASK) -		oldset = ¤t->saved_sigmask; -	else -		oldset = ¤t->blocked; -  	signr = get_signal_to_deliver(&info, &ka, regs, NULL);  	if (signr > 0) {  		/* Whee! Actually deliver the signal.  */ -		if (handle_signal(signr, &info, &ka, oldset, regs) == 0) { -			/* -			 * A signal was successfully delivered; the saved -			 * sigmask will have been stored in the signal frame, -			 * and will be restored by sigreturn, so we can simply -			 * clear the TS_RESTORE_SIGMASK flag. -			 */ -			current_thread_info()->status &= ~TS_RESTORE_SIGMASK; -		} - +		handle_signal(signr, &info, &ka, regs);  		goto done;  	} @@ -357,12 +310,123 @@ void do_signal(struct pt_regs *regs)  	}  	/* If there's no signal to deliver, just put the saved sigmask back. */ -	if (current_thread_info()->status & TS_RESTORE_SIGMASK) { -		current_thread_info()->status &= ~TS_RESTORE_SIGMASK; -		sigprocmask(SIG_SETMASK, ¤t->saved_sigmask, NULL); -	} +	restore_saved_sigmask();  done:  	/* Avoid double syscall restart if there are nested signals. */  	regs->faultnum = INT_SWINT_1_SIGRETURN;  } + +int show_unhandled_signals = 1; + +static int __init crashinfo(char *str) +{ +	const char *word; + +	if (*str == '\0') +		show_unhandled_signals = 2; +	else if (*str != '=' || kstrtoint(++str, 0, &show_unhandled_signals) != 0) +		return 0; + +	switch (show_unhandled_signals) { +	case 0: +		word = "No"; +		break; +	case 1: +		word = "One-line"; +		break; +	default: +		word = "Detailed"; +		break; +	} +	pr_info("%s crash reports will be generated on the console\n", word); +	return 1; +} +__setup("crashinfo", crashinfo); + +static void dump_mem(void __user *address) +{ +	void __user *addr; +	enum { region_size = 256, bytes_per_line = 16 }; +	int i, j, k; +	int found_readable_mem = 0; + +	pr_err("\n"); +	if (!access_ok(VERIFY_READ, address, 1)) { +		pr_err("Not dumping at address 0x%lx (kernel address)\n", +		       (unsigned long)address); +		return; +	} + +	addr = (void __user *) +		(((unsigned long)address & -bytes_per_line) - region_size/2); +	if (addr > address) +		addr = NULL; +	for (i = 0; i < region_size; +	     addr += bytes_per_line, i += bytes_per_line) { +		unsigned char buf[bytes_per_line]; +		char line[100]; +		if (copy_from_user(buf, addr, bytes_per_line)) +			continue; +		if (!found_readable_mem) { +			pr_err("Dumping memory around address 0x%lx:\n", +			       (unsigned long)address); +			found_readable_mem = 1; +		} +		j = sprintf(line, REGFMT":", (unsigned long)addr); +		for (k = 0; k < bytes_per_line; ++k) +			j += sprintf(&line[j], " %02x", buf[k]); +		pr_err("%s\n", line); +	} +	if (!found_readable_mem) +		pr_err("No readable memory around address 0x%lx\n", +		       (unsigned long)address); +} + +void trace_unhandled_signal(const char *type, struct pt_regs *regs, +			    unsigned long address, int sig) +{ +	struct task_struct *tsk = current; + +	if (show_unhandled_signals == 0) +		return; + +	/* If the signal is handled, don't show it here. */ +	if (!is_global_init(tsk)) { +		void __user *handler = +			tsk->sighand->action[sig-1].sa.sa_handler; +		if (handler != SIG_IGN && handler != SIG_DFL) +			return; +	} + +	/* Rate-limit the one-line output, not the detailed output. */ +	if (show_unhandled_signals <= 1 && !printk_ratelimit()) +		return; + +	printk("%s%s[%d]: %s at %lx pc "REGFMT" signal %d", +	       task_pid_nr(tsk) > 1 ? KERN_INFO : KERN_EMERG, +	       tsk->comm, task_pid_nr(tsk), type, address, regs->pc, sig); + +	print_vma_addr(KERN_CONT " in ", regs->pc); + +	printk(KERN_CONT "\n"); + +	if (show_unhandled_signals > 1) { +		switch (sig) { +		case SIGILL: +		case SIGFPE: +		case SIGSEGV: +		case SIGBUS: +			pr_err("User crash: signal %d," +			       " trap %ld, address 0x%lx\n", +			       sig, regs->faultnum, address); +			show_regs(regs); +			dump_mem((void __user *)address); +			break; +		default: +			pr_err("User crash: signal %d, trap %ld\n", +			       sig, regs->faultnum); +			break; +		} +	} +}  | 
