/* char rcsid[] = "$Header: context.h,v 820.1 86/12/04 19:52:58 root Exp $"; */
/* sccsid[]="%W% %Y% %Q% %G%"; */

/************************************************************************
*									*
*				Copyright 1984				*
*			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.				*
*									*
************************************************************************/

#ifndef	CONTEXT_ALREADY_DEFINED
#define	CONTEXT_ALREADY_DEFINED

#ifdef	KERNEL
#include "../h/types.h"
#include "../machine/vmparam.h"
#include "../h/map.h"			/* NCONTEXTS	*/
#else	KERNEL
#include <sys/types.h>
#include <machine/vmparam.h>
#include <sys/map.h>
#endif	KERNEL

#ifdef	M68020_REV_B	/* NOTE: Release 8.1 does not use this */
/*
 * Context manipulation definitions.
 *
 * jam 850??? -- Origional version.
 * jht 860201 -- Add code for variable maximum process size: SETTABLE_MMU_SIZE
 * jht 860209 -- Add code to support 32Mb, 64Mb, 128Mb vaddr: M68020_REV_B
 * jht 860308 -- Add code for LRU allocation of virtual segments.
 * jht 860312 -- Add code for virtual-smap list management;
 */


/*
 * Debugger-ing macros:
 */
#ifdef	CTXT_DEBUG
#undef	CTXT_DEBUG
#define	CTXT_DEBUG(stuff)	DODEBUG(D_CTXT,stuff)
#else	CTXT_DEBUG

#define	CTXT_DEBUG(stuff)
#endif	CTXT_DEBUG


#ifdef	CMD_BINGO		/* C.f., s32/{context.c,map.c,startup.c}    */
short	cmd_BINGO;	  	/* 0==>Disable; 1==>Enable pattern match'g  */
char	cmd_BINGO_cmd[32];	/* Command for which a match ==> "BINGO!"   */
#endif	CMD_BINGO

#ifdef	MMU_BINGO		/* C.f., s32/{context.c,map.c,startup.c}    */
short	mmu_BINGO;		/* 0==>Disable; 1==>Enable pattern matching */
u_long	mmu_BINGO_vaddr;	/* Virt addr @ which a match ==> "BINGO!"   */
u_long	mmu_BINGO_paddr;	/* Phys addr @ which a match ==> "BINGO!"   */
#endif	MMU_BINGO



/*
 * Half of the total # of pages/segments
 * for the relevant address space.
 * NSMAPS is the lesser
 * of MAXPHYSMEM_PAGES and BIGMAP_PAGES,
 * since we are representing the amount
 * of real memory that can be mapped
 * by the MMU.
 */
#define	V_SEGMASK	(V_VSEGNO<<PGSHIFT)	/* Seg-addr from addr */
#define	V_VSEGNO_MASK	(V_VSEGNO>>PTOSSHIFT)	/* Seg-# from page #  */

#define HALFPAGES	btop(V_PAGEMAP)	/* Half of total pages in the map */
#define HALFSEGS	btos(V_SEGMAP)	/* Half of total segments in the map */
#define PPS		(1<<PTOSSHIFT)	/* Number of pages per segment */
#define SPMASK		(PPS-1)		/* Mask for pages within segment */

/*
 * Mnemonics for smaps, contexts and regions.
 */
#define	DUMMYSMAP	1	/* Dummy/unused/sacrificial segment	   */
#define	NOSMAP		NULL	/* No segment map has this value	   */
#define	NOCTXT		NULL	/* No context descriptor has this address  */
#define	NOLIST	  ((dlist_t *)0xbbbbbbbb) /* Designate "no list linkage"   */

#define	 TEXT_REGION	1	/* Segment/page partly/wholly within text  */
#define	 DATA_REGION	2	/* Segment/page partly/wholly within data  */
#define	  GAP_REGION	3	/* Segment/page partly/wholly within 'gap' */
#define	STACK_REGION	4	/* Segment/page partly/wholly within stack */
#define	 TOP_REGION	5	/* Segment/page partly/wholly within 'top' */


/*
 * Doubly linked lists and their manipulations.
 * These are typically put at the front
 * of other data structures to provide threads through the latter.
 *
 * NOTE:  We capitalize the 1st letter of structure constituents
 * for which there is a preprocessor MACRO shorthand premptive handle.
 * E.g., 'D_fwd' below.
 * Note - If you change the ordering in the proc structure you had better
 *	  change it here too ... or YOU'LL BE SORRY!!!
 */
typedef	struct	dlist
{
	struct	dlist *	D_fwd;	/*  Forward chain */
	struct	dlist *	D_bwd;	/* Backward chain */
} dlist_t;


/*
 * For our "context" application in sureg()
 */
#define	lru	D_fwd		/* Least recently used link	*/
#define	mru	D_bwd		/*  Most recently used link	*/


#ifdef	M68020_REV_B
/*
 * ctxt_dlist_t is a dlist_t augmentation used
 * in the sureg() code to get
 * at the bindings between contexts and procs.
 */
typedef	struct	ctxt_dlist
{
	dlist_t	d_dlist;	/* Nominal doubly-linked list */

	/*
	 * Baggage associated with our ctxt-"application"
	 */
	struct	ctxt  *	D_ctxt;		/* Associated context.		*/
	struct	proc  *	D_procp;	/* Associated process, if any	*/
} ctxt_dlist_t;

#define	d_lru	d_dlist.lru		/* Shorthand:  ctxt_list.d_lru */
#define	d_mru	d_dlist.mru

/*
 * Conversion macros
 * for traversing lists of contexts, etc.
 */
#define	dlistToCtxt(dList)	((ctxt_t *) ((dList). d_lru))

#define	nextCtxt(ctxtP)		((ctxt_t *) ((ctxtP)->c_lru))

#define	ctxtPAtEOL(ctxtP,dList)	((ctxtP) == ((ctxt_t *) &dlistToCtxt(dList)))

#define	dlistIsEmpty(dList)	(dlistToCtxt(dList) == ((ctxt_t *) &dlistToCtxt(dList)))
#endif	M68020_REV_B

#ifndef	CTXT_MACROS
#ifdef	CONTEXT_DOT_C	/* Only define the functions once...in s32/context.c */
/*
 * These were formerly macros,
 * upon which the compiler would run amuck.
 * They have been transformed temporarily (permanently?)
 * into nominal 'c' functions.
 *
 * jht 860501	I looked at these functions in detail -- they're OK.
 *		The only caveat is WHEN to use addMru/addLru.
 */


/*
 * dequeue  --	Delete an element from the MRU-end
 *		of a doubly linked list:
 *
 *	Legend:		o ==> OLD links;
 *	=======		n ==> NEW links.
 *			* ==> Origin of link;
 *			^ ==> Destination of MRU link;
 *			v ==> Destination of LRU link;
 *
 *
 *                   nn[#2]nn>>>nnnnnnnnnnnnnnnnnnnnn>>>nnnnnn               
 *                   n                                       v               
 * LRU:              n  ooooo>>>oooo           oooooo>>>oooo v               
 * ====      (prior) n  o          v     (our) o           v v  (next)       
 *                   n  o          v           o n>>>NULL  v v               
 *         ----------n--o---       ------------o-n--       -----------------
 *         |  MRU  | *  *  |       | * *   |   * * |       |  *  * |  LRU  |
 *         -----------------       --n-o------------       ---o--n----------
 *         ^  ^               NULL<<<n o  ^                   o  n         
 *         ^  ^                        o  ^                   o  n         
 * MRU:    ^  ooooooooooo<<<oooooooooooo  ooooooooooo<<<ooooooo  n           
 * ====    ^                                                     n          
 *         nnnnnnnnnnnnnn<<<nnnnnnnnnnnnnnnnnnnnnnnnn<<<nn[#1]nnnn          
 */
dequeue(our)
	register dlist_t * our;
{
	if (our == NULL || our == NOLIST)
		cdebugger("dequeue/E-0: Attempt to dequeue NULL element");
	our->lru->mru	= our->mru;	/* #1: Our next's prior to our prior */
	our->mru->lru	= our->lru;	/* #2: Our prior's next to our next  */

	our->lru	= NULL;		/* Tidy */
	our->mru	= NULL;		/* .... */
}

/*
 * addMru   --	Add an element to the MRU-end
 *		of a doubly linked list.
 *
 *
 *                   oooooooo>>>ooooooooooooooooooooo>>>oooooo               
 *                   o                                       v               
 * LRU:      (prior) o  n[#3]n>>>nnn     (our)  n[#1]n>>>nnn v  (head)       
 * ====              o  n          v            n          v v         oooo  
 *      <<<ooo       o  n          v            n          v v         v  o  
 *         --o-------o--n---       -------------n---       ---------------o-
 *         | * MRU | *  *  |       |  n    |    *  |       |  *  * |  LRU *|
 *         -----------------       ---n-------------       ---n--o----------
 *         ^  ^                       n  ^                    n  o         
 *         ^  ^                       n  ^                    n  o         
 * MRU:    ^  nnnnnnnnnnn<<<nnn[#2]nnnn  nnnnnnnnnnnn<<<n[#4]nn  o           
 * ====    ^                                                     o          
 *         oooooooooooooo<<<ooooooooooooooooooooooooo<<<oooooooooo          
 */
addMru(head, our)
	register dlist_t * head, * our;
{
	if (head == NULL || head == NOLIST)
		cdebugger("addMru/E-0: Attempt to add NULL head");
	if (our == NULL || our == NOLIST)
		cdebugger("addMru/E-1: Attempt to add NULL element");
	our->lru	= head;		/* #1: Our next to head		    */
	our->mru	= head->mru;	/* #2: Our prior to head's prior    */
	our->mru->lru	= our;		/* #3: Our (new) prior's next to us */
	head->mru	= our;		/* #4: Head's prior to us	    */
}

/*
 * addLru   --	Add an element to the LRU-end
 *		of a doubly linked list.
 *
 *
 *                   oooooooo>>>ooooooooooooooooooooo>>>oooooo               
 *           (head)  o                                       v  (prior)      
 * LRU:              o  n[#4]n>>>nnn     (our)  n[#2]n>>>nnn v                
 * ====      oo>>oo  o  n          v            n          v v           oo>>>..
 *           o    v  o  n          v            n          v v           o   
 *         --o-------o--n---       -------------n---       --------------o--
 *         | * MRU | *  *  |       |  n    |    *  |       |  *  * | LRU * |
 *         -----------------       ---n-------------       ---n--o----------
 *         ^  ^                       n  ^                    n  o         
 *         ^  ^                       n  ^                    n  o         
 * MRU:    ^  nnnnnnnnnnn<<<nnn[#1]nnnn  nnnnnnnnnnnn<<<n[#3]nn  o           
 * ====    ^                                                     o          
 *         oooooooooooooo<<<ooooooooooooooooooooooooo<<<oooooooooo          
 */
addLru(head, our)
	register dlist_t * head, * our;
{
	if (head == NULL || head == NOLIST)
		cdebugger("addLru/E-0: Attempt to add NULL head");
	if (our == NULL || our == NOLIST)
		cdebugger("addLru/E-1: Attempt to add NULL element");
	our->mru	= head;		/* #1: Our prior to head	*/
	our->lru	= head->lru;	/* #2: Our next to heads's next	*/
	our->lru->mru	= our;		/* #3: Our next's prior to us	*/
	head->lru	= our;		/* #4: Head's next to us	*/
}

/*
 * emptyList   --	Effectively delete any/all of the elements
 *			of a doubly linked list.
 */
emptyList(head)
	register dlist_t * head;
{
	if (head == NULL || head == NOLIST)
		cdebugger("emptyList/E-0: Attempt to nulify NULL head");
	head->mru = head;		/* Point head at itself		*/
	head->lru = head;		/* Point head at itself		*/
}
#endif	CONTEXT_DOT_C
#else	CTXT_MACROS

/*
 * Macro formulation
 * of the above context manipulation functions.
 */
#define	L(l)		((dlist_t *)(l))

#define dequeue(l)	(L(l)->lru->mru = L(l)->mru, \
			 L(l)->mru->lru = L(l)->lru)

#define addMru(h,l)	(L(l)->lru = L(h), L(l)->mru = L(h)->mru, \
			 L(l)->mru->lru = L(l), L(h)->mru = L(l))

#define addLru(h,l)	(L(l)->mru = L(h), L(l)->lru = L(h)->lru, \
			 L(l)->lru->mru = L(l), L(h)->lru = L(l))

#define emptyList(h)	(L(h)->mru = L(h)->lru = L(h))
#endif	CTXT_MACROS


/*
 * Synonyms for addition/deletion of elements
 * from the LRU/MRU virtual segment list.
 */
#define	addAtHeadOfMRU	addAtTailOfLRU
#define	delAtTailOfLRU	delAtHeadOfMRU


#ifdef	M68020_REV_B
#define	P(l)		((l) -> procp)
#else	M68020_REV_B
#define	P(l)		((struct proc *) \
			    ((int)(l) - (int)&((struct proc *)0)->p_lru))
#endif	M68020_REV_B

/*
 * Segment maps
 */
#define	COUNTSMAPS(p)	((p) == &proc[PAGE_DAEMON_SLOTNUM] ? 1 :	  \
				(ptos((p)->p_tsize) +	/* Text space */  \
				 ptos((p)->p_dsize) +	/* Data space */  \
				 ptos((p)->p_ssize) +	/* Stack space */ \
				 1)			/* Top 64K of memory */)

#ifdef	M68020_REV_B
typedef	struct	{
	union	{
		/*
		 * Use sMapStruct_0 for performace
		 * ...avoids bitfield compiler headaches
		 */
		struct	vSMaps_smap_dlist_s	{
#ifdef	BIDIRECTIONAL_LRU
			smap_t	s_bwdIndx_s;
#endif	BIDIRECTIONAL_LRU
			smap_t	s_fwdIndx_s;	/* s_instantiated_B==0 */
		} sMapS0;

		struct	vSMaps_smap_both_s	{
			/*
			 * We admit to omniscence...
			 */
#ifdef	BIDIRECTIONAL_LRU
			u_long	s_bwdFwd_s;	/* s_instantiated_B==0 */
#else	BIDIRECTIONAL_LRU
			u_short	s_bwdFwd_s;	/* s_instantiated_B==0 */
#endif	BIDIRECTIONAL_LRU
		} sMapS1;

		/*
		 * Fine-structure of the fields.
		 */
		struct	vSMaps_smap_BitFields_s {
#ifdef	BIDIRECTIONAL_LRU
			unsigned	s_bwdIndx_B		: 16;
#endif	BIDIRECTIONAL_LRU

			u_short		s_instantiated_B	:  1;
#define S_INSTANTIATED	0x8000	    /* 's_instantiated_B' */
			u_short		s_fwdIndx_B		: 15;
		} sMapS2;
	} sMapUn;		/* Union */
} vSMaps_dlist_t;

extern	vSMaps_dlist_t	U_sMaps[];	/* @ top of upage */

/*
 * Macros to simplify acces to the above fields.
 *
 * NOTE:	1) 'Both'	==> Both set and fetch;
 *		2) 'CAREFUL'	==> Only fetch/get -- carful when SET-ing it.
 */
#define	s_bwdIndx	 sMapUn.sMapS0.s_bwdIndx_s		/*Both*/
#define	s_fwdIndx	 sMapUn.sMapS0.s_fwdIndx_s 		/*CAREFUL!*/

#define	s_bwdFwd	 sMapUn.sMapS1.s_bwdFwd_s		/*Both*/

#define	s_bwdIndx_b	 sMapUn.sMapS2.s_bwdIndx_B		/*Both*/
#define	s_fwdIndx_b	 sMapUn.sMapS2.s_fwdIndx_B		/*Both*/
#define	s_instantiated_b sMapUn.sMapS2.s_instantiated_B		/*Both*/
#endif	M68020_REV_B

/*
 * Contexts
 */
typedef	struct	ctxt
{
#ifndef	M68020_REV_B
	struct dlist  *	lru;		/* Least recently used (context list) */
	struct dlist  *	mru;		/*  Most recently used (context list) */
#else	M68020_REV_B
	ctxt_dlist_t	c_dlist;	/* Nominal doubly-linked list	*/
#endif	M68020_REV_B
	u_char		number;		/* Context number		*/
	u_char		mmuIndx;	/* Index into 'mmu_sizing'	*/
	u_char		mmuIndxPrev;	/* Previous index into 'mmu_sizing' */
#if	0	/* Already in ctxt_dlist_t */
	struct	proc	*procp;		/* Process in this context	*/
#endif	0	/* Already in ctxt_dlist_t */
	int		gapstart;	/* Start of gap in this context	*/
	int		gapend;		/* End of gap in this context	*/
} ctxt_t;

#define	c_lru	c_dlist.d_lru		/* Shorthand:  (ctxt)->c_lru */
#define	c_mru	c_dlist.d_mru
#define	c_ctxt	c_dlist.D_ctxt
#define	c_procp	c_dlist.D_procp

#ifdef CTXT_STATS
/*
 * Statistics for mmu contexts
 */
typedef	struct	ctxt_stats
{
	int	sureg;		/* calls to sureg() */
	int	needmap;	/* times we needed smaps */
	int	freetaken;	/* smaps taken from free list */
	int	noctxt;		/* noctxt processes examined */
	int	noctxttaken;	/* smaps taken from noctxt processes */
	int	noctxtempty;	/* noctxt processes emptied */
	int	havectxt;	/* havectxt processes examined */
	int	havectxttaken;	/* smaps taken from havectxt processes */
	int	havectxtempty;	/* havectxt processes emptied */
	int	needctxt;	/* times we needed a context */
	int	procctxt;	/* processes having context stolen */
	int	mapsegs;	/* times we needed to map segments */
	int	segstomap;	/* total segments mapped by sureg() */
	int	mappages;	/* times we needed to map pages */
	int	pagestomap;	/* total pages mapped by sureg() */
	int	inval;		/* calls to ctxt_invalidate() */
	int	invalctxt;	/* contexts invalidated */
	int	invalsmaps;	/* times smaps invalidated */
	int	invalneed;	/* total smaps needed by processes */
	int	pageupdates;	/* calls to ctxt_pageupdate() */
	int	realupdates;	/* out-of-context updates */
	int	hardupdates;	/* updates to non-context smaps */
	int	updateinvals;	/* updates which forced invalidates */
	int	getprocmap;	/* time getprocmap() invoked */
} ctxt_stats_t;

#define	CTXT_INCSTAT(name)	++ctxt_stats.name
#define	CTXT_ADDSTAT(name,inc)	ctxt_stats.name += inc

struct	ctxt_stats	ctxt_stats;	/* Context switching statistics */

#else	CTXT_STATS
#define	CTXT_INCSTAT(name)
#define	CTXT_ADDSTAT(name,inc)
#endif	CTXT_STATS

/*
 * List of (physical) contexts.
 */
ctxt_t		ctxts[NCONTEXTS];	/* Context descriptors		      */

/*
 * List-heads for various
 * aggregate kinds of contexts.
 */
ctxt_dlist_t	ctxt_list;		/* List of contexts		      */
ctxt_dlist_t	procsSansCtxt;		/* List of procs with NO context      */
ctxt_dlist_t	procsWithCtxt;		/* List of procs which have a context */

#ifndef	CONTEXT_DOT_C
short		nContexts;		/* Alterable @ startup() time	      */
#else	CONTEXT_DOT_C
short		nContexts = NCONTEXTS;
/*
 * Segment map management table.
 * -----------------------------
 *
 * The table represent the PHYSICAL segments of primary storage,
 * the table-index being the physical segment #.
 *
 * The table is created as a forward-chained linear list.
 * A freelist selector points into the table
 * at the beginning of the available segment maps.
 *
 * NOTE: As the smaps are allocated, deallocated or stolen
 * during normal context switching from process to process,
 * the initial monotonicity of the linear list will disappear.
 *
 *	Descr.		Entry #I through #J				Contents
 *	======		========================================	========
 *	Kernel		0		--> (ptos(usrbase)-1)		I+1
 *
 *	User		ptos(usrbase)	--> (ptos(usrtop)-1)		I+1
 *
 *	LastSeg		ptos(usrtop)	--> (nSmaps-1)			0=NOSMAP
 */
smap_t	smaps[NSMAPS];		/* Table of 'real' segment maps  */
smap_t	smap_free;		/* Head of free segment map list */
smap_t	smap;			/* Index into smaps[]		 */
#else	CONTEXT_DOT_C
extern	smap_t	smaps[];		/* Table of 'real' segment maps  */
extern	smap_t	smap_free;		/* Head of free segment map list */
extern	smap_t	smap;			/* Index into smaps[]		 */
#endif	CONTEXT_DOT_C


/*
 * Bean counters for pages and segments.
 */
#ifndef	CONTEXT_DOT_C
int	nSmaps;			/* # segment maps for (16Mb) proc[0] */
#else	CONTEXT_DOT_C
int	nSmaps = NSMAPS;
#endif	CONTEXT_DOT_C

int	segsMappedSoFar;
int	pagesMappedSoFar;
int	unMappedPages;


#if	defined(M68020_REV_B)
short	maxPSmapsAvail;		/* PHYSICAL address space limit		    */
int	maxVSmapsAvail;		/* VIRTUAL  addr space limit: normalMmuIndx */
#endif	defined(M68020_REV_B)

/*
 * LRU/MRU list management macros.
 *
 * jht 860312 -- Add code for list management;
 */
#define	M_		0x7fff		/* Mask for fwd-field, only */
#define	I_		S_INSTANTIATED	/* Designates instantiation of smap */

#define	LRU_HEAD	u.u_smapLRU_HEAD
#define	LRU_TAIL	u.u_smapLRU_TAIL

#define	MRU_HEAD	u.u_smapLRU_TAIL	/* Note the reversal...*/
#define	MRU_TAIL	u.u_smapLRU_HEAD

#define	HEAD		LRU_HEAD		/* Shorthand d'jour */
#define	TAIL		LRU_TAIL

#define	emptyVSmapList()	(HEAD = TAIL = NOSMAP)


/*
 * Macro to transfer smaps from various lists
 * to the smap-pool for process 'p'.
 */
#define TAKESMAPS(list,p)	\
 if ((list) != NOSMAP) \
 { \
/*D5*/	register smap_t	next; \
/*D4*/	register smap_t	prevMap; \
/*D3*/	register smap_t	firstMap  = -1;		/* Overload: state-var	*/ \
	\
	smap	= (list);		/* Front of list */ \
	next	= smaps[smap]; \
	prevMap	= smap; \
	taken	= wantSmaps;		/* Predict we get all we want-ed */ \
	while (--wantSmaps && next != NOSMAP) \
	{ \
		if (firstMap == -1) {		/* Initial-State */ \
			firstMap = prevMap; \
			 prevMap = smap; \
		} else if (prevMap+1 == smap) {	/* Mid-run*/ \
			   prevMap++; \
		} else {			/* End-of-run */ \
			/* \
			 * This used to print 'next' rather \
			 * that "current" or "range". \
			 */ \
			if ((prevMap > firstMap) /* Ascending sequence	*/ \
			 && (prevMap > smap)) {  /* ...but broken...	*/ \
				CTXT_DEBUG((" [%x-%x]",	\
					firstMap, prevMap)); \
			} else {		/* Singleton */ \
				CTXT_DEBUG((" %x", firstMap)); \
			} \
			firstMap = prevMap  = smap; /* Set start-of-run */ \
		} \
		smap = next; \
		next = smaps[smap]; \
	} \
	if (prevMap > firstMap) /* Ascending sequence	*/ \
	{			/* ...but never printed	*/ \
		CTXT_DEBUG((" [%x-%x]", firstMap, prevMap)); \
	} else {		/* Singleton */ \
		CTXT_DEBUG((" %x", (firstMap != -1) ? firstMap : prevMap)); \
	} \
	smaps[smap]  = (p)->p_smaps;	/* Join the lists  */ \
	(p)->p_smaps = (list);		/* Front of joined list */ \
	(list)       = next;		/* Remainder of what we didn't take */ \
	taken	    -= wantSmaps;	/* Say we took only what we got */ \
 } else \
	taken	     = 0;

#ifndef	M68020_REV_B
/*
 * This following scheme is incorrect for address spaces larger
 * than 16Mb.  Recall that 'large' address spaces are really
 * the aggregation of 2**(N-1) nominal (16Mb) contexts,
 * where N is the # of extra high-order bits of addressibility
 * beyond 16Mb.
 *	E.g.,	 32Mb	==> N==1	==> 2**0	==> No extra contexts;
 *		 64Mb	==> N==2	==> 2**1	==> 2 contexts/space;
 *		128Mb	==> N==3	==> 2**2	==> 4 contexts/space;
 *
 *	I.e., one must toggle V_SEGMAP in 'saddr' @ EACH 8Mb increment
 *	within the address space:  0, 8, 16, 24, 32, 40, 48, 56, 64, etc...
 *	'nleft' only toggles once @ HALFSEGS, which is OK
 *	for a single 16Mb address space.
 */
#define SEGLOOP(work)	if (nleft < count) \
			{ \
				count -= nleft; \
				while (--nleft >= 0) \
				{ \
					work; \
					saddr += ss; \
				} \
				R_V_CONTEXT = ctxt->number | V_ACCSM|V_ACCHIGH;\
				saddr = (u_short *)V_SEGMAP; \
				nleft = HALFSEGS; \
			} \
			nleft -= count; \
			while (--count >= 0) \
			{ \
				work; \
				saddr += ss; \
			}
#endif	M68020_REV_B


#ifdef WHITE
#define MAPSEGS(prot)	SEGLOOP(CTXT_DEBUG((" %x", smap)); \
				*saddr = smap | (prot); \
				R_V_CONTEXT ^= V_ACCUSER; \
				*saddr = smap | (prot); \
				smap = smaps[smap] \
			)
#define GAPSEGS()	SEGLOOP(*saddr = 0;\
				R_V_CONTEXT ^= V_ACCUSER; \
				*saddr = 0 \
			)
#else WHITE

#ifndef	M68020_REV_B
#define MAPSEGS(prot)	SEGLOOP(CTXT_DEBUG((" %x", smap)); \
				*saddr = smap | (prot); \
				smap = smaps[smap] \
			)
#define GAPSEGS()	SEGLOOP(*saddr = 0)
#endif	M68020_REV_B
#endif WHITE

#ifdef	M68020
	/*
	 * Non-cached pages are strictly fill-on-demand, pg_fileno==FPHYS;
	 * and the may only reside as a "DATAENTRY"
	 * or as a "TOPENTRY" in the programs's address space.
	 */
#define	M68020_CACHE	if (((struct fpte *)pte)->pg_noncached) \
				entry |= V_NONCACHED;
#else	M68020
#define	M68020_CACHE	;
#endif	M68020

/*
 * Pte-s for iospace pages are constrained
 * to ONLY the fill-on-demand s/w pte format.
 * Any V_MBIO-ness is buried in the pg_blkno subfield
 * of the p_fod==1 fpte's.
 * Thereby we pass along all the information
 * from the s/w pte's to the h/w pte's.
 */
#define DATAENTRY	if (pte->pg_v) \
				entry = pte->pg_pfnum; \
			else if (pte->pg_fod && ((struct fpte *)pte)->pg_fileno == PG_FPHYS) { \
				entry = ((struct fpte *)pte)->pg_blkno; \
				M68020_CACHE; \
			} else \
				entry = V_PAGE_INVALID; \
			if (pte->pg_m) \
				entry |= V_DIRTY; \
			++pte;


#define STACKENTRY	if (pte->pg_v) \
				entry = pte->pg_pfnum; \
			else \
				entry = V_PAGE_INVALID; \
			if (pte->pg_m) \
				entry |= V_DIRTY; \
			++pte;


#define TOPENTRY	if (pte->pg_fod && ((struct fpte *)pte)->pg_fileno == PG_FPHYS) { \
				entry = ((struct fpte *)pte)->pg_blkno; \
				M68020_CACHE; \
			} else \
				entry = V_PAGE_INVALID; \
			if (pte->pg_m) \
				entry |= V_DIRTY; \
			++pte;


#define	GAPENTRY	/* This space left intentionally blank */


#ifdef WHITE
#define WHITEMAPPAGES	R_V_CONTEXT = newctxt | V_ACCUSER; \
			*paddr = entry;
#else WHITE
#define WHITEMAPPAGES
#endif WHITE

#ifndef	M68020_REV_B
/*
 * This following scheme is incorrect for address spaces larger
 * than 16Mb.  Recall that 'large' address spaces are really
 * the aggregation of 2**(N-1) nominal (16Mb) contexts,
 * where N is the # of extra high-order bits of addressibility
 * beyond 16Mb.
 *	E.g.,	 32Mb	==> N==1	==> 2**0	==> No extra contexts;
 *		 64Mb	==> N==2	==> 2**1	==> 2 contexts/space;
 *		128Mb	==> N==3	==> 2**2	==> 4 contexts/space;
 *
 *	I.e., one must toggle V_PAGEMAP in 'paddr' at each 8Mb increment
 *	within the address space:  0, 8, 16, 24, 32, 40, 48, 56, 64, etc...
 *	'nleft' only toggles once @ HALFPAGES, which is OK
 *	for a single 16Mb address space.
 */
#define MAPPAGES(mkentry) if (nleft < count) \
			{ \
				count -= nleft; \
				while (--nleft >= 0) \
				{ \
					mkentry; \
					CTXT_DEBUG((" %x", entry)); \
					R_V_CONTEXT = newctxt; \
					*paddr = entry; \
					WHITEMAPPAGES \
					R_V_CONTEXT = ctxt->number; \
					paddr += ps; \
				} \
				newctxt = ctxt->number | V_ACCPM | V_ACCHIGH; \
				paddr = (u_short *)V_PAGEMAP; \
				nleft = HALFPAGES; \
			} \
			nleft -= count; \
			while (--count >= 0) \
			{ \
				mkentry; \
				CTXT_DEBUG((" %x", entry)); \
				R_V_CONTEXT = newctxt; \
				*paddr = entry; \
				WHITEMAPPAGES \
				R_V_CONTEXT = ctxt->number; \
				paddr += ps; \
			}

#else	M68020_REV_B

/*
 * MAPPAGES  -- Only map up to the theoretical limit
 *		of the # of pages that the PHYSICAL memory will support.
 *
 * CONVENTIONS:
 * ===========
 *	1) 'oldPpaddr' is virtual address of 1st page
 *	   in the group of pages to be given a new PMAP entry.
 *	   'paddr' is herein computed from 'oldPaddr'.
 *	   NOTE:  This is the REVERSE of the non-M68020_REV_B convention.
 *
 *	2) 'mkentry' is a thunk that computes the value
 *	   of the new mmu entry upon each of 'count' iterations;
 *
 *	3) 'count' is the # of pages to be so mapped;
 *
 *	4) 'newctxt' is the baseline context value:  ctxt# + V_ACCPM;
 */
#define MAPPAGES(mkentry) \
 {  register missingCnt = 0; \
    int	     skippedMRUs = 0; /* ~0==> We skipped remapping MRU_HEAD entries */\
    /* \
     * Guarantee, rather than inhierit initial conditions. \
     */ \
    olda23 = 0; \
    newctxt = ctxt->number | V_ACCPM | V_ACCLOW; \
    paddr  = (u_short *) (oldPaddr | V_PAGEMAP); \
    CTXT_DEBUG((" (cnt=%d) oldPaddr=0x%x", count, oldPaddr)); \
    while (--count >= 0) \
    { \
	if (mmu_BINGO && (oldPaddr == mmu_BINGO_vaddr)) cdebugger("BINGO! sureg: Page"); \
	if (skipMRU							\
	 && ((u.u_smapLRU_TAIL & V_VSEGNO_MASK) << SEGSHIFT) == (oldPaddr & V_SEGMASK)) { \
	   skippedMRUs++;		/* Don't tally it again */	\
	   CTXT_DEBUG((" SKIPMRU=x%x,", oldPaddr>>PGSHIFT));		\
	   paddr    += ps;		/* Advance to next page */	\
	   oldPaddr += pagesize;					\
	   continue;			/* Dont' (re)map this page */	\
	} \
	if (++pagesMappedSoFar > maxPagesAvail) {	/* Don't overrun segs*/\
	        --pagesMappedSoFar; \
		unMappedPages += ++count;		/* Recover */	\
	        if (missingCnt) {	/* Last was a "MISSING" */	\
			CTXT_DEBUG((" %x*%x's", missingCnt,V_MISSING));	\
			missingCnt = 0; \
	        } \
		CTXT_DEBUG((" pagesMappedSoFar=0x%x maxPagesAvail=0x%x", \
			      pagesMappedSoFar,	    maxPagesAvail)); \
		CTXT_DEBUG((" count=0x%x", count)); \
	        CTXT_DEBUG((" oldPaddr=0x%x;", oldPaddr)); \
		goto canDoNoMorePages; /* Punt...*/ \
	} \
	mkentry;			/* Build pagemap entry	    */ \
	if (entry==V_MISSING) {		/* Collapse redundant tally */ \
		missingCnt++; \
	} else if (missingCnt) { \
		CTXT_DEBUG((" %x*%x's", missingCnt,V_MISSING));\
		missingCnt = 0; \
		CTXT_DEBUG((" %x", entry)); \
	} else	CTXT_DEBUG((" %x", entry)); \
	/*								\
	 * 'paddr' always has V_PAGEMAP asserted;			\
	 * Toggle between V_ACCLOW & V_ACCHIGH every V_PAGEMAP bytes in 'paddr'\
	 * Advance 'paddr' and 'oldPaddr' for next page;		\
	 */								\
	if (olda23 != (oldPaddr & V_PAGEMAP)) /* Should we toggle? */	\
	{ \
		paddr	= (u_short *)(oldPaddr|V_PAGEMAP);		\
		olda23	= oldPaddr & V_PAGEMAP; 			\
		newctxt	= ctxt->number | V_ACCPM			\
			| (olda23	/* Toggle */			\
				? V_ACCHIGH				\
				: V_ACCLOW);				\
	} \
	/*								\
	 * Maintain TIGHT control on acces to the page map!		\
	 */								\
	R_V_CONTEXT	 = newctxt;	/* Access h/w pagemap	 */	\
	*paddr		 = entry;	/* Install pagemap entry */	\
	WHITEMAPPAGES							\
	R_V_CONTEXT	 = ctxt->number;/* Revert		 */	\
	paddr		+= ps;		/* Advance to next page	 */	\
	oldPaddr	+= pagesize;					\
    } \
    if (missingCnt) {	/* Last was a "MISSING" */			\
	CTXT_DEBUG((" %x*%x's", missingCnt,V_MISSING));			\
	missingCnt = 0; \
    } \
    CTXT_DEBUG((" oldPaddr=0x%x;", oldPaddr)); \
    CTXT_DEBUG((" pagesMappedSoFar=0x%x", pagesMappedSoFar)); \
 }
#endif	M68020_REV_B

#define	DATAGAP() \
		/* \
		 * Invalidate all pages which are in the \
		 * same segment as data but which are not \
		 * in data themselves. \
		 */ \
		if (count = suregp0lr & SPMASK) \
		{ \
			count = PPS - count; \
			/* \
			 * Keep track of what we're doing \
			 * for a diagnostic audit trail. \
			 */ \
			if (unMappedPages < count) \
				count = unMappedPages; \
			unMappedPages -= count; \
			entry = V_PAGE_INVALID; \
			CTXT_DEBUG((" DG:")); \
			MAPPAGES(GAPENTRY); \
		}

#define	STACKGAP() \
	{ \
		/* \
		 * Invalidate all pages which are in the \
		 * same segment as the stack but which \
		 * are not in the stack themselves. \
		 */ \
		oldPaddr  = (u_long) \
			ptob(LOWPAGES + suregp0lr + (PPS - \
			((suregp0lr & SPMASK) ? (suregp0lr & SPMASK) : PPS))) \
			+ (stoc(gap) * ps * sizeof(u_short)); \
		paddr     = (u_short *)(oldPaddr | V_PAGEMAP); \
		count	  = suregp1lr & SPMASK; \
		if (unMappedPages < count) \
			count   = unMappedPages; \
		unMappedPages  -= count; \
						\
		entry		= V_PAGE_INVALID; \
						\
		CTXT_DEBUG((" SG:")); \
		MAPPAGES(GAPENTRY); \
	}
#endif	M68020_REV_B
#endif	CONTEXT_ALREADY_DEFINED
