/*
 * 5799-WZQ (C) COPYRIGHT IBM CORPORATION  1986,1987,1988
 * LICENSED MATERIALS - PROPERTY OF IBM
 * REFER TO COPYRIGHT INSTRUCTIONS FORM NUMBER G120-2083
 */
/* $Header:vm_machdep.c 12.0$ */
/* $ACIS:vm_machdep.c 12.0$ */
/* $Source: /ibm/acis/usr/sys/ca/RCS/vm_machdep.c,v $ */

#if !defined(lint) && !defined(NO_RCS_HDRS)
static char *rcsid = "$Header:vm_machdep.c 12.0$";
#endif

/*     vm_machdep.c    6.1     83/07/29        */
/* $Header:vm_machdep.c 12.0$ */
/* $ACIS:vm_machdep.c 12.0$ */
/* $Source: /ibm/acis/usr/sys/ca/RCS/vm_machdep.c,v $ */

#include "debug.h"
#include "mmu.h"
#include "reg.h"
#include "pte.h"
#include "io.h"

#include "param.h"
#include "systm.h"
#include "dir.h"
#include "user.h"
#include "proc.h"
#include "cmap.h"
#ifdef VFS
#include "vfs.h"
#include "../ufs/mount.h"
#else VFS
#include "mount.h"
#endif VFS
#include "vm.h"
#include "text.h"
#include "buf.h"


#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif

/*
 * Set a red zone in the kernel stack after the u. area.
 */
setredzone(pte)
	register struct pte *pte;
{
	DEBUGF (vmdebug,
		printf("setredzone( pte=%x )\n", pte));
	*(int *)(pte - 1) = PG_URKR;	  /* fake pte in memory of VAX */
}


/*
 * Map a page into the HATIPT.  Make sure the hash chains are always valid,
 * as MMU may saunter down one at any time.
 */

mapin(pte, vpage, model, count)
	struct pte *pte;
	register u_int vpage;
	register u_int model;		  /* THIS IS REALLY A STRUCT PTE */
	int count;
{
	register struct hatipt_entry *ipte, *head;
	register struct cmap *c;
	u_int sid, key;

#ifdef	DEBUG
	if (count != 1)
		panic("mapin count");
#endif

	DEBUGF(vmdebug,
		printf("mapin(pte=0x%x,vpage=0x%x,model=0x%x,count=0x%x)",
		    pte, vpage, model, count));

	while (--count >= 0) {
		/* point to the ipte for this page frame */
		ipte = &MMU_HATIPT[model & PG_PFNUM];
		if (ipte->ipt_ptr != MMU_UNDEF_PTR) { /* debug */
			printf("mapin: pfnum=%x ipte=%x ipt_ptr=%x key_addrtag=%x hat_ptr=%x\n",model&PG_PFNUM,ipte,ipte->ipt_ptr,ipte->key_addrtag,ipte->hat_ptr);
			panic("dup mapin");
		}

		/* get the segment id (placed there by memall or setsid) */
		sid = (ipte->key_addrtag & MMU_ADDRTAG_MASK) >> MMU_VPAGE_BITS;

		DEBUGF (vmdebug,
			printf(" sid=%x,vpage=%x,seg=%x,", sid, vpage & btop(0x0fffffff), (vpage & btop(0xf0000000)) >> MMU_VPAGE_BITS ));

		/* determine the vpage and segment from the virt addr */
		vpage &= btop(0x0fffffff);

		/* determine the key, write, tid, lockbits */
		key = rtakey(model);	  /* non-special segments only */

		/* fill in the fields to reflect the new virt addr and prot */
		ipte->key_addrtag = (key << MMU_KEY_SHIFT)
			|(sid << MMU_VPAGE_BITS)
			|vpage;
		ipte->w = 1;		  /* special segments only */
		ipte->tid = 0;		  /* special segments only */
		ipte->lockbits = -1;	  /* special segments only */

		/* hash to find the head of the ipte chain */
		head = &MMU_HATIPT[MMU_HASH(sid, vpage)];

		DEBUGF(vmdebug,
			printf("head=%x\n", head));

		/* link in the new ipte (first on the chain) */
		ipte->ipt_ptr = head->hat_ptr;
		set_hatptr(head, model & PG_PFNUM);

		/* paranoia */
		invalidate_tlb(vpage);

		/* set the mod bit to reflect prot; reset the referenced bit */
		set_refmod_bits(model & PG_PFNUM, 0, ((model & PG_M) != 0));

		/* remember that this page is mapped in if in cmap */
		if ((c = &cmap[pgtocm(model&PG_PFNUM)]) > cmap && c < ecmap)
			c->c_mapped = 1;

		/* set up the vax page table entry and go on */
		*(unsigned *)pte++ = model++; /* model++ IS REALLY model.pg_pgnum++ */
	}
#ifdef	DEBUG
	{
		struct hatipt_entry *ipte2;
		int	i;

		for (ipte2 = &MMU_HATIPT[head->hat_ptr], i = 1;
		     ipte2->ipt_ptr <= MMU_HASHMASK;
		     ipte2 = &MMU_HATIPT[ipte2->ipt_ptr])
			if (++i >= 128)
				panic("mapin chain length");
		if (!MMU_ENDCHAIN(ipte2->ipt_ptr))
			panic("mapin chain ending");
	}
#endif	DEBUG
}


rtakey(apte)
	register unsigned apte;		  /* THIS IS REALLY A STRUCT PTE */
{
	switch (apte & PG_PROT) {
	case PG_KW:
		return (MMU_KEY_KW);
	case PG_URKW:
		return (MMU_KEY_URKW);
	case PG_UW:
#ifdef SIM_CHG_BIT
		/* 
		 * if we are simulating the change bit then if M bit is on
		 * allow read-write access; otherwise make it read-only,
		 * so that we can determine when it is modified.
		 */
		return ((apte & PG_M) ? MMU_KEY_UW : MMU_KEY_URKR);
#else
		return (MMU_KEY_UW);
#endif
	case PG_URKR:
		return (MMU_KEY_URKR);
	}
	printf("\nvax pte = 0x%x\n", apte);
	panic("rtakey");
	/*NOTREACHED*/
}


/* set the segment register for the specified virtual address */

alias(pte, vpage)
	register struct pte *pte;
	register u_int vpage;
{
	register struct hatipt_entry *ipte;
	register u_int sid, seg;

	DEBUGF (vmdebug,
		printf("alias( pte 0x%x -> 0x%x, vpage=0x%x ) ",
		    pte, *(unsigned *)pte, vpage));

	/* point to the ipte for this page frame */
	ipte = &MMU_HATIPT[pte->pg_pfnum];

	/* get the segment id (placed there by memall or setsid) */
	sid = (ipte->key_addrtag & MMU_ADDRTAG_MASK) >> MMU_VPAGE_BITS;

	/* determine the vpage and segment from the virt addr */
	seg = (vpage & btop(0xf0000000)) >> MMU_VPAGE_BITS;

	DEBUGF (vmdebug,
		printf("sid=%x,seg=%x\n", sid, seg));

	/* load the segment register for this seg */
	set_segreg(seg, sid);
}

/*
 * reset the segment register after kernel is finished with it.
 * We determine the segment number from the virtual address given.
 */

unalias(addr)
caddr_t addr;
{
	register u_int seg;

	seg = btop(addr) >> MMU_VPAGE_BITS;
	set_segreg(seg, MMU_SID_UNUSED);
}
	

/*
 * Unmap a page from the HATIPT, and keep those hash chains valid !
 */

mapout(pte, size)
	struct pte *pte;
	int size;
{
	register struct hatipt_entry *ipte, *this, *next;
	register u_int t, sid, vpage;
	register struct cmap *c;

	DEBUGF (vmdebug,
		printf("mapout:  pte=%x,size=%x,", pte, size));

	while (--size >= 0) {
		/* point to the ipte for this page frame */
		ipte = &MMU_HATIPT[pte->pg_pfnum];

		DEBUGF (vmdebug,
			printf("pfnum=%x,ipte=%x\n", pte->pg_pfnum, ipte));

		/* look up the sid and vpage fields */
		t = ipte->key_addrtag & MMU_ADDRTAG_MASK;
		sid = t >> MMU_VPAGE_BITS;
		vpage = t & MMU_VPAGE_MASK;

		/* hash to find the head of the ipt chain */
		this = &MMU_HATIPT[MMU_HASH(sid, vpage)];

		/* check the chain for empty */
		if (MMU_ENDCHAIN(t = get_hatptr(this)))
			panic("mapout: empty hash chain");
		next = &MMU_HATIPT[t];

		/* if first on the hash chain, simply point hash anchor table at the /*
		   successor of the entry being unmapped. */
		if (next == ipte) {	  /* was first, unlink */
			this->hat_ptr = ipte->ipt_ptr;
		}
		/* end case this one first */

		else {			  /* not first, chase down the chain */
			while (next != ipte) {
				/* go on */
				this = next;
				/* not found? */
				if (MMU_ENDCHAIN(t = get_iptptr(this)))
					panic("mapout: ipte not on chain");
				next = &MMU_HATIPT[t];
			}
			/* point predecessor of entry being unmapped to successor of *
			   entry being unmapped. */
			this->ipt_ptr = ipte->ipt_ptr;
		}
		/* end case not first */

		ipte->ipt_ptr = MMU_UNDEF_PTR; /* debug */
		invalidate_tlb(vpage);

		/* remember that this page is unmapped in if in cmap */
		if ((c = &cmap[pgtocm(pte->pg_pfnum)]) > cmap && c < ecmap)
			c->c_mapped = 0;

		/* clear the vax pte and go on */
		/* *(int *)pte++ = 0;          */
	}
}

/*
 * Check for valid program size
 * NB - Check data and data growth separately as they may overflow 
 * when summed together.
 */
chksize(ts, ids, uds, ss)
	unsigned ts, ids, uds, ss;
{
	extern unsigned maxtsize;

	if (ctob(ts) > maxtsize ||
	    ctob(ids) > u.u_rlimit[RLIMIT_DATA].rlim_cur ||
	    ctob(uds) > u.u_rlimit[RLIMIT_DATA].rlim_cur ||
	    ctob(ids + uds) > u.u_rlimit[RLIMIT_DATA].rlim_cur ||
	    ctob(ss) > u.u_rlimit[RLIMIT_STACK].rlim_cur) {
		u.u_error = ENOMEM;
		return (1);
	}
	return (0);
}


/*ARGSUSED*/
newptes(pte, v, size)
	register struct pte *pte;
	register u_int v;
	register int size;
{
	while (--size >= 0) {
	DEBUGF (vmdebug,
		printf("newptes: v=0x%x, pte 0x%x -> 0x%x\n", v, pte, *(unsigned *) pte));
		if (pte->pg_v)
			mapin(pte, v, *(unsigned *)pte, 1);
		invalidate_tlb(v);
		pte++;
		v++;
	}
}


#ifndef NEWPORT
 /* TODO: pass more info to this proc so that non VTL_RELOCATE hardware * can selectively
    purge TLB entries as required.  Better yet, make is * proc a preprocessor macro on
    MMU harwdare. */
invalidate_tlb(vpage)
	register unsigned vpage;
{
	register unsigned i;

	i = (vpage & (MMU_NTLBS - 1)) * MMU_TLBSTEP;
	iow(MMU_TLBAW1 + i, MMU_TLBUNUSED);
	iow(MMU_TLBBW1 + i, MMU_TLBUNUSED);
}


#else

/* TODO: pass more info to this proc so that non VTL_RELOCATE hardware
 * can selectively purge TLB entries as required.  Better yet, make is
 * proc a preprocessor macro on MMU harwdare:
 * #define invalidate_tlb(vpage) iow(MMU_INV_ADDR, ptob(vpage))
 */
invalidate_tlb(vpage)
	register unsigned vpage;
{

	iow(MMU_INV_ADDR, ptob(vpage));
}


#endif

/*
 * Change protection codes of text segment.
 * Have to flush translation buffer since this
 * affect virtual memory mapping of current process.
 */
chgprot(addr, tprot)
	caddr_t addr;
	long tprot;
{
	unsigned v;
	int tp;
	register struct pte *pte;
	register struct cmap *c;
	register struct hatipt_entry *ipte;

	DEBUGF (vmdebug,
		printf("chgprot(addr=%x,tprot=%x)\n", addr, tprot));

	v = clbase(btop(addr));
	if (!isatsv(u.u_procp, v)) {
		u.u_error = EFAULT;
		return (0);
	}
	tp = vtotp(u.u_procp, v);
	pte = tptopte(u.u_procp, tp);
	if (pte->pg_fod == 0 && pte->pg_pfnum) {
		c = &cmap[pgtocm(pte->pg_pfnum)];
#ifdef VFS
		if (c->c_blkno && c->c_vp != swapdev_vp)
			munhash(c->c_vp, (daddr_t)(u_long)c->c_blkno);
#else VFS
		if (c->c_blkno && c->c_mdev != MSWAPX)
			munhash(mount[c->c_mdev].m_dev,
			    (daddr_t)(u_long)c->c_blkno);
#endif VFS
	}
	*(int *)pte &= ~PG_PROT;
	*(int *)pte |= tprot;

	/* fix ipte for non-special segments only -- added for ibm032 6/84 */
	if (pte->pg_fod == 0 && pte->pg_pfnum) {
		ipte = &MMU_HATIPT[pte->pg_pfnum];
		ipte->key_addrtag = (ipte->key_addrtag & (~MMU_KEY_BITS))
			|(rtakey(*(int *)pte) << MMU_KEY_SHIFT);
		invalidate_tlb(v);
	}
	distcl(pte);
/***   tbiscl(v);   ***/
	return (1);
}


settprot(tprot)
	register long tprot;
{
	register unsigned *pteadr, i;
	register struct hatipt_entry *ipte;

	DEBUGF (vmdebug,
		printf("settprot(tprot=%x,pid=%d)\n", tprot, u.u_procp->p_pid));

	pteadr = (unsigned *)u.u_pcb.pcb_p0br;
	for (i = 0; i < u.u_procp->p_tsize; i += 1, pteadr += 1) {
		*pteadr &= ~PG_PROT;
		*pteadr |= tprot;

		/* fix ipte for non-special segments only -- added for ibm032 6/84 */
		if (!(*pteadr & PG_FOD) && (*pteadr & PG_PFNUM)) {
			ipte = &MMU_HATIPT[*pteadr & PG_PFNUM];
			ipte->key_addrtag = (ipte->key_addrtag & (~MMU_KEY_BITS))
				|(rtakey(*pteadr) << MMU_KEY_SHIFT);
		}
	}
}


/*
 * Rest are machine-dependent
 */

getmemc(addr)
	caddr_t addr;
{
#ifdef notdef
	register int c;
	struct pte savemap;

	savemap = mmap[0];
	*(int *)mmap = PG_V | PG_KR | btop(addr);
	mtpr(TBIS, vmmap);
	c = *(char *) & vmmap[(int)addr & PGOFSET];
	mmap[0] = savemap;
	mtpr(TBIS, vmmap);
	return (c & 0377);
#endif
	printf("getmemc(addr=%x)\n", addr);
	return (0);
}


putmemc(addr, val)
	caddr_t addr;
{
#ifdef notdef
	struct pte savemap;

	savemap = mmap[0];
	*(int *)mmap = PG_V | PG_KW | btop(addr);
	mtpr(TBIS, vmmap);
	*(char *) & vmmap[(int)addr & PGOFSET] = val;
	mmap[0] = savemap;
	mtpr(TBIS, vmmap);
#endif
	printf("putmemc(addr=%x,val=%x)\n", addr, val);
}


/*
 * Move pages from one kernel virtual address to another.
 * Both addresses are assumed to reside in the Sysmap,
 * and size must be a multiple of CLSIZE.
 */
pagemove(from, to, size)
	register caddr_t from, to;
	register int size;
{

	register caddr_t ra;
	struct pte temp_pte;

	DEBUGF (vmdebug,
		printf("pagemove(from=0x%x,to=0x%x,size0x%x)\n", from, to, size));

	if (size % CLBYTES)
		panic("pagemove");
	while ((size -= NBPG) >= 0) {
		if ((ra = (caddr_t)vtop(from)) == (caddr_t) - 1)
			panic("pagemove vtop");
		temp_pte.pg_pfnum = btop(ra);
		mapout(&temp_pte, 1);
		mapin(&temp_pte, btop(to), PG_V | PG_KW | (btop(ra)), 1);
		from += NBPG;
		to += NBPG;
	}
}


/****************************************************************************
  cloneseg -  Copy one page (NBPG bytes) from the running process' virtual
              memory address "from", to the same virtual memory address in
              the process owning "pte".
 ****************************************************************************/

cloneseg(from, pte)
	register from;
	register struct pte *pte;
{
	register to;
	extern copybase;

	DEBUGF (vmdebug,
		printf("cloneseg( from=0x%x, to pte 0x%x -> 0x%x)\n",
		    from, pte, *(unsigned *)pte));

	mapin(pte, btop(from), PG_V | PG_M | PG_UW | (*(unsigned *)pte), 1);
	to = ((int) & copybase) | (from & SEGMENT_OFFSET);
	alias(pte, btop(to));
#ifdef notdef
	bcopy(from, to, NBPG);
#else
	*(int *)to = *(int *)from;		/* force pagein to occur */
	copypage(vtop(from), vtop(to));
	unalias((caddr_t) to);			/* get rid of alias */
#endif
}


/****************************************************************************
  clearseg -   Clear one relocation unit (NBPG bytes)
 ****************************************************************************/
#ifdef notdef
clearseg(where)				  /* === see locore.ruasm === */
{
	printf("clearseg: where=%x,ptov(where)=%x\n", where, ptov(ctob(where)));
	mfill(ptov(ctob(where)), ctob(1), 0);
}


#endif
 /* *************************************************************************** vtop -
    map virtual address(bytes) to physical address(bytes) *************************************************************************** 
 */

#ifdef notdef	/* now done in loutil.s */
vtop(where)
	register int *where;
{
	register caddr_t paddr;
	register u_int x;

	x = splimp();
	iow(MMU_VTOP, where);
	paddr = (caddr_t)ior(MMU_REALADDR);
	splx(x);
	return ((int)paddr < 0 ? -1 : (int)paddr);
}


#endif

/****************************************************************************
  ptov - map physical address(bytes) to virtual address(bytes)
 ****************************************************************************/
ptov(where)
	register unsigned int where;
{
	register struct hatipt_entry *ipte;
	register unsigned int rpage, addrtag, vpage, sid;
	rpage = btop(where);		  /* shift by log2(pagesize) */
	ipte = &MMU_HATIPT[rpage];
	addrtag = ipte->key_addrtag & 0x1fffffff;
/*printf("ptov:  where=%x,addrtag=%x,ipte=%x  ",where,addrtag,ipte);*/
	vpage = addrtag & MMU_VPAGE_MASK;
	sid = addrtag >> MMU_VPAGE_BITS;
/*printf("  rpage=%x,addrtag=%x,vpage=%x,sid=%x - ",rpage,addrtag,vpage,sid);*/
	where = (where & PGOFSET) | ctob(vpage);
	if (sid == MMU_SID_SYSTEM) {
/*printf("system:%x\n",where+SYSBASE);*/
		return (where | SYSBASE);
	} else if (sid == MMU_SID_UNUSED) {
/*printf("unused\n");*/
		return (-1);
	} else if (sid < MMU_SID_SYSTEM) { /* user text */
/*printf("user text:%x\n",where);*/
		return (where);
	} else {
/*printf("user data:%x\n",where | DATABASE);*/
		return (where | DATABASE);
	}
}


 /* end of ptov */

/****************************************************************************
  isitok - check the validity of an access to a range of virtual addresses
 ****************************************************************************/
isitok(vaddr, length, access)
	register unsigned vaddr;	  /* THIS IS REALLY A CADDR_T */
	register unsigned length;
	register unsigned access;
{
#define v vaddr
#define l length
	l = btop(vaddr+length-1);	/* last page */
	v = btop(vaddr);		/* first page */

	if (l < v)			  /* Negative or really huge values */
		return(FALSE);		  /* are no good. */

	if (v < btop(ENDOFP1)-UPAGES) {		  /* user virtual address */
		register struct pte *pte;
		{
/* calculate pte and v_max (largest legal virtual address in segment) */
			register struct proc *p;
			register unsigned v_max;

			p = u.u_procp;

			if (isatsv(p, v)) {
				pte = tptopte(p, vtotp(p, v));
				v_max = (p)->p_tsize;
			} else if (ISADSV(p, v)) {	/* already done isatsv */
				pte = dptopte(p, vtodp(p, v));
				v_max = (p)->p_dsize + ((p)->p_tsize ? btop(DATABASE) : 0);
			} else if (isassv(p, v)) {
				pte = sptopte(p, vtosp(p, v));
				v_max = btop(USRSTACK);
			} else
				return (FALSE);
			if (v >= v_max || l >= v_max)
				return (FALSE);
		}
		for (;v <= l;++v) {
			register int i;
			i = *(int *)pte++ & PG_PROT;
			switch (access) {
			case UWRITE_OK:
				if (i == PG_UW)
					break;
				return (FALSE);
			case UREAD_OK:
				if (i >= PG_UW)
					break;
				return (FALSE);
			case KWRITE_OK:
				if (i == PG_UW || i == PG_URKW || i == PG_KW)
					break;
				return (FALSE);
			case KREAD_OK:
				if (i != PG_NOACC)
					break;
				return (FALSE);
			}
			/* end switch */
		}

		return (TRUE);
	}

	if (access != KREAD_OK && access != KWRITE_OK)
		return (FALSE);

	/* kernel virtual address -- used only by kernel memory device driver */

	{
		register unsigned vtmp = v >> IOPAGESHIFT;

		if (vtmp == (IOSPACE >> IOSPACESHIFT) ||
				vtmp == (IOSPACE2 >> IOSPACESHIFT))
			return ((vtmp == (l >> IOPAGESHIFT)) ? TRUE : FALSE);
	}
	for (;v <= l;++v) {
		register int i;
		if ((i = vtop(ptob(v)) >> PGSHIFT) == -1)
			return (FALSE);
		if (!(MMU_KEYBIT(MMU_HATIPT[i].key_addrtag) & access))
			return (FALSE);
	}

	return (TRUE);
#undef v
#undef l
}


#ifdef notdef
 /* *************************************************************************** willbeok
    - check the validity of a future access to a virtual addresses *************************************************************************** 
 */
willbeok(vaddr, access)
	register unsigned vaddr;	  /* THIS IS REALLY A CADDR_T */
	register unsigned access;
{
	register struct pte *pte;

	return ((pte = vtopte(u.u_procp, btop(vaddr)))
			&&pte->pg_fod
	    &&(access & MMU_KEY_OK(rtakey(*(unsigned *)pte)))
		);
}


#endif

/****************************************************************************
real_buf_addr - translate a virtual address into a real memory address.
		The virtual address need not be mapped into an address space.
****************************************************************************/
caddr_t
real_buf_addr(bp, bufaddr)
	register struct buf *bp;
	register char *bufaddr;
{
	register v, o;
	register struct pte *pte;
	register struct proc *p;

	if ((bp->b_flags & B_PHYS) == 0)
		return ((caddr_t)vtop(bufaddr));

	v = btop(bufaddr);
	o = (int)bufaddr & PGOFSET;
	p = bp->b_flags & B_DIRTY ? &proc[2] : bp->b_proc;

	if (bp->b_flags & B_UAREA)
		pte = &p->p_addr[v];
	else if (bp->b_flags & B_PAGET)
		pte = &Usrptmap[btokmx((struct pte *)bufaddr)];
	else if ((pte = vtopte(p, v)) == 0)
		panic("real_buf_addr");

	return ((caddr_t)((pte->pg_pfnum << PGSHIFT) | o));
}

/*
 * make the kernel text read-only
 * also protect the POST against modification.
 * we do this by setting the the key bits to '11' which is
 * public read-only. The user can be prevented from reading the
 * kernel text by setting C=1 in the segment register, but
 * we don't do this because the user must be able to access
 * the floating point emulator code.
 * stext is the start of the kernel code to be protected.
 * we assume that we can protect the page that stext is in
 */
#define POST	0x800		/* POST area to protect */

set_kernel_read_only()
{
	extern char stext, etext;
	register int startpg = btop(&stext - SYSBASE);
	register int endpg = btop(&etext + NBPG - 1 - SYSBASE);
	register int i;
	register struct hatipt_entry *h;

	h = &MMU_HATIPT[btop(0)];
	h->key_addrtag |= MMU_KEY_URKR << MMU_KEY_SHIFT;
	h = &MMU_HATIPT[btop(POST)];
	h->key_addrtag |= MMU_KEY_URKR << MMU_KEY_SHIFT;

	for (i=startpg, h = &MMU_HATIPT[startpg]; i<endpg; ++i, ++h)
		h->key_addrtag |= MMU_KEY_URKR << MMU_KEY_SHIFT;
}



/*
 * Copy a null terminated string from the user address space into
 * the kernel address space.
 *
 * copyinstr(fromaddr, toaddr, maxlength, &lencopied)
 */
copyinstr(fromaddr, toaddr, maxlength, lencopied)
register char *fromaddr, *toaddr;
register int maxlength;
register int *lencopied;
{
	register char *to = toaddr;
	register int len;
	int length;
	register int error;
	for (;;) {
		len = NBPG - ((int) fromaddr & (NBPG-1));	/* bytes left on page */
		if (len > maxlength)
			len = maxlength;
		if  (!isitok(fromaddr, len, UREAD_OK)) {
			error = EFAULT;
			break;
		}
		error = copystr(fromaddr, to, len, &length);
		fromaddr += length;
		to += length;
		maxlength -= length;
		if (error == 0 || maxlength == 0)
			break;		/* found the null byte or maxlength */
	}
	if (lencopied)
		*lencopied = to - toaddr;
	return(error);	/* copied it */
}


/*
 * Copy a null terminated string from the kernel
 * address space to the user address space.
 *
 * copyoutstr(fromaddr, toaddr, maxlength, &lencopied)
 */
copyoutstr(fromaddr, toaddr, maxlength, lencopied)
register char *fromaddr, *toaddr;
register int maxlength;
register int *lencopied;
{
	register char *to = toaddr;
	register int len;
	int length;
	register int error;
	for (;;) {
		len = NBPG - ((int) to & (NBPG-1));	/* bytes left on page */
		if (len > maxlength)
			len = maxlength;
		if  (!isitok(to, len, UWRITE_OK)) {
			error = EFAULT;
			break;
		}
		error = copystr(fromaddr, to, len, &length);
		fromaddr += length;
		to += length;
		maxlength -= length;
		if (error == 0 || maxlength == 0)
			break;		/* found the null byte or maxlength */
	}
	if (lencopied)
		*lencopied = to - toaddr;
	return(error);	/* copied it */
}

/*
 * Copy a null terminated string from one point to another in
 * the kernel address space.
 *
 * copystr(fromaddr, toaddr, maxlength, &lencopied)
 * return 0 if found null terminating byte, ENOENT if not.
 */
copystr(fromaddr, toaddr, maxlength, lencopied)
register char *fromaddr, *toaddr;
register int maxlength;
register int *lencopied;
{
	register char *to = toaddr;
	register char *end = toaddr + maxlength;
	register int error = ENOENT;

	while (to < end)
		if ((*to++ = *fromaddr++) == 0) {
			error = 0;
			break;
		}
	if (lencopied)
		*lencopied = to - toaddr;
	return(error);
}

#ifdef DEBUG
/*
 * print the string 
 * called from execve and namei
 * when (if) null byte encountered print 'ch' if that is 
 * non-zero.
 */
strdebug(str,length,ch)
	register char *str;
	register int length;
	register char ch;
{
	for (;--length >= 0; ++str)
		if (*str)
			Tprintf("%c",*str);
		else {
			if (ch)
				Tprintf("%c",ch);
			break;
		}
}
#endif DEBUG

#ifdef SIM_CHG_BIT

/*
 * take a protection exception and determine if we are simulating the 
 * modified bit. If not then the caller will deliver the appropriate
 * signal. If the access in the pte was UW and the access in the HAT/IPT
 * is URKR, then set M=1 and change the protection to UW.
 */

protection(mcs_pcs,info,locr0)
	unsigned info;
	int *locr0;
{
	register struct proc *p;
	register struct pte *pte;
	register unsigned v;
	register struct hatipt_entry *ipte;
	int type;

	DEBUGF(padebug & SHOW_PROTSTATE,
		prstate("protection", mcs_pcs, info, locr0[IAR], locr0[ICSCS],locr0));

	/*
	 * Classify faulted page into a segment and get a pte
	 * for the faulted page.
	 */
	v = clbase(btop(info));
	p = u.u_procp;
	if (isatsv(p, v)) {
		type = CTEXT;
		pte = tptopte(p, vtotp(p, v));
	} else if (isadsv(p, v)) {
		type = CDATA;
		pte = dptopte(p, vtodp(p, v));
	} else if (isassv(p, v)) {
		type = CSTACK;
		pte = sptopte(p, vtosp(p, v));
	} else return (-1);		/* couldn't find a pte for it! */
#ifdef DEBUG
	if (p->p_p0br == 0)
		panic("panic: p0br not set!");
#endif DEBUG
	DEBUGF ((padebug & SHOW_PROT),
		printf("protection: pid=%d, vaddr=0x%x, %s pte 0x%x -> 0x%x ", p->p_pid, info, (type == CTEXT ? "text" :
		    (type == CDATA ? "data" : "stack")),
		    pte, (*(unsigned *)pte)));

	if (pte->pg_v == 0 || pte->pg_m) {
		DEBUGF ((padebug & SHOW_PROT), printf("v=%d m=%d\n",
			pte->pg_v,pte->pg_m));
		return(-1);		/* cannot be our protection violation */
	}
	
	/*
	 * test to make sure that the proper protection is PG_UW, and that
	 * the actual (HAT/IPT) protect is read-only.
	 */
	ipte = &MMU_HATIPT[pte->pg_pfnum];
	if (((* (int *) pte) & PG_PROT) != PG_UW ||
			((unsigned) ipte->key_addrtag >> MMU_KEY_SHIFT) != MMU_KEY_URKR) {
		DEBUGF ((padebug & SHOW_PROT), printf("addrtag=%x\n",ipte->key_addrtag));
		return(-1);	/* its a real protection exception */
	}
	/*
	 * at this point we know that we have taken an informational protection
	 * exception and can set m=1, and change the HAT/IPT protection to the
	 * proper value and restart execution.
	 */
	ipte->key_addrtag ^= (MMU_KEY_URKR^MMU_KEY_UW) << MMU_KEY_SHIFT; /* new prot */
	pte->pg_m = 1;
	invalidate_tlb(v);	/* flush translate buffer */
	cnt.v_pdma++;		/* a lie, but at least its counted */
	DEBUGF ((padebug & SHOW_PROT), printf("done\n"));
	return(0);		/* re-execute the packets */
}

/*
 * we suspect that the modified bit has been changed, so we adjust
 * HAT/IPT key bits if they aren't correct.
 */
chg_mod_bit(pte,v)
struct pte *pte;
unsigned v;
{
	register struct hatipt_entry *ipte;
	int apte = * (int *) pte;

	if ((apte & PG_V) == 0)
		return;		/* not valid so not in HAT/IPT */
	if ((apte & PG_PROT) != PG_UW)
		return;		/* not user writable */
	ipte = &MMU_HATIPT[pte->pg_pfnum];
	if (apte&PG_M) {
		if (((unsigned) ipte->key_addrtag >> MMU_KEY_SHIFT) == MMU_KEY_UW)
			return;		/* is writeable */
	} else if (((unsigned) ipte->key_addrtag >> MMU_KEY_SHIFT) == MMU_KEY_URKR)
		return;			/* is read-only */
	DEBUGF ((padebug & SHOW_CHG_PROT),
		printf("chg_mod_bit: pid=%d, vaddr=0x%x, pte 0x%x -> 0x%x addrtag=%x\n", u.u_procp->p_pid, v, pte, * (int *) pte, ipte->key_addrtag));
	/* we know that we have to invert the protections */
	ipte->key_addrtag ^= (MMU_KEY_URKR^MMU_KEY_UW) << MMU_KEY_SHIFT; /* new prot */
	invalidate_tlb(v);	/* flush translate buffer */
}

/*
 * test to see if a store to addr would cause a protection exception
 * if the page isn't mapped in then it couldn't cause a protection
 * exception.
 */
test_protection(addr)
unsigned addr;
{
	unsigned page = vtop(addr);
	register struct hatipt_entry *ipte;

	if (page&0x80000000)
		return(0);		/* not mapped in */
	ipte = &MMU_HATIPT[btop(page)];
	return (((unsigned) ipte->key_addrtag >> MMU_KEY_SHIFT) == MMU_KEY_URKR);
}
#endif SIM_CHG_BIT
