static char rcsid[] = "$Header: mem.c,v 820.1 86/12/04 19:53:40 root Exp $";
static char sccsid[]="%W% %Y% %Q% %G%";

/************************************************************************
 *									*
 *				Copyright 1984, 1985			*
 *			VALID LOGIC SYSTEMS INCORPORATED		*
 *									*
 *	This listing contains confidential proprietary information	*
 *	which is not to be disclosed to unauthorized persons without	*
 *	written consent of an officer of Valid Logic Systems 		*
 *	Incorporated.							*
 *									*
 *	The copyright notice appearing above is included to provide	*
 *	statutory protection in the event of unauthorized or 		*
 *	unintentional public disclosure.				*
 *									*
 ************************************************************************/

/*	mem.c	4.12	82/12/17	*/

/*
 * Memory special file
 *
 * Minor device map:
 *		0	/dev/mem 	Physical memory device
 *		1	/dev/kmem	Kernel memory device
 *		2	/dev/null	Data sink
 *		4	/dev/iospace	mbio space for diagnostics
#ifndef	WHITE
 *		3	/dev/lasar	LASAR validation device
 *		16	/dev/realchip0	Realchip device #0
 *		17	/dev/realchip1	Realchip device #1
 *		18	/dev/realchip2	Realchip device #2
 *		19	/dev/realchip3	Realchip device #3
 *		20	/dev/ffp	SKY FFP
 *		21	/dev/colorctl	Color control
 *		22	/dev/colormem	Color memory
 *		23	/dev/realfast0	Realfast device #0
#endif	WHITE
 */

#include "../DOT/sky.h"		/* For NSKY */
#include "../machine/pte.h"

#define	MemDotCSource		/* Enable initialization of 'boardmap[]' */
#include "../machine/mem.h"

#include "../h/param.h"
#include "../h/dir.h"
#include "../h/user.h"
#include "../h/conf.h"
#include "../h/buf.h"
#include "../h/systm.h"
#include "../h/vm.h"
#include "../h/cmap.h"
#include "../h/uio.h"
#ifdef s32
#include "../h/file.h"
#include "../h/ioctl.h"
#include "../h/proc.h"
#ifndef WHITE
#include "../usr.include/setjmp.h"
#include "../s32dev/skyreg.h"
#endif WHITE
#endif s32

#include "../machine/mtpr.h"

extern short ramdiskenabled, ramdiskdebug;

mmopen(dev, flag)
   dev_t dev;
{
#ifndef	WHITE
	register int boardnum = minor(dev);

	/* If a special file representing a board in
	 * the system is being opened, it is checked
	 * to verify that it exists @ (mp->paddr + mp->offset),
	 * and that it responds to a read with the specified
	 * data value (mp->value masked by mp->mask).
	 *
	 * Then, the user may issue an ioctl() to map the
	 * board into his address space.
	 * If the board is the SKY FFP, then we do the
	 * ioctl() as well.
 	 */

	if( ramdiskdebug )
		printf( "in mmopen, dev=%d, flag=%d\n", dev, flag );

	if (isboard(boardnum)) {
		register struct boardmap_t *mp = &boardmap[boardnum];
		jmp_buf			 jb;
		int			 *saved_jb;
		extern	int		 *nofault;

		saved_jb = nofault;
		if (setjmp(jb)) {
			nofault = saved_jb;
			return(ENXIO);
		}

#define	FODPTE	((struct fpte *)mmap)		/* Shorthand */

#ifdef	M68020
		if (chipType==CHIPTYPE_68020) {
			if (mp->flags & BMF_NONCACHED) {
				FODPTE->pg_noncached	= 1;
				mp->nonCached		= 1;
			} else {
				mp->nonCached		= 0;
			}
		}
#endif	M68020
		*(long *)mmap		= 0;
		FODPTE->pg_fod		= 1;
		FODPTE->pg_fileno	= PG_FPHYS;
		FODPTE->pg_blkno	= btop(mp->paddr + mp->offset)
					/*
					 * H/w values! Not s/w pte bits
					 */
					| ((mp->flags & BMF_IOSPACE)
						? V_MBIO
						: V_MBMEM
					  );
		/*
		 * setsysmap() writes the entire entry
		 * directly into the physical page map
		 * for the fill-on-demand/PG_FPHYS case.
		 *
		 * So, the V_MBIO/V_MBMEM kludge immediately above
		 * works because the btop()'d MBIO paddr+offset is 0 or 1
		 * AND the V_MBIO/V_MBMEM value is 0x1000/0;
		 * AND because pg_blkno is large enough to hold these values.
		 */
		setsysmap(btop(vmmap), mmap, 1);
		nofault = jb;

		if ((*(u_short *)&vmmap[(mp->paddr + mp->offset) & PGOFSET] &
				mp->mask) != mp->value)
			return(ENXIO);
		nofault = saved_jb;

#if NSKY > 0
		/*
		 * Check to see if its a SKY FFP.
		 * Shared automap upon open().
		 */
		if (issky(boardnum)) { 
			register int	j;

			/*
			 * Only map if correct autoconfig
			 */
			if (!skysvs)
				return EFAULT;
			/*
			 * Map the board in...
			 */
			if (j = mmioctl(dev, IOCMAPBOARD, &skysvs, flag))
				return(j);

			u.u_fppc = SKYNOP;

		/*
		 * Test to see if it has already been opened
		 * (with exclusive access;)
		 * and refuse the open() if 'isopen' is nonzero
		 */

		} else if( !( ramdiskenabled && boardnum == MEM_RAMDISK ) && mp->isopen)
			return(EIO);
#endif NSKY > 0
		mp->isopen++;
	}
#endif	WHITE

	return(0);
}

mmclose(dev, flag)
	dev_t dev;
{
#ifndef WHITE
	register int boardnum = minor(dev);

	if (isboard(boardnum))
		boardmap[boardnum].isopen--;
#endif WHITE
}

mmread(dev, uio)
	dev_t		 dev;
	struct	uio	*uio;
{

	return (mmrw(dev, uio, UIO_READ));
}

mmwrite(dev, uio)
	dev_t		 dev;
	struct	uio	*uio;
{

	return (mmrw(dev, uio, UIO_WRITE));
}

mmrw(dev, uio, rw)
	dev_t	dev;
	struct	uio	*uio;
	enum	uio_rw	 rw;
{
	register int		o;
	register u_int		c, v;
	register struct	iovec	*iov;
	int			error = 0;
#ifndef s32
	extern	int		umbabeg, umbaend;
#endif !s32


	while (uio->uio_resid > 0 && error == 0) {
		iov = uio->uio_iov;
		if (iov->iov_len == 0) {
			uio->uio_iov++;
			uio->uio_iovcnt--;
			if (uio->uio_iovcnt < 0)
				panic("mmrw");
			continue;
		}
		switch (minor(dev)) {

		/*
		 * minor device 0 is physical memory
		 */
		case MEM_MINOR:
		case MEM_RAMDISK:
			v = btop(boardmap[minor(dev)].paddr + uio->uio_offset);
#ifdef notdef
			if (v >= physmem)
				goto fault;
#endif
			*(int *)mmap = v | PG_V |
				(rw == UIO_READ ? PG_KR : PG_KW);
#ifdef s32
			setsysmap(btop(vmmap), mmap, 1);
#else s32
			mtpr(TBIS, vmmap);
#endif s32
			o = (int)uio->uio_offset & PGOFSET;
			c = min(   (u_int)(NBPG - o), (u_int)iov->iov_len);
			c = min(c, (u_int)(NBPG -      ((int)iov->iov_base&PGOFSET)));
			error = uiomove((caddr_t)&vmmap[o], (int)c, rw, uio);
			continue;

		/*
		 * minor device 1 is kernel memory
		 */
		case KMEM_MINOR:
#ifndef s32
			if ((caddr_t)uio->uio_offset < (caddr_t)&umbabeg &&
			    (caddr_t)uio->uio_offset + uio->uio_resid >= (caddr_t)&umbabeg)
				goto fault;
			if ((caddr_t)uio->uio_offset >= (caddr_t)&umbabeg &&
			    (caddr_t)uio->uio_offset < (caddr_t)&umbaend)
				goto fault;
#endif !s32
			c = iov->iov_len;
			if (!kernacc((caddr_t)uio->uio_offset, c, rw == UIO_READ ? B_READ : B_WRITE))
				goto fault;
			error = uiomove((caddr_t)uio->uio_offset, (int)c, rw, uio);
			continue;

		/*
		 * minor device 2 is EOF/RATHOLE
		 */
		case NULL_MINOR:
			if (rw == UIO_READ)
				return (0);
			c = iov->iov_len;
			break;
#ifdef s32
		default:
			return(EIO);
#endif s32
		}

		if (error)
			break;
		iov->iov_base	+= c;
		iov->iov_len	-= c;
		uio->uio_offset	+= c;
		uio->uio_resid	-= c;
	}
	return (error);
fault:
	return (EFAULT);
}

#ifdef s32

#ifndef WHITE
/*
 * mmskymap
 *	Called from procdup (../sys/vm_proc.c) when we fork a process that
 *	has the SKY board open.  We explicitly map the board into the
 *	child so that the child can access the board.
 */
mmskymap(p)
	register struct proc *p;
{
	register struct boardmap_t *skymp = &boardmap[SKY_BOARDNUM];

	if (u.u_fppc && skysvs)
		fillphys(p, skysvs, skymp->paddr, skymp->size,
			V_MBIO/*pteFlag*/);
}
#endif not WHITE

/*
 * ioctl() interface for memory-mapped stuff.
 */
mmioctl(dev, com, data, flag)
	dev_t		dev;
	int		com;
	char		*data;
	int		flag;
{
	if( ramdiskdebug )
		printf( "in mmioctl, dev=%d\n", dev );

	switch (com)
	{

	/*
	 * If /dev/mem has been opened for writing,
	 * then the caller may use IOCMAPPHYS to map
	 * PHYSICAL memory into his virtual address
	 * space.
	 */
	case IOCMAPPHYS:
	{
		struct mapphys *mp = (struct mapphys *)data;

		if (minor(dev) != MEM_MINOR)
			return(EIO);
		if (!(flag & FWRITE))
			return(EACCES);
#ifdef	M68020
		/*
		 * This now has the side-effect
		 * of setting all of the pages so mapped,
		 * to being NONCACHED.
		 * BUG: is this too Draconian?
		 *
		 * Grumble, Grumble .....
		 * The f...ing loadskyffp has an intimate knowledge
		 * of how to use IOCMAPPHYS to map in I/O space
		 * rather than physical memory, as god intended it.
		 * We must therefore check to see if we are doing sky 
		 * braindamage and gracefully recover from it.
		 */
		fillphys(u.u_procp, mp->vaddr, mp->paddr, mp->size,
			((chipType==CHIPTYPE_68020) &&
			 (!((int) mp->paddr & 0x1000000))
				? V_NONCACHED
				: 0/*No pteFlag*/));
#else	M68020
		fillphys(u.u_procp, mp->vaddr, mp->paddr, mp->size,
			0/*No pteFlag*/);
#endif	M68020
		return(u.u_error);
	}

#ifndef	WHITE
	/*
	 * If a special file representing i/o space
	 * in the system has been opened, it may be mapped
	 * into the user's address space using the
	 * address he specifies.
	 * The open() has already checked to see that the board
	 * exists; now we map the board in using fillphys().
	 */
	case IOCMAPIOSPACE:
	{
		caddr_t	 vaddr = *(caddr_t *)data;
		register struct boardmap_t *mp = &boardmap[minor(dev)];

		if (!isboard(minor(dev)))
			return(ENXIO);

		/*
		 * mbio pages aren't cached,
		 * but they do need to be V_MBIO mapped.
		 */
		fillphys(u.u_procp, vaddr, mp->paddr, mp->size,
			V_MBIO);
		return(u.u_error);
	}
#endif	WHITE

#ifndef WHITE
	/*
	 * If a special file representing a board in
	 * the system has been opened it may be mapped
	 * into the user's address space using the
	 * address he specifies.
	 * The open() has already checked to see that the board
	 * exists; now we map the board in using fillphys().
	 */
	case IOCMAPBOARD:
	{
		caddr_t	 vaddr = *(caddr_t *)data;
		register struct boardmap_t *mp = &boardmap[minor(dev)];

		if (!isboard(minor(dev)))
			return(ENXIO);

#ifdef	M68020
		/*
		 * This now has the side-effect
		 * of setting all of the (non-io) pages hereby mapped,
		 * to being NONCACHED.
		 * BUG: is this too Draconian?
		 */
		fillphys(u.u_procp, vaddr, mp->paddr, mp->size,
			(mp->flags & BMF_IOSPACE)
				? V_MBIO	/* Multibus i/o space h/w */
				: ((chipType==CHIPTYPE_68020)
					? V_NONCACHED
					: 0/*No pteFlag*/));
#else	M68020
		fillphys(u.u_procp, vaddr, mp->paddr, mp->size,
			(mp->flags & BMF_IOSPACE)
				? V_MBIO	/* Multibus i/o space h/w */
				: 0/*No pteFlag*/);
#endif	M68020
		return(u.u_error);
	}

#endif	WHITE

#ifndef	WHITE
	case LIOCVALIDATE:
		/*
		 * The address of the LASAR validation location
		 * is assumed to be in the boot PROMs and therefore
		 * directly addressible.
		 */
		if (minor(dev) == LASAR_MINOR) {
		register struct boardmap_t *mp= &boardmap[minor(dev)];
			bcopy(LASARADDR, data, sizeof(short));
			return 0;
		}
#endif WHITE
	case IOCPROCPROF:
		/*
		 * This probably shouldn't go here but...
		 * Turn on per-process profiling for this process
		 * number.  Get the pid from the user page process
		 * struct pointer.  Zero out the "kprof" kernel
		 * profiling array.
		 *	procprof -- Switch for kernel or per-process profile.
		 *	profpid -- Process id.
		 * NOTE >>> The size of the array is set in ../s32/locore.s
		 */
		{
		extern char kprof;
		extern short procprof;
		extern short profpid;
		int s = spl6();


		profpid = u.u_procp->p_pid;
		procprof = 1;
		bzero(&kprof, 24576);
		splx(s);
		return 0;
		}
	default:
		return(ENXIO);
	}
}
#endif s32
