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

Conf conf;

enum { MAXCONF = 64 };

char *confname[MAXCONF], *confval[MAXCONF];
int nconf;

void
exit(int)
{
	cpushutdown();
	splhi();
	if(m->machno == 0){
		/* clear secrets */
		zeroprivatepages();
		poolreset(secrmem);
	}
	for(;;) idlehands();
}

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

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

ulong *l2;

void
l2init(void)
{
	enum {
		CTRL = 0x100/4,
		AUX,
		TAGRAM,
		DATARAM,
		INVPA = 0x770/4,
		INVWAY = 0x77C/4,
		PREFETCH = 0xF60/4,
	};

	mpcore[0] |= 1;
	slcr[0xa1c/4] = 0x020202;
	l2 = vmap(L2_BASE, BY2PG);
	l2[CTRL] &= ~1;
	l2[TAGRAM] = l2[TAGRAM] & ~0x777 | 0x111;
	l2[DATARAM] = l2[DATARAM] & ~0x777 | 0x121;
	l2[PREFETCH] |= 3<<28 | 1<<24;
	l2[AUX] |= 3<<28 | 1<<20;
	l2[INVWAY] = 0xff;
	while((l2[INVPA] & 1) != 0)
		;
	l2[CTRL] = 1;
}

void
clean2pa(uintptr start, uintptr end)
{
	uintptr pa;
	
	start &= ~31;
	end = (end + 31) & ~31;
	for(pa = start; pa < end; pa += 32)
		l2[0x7b0/4] = pa;
}

void
inval2pa(uintptr start, uintptr end)
{
	uintptr pa;
	
	start &= ~31;
	end = (end + 31) & ~31;
	for(pa = start; pa < end; pa += 32)
		l2[0x770/4] = pa;
}

void
dmaflush(int clean, void *data, ulong len)
{
	uintptr va, pa;

	va = (uintptr)data & ~31;
	pa = PADDR(va);
	len = ROUND(len, 32);
	if(clean){
		/* flush cache before write */
		cleandse((uchar*)va, (uchar*)va+len);
		clean2pa(pa, pa+len);
	} else {
		/* invalidate cache before read */
		invaldse((uchar*)va, (uchar*)va+len);
		inval2pa(pa, pa+len);
	}
}

static void
options(void)
{
	long i, n;
	char *cp, *line[MAXCONF], *p, *q;

	cp = (char *) CONFADDR;

	p = cp;
	for(q = cp; *q; q++){
		if(*q == '\r')
			continue;
		if(*q == '\t')
			*q = ' ';
		*p++ = *q;
	}
	*p = 0;

	n = getfields(cp, line, MAXCONF, 1, "\n");
	for(i = 0; i < n; i++){
		if(*line[i] == '#')
			continue;
		cp = strchr(line[i], '=');
		if(cp == nil)
			continue;
		*cp++ = '\0';
		confname[nconf] = line[i];
		confval[nconf] = cp;
		nconf++;
	}

}

void
confinit(void)
{
	ulong kmem;
	int i;

	conf.nmach = 1;
	conf.nproc = 2000;
	conf.ialloc = 16*1024*1024;
	conf.nimage = 200;
	conf.mem[0].base = PGROUND((ulong)end - KZERO);
	conf.mem[0].npage = (1024*1024*1024 - conf.mem[0].base) >> PGSHIFT;

	ramdiskinit();

	conf.npage = 0;
	for(i = 0; i < nelem(conf.mem); i++)
		conf.npage += conf.mem[i].npage;

	kmem = 200*1024*1024;
	conf.upages = conf.npage - kmem/BY2PG;
	kmem -= conf.upages*sizeof(Page)
		+ conf.nproc*sizeof(Proc*)
		+ conf.nimage*sizeof(Image);
	mainmem->maxsize = kmem;
	imagmem->maxsize = kmem - (kmem/10);
}

void
init0(void)
{
	char buf[ERRMAX], **sp;
	int i;

	chandevinit();
	
	if(!waserror()){
		ksetenv("cputype", "arm", 0);
		if(cpuserver)
			ksetenv("service", "cpu", 0);
		else
			ksetenv("service", "terminal", 0);
		ksetenv("console", "0", 0);
		snprint(buf, sizeof(buf), "zynq %s", conffile);
		ksetenv("terminal", buf, 0);
		for(i = 0; i < nconf; i++){
			if(*confname[i] != '*')
				ksetenv(confname[i], confval[i], 0);
			ksetenv(confname[i], confval[i], 1);
		}
		poperror();
	}
	kproc("alarm", alarmkproc, 0);

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

void
sanity(void)
{
	static int dat = 0xdeadbeef;
	extern ulong vectors[];

	assert(dat == 0xdeadbeef);
	assert(((uintptr)vectors & 31) == 0);
	assert(sizeof(Mach) + KSTACK <= MACHSIZE);
	assert((KZERO & SECSZ - 1) == 0);
}

char *
getconf(char *n)
{
	int i;
	
	for(i = 0; i < nconf; i++)
		if(cistrcmp(confname[i], n) == 0)
			return confval[i];
	return nil;
}

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

void
cpuidprint(void)
{
	print("cpu%d: %dMHz ARM Cortex-A9\n", m->machno, m->cpumhz);
}

void
mpinit(void)
{
	extern void mpbootstrap(void);	/* l.s */
	Mach *m1;
	ulong *v;
	int i;

	if(getconf("*nomp"))
		return;

	conf.nmach = 2;
	conf.copymode = 1;

	m1 = MACHP(1);
	memset(m1, 0, MACHSIZE);
	m1->machno = 1;
	m1->l1.pa = MACHL1(m1->machno)-KZERO;
	m1->l1.va = KADDR(m1->l1.pa);

	memset(m1->l1.va, 0, L1SZ);
	for(i=0; i<L1X(VMAPSZ); i++)
		m1->l1.va[L1X(VMAP)+i] = m->l1.va[L1X(VMAP)+i];
	for(i=0; i<L1X(-KZERO); i++)
		m1->l1.va[L1X(KZERO)+i] = m->l1.va[L1X(KZERO)+i];
	coherence();
	cleandse((uchar*)KZERO, (uchar*)0xFFFFFFFF);
	invaldse((uchar*)KZERO, (uchar*)0xFFFFFFFF);

	/* ocm is uncached */
	v = KADDR(0xFFFFF000);
	v[0xFF0/4] = PADDR(mpbootstrap);
	coherence();

	sendevent();
	synccycles();
}

void
main(void)
{
	active.machs[m->machno] = 1;
	if(m->machno != 0){
		uartputs("\n", 1);
		mmuinit();
		intrinit();
		timerinit();
		cpuidprint();
		synccycles();
		timersinit();
		schedinit();
	}
	uartconsinit();
	mmuinit();
	l2init();
	intrinit();
	options();
	confinit();
	timerinit();
	uartputs(" from Bell Labs\n", 16);
	xinit();
	printinit();
	quotefmtinstall();
	cpuidprint();
	sanity();
	mpinit();
	todinit();
	timersinit();
	procinit0();
	initseg(); 
	links();
	archinit();
	chandevreset();
	pageinit();
	screeninit();
	userinit();
	schedinit();
}

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