#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;
int normalprint, delaylink;

enum { MAXCONF = 64 };

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

void
exit(int)
{
       cpushutdown();
       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);
       }
}

void
procfork(Proc *p)
{
       ulong s;

       p->kentry = up->kentry;
       p->pcycles = -p->kentry;

       s = splhi();
       switch(up->fpstate & ~FPillegal){
       case FPactive:
               fpsave(up->fpsave);
               up->fpstate = FPinactive;
       case FPinactive:
               memmove(p->fpsave, up->fpsave, sizeof(FPsave));
               p->fpstate = FPinactive;
       }
       splx(s);
}

void
procsetup(Proc *p)
{
       p->fpstate = FPinit;
       fpoff();

       cycles(&p->kentry);
       p->pcycles = -p->kentry;
}

void
kexit(Ureg *)
{
       Tos *tos;
       uvlong t;

       tos = (Tos*)(USTKTOP-sizeof(Tos));
       cycles(&t);
       tos->kcycles += t - up->kentry;
       tos->pcycles = t + up->pcycles;
       tos->pid = up->pid;
}

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();
       uartconsole();

       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();
               return;
       }
       uartinit();
       mmuinit();
       l2init();
       intrinit();
       options();
       confinit();
       timerinit();
       uartputs(" from Bell Labs\n", 16);
       xinit();
       printinit();
       quotefmtinstall();
       cpuidprint();
       sanity();
       mpinit();
       todinit();
       timersinit();
       procinit0();
       initseg();
       if(delaylink)
               bootlinks();
       else
               links();
       archinit();
       chandevreset();
       pageinit();
       screeninit();
       userinit();
       schedinit();
}

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