⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 signal.c

📁 linux 内核源代码
💻 C
字号:
/* * arch/xtensa/kernel/signal.c * * Default platform functions. * * This file is subject to the terms and conditions of the GNU General Public * License.  See the file "COPYING" in the main directory of this archive * for more details. * * Copyright (C) 2005, 2006 Tensilica Inc. * Copyright (C) 1991, 1992  Linus Torvalds * 1997-11-28  Modified for POSIX.1b signals by Richard Henderson * * Chris Zankel <chris@zankel.net> * Joe Taylor <joe@tensilica.com> */#include <linux/signal.h>#include <linux/errno.h>#include <linux/ptrace.h>#include <linux/personality.h>#include <linux/freezer.h>#include <asm/ucontext.h>#include <asm/uaccess.h>#include <asm/cacheflush.h>#include <asm/coprocessor.h>#include <asm/unistd.h>#define DEBUG_SIG  0#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))asmlinkage int do_signal(struct pt_regs *regs, sigset_t *oldset);extern struct task_struct *coproc_owners[];extern void release_all_cp (struct task_struct *);struct rt_sigframe{	struct siginfo info;	struct ucontext uc;	cp_state_t cpstate;	unsigned char retcode[6];	unsigned int window[4];};/*  * Flush register windows stored in pt_regs to stack. * Returns 1 for errors. * * Note that windowbase, windowstart, and wmask are not updated! */intflush_window_regs_user(struct pt_regs *regs){	const unsigned long ws = regs->windowstart;	const unsigned long wb = regs->windowbase;	unsigned long sp = 0;	unsigned long wm;	int err = 1;	int base;	/* Return if no other frames. */	if (regs->wmask == 1)		return 0;	/* Rotate windowmask and skip empty frames. */	wm = (ws >> wb) | (ws << (XCHAL_NUM_AREGS / 4 - wb));	base = (XCHAL_NUM_AREGS / 4) - (regs->wmask >> 4);			/* For call8 or call12 frames, we need the previous stack pointer. */	if ((regs->wmask & 2) == 0)		if (__get_user(sp, (int*)(regs->areg[base * 4 + 1] - 12)))			goto errout;	/* Spill frames to stack. */	while (base < XCHAL_NUM_AREGS / 4) {		int m = (wm >> base);		int inc = 0;		/* Save registers a4..a7 (call8) or a4...a11 (call12) */		if (m & 2) {			/* call4 */			inc = 1;		} else if (m & 4) {		/* call8 */			if (copy_to_user((void*)(sp - 32),					   &regs->areg[(base + 1) * 4], 16))				goto errout;			inc = 2;		} else if (m & 8) {	/* call12 */			if (copy_to_user((void*)(sp - 48),					   &regs->areg[(base + 1) * 4], 32))				goto errout;			inc = 3;		}		/* Save current frame a0..a3 under next SP */		sp = regs->areg[((base + inc) * 4 + 1) % XCHAL_NUM_AREGS];		if (copy_to_user((void*)(sp - 16), &regs->areg[base * 4], 16))			goto errout;		/* Get current stack pointer for next loop iteration. */		sp = regs->areg[base * 4 + 1];		base += inc;	}	return 0;errout:	return err;}/* * Note: We don't copy double exception 'regs', we have to finish double exc.  * first before we return to signal handler! This dbl.exc.handler might cause  * another double exception, but I think we are fine as the situation is the  * same as if we had returned to the signal handerl and got an interrupt  * immediately... */static intsetup_sigcontext(struct sigcontext __user *sc, cp_state_t *cpstate,		 struct pt_regs *regs, unsigned long mask){	int err = 0;#define COPY(x)	err |= __put_user(regs->x, &sc->sc_##x)	COPY(pc);	COPY(ps);	COPY(lbeg);	COPY(lend);	COPY(lcount);	COPY(sar);#undef COPY	err |= flush_window_regs_user(regs);	err |= __copy_to_user (sc->sc_a, regs->areg, 16 * 4);	// err |= __copy_to_user (sc->sc_a, regs->areg, XCHAL_NUM_AREGS * 4)#if XCHAL_HAVE_CP# error Coprocessors unsupported	err |= save_cpextra(cpstate);	err |= __put_user(err ? NULL : cpstate, &sc->sc_cpstate);#endif	/* non-iBCS2 extensions.. */	err |= __put_user(mask, &sc->oldmask);	return err;}static intrestore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc){	unsigned int err = 0;	unsigned long ps;#define COPY(x)	err |= __get_user(regs->x, &sc->sc_##x)	COPY(pc);	COPY(lbeg);	COPY(lend);	COPY(lcount);	COPY(sar);#undef COPY	/* All registers were flushed to stack. Start with a prestine frame. */	regs->wmask = 1;	regs->windowbase = 0;	regs->windowstart = 1;	/* For PS, restore only PS.CALLINC.	 * Assume that all other bits are either the same as for the signal	 * handler, or the user mode value doesn't matter (e.g. PS.OWB).	 */	err |= __get_user(ps, &sc->sc_ps);	regs->ps = (regs->ps & ~PS_CALLINC_MASK) | (ps & PS_CALLINC_MASK);	/* Additional corruption checks */	if ((regs->lcount > 0)	    && ((regs->lbeg > TASK_SIZE) || (regs->lend > TASK_SIZE)) )		err = 1;	err |= __copy_from_user(regs->areg, sc->sc_a, 16 * 4);#if XCHAL_HAVE_CP# error Coprocessors unsupported 	/* The signal handler may have used coprocessors in which	 * case they are still enabled.  We disable them to force a	 * reloading of the original task's CP state by the lazy	 * context-switching mechanisms of CP exception handling.	 * Also, we essentially discard any coprocessor state that the	 * signal handler created. */	if (!err) {	  struct task_struct *tsk = current;	  release_all_cp(tsk);	  err |= __copy_from_user(tsk->thread.cpextra, sc->sc_cpstate, 	      			  XTENSA_CP_EXTRA_SIZE);	}#endif	regs->syscall = -1;		/* disable syscall checks */	return err;}/* * Do a signal return; undo the signal stack. */asmlinkage long xtensa_rt_sigreturn(long a0, long a1, long a2, long a3,				    long a4, long a5, struct pt_regs *regs){	struct rt_sigframe __user *frame;	sigset_t set;	int ret;	if (regs->depc > 64)		panic("rt_sigreturn in double exception!\n");	frame = (struct rt_sigframe __user *) regs->areg[1];	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(&current->sighand->siglock);	current->blocked = set;	recalc_sigpending();	spin_unlock_irq(&current->sighand->siglock);	if (restore_sigcontext(regs, &frame->uc.uc_mcontext))		goto badframe;	ret = regs->areg[2];	if (do_sigaltstack(&frame->uc.uc_stack, NULL, regs->areg[1]) == -EFAULT)		goto badframe;	return ret;badframe:	force_sig(SIGSEGV, current);	return 0;}/* * Set up a signal frame. */static intgen_return_code(unsigned char *codemem){	int err = 0;	/*	 * The 12-bit immediate is really split up within the 24-bit MOVI	 * instruction.  As long as the above system call numbers fit within	 * 8-bits, the following code works fine. See the Xtensa ISA for	 * details.	 */#if __NR_rt_sigreturn > 255# error Generating the MOVI instruction below breaks!#endif#ifdef __XTENSA_EB__   /* Big Endian version */	/* Generate instruction:  MOVI a2, __NR_rt_sigreturn */	err |= __put_user(0x22, &codemem[0]);	err |= __put_user(0x0a, &codemem[1]);	err |= __put_user(__NR_rt_sigreturn, &codemem[2]);	/* Generate instruction:  SYSCALL */	err |= __put_user(0x00, &codemem[3]);	err |= __put_user(0x05, &codemem[4]);	err |= __put_user(0x00, &codemem[5]);#elif defined __XTENSA_EL__   /* Little Endian version */	/* Generate instruction:  MOVI a2, __NR_rt_sigreturn */	err |= __put_user(0x22, &codemem[0]);	err |= __put_user(0xa0, &codemem[1]);	err |= __put_user(__NR_rt_sigreturn, &codemem[2]);	/* Generate instruction:  SYSCALL */	err |= __put_user(0x00, &codemem[3]);	err |= __put_user(0x50, &codemem[4]);	err |= __put_user(0x00, &codemem[5]);#else# error Must use compiler for Xtensa processors.#endif	/* Flush generated code out of the data cache */	if (err == 0) {		__invalidate_icache_range((unsigned long)codemem, 6UL);		__flush_invalidate_dcache_range((unsigned long)codemem, 6UL);	}	return err;}static void setup_frame(int sig, struct k_sigaction *ka, siginfo_t *info,			sigset_t *set, struct pt_regs *regs){	struct rt_sigframe *frame;	int err = 0;	int signal;	unsigned long sp, ra;	sp = regs->areg[1];	if ((ka->sa.sa_flags & SA_ONSTACK) != 0 && ! on_sig_stack(sp)) {		sp = current->sas_ss_sp + current->sas_ss_size;	}	frame = (void *)((sp - sizeof(*frame)) & -16ul);	if (regs->depc > 64)		panic ("Double exception sys_sigreturn\n");	if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) {		goto give_sigsegv;	}	signal = current_thread_info()->exec_domain		&& current_thread_info()->exec_domain->signal_invmap		&& sig < 32		? current_thread_info()->exec_domain->signal_invmap[sig]		: sig;	if (ka->sa.sa_flags & SA_SIGINFO) {		err |= copy_siginfo_to_user(&frame->info, info);	}	/* Create the user context.  */	err |= __put_user(0, &frame->uc.uc_flags);	err |= __put_user(0, &frame->uc.uc_link);	err |= __put_user((void *)current->sas_ss_sp,			  &frame->uc.uc_stack.ss_sp);	err |= __put_user(sas_ss_flags(regs->areg[1]),			  &frame->uc.uc_stack.ss_flags);	err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size);	err |= setup_sigcontext(&frame->uc.uc_mcontext, &frame->cpstate,			        regs, set->sig[0]);	err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));	/* Create sys_rt_sigreturn syscall in stack frame */	err |= gen_return_code(frame->retcode);	if (err) {		goto give_sigsegv;	}			/* 	 * Create signal handler execution context.	 * Return context not modified until this point.	 */	/* Set up registers for signal handler */	start_thread(regs, (unsigned long) ka->sa.sa_handler, 		     (unsigned long) frame);	/* Set up a stack frame for a call4	 * Note: PS.CALLINC is set to one by start_thread	 */	ra = (unsigned long) frame->retcode;	regs->areg[4] = (((unsigned long) ra) & 0x3fffffff) | 0x40000000;	regs->areg[6] = (unsigned long) signal;	regs->areg[7] = (unsigned long) &frame->info;	regs->areg[8] = (unsigned long) &frame->uc;	/* Set access mode to USER_DS.  Nomenclature is outdated, but	 * functionality is used in uaccess.h	 */	set_fs(USER_DS);#if DEBUG_SIG	printk("SIG rt deliver (%s:%d): signal=%d sp=%p pc=%08x\n",		current->comm, current->pid, signal, frame, regs->pc);#endif	return;give_sigsegv:	if (sig == SIGSEGV)		ka->sa.sa_handler = SIG_DFL;	force_sig(SIGSEGV, current);}/* * Atomically swap in the new signal mask, and wait for a signal. */asmlinkage long xtensa_rt_sigsuspend(sigset_t __user *unewset,     				     size_t sigsetsize,    				     long a2, long a3, long a4, long a5, 				     struct pt_regs *regs){	sigset_t saveset, newset;	/* XXX: Don't preclude handling different sized sigset_t's.  */	if (sigsetsize != sizeof(sigset_t))		return -EINVAL;	if (copy_from_user(&newset, unewset, sizeof(newset)))		return -EFAULT;	sigdelsetmask(&newset, ~_BLOCKABLE);	spin_lock_irq(&current->sighand->siglock);	saveset = current->blocked;	current->blocked = newset;	recalc_sigpending();	spin_unlock_irq(&current->sighand->siglock);	regs->areg[2] = -EINTR;	while (1) {		current->state = TASK_INTERRUPTIBLE;		schedule();		if (do_signal(regs, &saveset))			return -EINTR;	}}asmlinkage long xtensa_sigaltstack(const stack_t __user *uss, 				   stack_t __user *uoss,    				   long a2, long a3, long a4, long a5,				   struct pt_regs *regs){	return do_sigaltstack(uss, uoss, regs->areg[1]);}/* * 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. */int do_signal(struct pt_regs *regs, sigset_t *oldset){	siginfo_t info;	int signr;	struct k_sigaction ka;	if (!user_mode(regs))		return 0;	if (try_to_freeze())		goto no_signal;	if (!oldset)		oldset = &current->blocked;	task_pt_regs(current)->icountlevel = 0;	signr = get_signal_to_deliver(&info, &ka, regs, NULL);	if (signr > 0) {		/* Are we from a system call? */		if ((signed)regs->syscall >= 0) {			/* If so, check system call restarting.. */			switch (regs->areg[2]) {				case -ERESTARTNOHAND:				case -ERESTART_RESTARTBLOCK:					regs->areg[2] = -EINTR;					break;				case -ERESTARTSYS:					if (!(ka.sa.sa_flags & SA_RESTART)) {						regs->areg[2] = -EINTR;						break;					}					/* fallthrough */				case -ERESTARTNOINTR:					regs->areg[2] = regs->syscall;					regs->pc -= 3;					break;				default:					/* nothing to do */					if (regs->areg[2] != 0)					break;			}		}		/* Whee!  Actually deliver the signal.  */		/* Set up the stack frame */		setup_frame(signr, &ka, &info, oldset, regs);		if (ka.sa.sa_flags & SA_ONESHOT)			ka.sa.sa_handler = SIG_DFL;		spin_lock_irq(&current->sighand->siglock);		sigorsets(&current->blocked, &current->blocked, &ka.sa.sa_mask);		if (!(ka.sa.sa_flags & SA_NODEFER))			sigaddset(&current->blocked, signr);		recalc_sigpending();		spin_unlock_irq(&current->sighand->siglock);		if (current->ptrace & PT_SINGLESTEP)			task_pt_regs(current)->icountlevel = 1;		return 1;	}no_signal:	/* Did we come from a system call? */	if ((signed) regs->syscall >= 0) {		/* Restart the system call - no handlers present */		switch (regs->areg[2]) {		case -ERESTARTNOHAND:		case -ERESTARTSYS:		case -ERESTARTNOINTR:			regs->areg[2] = regs->syscall;			regs->pc -= 3;			break;		case -ERESTART_RESTARTBLOCK:			regs->areg[2] = __NR_restart_syscall;			regs->pc -= 3;			break;		}	}	if (current->ptrace & PT_SINGLESTEP)		task_pt_regs(current)->icountlevel = 1;	return 0;}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -