/* $Copyright:	$
 * Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 
 * Sequent Computer Systems, Inc.   All rights reserved.
 *  
 * This software is furnished under a license and may be used
 * only in accordance with the terms of that license and with the
 * inclusion of the above copyright notice.   This software may not
 * be provided or otherwise made available to, or used by, any
 * other person.  No title to or ownership of the software is
 * hereby transferred.
 */

#ident	"$Header: bcs.c 1.13 90/06/28 $"

/*
 * bcs.c
 *	Binary Compatibility Specification (BCS) support.
 */

/* $Log:	bcs.c,v $
 */

#include "sys/signal.h"
#include "sys/types.h"
#include "sys/devsw.h"
#include "sys/param.h"
#include "sys/debug.h"
#include "sys/systm.h"
#include "sys/mutex.h"
#include "sys/conf.h"
#include "sys/errno.h"
#include "sys/user.h"
#include "sys/timer.h"
#include "sys/proc.h"
#include "sys/acct.h"
#include "sys/vm.h"
#include "sys/var.h"
#include "sys/cmn_err.h"
#include "sys/stat.h"
#include "sys/file.h"
#include "sys/uio.h"
#include "sys/vnode.h"
#include "sys/utsname.h"
#include "sys/ustat.h"
#include "sys/statfs.h"
#include "sys/mount.h"
#include "sys/vfs.h"

#include "sys/engine.h"
#include "sys/slic.h"
#include "sys/slicreg.h"
#include "sys/cfg.h"

#include "sys/psl.h"
#include "sys/reg.h"
#include "sys/mmu.h"
#include "sys/pte.h"
#include "sys/plocal.h"
#include "sys/trap.h"
#include "sys/intctl.h"
#include "sys/inline.h"
#include "sys/mftpr.h"
#include "sys/hwparam.h"

static	void	sysargs();

#ifdef	DEBUG
extern	int	syscalltrace;
#endif	/* DEBUG */

/*
 * The table of supported system calls derived from the document
 * "System V Binary Compatibility Specification" (6/27/89 draft).
 *
 * See sections 3-23 through 3-30 of the above document for further details.
 */

int alarm(), chdir(), chmod(), chown(), chroot(), close(), creat(), dup();
int exece(), fcntl(), fork(), bcs_fstat(), getdents(), getgid(), getpid();
int getuid(), gtime(), ioctl(), kill(), link(), lock(), mkdir(), mknod();
int msgsys(), nice(), nosys(), open(), pause(), pipe(), read(), rexit();
int rmdir(), saccess(), sbreak(), lseek(), semsys(), setgid(), setpgrp();
int setuid(), shmsys(), ssig(), bcs_stat(), stime(), sync(), sysi86(), times();
int ulimit(), umask(), unlink(), utime(), bcs_utssys(), bcs_wait(), write();

/*
 * List of non-BCS system calls supported.
 */
int exec(), bcs_fstatfs(), getmsg(), gtty(), poll(), profil(), bcs_ptrace();
int putmsg(), smount(), bcs_statfs(), stty(), sumount(), sysacct(), uadmin();

/*
 * Wrapper functions.
 */

int bcs_ssig();

/*
 * Binary config variables.
 */

int bcs_strict = 0;			/* strictly enforce BCS system calls */
int bcs_print_nonbcs = 0;		/* print any non BCS system call */

struct	bcs_sysent {
	int	bcs_nargs;		/* number of arguments */
	ushort	bcs_nosetjmp;		/* '1' if setjmp not needed */
	ushort	bcs_isbcs;		/* '1' if syscall part of BCS spec */
	char	*bcs_name;		/* name */
	int	(*bcs_syscall)();	/* handler */
} bcs_sysent[] = {
	0, 1, 0, "nosys-0",  nosys,	/*  0 = indir */
	1, 1, 1, "exit",     rexit,	/*  1 = exit */
	0, 1, 1, "fork",     fork,	/*  2 = fork */
	3, 0, 1, "read",     read,	/*  3 = read */
	3, 0, 1, "write",    write,	/*  4 = write */
	3, 0, 1, "open",     open,	/*  5 = open */
	1, 0, 1, "close",    close,	/*  6 = close */
	0, 0, 1, "wait",     bcs_wait,	/*  7 = wait */
	2, 0, 1, "creat",    creat,	/*  8 = creat */
	2, 1, 1, "link",     link,	/*  9 = link */
	1, 1, 1, "unlink",   unlink,	/* 10 = unlink */
	2, 1, 0, "exec",     exec,	/* 11 = exec */
	1, 1, 1, "chdir",    chdir,	/* 12 = chdir */
	0, 1, 1, "time",     gtime,	/* 13 = time */
	3, 1, 1, "mknod",    mknod,	/* 14 = mknod */
	2, 1, 1, "chmod",    chmod,	/* 15 = chmod */
	3, 1, 1, "chown",    chown,	/* 16 = chown; now 3 args */
	1, 1, 1, "break",    sbreak,	/* 17 = break */
	2, 1, 1, "stat",     bcs_stat,	/* 18 = stat */
	3, 1, 1, "lseek",    lseek,	/* 19 = lseek */
	0, 1, 1, "getpid",   getpid,	/* 20 = getpid */
	6, 1, 0, "mount",    smount,	/* 21 = mount */
	1, 1, 0, "umount",   sumount,	/* 22 = umount */
	1, 1, 1, "setuid",   setuid,	/* 23 = setuid */
	0, 1, 1, "getuid",   getuid,	/* 24 = getuid */
	1, 1, 1, "stime",    stime,	/* 25 = stime */
	4, 1, 0, "ptrace",   bcs_ptrace,/* 26 = ptrace */
	1, 1, 1, "alarm",    alarm,	/* 27 = alarm */
	2, 1, 1, "fstat",    bcs_fstat,	/* 28 = fstat */
	0, 0, 1, "pause",    pause,	/* 29 = pause */
	2, 1, 1, "utime",    utime,	/* 30 = utime */
	2, 0, 0, "stty",     stty,	/* 31 = stty */
	2, 0, 0, "gtty",     gtty,	/* 32 = gtty */
	2, 1, 1, "access",   saccess,	/* 33 = access */
	1, 1, 1, "nice",     nice,	/* 34 = nice */
	4, 1, 0, "statfs",   bcs_statfs,/* 35 = statfs */
	0, 0, 1, "sync",     sync,	/* 36 = sync */
	2, 1, 1, "kill",     kill,	/* 37 = kill */
	4, 1, 0, "fstatfs",  bcs_fstatfs,/* 38 = fstatfs */
	1, 1, 1, "setpgrp",  setpgrp,	/* 39 = setpgrp */
	0, 1, 0, "nosys-40", nosys,	/* 40 = tell - obsolete */
	1, 1, 1, "dup",      dup,	/* 41 = dup */
	0, 0, 1, "pipe",     pipe,	/* 42 = pipe */
	1, 1, 1, "times",    times,	/* 43 = times */
	4, 1, 0, "profil",   profil,	/* 44 = profil */
	1, 1, 1, "lock",     lock,	/* 45 = proc lock */
	1, 1, 1, "setgid",   setgid,	/* 46 = setgid */
	0, 1, 1, "getgid",   getgid,	/* 47 = getgid */
	2, 0, 1, "sig",      bcs_ssig,	/* 48 = sig */
	6, 0, 1, "msgsys",   msgsys,	/* 49 = IPC message */
	4, 1, 1, "sysi86",   sysi86,	/* 50 = Machine-specific system call */
	1, 1, 0, "acct",     sysacct,	/* 51 = turn acct off/on */
	4, 0, 1, "shmsys",   shmsys,	/* 52 = shared memory */
	5, 0, 1, "semsys",   semsys,	/* 53 = IPC semaphores */
	3, 0, 1, "ioctl",    ioctl,	/* 54 = ioctl */
	3, 0, 0, "uadmin",   uadmin,	/* 55 = uadmin */
	0, 1, 0, "nosys-56", nosys,	/* 56 = reserved for exch */
	3, 1, 1, "utssys",   bcs_utssys,/* 57 = utssys */
	0, 1, 0, "nosys-58", nosys,	/* 58 = reserved for USG */
	3, 1, 1, "exece",    exece,	/* 59 = exece */
	1, 1, 1, "umask",    umask,	/* 60 = umask */
	1, 1, 1, "chroot",   chroot,	/* 61 = chroot */
	3, 0, 1, "fcntl",    fcntl,	/* 62 = fcntl */
	2, 1, 1, "ulimit",   ulimit,	/* 63 = ulimit */
	0, 1, 0, "nosys-64", nosys,	/* 64 +0 = nosys, 64-69 for safari 4 */
	0, 1, 0, "nosys-65", nosys,	/* 64 +1 = nosys */
	0, 1, 0, "nosys-66", nosys,	/* 64 +2 = nosys */
	0, 1, 0, "nosys-67", nosys,	/* 64 +3 = file locking call */
	0, 1, 0, "nosys-68", nosys,	/* 64 +4 = local system calls */
	0, 1, 0, "nosys-69", nosys,	/* 64 +5 = inode open */
	0, 1, 0, "nosys-70", nosys,	/* 70 = advfs */
	0, 1, 0, "nosys-71", nosys,	/* 71 = unadvfs */
	0, 1, 0, "nosys-72", nosys,	/* 72 = rmount */
	0, 1, 0, "nosys-73", nosys,	/* 73 = rumount */
	0, 1, 0, "nosys-74", nosys,	/* 74 = rfstart */
	0, 1, 0, "nosys-75", nosys,	/* 75 = not used */
	0, 1, 0, "nosys-76", nosys,	/* 76 = rdebug */
	0, 1, 0, "nosys-77", nosys,	/* 77 = rfstop */
	0, 1, 0, "nosys-78", nosys,	/* 78 = rfsys */
	1, 1, 1, "rmdir",    rmdir,	/* 79 = rmdir */
	2, 1, 1, "mkdir",    mkdir,	/* 80 = mkdir */
	4, 1, 1, "getdents", getdents,	/* 81 = getdents */
	0, 1, 0, "nosys-82", nosys,	/* 82 = libattach */
	0, 1, 0, "nosys-83", nosys,	/* 83 = libdetach */
	0, 1, 0, "nosys-84", nosys,	/* 84 = sysfs */
	4, 0, 0, "getmsg",   getmsg,	/* 85 = getmsg */
	4, 0, 0, "putmsg",   putmsg,	/* 86 = putmsg */
	3, 0, 0, "poll",     poll,	/* 87 = poll */
};

int bcs_nsysent = sizeof(bcs_sysent)/sizeof(struct bcs_sysent);


/*
 * systrap()
 *
 * On entry, all registers have been pushed.  Register EAX contains the
 * system call number.  All the arguments to the system call are still on
 * the users stack.
 *
 * When this function returns, all registers will be popped, so modifications
 * to registers on the stack will modify registers in the user process.
 *
 * Called from locore.s when a lcall through a call gate used for
 * system calls occurs.
 */

/*ARGSUSED*/
systrap(edi, esi, ebp, unused, ebx, edx, ecx, eax, eip, cs, flags, sp, ss)
	volatile uint edi;	/* user register */
	volatile uint esi;	/* user register */
	volatile uint ebp;	/* user frame pointer register */
	volatile uint unused;	/* temp from "pushal" instruction */
	volatile uint ebx;	/* user register */
	volatile uint edx;	/* user register */
	volatile uint ecx;	/* user register */
	volatile uint eax;	/* user register */
	volatile uint eip;	/* user instruction pointer register */
	volatile uint cs;	/* user code segment register */
	volatile uint flags;	/* user flags register */
	volatile uint sp;	/* user stack pointer register */
	volatile uint ss;	/* user stack segment register */
{
	register struct proc *p;
	register int n;
	register struct bcs_sysent *b;
	extern	unchar	arch_type;


	/*
	 * Always sample ru_stime, since syscall may be profile.
	 */

	u.u_syst = u.u_pst.ps_stime;
	l.cnt.v_syscall++;
	u.u_ar0 = (int *)&eax;
	u.u_error = 0;
	u.u_r.r_val1 = 0;
	u.u_r.r_val2 = edx;

	/*
	 * Validate syscall number (from %eax) and
	 * get arguments from the user stack.
	 */

	n = (eax & 0x7f);
	if (n >= bcs_nsysent) {
#ifdef	DEBUG
		if (syscalltrace) {
			CPRINTF("<%d,%s>BCS syscall[%x] - bad syscall #\n",
					u.u_procp-proc, u.u_comm, n);
		}
#endif	/* DEBUG */
		n = 0;
	}
	b = &bcs_sysent[n];
	if (b->bcs_isbcs == 0) {
		if (bcs_print_nonbcs)
			CPRINTF("systrap: %s: bad BCS system call #%d.\n",
				u.u_comm, (unsigned char)eax);
		if (bcs_strict)
			b = &bcs_sysent[0];
	}
	sysargs(b->bcs_nargs, (int *)sp);

	/*
	 * Set up signal return and do sys call.
	 * Signal wakeup will return here unless changed.
	 * Can alter to RESTARTSYS, etc.
	 *
	 * NOTE: fsetjmp() doesn't save registers.
	 * Note that stacked EIP could change if process stopped in debugger,
	 * debugger altered EIP, then process takes signal -- in this case,
	 * saving entry EIP doesn't help.
	 */

	flags &= ~FLAGS_CF;			/* no error! */
	if (!b->bcs_nosetjmp && fsetjmp(&u.u_qsave)) {	/* return from signal */
		if (u.u_error == 0)
			u.u_error = EINTR;
	} else {				/* do real syscall */
#ifdef	DEBUG
		if (syscalltrace) {
			int	i;
			CPRINTF("<%d,%s>%s(", u.u_procp-proc,
					u.u_comm, b->bcs_name);
			for(i = 0; i < b->bcs_nargs; i++) {
				if (i > 0)
					CPRINTF(",");
				CPRINTF("%x", u.u_arg[i]);
			}
			CPRINTF(") ");
		}
#endif	/* DEBUG  */
		u.u_pst.ps_syscall++;
		(*(b->bcs_syscall))();
	}

	/*
	 * Returned from syscall.  Set up return to user.
	 */

	if (u.u_error) {
		/*
		 * Map error values to BCS.
		 */
		switch (u.u_error) {
		case ENAMETOOLONG:
			u.u_error = EINVAL;
			break;
		}
		eax = u.u_error;		/* error # */
		flags |= FLAGS_CF;		/* error indicator */
	} else {
		eax = u.u_r.r_val1;		/* 1st return value */
		edx = u.u_r.r_val2;		/* 2nd return value */
#ifdef	lint
		lint_ref_int((int)edx);
#endif	/* lint  */
	}

	/*
	 * Done.  See about swapping/switching/etc & return.
	 *
	 * Check swap before signal/qswtch to decrease latency for swapper.
	 */

	p = u.u_procp;			/* do here in case fork/signal */

	if (p->p_flag & SFSWAP)
		(void) swapout(p, (size_t) p->p_dsize, (size_t) p->p_ssize);

	/*
	 * Pre-emptive context switch?
	 */

	if (l.runrun) {
		p->p_pri = p->p_usrpri;
		qswtch();
	}

	/*
	 * If entered kernel with single-step trap, notice it now.
	 */

	if (u.u_debugpend) {
		psignal(p, SIGTRAP);
		u.u_debugpend = 0;
	}

	/*
	 * if (p->p_cursig || ISSIG(p))
	 *	psig();
	 *
	 * No need to lock state to test p_sig.
	 */

	if (p->p_cursig)
		psig();
	else if (QUEUEDSIG(p)) {
		p->p_pri = p->p_usrpri;			/* in case stop */
		(void) p_lock(&p->p_state, SPLHI);
		if (issig((lock_t *)NULL) > 0) {
			v_lock(&p->p_state, SPL0);
			psig();
		} else
			v_lock(&p->p_state, SPL0);
	}

	if (u.u_prof.pr_scale) {
		timeval_t lstime;
		int	i;

		DISABLE();
		lstime = u.u_pst.ps_stime;
		ENABLE();
		/* calculate number of ticks in system */
		i = ((lstime.tv_sec - u.u_syst.tv_sec) * 1000 +
			(lstime.tv_usec - u.u_syst.tv_usec) / 1000) / (tick / 1000);
		if (i)
			addupc((int)eip, &u.u_prof, i);
	}

	/*
	 * Going back to user mode so fix p->pri, and e_pri (process may have
	 * blocked at higher priority while in the system-call).
	 */

	p->p_pri = p->p_usrpri;
	l.eng->e_pri = p->p_pri;
	if (arch_type != SYSTYP_S1)
		SLICPRI(l.eng->e_pri>>2);


#ifdef	DEBUG
	if (syscalltrace) {
		if (flags & FLAGS_CF)
			CPRINTF("<%d,%s>:Error %d\n", p-proc, u.u_comm, eax);
		else
			CPRINTF("<%d,%s>:%x,%x ret to %x\n", p-proc,
				u.u_comm, u.u_r.r_val1, u.u_r.r_val2, eip);
	}
#endif	/* DEBUG  */
}


/*
 * sysargs - get system call arguments from user space and place in u-area.
 *
 * We let fuword failures put -1 in the the u_arg array as this is *exactly*
 * the behavior of SVR3.2 on the 386.
 */

static void
sysargs(cnt, sp)
	int 	cnt;		/* number of arguments */
	int	*sp;		/* args on user's stack */
{
	int	i;

	sp++;		/* sp points to the return addr on the user's
			 * stack. bump it up to the actual args.
			 */
	for (i = 0; i < cnt; i++)
		u.u_arg[i] = fuword((char *)sp++);
}

/*
 * Structures needed for sendsig() and sigclean().
 */

struct	_fpstate {
	unsigned long cw,
		      aw,
		      tag,
		      ipoff,
		      cssel,
		      dataoff,
		      datasel;
	struct _fpreg {
		unsigned short significand[4];
		unsigned short exponent;
	} _st[8];
	unsigned long status;
};

/*
 * SUPPORT_FPA is turned off because of the following issues:
 *
 * 1. The SVR3.2 386 code expects 33 longs not the 32 we use.
 * 2. We do not keep state about whether a processes have ever used the fpa
 *    so this makes if very hard to do the sigclean() business.
 * 3. The FPA restore routines need to be looked at carefully for
 *    compatability with SVR3.2 386.
 *
 * These issues should be resolved before trying to turn on the FPA support.
 */

#undef	SUPPORT_FPA

#ifdef	SUPPORT_FPA
struct	_fpastate {
	unsigned long weitek_reg[33];
};
#endif	/* SUPPORT_FPA */

/* 
 * This structure defines what is pushed on the user stack
 * before the kernel returns to the user signal handler.
 *
 * When the signal handler returns, this structure is still
 * on top of the stack except for `retaddr'.
 */

struct stackframe {
	void (*retaddr)();	/* return address for user signal handler */
	int signo;	 	/* signal # as arg */
	int regs[19];		/* all user registers */
	struct _fpstate *fpup;	/* address of pushed fp state on stack */
#ifdef	SUPPORT_FPA
	struct _fpastate *fpap;	/* address of pushed FPA state on stack */
#endif	/* SUPPORT_FPA */
};

/*
 * bcs_sendsig()
 *
 * Send an interrupt to process following the BCS convention.
 *
 * Pass the signal number as an argument to the user signal handler.
 * Also pushed on the user stack is all of the user registers and the
 * ret addr for a piece of code that calls back into the kernel
 * (after executing the user's signal handler) to restore the
 * context of the user process.
 */

void
bcs_sendsig(hand, sig, old_mask)
	void    	(*hand)();
	int		sig;
	sigset_t	old_mask;
{
	register proc_t		*p;
	register uint		usp;
	struct stackframe 	f;

	p = u.u_procp;
	usp = (unsigned)u.u_ar0[ESP];

	/*
	 * If using the FPU, save it.
	 */

	if (l.usingfpu)
		save_fpu();
	usp -= sizeof(struct _fpstate);
	f.fpup = (struct _fpstate *)usp;

#ifdef	SUPPORT_FPA
	/*
	 * If using the FPA, save it.
	 */

	if (p->p_fpa)
		save_fpa();
	usp -= sizeof(struct _fpastate);
	f.fpap = (struct _fpastate *)usp;
#endif	/* SUPPORT_FPA */

	/*
	 * Allocate space on the user stack for
	 * floating point contexts and signal frame.
	 * Failure of grow() will be caught by copyout().
	 */

	usp -= sizeof(struct stackframe);
	(void)grow(usp);

	/*
	 * Fill out copy of signal frame.
	 */

	f.retaddr = u.u_sigreturn;
	f.signo = sig;
	f.regs[0x00] = 0;		/* %gs register */
	f.regs[0x01] = 0;		/* %fs register */
	f.regs[0x02] = 0;		/* %es register */
	f.regs[0x03] = 0;		/* %ds register */
	f.regs[0x04] = u.u_ar0[EDI];
	f.regs[0x05] = u.u_ar0[ESI];
	f.regs[0x06] = u.u_ar0[EBP];
	f.regs[0x07] = u.u_ar0[SP];
	f.regs[0x08] = u.u_ar0[EBX];
	f.regs[0x09] = u.u_ar0[EDX];
	f.regs[0x0a] = u.u_ar0[ECX];
	f.regs[0x0b] = u.u_ar0[EAX];
	f.regs[0x0c] = 0;		/* trap number */
	f.regs[0x0d] = 0;		/* error code */
	f.regs[0x0e] = u.u_ar0[EIP];
	f.regs[0x0f] = u.u_ar0[CS];
	f.regs[0x10] = u.u_ar0[FLAGS];
	f.regs[0x11] = u.u_ar0[ESP];
	f.regs[0x12] = u.u_ar0[SS];

	/*
	 * Special case: user has done a sigpause.  Here the
	 * current mask is not of interest, but rather the
	 * mask from before the sigpause is what we want restored
	 * after the signal processing is completed.
	 * NOTE: we reuse the %gs and %fs register slots for mask and flags.
	 */
	if (u.u_oldmask) {
		f.regs[0x00] = u.u_oldmask;
		f.regs[0x01] = u.u_sa[sig].sa_flags | SA_SUSP;
		SIGM_CLR(u.u_oldmask);
	} else {
		f.regs[0x00] = old_mask;
		f.regs[0x01] = u.u_sa[sig].sa_flags;
	}

	/*
	 * Copy out to the user stack the fpu context, fpa
	 * context, and the stack frame for the signal call.
	 */

	if (copyout((char *)&u.u_fpusave, (char *)f.fpup, sizeof(u.u_fpusave))
#ifdef	SUPPORT_FPA
	||  copyout((char *)&u.u_fpasave, (char *)f.fpap, sizeof(u.u_fpasave))
#endif	/* SUPPORT_FPA */
	||  copyout((char *)&f, (char *)usp, sizeof(f))) {
		/*
		 * force an illegal instruction core dump.
		 */
		u.u_sa[SIGILL].sa_handler = SIG_DFL;
		SIGM_DELSET(p->p_sigignore,sigmask(SIGILL));
		SIGM_DELSET(p->p_sigcatch,sigmask(SIGILL));
		SIGM_DELSET(p->p_sigmask,sigmask(SIGILL));
		psignal(p, SIGILL);
		return;
	}

	/*
	 * Give the signal handler a clean floating point context.
	 */

	disable_fpu();
	u.u_fpusave.fpu_control = FPU_CONTROL_INIT;
	u.u_fpusave.fpu_status = FPU_STATUS_INIT;
	u.u_fpusave.fpu_tag = FPU_TAG_INIT;

	/*
	 * Setup to return to the signal handler.
	 */

	u.u_ar0[EIP] = (int)hand;
	u.u_ar0[ESP] = (int)usp;
	u.u_ar0[FLAGS] &= ~FLAGS_TF;	/* disable single step */
}

/*
 * sigclean()
 *
 * Restore user's context after execution of user signal handler.
 * This code restores all registers to what they were at the time
 * the signal occured (any changes made to user registers/flags will
 * disappear).
 *
 * The saved context is assumed to be on the user stack;
 * If the user has mucked with his stack, he will suffer.
 *
 * Called from locore.s when a lcall through a call gate used for
 * sig_clean occurs.
 */

/*ARGSUSED*/
sigclean(edi, esi, ebp, unused, ebx, edx, ecx, eax, eip, cs, flags, sp, ss)
	volatile uint edi;	/* user register */
	volatile uint esi;	/* user register */
	volatile uint ebp;	/* user frame pointer register */
	volatile uint unused;	/* temp from "pushal" instruction */
	volatile uint ebx;	/* user register */
	volatile uint edx;	/* user register */
	volatile uint ecx;	/* user register */
	volatile uint eax;	/* user register */
	volatile uint eip;	/* user instruction pointer register */
	volatile uint cs;	/* user code segment register */
	volatile uint flags;	/* user flags register */
	volatile uint sp;	/* user stack pointer register */
	volatile uint ss;	/* user stack segment register */
{
	register proc_t		*p;
	register uint		usp;
	struct stackframe 	f;

	/*
	 * User stack pointer upon entry is pointing at stackframe.regs.
	 * Warning, current C library/kernel signal interface has us
	 * fetching the signal number from below the current stack pointer.
	 */

	usp = (unsigned)(u.u_ar0[ESP] - 8);
	p = u.u_procp;

	if (copyin((char *)usp, (char *)&f, sizeof(struct stackframe))
	||  copyin((char *)f.fpup, (char *)&u.u_fpusave, sizeof(u.u_fpusave))
#ifdef	SUPPORT_FPA
	||  copyin((char *)f.fpap, (char *)&u.u_fpasave, sizeof(u.u_fpasave))
#endif	/* SUPPORT_FPA */
	   ) {
		/*
		 * force an illegal instruction core dump.
		 */
		u.u_sa[SIGILL].sa_handler = SIG_DFL;
		SIGM_DELSET(p->p_sigignore, sigmask(SIGILL));
		SIGM_DELSET(p->p_sigcatch, sigmask(SIGILL));
		SIGM_DELSET(p->p_sigmask, sigmask(SIGILL));
		psignal(p, SIGILL);
		return;
	}

	/*
	 * Disable the floating point unit in case the signal handler
	 * used floating point so the next usage will reload from the
	 * restored context.
	 */

	disable_fpu();

	/*
	 * Clear out any exceptions in the floating point context.
	 * This has the same effect of executing the "fnclex" instruction.
	 */

	u.u_fpusave.fpu_status &= 0x7f00;

	/*
	 * CHOLD flag is not set for old style signal().
	 */
	if (!(f.regs[0x01] & SA_NOHOLD)) {
		/*
		 * release the signal.
		 * restore mask for sigaction() set.
		 * or undo the current signal for sigset().
		 */
		(void)p_lock(&p->p_state, SPLHI);
		if (f.regs[0x01] & SA_SIGSET)
			SIGM_DELSET(p->p_sigmask, sigmask(f.signo));
		else
			p->p_sigmask = f.regs[0x00];
		/*
		 * don't trust user's copy.
		 */
		SIGM_DELSET(p->p_sigmask,CANTMASK);
		v_lock(&p->p_state, SPL0);
	} 

	if (f.regs[0x01] & SA_SUSP) {
		/*
		 * Always retore oldmask for routines called while
		 * stopped with sigpause() or sigsuspend().
		 */
		(void)p_lock(&p->p_state, SPLHI);
		p->p_sigmask = f.regs[0x00];
		/*
		 * don't trust user's copy.
		 */
		SIGM_DELSET(p->p_sigmask,CANTMASK);
		v_lock(&p->p_state, SPL0);
	}
	/*
	 * Validate the flags register.
	 */

	f.regs[0x10] &= ~(FLAGS_IOPL | FLAGS_VM);	/* set IOPL, clear VM */
	f.regs[0x10] |= FLAGS_IF;			/* enable interrupts */

	/*
	 * Replace registers with ones from saved context.
	 */

	u.u_ar0[EDI]   = f.regs[0x04];
	u.u_ar0[ESI]   = f.regs[0x05];
	u.u_ar0[EBP]   = f.regs[0x06];
	u.u_ar0[SP]    = f.regs[0x07];
	u.u_ar0[EBX]   = f.regs[0x08];
	u.u_ar0[EDX]   = f.regs[0x09];
	u.u_ar0[ECX]   = f.regs[0x0a];
	u.u_ar0[EAX]   = f.regs[0x0b];
	u.u_ar0[EIP]   = f.regs[0x0e];
	u.u_ar0[CS]    = f.regs[0x0f];
	u.u_ar0[FLAGS] = f.regs[0x10];
	u.u_ar0[ESP]   = f.regs[0x11];
	u.u_ar0[SS]    = f.regs[0x12];

	/*
	 * Set "syscall return values" to correctly fix r0 and r1.
	 */
	u.u_r.r_val1 = f.regs[0x0b];	/* %eax */
	u.u_r.r_val2 = f.regs[0x09];	/* %edx */
}


/*
 * Wrapper and replacement functions.
 */

/*
 * bcs_ssig()
 *
 * The BCS interface passes the "u_sigretrun" value in the EDX register
 * while DYNIX/pts uses the third argument.
 */

bcs_ssig()
{
	u.u_arg[2] = u.u_ar0[EDX];
	u.u_arg[3] = 0;
	ssig();
}


struct	_stat {
	short	_st_dev;	/* was dev_t */
	ushort	_st_ino;	/* was ino_t */
	ushort 	_st_mode;
	short  	_st_nlink;
	ushort 	_st_uid;
	ushort 	_st_gid;
	short	_st_rdev;	/* was dev_t */
	long	_st_size;	/* was off_t */
	long	_st_atime;	/* was time_t */
	long	_st_mtime;	/* was time_t */
	long	_st_ctime;	/* was time_t */
};

#define	DEVTOSHORT(a)	((short)(((a)>>8)&0xff00)|((a)&0xff))
#define	SHORTTODEV(a)	((dev_t)(((a)<<8)&0xff0000)|((a)&0xff))

/*
 * the fstat system call.
 */
bcs_fstat()
{
	register struct file *fp;
	register struct a {
		int	fdes;
		struct	stat *sb;
	} *uap = (struct a *) u.u_ap;
	struct  stat ub;
	struct _stat tb;

	fp = getf(uap->fdes);
	if (fp == 0)
		return;

	/*
	 * Need locked vnode to insure consistent data.
	 */
	VN_LOCKNODE(fp->f_vnode);
	u.u_error = vno_stat((struct vnode *)fp->f_vnode, &ub);
	VN_UNLOCKNODE(fp->f_vnode);

	if (u.u_error == 0) {
		/*
		 * Convert native format to BCS format.
		 */
		tb._st_dev = DEVTOSHORT(ub.st_dev);
		tb._st_ino = (ushort)ub.st_ino;
		tb._st_mode = ub.st_mode;
		tb._st_nlink = ub.st_nlink;
		tb._st_uid = ub.st_uid;
		tb._st_gid = ub.st_gid;
		tb._st_rdev = DEVTOSHORT(ub.st_rdev);
		tb._st_size = ub.st_size;
		tb._st_atime = ub.st_atimeval.tv_sec;
		tb._st_mtime = ub.st_mtimeval.tv_sec;
		tb._st_ctime = ub.st_ctimeval.tv_sec;
		u.u_error = copyout((caddr_t)&tb, (caddr_t)uap->sb, sizeof(tb));
	}
}

/*
 * the stat system call.
 */
bcs_stat()
{
	register struct a {
		char	*fname;
		struct stat *sb;
	} *uap = (struct a *) u.u_ap;
	struct vnode *vp;
	struct  stat ub;
	struct _stat tb;

	u.u_error =
		lookupname(uap->fname, UIOSEG_USER, FOLLOW_LINK,
			(struct vnode **)0, &vp);
	if (u.u_error)
		return;
	u.u_error = vno_stat(vp, &ub);
	VN_PUT(vp);
	if (u.u_error)
		return;
	if (u.u_error == 0) {
		/*
		 * Convert native format to BCS format.
		 */
		tb._st_dev = DEVTOSHORT(ub.st_dev);
		tb._st_ino = (ushort)ub.st_ino;
		tb._st_mode = ub.st_mode;
		tb._st_nlink = ub.st_nlink;
		tb._st_uid = ub.st_uid;
		tb._st_gid = ub.st_gid;
		tb._st_rdev = DEVTOSHORT(ub.st_rdev);
		tb._st_size = ub.st_size;
		tb._st_atime = ub.st_atimeval.tv_sec;
		tb._st_mtime = ub.st_mtimeval.tv_sec;
		tb._st_ctime = ub.st_ctimeval.tv_sec;
		u.u_error = copyout((caddr_t)&tb, (caddr_t)uap->sb, sizeof(tb));
	}
}

/*
 * The utssys() system call.
 */
bcs_utssys()
{
	register struct a {
		char	*cbuf;
		int	mv;
		int	type;
	} *uap = (struct a *)u.u_ap;
	struct	ustat	ub;
	struct _ustat	{
		long	_f_tfree;	/* total free (daddr_t) */
		ushort	_f_tinode;	/* total inodes free (ino_t) */
		char	_f_fname[6];	/* filsys name */
		char	_f_fpack[6];	/* filsys pack name */
	} tb;

	switch (uap->type) {
	case 0:		/* uname */
		if (copyout((char *)&utsname, uap->cbuf,
		    sizeof(struct utsname)))
			u.u_error = EFAULT;
		return;

	/* case 1 was umask */

	case 2:		/* ustat */
		u.u_error = vfs_ustat(SHORTTODEV(uap->mv), &ub);
		if (u.u_error == 0) {
			/*
			 * Convert native format to BCS format.
			 */
			tb._f_tfree = ub.f_tfree;
			tb._f_tinode = (ushort)ub.f_tinode;
			bcopy(ub.f_fname, tb._f_fname, sizeof(ub.f_fname));
			bcopy(ub.f_fpack, tb._f_fpack, sizeof(ub.f_fpack));
			u.u_error = copyout((caddr_t)&tb,
					uap->cbuf, sizeof(tb));
		}
		return;

	/* case 3 setuname() in libc but SVR3 kernel has no support */

	default:
		u.u_error = EFAULT;
	}
}

/*
 * Structure returned by the BCS statfs() system call.
 */

struct	_statfs {
	short	_f_fstyp;	/* File system type */
	long	_f_bsize;	/* Block size */
	long	_f_frsize;	/* Fragment size (if supported) */
	long	_f_blocks;	/* Total number of blocks on file system */
	long	_f_bfree;	/* Total number of free blocks */
	long	_f_files;	/* Total number of file nodes (inodes) */
	long	_f_ffree;	/* Total number of free file nodes */
	char	_f_fname[6];	/* Volume name */
	char	_f_fpack[6];	/* Pack name */
};

/*
 * get filesystem statistics
 */
bcs_statfs()
{
	register struct a {
		char	*fname;
		struct	_statfs *sb;
		int	len;
		int	fstyp;
	} *uap = (struct a *)u.u_ap;
	struct vnode *vp;

	u.u_error =
	    lookupname(uap->fname, UIOSEG_USER, FOLLOW_LINK,
		(struct vnode **)0, &vp);
	if (u.u_error)
		return;
	bcs_cstatfs(vp, uap->sb, uap->len, uap->fstyp);
	VN_PUT(vp);
}

bcs_fstatfs()
{
	register struct a {
		int	fd;
		struct	_statfs *sb;
		int	len;
		int	fstyp;
	} *uap = (struct a *)u.u_ap;
	register struct vnode *vp;
	struct file *fp;

	u.u_error = getvnodefp(uap->fd, &fp);
	if (u.u_error)
		return;
	vp = fp->f_vnode;
	VN_LOCKNODE(vp);
	bcs_cstatfs(vp, uap->sb, uap->len, uap->fstyp);
	VN_UNLOCKNODE(vp);
}

bcs_cstatfs(vp, ubuf, len, fstyp)
	struct vnode *vp;
	struct _statfs *ubuf;
	int len, fstyp;
{
	struct  statfs sb;
	struct _statfs tb;

	if (len < 0 || len > sizeof(tb) || fstyp < 0 || fstyp >= mount_ntypes) {
		u.u_error = EINVAL;
		return;
	}
	if (fstyp && vp->v_type != VCHR && vp->v_type != VBLK) {
		u.u_error = EINVAL;
		return;
	}
	if (fstyp)
		u.u_error = (*(vfssw[fstyp])->vfs_statfs)((struct vfs *)0,
					vp, &sb);
	else {
		/*
		 * Let's avoid dereferencing vp->v_vfsp if it's NULL.
		 * This happens when fstatfs() was called with an
		 * fd that was returned from a clone device.
		 */
		if (vp->v_vfsp == (struct vfs *)NULL) {
			u.u_error = EINVAL;
			return;
		}
		u.u_error = VFS_STATFS(vp->v_vfsp, (struct vnode *)0, &sb);
	}
	if (u.u_error)
		return;
	/*
	 * Convert native format to BCS format.
	 */
	tb._f_fstyp = sb.f_fstyp;
	tb._f_bsize = sb.f_bsize;
	tb._f_frsize = sb.f_frsize;
	tb._f_blocks = sb.f_blocks;
	tb._f_bfree = sb.f_bfree;
	tb._f_files = sb.f_files;
	tb._f_ffree = sb.f_ffree;
	bcopy(sb.f_fname, tb._f_fname, sizeof(sb.f_fname));
	bcopy(sb.f_fpack, tb._f_fpack, sizeof(sb.f_fpack));
	u.u_error = copyout((caddr_t)&tb, (caddr_t)ubuf, (unsigned int)len);
}

/*
 * ptrace()
 *	process-trace system call (BCS style).
 */

#define	BCS_PT_U_SIGRETURN	0x11ec
#define	BCS_PT_U_AR0		0x1200
#define	BCS_PT_U_FPVALID	0xe82
#define	BCS_PT_U_FPSTATE	0xe84

#ifdef	DEBUG
#define	BCS_PTRACE_DEBUG
#endif	/* DEBUG */

#ifdef	BCS_PTRACE_DEBUG
int	bcs_ptrace_debug = 0;
#define	BCS_PTRACE_DBG1 \
	if (bcs_ptrace_debug) \
		CPRINTF("%s: req %d pid %d addr 0x%x data 0x%x ", \
			u.u_comm, uap->req, uap->pid, uap->addr, uap->data)
#define	BCS_PTRACE_DBG2 \
	if (bcs_ptrace_debug) \
		CPRINTF("ret 0x%x errno %d\n", u.u_r.r_val1, u.u_error)
#else	/* !BCS_PTRACE_DEBUG */
#define	BCS_PTRACE_DBG1
#define	BCS_PTRACE_DBG2
#endif	/* BCS_PTRACE_DEBUG */

bcs_ptrace()
{
	struct a {
		int	req;
		int	pid;
		caddr_t	addr;
		int	data;
	} *uap = (struct a *)u.u_ap;
	register int i;
	int	mapped = 1;

	BCS_PTRACE_DBG1;

	/*
	 * If request does not deal with u-area,
	 * just call native ptrace().
	 */

	if (uap->req != PT_RUSER && uap->req != PT_WUSER) {
		ptrace();
		BCS_PTRACE_DBG2;
		return;
	}

	/*
	 * Map BCS u-area offsets into native u-area offsets.
	 */

	switch ((int)uap->addr) {

	case 0: /*
		 * Special case: pass address 0 on to native ptrace to
		 * pass SVVS3.  
		 */
		break;

	case BCS_PT_U_SIGRETURN:
		uap->addr = (caddr_t)&((struct user *)0)->u_sigreturn;
		break;

	case BCS_PT_U_AR0:
		uap->addr = (caddr_t)&((struct user *)0)->u_ar0;
		break;

	case BCS_PT_U_FPVALID:
		if (uap->req == PT_RUSER) {
			/*
			 * NOTE: u_fpvalid is a `char' variable but
			 * ptrace reads in double words making the
			 * following magic is necessary.
			 */
			u.u_r.r_val1 = 1 << ((BCS_PT_U_FPVALID & 3)*8);
		} else
			u.u_error = EIO;
		BCS_PTRACE_DBG2;
		return;

	default:
		mapped = 0;
		break;
	}

	/*
	 * If address mapped, call native ptrace().
	 */

	if (mapped) {
		ptrace();
		BCS_PTRACE_DBG2;
		return;
	}

	/*
	 * Check for accesses to general registers
	 */

	mapped = 1;
	i = (int)uap->addr - (int)u.u_ar0;
	switch (i) {

	/*
	 * Requests for unsupported registers return zero
	 * on reads and writes but do nothing.
	 */

	case 0*4:	/* %gs */
	case 1*4:	/* %fs */
	case 2*4:	/* %es */
	case 3*4:	/* %ds */
	case 12*4:	/* trapno */
	case 13*4:	/* err */
		BCS_PTRACE_DBG2;
		return;

	/*
	 * Map u-area offsets for standard registers
	 */

	case  4*4: uap->addr = (caddr_t)&u.u_ar0[EDI]; break;
	case  5*4: uap->addr = (caddr_t)&u.u_ar0[ESI]; break;
	case  6*4: uap->addr = (caddr_t)&u.u_ar0[EBP]; break;
	case  7*4: uap->addr = (caddr_t)&u.u_ar0[EBP+1]; break;
	case  8*4: uap->addr = (caddr_t)&u.u_ar0[EBX]; break;
	case  9*4: uap->addr = (caddr_t)&u.u_ar0[EDX]; break;
	case 10*4: uap->addr = (caddr_t)&u.u_ar0[ECX]; break;
	case 11*4: uap->addr = (caddr_t)&u.u_ar0[EAX]; break;
	case 14*4: uap->addr = (caddr_t)&u.u_ar0[EIP]; break;
	case 15*4: uap->addr = (caddr_t)&u.u_ar0[CS]; break;
	case 16*4: uap->addr = (caddr_t)&u.u_ar0[FLAGS]; break;
	case 17*4: uap->addr = (caddr_t)&u.u_ar0[ESP]; break;
	case 18*4: uap->addr = (caddr_t)&u.u_ar0[SS]; break;

	default:
		mapped = 0;
		break;
	}

	/*
	 * If address mapped, call native ptrace().
	 */

	if (mapped) {
		ptrace();
		BCS_PTRACE_DBG2;
		return;
	}

	/*
	 * Check for access to the floating point registers
	 */

	i = (int)uap->addr - BCS_PT_U_FPSTATE;
	if ((unsigned)i < sizeof(struct fpusave)) {
		uap->addr = (caddr_t)((int)&((struct user *)0)->u_fpusave+i);
		ptrace();
		BCS_PTRACE_DBG2;
		return;
	}

	/*
	 * The SVR3 kernel declares the fpusave area to include an extra
	 * 4 bytes for a temporary copy of the status register. 
	 */

	if (i == sizeof(struct fpusave)) {
		BCS_PTRACE_DBG2;
		return;
	}

	/*
	 * We have failed to recognize this ptrace offset.
	 * Return a suitable error.
	 */

	u.u_error = EIO;
	BCS_PTRACE_DBG2;
	return;
}

/*
 * bcs_wait
 *
 * Attempt to fix the following problem:
 *
 *	printf("%d\n", system("exit 255")); 
 *	DYNIX/ptx prints    65280
 *	AT&T System V prints -255
 *
 * This incompatability is because DYNIX/ptx stores the status of a ZOMBIE
 * in a "u_short" instead of a "short" and thus sign extension does not
 * occur in the wait system call.  Thus, we sign extend the return value
 * here to compensate but only if u.u_r.r_val1 contains a valid return pid.
 */

bcs_wait()
{
	wait();
	if (u.u_r.r_val1)
		u.u_r.r_val2 = (short)u.u_r.r_val2;
}
