#ifdef RCI

int rcicrashdebug = 0;

/*
 * This file contains RCI support procedures which are not associated
 *  with any specific interface device, such as the Kentucky process
 *  switch stuff.
 */

#define CPMBXPHYS 0x200b801c /* CPMBX physical (KA620/630 manual, pg 4-41) */

#include "../h/param.h"
#include "../h/systm.h"
#include "../machine/pte.h"
#include "../machine/mtpr.h"
#include "../h/map.h"
#include "../h/dir.h"
#include "../h/user.h"
#include "../h/proc.h"
#include "../h/vmmac.h"
#include "../h/buf.h"
#include "../machine/vmparam.h"
#include "../machine/trap.h"

#include "../rciext/kacomm.h"
#include "../rciext/crashinfo.h"
#include "../rciext/rci.h"
#include "../rciext/rciioctl.h"

#include "../rciext/mchk_frame.h"

static int didinit;
static caddr_t IOpage;
static caddr_t bus;
static int unswitch_sig;
static char kernel_pgbuf[2048];
static struct pte *kernel_pte;
static struct pcb *pcb;
static int RCItime = 0;
static CRASHINFO *crashinfo;
static int (*kut_fxn)();
static int kut_arg;
static RCIINFO *kut_rcii = 0;
static int kut_rv;

extern int kernel_user_traps;
extern label_t kernel_user_catch;
extern struct proc *kernel_user_proc;
extern int (*kernel_user_fxn)();

/* flag to indicate if there is a separate device for RCI clock */
int RCIclockDevice = 0;	

caddr_t kvall();

/* Interface */

RCI_init()
{
 int s;

 if (didinit)
  { return;
  }
 s = splhigh();
 didinit = 1;
 bus = kvall(8192);
 if (bus == 0)
  { didinit = -1;
    printf("RCI_init: failed (bus)\n");
    splx(s);
    return;
  }
 IOpage = kvall(16);
 if (IOpage == 0)
  { didinit = -1;
    kvfree(bus,8192);
    printf("RCI_init: failed (IOpage)\n");
    splx(s);
    return;
  }
 kmapphys(bus,8192,0x30000000);
 kmapphys(IOpage,16,0x20000000);
 pcb = (struct pcb *) ((511+(int)&kernel_pgbuf[0]) & ~511);
 kernel_pte = &Sysmap[(((int)pcb)>>9)&~0xffe00000];
 crashinfo = 0;
  { unsigned short int oldvalue;
    read_physical(CPMBXPHYS,(caddr_t)&oldvalue,2);
    oldvalue |= 3;
    write_physical(CPMBXPHYS,(caddr_t)&oldvalue,2);
  }
 splx(s);
}

RCI_alive()
{
 return(didinit>0);
}

RCI_get_interface(intf)
struct RCI_interface *intf;
{
 struct kabboard *kabbaddr();
 int sendsig();
 extern printf();
 extern uprintf();
 extern badaddr();
 int internaltimeout();
 extern ka_asyncsig();
 int useracc();
 extern vslock();
 extern vsunlock();
 extern kut_callit();
 extern lockReadBuf();
 extern lockWriteBuf();

 intf->bus = (char *) bus;
 intf->IOpage = (char *) IOpage;
 intf->time = &RCItime;
 intf->bboards[0] = kabbaddr(0);
 intf->bboards[1] = kabbaddr(1);
 intf->bboards[2] = kabbaddr(2);
 intf->bboards[3] = kabbaddr(3);
 intf->signal = sendsig;
 intf->printf = printf;
 intf->badaddr = badaddr;
 intf->timeout = internaltimeout;
 intf->sigslave = ka_asyncsig;
 intf->useracc = useracc;
 intf->vslock = vslock;
 intf->vsunlock = vsunlock;
 intf->kutcall = kut_callit;
 intf->uprintf = uprintf;
 intf->lockReadBuf = lockReadBuf;
 intf->lockWriteBuf = lockWriteBuf;
}

RCI_tick()
{
 RCItime ++;
}

RCI_setup_timeout(rcii,to)
RCIINFO *rcii;
struct RCI_timeout *to;
{
 RCI_setup_switch(rcii);
 do_timeout(rcii,to->clockno,to->when,to->function,to->argument);
}

RCI_setup_switch(ctx)
RCIINFO *ctx;
{
 ctx->proc = u.u_procp;
 ctx->p0br = mfpr(P0BR);
 ctx->p0lr = mfpr(P0LR);
 ctx->p1br = mfpr(P1BR);
 ctx->p1lr = mfpr(P1LR);
}

RCI_kut_call(fxn,arg,rcii)
int (*fxn)();
int arg;
RCIINFO *rcii;
{
 static int busy = 0;
 int s;
 int rv;
 CRASHINFO *crash;
 int kut_doit();

 crash = &rcii->lastcrash;
 s = splhigh();
 while (busy)
  { sleep((caddr_t)&busy,PZERO+1);
  }
 busy = 1;
 RCI_setup_switch(rcii);
 kut_fxn = fxn;
 kut_arg = arg;
 kut_rcii = rcii;
 timeout(kut_doit,(caddr_t)0,0);
 while (kut_fxn)
  { sleep((caddr_t)&kut_fxn,PZERO);
  }
 kut_rcii = 0;
 rv = kut_rv;
 busy = 0;
 wakeup((caddr_t)&busy);
 splx(s);
 return(rv);
}

int RCI_switch(ctx,fxn,arg)
RCIINFO *ctx;
int (*fxn)();
int arg;
{
 int s;
 struct proc *p;
 int remap;
 int p0br;
 int p0lr;
 int p1br;
 int p1lr;
 int rv;
 RCIINFO *old_kut_rcii;
 int kut_crash();

 s = splhigh();
 if (! (0x80000000 & (int)ctx))
  { panic("non-kernel context to RCI_switch");
  }
 unswitch_sig = 0;
 for (p=ctx->proc;p;p=p->p_xlink)
  { if (p->p_flag & SLOSTVM)
     { p = 0;
       break;
     }
    if (! (p->p_flag&SNOVM))
     { break;
     }
  }
 if (p && (p != u.u_procp))
  { int old_pfn;
    old_pfn = kernel_pte->pg_pfnum;
    kernel_pte->pg_pfnum = pcbb(p);
    mtpr(TBIA,0);
    ctx->p0br = (int) pcb->pcb_p0br;
    ctx->p0lr = pcb->pcb_p0lr;
    ctx->p1br = (int) pcb->pcb_p1br;
    ctx->p1lr = pcb->pcb_p1lr;
    kernel_pte->pg_pfnum = old_pfn;
    mtpr(TBIA,0);
  }
 if (p != u.u_procp)
  { p0br = mfpr(P0BR);
    p0lr = mfpr(P0LR);
    p1br = mfpr(P1BR);
    p1lr = mfpr(P1LR);
    mtpr(P0BR,ctx->p0br);
    mtpr(P0LR,ctx->p0lr);
    mtpr(P1BR,ctx->p1br);
    mtpr(P1LR,ctx->p1lr);
    mtpr(TBIA,0);
    remap = 1;
  }
 else
  { remap = 0;
  }
 crashinfo = &ctx->lastcrash;
 kernel_user_proc = 0;
 kernel_user_fxn = kut_crash;
 kernel_user_traps = 1;
 old_kut_rcii = kut_rcii;
 kut_rcii = ctx;
 rv = 0;
 if (setjmp(&kernel_user_catch) == 0)
  { (*fxn)(arg);
  }
 else
  { rv = 1;
  }
 kut_rcii = old_kut_rcii;
 kernel_user_traps = 0;
 crashinfo = 0;
 if (remap)
  { mtpr(P0BR,p0br);
    mtpr(P0LR,p0lr);
    mtpr(P1BR,p1br);
    mtpr(P1LR,p1lr);
    mtpr(TBIA,0);
  }
 if (unswitch_sig)
  { psignal(ctx->proc,unswitch_sig);
  }
 splx(s);
 return(rv);
}

RCI_signal(pid,sig)
int pid;
int sig;
{
 struct proc *p;

 printf("kill %d with %d:",pid,sig);
 if (! sig)
  { printf(" null\n");
    return;
  }
 p = pfind(pid);
 if (p)
  { psignal(p,sig);
    printf(" psignal\n");
  }
 else
  { printf(" ESRCH\n");
  }
}

int RCI_kaqbcrash(mcf)
struct mc630frame *mcf;
{
 if (crashinfo && kernel_user_traps)
  { crashinfo->type = CI_MEMFAULT;
    crashinfo->pc = mcf->mc63_pc;
    crashinfo->psl = mcf->mc63_psl;
    crashinfo->u.memfault.bits = ((mcf->mc63_summary & 2) ? CI_MF_WRITE : CI_MF_READ) | CI_MF_PAGE | CI_MF_PROT | CI_MF_QBUS;
    crashinfo->u.memfault.vaddr = mcf->mc63_mrvaddr;
    getcrashstack();
    longjmp(&kernel_user_catch);
  }
 return(0);
}

RCI_heartbeat(rciica)
caddr_t rciica;
{
 RCIINFO *rcii;

 rcii = (RCIINFO *) rciica;
 if (RCI_switch(rcii,rcii->hbfxn,rcii->hbarg))
  { RCI_signal(rcii->pid,rcii->crashsig);
  }
 else
  { timeout(RCI_heartbeat,rciica,rcii->hbticks);
  }
}

RCI_cleari(rcii)
register RCIINFO *rcii;
{
 int s;

 s = splhigh();
 rcii->flags = 0;
 rcii->ifxn = 0;
 rcii->pid = 0;
 rcii->crashsig = 0;
 rcii->lastcrash.type = CI_NULL;
 untimeout(RCI_heartbeat,(caddr_t)rcii);
 rcii->hbticks = 0;
 rcii->hbfxn = 0;
 rcii->timeout.info = rcii;
 rcii->timeout.handler = 0;
 splx(s);
}

RCI_closei(rcii)
register RCIINFO *rcii;
{
 int s;

 s = splhigh();
 if (rcii->timeout.handler)
  { ka_untimeout(&rcii->timeout);
  }
 RCI_cleari(rcii);
 splx(s);
}

/* Implementation */

kut_doit()
{
 int s;

 s = splhigh();
 kut_rv = RCI_switch(kut_rcii,kut_fxn,kut_arg);
 kut_fxn = 0;
 wakeup((caddr_t)&kut_fxn);
 splx(s);
}

static int kut_crash(type,code,pc,psl,locr0)
int type;
unsigned int code;
int pc;
int psl;
int *locr0;
{
 printf("kut_crash: type=%x code=%x pc=%x psl=%x\n",type,code,pc,psl);
 crashinfo->pc = pc;
 crashinfo->psl = psl;
 switch (type)
  { default:
       printf("bad type %d in RCI kut_crash\n",type);
       panic("kut_crash");
       break;
    case T_ARITHTRAP:
       crashinfo->type = CI_ARITHTRAP;
       crashinfo->u.subtype = code;
       break;
    case T_RESADFLT:
       crashinfo->type = CI_ILLINST;
       crashinfo->u.subtype = CI_II_RADRMOD;
       break;
    case T_RESOPFLT:
       crashinfo->type = CI_ILLINST;
       crashinfo->u.subtype = CI_II_RESOPRND;
       break;
    case T_BPTFLT:
       crashinfo->type = CI_ILLINST;
       crashinfo->u.subtype = CI_II_BPT;
       break;
    case T_COMPATFLT:
    case T_PRIVINFLT:
    case T_SYSCALL:
    case T_XFCFLT:
       crashinfo->type = CI_ILLINST;
       crashinfo->u.subtype = CI_II_PRIVINST;
       break;
    case T_TRCTRAP:
       crashinfo->type = CI_TRACE;
       break;
    case T_PAGEFLT:
       crashinfo->type = CI_MEMFAULT;
       crashinfo->u.memfault.bits = CI_MF_PAGE;
       crashinfo->u.memfault.vaddr = code;
       break;
    case T_TABLEFLT:
       crashinfo->type = CI_MEMFAULT;
       crashinfo->u.memfault.bits = CI_MF_PTE;
       crashinfo->u.memfault.vaddr = code;
       break;
    case T_PROTFLT:
       crashinfo->type = CI_MEMFAULT;
       crashinfo->u.memfault.bits = CI_MF_PROT;
       crashinfo->u.memfault.vaddr = code;
       break;
    case T_SEGFLT:
       crashinfo->type = CI_MEMFAULT;
       crashinfo->u.memfault.bits = CI_MF_LENGTH;
       crashinfo->u.memfault.vaddr = code;
       break;
    case T_ASTFLT:
       panic("AST fault in RCI kut_crash");
       break;
  }
 getcrashstack();
}

/* This routine is EXTREMELY paranoid.  It reads a longword from the	 */
/*  given addr and stores it through the given pointer.  If the address	 */
/*  is not a valid virtual address, or the longword does not reside in	 */
/*  physical memory, return value is nonzero (and *place may be		 */
/*  modified).  If no error occurs, return value is zero and *place	 */
/*  will contain the value read.					 */

static int readlong(addr,place)
int addr;
int *place;
{
 int pws;
 int bwp;
 int pa;
 int br;
 int lr;
 int pte;
 int ptea;
 int n;
 char *vp;
 extern int cpu;
 extern int physmem;
#define memsize (physmem<<9)

 bwp = addr &~ 0xc0000000;
 pws = bwp >> 9;
 bwp &= 511;
 switch (addr & 0xc0000000)
  { case 0x80000000:
       br = mfpr(SBR);
       lr = mfpr(SLR);
       if (bwp >= 509)
	{ if (pws+1 >= lr) return(1);
	  ptea = (br + (pws*4)) | 0x80000000;
	  if (ptea > (memsize+0x80000000-4)) return(1);
	  pte = * (int *) ptea;
	  if (! goodpte(pte)) return(1);
	  n = 512 - bwp;
	  vp = (char *) place;
	  bcopy((char *)(0x80000000|((pte&PG_PFNUM)<<9)|bwp),vp,n);
	  vp += n;
	  n = 4 - n;
	  ptea = (br + (pws*4) + 4) | 0x80000000;
	  if (ptea > (memsize+0x80000000-4)) return(1);
	  pte = * (int *) ptea;
	  if (! goodpte(pte)) return(1);
	  bcopy((char *)(0x80000000|((pte&PG_PFNUM)<<9)),vp,n);
	}
       else
	{ if (pws >= lr) return(1);
	  ptea = (br + (pws*4)) | 0x80000000;
	  if (ptea > (memsize+0x80000000-4)) return(1);
	  pte = * (int *) ptea;
	  if (! goodpte(pte)) return(1);
	  *place = * (int *) (0x80000000|((pte&PG_PFNUM)<<9)|bwp);
	}
       break;
    default:
       return(1);
       break;
  }
 return(0);
#undef memsize
}

/* Helper for the above */
static int goodpte(pte)
register int pte;
{
 extern int physmem;
#define memsize (physmem<<9)

 if (! (pte & PG_V)) return(0);
 switch (pte & PG_PROT)
  { default:
       return(0);
       break;
    case PG_KW:
    case PG_KR:
    case PG_URKW:
    case PG_URKR:
    case PG_UW:
       break;
  }
 if ((pte&PG_PFNUM) >= (memsize>>9)) return(0);
 return(1);
#undef memsize
}

static getcrashstack()
{
 int *regs;
 CRASHINFO *ci;
 int fp;
 int ap;
 int na;
 int i;
 int newfp;
 int newap;
 int newpc;
 int argi;
 CRASHFRAME *cf;
 extern int kut_regbank[];

 regs = kut_regbank;
 ci = crashinfo;
 bcopy((caddr_t)kut_regbank,(caddr_t)ci->registers,16*4);
 newfp = regs[13];
 newap = regs[12];
 newpc = ci->pc;
 if (rcicrashdebug)
  { printf("initial fp=%x ap=%x pc=%x\n",newfp,newap,newpc);
  }
 i = 0;
 argi = 0;
 while (i < KAC_MAXCALLS)
  { cf = &ci->callstack[i];
    cf->pc = newpc;
    fp = newfp;
    ap = newap;
    cf->flags = 0;
    if (rcicrashdebug)
     { printf("frame fp=%x ap=%x pc=%x\n",fp,ap,cf->pc);
     }
    if (readlong(ap,&na))
     { cf->nargs = ~0;
       if (rcicrashdebug)
	{ printf("can't read nargs\n");
	}
     }
    else
     { na &= 0xff;
       if (rcicrashdebug)
	{ printf("%d args:",na);
	}
       if (na > KAC_MAXARGS-argi)
	{ cf->flags |= CI_CF_ARGLOST;
	  na = KAC_MAXARGS - argi;
	  if (rcicrashdebug)
	   { printf(" (reduced to %d)",na);
	   }
	}
       cf->nargs = na;
       cf->argindex = argi;
       for (;na>0;na--)
	{ if (readlong(ap+=4,&ci->callargs[argi]))
	   { ci->callargs[argi] = -1;
	     if (rcicrashdebug)
	      { printf(" (unreadable)");
	      }
	   }
	  if (rcicrashdebug)
	   { printf(" %x",ci->callargs[argi]);
	   }
	  argi ++;
	}
       if (rcicrashdebug)
	{ printf("\n");
	}
     }
    if (rcicrashdebug)
     { printf("frame %d valid\n",i);
     }
    cf->flags |= CI_CF_VALID;
    i ++;
    if ( readlong(fp+ 8,&newap) ||
	 readlong(fp+12,&newfp) ||
	 readlong(fp+16,&newpc) )
     { if (rcicrashdebug)
	{ printf("can't read new ap/fp/pc\n");
	}
       break;
     }
  }
 for (;i<KAC_MAXCALLS;i++)
  { ci->callstack[i].flags = 0;
    if (rcicrashdebug)
     { printf("frame %d invalid\n",i);
     }
  }
}

static long int lrmalloc(mp,size)
register struct map *mp;
long size;
{
 register struct mapent *ep;
 register int addr;
 register struct mapent *bp;
 swblk_t first;
 swblk_t rest;

 if (size <= 0)
  { panic("lrmalloc");
  }
#ifdef RCIDEBUG
 for (bp=(struct mapent *)(mp+1);bp->m_size;bp++)
  { printf("%d at %d\n",bp->m_size,bp->m_addr);
  }
#endif RCIDEBUG
 for (bp=(struct mapent *)(mp+1);bp->m_size;bp++)
  { if (bp->m_size >= size)
     { addr = bp->m_addr;
       bp->m_addr += size;
       if ((bp->m_size -= size) == 0)
	{ do
	   { bp ++;
	     (bp-1)->m_addr = bp->m_addr;
	   } while ((bp-1)->m_size=bp->m_size);
	}
       return(addr);
     }
  }
 return(0);
}

static caddr_t kvall(npg)
int npg;
{
 int a;

 a = lrmalloc(kernelmap,(long int)clrnd(npg));
 if (a == 0)
  { return(0);
  }
 return((caddr_t)kmxtob(a));
}

static kvfree(ka,npg)
caddr_t ka;
int npg;
{
 rmfree(kernelmap,(long int)clrnd(npg),(long int)btokmx((struct pte *)ka));
}

static kmapphys(ka,npg,phys)
caddr_t ka;
int npg;
int phys;
{
 struct pte *ptep;
 int pf;

 ptep = (struct pte *) &Usrptmap[btokmx((struct pte *)ka)];
 npg = clrnd(npg);
 pf = btop(phys);
 for (;npg>0;npg--)
  { mapin(ptep,btop(ka),(unsigned int)pf,1,(int)(PG_V|PG_KW));
    ptep ++;
    ka += NBPG;
    pf ++;
  }
}

static sendsig(cpu,sig)
int cpu;
int sig;
{
 if (cpu == 0)
  { unswitch_sig = sig;
  }
}

static internaltimeout(clockno,when,fxn,arg)
int clockno;
int when;
int (*fxn)();
int arg;
{
 if (kut_rcii == 0) panic("RCI internaltimeout");
 do_timeout(kut_rcii,clockno,when,fxn,arg);
}

do_timeout(rcii,clockno,when,function,argument)
RCIINFO *rcii;
int clockno;
int when;
int (*function)();
int argument;
{
 struct rcitimeout *to;
 int s;

 to = &rcii->timeout;
 s = splhigh();
 if (to->handler)
  { ka_untimeout(to);
  }
 if (function)
  { to->clockno = clockno;
    to->when = when;
    to->handler = function;
    to->argument = argument;
    ka_timeout(to);
  }
 splx(s);
}

lockReadBuf(addr, size)
char *addr;
int size;
{
	if (useracc (addr, size, B_READ) != 1)
	 { return(0);
	 }
	vslock (addr, size);
	return (1);
}

lockWriteBuf(addr, size)
char *addr;
int size;
{
	if (useracc (addr, size, B_READ) != 1)
	 { return(0);
	 }
	vslock (addr, size);
	return (1);
}

RCI_kutexec(arg)
int arg;
{
	struct RCI_fxn_arg4 *ap;

	ap = (struct RCI_fxn_arg4*)arg;
	ap->rval = (*ap->fxn)(ap->arg[0],ap->arg[1],ap->arg[2],ap->arg[3]);
}

#endif
