/* * linux/arch/alpha/kernel/signal.c * * Copyright (C) 1995 Linus Torvalds */ #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_waitpid(pid_t pid,unsigned long * stat_addr, int options); asmlinkage void ret_from_sys_call(void); asmlinkage int do_signal(unsigned long, struct pt_regs *, struct switch_stack *, unsigned long, unsigned long); asmlinkage void imb(void); /* * The OSF/1 sigprocmask calling sequence is different from the * C sigprocmask() sequence.. */ asmlinkage unsigned long osf_sigprocmask(int how, unsigned long newmask) { unsigned long oldmask = current->blocked; newmask &= _BLOCKABLE; switch (how) { case SIG_BLOCK: current->blocked |= newmask; return oldmask; case SIG_UNBLOCK: current->blocked &= ~newmask; return oldmask; case SIG_SETMASK: current->blocked = newmask; return oldmask; } return -EINVAL; } /* * 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 = current->blocked; current->blocked = mask & _BLOCKABLE; 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_struct * sc, struct pt_regs * regs, struct switch_stack * sw) { unsigned long mask; int i; /* verify that it's a good sigcontext before using it */ if (verify_area(VERIFY_READ, sc, sizeof(*sc))) do_exit(SIGSEGV); if (get_fs_quad(&sc->sc_ps) != 8) do_exit(SIGSEGV); mask = get_fs_quad(&sc->sc_mask); if (mask & ~_BLOCKABLE) do_exit(SIGSEGV); /* ok, looks fine, start restoring */ wrusp(get_fs_quad(sc->sc_regs+30)); regs->pc = get_fs_quad(&sc->sc_pc); sw->r26 = (unsigned long) ret_from_sys_call; current->blocked = mask; regs->r0 = get_fs_quad(sc->sc_regs+0); regs->r1 = get_fs_quad(sc->sc_regs+1); regs->r2 = get_fs_quad(sc->sc_regs+2); regs->r3 = get_fs_quad(sc->sc_regs+3); regs->r4 = get_fs_quad(sc->sc_regs+4); regs->r5 = get_fs_quad(sc->sc_regs+5); regs->r6 = get_fs_quad(sc->sc_regs+6); regs->r7 = get_fs_quad(sc->sc_regs+7); regs->r8 = get_fs_quad(sc->sc_regs+8); sw->r9 = get_fs_quad(sc->sc_regs+9); sw->r10 = get_fs_quad(sc->sc_regs+10); sw->r11 = get_fs_quad(sc->sc_regs+11); sw->r12 = get_fs_quad(sc->sc_regs+12); sw->r13 = get_fs_quad(sc->sc_regs+13); sw->r14 = get_fs_quad(sc->sc_regs+14); sw->r15 = get_fs_quad(sc->sc_regs+15); regs->r16 = get_fs_quad(sc->sc_regs+16); regs->r17 = get_fs_quad(sc->sc_regs+17); regs->r18 = get_fs_quad(sc->sc_regs+18); regs->r19 = get_fs_quad(sc->sc_regs+19); regs->r20 = get_fs_quad(sc->sc_regs+20); regs->r21 = get_fs_quad(sc->sc_regs+21); regs->r22 = get_fs_quad(sc->sc_regs+22); regs->r23 = get_fs_quad(sc->sc_regs+23); regs->r24 = get_fs_quad(sc->sc_regs+24); regs->r25 = get_fs_quad(sc->sc_regs+25); regs->r26 = get_fs_quad(sc->sc_regs+26); regs->r27 = get_fs_quad(sc->sc_regs+27); regs->r28 = get_fs_quad(sc->sc_regs+28); regs->gp = get_fs_quad(sc->sc_regs+29); for (i = 0; i < 31; i++) sw->fp[i] = get_fs_quad(sc->sc_fpregs+i); } /* * Set up a signal frame... */ static void setup_frame(struct sigaction * sa, struct sigcontext_struct ** fp, unsigned long pc, struct pt_regs * regs, struct switch_stack * sw, int signr, unsigned long oldmask) { int i; struct sigcontext_struct * sc; sc = *fp; /* check here if we would need to switch stacks.. */ sc--; if (verify_area(VERIFY_WRITE, sc, sizeof(*sc))) do_exit(SIGSEGV); put_fs_quad(oldmask, &sc->sc_mask); put_fs_quad(8, &sc->sc_ps); put_fs_quad(pc, &sc->sc_pc); put_fs_quad((unsigned long)*fp, sc->sc_regs+30); put_fs_quad(regs->r0 , sc->sc_regs+0); put_fs_quad(regs->r1 , sc->sc_regs+1); put_fs_quad(regs->r2 , sc->sc_regs+2); put_fs_quad(regs->r3 , sc->sc_regs+3); put_fs_quad(regs->r4 , sc->sc_regs+4); put_fs_quad(regs->r5 , sc->sc_regs+5); put_fs_quad(regs->r6 , sc->sc_regs+6); put_fs_quad(regs->r7 , sc->sc_regs+7); put_fs_quad(regs->r8 , sc->sc_regs+8); put_fs_quad(sw->r9 , sc->sc_regs+9); put_fs_quad(sw->r10 , sc->sc_regs+10); put_fs_quad(sw->r11 , sc->sc_regs+11); put_fs_quad(sw->r12 , sc->sc_regs+12); put_fs_quad(sw->r13 , sc->sc_regs+13); put_fs_quad(sw->r14 , sc->sc_regs+14); put_fs_quad(sw->r15 , sc->sc_regs+15); put_fs_quad(regs->r16, sc->sc_regs+16); put_fs_quad(regs->r17, sc->sc_regs+17); put_fs_quad(regs->r18, sc->sc_regs+18); put_fs_quad(regs->r19, sc->sc_regs+19); put_fs_quad(regs->r20, sc->sc_regs+20); put_fs_quad(regs->r21, sc->sc_regs+21); put_fs_quad(regs->r22, sc->sc_regs+22); put_fs_quad(regs->r23, sc->sc_regs+23); put_fs_quad(regs->r24, sc->sc_regs+24); put_fs_quad(regs->r25, sc->sc_regs+25); put_fs_quad(regs->r26, sc->sc_regs+26); put_fs_quad(regs->r27, sc->sc_regs+27); put_fs_quad(regs->r28, sc->sc_regs+28); put_fs_quad(regs->gp , sc->sc_regs+29); for (i = 0; i < 31; i++) put_fs_quad(sw->fp[i], sc->sc_fpregs+i); /* * The following is: * * bis $30,$30,$16 * addq $31,0x67,$0 * call_pal callsys * * ie, "sigreturn(stack-pointer)" */ put_fs_quad(0x43ecf40047de0410, sc->sc_retcode+0); put_fs_quad(0x0000000000000083, sc->sc_retcode+1); regs->r26 = (unsigned long) sc->sc_retcode; regs->r16 = signr; *fp = sc; } /* * 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 = ~current->blocked; unsigned long handler_signal = 0; struct sigcontext_struct *frame = NULL; unsigned long pc = 0; unsigned long signr; struct sigaction * sa; while ((signr = current->signal & mask) != 0) { signr = ffz(~signr); clear_bit(signr, ¤t->signal); sa = current->sigaction + signr; signr++; if ((current->flags & PF_PTRACED) && signr != SIGKILL) { current->exit_code = signr; current->state = TASK_STOPPED; notify_parent(current); schedule(); 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->sigaction + signr - 1; } if (sa->sa_handler == SIG_IGN) { if (signr != SIGCHLD) continue; /* check for SIGCHLD: it's special */ while (sys_waitpid(-1,NULL,WNOHANG) > 0) /* nothing */; continue; } if (sa->sa_handler == SIG_DFL) { if (current->pid == 1) continue; switch (signr) { case SIGCONT: case SIGCHLD: case SIGWINCH: continue; case SIGSTOP: case SIGTSTP: case SIGTTIN: case SIGTTOU: if (current->flags & PF_PTRACED) continue; current->state = TASK_STOPPED; current->exit_code = signr; if (!(current->p_pptr->sigaction[SIGCHLD-1].sa_flags & SA_NOCLDSTOP)) notify_parent(current); schedule(); 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); do_exit(signr); } } /* * OK, we're invoking a handler */ if (r0) { if (regs->r0 == ERESTARTNOHAND || (regs->r0 == ERESTARTSYS && !(sa->sa_flags & SA_RESTART))) regs->r0 = EINTR; } handler_signal |= 1 << (signr-1); mask &= ~sa->sa_mask; } if (r0 && (regs->r0 == ERESTARTNOHAND || regs->r0 == ERESTARTSYS || regs->r0 == ERESTARTNOINTR)) { regs->r0 = r0; regs->r19 = r19; regs->pc -= 4; } if (!handler_signal) /* no handler will be called - return 0 */ return 0; pc = regs->pc; frame = (struct sigcontext_struct *) rdusp(); signr = 1; sa = current->sigaction; for (mask = 1 ; mask ; sa++,signr++,mask += mask) { if (mask > handler_signal) break; if (!(mask & handler_signal)) continue; setup_frame(sa,&frame,pc,regs,sw,signr,oldmask); pc = (unsigned long) sa->sa_handler; regs->r27 = pc; if (sa->sa_flags & SA_ONESHOT) sa->sa_handler = NULL; current->blocked |= sa->sa_mask; oldmask |= sa->sa_mask; } imb(); wrusp((unsigned long) frame); regs->pc = pc; /* "return" to the first handler */ return 1; }