home *** CD-ROM | disk | FTP | other *** search
- /*
- Copyright 1990,1991,1992 Eric R. Smith.
- Copyright 1992,1993,1994 Atari Corporation.
- All rights reserved.
- */
-
- /* signal.c:: signal handling routines */
-
- #include "mint.h"
-
- void (*sig_routine)(); /* used in intr.s */
- short sig_exc; /* used in intr.s */
-
- /*
- * killgroup(pgrp, sig): send a signal to all members of a process group
- * returns 0 on success, or an error code on failure
- */
-
- long
- killgroup(pgrp, sig)
- int pgrp, sig;
- {
- PROC *p;
- int found = 0;
-
- TRACE(("killgroup %d %d", pgrp, sig));
-
- if (pgrp < 0)
- return EINTRN;
-
- for (p = proclist; p; p = p->gl_next) {
- if (p->pgrp == pgrp) {
- post_sig(p, sig);
- found++;
- }
- }
- if (found) {
- check_sigs(); /* see if the current process is affected */
- return 0;
- }
- else {
- DEBUG(("killgroup: no processes found"));
- return EFILNF;
- }
- }
-
- /* post_sig: post a signal as being pending. It is assumed that the
- caller has already verified that "sig" is a valid signal, and
- moreover it is the caller's responsibility to call check_sigs()
- if it's possible that p == curproc
- */
-
- void
- post_sig(p, sig)
- PROC *p;
- int sig;
- {
- ulong sigm;
-
- /* if process is ignoring this signal, do nothing
- * also: signal 0 is SIGNULL, and should never be delivered through
- * the normal channels (indeed, it's filtered out in dossig.c,
- * but the extra sanity check here is harmless). The kernel uses
- * signal 0 internally for some purposes, but it is handled
- * specially (see supexec() in xbios.c, for example).
- */
- if (p->sighandle[sig] == SIG_IGN || sig == 0)
- return;
-
- /* if the process is already dead, do nothing */
- if (p->wait_q == ZOMBIE_Q || p->wait_q == TSR_Q)
- return;
-
- /* mark the signal as pending */
- sigm = (1L << (unsigned long)sig);
- p->sigpending |= sigm;
-
- /* if the signal is masked, do nothing further */
- /* note: some signals can't be masked, and we handle those elsewhere so
- * that p->sigmask is always valid. SIGCONT is among the unmaskable
- * signals
- */
- if ( (p->sigmask & sigm) != 0 )
- return;
-
- /* otherwise, make sure the process is awake */
- if (p->wait_q && p->wait_q != READY_Q) {
- short sr = spl7();
- rm_q(p->wait_q, p);
- add_q(READY_Q, p);
- spl(sr);
- }
- }
-
- /*
- * check_sigs: see if we have any signals pending. if so,
- * handle them.
- */
-
- void
- check_sigs()
- {
- ulong sigs, sigm;
- int i;
- short deliversig;
-
- if (curproc->pid == 0) return;
- top:
- sigs = curproc->sigpending & ~(curproc->sigmask);
- if (sigs) {
- sigm = 2;
- /* with tracing we need a mechanism to allow a signal to be delivered
- * to the child (curproc); Fcntl(...TRACEGO...) passes a SIGNULL to indicate that we
- * should really deliver the signal, hence its always safe to remove it
- * from pending.
- */
- deliversig = (curproc->sigpending & 1L);
- curproc->sigpending &= ~1L;
-
- for (i = 1; i < NSIG; i++) {
- if (sigs & sigm) {
- curproc->sigpending &= ~sigm;
- if (curproc->ptracer && !deliversig &&
- i != SIGCONT) {
- TRACE(("tracer being notified of signal %d", i));
- stop(i);
- /* the parent may reset our pending signals, so check again */
- goto top;
- } else {
- ulong omask;
-
- curproc->sigpending &= ~sigm;
- omask = curproc->sigmask;
-
- /* sigextra gives which extra signals should also be masked */
- curproc->sigmask |= curproc->sigextra[i] | sigm;
- handle_sig(i);
-
-
- /*
- * POSIX.1-3.3.4.2(723) "If and when the user's signal handler returns
- * normally, the original signal mask is restored."
- *
- * BUG?: This unmasking could unmask a pending signal which we will not
- * see this time around (if the signal number is less than i) and which
- * was not pending when we started; should we detect this condition and
- * loop around for a second try? POSIX only guarantees delivery of
- * one signal per kernel entry, so this shouldn't really be a problem.
- */
- curproc->sigmask = omask; /* unmask signals */
- }
- }
- sigm = sigm << 1;
- }
- }
- }
-
- /*
- * raise: cause a signal to be raised in the current process
- */
-
- void
- raise(sig)
- int sig;
- {
- post_sig(curproc, sig);
- check_sigs();
- }
-
- #ifdef EXCEPTION_SIGS
- /* exception numbers corresponding to signals */
- char excep_num[NSIG] =
- { 0, 0, 0, 0,
- 4, /* SIGILL == illegal instruction */
- 9, /* SIGTRAP == trace trap */
- 4, /* pretend SIGABRT is also illegal instruction */
- 8, /* SIGPRIV == privileged instruction exception */
- 5, /* SIGFPE == divide by zero */
- 0, 2, /* SIGBUS == bus error */
- 3 /* SIGSEGV == address error */
- /* everything else gets zeros */
- };
-
- /* a "0" means we don't print a message when it happens -- typically the
- user is expecting a synchronous signal, so we don't need to report it
- */
-
- const char *signames[NSIG] = { 0,
- 0, 0, 0, "ILLEGAL INSTRUCTION", "TRACE TRAP",
- 0, "PRIVILEGE VIOLATION", "DIVISION BY ZERO", 0, "BUS ERROR",
- "ADDRESS ERROR", "BAD SYSTEM CALL", 0, 0, 0,
- 0, 0, 0, 0, 0,
- 0, 0, 0, "CPU TIME EXHAUSTED", "FILE TOO BIG",
- 0, 0, 0, 0, 0
- };
-
- /*
- * replaces the TOS "show bombs" routine: for now, print the name of the
- * interrupt on the console, and save info on the crash in the appropriate
- * system area
- */
-
- void
- bombs(sig)
- int sig;
- {
- long *procinfo = (long *)0x380L;
- int i;
- CONTEXT *crash;
- extern int no_mem_prot;
-
- if (sig < 0 || sig > 31) {
- ALERT("bombs(%d): sig out of range", sig);
- }
- else if (signames[sig]) {
- if (!no_mem_prot && sig == SIGBUS) {
- /* already reported by report_buserr */
- } else {
- ALERT("%s: User PC=%lx (basepage=%lx)",
- signames[sig],
- curproc->exception_pc, curproc->base);
- }
- /* save the processor state at crash time */
- /* assumes that "crash time" is the context curproc->ctxt[SYSCALL] */
- /* BUG: this is not true if the crash happened in the kernel; in the
- * latter case, the crash context wasn't saved anywhere.
- */
- crash = &curproc->ctxt[SYSCALL];
- *procinfo++ = 0x12345678L; /* magic flag for valid info */
- for (i = 0; i < 15; i++)
- *procinfo++ = crash->regs[i];
- *procinfo++ = curproc->exception_ssp;
- *procinfo++ = ((long)excep_num[sig]) << 24L;
- *procinfo = crash->usp;
-
- /* we're also supposed to save some info from the supervisor stack. it's not
- * clear what we should do for MiNT, since most of the stuff that used to be
- * on the stack has been put in the CONTXT struct. Moreover, we don't want
- * to crash because of an attempt to access illegal memory. Hence, we do
- * nothing here...
- */
- } else {
- TRACE(("bombs(%d)", sig));
- }
- }
- #endif
-
- /*
- * handle_sig: do whatever is appropriate to handle a signal
- */
-
- static long unwound_stack = 0;
-
- void
- handle_sig(sig)
- int sig;
- {
- long oldstack, newstack;
- long *stack;
- CONTEXT *call, contexts[2];
- #define oldsysctxt (contexts[0])
- #define newcurrent (contexts[1])
-
- extern void sig_return();
-
- if (curproc->sighandle[sig] == SIG_IGN)
- return;
- ++curproc->nsigs;
- if (curproc->sighandle[sig] == SIG_DFL) {
- _default:
- switch(sig) {
- #if 0
- /* Note: SIGNULL is filtered out in dossig.c and is never actually
- * delivered (its only purpose for the user is to test for the existence of
- * a process, it isn't a real signal). The kernel uses SIGNULL
- * internally, but all such code does the signal handling "by hand"
- * and so no default handling is necessary.
- */
- case SIGNULL:
- #endif
- case SIGWINCH:
- case SIGCHLD:
- /* SIGFPE is divide by 0; TOS ignores this, so we will too */
- case SIGFPE:
- return; /* do nothing */
- case SIGSTOP:
- case SIGTSTP:
- case SIGTTIN:
- case SIGTTOU:
- stop(sig);
- return;
- case SIGCONT:
- curproc->sigpending &= ~STOPSIGS;
- return;
-
- /* here are the fatal signals. for SIGINT, we use p_term(-32) so that
- * TOS programs that catch ^C via the vector at 0x400 and which expect
- * TOS's error code (-32) to be sent will work. For most other signals,
- * we p_term with an error code; for SIGKILL, we don't want to allow
- * the program any chance to recover, so we call terminate() directly
- * to avoid calling through to the user's terminate vector.
- */
- case SIGINT: /* ^C */
- if (curproc->domain == DOM_TOS) {
- p_term(-32);
- return;
- }
- /* otherwise, fall through */
- default:
- #ifdef EXCEPTION_SIGS
- bombs(sig); /* tell the user what happened */
- #endif
- /* the "sigmask" check is in case a bus error happens in the user's
- * term_vec code; we don't want to get stuck in an infinite loop!
- */
- if ((curproc->sigmask & 1L) || sig == SIGKILL)
- terminate(sig << 8, ZOMBIE_Q);
- else
- p_term(sig << 8);
- }
- }
- else { /* user wants to handle it himself */
-
- /* another kludge: there is one case in which the p_sigreturn mechanism
- * is invoked by the kernel, namely when the user calls Supexec()
- * or when s/he installs a handler for the GEMDOS terminate vector (#0x102)
- * and the program terminates. MiNT fakes the call to user code with
- * signal 0 (SIGNULL); programs that longjmp out of the user function
- * and are later sent back to it again (e.g. if ^C keeps getting pressed
- * and a terminate vector has been installed) will grow the stack without
- * bound unless we watch for this case.
- *
- * Solution (sort of): whenever Pterm() is called, we unwind the
- * stack; otherwise, we let it grow, so that nested Supexec()
- * calls work.
- *
- * Note that SIGNULL is thrown away when sent by user processes,
- * and the user can't mask it (it's UNMASKABLE), so there is
- * is no possibility of confusion with anything the user does.
- */
- if (sig == 0) {
- /* p_term() sets sigmask to let us know to do Psigreturn */
- if (curproc->sigmask & 1L) {
- p_sigreturn();
- curproc->sigmask &= ~1L;
- } else {
- unwound_stack = 0;
- }
- }
-
- call = &curproc->ctxt[SYSCALL];
- /*
- * what we do is build two fake stack frames; the bottom one is
- * for a call to the user function, with (long)parameter being the
- * signal number; the top one is for sig_return.
- * When the user function returns, it returns to sig_return, which
- * calls into the kernel to restore the context in prev_ctxt
- * (thus putting us back here). We can then continue on our way.
- */
-
- /* set a new system stack, with a bit of buffer space */
- oldstack = curproc->sysstack;
- newstack = ((long) ( (&newcurrent) - 2 )) - 12;
-
- if (newstack < (long)curproc->stack + ISTKSIZE + 256) {
- ALERT("stack overflow");
- goto _default;
- }
- else if ((long) curproc->stack + STKSIZE < newstack) {
- FATAL("system stack not in proc structure");
- }
-
- /* unwound_stack is set by p_sigreturn() */
- if (sig == 0 && unwound_stack)
- curproc->sysstack = unwound_stack;
- else
- curproc->sysstack = newstack;
- oldsysctxt = *call;
- stack = (long *)(call->sr & 0x2000 ? call->ssp :
- call->usp);
- /*
- Hmmm... here's another potential problem for the signal 0 terminate
- vector: if the program keeps returning back to user mode without
- worrying about the supervisor stack, we'll eventually overflow it.
- However, if the program is in supervisor mode itself, then we don't
- want to stomp on its stack. Temporary solution: ignore the problem,
- the stack's only growing 12 bytes at a time.
- */
- /*
- * in addition to the signal number we stuff the vector offset on the
- * stack; if the user is interested they can sniff it, if not ignoring
- * it needs no action on their part. Why do we need this? So that a
- * single SIGFPE handler (for example) can discriminate amongst the
- * multiple things which may get thrown its way
- */
- *(--stack) = (long)call->sfmt & 0xfff;
- *(--stack) = (long)sig;
- *(--stack) = (long)sig_return;
- if (call->sr & 0x2000)
- call->ssp = ((long) stack);
- else
- call->usp = ((long) stack);
- call->pc = (long) curproc->sighandle[sig];
- call->sfmt = call->fstate[0] = 0; /* don't restart FPU communication */
-
- ((long *)curproc->sysstack)[1] = FRAME_MAGIC;
- ((long *)curproc->sysstack)[2] = oldstack;
- ((long *)curproc->sysstack)[3] = sig;
-
- if (curproc->sigflags[sig] & SA_RESET) {
- curproc->sighandle[sig] = SIG_DFL;
- curproc->sigflags[sig] &= ~SA_RESET;
- }
-
- if (save_context(&newcurrent) == 0 ) {
- /*
- * go do the signal; eventually, we'll restore this context (unless the
- * user longjmp'd out of his signal handler). while the user is handling
- * the signal, it's masked out to prevent race conditions. p_sigreturn()
- * will unmask it for us when the user is finished.
- */
- newcurrent.regs[0] = CTXT_MAGIC;
- /* set D0 so next return is different */
- assert(curproc->magic == CTXT_MAGIC);
- leave_kernel();
- restore_context(call);
- }
- /*
- * OK, we get here from p_sigreturn, via the user returning from
- * the handler to sig_return. Restoring the stack and unmasking the
- * signal have been done already for us by p_sigreturn.
- * We should just restore the old system call context
- * and continue with whatever it was we were doing.
- */
- TRACE(("done handling signal"));
- curproc->ctxt[SYSCALL] = oldsysctxt;
- assert(curproc->magic == CTXT_MAGIC);
- }
- #undef oldsysctxt
- #undef newcurrent
- }
-
- /*
- * the p_sigreturn system call
- * When called by the user from inside a signal handler, it indicates a
- * desire to restore the old stack frame prior to a longjmp() out of
- * the handler.
- * When called from the sig_return module, it indicates that the user
- * is finished a handler, and we should not only restore the stack
- * frame but also the old context we were working in (which is on the
- * system call stack -- see handle_sig).
- * The "valid_return" variable is 0 in the first case, 1 in the second.
- */
-
- short valid_return;
-
- long ARGS_ON_STACK
- p_sigreturn()
- {
- CONTEXT *oldctxt;
- long *frame;
- long sig;
-
- unwound_stack = 0;
- top:
- frame = (long *)curproc->sysstack;
- frame++; /* frame should point at FRAME_MAGIC, now */
- sig = frame[2];
- if (*frame != FRAME_MAGIC || (sig < 0) || (sig >= NSIG)) {
- FATAL("Psigreturn: system stack corrupted");
- }
- if (frame[1] == 0) {
- DEBUG(("Psigreturn: frame at %lx points to 0", frame-1));
- return 0;
- }
- unwound_stack = curproc->sysstack;
- TRACE(("Psigreturn(%d)", (int)sig));
-
- curproc->sysstack = frame[1]; /* restore frame */
- curproc->sigmask &= ~(1L<<sig); /* unblock signal */
-
- if (!valid_return) {
- /* here, the user is telling us that a longjmp out of a signal handler is
- * about to occur; so we should unwind *all* the signal frames
- */
- goto top;
- }
- else {
- valid_return = 0;
- oldctxt = ((CONTEXT *)(&frame[2])) + 2;
- if (oldctxt->regs[0] != CTXT_MAGIC) {
- FATAL("p_sigreturn: corrupted context");
- }
- assert(curproc->magic == CTXT_MAGIC);
- restore_context(oldctxt);
- return 0; /* dummy -- this isn't reached */
- }
- }
-
- /*
- * stop a process because of signal "sig"
- */
-
- void
- stop(sig)
- int sig;
- {
- unsigned int code;
- unsigned long oldmask;
- PROC *p;
-
- code = sig << 8;
-
- if (curproc->pid == 0) {
- FORCE("attempt to stop MiNT");
- return;
- }
-
- /* notify parent */
- if (curproc->ptracer) {
- p = curproc->ptracer;
- post_sig(p, SIGCHLD);
- } else {
- p = pid2proc(curproc->ppid);
- if (p && !(p->sigflags[SIGCHLD] & SA_NOCLDSTOP))
- post_sig(p, SIGCHLD);
- }
-
- oldmask = curproc->sigmask;
-
- if ((1L << sig) & STOPSIGS) {
- /* mask out most signals */
- curproc->sigmask |= ~(UNMASKABLE | SIGTERM);
- }
- else
- assert(curproc->ptracer);
-
- /* sleep until someone signals us awake */
- sleep(STOP_Q, (long) code | 0177);
-
- /* when we wake up, restore the signal mask */
- curproc->sigmask = oldmask;
-
- /* and discard any signals that would cause us to stop again */
- curproc->sigpending &= ~STOPSIGS;
- }
-
- /*
- * interrupt handlers to raise SIGBUS, SIGSEGV, etc. Note that for
- * really fatal errors we reset the handler to SIG_DFL, so that
- * a second such error kills us
- */
-
- void
- exception(sig)
- int sig;
- {
- curproc->sigflags[sig] |= SA_RESET;
- DEBUG(("exception #%d raised", sig));
- raise(sig);
- }
-
- void
- sigbus()
- {
- if (curproc->sighandle[SIGBUS] == SIG_DFL)
- report_buserr();
- exception(SIGBUS);
- }
-
- void
- sigaddr()
- {
- exception(SIGSEGV);
- }
-
- void
- sigill()
- {
- exception(SIGILL);
- }
-
- void
- sigpriv()
- {
- raise(SIGPRIV);
- }
-
- void
- sigfpe()
- {
- extern short fpu; /* in main.c */
-
- if (fpu) {
- CONTEXT *ctxt;
-
- ctxt = &curproc->ctxt[SYSCALL];
-
- /* 0x1f38 is a Motorola magic cookie to detect a 68882 idle state frame */
- if (*(ushort *)ctxt->fstate == 0x1f38 &&
- (ctxt->sfmt & 0xfff) >= 0xc0L && (ctxt->sfmt & 0xfff) <= 0xd8L) {
- /* fix a bug in the 68882 - Motorola call it a feature :-) */
- ctxt->fstate[ctxt->fstate[1]] |= 1 << 3;
- }
- }
- raise(SIGFPE);
- }
-
- void
- sigtrap()
- {
- raise(SIGTRAP);
- }
-
- void
- haltformat()
- {
- FATAL("halt: invalid stack frame format");
- }
-
- void
- haltcpv()
- {
- FATAL("halt: coprocessor protocol violation");
- }
-