/* @(#)sig.c	1.3 */
#include "sys/param.h"
#include "sys/config.h"
#include "sys/mmu.h"
#include "sys/types.h"
#include "sys/sysmacros.h"
#include "sys/systm.h"
#include "sys/dir.h"
#include "sys/signal.h"
#include "sys/user.h"
#include "sys/errno.h"
#include "sys/proc.h"
#include "sys/inode.h"
#include "sys/file.h"
#include "sys/reg.h"
#include "sys/text.h"
#include "sys/seg.h"
#include "sys/var.h"
#include "sys/psl.h"
#include "sys/scat.h"

/*
 * Priority for tracing
 */
#define	IPCPRI	PZERO

/*
 * Tracing variables.
 * Used to pass trace command from
 * parent to child being traced.
 * This data base cannot be
 * shared and is locked
 * per user.
 */
struct ipctrace
{
	int	ip_data;
	int	ip_lock;
	int	ip_req;
	int	*ip_addr;
} ipc;

/*
 * Send the specified signal to
 * all processes with 'pgrp' as
 * process group.
 * Called by tty.c for quits and
 * interrupts.
 */
signal(pgrp, sig)
register pgrp;
{
	register struct proc *p;

	if (pgrp == 0)
		return;
	for(p = &proc[1]; p < (struct proc *)v.ve_proc; p++)
		if (p->p_pgrp == pgrp)
			psignal(p, sig);
}

/*
 * Send the specified signal to
 * the specified process.
 */
psignal(p, sig)
register struct proc *p;
register sig;
{

	sig--;
	if (sig < 0 || sig >= NSIG)
		return;
	if (((p->p_sigign >> sig) & 1) && sig != (SIGCLD - 1))
		return;
	p->p_sig |= 1L<<sig;
	if (p->p_stat == SSLEEP && p->p_pri > PZERO) {
		if (p->p_pri > PUSER)
			p->p_pri = PUSER;
		setrun(p);
	}
}

/*
 * Returns true if the current
 * process has a signal to process.
 * This is asked at least once
 * each time a process enters the
 * system.
 * A signal does not do anything
 * directly to a process; it sets
 * a flag that asks the process to
 * do something to itself.
 */
issig()
{
	register n;
	register struct proc *p, *q;

	p = u.u_procp;
	while(p->p_sig) {
		n = fsig(p);
		if (n == SIGCLD) {
			if (u.u_signal[SIGCLD-1]&01) {
				for (q = &proc[1];
				 q < (struct proc *)v.ve_proc; q++)
					if (p->p_pid == q->p_ppid &&
					 q->p_stat == SZOMB)
						freeproc(q, 0);
			} else if (u.u_signal[SIGCLD-1])
				return(n);
		} else if (n == SIGPWR) {
			if (u.u_signal[SIGPWR-1] && (u.u_signal[SIGPWR-1]&1)==0)
				return(n);
		} else if ((u.u_signal[n-1]&1) == 0 || (p->p_flag&STRC))
			return(n);
		p->p_sig &= ~(1L<<(n-1));
	}
	return(0);
}

/*
 * Enter the tracing STOP state.
 * In this state, the parent is
 * informed and the process is able to
 * receive commands from the parent.
 */
stop()
{
	register struct proc *pp, *cp;

loop:
	cp = u.u_procp;
	if (cp->p_ppid != 1)
	for (pp = &proc[0]; pp < (struct proc *)v.ve_proc; pp++)
		if (pp->p_pid == cp->p_ppid) {
			wakeup((caddr_t)pp);
			cp->p_stat = SSTOP;
			swtch();
			if ((cp->p_flag&STRC)==0 || procxmt())
				return;
			goto loop;
		}
	exit(fsig(u.u_procp));
}

/*
 * Perform the action specified by
 * the current signal.
 * The usual sequence is:
 *	if (issig())
 *		psig();
 */
psig()
{
	register n, p;
	register struct proc *rp;
#ifdef mc68881		/* MC68881 floating-point coprocessor */
	extern short fp881;		/* is there an MC68881? */
#endif mc68881

	rp = u.u_procp;
#ifdef FLOAT		/* sky floating point board */
	if (u.u_fpinuse && u.u_fpsaved==0) {
		savfp();
		u.u_fpsaved = 1;
	}
#endif
#ifdef mc68881		/* MC68881 floating-point coprocessor */
	if (fp881)
		fpsave();
#endif mc68881
	if (rp->p_flag&STRC)
		stop();
	n = fsig(rp);
	if (n==0)
		return;
	rp->p_sig &= ~(1L<<(n-1));
	if ((p=u.u_signal[n-1]) != 0) {
		if (p & 1)
			return;
		u.u_error = 0;
		if (n != SIGILL && n != SIGTRAP)
			u.u_signal[n-1] = 0;
		sendsig((caddr_t)p, n);
		return;
	}
	switch(n) {

	case SIGQUIT:
	case SIGILL:
	case SIGTRAP:
	case SIGIOT:
	case SIGEMT:
	case SIGFPE:
	case SIGBUS:
	case SIGSEGV:
	case SIGSYS:
		if (core())
			n += 0200;
	}
	exit(n);
}

/*
 * find the signal in bit-position
 * representation in p_sig.
 */
fsig(p)
struct proc *p;
{
	register short i;
	register long n;

	n = p->p_sig;
	i = NSIG - 1;
	do {
		if (n & 1L)
			return(NSIG - i);
		n >>= 1;
	} while (--i != -1);
	return(0);
}

/*
 * Create a core image on the file "core"
 *
 * It writes USIZE (v.v_usize) block of the
 * user.h area followed by the entire
 * data+stack segments.
 */
core()
{
	register struct inode *ip;
	register struct user *up;
	register s;
	extern schar();

	up = &u;
	if (up->u_uid != up->u_ruid)
		return(0);
	up->u_error = 0;
	up->u_dirp = "core";
	ip = namei(schar, 1);
	if (ip == NULL) {
		if (up->u_error)
			return(0);
		ip = maknode(0666);
		if (ip==NULL)
			return(0);
	}
	if (!access(ip, IWRITE) && (ip->i_mode&IFMT) == IFREG) {
		itrunc(ip);
		up->u_offset = 0;
		up->u_base = (caddr_t)up;
		up->u_count = ctob(v.v_usize);
		up->u_segflg = 1;
		up->u_limit = (daddr_t)ctod(MAXMEM);
		up->u_fmode = FWRITE;
		/* make register pointer relative for adb */
		up->u_ar0 = (int *)((int)up->u_ar0 - (int)up);
		up->u_usrtop = btoc(v.v_uend);
		writei(ip);
		up->u_ar0 = (int *)((int)up->u_ar0 + (int)up);
		s = up->u_procp->p_size - v.v_usize;
		(void) estabur((unsigned)0, (unsigned)s, (unsigned)0, 0, RO);
		up->u_base = (caddr_t)v.v_ustart;
		up->u_count = ctob(s);
		up->u_segflg = 0;
		writei(ip);
	} else
		up->u_error = EACCES;
	iput(ip);
	return(up->u_error==0);
}

/*
 * grow the stack to include the SP
 * true return if successful.
 */
#ifdef NONSCATLOAD
grow(sp)
unsigned sp;
{
	register struct user *up;
	register struct proc *p;
	register si, i, a1;

	up = &u;
	if ((v.v_uend-sp) < ctob(up->u_ssize))
		return(0);
	si = btoct(v.v_uend-sp) - up->u_ssize + SINCR;
	if (si <= 0)
		return(0);
	if (estabur(up->u_tsize, up->u_dsize, up->u_ssize+si, 0, RO))
		return(0);
	p = up->u_procp;
	expand((int)(p->p_size+si));
	a1 = p->p_addr + p->p_size;
	for(i=up->u_ssize; i; i--) {
		a1--;
		copyseg(a1-si, a1);
	}
	for(i=si; i; i--)
		clearseg(--a1);
	up->u_ssize += si;
	return(1);
}
#else
grow(sp)
unsigned sp;
{
	register struct user *up;
	register struct proc *p;
	register si, i, a1;
	register struct scatter *s;
	register a2, n;
	short t;

	up = &u;
	if ((v.v_uend-sp) < ctob(up->u_ssize))
		return(0);
	si = btoct(v.v_uend-sp) - up->u_ssize + SINCR;
	if (si <= 0)
		return(0);
	if (chksize(up->u_tsize, up->u_dsize, up->u_ssize+si))
		return(0);
	p = up->u_procp;
	expand((int)(p->p_size+si));
	/*
	 * locate last click of old data size
	 */
	s = scatmap;
	a1 = p->p_scat;
	n = v.v_usize + up->u_dsize;
	for (i=1; i<n; i++)
		a1 = s[a1].sc_index;
	/*
	 * locate last click of old stack
	 */
	a2 = s[a1].sc_index;
	n = up->u_ssize;
	if (n == 0)
		printf("grow:ssize not expected to be zero\n");
	for (i=1; i<n; i++)
		a2 = s[a2].sc_index;
	/*
	 * chain new clicks into stack space following data space
	 */
	t = s[a1].sc_index;
	s[a1].sc_index = s[a2].sc_index;
	n = si;
	for (i=0; i<n; i++)
		clearseg(ixtoc(a1 = s[a1].sc_index));
	if (s[a1].sc_index != SCATEND)
		printf("grow failure\n");
	s[a1].sc_index = t;
	s[a2].sc_index = SCATEND;
	p->p_flag &= ~SCONTIG;
	up->u_ssize += si;
	(void) estabur(up->u_tsize, up->u_dsize, up->u_ssize, 0, RO);
	return(1);
}
#endif

/*
 * sys-trace system call.
 */
ptrace()
{
	register struct ipctrace *ipcp;
	register struct user *up;
	register struct proc *p;
	register struct a {
		int	req;
		int	pid;
		int	*addr;
		int	data;
	} *uap;

	up = &u;
	uap = (struct a *)up->u_ap;
	if (uap->req <= 0) {
		up->u_procp->p_flag |= STRC;
		return;
	}
	for (p=proc; p < (struct proc *)v.ve_proc; p++) 
		if (p->p_stat==SSTOP
		 && p->p_pid==uap->pid
		 && p->p_ppid==up->u_procp->p_pid)
			goto found;
	up->u_error = ESRCH;
	return;

    found:
	ipcp = &ipc;
	while (ipcp->ip_lock)
		(void) sleep((caddr_t)ipcp, IPCPRI);
	ipcp->ip_lock = p->p_pid;
	ipcp->ip_data = uap->data;
	ipcp->ip_addr = uap->addr;
	ipcp->ip_req = uap->req;
	p->p_flag &= ~SWTED;
	setrun(p);
	while (ipcp->ip_req > 0)
		(void) sleep((caddr_t)ipcp, IPCPRI);
	up->u_rval1 = ipcp->ip_data;
	if (ipcp->ip_req < 0)
		up->u_error = EIO;
	ipcp->ip_lock = 0;
	wakeup((caddr_t)ipcp);
}

/*
 * Code that the child process
 * executes to implement the command
 * of the parent process in tracing.
 */
procxmt()
{
	register struct ipctrace *ipcp;
	register struct user *up;
	register int i;
	register *p;
	register struct text *xp;

	up = &u;
	ipcp = &ipc;
	if (ipcp->ip_lock != up->u_procp->p_pid)
		return(0);
	i = ipcp->ip_req;
	ipcp->ip_req = 0;
	wakeup((caddr_t)ipcp);
	switch (i) {

	/* read user I */
	case 1:
		ipcp->ip_data = fuiword((caddr_t)ipcp->ip_addr);
		break;

	/* read user D */
	case 2:
		ipcp->ip_data = fuword((caddr_t)ipcp->ip_addr);
		break;

	/* read u */
	case 3:
		i = (int)ipcp->ip_addr;
		if (i<0 || i >= ctob(v.v_usize))
			goto error;
		ipcp->ip_data = *((int *)((char *)up + (i & ~1)));
		break;

	/* write user I */
	/* Must set up to allow writing */
	case 4:
		/*
		 * If text, must assure exclusive use
		 */
		if (xp = up->u_procp->p_textp) {
			if (xp->x_count!=1 || xp->x_iptr->i_mode&ISVTX)
				goto error;
			xp->x_iptr->i_flag &= ~ITEXT;
		}
		(void) estabur(up->u_tsize, up->u_dsize, up->u_ssize, 0, RW);
		i = suiword((caddr_t)ipcp->ip_addr, 0);
		(void) suiword((caddr_t)ipcp->ip_addr, ipcp->ip_data);
		(void) estabur(up->u_tsize, up->u_dsize, up->u_ssize, 0, RO);
		if (i<0)
			goto error;
		if (xp)
			xp->x_flag |= XWRIT;
		break;

	/* write user D */
	case 5:
		if (suword((caddr_t)ipcp->ip_addr, 0) < 0)
			goto error;
		(void) suword((caddr_t)ipcp->ip_addr, ipcp->ip_data);
		break;

	/* write u */
	case 6:
		i = (int)ipcp->ip_addr;
		p = (int *)((char *)up + (i & ~1));
		for (i=0; i<17; i++)
			if (p == &up->u_ar0[regloc[i]])
				goto ok;
		if (p == &up->u_ar0[RPS]) {
			/* assure user space and priority 0 */
			ipcp->ip_data &= ~0x2700;
			goto ok;
		}
		goto error;

	ok:
		*p = ipcp->ip_data;
		break;

	/* set signal and continue */
	/* one version causes a trace-trap */
	case 9:
		up->u_ar0[RPS] |= PS_T;
	case 7:
		if ((int)ipcp->ip_addr != 1)
			up->u_ar0[PC] = (int)ipcp->ip_addr;
		up->u_procp->p_sig = 0L;
		if (ipcp->ip_data)
			psignal(up->u_procp, ipcp->ip_data);
		return(1);

	/* force exit */
	case 8:
		exit(fsig(up->u_procp));

	/* read u registers */
	case 10:
		if ((i = (int)ipcp->ip_addr) < 0 || i > 17)
			goto error;
		if (i == 17)
			ipcp->ip_data = up->u_ar0[regloc[17]] & 0xFFFF;
		else
			ipcp->ip_data = up->u_ar0[regloc[i]];
		break;

	/* write u registers */
	case 11:
		
		if ((i = (int)ipcp->ip_addr) < 0 || i > 17)
			goto error;
		if (i == 17) {
			ipcp->ip_data &= ~0x2700;	/* user only */
			up->u_ar0[regloc[17]] =
				(up->u_ar0[regloc[17]] & ~0xFFFF) |
				(ipcp->ip_data & 0xFFFF);
		} else
			up->u_ar0[regloc[i]] = ipcp->ip_data;
		break;
		
	default:
	error:
		ipcp->ip_req = -1;
	}
	return(0);
}
