#include	"u.h"
#include	"tos.h"
#include	"../port/lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"io.h"
#include	"pool.h"
#include	"../ip/ip.h"
#include	<../port/error.h>

static FPsave initfp;

/*
 * software tlb simulation
 */
static Softtlb stlb[MAXMACH][STLBSIZE];

Conf	conf;

char*
getconf(char *name)
{
	return (char*)arcs(0x78, name);
}

static void
fmtinit(void)
{
	printinit();
	quotefmtinstall();
	/* ipreset installs these when chandevreset runs */
	fmtinstall('i', eipfmt);
	fmtinstall('I', eipfmt);
	fmtinstall('E', eipfmt);
	fmtinstall('V', eipfmt);
	fmtinstall('M', eipfmt);
}

static int
ckpagemask(ulong mask, ulong size)
{
	int s;
	ulong pm;

	s = splhi();
	setpagemask(mask);
	pm = getpagemask();
	splx(s);
	if(pm != mask){
		iprint("page size %ldK not supported on this cpu; "
			"mask %#lux read back as %#lux\n", size/1024, mask, pm);
		return -1;
	}
	return 0;
}

void
addmem(uintptr base, uintptr top)
{
	uintptr s, e;
	ulong *m;
	int i;

	if(base >= top)
		return;

	/* exclude kernel */
	s = 0;
	e = PADDR(PGROUND((uintptr)end));
	if(s < top && e > base){
		if(s > base)
			addmem(base, s);
		if(e < top)
			addmem(e, top);
		return;
	}

	/* exclude reserved firmware memory regions */
	m = nil;
	while((m = (ulong*)arcs(0x48, m)) != nil){
		s = m[1]<<12;
		e = s + (m[2]<<12);
		switch(m[0]){
		case 2:	/* FreeMemory */
		case 3:	/* BadMemory */
			continue;
		}
		if(s < top && e > base){
			if(s > base)
				addmem(base, s);
			if(e < top)
				addmem(e, top);
			return;
		}
	}
	for(i=0; i<nelem(conf.mem); i++){
		if(conf.mem[i].npage == 0){
			conf.mem[i].base = base;
			conf.mem[i].npage = (top - base)/BY2PG;

			conf.npage += conf.mem[i].npage;
			return;
		}
	}
	print("conf.mem[] too small\n");
}

/* 
 * get memory configuration word for a bank
 */
ulong
bank_conf(int bank)
{
	switch(bank){
	case 0:
		return *(ulong *)(KSEG1|MEMCFG0) >> 16;
	case 1:
		return *(ulong *)(KSEG1|MEMCFG0) & 0xffff;
	case 2:
		return *(ulong *)(KSEG1|MEMCFG1) >> 16;
	case 3:
		return *(ulong *)(KSEG1|MEMCFG1) & 0xffff;
	}
	return 0;
}

void
meminit(void)
{
	uintptr base, size, top;
	ulong mconf;
	int i;

	/*
	 *  divide memory twixt user pages and kernel.
	 */
	conf.npage = 0;
	for(i=0; i<4; i++){
		mconf = bank_conf(i);
		if(!(mconf & 0x2000))
			continue;
		base = (mconf & 0xff) << 22;
		size = ((mconf & 0x1f00) + 0x0100) << 14;
		top = base + size;
		addmem(base, top);
	}
}

static int
havegfx(void)
{
	char *s = getconf("ConsoleOut");
	return s != nil && strstr(s, "video()") != nil;
}

void
main(void)
{
	savefpregs(&initfp);

	arcsconsinit();

	meminit();
	confinit();
	machinit();			/* calls clockinit */
	active.exiting = 0;
	active.machs[0] = 1;
	print("\nPlan 9\n");

	kmapinit();
	xinit();
	timersinit();
	fmtinit();

	if(havegfx()){
		conf.monitor = 1;
		screeninit();
	}

	ckpagemask(PGSZ, BY2PG);
	tlbinit();
	pageinit();
	procinit0();
	initseg();
	links();
	chandevreset();
	userinit();
	schedinit();
}

/*
 *  initialize a processor's mach structure.  each processor does this
 *  for itself.
 */
void
machinit(void)
{
	extern void gevector(void);	/* l.s */
	extern void utlbmiss(void);
	extern void vector0(void);
	extern void vector180(void);

	void **sbp = (void*)SPBADDR;

	m->stb = stlb[m->machno];

	/* install exception handlers */
	sbp[0x18/4] = utlbmiss;
	sbp[0x14/4] = gevector;

	/* we could install our own vectors directly, but we'll try to play nice */
	if(0){
		memmove((void*)(KSEG0+0x0), (void*)vector0, 0x80);
		memmove((void*)(KSEG0+0x180), (void*)vector180, 0x80);
		icflush((void*)(KSEG0+0x0), 0x80);
		icflush((void*)(KSEG0+0x180), 0x80);
	}

	/* Ensure CU1 is off */
	clrfpintr();
	clockinit();
}

void
init0(void)
{
	char buf[128], **sp;

	chandevinit();

	if(!waserror()){
		ksetenv("cputype", "mips", 0);
		snprint(buf, sizeof buf, "mips %s", conffile);
		ksetenv("terminal", buf, 0);
		if(cpuserver)
			ksetenv("service", "cpu", 0);
		else
			ksetenv("service", "terminal", 0);

		ksetenv("bootargs", "tcp", 0);
		ksetenv("console", "0", 0);

		/* no usb */
		ksetenv("usbwait", "0", 0);
		ksetenv("nousbrc", "1", 0);

		poperror();
	}

	/* process input for arcs console */
	if(!conf.keyboard)
		kproc("arcs", arcsproc, 0);

	kproc("alarm", alarmkproc, 0);

	sp = (char**)(USTKTOP-sizeof(Tos) - 8 - sizeof(sp[0])*4);
	sp[3] = sp[2] = sp[1] = nil;
	strcpy(sp[0] = (char*)&sp[4], "boot");
	touser(sp);
}

void
exit(int)
{
	cpushutdown();
	splhi();

	/* clear secrets */
	zeroprivatepages();
	poolreset(secrmem);

	arcs(0x18);	/* reboot */
}

void
reboot(void *, void *, ulong)
{
}

void
evenaddr(uintptr va)
{
	if((va & 3) != 0){
		dumpstack();
		postnote(up, 1, "sys: odd address", NDebug);
		error(Ebadarg);
	}
}

void
procsetup(Proc *p)
{
	p->fpstate = FPinit;
	memmove(p->fpsave, &initfp, sizeof(FPsave));
}

void
procfork(Proc *p)
{
	int s;

	s = splhi();
	switch(up->fpstate & ~FPnotify){
	case FPactive:
		savefpregs(up->fpsave);
		up->fpstate = FPinactive;
		/* wet floor */
	case FPinactive:
		memmove(p->fpsave, up->fpsave, sizeof(FPsave));
		p->fpstate = FPinactive;
	}
	splx(s);
}

void
procsave(Proc *p)
{
	if(p->fpstate == FPactive){
		if(p->state != Moribund) {
			savefpregs(p->fpsave);
			p->fpstate = FPinactive;
		}
	}
}

void
procrestore(Proc *)
{
}

void
idlehands(void)
{
}

void
confinit(void)
{
	ulong kpages;

	/*
	 *  set up CPU's mach structure
	 *  cpu0's was zeroed in l.s and our stack is in Mach, so don't zero it.
	 */
	m->machno = 0;
	m->speed = 150;			/* initial guess at MHz */
	m->hz = m->speed * Mhz;
	conf.nmach = 1;

	/* set up other configuration parameters */
	conf.nproc = 2000;
	conf.nswap = 262144;
	conf.nswppo = 4096;
	conf.nimage = 200;

	conf.copymode = 0;		/* copy on write */

	kpages = conf.npage - (conf.npage*80)/100;
	if(kpages > (64*MB + conf.npage*sizeof(Page))/BY2PG){
		kpages = (64*MB + conf.npage*sizeof(Page))/BY2PG;
		kpages += (conf.nproc*KSTACK)/BY2PG;
	}
	conf.upages = conf.npage - kpages;
	conf.ialloc = (kpages/2)*BY2PG;

	kpages *= BY2PG;
	kpages -= conf.upages*sizeof(Page)
		+ conf.nproc*sizeof(Proc*)
		+ conf.nimage*sizeof(Image)
		+ conf.nswap
		+ conf.nswppo*sizeof(Page*);
	mainmem->maxsize = kpages;
	imagmem->maxsize = kpages;
//	mainmem->flags |= POOL_PARANOIA;
}

void
setupwatchpts(Proc *, Watchpt *, int n)
{
	if(n > 0)
		error("no watchpoints");
}

int
isaconfig(char *, int, ISAConf*)
{
	return 0;
}
