/*	mem.c	2.2	1/14/80	*/

/*
 * Memory special file
 *	minor device 0 is physical memory
 *	minor device 1 is kernel memory 
 *	minor device 2 is EOF/RATHOLE
 *	minor device 3 is unibus memory (addressed by shorts)
 */

#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/pte.h"
#include "../h/mtpr.h"
#include "../h/vm.h"
#include "../h/cmap.h"

mmread(dev)
{
	register int o;
	register unsigned c, v;

	switch (minor(dev)) {

	case 0:
		while (u.u_count != 0 && u.u_error == 0) {
			if (fubyte(u.u_base) == -1)
				goto fault;
			v = btop(u.u_offset);
			if (v >= physmem)
				goto fault;
			*(int *)mmap = v | (PG_V | PG_KR);
			mtpr(TBIS, vmmap);
			o = (int)u.u_offset & PGOFSET;
			c = min((unsigned)(NBPG - o), u.u_count);
			if (copyout((caddr_t)&vmmap[o], u.u_base, c))
				goto fault;
			u.u_count -= c;
			u.u_base += c;
			u.u_offset += c;
		}
		return;

	case 1:
		if (u.u_uid != 0) {
			if ((caddr_t)u.u_offset < (caddr_t)&umbabeg &&
			    (caddr_t)u.u_offset + u.u_count >= (caddr_t)&umbabeg)
				goto fault;
			if ((caddr_t)u.u_offset >= (caddr_t)&umbabeg &&
			    (caddr_t)u.u_offset < (caddr_t)&umbaend)
				goto fault;
		}
		if (!kernacc((caddr_t)u.u_offset, u.u_count, B_READ))
			goto fault;
		if (copyout((caddr_t)u.u_offset, u.u_base, u.u_count))
			goto fault;
		c = u.u_count;
		u.u_count = 0;
		u.u_base += c;
		u.u_offset += c;
		return;

	case 2:
		return;

	case 3:
		if (!kernacc((caddr_t)u.u_offset, u.u_count, B_READ))
			goto fault;
		if (!useracc(u.u_base, u.u_count, B_WRITE))
			goto fault;
		UNIcpy((caddr_t)u.u_offset, u.u_base, u.u_count, B_READ);
		c = u.u_count;
		u.u_count = 0;
		u.u_base += c;
		u.u_offset += c;
		return;
	}
fault:
	u.u_error = EFAULT;
	return;
}

mmwrite(dev)
{
	register int o;
	register unsigned c, v;

	switch (minor(dev)) {

	case 0:
		while (u.u_count != 0 && u.u_error == 0) {
			if (fubyte(u.u_base) == -1)
				goto fault;
			v = btop(u.u_offset);
			if (v >= physmem)
				goto fault;
			*(int *)mmap = v | (PG_V | PG_KW);
			mtpr(TBIS, vmmap);
			o = (int)u.u_offset & PGOFSET;
			c = min((unsigned)(NBPG - o), u.u_count);
			if (copyin(u.u_base, (caddr_t)&vmmap[o], c))
				goto fault;
			u.u_count -= c;
			u.u_base += c;
			u.u_offset += c;
		}
		return;

	case 1:
		if (!kernacc((caddr_t)u.u_offset, u.u_count, B_WRITE))
			goto fault;
		if (copyin(u.u_base, (caddr_t)u.u_offset, u.u_count))
			goto fault;
		u.u_base += u.u_count;
		u.u_offset += u.u_count;
		u.u_count = 0;
		return;

	case 2:
		u.u_offset += u.u_count;
		u.u_count = 0;
		return;

	case 3:
		if (!kernacc((caddr_t)u.u_offset, u.u_count, B_WRITE))
			goto fault;
		if (!useracc(u.u_base, u.u_count, B_READ))
			goto fault;
		UNIcpy((caddr_t)u.u_offset, u.u_base, u.u_count, B_WRITE);
		u.u_base += u.u_count;
		u.u_offset += u.u_count;
		u.u_count = 0;
		return;
	}
fault:
	u.u_error = EFAULT;
	return;
}

/*
 * UNIBUS Address Space <--> User Space transfer
 */
UNIcpy(uniadd, usradd, bknt, direct)
	caddr_t uniadd, usradd;
	unsigned bknt;
{
	register short *from, *to;
	register int i;
 
	if (direct == B_READ) {
		from = (short *) uniadd;
		to = (short *) usradd;
	} else {
		from = (short *) usradd;
		to = (short *) uniadd;
	}
	for (i = (bknt>>1); i > 0; i--)
		*to++ = *from++;
}
