/* * linux/arch/alpha/kernel/signal.c * * Copyright (C) 1995 Linus Torvalds */ #include #include #include #include #include #include #include #include #include #include #include #include #define _S(nr) (1<<((nr)-1)) #define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP))) asmlinkage int sys_wait4(int, int *, int, struct rusage *); asmlinkage void ret_from_sys_call(void); asmlinkage int do_signal(unsigned long, struct pt_regs *, struct switch_stack *, unsigned long, unsigned long); extern int ptrace_set_bpt (struct task_struct *child); extern int ptrace_cancel_bpt (struct task_struct *child); /* * The OSF/1 sigprocmask calling sequence is different from the * C sigprocmask() sequence.. * * how: * 1 - SIG_BLOCK * 2 - SIG_UNBLOCK * 3 - SIG_SETMASK * * We change the range to -1 .. 1 in order to let gcc easily * use the conditional move instructions. * * Note that we don't need to acquire the kernel lock for SMP * operation, as all of this is local to this thread. */ asmlinkage unsigned long osf_sigprocmask(int how, unsigned long newmask, long a2, long a3, long a4, long a5, struct pt_regs regs) { unsigned long ok, oldmask; struct task_struct * tsk; ok = how-1; /* 0 .. 2 */ tsk = current; ok = ok <= 2; oldmask = -EINVAL; if (ok) { long sign; /* -1 .. 1 */ unsigned long block, unblock; oldmask = tsk->blocked; newmask &= _BLOCKABLE; sign = how-2; unblock = oldmask & ~newmask; block = oldmask | newmask; if (!sign) block = unblock; regs.r0 = 0; /* special no error return */ if (sign <= 0) newmask = block; tsk->blocked = newmask; } return oldmask; } /* * atomically swap in the new signal mask, and wait for a signal. */ asmlinkage int do_sigsuspend(unsigned long mask, struct pt_regs * regs, struct switch_stack * sw) { unsigned long oldmask; spin_lock_irq(¤t->sigmask_lock); oldmask = current->blocked; current->blocked = mask & _BLOCKABLE; spin_unlock_irq(¤t->sigmask_lock); while (1) { current->state = TASK_INTERRUPTIBLE; schedule(); if (do_signal(oldmask, regs, sw, 0, 0)) return -EINTR; } } /* * Do a signal return; undo the signal stack. */ asmlinkage void do_sigreturn(struct sigcontext * sc, struct pt_regs * regs, struct switch_stack * sw) { unsigned long mask, ps, usp; int i; /* verify that it's a good sigcontext before using it */ if (verify_area(VERIFY_READ, sc, sizeof(*sc))) goto give_sigsegv; if (__get_user(ps, &sc->sc_ps) || ps != 8) goto give_sigsegv; if (__get_user(mask, &sc->sc_mask) || (mask & ~_BLOCKABLE)) goto give_sigsegv; /* ok, looks fine, start restoring */ __get_user(usp, sc->sc_regs+30); wrusp(usp); __get_user(regs->pc, &sc->sc_pc); sw->r26 = (unsigned long) ret_from_sys_call; current->blocked = mask; __get_user(regs->r0, sc->sc_regs+0); __get_user(regs->r1, sc->sc_regs+1); __get_user(regs->r2, sc->sc_regs+2); __get_user(regs->r3, sc->sc_regs+3); __get_user(regs->r4, sc->sc_regs+4); __get_user(regs->r5, sc->sc_regs+5); __get_user(regs->r6, sc->sc_regs+6); __get_user(regs->r7, sc->sc_regs+7); __get_user(regs->r8, sc->sc_regs+8); __get_user(sw->r9, sc->sc_regs+9); __get_user(sw->r10, sc->sc_regs+10); __get_user(sw->r11, sc->sc_regs+11); __get_user(sw->r12, sc->sc_regs+12); __get_user(sw->r13, sc->sc_regs+13); __get_user(sw->r14, sc->sc_regs+14); __get_user(sw->r15, sc->sc_regs+15); __get_user(regs->r16, sc->sc_regs+16); __get_user(regs->r17, sc->sc_regs+17); __get_user(regs->r18, sc->sc_regs+18); __get_user(regs->r19, sc->sc_regs+19); __get_user(regs->r20, sc->sc_regs+20); __get_user(regs->r21, sc->sc_regs+21); __get_user(regs->r22, sc->sc_regs+22); __get_user(regs->r23, sc->sc_regs+23); __get_user(regs->r24, sc->sc_regs+24); __get_user(regs->r25, sc->sc_regs+25); __get_user(regs->r26, sc->sc_regs+26); __get_user(regs->r27, sc->sc_regs+27); __get_user(regs->r28, sc->sc_regs+28); __get_user(regs->gp, sc->sc_regs+29); for (i = 0; i < 31; i++) __get_user(sw->fp[i], sc->sc_fpregs+i); /* send SIGTRAP if we're single-stepping: */ lock_kernel(); if (ptrace_cancel_bpt (current)) send_sig(SIGTRAP, current, 1); unlock_kernel(); return; give_sigsegv: lock_kernel(); do_exit(SIGSEGV); unlock_kernel(); } /* * Set up a signal frame... */ static void setup_frame(struct sigaction * sa, struct pt_regs * regs, struct switch_stack * sw, int signr, unsigned long oldmask) { int i; unsigned long oldsp; struct sigcontext * sc; oldsp = rdusp(); sc = ((struct sigcontext *) oldsp) - 1; /* check here if we would need to switch stacks.. */ if (verify_area(VERIFY_WRITE, sc, sizeof(*sc))) do_exit(SIGSEGV); wrusp((unsigned long) sc); __put_user(oldmask, &sc->sc_mask); __put_user(8, &sc->sc_ps); __put_user(regs->pc, &sc->sc_pc); __put_user(oldsp, sc->sc_regs+30); __put_user(regs->r0 , sc->sc_regs+0); __put_user(regs->r1 , sc->sc_regs+1); __put_user(regs->r2 , sc->sc_regs+2); __put_user(regs->r3 , sc->sc_regs+3); __put_user(regs->r4 , sc->sc_regs+4); __put_user(regs->r5 , sc->sc_regs+5); __put_user(regs->r6 , sc->sc_regs+6); __put_user(regs->r7 , sc->sc_regs+7); __put_user(regs->r8 , sc->sc_regs+8); __put_user(sw->r9 , sc->sc_regs+9); __put_user(sw->r10 , sc->sc_regs+10); __put_user(sw->r11 , sc->sc_regs+11); __put_user(sw->r12 , sc->sc_regs+12); __put_user(sw->r13 , sc->sc_regs+13); __put_user(sw->r14 , sc->sc_regs+14); __put_user(sw->r15 , sc->sc_regs+15); __put_user(regs->r16, sc->sc_regs+16); __put_user(regs->r17, sc->sc_regs+17); __put_user(regs->r18, sc->sc_regs+18); __put_user(regs->r19, sc->sc_regs+19); __put_user(regs->r20, sc->sc_regs+20); __put_user(regs->r21, sc->sc_regs+21); __put_user(regs->r22, sc->sc_regs+22); __put_user(regs->r23, sc->sc_regs+23); __put_user(regs->r24, sc->sc_regs+24); __put_user(regs->r25, sc->sc_regs+25); __put_user(regs->r26, sc->sc_regs+26); __put_user(regs->r27, sc->sc_regs+27); __put_user(regs->r28, sc->sc_regs+28); __put_user(regs->gp , sc->sc_regs+29); for (i = 0; i < 31; i++) __put_user(sw->fp[i], sc->sc_fpregs+i); __put_user(regs->trap_a0, &sc->sc_traparg_a0); __put_user(regs->trap_a1, &sc->sc_traparg_a1); __put_user(regs->trap_a2, &sc->sc_traparg_a2); /* * The following is: * * bis $30,$30,$16 * addq $31,0x67,$0 * call_pal callsys * * ie, "sigreturn(stack-pointer)" */ __put_user(0x43ecf40047de0410, sc->sc_retcode+0); __put_user(0x0000000000000083, sc->sc_retcode+1); imb(); /* "return" to the handler */ regs->r27 = regs->pc = (unsigned long) sa->sa_handler; regs->r26 = (unsigned long) sc->sc_retcode; regs->r16 = signr; /* a0: signal number */ regs->r17 = 0; /* a1: exception code; see gentrap.h */ regs->r18 = (unsigned long) sc; /* a2: sigcontext pointer */ } /* * OK, we're invoking a handler */ static inline void handle_signal(unsigned long signr, struct sigaction *sa, unsigned long oldmask, struct pt_regs * regs, struct switch_stack *sw) { setup_frame(sa,regs,sw,signr,oldmask); if (sa->sa_flags & SA_ONESHOT) sa->sa_handler = NULL; if (!(sa->sa_flags & SA_NOMASK)) current->blocked |= (sa->sa_mask | _S(signr)) & _BLOCKABLE; } static inline void syscall_restart(unsigned long r0, unsigned long r19, struct pt_regs * regs, struct sigaction * sa) { switch (regs->r0) { case ERESTARTNOHAND: no_system_call_restart: regs->r0 = EINTR; break; case ERESTARTSYS: if (!(sa->sa_flags & SA_RESTART)) goto no_system_call_restart; /* fallthrough */ case ERESTARTNOINTR: regs->r0 = r0; /* reset v0 and a3 and replay syscall */ regs->r19 = r19; regs->pc -= 4; } } /* * 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. * * "r0" and "r19" are the registers we need to restore for system call * restart. "r0" is also used as an indicator whether we can restart at * all (if we get here from anything but a syscall return, it will be 0) */ asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs, struct switch_stack * sw, unsigned long r0, unsigned long r19) { unsigned long mask; unsigned long signr, single_stepping; struct sigaction * sa; int ret; lock_kernel(); mask = ~current->blocked; single_stepping = ptrace_cancel_bpt(current); while ((signr = current->signal & mask) != 0) { signr = ffz(~signr); clear_bit(signr, ¤t->signal); sa = current->sig->action + signr; signr++; if ((current->flags & PF_PTRACED) && signr != SIGKILL) { current->exit_code = signr; current->state = TASK_STOPPED; notify_parent(current, SIGCHLD); schedule(); single_stepping |= ptrace_cancel_bpt(current); if (!(signr = current->exit_code)) continue; current->exit_code = 0; if (signr == SIGSTOP) continue; if (_S(signr) & current->blocked) { current->signal |= _S(signr); continue; } sa = current->sig->action + signr - 1; } if (sa->sa_handler == SIG_IGN) { if (signr != SIGCHLD) continue; /* check for SIGCHLD: it's special */ while (sys_wait4(-1, NULL, WNOHANG, NULL) > 0) /* nothing */; continue; } if (sa->sa_handler == SIG_DFL) { if (current->pid == 1) continue; switch (signr) { case SIGCONT: case SIGCHLD: case SIGWINCH: continue; case SIGTSTP: case SIGTTIN: case SIGTTOU: if (is_orphaned_pgrp(current->pgrp)) continue; case SIGSTOP: if (current->flags & PF_PTRACED) continue; current->state = TASK_STOPPED; current->exit_code = signr; if (!(current->p_pptr->sig->action[SIGCHLD-1].sa_flags & SA_NOCLDSTOP)) notify_parent(current, SIGCHLD); schedule(); single_stepping |= ptrace_cancel_bpt(current); continue; case SIGQUIT: case SIGILL: case SIGTRAP: case SIGABRT: case SIGFPE: case SIGSEGV: if (current->binfmt && current->binfmt->core_dump) { if (current->binfmt->core_dump(signr, regs)) signr |= 0x80; } /* fall through */ default: current->signal |= _S(signr & 0x7f); current->flags |= PF_SIGNALED; do_exit(signr); } } if (r0) syscall_restart(r0, r19, regs, sa); handle_signal(signr, sa, oldmask, regs, sw); if (single_stepping) { ptrace_set_bpt(current); /* re-set breakpoint */ } ret = 1; goto out; } if (r0 && (regs->r0 == ERESTARTNOHAND || regs->r0 == ERESTARTSYS || regs->r0 == ERESTARTNOINTR)) { regs->r0 = r0; /* reset v0 and a3 and replay syscall */ regs->r19 = r19; regs->pc -= 4; } if (single_stepping) { ptrace_set_bpt(current); /* re-set breakpoint */ } ret = 0; out: unlock_kernel(); return ret; }