/*-=-=-=-=-=-=-=-=-=-=-=- 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:	remoteMemory
 *
 * DESCRIPTION:	Fairly low level routines to export, import, and
 *		remotely allocate regions of memory on different
 *		CPUs.
 *
 * AUTHOR/DATE:	John Lloyd, Fall 1989
 *
 * MODIFICATION HISTORY:
 *
 *--------------------------------------------------------------------------*/

#include <errno.h>

#include "../h/rciConfig.h"
#include "../h/cdefs.h"
#include "../h/errorCodes.h"

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

extern int errno;
static int pagesize = 0;
char *sbrk();

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

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

static char *badIoctlMsg	= "%s -- %s ioctl failed, errno %d\n";
static char *badSizeMsg		= "%s -- illegal size (0x%x) requested\n";
static char *badAddrMsg		= "%s -- illegal address (0x%x) requested\n";
static char *noAuxCpusMsg	= "%s -- aux cpus not implemented\n";
static char *cantAllocMemMsg	= "%s -- can't allocate memory on cpu %d\n";
static char *cantLockMemMsg     = "%s -- can't lock 0x%x to 0x%x, errno %d\n";

exportMemory (cpu, addr, size, aux)
int cpu, size, aux;
void *addr;
{
	/* Exports the region of memory indicated by 'addr' and 'size'
	   on CPU 'cpu' to the external bus. Returns a handle describing
	   the location of this memory on the external bus.
	 */
	   
	char *fxnName = "exportMemory()";

#if defined(uVAX_CFG)
	struct qballocreq qbrq;
	struct sharedesc shd;

	if (!pagesize)
	 { pagesize = getpagesize();
	 }
	if (size <= 0 || (size % pagesize) != 0)
	 { errorMsg (EInternal, badSizeMsg, fxnName, size);
	   return (-1);
	 }
#define VAX_PAGESIZE	512
	if ((int)addr != ((int)addr & ~(VAX_PAGESIZE-1)) )
	 { errorMsg (EInternal, badAddrMsg, fxnName, addr);
	   return (-1);
	 }
	qbrq.size = size;
	qbrq.cpu = cpu;
	if (ioctl (KAFD(aux), KAIOC_QBALLOC, &qbrq) < 0)
	 { errorMsg (errno == ENOMEM ? EMemory : EInternal,	/* EShareMem*/
		     badIoctlMsg, fxnName, "QBALLOC", errno);
	   return (-1);
	 }
	shd.size = size;
	shd.addr = (char *)addr;
	shd.qbhandle = qbrq.qbhandle;

	if (cpu == ARBITER_CPU)
  	 { if (ioctl (KAFD(cpu), KAIOC_EXPORT, &shd) < 0)
	    { errorMsg (EInternal, badIoctlMsg, fxnName, "EXPORT", errno);
	      return (-1);
	    }
	 }
	else
  	 { if (ioctl (KAFD(cpu), KAIOC_SEXPORT, &shd) < 0)
	    { errorMsg (EInternal, badIoctlMsg, fxnName, "SEXPORT", errno);
	      return (-1);
	    }
	 }
	return (shd.qbhandle);
#else
	errorMsg (ENotSupported, noAuxCpusMsg, fxnName);
	return (-1);
#endif
}

importMemory (cpu, qbhandle, size, addr)
int cpu, qbhandle, size;
void *addr;
{
	/* Imports the exported memory region described by 'handle'
	   (returned from exportMemory()) to the local virtual region
	   described by 'addr' and 'size' on CPU 'cpu'.
	 */

	char *fxnName = "importMemory()";

#if defined(uVAX_CFG)
	struct sharedesc shd;

	if (!pagesize)
	 { pagesize = getpagesize();
	 }
	if (size <= 0 || (size % pagesize) != 0)
	 { errorMsg (EInternal, badSizeMsg, fxnName, size);
	   return (-1);
	 }
	shd.size = size;
	shd.addr = (char *)addr;
	shd.qbhandle = qbhandle;
	if (cpu == ARBITER_CPU)
	 { if (ioctl (KAFD(cpu), KAIOC_IMPORT, &shd) < 0)
	    { errorMsg (EInternal, badIoctlMsg, fxnName, "IMPORT", errno);
	      return (-1);
	    }
	 }
	else
	 { if (ioctl (KAFD(cpu), KAIOC_SIMPORT, &shd) < 0)
	    { errorMsg (EInternal, badIoctlMsg, fxnName, "SIMPORT", errno);
	      return (-1);
	    }
	 }
	return (1);
#else
	errorMsg (ENotSupported, noAuxCpusMsg, fxnName);
	return (-1);
#endif
}

#ifndef vax
#define ETABLEFULL	0	/* this error doe snot exist on suns */
#endif

localMemoryAlloc (cpu, addr, size)
int cpu, size;
void **addr;
{
	/* Allocate 'size' bytes of local memory on CPU 'cpu' and
	   return the address in 'addr'.
	 */

	char *fxnName = "localMemoryAlloc()";
	char *mem;

	if (!pagesize)
	 { pagesize = getpagesize();
	 }
	if (size <= 0 || (size % pagesize) != 0)
	 { errorMsg (EInternal, badSizeMsg, fxnName, size);
	   return (-1);
	 }
	if (cpu == ARBITER_CPU)
	 { int pad;
#ifndef VXWORKS_CFG
	   pad = pagesize - ((unsigned int)sbrk(0)%pagesize);
	   if (pad != 0 && pad != pagesize)
	    { sbrk(pad);
	    }
#endif
	   if ((mem = (char*)sbrk(size)) == (char *)-1)
	    { errorMsg (EMemory, cantAllocMemMsg, fxnName, ARBITER_CPU);
	      return (-1);
	    }
	   if (lockMemory ((void*)mem, size, /*text=*/0) < 0)
	    { errorMsg (errno == ETABLEFULL ? EMemory : EInternal, 
			cantLockMemMsg, fxnName, mem, mem+size, errno);
	      return (-1);
	    }
	 }
	else
	 { 
#if defined(uVAX_CFG)
	   int data = size;
	   if (ioctl (KAFD(cpu), KAIOC_SBRK, &data) < 0)
	    { errorMsg (EInternal, badIoctlMsg, fxnName, "SBRK", errno);
	      return (-1);
	    }
	   if ((mem = (char*)data) == (char *)-1)
	    { errorMsg (EMemory, cantAllocMemMsg, fxnName, cpu);
	      return (-1);
	    }
#else
	   errorMsg (ENotSupported, noAuxCpusMsg, fxnName);
	   return (-1);
#endif
	 }
	*addr = (void*)mem;
	return (1);
}

#if TEST

/* this testing program works by openning /dev/ka1 and then calling
   various memory allocation routines, referencing that CPU if
   necessary. We assume that the aux cpu is up and running the
   minikernel */

#include <sys/file.h>

#define SLAVE_KERNEL_VERSION	"RCI minikernel 1.00"

#define	COMMAND(s)	(strcmp (key_buf, s) == 0)
#define	COMMANDS(s)	(strncmp (key_buf, s, strlen(s)) == 0)
char key_buf [256];

int cpuFileDescriptors[2];

main()
{
#define AUX	1
#define ARB	ARBITER_CPU

	int status;
	int address;
	int size;
	int qbhandle;
	int data;
	unsigned *addr;

	char *keytree, *tree_match_parse(), *tree_match_err_at();

	keytree = tree_match_parse("					\
(	quit								\
	exportMemory							\
	 ([arb aux]							\
	   (%x", &address, ",,,		\"address (hex)\"		\
	     (%d", &size, ",,,		\"size (dec)\")))		\
	importMemory							\
	 ([arb aux]							\
	   (%x", &qbhandle, ",,,	\"qbhandle (hex)\"		\
	     (%d", &size, ",,,		\"size (dec)\"			\
	       (%x", &address, ",,,	\"address (hex)\"))))		\
	localMemoryAlloc						\
	 ([arb aux]							\
	   (%d", &size, ",,,		\"size (dec)\"))		\
	peek								\
	 (%x", &addr, ",,,		\"address (hex)\")		\
	poke								\
	 (%x", &addr, ",,,		\"address (hex)\"		\
	   (%x", &data, ",,,		\"data (hex)\"))		\
	dump 								\
	 (%x", &addr, ",,,		\"address (hex)\"		\
	   (%d", &size, ",,,		\"number of long words \"))	\
	fill								\
	 (%x", &addr, ",,,		\"address (hex)\"		\
	   (%d", &size, ",,,		\"number of long words \"))	\
	verify								\
	 (%x", &addr, ",,,		\"address (hex)\"		\
	   (%d", &size, ",,,		\"number of long words \"	\
	      (%x", &address, ",,,	\"old address (hex)\")))	\
)",0);

	if (keytree == 0)
	 { fatalMsg (-1, "Ctree parse error at %.30s\n", tree_match_err_at());
	 }
	if ((KAFD(ARB) = open ("/dev/ka0", O_RDWR)) < 0)
  	 { fatalMsg (-1, "can't open /dev/ka1, errno %d\n", errno);
	 }
	if ((KAFD(AUX) = open ("/dev/ka1", O_RDWR)) < 0)
  	 { fatalMsg (-1, "can't open /dev/ka1, errno %d\n", errno);
	 }
 	if ((status = auxKernelVerify (KAFD(AUX), SLAVE_KERNEL_VERSION)) < 0)
  	 { printErrors ();
	   exit (-1);
	 }
 	if (status == 0)
  	 { fatalMsg (-1, "not running aux kernel \"%s\"\n", SLAVE_KERNEL_VERSION);
	 }
	do
 	 { tree_match (keytree, "MEMORYALLOC> ", key_buf);

	   if (COMMAND("exportMemory arb"))
	    { status = exportMemory (ARB, address, size, AUX);
	      if (status >= 0)
	       { printf ("qbhandle = 0x%x\n", status);
	       }
	    }
	   else if (COMMAND ("exportMemory aux"))
	    { status = exportMemory (AUX, address, size, 0);
	      if (status >= 0)
	       { printf ("qbhandle = 0x%x\n", status);
	       }
	    }
	   else if (COMMAND ("importMemory arb"))
	    { status = importMemory (ARB, qbhandle, size, address);
	    }
	   else if (COMMAND ("importMemory aux"))
	    { status = importMemory (AUX, qbhandle, size, address);
	    }
	   else if (COMMAND ("localMemoryAlloc arb"))
	    { status = localMemoryAlloc (ARB, &address, size);
	      if (status >= 0)
	       { printf ("address =  0x%x\n", address);
	       }
	    }
	   else if (COMMAND ("localMemoryAlloc aux"))
	    { status = localMemoryAlloc (AUX, &address, size);
	      if (status >= 0)
	       { printf ("address =  0x%x\n", address);
	       }
	    }
	   else if (COMMAND ("peek"))
	    { printf ("0x%x\n", *addr);
	    }
	   else if (COMMAND ("poke"))
	    { *addr = data;
	      printf ("0x%x\n", *addr);
	    }
	   else if (COMMAND ("dump"))
	    { int i;
	      for (i=0; i<size; i++)
	       { printf ("0x%x %x\n", &addr[i], addr[i]);
	       }
	    }
	   else if (COMMAND ("fill"))
	    { int i;
	      for (i=0; i<size; i++)
	       { addr[i] = (int)&addr[i];
	       }
	    }
	   else if (COMMAND ("verify"))
	    { int i;
	      int numbad = 0;

	      for (i=0; i<size; i++)
	       { if (addr[i] != address + sizeof(int)*i)
		  { printf ("Bad pattern: 0x%x = %x\n", &addr[i], addr[i]);
		    numbad++;
		  }
		 if (numbad >= 10)
		  { printf ("more than 10 bad entries; stopping\n");
		    break;
		  }
	       }
	    }
	   if (status < 0)
	    { printErrors();
	      clearErrors();
	    }
	 }
 	while (!(COMMAND("quit")));
}

#endif
