File:  [MW Coherent from dump] / coherent / b / STREAMS / coh.386 / sig.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Wed May 29 04:56:36 2019 UTC (7 years, 2 months ago) by root
Branches: MarkWilliams, MAIN
CVS tags: relic, HEAD
coherent

/* (lgl-
 *	The information contained herein is a trade secret of Mark Williams
 *	Company, and  is confidential information.  It is provided  under a
 *	license agreement,  and may be  copied or disclosed  only under the
 *	terms of  that agreement.  Any  reproduction or disclosure  of this
 *	material without the express written authorization of Mark Williams
 *	Company or persuant to the license agreement is unlawful.
 *
 *	COHERENT Version 5.0
 *	Copyright (c) 1982, 1993.
 *	An unpublished work by Mark Williams Company, Chicago.
 *	All rights reserved.
 -lgl) */
/*
 * File:	coh.386/sig.c
 *
 * Purpose:	signal handling
 *
 * Revised: Tue May  4 11:59:15 1993 CDT
 */

/*
 * ----------------------------------------------------------------------
 * Includes.
 */

#include <common/_limits.h>
#include <common/_wait.h>
#include <common/_gregset.h>
#include <kernel/sigproc.h>

#include <sys/coherent.h>
#include <sys/errno.h>
#include <sys/ino.h>
#include <sys/inode.h>
#include <sys/io.h>
#include <sys/proc.h>
#include <sys/ptrace.h>
#include <sys/sched.h>
#include <sys/seg.h>
#include <sys/file.h>
#include <sys/core.h>


/*
 * ----------------------------------------------------------------------
 * Definitions.
 *	Constants.
 *	Macros with argument lists.
 *	Typedefs.
 *	Enums.
 */

/*
 * ----------------------------------------------------------------------
 * Functions.
 *	Import Functions.
 *	Export Functions.
 *	Local Functions.
 */
void	curr_check_signals();
int	ptset();
void	sendsig();
int	sigdump();
__sigfunc_t	usigsys();

static struct _fpstate * empack();
static	int		ptret();

/*
 * ----------------------------------------------------------------------
 * Global Data.
 *	Import Variables.
 *	Export Variables.
 *	Local Variables.
 */
/*
 * Patchable variables.
 *
 * Patch DUMP_TEXT nonzero to make text segment show up in core files.
 * Patch DUMP_LIM set the upper limit in bytes of how much of a
 * segment is written to a core file.
 *
 * CATCH_SEGV can be patched to allow signal () to be used to catch the
 * SIGSEGV signal (). This is off by default because:
 *   i)	Experience has shown that kernel printf () messages being on by
 *	default is very useful for catching bugs.
 *  ii)	Certain extremely ill-behaved applications apparently catch SIGSEGV
 *	blindly as part of some catch-all behaviour, and when such faults
 *	happen have been known to loop generating kernel printf () output
 *	which bogs down the system unacceptably.
 * iii)	Turning the signal off to avoid ii) causes less actual disruption than
 *	leaving it on.
 * This only applies to the signal () interface; we assume that apps which are
 * smart enough to use sigset () or sigaction () also know what to do with
 * SIGSEGV.
 */

int	DUMP_TEXT = 0;
int	DUMP_LIM=512*1024;
int	CATCH_SEGV = 0;

/*
 * ----------------------------------------------------------------------
 * Code.
 */

/*
 * Or an entire signal mask into the "hold" mask.
 */

static void
sigMask (mask)
__sigset_t * mask;
{
	int		i;
	__sigmask_t   *	loop = mask->_sigbits;
	__sigset_t	signal_mask;

	curr_signal_mask (NULL, & signal_mask);

	for (i = 0 ; i < __ARRAY_LENGTH (mask->_sigbits) ; i ++)
		signal_mask._sigbits [i] |= * loop ++;

	curr_signal_mask (& signal_mask, NULL);
}


/*
 * Set up the action to be taken on a signal.
 */

__sigfunc_t
usigsys (signal, func, regsetp)
unsigned	signal;
__sigfunc_t	func;
gregset_t     *	regsetp;
{
	int		sigtype;
	__sigmask_t	mask;
	__sigset_t	signal_mask;
	__sigaction_t	signal_action;

	sigtype = signal & ~ 0xFF;
	signal &= 0xFF;

#if 0
	T_HAL(8, printf("[%d]sig(%d, %x) ", SELF->p_pid, signal, func));
#endif

	/*
	 * Check the passed signal number. SIGKILL and SIGSTOP are not allowed
	 * to be caught.
	 */

	/* Range check on 1-based signal number. */
	if (signal <= 0 || signal > NSIG || signal == SIGSTOP ||
	    signal == SIGKILL) {
		u.u_error = EINVAL;
		return;
	}

	signal_action.sa_handler = func;
	signal_action.sa_flags = 0;
	__SIGSET_EMPTY (signal_action.sa_mask);
	func = 0;

	curr_signal_mask (NULL, & signal_mask);

	mask = __SIGSET_MASK (signal);

	switch (sigtype) {
	case SIGHOLD:
		__SIGSET_ADDMASK (signal_mask, signal, mask);
		break;

	case SIGRELSE:
		__SIGSET_CLRMASK (signal_mask, signal, mask);
		break;

	case SIGIGNORE:
		__SIGSET_CLRMASK (signal_mask, signal, mask);
		signal_action.sa_handler = SIG_IGN;
		curr_signal_action (signal, & signal_action, NULL);
		break;

	case 0:				/* signal () */
		if (signal == SIGSEGV && CATCH_SEGV == 0) {
			u.u_error = EINVAL;
			return 0;
		}
		u.u_sigreturn = (__sigfunc_t) regsetp->_i386._edx;

		if (signal == SIGCHLD) {
			/*
			 * With signal (), the argument implicitly mangles the
			 * SA_NOCLDWAIT flag. I have no idea whether the
			 * SA_NOCLDSTOP flag is also implicitly affected, but
			 * for now we leave it alone.
			 */

			curr_signal_misc (__SF_NOCLDWAIT,
					  signal_action.sa_handler ==
						SIG_IGN ? __SF_NOCLDWAIT : 0);
		}
		signal_action.sa_flags |= __SA_RESETHAND;
		curr_signal_action (signal, & signal_action, & signal_action);

		/*
		 * Using the signal () interface automatically causes a
		 * pending signal to be discarded.
		 */

		proc_unkill (SELF, signal);
		return signal_action.sa_handler;		

	case SIGSET:
		u.u_sigreturn = (__sigfunc_t) regsetp->_i386._edx;

		if (__SIGSET_TSTMASK (signal_mask, signal, mask))
			func = SIG_HOLD;

		if (signal_action.sa_handler == SIG_HOLD) {

			__SIGSET_ADDMASK (signal_mask, signal, mask);

			if (func != SIG_HOLD) {
				curr_signal_action (signal, NULL,
						    & signal_action);
				func = signal_action.sa_handler;
			}
			break;
		}

		if (signal == SIGCHLD) {
			/*
			 * With sigset (), the argument implicitly mangles the
			 * SA_NOCLDWAIT flag. I have no idea whether the
			 * SA_NOCLDSTOP flag is also implicitly affected, but
			 * for now we leave it alone.
			 */

			curr_signal_misc (__SF_NOCLDWAIT,
					  signal_action.sa_handler ==
						SIG_IGN ? __SF_NOCLDWAIT : 0);
		}
		__SIGSET_CLRMASK (signal_mask, signal, mask);
		__SIGSET_ADDMASK (signal_action.sa_mask, signal, mask);
		curr_signal_action (signal, & signal_action, & signal_action);

		if (func != SIG_HOLD)
			func = signal_action.sa_handler;
		break;

	case SIGPAUSE:
		/*
		 * Like upause(), do a sleep on an event which never gets a
		 * wakeup. The sleep returns immediately if a signal was
		 * already holding.
		 */

		__SIGSET_CLRMASK (signal_mask, signal, mask);
		curr_signal_mask (& signal_mask, NULL);
		(void) x_sleep ((char *) & u, prilo, slpriSigCatch,
				"sigpause");
		return 0;

	default:
		u.u_error = SIGSYS;
		return 0;
	}

	curr_signal_mask (& signal_mask, NULL);
	return func;
}

/*
 * Send a signal to the process `pp'. This function is only valid for use with
 * signals generated from user level with kill (), sigsend () or
 * sigsendset ().
 */

void
sendsig(sig, pp)
register unsigned sig;
register PROC *pp;
{
	__siginfo_t	siginfo;

#if 0
	T_HAL(8, if (sig == SIGINT) printf("[%d]gets int ", pp->p_pid));
#else
	T_HAL(8, printf ("[%d] sig=%d ", pp->p_pid, sig));
#endif

	siginfo.__si_signo = sig;
	siginfo.__si_code = 0;
	siginfo.__si_errno = 0;
	siginfo.__si_pid = SELF->p_pid;
	siginfo.__si_uid = SELF->p_uid;

	proc_send_signal (pp, & siginfo);
}


/*
 * Return signal number if we have a non ignored or delayed signal, else zero.
 */

int
nondsig()
{
	return curr_signal_pending ();
}


/*
 * If we have a signal that isn't ignored, activate it. The register set is
 * not "const" because the low-level trap code that uses it wants to modify
 * it.
 */

#if	__USE_PROTO__
void curr_check_signals (gregset_t * regsetp)
#else
void
curr_check_signals (regsetp)
gregset_t     *	regsetp;
#endif
{
	register int signum;
	int ptval;

	/*
	 * Fetch an unprocessed signal.
	 * Return if there are none.
	 * The while() structure is only for traced processes.
	 */
	while ((signum = curr_signal_pending ()) != 0) {
		__sigaction_t	signal_action;

		/*
		 * Reset the signal to indicate that it has been processed,
		 * and fetch the signal disposition.
		 */

got_signal:
		proc_unkill (SELF, signum);
		curr_signal_action (signum, NULL, & signal_action);

		/*
		 * Store the signal number in the u area.
		 * This is how a core dump records the death signal.
		 */
		u.u_signo = signum;

		/*
		 * If the signal is not defaulted, go run the requested
		 * function.
		 */

		if (signal_action.sa_handler != SIG_DFL) {
			if (__xmode_286 (regsetp))
				oldsigstart (signum, signal_action.sa_handler,
					     regsetp);
			else
				msigstart (signum, signal_action.sa_handler,
					   regsetp);

	/*
	 * If the signal needs to be reset after delivery, do so. Note that
	 * all signal-related activity goes though the defined function
	 * interface; many subtle things may need to happen, so we let the
	 * layering take care of it.
	 */

			if ((signal_action.sa_flags & __SA_RESETHAND) != 0) {
				signal_action.sa_handler = SIG_DFL;
				curr_signal_action (signum, & signal_action,
						    NULL);
			} else
				sigMask (& signal_action.sa_mask);

			return;
		}

		/*
		 * msysgen() is a nop for COHERENT 4.0.  The comment in the
		 * assembly code is "Nothing useful to save".
		 */

		msysgen (u.u_sysgen);

		/*
		 * When a traced process is signaled, it may need to exchange
		 * data with its parent (via ptret).
		 */

		if ((SELF->p_flags & PFTRAC) != 0) {
			SELF->p_flags |= PFWAIT;
			ptval = ptret (regsetp);

			T_HAL(0x10000, printf("ptret()=%x ", ptval));

			SELF->p_flags &= ~ (PFWAIT | PFSTOP);

			while ((signum = curr_signal_pending ()) != 0)
				proc_unkill (SELF, signum);

			if (ptval == 0)
				/* see if another signal came in */
				continue;

			if ((signum = ptval) != SIGKILL)
				goto got_signal;
		}

		/*
		 * Some signals cause a core file to be written.
		 */
		switch (signum) {
		case SIGQUIT:
		case SIGILL:
		case SIGTRAP:
		case SIGABRT:
		case SIGFPE:
		case SIGSEGV:
		case SIGSYS:
			if (sigdump ())
				signum |= __WCOREFLG;
			break;
		}
		pexit (signum);
	}
}

/*
 * Create a dump of ourselves onto the file `core'.
 */

int
sigdump ()
{
	register INODE *ip;
	register SR *srp;
	register SEG * sp;
	register int n;
	register paddr_t ssize;
	extern	int	DUMP_LIM;
	struct ch_info chInfo;
	IO		io;
	struct direct	dir;

	if (SELF->p_flags & PFNDMP)
		return 0;

	/* Make the core with the real owners */
	schizo ();

	io.io_seg = IOSYS;
	io.io_flag = 0;
	if (ftoi ("core", 'c', & io, & dir)) {
		schizo ();
		return 0;
	}

	if ((ip = u.u_cdiri) == NULL) {
		if ((ip = imake (IFREG | 0644, 0, & io, & dir)) == NULL) {
			schizo ();
			return 0;
		}
	} else {
		if ((ip->i_mode & IFMT) != IFREG || iaccess (ip, IPW) == 0 ||
		    getment (ip->i_dev, 1) == NULL) {
			idetach (ip);
			schizo ();
			return 0;
		}
		iclear (ip);
	}
	schizo ();
	u.u_error = 0;

	/* Write core file header */
	chInfo.ch_magic = CORE_MAGIC;
	chInfo.ch_info_len = sizeof (chInfo);
	chInfo.ch_uproc_offset = U_OFFSET;

	io.io_seek = 0;
	io.io_seg = IOSYS;
	io.io.vbase = & chInfo;
	io.io_ioc = sizeof (chInfo);
	io.io_flag = 0;

	iwrite (ip, & io);

	/*
	 * Added to aid in kernel debugging - if DUMP_TEXT is nonzero,
	 * dump the text segment (to see if it was corrupted) and set
	 * the dump flag so that postmortem utilities will know that
	 * text is present in the core file.
	 */

	if (DUMP_TEXT)
		u.u_segl [SISTEXT].sr_flag |= SRFDUMP;

	for (srp = u.u_segl + 1 ; u.u_error == 0 && srp < u.u_segl + NUSEG ;
	     srp ++) {

		if ((srp->sr_flag & SRFDUMP) == 0)
			continue;

		/* Don't try to dump empty segments. */
		if ((sp = srp->sr_segp) == NULL) {
			srp->sr_flag &= ~SRFDUMP;
			continue;
		}

		/* Don't dump segments too big to dump. */
		if (sp->s_size > DUMP_LIM)
			srp->sr_flag &= ~SRFDUMP;
	}

	/* Always dump the U segment. */
	u.u_segl [SIUSERP].sr_flag |= SRFDUMP;

	for (srp = u.u_segl ; u.u_error == 0 && srp < u.u_segl + NUSEG ;
	     srp ++) {

		/* Only dump segments flagged for dumping. */
		if ((srp->sr_flag & SRFDUMP) == 0)
			continue;

		sp = srp->sr_segp;

		ssize = sp->s_size;
		io.io_seg = IOPHY;
		io.io.pbase = MAPIO (sp->s_vmem, 0);
		io.io_flag = 0;
		sp->s_lrefc ++;
		while (u.u_error == 0 && ssize != 0) {
			n = ssize > SCHUNK ? SCHUNK : ssize;
			io.io_ioc = n;
			iwrite (ip, & io);
			io.io.pbase += n;
			ssize -= (paddr_t) n;
		}
		sp->s_lrefc --;
	}
	idetach (ip);
	return u.u_error == 0;
}

/*
 * Send a ptrace command to the child.
 *
 * "pid" is child pid.
 */

int
ptset(req, pid, addr, data)
unsigned req;
int *addr;
{
	register PROC *pp;

	lock (pnxgate);
	for (pp = procq.p_nforw ; pp != & procq ; pp = pp->p_nforw)
		if (pp->p_pid == pid)
			break;
	unlock (pnxgate);
	if (pp == & procq || (pp->p_flags & PFSTOP) == 0 ||
	    pp->p_ppid != SELF->p_pid) {
		u.u_error = ESRCH;
		return;
	}

	lock (pts.pt_gate);
	pts.pt_req = req;
	pts.pt_pid = pid;
	pts.pt_addr = addr;
	pts.pt_data = data;
	pts.pt_errs = 0;
	pts.pt_rval = 0;
	pts.pt_busy = 1;

	wakeup ((char *) & pts.pt_req);

	while (pts.pt_busy) {
		x_sleep ((char *) & pts.pt_busy, primed, slpriSigCatch,
			 "ptrace");
		/* Send a ptrace command to the child.  */
	}

	u.u_error = pts.pt_errs;
	unlock (pts.pt_gate);
	return pts.pt_rval;
}

/*
 * This routine is called when a child that is being traced receives a signal
 * that is not caught or ignored.  It follows up on any requests by the parent
 * and returns when done.
 *
 * After ptrace handling done in this routine, a real or simulated signal
 * may need to be sent to the traced process.
 * Return a signal number to be sent to the child process, or 0 if none.
 */

static int
ptret (regsetp)
gregset_t     *	regsetp;
{
	extern void (*ndpKfrstor)();
	register PROC *pp;
	register PROC *pp1;
	register int sign;
	unsigned off;
	int doEmUnpack = 0;
	struct _fpstate * fstp = empack ();

	pp = SELF;
next:
	u.u_error = 0;
	if (pp->p_ppid == 1)
		return SIGKILL;
	sign = -1;

	/* wake up parent if it is sleeping */
	lock (pnxgate);
	pp1 = & procq;
	for (;;) {
		if ((pp1 = pp1->p_nforw) == & procq) {
			sign = SIGKILL;
			break;
		}
		if (pp1->p_pid != pp->p_ppid)
			continue;
		if (ASLEEP (pp1))
			wakeup ((char *) pp1);
		break;
	}
	unlock (pnxgate);

	while (sign < 0) {
		/* If no pending ptrace transaction for this process, sleep. */
		if (pts.pt_busy == 0 || pp->p_pid != pts.pt_pid) {
			/*
			 * If a signal bit is set now, just exit - let
			 * actvsig() handle it next time through.
			 * Doing sleep and goto next will stick us in a loop
			 */

			if (nondsig ())
				return 0;
			x_sleep ((char *) & pts.pt_req, primed,
				 slpriSigCatch, "ptret");
			goto next;
		}

		switch (pts.pt_req) {
		case PTRACE_RD_TXT:
			if (__xmode_286 (regsetp)) {
				pts.pt_rval = getuwd (NBPS + pts.pt_addr);
				break;
			}
			/* Fall through for 386 mode processes. */

		case PTRACE_RD_DAT:
			pts.pt_rval = getuwd (pts.pt_addr);
			break;

		case PTRACE_RD_USR:
			/* See ptrace.h for valid offsets. */
			off = (unsigned) pts.pt_addr;
			if (off & 3)
				u.u_error = EINVAL;
			else if (off < PTRACE_FP_CW) {
				/* Reading CPU general register state */
				if (off == PTRACE_SIG)
					pts.pt_rval = u.u_signo;
				else
					pts.pt_rval =
						((int *) regsetp) [off >> 2];
			} else if (off < PTRACE_DR0) {
				/*
				 * Reading NDP state.
				 * If NDP state not already saved, save it.
				 * Fetch desired info.
				 * Restore NDP state in case we will resume.
				 */
				if (rdNdpUser ()) {
					/* if using coprocessor */
					if (! rdNdpSaved ()) {
						ndpSave (& u.u_ndpCon);
						wrNdpSaved (1);
					}
pts.pt_rval = ((int *) & u.u_ndpCon) [(off - PTRACE_FP_CW) >> 2];
					ndpRestore (& u.u_ndpCon);
					wrNdpSaved (0);
				} else if (fstp) {
pts.pt_rval = getuwd(((int *) fstp) + ((off - PTRACE_FP_CW) >> 2));
					/* if emulating */
				} else { /* no ndp state to display */
					pts.pt_rval = 0;
					u.u_error = EFAULT;
				}
			} else /* Bad pseudo offset. */
				u.u_error = EINVAL;
			break;

		case PTRACE_WR_TXT:
			if (__xmode_286 (regsetp)) {
				putuwd (NBPS + pts.pt_addr, pts.pt_data);
				break;
			}
			/* Fall through for 386 mode processes. */

		case PTRACE_WR_DAT:
			putuwd (pts.pt_addr, pts.pt_data);
			break;

		case PTRACE_WR_USR:
			/* See ptrace.h for valid offsets. */
			off = (unsigned) pts.pt_addr;

			if (off & 3)
				u.u_error = EINVAL;
			else if (off < PTRACE_FP_CW) {
				/* Writing CPU general register state */
				if (off == PTRACE_SIG)
					u.u_error = EINVAL;
				else
					((int *) regsetp) [off >> 2] =
						pts.pt_data;
			} else if (off < PTRACE_DR0) {
				if (rdNdpUser ()) {
					/*
					 * Writing NDP state.
					 * If NDP state not already saved, save it.
					 * Store desired info.
					 * Restore NDP state in case we will resume.
					 */
					if (! rdNdpSaved ()) {
						ndpSave (& u.u_ndpCon);
						wrNdpSaved (1);
					}
((int *)&u.u_ndpCon)[(off - PTRACE_FP_CW)>>2] = pts.pt_data;
					ndpRestore (& u.u_ndpCon);
					wrNdpSaved (0);
				} else if (fstp && ndpKfrstor) {
putuwd(((int *)fstp) + ((off - PTRACE_FP_CW)>>2), pts.pt_data);
					doEmUnpack = 1;
				} else { /* No NDP state to modify. */
					u.u_error = EFAULT;
				}
			} else /* Bad pseudo offset. */
				u.u_error = EINVAL;
			break;

		case PTRACE_RESUME:
			regsetp->_i386._eflags &= ~ MFTTB;
			goto sig;

		case PTRACE_TERM:
			sign = SIGKILL;
			break;

		case PTRACE_SSTEP:
			regsetp->_i386._eflags |= MFTTB;
		sig:
			if (pts.pt_data < 0 || pts.pt_data > NSIG) {
				u.u_error = EINVAL;
				break;
			}
			sign = pts.pt_data;
			break;

		default:
			u.u_error = EINVAL;
		}

		if ((pts.pt_errs = u.u_error) == EFAULT)
			pts.pt_errs = EINVAL;

		pts.pt_busy = 0;
		wakeup((char *) & pts.pt_busy);
	}
	if (doEmUnpack)
		(* ndpKfrstor) (fstp, & u.u_ndpCon);
	return sign;
}

/*
 * If using floating point emulator, make room on user stack and save
 * floating point context there.  Code elsewhere takes care of floating
 * point context if there is a coprocessor.
 *
 * Return the virtual address in user space of the context area, or
 * return NULL if not using FP emulation.
 */

static struct _fpstate *
empack (regsetp)
gregset_t     *	regsetp;
{
	int		sphi;
	struct _fpstate * ret;
	SEG	      *	segp = u.u_segl [SISTACK].sr_segp;
	extern void (*ndpKfsave)();
	unsigned long sw_old;

	/* If not emulating, do nothing */
	if (rdNdpUser () || ! rdEmTrapped () || ! ndpKfsave)
		return NULL;

	/*
	 * Will copy at least u_sigreturn, _fpstackframe, and ndpFlags.
	 * If using ndp, need room for an _fpstate.
	 * If emulating, need room for an _fpemstate.
	 */

	ret = (struct _fpstate *)
		(__xmode_286 (regsetp) ? regsetp->_i286._usp :
					 regsetp->_i386._uesp) - 1;

	/* Add to user stack if necessary. */
	sphi = __xmode_286 (regsetp) ? ISP_286 : ISP_386;

	if (sphi - segp->s_size > (__ptr_arith_t) ret) {
		cseg_t	      *	pp;

		pp = c_extend (segp->s_vmem, btoc (segp->s_size));
		if (pp == 0) {
			printf ("Empack failed.  cmd=%s  c_extend(%x,%x)=0 ",
				u.u_comm, segp->s_vmem, btoc (segp->s_size));
			return NULL;
		}

		segp->s_vmem = pp;
		segp->s_size += NBPC;
		if (sproto (0) == 0) {
			printf ("Empack failed.  cmd=%s  sproto(0)=0 ",
				u.u_comm);
			return NULL;
		}

		segload ();
	}

	(* ndpKfsave) (& u.u_ndpCon, ret);
	sw_old = getuwd (& ret->sw);
	putuwd (& ret->status, sw_old);
	putuwd (& ret->sw, sw_old & 0x7f00);

	return ret;
}

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.