/*-=-=-=-=-=-=-=-=-=-=-=- RCCL/RCI Notice of Copyright -=-=-=-=-=-=-=-=-=-=-*
 *									    *
 *	Copyright (C) 1987, 1991 by Mike Parker.			    *
 *									    *
 *	Information on copyright permissions, as well as a complete	    *
 *	disclaimer of warranty, is given in the file COPYRIGHT.		    *
 *									    *
 *-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/

/* #define DEBUG_GROWSTACK /* define to get debug messages from growstack() */

#define LOCORE /* shut off the structs which (&^^@&#!!) require types.h */
#include <vax/cpu.h>
#undef LOCORE
#include <vax/pte.h>
#include <vax/mtpr.h>

#include "../sys/rciext/kacomm.h"

#define STACKSIZE (256*1024) /* max stack size, bytes */
#define GROWROOM (2*1024) /* slop to add when growing stack */

extern int cpu;

char *havephys;
int *p0ptes;
int *p1ptes;
int p1nptes;
int p0aptes;
int p1aptes;
int breakpt;

char *alloc();

setkmap(v,p,npg)
char *v;
int p;
int npg;
{
 int s;
 int *kptep;
 int vp;

 s = splhigh();
 p >>= 9;
 vp = (((int)v) & ~0x80000000) >> 9;
 kptep = (int *) (mfpr(SBR) | 0x80000000);
 for (;npg>0;npg--)
  { kptep[vp++] = PG_V | PG_KW | p++;
  }
 splx(s);
}

inituserpt()
{
 p0aptes = 0;
 p0ptes = (int *) alloc(0);
 havephys = (char *) alloc(0);
 p1nptes = 0;
 p1aptes = 0;
 p1ptes = (int *) alloc(0);
 breakpt = 0;
 loadp01regs();
}

growstack(vaddr)
int vaddr;
{
 int s;
 int stackbot;

#ifdef DEBUG_GROWSTACK
 printf("growstack: vaddr = %08x\n",vaddr);
#endif
 stackbot = 0x80000000 - (p1nptes << 9);
#ifdef DEBUG_GROWSTACK
 printf("growstack: stackbot = %08x\n",stackbot);
#endif
 if ((vaddr >= stackbot) || (vaddr < 0x80000000-STACKSIZE))
  {
#ifdef DEBUG_GROWSTACK
    printf("growstack: vaddr out of stack growth area [%08x-%08x]\n",
					0x80000000-STACKSIZE,stackbot);
#endif
    return(0);
  }
 stackbot = vaddr - GROWROOM;
 if (stackbot < 0x80000000-STACKSIZE)
  { stackbot = 0x80000000 - STACKSIZE;
  }
#ifdef DEBUG_GROWSTACK
 printf("growstack: new stackbot = %08x\n",stackbot);
#endif
 setuserstack(0x80000000-stackbot);
 return(1);
}

setuserstack(size)
int size;
{
 int pgs;
 int pf;
 int s;

 s = splhigh();
#ifdef DEBUG_GROWSTACK
 printf("setuserstack: size = %d\n",size);
#endif
 pgs = (size + 511) >> 9;
#ifdef DEBUG_GROWSTACK
 printf("setuserstack: pgs = %d, p1nptes = %d, p1aptes = %d\n",
						pgs,p1nptes,p1aptes);
#endif
 if (pgs > p1nptes)
  { if (pgs > p1aptes)
     { int newn;
       int *new;
#ifdef DEBUG_GROWSTACK
       printf("setuserstack: allocating new ptes\n");
#endif
       newn = (pgs + 127) & ~127;
       new = (int *) alloc(newn<<2);
       if (new == 0)
	{ panic("setuserstack: can't get ptes");
	}
       bcopy((char *)p1ptes,(char *)(new+newn-p1aptes),p1aptes*sizeof(int));
       bzero((char *)new,(newn-p1aptes)*sizeof(int));
       free((char *)p1ptes,p1aptes*sizeof(int));
       p1ptes = new;
       p1aptes = newn;
     }
    while (p1nptes < pgs)
     { int p;
#ifdef DEBUG_GROWSTACK
       printf("setuserstack: allocating new page\n");
#endif
       p = palloc(512);
#ifdef DEBUG_GROWSTACK
       printf("setuserstack: new page pfn %x (%x-%x)\n",p>>9,p,p+511);
#endif
       if (p == 0)
	{ panic("setuserstack: out of physical memory");
	}
       p1nptes ++;
#ifdef DEBUG_GROWSTACK
       printf("setuserstack: pte at %x gets %x\n",
			(int)&p1ptes[p1aptes-p1nptes],
			PG_V|PG_UW|(p>>9));
#endif
       p1ptes[p1aptes-p1nptes] = PG_V | PG_UW | (p >> 9);
	{ register int i;
	  register char *cp;
	  cp = (char *) (p | 0x80000000);
	  for (i=512;i>0;i--)
	   { *cp++ = 0x5a;
	   }
	}
#ifdef DEBUG_GROWSTACK
       conspause();
#endif
     }
  }
 loadp01regs();
 mtpr(TBIA,0);
 splx(s);
 return;
}

#ifdef DEBUG_GROWSTACK
static conspause()
{
 printf("...");
 consgetchar();
 printf("\b \b\b \b\b \b");
}
#endif

resetuserstack()
{
 mtpr(USP,0x80000000);
}

int vtop(va)
char *va;
{
 int br;
 int lr;
 int vpn;
 int bwp;
 int ptea;

 bwp = ((int)va) & ~0xc0000000;
 vpn = bwp >> 9;
 bwp &= 511;
 switch (((int)va) & 0xc0000000)
  { case 0x00000000:
       br = mfpr(P0BR);
       lr = mfpr(P0LR);
       if (vpn > lr)
	{ panic("vtop: p0 address out of segment");
	}
       ptea = br + (vpn << 2);
       if (cpu == VAX_620)
	{ ptea |= 0x80000000;
	}
       return((((*(int *)ptea)&PG_PFNUM)<<9)+bwp);
       break;
    case 0x40000000:
       br = mfpr(P1BR);
       lr = mfpr(P1LR);
       if (vpn < lr)
	{ panic("vtop: p1 address out of segment");
	}
       ptea = br + (vpn << 2);
       if (cpu == VAX_620)
	{ ptea |= 0x80000000;
	}
       return((((*(int *)ptea)&PG_PFNUM)<<9)+bwp);
       break;
    case 0x80000000:
       br = mfpr(SBR);
       lr = mfpr(SLR);
       if (vpn > lr)
	{ panic("vtop: system address out of segment");
	}
       ptea = br + (vpn << 2);
       ptea |= 0x80000000;
       return((((*(int *)ptea)&PG_PFNUM)<<9)+bwp);
       break;
    case 0xc0000000:
       panic("vtop: reserved space address");
       break;
  }
}

loadp01regs()
{
 mtpr(P0LR,p0aptes);
 mtpr(P1LR,(0x40000000>>9)-p1nptes);
 if (cpu == VAX_620)
  { mtpr(P0BR,vtop((char *)p0ptes));
    mtpr(P1BR,vtop((char *)(p1ptes+p1aptes-p1nptes))-(mfpr(P1LR)<<2));
  }
 else
  { mtpr(P0BR,(int)p0ptes);
    mtpr(P1BR,((int)(p1ptes+p1aptes-p1nptes))-(mfpr(P1LR)<<2));
  }
#ifdef DEBUG_GROWSTACK
 printf("loadp01regs: P0 LR=%x BR=%x, P1 LR=%x BR=%x\n",
			mfpr(P0LR),mfpr(P0BR),mfpr(P1LR),mfpr(P1BR));
 conspause();
#endif
}

int makevrange(addr,size)
int addr;
int size;
{
 int pgs;

 pgs = (addr+size-1 + 512) >> 9;
 if (pgs > p0aptes)
  { int newn;
    int *new;
    char *newhave;
    newn = (pgs + 127) & ~127;
    new = (int *) alloc(newn<<2);
    if (new == 0)
     { return(1);
     }
    newhave = (char *) alloc(newn);
    if (newhave == 0)
     { free((char *)new,newn<<2);
       return(1);
     }
    bcopy((char *)p0ptes,(char *)new,p0aptes*sizeof(int));
    bzero((char *)(new+p0aptes),(newn-p0aptes)*sizeof(int));
    free((char *)p0ptes,p0aptes*sizeof(int));
    bcopy(havephys,newhave,p0aptes);
    bzero(newhave+p0aptes,newn-p0aptes);
    free(havephys,p0aptes);
    p0ptes = new;
    p0aptes = newn;
    havephys = newhave;
  }
 return(0);
}

makeaddrrange(addr,size)
int addr;
int size;
{
 int pgs;
 int pf;
 int s;
 int a1;
 int a;

 s = splhigh();
 if (size < 0)
  { panic("makeaddrrange: size<0");
  }
 if (makevrange(addr,size))
  { panic("makeaddrrange: makevrange failed");
  }
 a = addr >> 9;
 a1 = (addr + size - 1) >> 9;
 for (;a<=a1;a++)
  { if (p0ptes[a] & PG_V)
     { continue;
     }
    pf = palloc(512);
    if (pf == 0)
     { panic("makeaddrrange: out of memory");
     }
    pf >>= 9;
    p0ptes[a] = PG_V | PG_UW | pf;
    havephys[a] = 1;
  }
 loadp01regs();
 mtpr(TBIA,0);
 splx(s);
 a1 = (a1+1) << 9;
 if (breakpt < a1)
  { breakpt = a1;
  }
 return;
}

int sbrk(incr,pgalign)
int incr;
int pgalign;
{
 if (pgalign)
  { int new;
    new = (breakpt + 511) & ~511;
    if (setbreak(new))
     { return(-1);
     }
  }
 if (setbreak(breakpt+incr))
  { return(-1);
  }
 else
  { return(breakpt-incr);
  }
}

int setbreak(addr)
int addr;
{
 int s;
 int a1;
 int a;
 int pf;

 s = splhigh();
 if (makevrange(0,addr+4))
  { splx(s);
    return(1);
  }
 a1 = addr >> 9;
 for (a=breakpt>>9;a<=a1;a++)
  { if (p0ptes[a] & PG_V)
     { continue;
     }
    pf = palloc(512);
    if (pf == 0)
     { splx(s);
       return(1);
     }
    pf >>= 9;
    p0ptes[a] = PG_V | PG_UW | pf;
    havephys[a] = 1;
  }
 breakpt = addr;
 loadp01regs();
 mtpr(TBIA,0);
 splx(s);
 return(0);
}

readonlyrange(addr,size)
int addr;
int size;
{
 int pf;
 int s;
 int a1;
 int a;

 s = splhigh();
 if (size < 0)
  { panic("readonlyrange: size<0");
  }
 a = addr >> 9;
 a1 = (addr + size - 1) >> 9;
 if (a1 >= p0aptes)
  { a1 = p0aptes - 1;
  }
 for (;a<=a1;a++)
  { if (! (p0ptes[a] & PG_V))
     { continue;
     }
    p0ptes[a] &= ~PG_PROT;
    p0ptes[a] |= PG_URKW;
  }
 loadp01regs();
 mtpr(TBIA,0);
 splx(s);
 return;
}

importmem(addr,size,phys)
int addr;
int size;
int phys;
{
 int s;
 int a1;
 int a;
 int pf;

 s = splhigh();
 if (size < 0)
  { panic("importmem: size<0");
  }
 if (makevrange(addr,size))
  { panic("importmem: makevrange failed");
  }
 a = addr >> 9;
 a1 = (addr + size - 1) >> 9;
 pf = phys >> 9;
 for (;a<=a1;a++)
  { if (havephys[a])
     { pfree((p0ptes[a]&PG_PFNUM)<<9,512);
     }
    p0ptes[a] = PG_V | PG_UW | pf;
    havephys[a] = 0;
    pf ++;
  }
 loadp01regs();
 mtpr(TBIA,0);
 splx(s);
 return;
}

exportmem(addr,size,phys)
int addr;
int size;
int phys;
{
 int pgs;
 int pf;
 int s;
 int a1;
 int a;
 int pa;
 extern int *Qbusmapregs;

 s = splhigh();
 if (size < 0)
  { panic("exportmem: size<0");
  }
 makeaddrrange(addr,size);
 a = addr >> 9;
 a1 = (addr + size - 1) >> 9;
 pa = (phys - KA_QBUSBASE) >> 9;
 for (;a<=a1;a++,pa++)
  { Qbusmapregs[pa] = KA_QMAP_V | (p0ptes[a] & PG_PFNUM);
  }
 splx(s);
 return;
}
