#include <sys/types.h>
#include <sys/param.h>
#include <sys/user.h>
#include <sys/proc.h>
#include <sys/map.h>
/*#include <sys/cmap.h>*/
#include <sys/vmmac.h>
#include <sys/kernel.h>
#include <machine/pte.h> /* grrr, <sys/param.h> should do this itself */
#include <machine/vmparam.h>
#include <machine/mmu.h>
#include <machine/psl.h>
#include <machine/vm_hat.h>
#include <machine/reg.h>

#include <vm/as.h>

#include <rciext/spl.h>

extern int fpu_exists;

int in_ksw = 0;
int ksw_enable = 1;
int ksw_fp = 1;
int ksw_fp_sync1 = 1;
int ksw_fp_sync2 = 1;

struct fpu kentucky_fpustate;
struct fpu *kentucky_fpctxpsave;
struct fpu *kentucky_fp_ctxpsave;
extern struct fpu *fp_ctxp;

kentucky_setup()
{
 u.u_procp->p_flag |= SULOCK;
 ctxlock();
}

/*
 * save_area is static to save stack space.  It is used only when we
 *  have to switch processes, so it can't be required by a recursive
 *  call - and we run at splhigh, so it can be static.
 */
kentucky(proc,fxn,arg)
struct proc *proc;
int (*fxn)();
int arg;
{
 int s;
 int c;
 int rv;
 struct proc *oldproc;
 int restores;
#define REST_PROC 0x00000001
 extern char intstack;
 int i;
 static int save_area[UPAGES+1];
 static struct fpu fpusave;

 s = splhigh();

 i = (char *)&s - &intstack; /* XXX assume stack layout, pointer handling, lots of stuff */
 if ((i > 0) && (i < 0x1000))
  { printf("INTSTACK SOFT REDZONE HIT in kentucky()\n");
    return(-1);
  }
 restores = 0;
 if (ksw_fp && !fpu_exists) ksw_fp = 0;
 if (proc->p_as == 0 || proc->p_as->a_hat.hat_ctx == 0)
  { panic("kentucky: proc has no context");
  }
 if (ksw_fp_sync1)
  { syncfpu((struct reg *)(u.u_pcb.pcb_psrp - PSR));
  }

 if (u.u_procp != proc)
  { oldproc = u.u_procp;
    kentucky_switchto(&save_area[0],proc);
    restores |= REST_PROC;
  }
 i = getcontext();
 if (i != proc->p_as->a_hat.hat_ctx->c_num)
  { printf("wanted context %d, got %d\n",
	    (int)proc->p_as->a_hat.hat_ctx->c_num,i);
    return(-1);
  }
 if (ksw_fp && !in_ksw)
  { kentucky_fp_enable();
    kentucky_fp_save(&fpusave);
    kentucky_fpctxpsave = u.u_pcb.pcb_fpctxp;
    kentucky_fp_ctxpsave = fp_ctxp;
    u.u_pcb.pcb_fpctxp = &kentucky_fpustate;
    fp_ctxp = &kentucky_fpustate;
  }
 if (ksw_fp && in_ksw)
  { printf ("--- in_ksw = TRUE ---\n");
  }
 if (ksw_enable)
  { in_ksw ++;
    rv = intstkcall(fxn,arg);
    in_ksw --;
    if (in_ksw < 0)
     { printf("in_ksw < 0\n");
       in_ksw = 0;
     }
  }
 if (ksw_fp && !in_ksw)
  { if (ksw_fp_sync2)
     { syncfpu(0);
     }
    u.u_pcb.pcb_fpctxp = kentucky_fpctxpsave;
    fp_ctxp = kentucky_fp_ctxpsave;
    kentucky_fp_restore(&fpusave);
  }
 if (restores & REST_PROC)
  { kentucky_switchback(&save_area[0]);
  }
 if (in_ksw && !atsplhigh())
  { printf("in_ksw > 0\n");
    in_ksw = 0;
  }
 splx(s);
 return(rv);
}

#if 0
int locked_ptexpand_msg = 0;

/*
 * ptexpand() for a process with a locked context.  Any of the hard
 *  cases cause us to sleep; we never try to swap the process out the
 *  way ptexpand does.
 */
locked_ptexpand(change,ods,oss)
int change;
size_t ods;
size_t oss;
{
 struct pte *p1;
 struct pte *p2;
 int i;
 int spages;
 int ss;
 int kold;
 int knew;
 int tdpages;
 int szpt;
 int s;

 if (locked_ptexpand_msg)
  { printf("locked_ptexpand: PID %d\n",u.u_procp->p_pid);
    sleep((caddr_t)&lbolt,PRIBIO);
    sleep((caddr_t)&lbolt,PRIBIO);
  }
 ss = P1PAGES - u.u_pcb.pcb_p1lr;
 kold = btokmx(u.u_pcb.pcb_p0br);
 szpt = u.u_pcb.pcb_szpt;
top:
 knew = (int) rmalloc(kernelmap,(long int)(szpt+change));
 if (knew == 0) goto bad;
 spages = ss / NPTEPG;
 tdpages = szpt - spages;
 if (memall(&Usrptmap[knew+tdpages],change,u.u_procp,CSYS) == 0)
  { rmfree(kernelmap,(long int)(szpt+change),(u_long)knew);
    goto bad;
  }
 s = splhigh();
 ctxunload(u.u_procp);
 kmcopy(knew,kold,tdpages);
 kmcopy(knew+tdpages+change,kold+tdpages,spages);
 i = knew + tdpages;
 vmaccess(&Usrptmap[i],(caddr_t)kmxtob(i),change);
 bzero((caddr_t)kmxtob(i),(u_int)(change*NBPG));
#ifdef vax
 mtpr(TBIA,0);
#endif
 p1 = u.u_pcb.pcb_p1br + u.u_pcb.pcb_p1lr;
 p2 = initp1br(kmxtob(knew+szpt+change)) + u.u_pcb.pcb_p1lr;
 for (i=kmxtob(kold+szpt)-p1;i!=0;i--)
  { *p2++ = *p1++;
  }
#ifdef vax
 mtpr(TBIA,0);
#endif
 u.u_procp->p_p0br = kmxtob(knew);
 setp0br(u.u_procp->p_p0br);
 u.u_pcb.pcb_p1br = initp1br(kmxtob(knew+szpt+change));
 u.u_procp->p_p1br = u.u_pcb.pcb_p1br;
 setp1br(u.u_pcb.pcb_p1br);
 u.u_pcb.pcb_szpt += change;
 u.u_procp->p_szpt += change;
 u.u_procp->p_addr = uaddr(u.u_procp);
#ifdef vax
 mtpr(TBIA,0);
#endif
 ctxsetup(u.u_procp);
 splx(s);
 if (szpt)
  {
#ifdef sun
    mapout(&Usrptmap[kold],szpt);
#endif
    rmfree(kernelmap,(long int)szpt,(u_long)kold);
  }
 return;
bad:
 sleep((caddr_t)&lbolt,PRIBIO);
 goto top;
}

/*
 * Just like usetup except that it does nothing if no p_ctx, rather
 *  than ctxalloc()ing.  This state occurs for a short time during
 *  process creation.
 */
switch_context(p)
register struct proc *p;
{
	if (p->p_ctx)
	 { setcontext((int)p->p_ctx->ctx_context);
	   if (p->p_flag & SPTECHG)
	    { ctxsetup(p);
	    }
	 }
}
#else
switch_context(p)
register struct proc *p;
{
	if (p->p_as)
	 { setcontext((int)p->p_as->a_hat.hat_ctx->c_num);
	 }
}
#endif
