#include <9pm/u.h>
#include <9pm/libc.h>
#include "dat.h"
#include "fns.h"

static int ndev;
Dev *devtab[32];

void
adddev(Dev *d)
{
	int i;

	for(i=0; devtab[i]; i++)
		if(devtab[i]==d)
			return;

	if(ndev >= nelem(devtab)-1)
		pm_panic("too many devs");
	devtab[ndev++] = d;
	devtab[ndev] = nil;
	(*d->reset)();
}

long
copyproc(Proc *p, int flag)
{
	Proc *xp;
	ulong arg;
	long ret;

	xp = up;
	up = p;
	arg = RFPROC|flag;
	ret = sysrfork(&arg);
	up = xp;
	return ret;
}

Proc*
findproc(long pid)
{
	int s;
	Proc *p;

	if(pid==0){
		s = procindex(1);
		if(s==-1 || (p=procalloc.arena[s])==nil)
			panic("findproc");
		pid = copyproc(p, RFNOTEG|RFCFDG);
	}
	s = procindex(pid);
	if(s==-1 || (p=procalloc.arena[s])==nil || !canlock(&p->nascent))
		return nil;
	return p;
}

/*
 * The first proc never runs.  It exists only to be cloned.
 */
void
userinit(void)
{
	Proc *p;

	p = newproc();
	p->pgrp = newpgrp();
	p->egrp = smalloc(sizeof(Egrp));
	p->egrp->ref.ref = 1;
	p->fgrp = dupfgrp(nil);
	p->rgrp = newrgrp();
	p->procmode = 0640;

	kstrdup(&p->text, "adam");
	kstrdup(&p->user, eve);

	p->state = Nascent;
	lock(&p->nascent);

	up = p;
}

Systab systab[Nsyscall] = {
	{ sysr1,		"",		"Sysr1" },
	{ sysbind,		"ssl",		"Bind" },
	{ syschdir,		"s",		"Chdir" },
	{ sysclose,		"l",		"Close" },
	{ sysdup,		"ll",		"Dup" },
	{ sysalarm,	"l",		"Alarm" },
	{ sysexec,		"sS",		"Exec" },
	{ sysexits,		"s",		"Exits" },
	{ sysfauth,		"ls",		"Fauth" },
	{ sysopen,		"sl",		"Open" },
	{ syssleep,		"l",		"Sleep" },
	{ sysrfork,		"l",		"Rfork" },
	{ syspipe,		"f",		"Pipe" },
	{ syscreate,	"sll",		"Create" },
	{ sysfd2path,	"lb",		"Fd2path" },
	{ sysfd2path,	"lb",		"Fd2syspath" },
	{ sysremove,	"s",		"Remove" },
	{ sysnoted,	"l",		"Noted" },
	{ sysrendezvous, "ll",	"Rendez" },
	{ sysunmount,	"ns",		"Unmount" },
	{ sysseek,		"Vlvl",	"Seek" },
	{ sysfversion,	"llb",		"Fversion" },
	{ syserrstr,	"b",		"Errstr" },
	{ sysstat,		"sb",		"Stat" },
	{ sysfstat,		"lb",		"Fstat" },
	{ syswstat,	"sb",		"Wstat" },
	{ sysfwstat,	"lb",		"Fwstat" },
	{ sysmount,	"llsls",	"Mount" },
	{ sysawait,	"b",		"Await" },
	{ syspread,	"lbv",	"Pread" },
	{ syspwrite,	"lbv",	"Pwrite" },
	{ syspassfd,	"ll",		"Passfd" },
	{ syspassdir,	"s",		"Passdir" },
};

static char Eaddr[] = "invalid address in system call";

void
validargs(ulong *arg, void *vs, void *ve, char *fmt)
{
	char *p;
	ulong *a, *b, *c, *s, *e;
	ulong n;

	if(fmt == nil)
		error("bad fmt for system call (bug)");

	s = vs;
	e = ve;
	for(p=fmt; *p; p++){
		switch(*p){
		default:
			error("unknown fmt char (bug)");
		case 'l':		/* integer */
			arg++;
			break;
		case 'n':		/* possibly null string pointer */
			if(*arg == ~0){
				*arg = 0;
				break;
			}
			/* fall through */
		case 's':		/* string pointer */
			*arg += (ulong)s;
			a = (ulong*)(*arg++);
			if(a < s || a >= e || memchr(a, 0, (uchar*)e-(uchar*)a) == nil)
				error(Eaddr);
			break;
		case 'S':		/* pointer to zero-terminated array of string pointers */
			*arg += (ulong)s;
			a = (ulong*)(*arg++);
			if(a < s || ((ulong)a&(sizeof(ulong)-1)))
				error(Eaddr);
			for(b=a; b<e; b++){
				if(*b == ~0){
					*b = 0;
					break;
				}
				*b += (ulong)s;
				c = (ulong*)*b;
				if(c < s || c >= e || memchr(c, 0, (uchar*)e-(uchar*)c) == nil)
					error(Eaddr);
			}
			if(b >= e)
				error(Eaddr);
			break;
		case 'V':		/* pointer to vlong */
		case 'f':		/* pointer to two integers */
			*arg += (ulong)s;
			a = (ulong*)(*arg++);
			if(a < s || a+2 > e)
				error(Eaddr);
			break;
		case 'v':		/* vlong */
			arg += 2;
			break;
		case 'b':		/* buffer: pointer followed by length */
			*arg += (ulong)s;
			a = (ulong*)(*arg++);
			n = *arg++;
			if(a < s || (uchar*)a+n > (uchar*)e)
				error(Eaddr);
			break;
		}
	}
}

/*
 * These don't have any effect on whether we can get preempted
 * for another process, of course, but they do change our behavior
 * when spinning for a test-and-set lock: if we are spl hi, we call
 * ossched to yield the processor after glaring for a little while.
 * Probably this is a kludge and what we should really do is always yield.
 */
int
splhi(void)
{
	int s;

	s = M->spl;
	M->spl = 1;
	return s;
}

int
spllo(void)
{
	int s;

	s = M->spl;
	M->spl = 0;
	return s;
}

int
islo(void)
{
	return M->spl==0;
}

void
splx(int s)
{
	M->spl = s;
}
