/* Some of our kernel additions require process specific data to
   be kept around. This delightful piece of code maintains 'procx',
   an extension structure to the proc struct. 

   The implementation is currently really crude. For instance, no
   more the MAXPROCX processes can exist at any one time. It would
   gladly make this a dynamic structure if sun would provide me the
   kernel level tools to do it (like a kmem_alloc() that returns 
   NULL when out of memory instead of panicing).
 */

#include <sys/types.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/user.h>
#include <sys/proc.h>

#include <rciext/procx.h>
#include <rciext/link.h>
#include <rciext/cdefs.h>
#include <rciext/spl.h>

#define pxpool		px_pxpool
#define pxfree		px_pxfree
#define pxlist		px_pxlist
#define initdone	px_initdone

static struct procx pxpool[MAXPROCX];
static struct procx *pxfree;
static struct procx *pxlist;
static int initdone = 0;

#define	INIT_PX()	(initdone||procx_doinit())

static procx_doinit()
{
	extern exit();
	extern procx_exit();
	extern wrapped_exit();

	extern execve();
	extern procx_execve();
	extern wrapped_execve();

	int i, s;

	s = splhigh();
	if (initdone)
	 { while (initdone & 2)
	    { sleep((caddr_t)&initdone,PZERO+1);
	    }
	   splx(s);
	   return;
	 }
	initdone = 3;
	splx(s);
	pxfree = &pxpool[0];
	for (i=0; i<MAXPROCX-1; i++)
	 { pxpool[i].next = &pxpool[i+1];
	 }
	pxpool[MAXPROCX-1].next = NULL;
	pxlist = NULL;

	printf ("patching exit() ...\n");
	patch_kernel_text ((caddr_t)exit, 
		    (caddr_t)procx_exit, 
		    (caddr_t)wrapped_exit);

	printf ("patching execve() ...\n");
	patch_kernel_text ((caddr_t)execve, 
		    (caddr_t)procx_execve, 
		    (caddr_t)wrapped_execve);

	initdone = 1;
	wakeup((caddr_t)&initdone);
}

struct procx *procx_get()
{
	struct procx *px;
	int s;

	INIT_PX();
	if (px = procx_find())
	 { return (px);
	 }
	s = splhigh();
	if (px = pxfree)
	 { pxfree = px->next;
	   px->px_proc = u.u_procp;
	   u.u_procp->p_flag |= SPROCX;
	   LINK_H (pxlist, px);
	 }
	splx(s);
	return (px);
}

struct procx *procx_find()
{
	register struct procx *px;

	INIT_PX();
	for (px=pxlist; px; px=px->next)
	 { if (px->px_proc == u.u_procp)
	    { return (px);
	    }
	 }
	return (NULL);
}

procx_exit(rv)
int rv;
{
	if (u.u_procp->p_flag & SPROCX)
	 { struct procx *px;
	   int s;

	   if ((px = procx_find()) == NULL)
	    { panic ("procx_exit: can't find px");
	    }
	   dbmsg("procx_exit()");
	   RCI_exit();
	   ctxunlock();
#ifdef MEMLOCK
	   memlock_clear(px);
#endif
	   u.u_procp->p_flag &= ~SPROCX;
	   s = splhigh();
	   UNLINK_H (pxlist, px);
	   px->next = pxfree;
	   pxfree = px;
	   splx(s);
	 }
	return (wrapped_exit (rv));
}

procx_execve()
{
	if (u.u_procp->p_flag & SPROCX)
	 { struct procx *px;
	   int s;

	   if ((px = procx_find()) == NULL)
	    { panic ("procx_execve: can't find px");
	    }
	   RCI_exit();
	   ctxunlock();
#ifdef MEMLOCK
	   memlock_clear(px);
#endif
	   u.u_procp->p_flag &= ~SPROCX;
	   s = splhigh();
	   UNLINK_H (pxlist, px);
	   px->next = pxfree;
	   pxfree = px;
	   splx(s);
	 }
	return (wrapped_execve());
}

set_swaplock(px, flag)
struct procx *px;
int flag;
{
	px->px_swaplock |= flag;
	u.u_procp->p_flag |= SULOCK;
}

clear_swaplock(px, flag)
struct procx *px;
int flag;
{
	px->px_swaplock &= ~flag;
	if (!px->px_swaplock)
	 { u.u_procp->p_flag &= ~SULOCK;
	 }	
}
