📄 signal32.c
字号:
/* $Id: signal32.c,v 1.1.1.1 2004/02/04 12:55:51 laputa Exp $ * arch/sparc64/kernel/signal32.c * * Copyright (C) 1991, 1992 Linus Torvalds * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx) * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) */#include <linux/sched.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/mm.h>#include <linux/smp_lock.h>#include <asm/uaccess.h>#include <asm/bitops.h>#include <asm/ptrace.h>#include <asm/svr4.h>#include <asm/pgtable.h>#include <asm/psrcompat.h>#include <asm/fpumacro.h>#include <asm/visasm.h>#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))asmlinkage int do_signal32(sigset_t *oldset, struct pt_regs *regs, unsigned long orig_o0, int ret_from_syscall);/* This turned off for production... *//* #define DEBUG_SIGNALS 1 *//* #define DEBUG_SIGNALS_TRACE 1 *//* #define DEBUG_SIGNALS_MAPS 1 *//* #define DEBUG_SIGNALS_TLB 1 *//* Signal frames: the original one (compatible with SunOS): * * Set up a signal frame... Make the stack look the way SunOS * expects it to look which is basically: * * ---------------------------------- <-- %sp at signal time * Struct sigcontext * Signal address * Ptr to sigcontext area above * Signal code * The signal number itself * One register window * ---------------------------------- <-- New %sp */struct signal_sframe32 { struct reg_window32 sig_window; int sig_num; int sig_code; /* struct sigcontext32 * */ u32 sig_scptr; int sig_address; struct sigcontext32 sig_context; unsigned extramask[_NSIG_WORDS32 - 1];};/* * And the new one, intended to be used for Linux applications only * (we have enough in there to work with clone). * All the interesting bits are in the info field. */struct new_signal_frame32 { struct sparc_stackf32 ss; __siginfo32_t info; /* __siginfo_fpu32_t * */ u32 fpu_save; unsigned int insns [2]; unsigned extramask[_NSIG_WORDS32 - 1]; unsigned extra_size; /* Should be sizeof(siginfo_extra_v8plus_t) */ /* Only valid if (info.si_regs.psr & (PSR_VERS|PSR_IMPL)) == PSR_V8PLUS */ siginfo_extra_v8plus_t v8plus; __siginfo_fpu_t fpu_state;};struct rt_signal_frame32 { struct sparc_stackf32 ss; siginfo_t32 info; struct pt_regs32 regs; sigset_t32 mask; /* __siginfo_fpu32_t * */ u32 fpu_save; unsigned int insns [2]; stack_t32 stack; unsigned extra_size; /* Should be sizeof(siginfo_extra_v8plus_t) */ /* Only valid if (regs.psr & (PSR_VERS|PSR_IMPL)) == PSR_V8PLUS */ siginfo_extra_v8plus_t v8plus; __siginfo_fpu_t fpu_state;};/* Align macros */#define SF_ALIGNEDSZ (((sizeof(struct signal_sframe32) + 7) & (~7)))#define NF_ALIGNEDSZ (((sizeof(struct new_signal_frame32) + 7) & (~7)))#define RT_ALIGNEDSZ (((sizeof(struct rt_signal_frame32) + 7) & (~7)))int copy_siginfo_to_user32(siginfo_t32 *to, siginfo_t *from){ int err; if (!access_ok (VERIFY_WRITE, to, sizeof(siginfo_t32))) return -EFAULT; /* If you change siginfo_t structure, please be sure this code is fixed accordingly. It should never copy any pad contained in the structure to avoid security leaks, but must copy the generic 3 ints plus the relevant union member. This routine must convert siginfo from 64bit to 32bit as well at the same time. */ err = __put_user(from->si_signo, &to->si_signo); err |= __put_user(from->si_errno, &to->si_errno); err |= __put_user((short)from->si_code, &to->si_code); if (from->si_code < 0) err |= __copy_to_user(&to->_sifields._pad, &from->_sifields._pad, SI_PAD_SIZE); else { switch (from->si_code >> 16) { case __SI_CHLD >> 16: err |= __put_user(from->si_utime, &to->si_utime); err |= __put_user(from->si_stime, &to->si_stime); err |= __put_user(from->si_status, &to->si_status); default: err |= __put_user(from->si_pid, &to->si_pid); err |= __put_user(from->si_uid, &to->si_uid); break; case __SI_FAULT >> 16: case __SI_POLL >> 16: err |= __put_user(from->si_trapno, &to->si_trapno); err |= __put_user((long)from->si_addr, &to->si_addr); break; /* case __SI_RT: This is not generated by the kernel as of now. */ } } return err;}/* * atomically swap in the new signal mask, and wait for a signal. * This is really tricky on the Sparc, watch out... */asmlinkage void _sigpause32_common(old_sigset_t32 set, struct pt_regs *regs){ sigset_t saveset; set &= _BLOCKABLE; spin_lock_irq(¤t->sigmask_lock); saveset = current->blocked; siginitset(¤t->blocked, set); recalc_sigpending(current); spin_unlock_irq(¤t->sigmask_lock); regs->tpc = regs->tnpc; regs->tnpc += 4; if ((current->thread.flags & SPARC_FLAG_32BIT) != 0) { regs->tpc &= 0xffffffff; regs->tnpc &= 0xffffffff; } /* Condition codes and return value where set here for sigpause, * and so got used by setup_frame, which again causes sigreturn() * to return -EINTR. */ while (1) { current->state = TASK_INTERRUPTIBLE; schedule(); /* * Return -EINTR and set condition code here, * so the interrupted system call actually returns * these. */ regs->tstate |= TSTATE_ICARRY; regs->u_regs[UREG_I0] = EINTR; if (do_signal32(&saveset, regs, 0, 0)) return; }}asmlinkage void do_rt_sigsuspend32(u32 uset, size_t sigsetsize, struct pt_regs *regs){ sigset_t oldset, set; sigset_t32 set32; /* XXX: Don't preclude handling different sized sigset_t's. */ if (((__kernel_size_t32)sigsetsize) != sizeof(sigset_t)) { regs->tstate |= TSTATE_ICARRY; regs->u_regs[UREG_I0] = EINVAL; return; } if (copy_from_user(&set32, (void *)(long)uset, sizeof(set32))) { regs->tstate |= TSTATE_ICARRY; regs->u_regs[UREG_I0] = EFAULT; return; } switch (_NSIG_WORDS) { case 4: set.sig[3] = set32.sig[6] + (((long)set32.sig[7]) << 32); case 3: set.sig[2] = set32.sig[4] + (((long)set32.sig[5]) << 32); case 2: set.sig[1] = set32.sig[2] + (((long)set32.sig[3]) << 32); case 1: set.sig[0] = set32.sig[0] + (((long)set32.sig[1]) << 32); } sigdelsetmask(&set, ~_BLOCKABLE); spin_lock_irq(¤t->sigmask_lock); oldset = current->blocked; current->blocked = set; recalc_sigpending(current); spin_unlock_irq(¤t->sigmask_lock); regs->tpc = regs->tnpc; regs->tnpc += 4; if ((current->thread.flags & SPARC_FLAG_32BIT) != 0) { regs->tpc &= 0xffffffff; regs->tnpc &= 0xffffffff; } /* Condition codes and return value where set here for sigpause, * and so got used by setup_frame, which again causes sigreturn() * to return -EINTR. */ while (1) { current->state = TASK_INTERRUPTIBLE; schedule(); /* * Return -EINTR and set condition code here, * so the interrupted system call actually returns * these. */ regs->tstate |= TSTATE_ICARRY; regs->u_regs[UREG_I0] = EINTR; if (do_signal32(&oldset, regs, 0, 0)) return; }}static inline int restore_fpu_state32(struct pt_regs *regs, __siginfo_fpu_t *fpu){ unsigned long *fpregs = (unsigned long *)(((char *)current) + AOFF_task_fpregs); unsigned long fprs; int err; err = __get_user(fprs, &fpu->si_fprs); fprs_write(0); regs->tstate &= ~TSTATE_PEF; if (fprs & FPRS_DL) err |= copy_from_user(fpregs, &fpu->si_float_regs[0], (sizeof(unsigned int) * 32)); if (fprs & FPRS_DU) err |= copy_from_user(fpregs+16, &fpu->si_float_regs[32], (sizeof(unsigned int) * 32)); err |= __get_user(current->thread.xfsr[0], &fpu->si_fsr); err |= __get_user(current->thread.gsr[0], &fpu->si_gsr); current->thread.fpsaved[0] |= fprs; return err;}void do_new_sigreturn32(struct pt_regs *regs){ struct new_signal_frame32 *sf; unsigned int psr; unsigned pc, npc, fpu_save; sigset_t set; unsigned seta[_NSIG_WORDS32]; int err, i; regs->u_regs[UREG_FP] &= 0x00000000ffffffffUL; sf = (struct new_signal_frame32 *) regs->u_regs [UREG_FP]; /* 1. Make sure we are not getting garbage from the user */ if (verify_area (VERIFY_READ, sf, sizeof (*sf)) || (((unsigned long) sf) & 3)) goto segv; get_user(pc, &sf->info.si_regs.pc); __get_user(npc, &sf->info.si_regs.npc); if ((pc | npc) & 3) goto segv; if ((current->thread.flags & SPARC_FLAG_32BIT) != 0) { pc &= 0xffffffff; npc &= 0xffffffff; } regs->tpc = pc; regs->tnpc = npc; /* 2. Restore the state */ err = __get_user(regs->y, &sf->info.si_regs.y); err |= __get_user(psr, &sf->info.si_regs.psr); for (i = UREG_G1; i <= UREG_I7; i++) err |= __get_user(regs->u_regs[i], &sf->info.si_regs.u_regs[i]); if ((psr & (PSR_VERS|PSR_IMPL)) == PSR_V8PLUS) { err |= __get_user(i, &sf->v8plus.g_upper[0]); if (i == SIGINFO_EXTRA_V8PLUS_MAGIC) { for (i = UREG_G1; i <= UREG_I7; i++) err |= __get_user(((u32 *)regs->u_regs)[2*i], &sf->v8plus.g_upper[i]); } } /* User can only change condition codes in %tstate. */ regs->tstate &= ~(TSTATE_ICC|TSTATE_XCC); regs->tstate |= psr_to_tstate_icc(psr); err |= __get_user(fpu_save, &sf->fpu_save); if (fpu_save) err |= restore_fpu_state32(regs, &sf->fpu_state); err |= __get_user(seta[0], &sf->info.si_mask); err |= copy_from_user(seta+1, &sf->extramask, (_NSIG_WORDS32 - 1) * sizeof(unsigned)); if (err) goto segv; switch (_NSIG_WORDS) { case 4: set.sig[3] = seta[6] + (((long)seta[7]) << 32); case 3: set.sig[2] = seta[4] + (((long)seta[5]) << 32); case 2: set.sig[1] = seta[2] + (((long)seta[3]) << 32); case 1: set.sig[0] = seta[0] + (((long)seta[1]) << 32); } sigdelsetmask(&set, ~_BLOCKABLE); spin_lock_irq(¤t->sigmask_lock); current->blocked = set; recalc_sigpending(current); spin_unlock_irq(¤t->sigmask_lock); return;segv: do_exit(SIGSEGV);}asmlinkage void do_sigreturn32(struct pt_regs *regs){ struct sigcontext32 *scptr; unsigned pc, npc, psr; sigset_t set; unsigned seta[_NSIG_WORDS32]; int err; synchronize_user_stack(); if (current->thread.flags & SPARC_FLAG_NEWSIGNALS) return do_new_sigreturn32(regs); scptr = (struct sigcontext32 *) (regs->u_regs[UREG_I0] & 0x00000000ffffffffUL); /* Check sanity of the user arg. */ if(verify_area(VERIFY_READ, scptr, sizeof(struct sigcontext32)) || (((unsigned long) scptr) & 3)) goto segv; err = __get_user(pc, &scptr->sigc_pc); err |= __get_user(npc, &scptr->sigc_npc); if((pc | npc) & 3) goto segv; /* Nice try. */ err |= __get_user(seta[0], &scptr->sigc_mask); /* Note that scptr + 1 points to extramask */ err |= copy_from_user(seta+1, scptr + 1, (_NSIG_WORDS32 - 1) * sizeof(unsigned)); if (err) goto segv; switch (_NSIG_WORDS) { case 4: set.sig[3] = seta[6] + (((long)seta[7]) << 32); case 3: set.sig[2] = seta[4] + (((long)seta[5]) << 32); case 2: set.sig[1] = seta[2] + (((long)seta[3]) << 32); case 1: set.sig[0] = seta[0] + (((long)seta[1]) << 32); } sigdelsetmask(&set, ~_BLOCKABLE); spin_lock_irq(¤t->sigmask_lock); current->blocked = set; recalc_sigpending(current); spin_unlock_irq(¤t->sigmask_lock); if ((current->thread.flags & SPARC_FLAG_32BIT) != 0) { pc &= 0xffffffff; npc &= 0xffffffff; } regs->tpc = pc; regs->tnpc = npc; err = __get_user(regs->u_regs[UREG_FP], &scptr->sigc_sp); err |= __get_user(regs->u_regs[UREG_I0], &scptr->sigc_o0); err |= __get_user(regs->u_regs[UREG_G1], &scptr->sigc_g1); /* User can only change condition codes in %tstate. */ err |= __get_user(psr, &scptr->sigc_psr); if (err) goto segv; regs->tstate &= ~(TSTATE_ICC|TSTATE_XCC); regs->tstate |= psr_to_tstate_icc(psr); return;segv: do_exit(SIGSEGV);}asmlinkage void do_rt_sigreturn32(struct pt_regs *regs)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -