📄 signal.c
字号:
/* * Copyright (C) 2003, Axis Communications AB. */#include <linux/sched.h>#include <linux/mm.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/syscalls.h>#include <linux/vmalloc.h>#include <asm/io.h>#include <asm/processor.h>#include <asm/ucontext.h>#include <asm/uaccess.h>#include <asm/arch/ptrace.h>#include <asm/arch/hwregs/cpu_vect.h>extern unsigned long cris_signal_return_page;/* Flag to check if a signal is blockable. */#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))/* * A syscall in CRIS is really a "break 13" instruction, which is 2 * bytes. The registers is manipulated so upon return the instruction * will be executed again. * * This relies on that PC points to the instruction after the break call. */#define RESTART_CRIS_SYS(regs) regs->r10 = regs->orig_r10; regs->erp -= 2;/* Signal frames. */struct signal_frame { struct sigcontext sc; unsigned long extramask[_NSIG_WORDS - 1]; unsigned char retcode[8]; /* Trampoline code. */};struct rt_signal_frame { struct siginfo *pinfo; void *puc; struct siginfo info; struct ucontext uc; unsigned char retcode[8]; /* Trampoline code. */};int do_signal(int restart, sigset_t *oldset, struct pt_regs *regs);void keep_debug_flags(unsigned long oldccs, unsigned long oldspc, struct pt_regs *regs);/* * Swap in the new signal mask, and wait for a signal. Define some * dummy arguments to be able to reach the regs argument. */intsys_sigsuspend(old_sigset_t mask, long r11, long r12, long r13, long mof, long srp, struct pt_regs *regs){ sigset_t saveset; mask &= _BLOCKABLE; spin_lock_irq(¤t->sighand->siglock); saveset = current->blocked; siginitset(¤t->blocked, mask); recalc_sigpending(); spin_unlock_irq(¤t->sighand->siglock); regs->r10 = -EINTR; while (1) { current->state = TASK_INTERRUPTIBLE; schedule(); if (do_signal(0, &saveset, regs)) { /* * This point is reached twice: once to call * the signal handler, then again to return * from the sigsuspend system call. When * calling the signal handler, R10 hold the * signal number as set by do_signal(). The * sigsuspend call will always return with * the restored value above; -EINTR. */ return regs->r10; } }}/* Define some dummy arguments to be able to reach the regs argument. */intsys_rt_sigsuspend(sigset_t *unewset, size_t sigsetsize, long r12, long r13, long mof, long srp, struct pt_regs *regs){ sigset_t saveset; sigset_t newset; if (sigsetsize != sizeof(sigset_t)) return -EINVAL; if (copy_from_user(&newset, unewset, sizeof(newset))) return -EFAULT; sigdelsetmask(&newset, ~_BLOCKABLE); spin_lock_irq(¤t->sighand->siglock); saveset = current->blocked; current->blocked = newset; recalc_sigpending(); spin_unlock_irq(¤t->sighand->siglock); regs->r10 = -EINTR; while (1) { current->state = TASK_INTERRUPTIBLE; schedule(); if (do_signal(0, &saveset, regs)) { /* See comment in function above. */ return regs->r10; } }}intsys_sigaction(int signal, const struct old_sigaction *act, struct old_sigaction *oact){ int retval; struct k_sigaction newk; struct k_sigaction oldk; if (act) { old_sigset_t mask; if (!access_ok(VERIFY_READ, act, sizeof(*act)) || __get_user(newk.sa.sa_handler, &act->sa_handler) || __get_user(newk.sa.sa_restorer, &act->sa_restorer)) return -EFAULT; __get_user(newk.sa.sa_flags, &act->sa_flags); __get_user(mask, &act->sa_mask); siginitset(&newk.sa.sa_mask, mask); } retval = do_sigaction(signal, act ? &newk : NULL, oact ? &oldk : NULL); if (!retval && oact) { if (!access_ok(VERIFY_WRITE, oact, sizeof(*oact)) || __put_user(oldk.sa.sa_handler, &oact->sa_handler) || __put_user(oldk.sa.sa_restorer, &oact->sa_restorer)) return -EFAULT; __put_user(oldk.sa.sa_flags, &oact->sa_flags); __put_user(oldk.sa.sa_mask.sig[0], &oact->sa_mask); } return retval;}intsys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss){ return do_sigaltstack(uss, uoss, rdusp());}static intrestore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc){ unsigned int err = 0; unsigned long old_usp; /* Always make any pending restarted system calls return -EINTR */ current_thread_info()->restart_block.fn = do_no_restart_syscall; /* * Restore the registers from &sc->regs. sc is already checked * for VERIFY_READ since the signal_frame was previously * checked in sys_sigreturn(). */ if (__copy_from_user(regs, sc, sizeof(struct pt_regs))) goto badframe; /* Make that the user-mode flag is set. */ regs->ccs |= (1 << (U_CCS_BITNR + CCS_SHIFT)); /* Restore the old USP. */ err |= __get_user(old_usp, &sc->usp); wrusp(old_usp); return err;badframe: return 1;}/* Define some dummy arguments to be able to reach the regs argument. */asmlinkage intsys_sigreturn(long r10, long r11, long r12, long r13, long mof, long srp, struct pt_regs *regs){ sigset_t set; struct signal_frame __user *frame; unsigned long oldspc = regs->spc; unsigned long oldccs = regs->ccs; frame = (struct signal_frame *) rdusp(); /* * Since the signal is stacked on a dword boundary, the frame * should be dword aligned here as well. It it's not, then the * user is trying some funny business. */ if (((long)frame) & 3) goto badframe; if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) goto badframe; if (__get_user(set.sig[0], &frame->sc.oldmask) || (_NSIG_WORDS > 1 && __copy_from_user(&set.sig[1], frame->extramask, sizeof(frame->extramask)))) goto badframe; sigdelsetmask(&set, ~_BLOCKABLE); spin_lock_irq(¤t->sighand->siglock); current->blocked = set; recalc_sigpending(); spin_unlock_irq(¤t->sighand->siglock); if (restore_sigcontext(regs, &frame->sc)) goto badframe; keep_debug_flags(oldccs, oldspc, regs); return regs->r10;badframe: force_sig(SIGSEGV, current); return 0;}/* Define some dummy variables to be able to reach the regs argument. */asmlinkage intsys_rt_sigreturn(long r10, long r11, long r12, long r13, long mof, long srp, struct pt_regs *regs){ sigset_t set; struct rt_signal_frame __user *frame; unsigned long oldspc = regs->spc; unsigned long oldccs = regs->ccs; frame = (struct rt_signal_frame *) rdusp(); /* * Since the signal is stacked on a dword boundary, the frame * should be dword aligned here as well. It it's not, then the * user is trying some funny business. */ if (((long)frame) & 3) goto badframe; 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); if (restore_sigcontext(regs, &frame->uc.uc_mcontext)) goto badframe; if (do_sigaltstack(&frame->uc.uc_stack, NULL, rdusp()) == -EFAULT) goto badframe; keep_debug_flags(oldccs, oldspc, regs); return regs->r10;badframe: force_sig(SIGSEGV, current); return 0;}/* Setup a signal frame. */static intsetup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs, unsigned long mask){ int err; unsigned long usp; err = 0; usp = rdusp(); /* * Copy the registers. They are located first in sc, so it's * possible to use sc directly. */ err |= __copy_to_user(sc, regs, sizeof(struct pt_regs)); err |= __put_user(mask, &sc->oldmask); err |= __put_user(usp, &sc->usp); return err;}/* Figure out where to put the new signal frame - usually on the stack. */static inline void __user *get_sigframe(struct k_sigaction *ka, struct pt_regs * regs, size_t frame_size){ unsigned long sp; sp = rdusp(); /* This is the X/Open sanctioned signal stack switching. */ if (ka->sa.sa_flags & SA_ONSTACK) { if (!on_sig_stack(sp)) sp = current->sas_ss_sp + current->sas_ss_size; } /* Make sure the frame is dword-aligned. */ sp &= ~3; return (void __user *)(sp - frame_size);}/* Grab and setup a signal frame. * * Basically a lot of state-info is stacked, and arranged for the * user-mode program to return to the kernel using either a trampiline * which performs the syscall sigreturn(), or a provided user-mode * trampoline. */static void
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -