/*-=-=-=-=-=-=-=-=-=-=-=- RCCL/RCI Notice of Copyright -=-=-=-=-=-=-=-=-=-=-*
 *									    *
 *	Copyright (C) 1987, 1991 by John E. Lloyd.			    *
 *									    *
 *	Information on copyright permissions, as well as a complete	    *
 *	disclaimer of warranty, is given in the file COPYRIGHT.		    *
 *									    *
 *-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*---------------------------------------------------------------------------
 *		
 * MODULE NAME:	loadtask
 *
 * DESCRIPTION:	Core routines for setting up RCI tasks and initizlizing
 *		the task monitors on the auxiliary CPUs.
 *
 * CAVEATS:	Downloading the control portion of the RCI program
 *		to a slave CPU --
 *
 *			Right now, on the uVAX version, we actually download
 *			all of the program. This works out OK because we have
 *			the memory available to do this. The reason we
 *			download all of the program is because even the
 *			control code ends up using things normanlly contained
 *			in the C startup code, and it was easier to load the
 *			whole thing rather than figure out what bits and
 *			pieces of code we needed to load. 
 *
 *			Look for this near the #define FULLSEND.
 *
 * AUTHOR/DATE:	John Lloyd, Fall 1987
 *
 * MODIFICATION HISTORY:
 *
 *--------------------------------------------------------------------------*/

#ifndef VXWORKS_CFG
#include <errno.h>
#include <sys/time.h>
#include <signal.h>
#include <sys/file.h>
#endif

#include "../h/cdefs.h"
#include "../h/rciConfig.h"
#include "../h/rciTask.h"
#include "../h/robot_jls.h"
#include "../h/errorCodes.h"
#include "../h/IOdevice.h"
#include "../h/assignMem.h"
#include "../h/errorStack.h"

#include "../rci/messageDefs.h"
#include "../rci/monitorCom.h"

#include "../rci/create.h"

#if defined(uVAX_CFG)
#include "crashinfo.h"
#endif

#if defined(uVAX_CFG)
#include "../sys.uVAX/rciext/kaioctl.h"		/* UNIX auxiliary CPU ioctls */ 
#endif

extern RCI_DESC rciTaskTable[];

/* Variables for communicating between monitor and planning task. */

extern char moncomReadBuf[];
extern char moncomMsgBuf[MAX_CPUS][MON_MSGBUF_SIZE];

#define PUT_MSG(msg,type,cpu)	*(struct type*)&moncomMsgBuf[cpu][0] = (msg);

typedef struct {
	void *share;
	int export;
	int tprivSize;
	int shareSize;
} TASK_MEMORY_INFO;

TASK_MEMORY_INFO taskMemoryInfo[MAX_RCI_TASKS];

static int pageSize = 0;
extern int errno;

/* ----- Error Messages. Defined externally to save space ----- */

static char *nullMsg		= "%s --\n";
static char *cantAllocLmemMsg	= "%s -- can't alloc local mem on cpu %d\n";
static char *cantAllocSmemMsg	= "%s -- can't alloc shared mem on cpu %d\n";
static char *cantSetupSmemMsg	= "%s -- can't set up shared mem on cpu %d\n";
static char *cantExportSmemMsg	= "%s -- can't export shared mem on cpu %d\n";
static char *cantImportSmemMsg	= "%s -- can't import shared mem to cpu %d\n";
static char *cantExecMonCmdMsg	= "%s -- can't execute monitor cmd\n";
static char *cantExecArbCmdMsg	= "%s -- can't execute command on arbiter\n";
static char *noAuxCpusMsg	= "%s -- aux cpus not implemented\n";
static char *cantLockMemMsg     = "%s -- can't lock 0x%x to 0x%x, errno %d\n";
static char *badAuxKernel	= "%s -- kernel trouble on cpu %d\n";
static char *cantPrimBootMsg	= "%s -- can't primary boot cpu %d\n";
static char *cantBootKernelMsg	= "%s -- can't boot kernel on cpu %d\n";
static char *noBootResponseMsg	= 
	    "%s no response booting cpu %d; it's probably in console mode\n";
static char *cantClearCpuMsg	= "%s -- can't clear cpu %d\n";
static char *badIoctlMsg	= "%s -- %s ioctl failed, errno %d\n";
static char *cantLoadCpuMsg	= "%s -- can't load cpu %d\n";
static char *cantStartCpuMsg	= "%s -- can't start cpu %d executing\n";
static char *cantOpenPsetMsg	= "%s -- error openning parameter set %s\n";
static char *psetNotDefMsg	= "%s -- parameter set %s not defined\n";
static char *paramNotDefMsg	= "%s -- parameter %s not in set %s\n";
static char *monTimeoutMsg	= "%s -- monitor on cpu %d timed out\n";
static char *cpuInCpuMaskMsg	= "%s -- cpu comtained in cpuMask\n";

extern void *allocSharedMemUpper();

createPlanningTask ()
{
	/* Sets up the planning task on the arbiter cpu, allocating
	   memory for it and doing the required initializations.
	   Called the first time rciCreate() is called.
	*/

	RCI_DESC *td = &rciTaskTable[0];
	TASK_MEMORY_INFO *tmi = &taskMemoryInfo[0];
	MSG_ROOT *mr;
	int hprivSize;
	int tprivSize;
	int shareSize;
	char *hpriv;
	char *share;

	char *fxnName = "createPlanningTask()";

 	/* about the only thing that the planning task needs to set up
	   is memory for messages and the shared data table. */

	if (!pageSize)
	 { pageSize = getpagesize();
	 }

	hprivSize =   ALIGN_SIZE(sizeof(MSG_ROOT)) 
		    + ALIGN_SIZE(sizeof(NOTICE_PACKET)*MAX_RCI_TASKS);
	tprivSize =   ALIGN_SIZE(sizeof(MSG_ROOT));
	shareSize =   ALIGN_SIZE(2*sizeof(NOTICE_BUF));

	if (hprivSize % pageSize)
	 { hprivSize = hprivSize + pageSize - (hprivSize % pageSize);
	 }
	if (tprivSize % pageSize)
	 { tprivSize = tprivSize + pageSize - (tprivSize % pageSize);
	 }
	if (shareSize % pageSize)
	 { shareSize = shareSize + pageSize - (shareSize % pageSize);
	 }

	if (localMemoryAlloc (ARBITER_CPU, &hpriv, hprivSize) < 0)
  	 { errorMsg (getErrorCode(0), cantAllocLmemMsg, fxnName, ARBITER_CPU);
	   return (-1);
	 }
	if ((share = (char*)allocSharedMemUpper (ARBITER_CPU, 
						 shareSize)) == NULL)
  	 { errorMsg (getErrorCode(0), cantAllocSmemMsg, fxnName, ARBITER_CPU);
	   return (-1);
	 }
	tmi->export = UNDEF;		/* don't export yet! */

	tmi->share = (void*)share;
	tmi->tprivSize = tprivSize;
	tmi->shareSize = shareSize;

	/* we don't call taskSetup() because this is so specialized for the
	   planning task ... we unroll it instead */

#define ASSIGN_LOCAL(var,typ,size)  ASSIGN_MEM(var,typ,size,hpriv)
#define ASSIGN_SHARE(var,typ,size)  ASSIGN_MEM(var,typ,size,share)

	td->taskID = 0;
 	td->taskCode = 1;
/* 	td->robotID = 0;	*/
 	td->cpu = ARBITER_CPU;
 	td->interruptNumber = UNDEF;

 	td->tcb = NULL;		/* planning task doesn't have these */
 	td->rbt = NULL;		/*  ...				    */

	/* set up the message areas */

	ASSIGN_LOCAL (mr, MSG_ROOT*, sizeof(MSG_ROOT));
	ASSIGN_SHARE (mr->free, NOTICE_BUF*, sizeof(NOTICE_BUF));
	ASSIGN_SHARE (mr->recv, NOTICE_BUF*, sizeof(NOTICE_BUF));
	ASSIGN_LOCAL (mr->msgs, NOTICE_PACKET*, 
		      sizeof(NOTICE_PACKET)*MAX_RCI_TASKS);
	td->msg = (void*)mr;
  	taskMessageInit (mr);

	putRciName ("planner", 0);

	return (1);
}

createAuxiliaryTask (id, name, cpu)
int id;
char *name;
int cpu;
{
	/* create a task on an auxiliary processor */

	TASK_MEMORY_INFO *tmi = &taskMemoryInfo[id];
	struct setupTaskMsg msg;
	int hprivSize;
	int tprivSize;
	int shareSize;
	char *hpriv;
	char *tpriv;
	char *share;

	char *fxnName = "createAuxiliaryTask()";

#if defined(uVAX_CFG)
	if (!pageSize)
	 { pageSize = getpagesize();
	 }

	hprivSize =   ALIGN_SIZE(sizeof(MSG_ROOT))
		    + ALIGN_SIZE(sizeof(NOTICE_PACKET)*MAX_RCI_TASKS)
		    + ALIGN_SIZE(sizeof(ERROR_STACK_DESC))
		    + ALIGN_SIZE(ERROR_STACK_SIZE);
	tprivSize =   ALIGN_SIZE(sizeof(MSG_ROOT));
	shareSize =   ALIGN_SIZE(sizeof(RCI_TCB))
		    + ALIGN_SIZE(sizeof(IO_DEVICE))
		    + ALIGN_SIZE(2*sizeof(NOTICE_BUF));

	if (hprivSize % pageSize)
	 { hprivSize = hprivSize + pageSize - (hprivSize % pageSize);
	 }
	if (tprivSize % pageSize)
	 { tprivSize = tprivSize + pageSize - (tprivSize % pageSize);
	 }
	if (shareSize % pageSize)
	 { shareSize = shareSize + pageSize - (shareSize % pageSize);
	 }

	if (localMemoryAlloc (cpu, &hpriv, hprivSize) < 0)
  	 { errorMsg (getErrorCode(0), cantAllocLmemMsg, fxnName, cpu);
	   return (-1);
	 }
	if ((share = (char*)allocSharedMemUpper (cpu, shareSize)) == NULL)
	 { errorMsg (getErrorCode(0), nullMsg, fxnName);
	   return (-1);
	 }
	tmi->share = (void*)share;
	tmi->tprivSize = tprivSize;
	tmi->shareSize = shareSize;

	msg.shareMem = (void*)share;
	msg.localMem = (void*)hpriv;

	msg.id = id;
 	msg.taskCpu = cpu;

	STRNCPY (msg.name, name, TASK_NAME_SIZE);

	msg.flags = TASK_HOST;

	PUT_MSG (msg, setupTaskMsg, cpu);
	
	if (issueMonitorCommand (MON_SETUP_TASK, 1<<cpu, 0) < 0)
	 { errorMsg (EInternal, cantExecMonCmdMsg, fxnName);
	   return (-1);
	 }

	/* now, import the task to the arbiter */

	if (localMemoryAlloc (ARBITER_CPU, &tpriv, hprivSize) < 0)
  	 { errorMsg (getErrorCode(0), cantAllocLmemMsg, fxnName, ARBITER_CPU);
	   return (-1);
	 }
	msg.shareMem = tmi->share;
	msg.localMem = (void*)tpriv;
	msg.flags = 0;

	monitorSetupTask (&msg);

	return (1);
#else
	errorMsg (ENotSupported, noAuxCpusMsg, fxnName);
	return (-1);
#endif
}

createArbiterTask (id, name) 
int id;
char *name;
{
	/* create a task on the arbiter CPU */

	TASK_MEMORY_INFO *tmi = &taskMemoryInfo[id];
	struct setupTaskMsg msg;
	int hprivSize;
	int tprivSize;
	int shareSize;
	char *hpriv;
	char *share;

	char *fxnName = "createArbiterTask()";

	if (!pageSize)
	 { pageSize = getpagesize();
	 }

	hprivSize =   ALIGN_SIZE(sizeof(MSG_ROOT)) 
		    + ALIGN_SIZE(sizeof(NOTICE_PACKET)*MAX_RCI_TASKS)
		    + ALIGN_SIZE(sizeof(ERROR_STACK_DESC))
		    + ALIGN_SIZE(ERROR_STACK_SIZE);
	tprivSize =   ALIGN_SIZE(sizeof(MSG_ROOT));
	shareSize =   ALIGN_SIZE(sizeof(RCI_TCB)) 
		    + ALIGN_SIZE(sizeof(IO_DEVICE))
		    + ALIGN_SIZE(2*sizeof(NOTICE_BUF));

	if (hprivSize % pageSize)
	 { hprivSize = hprivSize + pageSize - (hprivSize % pageSize);
	 }
	if (tprivSize % pageSize)
	 { tprivSize = tprivSize + pageSize - (tprivSize % pageSize);
	 }
	if (shareSize % pageSize)
	 { shareSize = shareSize + pageSize - (shareSize % pageSize);
	 }

	if (localMemoryAlloc (ARBITER_CPU, &hpriv, hprivSize) < 0)
  	 { errorMsg (getErrorCode(0), cantAllocLmemMsg, fxnName, ARBITER_CPU);
	   return (-1);
	 }
	if ((share = (char*)allocSharedMemUpper (ARBITER_CPU, 
						 shareSize)) == NULL)
  	 { errorMsg (getErrorCode(0), cantAllocSmemMsg, fxnName, ARBITER_CPU);
	   return (-1);
	 }
	tmi->export = UNDEF;		/* don't export yet! */

	tmi->share = (void*)share;
	tmi->tprivSize = tprivSize;
	tmi->shareSize = shareSize;

	msg.shareMem = (void*)share;
	msg.localMem = (void*)hpriv;

	msg.id = id;
 	msg.taskCpu = ARBITER_CPU;

	STRNCPY (msg.name, name, TASK_NAME_SIZE);

	msg.flags = TASK_HOST;

	monitorSetupTask (&msg);

	return (1);
}

importTaskOnAuxiliary (id, cpu)
int id;
int cpu;
{
	/* Makes an auxilairy CPU import all the data structures necessary
	   so that it is aware of the existence of a task on another CPU.
	*/

	RCI_DESC *td = &rciTaskTable[id];
	TASK_MEMORY_INFO *tmi = &taskMemoryInfo[id];
	struct setupTaskMsg msg;
	char *tpriv;

	char *fxnName = "importTaskOnAuxiliary()";

#if defined(uVAX_CFG)
	if (localMemoryAlloc (cpu, &tpriv, tmi->tprivSize) < 0)
  	 { errorMsg (getErrorCode(0), cantAllocLmemMsg, fxnName, cpu);
	   return (-1);
	 }
	msg.shareMem = tmi->share;
	msg.localMem = (void*)tpriv;
	msg.id = td->taskID;
 	msg.taskCpu = td->cpu;

	STRNCPY (msg.name, rciTaskName(td), TASK_NAME_SIZE);

	msg.flags = 0;

	PUT_MSG (msg, setupTaskMsg, cpu);

	if (issueMonitorCommand (MON_SETUP_TASK, 1<<cpu, 0) < 0)
	 { errorMsg (EInternal, cantExecMonCmdMsg, fxnName);
	   return (-1);
	 }
	return (1);
#else
	errorMsg (ENotSupported, noAuxCpusMsg, fxnName);
	return (-1);
#endif
}

#ifndef vax
#define ETABLEFULL	0	/* this error does not exist on suns */
#endif

#ifdef sun
#include <a.out.h>		/* to get SEGSIZ */
#endif

lockControlMemory ()
{
	/* Lock the memory on the arbiter that is compiled to
	   "the right of the CTRL option with the rcc command" so
	   that it will available at interrupt time to any control
	   task running on the arbiter.
	*/
#if !defined(VXWORKS_CFG) && !defined(SGI_CFG) && !defined(SIMONLY_CFG)

	int textSize;
	int dataSize;
	char *textBegin;
	char *dataBegin;
#if sun
	extern char etext;
#else
	extern char _RCIt_end, _RCIt_begin;
	extern char _RCId_end, _RCId_begin;
#endif

	char *fxnName = "lockControlMemory()";

	if (!pageSize)
	 { pageSize = getpagesize();
	 }
#if sun
	textBegin = (char*)(SEGSIZ);
	dataBegin = (char*)(SEGSIZ + ((unsigned int)&etext & ~(SEGSIZ - 1)));
 	textSize = (unsigned int)&etext - (unsigned int)textBegin;
	dataSize = (unsigned int)sbrk(0) - (unsigned int)dataBegin;
#else
	textBegin = &_RCIt_begin;
	dataBegin = &_RCId_begin;
	textSize = (unsigned int)&_RCIt_end - (unsigned int)textBegin;
	dataSize = (unsigned int)&_RCId_end - (unsigned int)dataBegin;
#endif
 	if (lockMemory (textBegin, textSize, /*text=*/1) < 0)
	 { errorMsg (errno == ETABLEFULL ? EMemory : EInternal, 
		     cantLockMemMsg, fxnName, 
		     textBegin, textBegin+textSize, errno);
	   return (-1);
	 }
        if (lockMemory (dataBegin, dataSize, /*text=*/0) < 0)
	 { errorMsg (errno == ETABLEFULL ? EMemory : EInternal,	/* ELockMem */
		     cantLockMemMsg, fxnName,
		     dataBegin, dataBegin+dataSize, errno);
	   return (-1);
	 }

#endif /*!defined(VXWORKS_CFG)&&!defined(SGI_CFG) && !defined(SIMONLY_CFG)*/

	return (1);
}

freeControlMemory ()
{
	/* unlock the memory locked by 'lockControlMemory()' */

#if !defined(VXWORKS_CFG) && !defined(SGI_CFG) && !defined(SIMONLY_CFG)
	int textSize;
	int dataSize;
	extern char _RCIt_end, _RCIt_begin;
	extern char _RCId_end, _RCId_begin;

 	textSize = &_RCIt_end - &_RCIt_begin;
 	dataSize = &_RCId_end - &_RCId_begin;
 	unlockMemory (&_RCIt_begin, textSize);
 	unlockMemory (&_RCId_begin, dataSize);
#endif
	return (1);
}

#define KAFD(cpu)	(cpuFileDescriptors[cpu])
extern int cpuFileDescriptors[];

#include <setjmp.h>

jmp_buf timeoutEnv;
timeoutHandler ()
{
	longjmp (timeoutEnv, 1);
}

loadAuxCpu (cpu)
int cpu;
{
	/* Loads a copy of the control portion of the RCI onto the
	   the indicated CPU, and start it running the at the point
	   'auxMain()'. The CPU is booted if necessary.

	   At the present time, the entire RCI program is downloaded.
	   This is because of a few subtle difficulties in making sure we
	   load everything we need.
	*/

	int kernelOK;
	int ioctlArg;
	
	char *fxnName = "loadAuxCpu()";

#if defined(uVAX_CFG)
	if ((kernelOK = auxKernelVerify(KAFD(cpu),"RCI minikernel 1.00")) < 0)
 	 { errorMsg (EInternal, badAuxKernel, fxnName, cpu);
	   return (-1);
	 }
	if (!kernelOK)
  	 { 	
	   /* ---- do downloading and booting. Set up an interval timer ----
		   to make sure that everything goes well 		   */

#define DOWNLOAD_TIME	5

	   struct itimerval timeout;
	   struct itimerval savetimer;
	   int (*saveAlrm)();

	   timerclear (&timeout.it_interval);
	   timeout.it_value.tv_sec = DOWNLOAD_TIME;
	   timeout.it_value.tv_usec = 0;

	   saveAlrm = signal (SIGALRM, timeoutHandler);
	   setitimer (ITIMER_REAL, &timeout, &savetimer);
	   if (setjmp (timeoutEnv) == 1)
	    { errorMsg (EAuxHalted, noBootResponseMsg, fxnName, cpu);
	      signal (SIGALRM, saveAlrm);
	      setitimer (ITIMER_REAL, &savetimer, 0);
	      return (-1);
	    }

	   if (auxPrimaryBoot(KAFD(cpu)) < 0)
	    { errorMsg (getErrorCode(0), cantPrimBootMsg, fxnName, cpu);
	      return (-1);
	    }
	   if (auxKernelBoot(KAFD(cpu)) < 0)
	    { if (getErrorCode(0) == EAuxHalted)
	       { clearErrors();
		 errorMsg (EAuxHalted, noBootResponseMsg, fxnName, cpu);
		 signal (SIGALRM, saveAlrm);
		 setitimer (ITIMER_REAL, &savetimer, 0);
		 return (-1);
	       }
	      else
	       { errorMsg (getErrorCode(0), cantBootKernelMsg, fxnName, cpu);
		 signal (SIGALRM, saveAlrm);
		 setitimer (ITIMER_REAL, &savetimer, 0);
		 return (-1);
	       }
	    }

	   signal (SIGALRM, saveAlrm);
	   setitimer (ITIMER_REAL, &savetimer, 0);

	   nap(0.2);
	 }
	else
	 { if (sendClear(KAFD(cpu)) < 0)
	    { errorMsg (EInternal, cantClearCpuMsg, fxnName, cpu);
	      return (-1);
	    }
	 }

	if (ioctl(KAFD(cpu), KAIOC_REQSIGS, 0) < 0)
	 { errorMsg (EInternal, badIoctlMsg, fxnName, "REQSIZE", errno);
	   return (-1);
	 }
	ioctlArg = SIGXCPU;
	if (ioctl(KAFD(cpu), KAIOC_ONCRASH, &ioctlArg) < 0)
	 { errorMsg (EInternal, badIoctlMsg, fxnName, "ONCRASH", errno);
	   return (-1);
	 }
	ioctlArg = YES;
	if (ioctl(KAFD(cpu), KAIOC_SETSTOP, &ioctlArg) < 0)
	 { errorMsg (EInternal, badIoctlMsg, fxnName, "SETCLEAR", errno);
	   return (-1);
	 }

	 { 
#define FULLSEND	1
#if FULLSEND == 0
	   int textSize;
	   int dataSize;
#endif

	   extern char _RCIt_end, _RCIt_begin;
	   extern char _RCId_end, _RCId_begin;
   	   extern auxMain();
	   extern int moncomPhysmem;

	   if (!pageSize)
	    { pageSize = getpagesize();
	    }

#if FULLSEND
	   if (sendtext (KAFD(cpu)) < 0)
	    { fatalMsg (-1, "can't send text\n");
	    }
	   if (senddata (KAFD(cpu)) < 0)
	    { fatalMsg (-1, "can't send data\n");
	    }
#else
	   textSize = (unsigned int)&_RCIt_end - (unsigned int)&_RCIt_begin;
	   dataSize = (unsigned int)&_RCId_end - (unsigned int)&_RCId_begin;

	   if (sendText (KAFD(cpu), &_RCIt_begin, textSize) < 0)
	    { errorMsg (getErrorCode(0), cantLoadCpuMsg, fxnName, cpu);
	      return (-1);
	    }
	   if (sendData (KAFD(cpu), &_RCId_begin, dataSize) < 0)
	    { errorMsg (getErrorCode(0), cantLoadCpuMsg, fxnName, cpu);
	      return (-1);
	    }
	
	   /* we have to load crt0 so that errno is accessible */

	   if (sendText (KAFD(cpu), crt0t_begin, pageSize) < 0)
	    { errorMsg (getErrorCode(0), cantLoadCpuMsg, fxnName, cpu);
	      return (-1);
	    }
	   if (sendData (KAFD(cpu), crt0d_begin, pageSize) < 0)
	    { errorMsg (getErrorCode(0), cantLoadCpuMsg, fxnName, cpu);
	      return (-1);
	    }

	   /* ----- Now set up the shared control memory ----- */
#endif

	   if (sharedMonitorMemory (cpu) < 0)
	    { return (-1);
	    }

	   if (sendExec (KAFD(cpu), auxMain, cpu) < 0)
	    { errorMsg (EInternal, cantStartCpuMsg, fxnName, cpu);
	      return (-1);
	    }

	   if (issueMonitorCommand (MON_INITIALIZE, 1<<cpu, 0) < 0)
	    { errorMsg (EInternal, cantExecMonCmdMsg, fxnName);
	      return (-1);
	    }
	 }

	return (1);
#else
	errorMsg (ENotSupported, noAuxCpusMsg, fxnName);
	return (-1);
#endif /* defined(uVAX_CFG) */
}

sharedMonitorMemory (cpu)
int cpu;
{
	/* Sets up the shared data area used to communicate between
	   the planning task and the monitors. These variables are defined
	   in auxShare.c. This routine exports them to the Qbus from
	   the arbiter, if necessary, and then has the auxilary CPU
	   import them.
	*/

	extern char moncomMemBegin[];
	extern char moncomMemEnd[];

	static exportVal = 0;
	static char *address;
	static int size;

	char *fxnName = "sharedMonitorMemory()";

#if !defined(uVAX_CFG)
	if (cpu != ARBITER_CPU)
	 { errorMsg (ENotSupported, noAuxCpusMsg, fxnName);
	   return (-1);
	 }
#endif

	if (!exportVal)
  	 { int pad1;
	   int pad2;

	   if (!pageSize)
	    { pageSize = getpagesize();
	    }
	   if ((int)moncomMemBegin % pageSize)
	    { pad1 = pageSize - ((int)moncomMemBegin % pageSize);
	    }
	   else
	    { pad1 = 0;
	    }
	   if ((int)moncomMemEnd % pageSize)
	    { pad2 = pageSize - ((int)moncomMemEnd % pageSize);
	    }
	   else
	    { pad2 = 0;
	    }
	   address = &moncomMemBegin[pad1];
	   size = (unsigned int)(&moncomMemEnd[pad2]) - (unsigned int)address;

	   if ((exportVal = exportMemory (ARBITER_CPU, address, size, cpu)) < 0)
	    { errorMsg (getErrorCode(0), cantExportSmemMsg, fxnName,
					 ARBITER_CPU);
	      exportVal = 0;
	      return (-1);
	    }
	   requestFlagInit (&exportVal);
	 }
 	if (importMemory (cpu, exportVal, size, address) < 0)
  	 { errorMsg (getErrorCode(0), cantImportSmemMsg, fxnName, cpu);
	   return (-1);
	 }
	return (1);
}

cpuSignal (cpu, sig)
int cpu;
int sig;
{
	/* Send a signal message to the indicated CPU */

	char *fxnName = "cpuSignal()";
	int ioctlArg = sig;

	extern int errno;

#if defined(uVAX_CFG)
	if (ioctl(KAFD(cpu), KAIOC_SIGNAL, &ioctlArg) < 0)
	 { errorMsg (EInternal, badIoctlMsg, fxnName, "SIGNAL", errno);
	   return (-1);
	 }
	return (1);
#else
	if (cpu != ARBITER_CPU)
	 { errorMsg (ENotSupported, noAuxCpusMsg, fxnName);
	   return (-1);
	 }
#endif
}

static float clockResolution;

startRciClock (hostCpu, oldHostCpu)
int hostCpu;
int oldHostCpu;
{

	char *fxnName = "startRciClock()";

	struct clockControlMsg msg;

	/* if we are starting the RCI clock on the ARBITER CPU, then ...

	   See if we can open an RCI clock device. If we can, then
	   get the resolution from the parameter file and go with that
	   clock instead. Otherwise, use the built in VAX clock
	*/

#if !defined(uVAX_CFG)
	if (hostCpu != ARBITER_CPU || oldHostCpu != UNDEF)
	 { errorMsg (ENotSupported, noAuxCpusMsg, fxnName);
	   return (-1);
	 }
#endif

	if (hostCpu == ARBITER_CPU)
	 { int status;
	   int period;

	   if ((status = rciArbClkGetPeriod (&period)) < 0)
	    { errorMsg (getErrorCode(0), nullMsg, fxnName);
	      return (-1);
	    }
	   else if (status == 0)
	    { msg.resolution = 10.0;	/* VAX clock resolution */
	    }
	   else
	    { msg.resolution = period;
	    }
	 }
	else
	 { msg.resolution = 10.0;	/* VAX clock resolution */
	 }

	msg.hostCpu = oldHostCpu;
	if (oldHostCpu != UNDEF)
	 { msg.oldResolution = clockResolution;
	 }

	PUT_MSG (msg, clockControlMsg, hostCpu);
	if (issueMonitorCommand (MON_START_CLOCK, (1<<hostCpu), 0) < 0)
	 { errorMsg (EInternal, cantExecMonCmdMsg, fxnName);
	   return (-1);
	 }

	clockResolution = msg.resolution;
	return (1);

}

importRciClock (hostCpu, targetCpu, reInitClock)
int hostCpu;
int targetCpu;
int reInitClock;
{
	char *fxnName = "importRciClock()";

	struct clockControlMsg msg;

#if !defined(uVAX_CFG)
	if (hostCpu != ARBITER_CPU || targetCpu != ARBITER_CPU)
	 { errorMsg (ENotSupported, noAuxCpusMsg, fxnName);
	   return (-1);
	 }
#endif

	msg.hostCpu = hostCpu;
	msg.resolution = clockResolution;
	msg.reInitClock = reInitClock;

	PUT_MSG (msg, clockControlMsg, targetCpu);	
	if (issueMonitorCommand (MON_IMPORT_CLOCK, (1<<targetCpu), 0) < 0)
	 { errorMsg (EInternal, cantExecMonCmdMsg, fxnName);
	   return (-1);
	 }
	return (1);
}

exportRciClock (hostCpu, targetCpu, exportCode)
int hostCpu;
int targetCpu;
int exportCode;
{
	char *fxnName = "exportRciClock()";

	struct clockControlMsg msg;

#if !defined(uVAX_CFG)
	if (hostCpu != ARBITER_CPU || targetCpu != ARBITER_CPU)
	 { errorMsg (ENotSupported, noAuxCpusMsg, fxnName);
	   return (-1);
	 }
#endif

	msg.targetCpu = targetCpu;
	msg.exportCode = exportCode;		/* VAX clock for now */

	PUT_MSG (msg, clockControlMsg, hostCpu);	
	if (issueMonitorCommand (MON_EXPORT_CLOCK, (1<<hostCpu), 0) < 0)
	 { errorMsg (EInternal, cantExecMonCmdMsg, fxnName);
	   return (-1);
	 }
	return (1);
}

#if defined(uVAX_CFG)

auxCrashInfo (crashInfo, cpu)
CRASHINFO *crashInfo;
int *cpu;
{


	/* Returns 1 and provides crash information if a task running on 
	   the indicated cpu has crashed, and returns 0 otherwise. This
	   is used by the crash handler as it polls to determine where
	   something went wrong.
	*/

	char *fxnName = "auxCrashInfo()";
	int n;

	for (n=1; n<MAX_CPUS; n++)
	 { if (KAFD(n) != UNDEF)
	    { if (ioctl (KAFD(n), KAIOC_GETCRASH, &crashInfo) < 0)
	       { errorMsg (EInternal, badIoctlMsg, fxnName, "GETCRASH",errno);
	       }
	      if (crashInfo->type != CI_NULL)
	       { *cpu = n;
		 return (1);
	       }
	    }
	 }
	return (0);
}

#else

auxCrashInfo()
{
	return (0);
}

#endif

#define MONITOR_TIMEOUT		1000000	/* also defined in primitives.c */

extern char moncomCmd[];

issueMonitorCommand (command, cpuMask, soft)
int command;
unsigned long cpuMask;
int soft;		/* can do at user level on arbiter CPU */
{
	char *fxnName = "issueMonitorCommand()";
	unsigned long mask;
	int cpu, k;

#if !defined(uVAX_CFG)
	if (cpuMask & AUXILIARY_MASK)
	 { errorMsg (ENotSupported, noAuxCpusMsg, fxnName);
	   return (-1);
	 }
#endif

	cpu = 0;
	for (mask = cpuMask; mask; mask >>= 1)
	 { if (mask & 1)
	    { moncomCmd[cpu] = command;
	    }
	   cpu++;
	 }

	if (cpuMask & ARBITER_MASK)
	 { if (soft)
	    { monitorCommand (moncomCmd[ARBITER_CPU] | MON_USERLEVEL);
	      moncomCmd[ARBITER_CPU] = 0;
	    }
	   else
	    { if (arbMonitorCommand() < 0)
	       { errorMsg (EInternal, cantExecArbCmdMsg, fxnName);
		 return (-1);
	       }
	    }
	 }

	k = MONITOR_TIMEOUT;
	cpu = 0;
	for (mask = cpuMask; mask; mask >>= 1)
	 { if (mask & 1)
	    { while (moncomCmd[cpu] && --k)
		 ;
	      if (k == 0)
	       { errorMsg (EInternal, monTimeoutMsg, fxnName, cpu);
		 return (-1);
	       }
	    }
	   cpu++;
	 }
	return (1);
}

void *msgMemoryAddr[MAX_CPUS];

createMsgMemory (cpu)
int cpu;
{
	void *addr;
	int x;
	char *fxnName = "createMsgMemory()";

	struct msgMemoryMsg msg;

#if !defined(uVAX_CFG)
	if (cpu != ARBITER_CPU)
	 { errorMsg (ENotSupported, noAuxCpusMsg, fxnName);
	   return (-1);
	 }
#endif

	/* allocates message pool memory on the indicated CPU */

	if ((addr = allocSharedMemUpper (cpu, MSG_POOL_SIZE)) == NULL)
	 { errorMsg (getErrorCode(0), cantSetupSmemMsg, fxnName, cpu);
	   return (-1);
	 }
	msgMemoryAddr[cpu] = addr;
	msg.addr = addr;

	/* send memory address to the cpu */

	msg.cpu = cpu;
	PUT_MSG (msg, msgMemoryMsg, cpu);
	if (issueMonitorCommand (MON_MSGMEM_ASSIGN, (1<<cpu), YES) < 0)
	 { errorMsg (EInternal, cantExecMonCmdMsg, fxnName);
	   return (-1);
	 }
	return (1);
}

importMsgMemory (cpu, cpuMask)
int cpu;
unsigned long cpuMask;
{
	/* imports message memory from host CPU 'cpu' onto all the CPUs
	   indicated by 'cpuMask'. It is assumed that 'cpu' is NOT
	   contained in 'cpuMask' */

	unsigned long mask;
	int k, x;

	char *fxnName = "importMsgMemory()";

	struct msgMemoryMsg msg;

#if !defined(uVAX_CFG)
	if (cpu != ARBITER_CPU || cpuMask & AUXILIARY_MASK)
	 { errorMsg (ENotSupported, noAuxCpusMsg, fxnName);
	   return (-1);
	 }
#endif
	if (cpuMask & (1<<cpu))
	 { errorMsg (EInternal, cpuInCpuMaskMsg, fxnName);
	   return (-1);
	 }

	k = 0;
	for (mask = cpuMask; mask; mask >>= 1)
	 { if (mask & 1)
	    { /* first, import the new CPU's memory onto all the
		 other CPUs.*/

	      msg.addr = msgMemoryAddr[cpu];
	      msg.cpu = cpu;
	      PUT_MSG (msg, msgMemoryMsg, k);
	      if (issueMonitorCommand (MON_MSGMEM_ASSIGN, (1<<k), YES) < 0)
	       { errorMsg (EInternal, cantExecMonCmdMsg, fxnName);
		 return (-1);
	       }

	      msg.addr = msgMemoryAddr[k];
	      msg.cpu = k;
	      PUT_MSG (msg, msgMemoryMsg, cpu);
	      if (issueMonitorCommand (MON_MSGMEM_ASSIGN, (1<<cpu), YES) < 0)
	       { errorMsg (EInternal, cantExecMonCmdMsg, fxnName);
		 return (-1);
	       }
	    }
	   k++;
	 }
	return (1);

}
