~kris/9p

9hist

5611c0b722b27673e252b8892b717742dff8dcec — David du Colombier 34 years ago 7378b28
Plan 9 from Bell Labs 1992-09-11
10 files changed, 736 insertions(+), 451 deletions(-)

M pc/l.s
M port/fault.c
M port/page.c
A ss/compile.c
M ss/dat.h
M ss/faultsparc.c
M ss/fns.h
M ss/main.c
M ss/mem.h
M ss/mmu.c
M pc/l.s => pc/l.s +18 -0
@@ 220,6 220,24 @@ TEXT	outb(SB),$0
	RET

/*
 * input a short
 */
TEXT ins(SB), $0
	MOVL	p+0(FP), DX
	XORL	AX, AX
	OP16; INSL
	RET

/*
 * output a short
 */
TEXT outs(SB), $0
	MOVL	p+0(FP), DX
	MOVL	s+4(FP), AX
	OP16; OUTSL
	RET

/*
 *  input a string of shorts from a port
 */
TEXT	inss(SB),$0

M port/fault.c => port/fault.c +2 -0
@@ 184,6 184,8 @@ pio(Segment *s, ulong addr, ulong soff, Page **p)
	kaddr = (char*)VA(k);
	
	if(loadrec == 0) {			/* This is demand load */
if(u && strcmp(u->p->text, "rc") == 0)
print("dl: %d %s %lux\n", u->p->pid, u->p->text, addr);
		c = s->image->c;
		while(waserror()) {
			if(strcmp(u->error, Eintr) == 0)

M port/page.c => port/page.c +2 -2
@@ 55,8 55,8 @@ pageinit(void)
	hw = swapalloc.highwater*BY2PG;
	hr = swapalloc.headroom*BY2PG;
	
	print("%lud free pages, %dK bytes, swap %dK, highwater %dK, headroom %dK\n", 
	palloc.user, pmem, vmem, hw/1024, hr/1024);
/*	print("%lud free pages, %dK bytes, swap %dK, highwater %dK, headroom %dK\n", 
	palloc.user, pmem, vmem, hw/1024, hr/1024);/**/
}

Page*

A ss/compile.c => ss/compile.c +192 -0
@@ 0,0 1,192 @@
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"

#define	NCODE	1024
static	ulong	code[NCODE];
static	ulong	*codep;

ulong	(*getcontext)(void);
void	(*putcontext)(ulong);
void	(*putenab)(ulong);
ulong	(*getenab)(void);
ulong	(*getsysspace)(ulong);
void	(*putsysspace)(ulong, ulong);
uchar	(*getsysspaceb)(ulong);
void	(*putsysspaceb)(ulong, uchar);
ulong	(*getsegspace)(ulong);
void	(*putsegspace)(ulong, ulong);
ulong	(*getpmegspace)(ulong);
void	(*putpmegspace)(ulong, ulong);
ulong	(*flushcx)(ulong);
ulong	(*flushpm)(ulong);
ulong	(*flushpg)(ulong);

/*
 * Compile MMU code for this machine, since the MMU can only
 * be addressed from parameterless machine instructions.
 * What's wrong with MMUs you can talk to from C?
 */

/* op3 */
#define	LD	0
#define	ADD	0
#define	OR	2
#define	LDA	16
#define	LDUBA	17
#define	STA	20
#define	STBA	21
#define	JMPL	56
/* op2 */
#define	SETHI	4

#define	ret()	{*codep++ = (2<<30)|(0<<25)|(JMPL<<19)|(15<<14)|(1<<13)|8;}
#define	nop()	{*codep++ = (0<<30)|(0<<25)|(SETHI<<22)|(0>>10);}

static void
parameter(int param, int reg)
{
	param += 1;	/* 0th parameter is 1st word on stack */
	param *= 4;
	/* LD #param(R1), Rreg */
	*codep++ = (3<<30)|(reg<<25)|(LD<<19)|(1<<14)|(1<<13)|param;
}

static void
constant(ulong c, int reg)
{
	*codep++ = (0<<30)|(reg<<25)|(SETHI<<22)|(c>>10);
	if(c & 0x3FF)
		*codep++ = (2<<30)|(reg<<25)|(OR<<19)|(reg<<14)|(1<<13)|(c&0x3FF);
}

/*
 * value to/from constant address
 * void f(int c) { *(word*,asi)addr = c } for stores
 * ulong f(void)  { return *(word*,asi)addr } for loads
 */
static void*
compileconst(int op3, ulong addr, int asi)
{
	void *a;

	a = codep;
	constant(addr, 8);	/* MOVW $CONSTANT, R8 */
	ret();			/* JMPL 8(R15), R0 */
	/* in delay slot 	   st or ld R7, (R8+R0, asi)	*/
	*codep++ = (3<<30)|(7<<25)|(op3<<19)|(8<<14)|(asi<<5);
	return a;
}

/*
 * value to parameter address
 * void f(ulong addr, int c) { *(word*,asi)addr = c }
 */
static void*
compilestaddr(int op3, int asi)
{
	void *a;

	a = codep;
	parameter(1, 8);	/* MOVW (4*1)(FP), R8 */
	ret();			/* JMPL 8(R15), R0 */
	/* in delay slot 	   st R8, (R7+R0, asi)	*/
	*codep++ = (3<<30)|(8<<25)|(op3<<19)|(7<<14)|(asi<<5);
	return a;
}

/*
 * value from parameter address
 * ulong f(ulong addr)  { return *(word*,asi)addr }
 */
static void*
compileldaddr(int op3, int asi)
{
	void *a;

	a = codep;
	ret();			/* JMPL 8(R15), R0 */
	/* in delay slot 	   ld (R7+R0, asi), R7	*/
	*codep++ = (3<<30)|(7<<25)|(op3<<19)|(7<<14)|(asi<<5);
	return a;
}

/*
 * 1 store of zero
 * ulong f(ulong addr) { *addr=0; addr+=offset; return addr}
 * offset can be anything
 */
static void*
compile1(ulong offset, int asi)
{
	void *a;

	a = codep;
	/* ST R0, (R7+R0, asi)	*/
	*codep++ = (3<<30)|(0<<25)|(STA<<19)|(7<<14)|(asi<<5);
	if(offset < (1<<12)){
		ret();			/* JMPL 8(R15), R0 */
		/* in delay slot ADD $offset, R7 */
		*codep++ = (2<<30)|(7<<25)|(ADD<<19)|(7<<14)|(1<<13)|offset;
	}else{
		constant(offset, 8);
		ret();			/* JMPL 8(R15), R0 */
		/* in delay slot ADD R8, R7 */
		*codep++ = (2<<30)|(7<<25)|(ADD<<19)|(7<<14)|(0<<13)|8;
	}
	return a;
}

/*
 * 16 stores of zero
 * ulong f(ulong addr) { for(i=0;i<16;i++) {*addr=0; addr+=offset}; return addr}
 * offset must be less than 1<<12
 */
static void*
compile16(ulong offset, int asi)
{
	void *a;
	int i;

	a = codep;
	for(i=0; i<16; i++){
		/* ST R0, (R7+R0, asi)	*/
		*codep++ = (3<<30)|(0<<25)|(STA<<19)|(7<<14)|(asi<<5);
		/* ADD $offset, R7 */
		*codep++ = (2<<30)|(7<<25)|(ADD<<19)|(7<<14)|(1<<13)|offset;
	}
	ret();			/* JMPL 8(R15), R0 */
	nop();
	return a;
}

void
compile(void)
{
	codep = code;

	getcontext = compileconst(LDUBA, CONTEXT, 2);
	putcontext = compileconst(STBA, CONTEXT, 2);
	getenab = compileconst(LDUBA, ENAB, 2);
	putenab = compileconst(STBA, ENAB, 2);
	getsysspace = compileldaddr(LDA, 2);
	putsysspace = compilestaddr(STA, 2);
	getsysspaceb = compileldaddr(LDUBA, 2);
	putsysspaceb = compilestaddr(STBA, 2);
	getsegspace = compileldaddr(LDUBA, 3);
	putsegspace = compilestaddr(STBA, 3);
	getpmegspace = compileldaddr(LDA, 4);
	putpmegspace = compilestaddr(STA, 4);
	if(conf.ss2){
		flushpg = compile1(BY2PG, 6);
		flushpm = compile16(conf.vaclinesize, 5);
		flushcx = compile16(conf.vaclinesize, 7);
	}else{
		flushpg = compile16(conf.vaclinesize, 0xD);
		flushpm = compile16(conf.vaclinesize, 0xC);
		flushcx = compile16(conf.vaclinesize, 0xE);
	}
}

M ss/dat.h => ss/dat.h +13 -5
@@ 1,4 1,5 @@
typedef struct Conf	Conf;
typedef struct Ctx	Ctx;
typedef struct FFrame	FFrame;
typedef struct FPsave	FPsave;
typedef struct KMap	KMap;


@@ 6,12 7,15 @@ typedef struct Lance	Lance;
typedef struct Lancemem	Lancemem;
typedef struct Label	Label;
typedef struct Lock	Lock;
typedef struct Pmeg	Pmeg;
typedef struct PMMU	PMMU;
typedef struct Mach	Mach;
typedef struct Ureg	Ureg;
typedef struct List	List;
typedef struct User	User;



#define	MACHP(n)	(n==0? &mach0 : *(Mach**)0)

extern	Mach	mach0;


@@ 87,13 91,13 @@ struct Conf
	ulong	frag;		/* Ip fragment assemble queue size */
};


/*
 *  mmu goo in the Proc structure
 */
struct PMMU
{
	int	pidonmach[MAXMACH];
	int	nmmuseg;	/* number of segments active in mmu */
	Ctx	*ctxonmach[MAXMACH];
};

#include "../port/portdat.h"


@@ 121,9 125,13 @@ struct Mach
	Lock	alarmlock;		/* access to alarm list */
	void	*alarm;			/* alarms bound to this clock */
	int	fpstate;		/* state of fp registers on machine */
	char	pidhere[NTLBPID];	/* is this tlbpid possibly in this mmu? */
	int	lastpid;		/* last pid allocated on this machine */
	Proc	*pidproc[NTLBPID];	/* process that owns this pid on this mach */

	Ctx	*cctx;			/* current context */
	List	*clist;			/* lru list of all contexts */
	Pmeg	*pmeg;			/* array of allocatable pmegs */
	int	pfirst;			/* index of first allocatable pmeg */
	List	*plist;			/* lru list of non-kernel Pmeg's */
	int	needpmeg;		/* a process needs a pmeg */

	int	tlbfault;
	int	tlbpurge;

M ss/faultsparc.c => ss/faultsparc.c +2 -0
@@ 33,6 33,8 @@ faultsparc(Ureg *ur)
		if(ser&(SE_WRITE|SE_PROT))
			read = 0;
	}
if(u && strcmp(u->p->text, "rc") == 0)
print("fault pc=%lux addr=%lux %d\n", ur->pc, addr, read);/**/
	spllo();
	if(u == 0){
		dumpregs(ur);

M ss/fns.h => ss/fns.h +16 -9
@@ 5,6 5,7 @@ ulong	call(void*, ...);
void	clearftt(ulong);
#define	clearmmucache()
void	clockinit(void);
void	compile(void);
void	disabfp(void);
void	enabfp(void);
void	evenaddr(ulong);


@@ 12,7 13,6 @@ char*	excname(ulong);
void	faultasync(Ureg*);
void	faultsparc(Ureg*);
void	flushcpucache(void);
#define	flushpage(x)
int	fpcr(int);
int	fpquiet(void);
void	fpregrestore(char*);


@@ 73,11 73,18 @@ void	trapinit(void);
/*
 * compiled entry points
 */
extern	void	(*putcontext)(ulong);
extern	void	(*putenab)(ulong);
extern	ulong	(*getenab)(void);
extern	void	(*putpmegspace)(ulong, ulong);
extern	void	(*putsysspace)(ulong, ulong);
extern	ulong	(*getsysspace)(ulong);
extern	ulong	(*flushcx)(ulong);
extern	ulong	(*flushpg)(ulong);
extern ulong	(*getcontext)(void);
extern void	(*putcontext)(ulong);
extern void	(*putenab)(ulong);
extern ulong	(*getenab)(void);
extern ulong	(*getsysspace)(ulong);
extern void	(*putsysspace)(ulong, ulong);
extern uchar	(*getsysspaceb)(ulong);
extern void	(*putsysspaceb)(ulong, uchar);
extern ulong	(*getsegspace)(ulong);
extern void	(*putsegspace)(ulong, ulong);
extern ulong	(*getpmegspace)(ulong);
extern void	(*putpmegspace)(ulong, ulong);
extern ulong	(*flushcx)(ulong);
extern ulong	(*flushpg)(ulong);
extern ulong	(*flushpm)(ulong);

M ss/main.c => ss/main.c +1 -4
@@ 19,7 19,6 @@ char	mempres[256];
char	fbstr[32];
ulong	fbslot;
Label	catch;
int	NKLUDGE;

typedef struct Sysparam Sysparam;
struct Sysparam


@@ 58,6 57,7 @@ main(void)
	active.machs = 1;
	trapinit();
	confinit();
conf.monitor = 0;
	xinit();
	mmuinit();
	kmapinit();


@@ 140,7 140,6 @@ init0(void)
	print("Sun Sparcstation %s\n", sparam->name);
	print("bank 0: %dM  1: %dM\n", bank[0], bank[1]);
	print("frame buffer id %lux slot %ld %s\n", conf.monitor, fbslot, fbstr);
	print("NKLUDGE %d\n", NKLUDGE);
	if(conf.npage1 != conf.base1)
		print("kernel mem from second bank: reboot and fix!\n");



@@ 374,8 373,6 @@ confinit(void)
	conf.vacsize = sparam->vacsize;
	conf.vaclinesize = sparam->vacline;
	conf.ncontext = sparam->ncontext;
	if(conf.ncontext > 8)
		conf.ncontext = 8;	/* BUG to enlarge NKLUDGE */
	conf.npmeg = sparam->npmeg;
	conf.ss2cachebug = sparam->cachebug;


M ss/mem.h => ss/mem.h +2 -11
@@ 58,8 58,6 @@
#define	VAMASK		0x3FFFFFFF
#define	BY2SEGM		(1<<18)
#define	PG2SEGM		(1<<6)
#define	NTLBPID		(1+MAXCONTEXT)	/* TLBPID 0 is unallocated */
#define	MAXCONTEXT	16
#define	CONTEXT		0x30000000	/* in ASI 2 */

/*


@@ 67,15 65,8 @@
 */
#define	INVALIDSEGM	0xFFFC0000	/* highest seg of VA reserved as invalid */
#define	INVALIDPMEG	(conf.npmeg-1)
#define	ROMPMEG		(conf.npmeg-2)
#define	ROMSEGM		0xFFE80000
#define	ROMEND		0xFFEC0000
#define	PG2ROM		((ROMEND-ROMSEGM)/BY2PG)
#define	NIOSEGM		((MB/BY2SEGM) + 8)	/* 1M for screen + overhead */
#define	IOSEGM0		(ROMSEGM-NIOSEGM*BY2SEGM)
#define	IOPMEG0		(ROMPMEG-NIOSEGM)
#define	IOEND		ROMSEGM
#define	TOPPMEG		IOPMEG0
#define IOSEGSIZE	(MB + 2*MB)	/* 1 meg for screen plus overhead */	
#define IOSEGM		(INVALIDSEGM - IOSEGSIZE)

/*
 * MMU entries

M ss/mmu.c => ss/mmu.c +488 -420
@@ 1,331 1,569 @@
#include	"u.h"
#include	"../port/lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"io.h"

void	compile(void);
#define	NCODE	1024
static	ulong	code[NCODE];
static	ulong	*codep = code;

void	(*putcontext)(ulong);
void	(*putenab)(ulong);
ulong	(*getenab)(void);
void	(*putpmegspace)(ulong, ulong);
void	(*putsysspace)(ulong, ulong);
ulong	(*getsysspace)(ulong);
ulong	(*flushcx)(ulong);
ulong	(*flushpg)(ulong);
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"

struct
#define SERIALPORT	0xF0000000	/* MMU bypass */

static int
rawputc(int c)
{
	Lock;
	int	lowpmeg;
	KMap	*free;
	KMap	arena[(IOEND-IOSEGM0)/BY2PG];
}kmapalloc;
	if(c == '\n')
		rawputc('\r');
	while((getsysspaceb(SERIALPORT+4) & (1<<2)) == 0)
		delay(10);
	putsysspaceb(SERIALPORT+6, c);
	if(c == '\n')
		delay(20);
	return c;
}

void
rawputs(char *s)
{
	while(*s)
		rawputc(*s++);
}

int	NKLUDGE;
void
rawprint(char *fmt, ...)
{
	char buf[PRINTSIZE];

	doprint(buf, buf+sizeof(buf), fmt, (&fmt+1));
	rawputs(buf);
}

static void
rawpanic(char *fmt, ...)
{
	char buf[PRINTSIZE];
	int s;

	s = splhi();
	doprint(buf, buf+sizeof(buf), fmt, (&fmt+1));
	rawputs("rawpanic: ");
	rawputs(buf);
	systemreset();
	splx(s);
}

/*
 *  WARNING:	Even though all MMU data is in the mach structure or
 *		pointed to by it, this code will not work on a multi-processor
 *		without modification.
 */
	
/*
 * On SPARC, tlbpid i == context i-1 so that 0 means unallocated
 *  doubly linked list for LRU algorithm
 */
struct List
{
	List *prev;		/* less recently used */
	List *next;		/* more recently used */
};

int	newpid(Proc*);
void	purgepid(int);
void	flushcontext(void);
void	putpmegnf(ulong, ulong);
/*
 *  a Sparc context description
 */
struct Ctx
{
	List;	/* MUST BE FIRST IN STRUCTURE!! */

int	pidtime[NTLBPID];	/* should be per m */
	Proc	*proc;	/* process that owns this context */
	Pmeg	*pmeg;	/* list of pmeg's used by this context */
	ushort	index;	/* which context this is */
};

/*
 * Called splhi, not in Running state
 *  a Sparc PMEG description
 */
void
mapstack(Proc *p)
struct Pmeg
{
	short tp;
	ulong tlbphys;
	List;	/* MUST BE FIRST IN STRUCTURE!! */

	ulong	lo;	/* low water mark of used entries */
	ulong	hi;	/* high water mark of used entries */
	ushort	index;	/* which Pmeg this is */

	ulong	virt;	/* virtual address this Pmeg represents */
	Ctx	*ctx;	/* context Pmeg belongs to */
	Pmeg	*cnext;	/* next pmeg used in same context */
};

#define	HOWMANY(x, y)	(((x)+((y)-1))/(y))
#define ROUNDUP(x, y)	(HOWMANY((x), (y))*(y))

static Pmeg*	allocpmeg(ulong);
static void	freepmeg(Pmeg*, int);
static Ctx*	allocctx(Proc*);
static void	freectx(Ctx*, int);
static void	putpme(ulong, ulong, int);
static void	mklast(List**, List*);
static void	mkfirst(List**, List*);
static void	remove(List**, List*);
static void	add(List**, List*);
static void	flushpage(ulong);
static void	putctx(Ctx*);

	if(p->newtlb) {
		mmurelease(p);
		p->newtlb = 0;
/*
 *  move to end of use list
 */
static void
mklast(List **list, List *entry)
{
	List *first;

	first = *list;
	if(entry == first){
		*list = first->next;
		return;	
	}

	tp = p->pidonmach[m->machno];
	if(tp == 0){
		tp = newpid(p);
		p->pidonmach[m->machno] = tp;
	/* remove from list */
	entry->prev->next = entry->next;
	entry->next->prev = entry->prev;

	/* chain back in */
	entry->prev = first->prev;
	entry->next = first;
	first->prev->next = entry;
	first->prev = entry;
}

/*
 *  move to front of use list
 */
static void
mkfirst(List **list, List *entry)
{
	List *first;

	first = *list;
	if(entry == first)
		return;		/* already at front */

	/* unchain */
	entry->prev->next = entry->next;
	entry->next->prev = entry->prev;

	/* chain back in */
	entry->prev = first->prev;
	entry->next = first;
	first->prev->next = entry;
	first->prev = entry;

	*list = entry;
}

/*
 *  remove from list
 */
static void
remove(List **list, List *entry)
{
	if(*list == entry)
		*list = entry->next;

	entry->prev->next = entry->next;
	entry->next->prev = entry->prev;
}

/*
 *  add to list
 */
static void
add(List **list, List *entry)
{
	List *first;

	first = *list;
	if(first == 0){
		entry->prev = entry;
		entry->next = entry;
	} else {
		entry->prev = first->prev;
		entry->next = first;
		first->prev->next = entry;
		first->prev = entry;
	}
	if(p->upage->va != (USERADDR|(p->pid&0xFFFF)) && p->pid != 0)
		panic("mapstack %s %d %lux 0x%lux 0x%lux", p->text, p->pid, p->upage, p->upage->pa, p->upage->va);
	tlbphys = PPN(p->upage->pa)|PTEVALID|PTEWRITE|PTEKERNEL|PTEMAINMEM;
	putcontext(tp-1);
	/*
	 * Don't need to flush cache because no other page has been
	 * mapped at USERADDR in this context; can call putpmegnf.
	 */
	putpmegnf(USERADDR, tlbphys);
	u = (User*)USERADDR;
	*list = entry;
}

void
mmurelease(Proc *p)
/*
 *  add a pmeg to the current context for the given address
 */
static Pmeg*
allocpmeg(ulong virt)
{
	int tp;
	Pmeg *p;
	Ctx *c;

	virt = virt & ~(BY2SEGM-1);
	for(;;){
		p = (Pmeg*)m->plist;
		c = p->ctx;
		if(c == 0)
			break;
		m->needpmeg = 1;
		sched();
		splhi();
	}

	tp = p->pidonmach[m->machno];
	if(tp)
		pidtime[tp] = 0;
	/* easiest is to forget what pid we had.... */
	memset(p->pidonmach, 0, sizeof p->pidonmach);
	/* add to current context */
	p->ctx = m->cctx;
	p->cnext = m->cctx->pmeg;
	m->cctx->pmeg = p;
	p->virt = virt;
	p->lo = virt;
	p->hi = virt;
	putsegspace(p->virt, p->index);
	mklast(&m->plist, p);
	return p;
}

static void
freepmeg(Pmeg *p, int flush)
{
	ulong x;

	/* invalidate any used PTE's, flush cache */
	for(x = p->lo; x <= p->hi; x += BY2PG)
		putpme(x, INVALIDPTE, flush);

	/* invalidate segment pointer */
	putsegspace(p->virt, INVALIDPMEG);

	p->hi = 0;
	p->lo = 0;
	p->cnext = 0;
	p->ctx = 0;
	p->virt = 0;
}

/*
 * Process must be non-interruptible
 *  get a context for a process.
 */
int
newpid(Proc *p)
static Ctx*
allocctx(Proc *proc)
{
	int i, j, nc;
	ulong t;
	Proc *sp;

	t = ~0;
	nc = conf.ncontext;
	i = 1+((m->ticks)&(nc-1));	/* random guess */
	nc++;
	for(j=1; t && j<nc; j++)
		if(pidtime[j] < t){
			i = j;
			t = pidtime[j];
		}
	
	sp = m->pidproc[i];
	if(sp)
		sp->pidonmach[m->machno] = 0;
	purgepid(i);	/* also does putcontext */
	pidtime[i] = m->ticks;
	m->pidproc[i] = p;
	m->lastpid = i;
	Ctx *c;

	c = (Ctx*)m->clist;
	putctx(c);
	if(c->proc)
		freectx(c, 1);
	c->proc = proc;
	c->proc->ctxonmach[m->machno] = c;
	mklast(&m->clist, c);
	return c;
}

	/*
	 * kludge: each context is allowed NKLUDGE pmegs.
	 * NKLUDGE-1 for text & data and 1 for stack.
	 * initialize by giving just a stack segment.
	 */
	i--;	/* now i==context */
	p->nmmuseg = 0;
	for(j=0; j<NKLUDGE-1; j++)
		putsegm(UZERO+j*BY2SEGM, INVALIDPMEG);
	putsegm(TSTKTOP-BY2SEGM, kmapalloc.lowpmeg+NKLUDGE*i+(NKLUDGE-1));
	for(j=0; j<PG2SEGM; j++)
		putpmegnf((TSTKTOP-BY2SEGM)+j*BY2PG, INVALIDPTE);
	return i+1;
static void
freectx(Ctx *c, int flush)
{
	Pmeg *p;

	putctx(c);

	/* give back mappings */
	while(p = c->pmeg){
		c->pmeg = p->cnext;
		freepmeg(p, flush);
		mkfirst(&m->plist, p);
	}

	/* flush u-> */
	flushpage(USERADDR);

	/* detach from process */
	c->proc->ctxonmach[m->machno] = 0;
	c->proc = 0;
}

void
flushcontext(void)
static void
putctx(Ctx *c)
{
	putcontext(c->index);
	m->cctx = c;
}

static void
flushpage(ulong virt)
{
	ulong a;
	ulong a, evirt;

	a = 0;
	a = virt;
	evirt = virt+BY2PG;
	do
		a = flushcx(a);
	while(a < conf.vacsize);
		a = flushpg(a);
	while(a < evirt);
}

/*
 *  stuff an entry into a pmeg
 */
static void
putpme(ulong virt, ulong phys, int flush)
{
	virt = virt & ~(BY2PG-1);
	if(flush)
		flushpage(virt);
	putpmegspace(virt, phys);
}

/*
 *  put a mapping into current context
 */
void
putmmu(ulong virt, ulong phys, Page *pg)
{
	ulong x, v;
	Pmeg *p;
	int s;

	USED(pg);
	s = splhi();
	v = virt & ~(BY2SEGM-1);
	x = getsegspace(v);
	if(x == INVALIDPMEG)
		p = allocpmeg(v);
	else
		p = &m->pmeg[x - m->pfirst];
	if(virt < p->lo)
		p->lo = virt;
	if(virt > p->hi)
		p->hi = virt;
	
	putpme(virt, phys, 1);
	splx(s);
}

/*
 * Initialize cache by clearing the valid bit
 * (along with the others) in all cache entries
 */
void
purgepid(int pid)
cacheinit(void)
{
	putcontext(pid-1);
	flushcontext();
	int i;

	for(i=0; i<conf.vacsize; i+=conf.vaclinesize)
		putsysspace(CACHETAGS+i, 0);

	/*
	 * Turn cache on
	 */
	putenab(getenab()&~ENABCACHE);
}

/*
 *  set up initial mappings and a the LRU lists for contexts and pmegs
 */
void
mmuinit(void)
{
	ulong ktop, l, i, j, c, pte;
	int c, i, j;
	ulong va, ktop, pme;
	int fp;		/* first free pmeg */
	Pmeg *p;
	Ctx *ctx;

conf.ncontext = 1; /**/
	compile();

	/*
	 * xinit sets conf.npage0 to maximum kernel address
	 *  First map kernel text, data, bss, and xalloc regions.
	 *  xinit sets conf.npage0 to end of these.
	 */
	ktop = PADDR(conf.npage0);
	ktop = PGROUND(conf.npage0);
	i = 0;
	for(c = 0; c < conf.ncontext; c++){
		i = 0;
		for(va = KZERO; va < ktop; va += BY2SEGM)
			putcxsegm(c, va, i++);
	}
	fp = i;

	/*
	 * First map lots of memory as kernel addressable in all contexts
	 *  Make sure cache is turned on for kernel
	 */
	i = 0;		/* used */
	for(c=0; c<conf.ncontext; c++)
		for(i=0; i < ktop/BY2SEGM; i++)
			putcxsegm(c, KZERO+i*BY2SEGM, i);

	kmapalloc.lowpmeg = i;
	if(ktop & (BY2SEGM-1))
		kmapalloc.lowpmeg++;
	pme = PTEVALID|PTEWRITE|PTEKERNEL|PTEMAINMEM;
	i = 0;
	for(va = KZERO; va < ktop; va += BY2PG, i++)
		putpme(va, pme+i, 1);

	/*
	 * Make sure cache is turned on for kernel
	 *  invalidate rest of kernel's PMEG
	 */
	pte = PTEVALID|PTEWRITE|PTEKERNEL|PTEMAINMEM;
	ktop /= BY2PG;
	for(i=0; i < ktop; i++)
		putpmeg(KZERO+i*BY2PG, pte+i);
	va = ROUNDUP(ktop, BY2SEGM);
	for(; ktop < va; ktop += BY2PG)
		putpme(ktop, INVALIDPTE, 1);

	/*
	 * Create invalid pmeg; use highest segment
	 *  allocate pmegs for kmap'ing
	 */
	putsegm(INVALIDSEGM, INVALIDPMEG);
	for(i=0; i<PG2SEGM; i++)
		putpmeg(INVALIDSEGM+i*BY2PG, INVALIDPTE);

	for(c=0; c<conf.ncontext; c++){
	for(c = 0; c < conf.ncontext; c++){
		putcontext(c);
		putsegm(INVALIDSEGM, INVALIDPMEG);
		/*
		 * Invalidate user addresses
		 */
		for(l=UZERO; l<(KZERO&VAMASK); l+=BY2SEGM)
			putsegm(l, INVALIDPMEG);

		/*
		 * Map ROM
		 */
		putsegm(ROMSEGM, ROMPMEG);
		if(c == 0){
			pte = PTEVALID|PTEKERNEL|PTENOCACHE|
				PTEIO|PPN(EPROM);
			for(i=0; i<PG2ROM; i++)
				putpmeg(ROMSEGM+i*BY2PG, pte+i);
			for(; i<PG2SEGM; i++)
				putpmeg(ROMSEGM+i*BY2PG, INVALIDPTE);
		}
		/*
		 * Segments for IO and kmap
		 */
		for(j=0; j<NIOSEGM; j++){
			putsegm(IOSEGM0+j*BY2SEGM, IOPMEG0+j);
			if(c == 0)
				for(i=0; i<PG2SEGM; i++)
					putpmeg(IOSEGM0+j*BY2SEGM+i*BY2PG, INVALIDPTE);
		}
		i = fp;
		for(va = IOSEGM; va < IOSEGM+IOSEGSIZE; va += BY2SEGM)
			putsegspace(va, i++);
	}
	putcontext(0);
	NKLUDGE = ((TOPPMEG-kmapalloc.lowpmeg)/conf.ncontext);
}

void
putmmu(ulong tlbvirt, ulong tlbphys, Page *pg)
{
	short tp;
	Proc *p;
	ulong seg, l;
	int j, k;
	fp = i;

	USED(pg);
	splhi();
	p = u->p;
	tp = p->pidonmach[m->machno];
	if(tp == 0){
		tp = newpid(p);
		p->pidonmach[m->machno] = tp;
	/*
	 *  Invalidate all entries in all other pmegs
	 */
	for(j = fp; j < conf.npmeg; j++){
		putsegspace(INVALIDSEGM, j);
		for(va = INVALIDSEGM; va < INVALIDSEGM+BY2SEGM; va += BY2PG)
			putpme(va, INVALIDPTE, 1);
	}

	/*
	 * kludge part 2: make sure we've got a valid segment
	 *  invalidate everything outside the kernel in every context
	 */
	if(TSTKTOP-BY2SEGM<=tlbvirt && tlbvirt<TSTKTOP)	/* stack; easy */
		goto put;
	/* UZERO is known to be zero here */
	if(tlbvirt < UZERO+p->nmmuseg*BY2SEGM)		/* in range; easy */
		goto put;
	seg = tlbvirt/BY2SEGM;
	if(seg >= (UZERO/BY2SEGM)+(NKLUDGE-1)){
		pprint("putmmu %lux\n", tlbvirt);
print("putmmu %lux %d %s\n", tlbvirt, seg, p->text);
		pexit("Suicide", 1);
	for(c = 0; c < conf.ncontext; c++){
		putcontext(c);
		for(va = UZERO; va < (KZERO & VAMASK); va += BY2SEGM)
			putsegspace(va, INVALIDPMEG);
		for(va = ktop; va < IOSEGM; va += BY2SEGM)
			putsegspace(va, INVALIDPMEG);
	}

	/*
	 * Prepare mmu up to this address
	 *  allocate MMU management data for this CPU
	 */
	tp = (tp-1)*NKLUDGE;	/* now tp==base of pmeg area for this proc */
	l = UZERO+p->nmmuseg*BY2SEGM;
	for(j=p->nmmuseg; j<=seg; j++){
		putsegm(l, kmapalloc.lowpmeg+tp+j);
		for(k=0; k<PG2SEGM; k++,l+=BY2PG)
			putpmegnf(l, INVALIDPTE);
	m->pmeg = p = xalloc((INVALIDPMEG - fp) * sizeof(Pmeg));
	m->pfirst = fp;
	for(; fp < INVALIDPMEG; fp++, p++){
		p->index = fp;
		add(&m->plist, p);
	}
	p->nmmuseg = seg+1;
    put:
	putpmeg(tlbvirt, tlbphys);
	spllo();
	m->cctx = ctx = xalloc(conf.ncontext * sizeof(Ctx));
	for(i = 0; i < conf.ncontext; i++, ctx++){
		ctx->index = i;
		add(&m->clist, ctx);
	}

	putctx(m->cctx);
}

/*
 *  give up our mmu context
 */
void
putpmeg(ulong virt, ulong phys)
mmurelease(Proc *p)
{
	ulong a, evirt;
	Ctx *c;

	virt &= VAMASK;
	virt &= ~(BY2PG-1);
	/*
	 * Flush old entries from cache
	 */
	a = virt;
	evirt = virt+BY2PG;
	do
		a = flushpg(a);
	while(a < evirt);
	putpmegspace(virt, phys);
	c = p->ctxonmach[m->machno];
	if(c == 0)
		return;

	freectx(c, 1);
	mkfirst(&m->clist, c);
}

/*
 *  set up context for a process
 */
void
putpmegnf(ulong virt, ulong phys)	/* no need to flush */
mapstack(Proc *p)
{
	virt &= VAMASK;
	virt &= ~(BY2PG-1);
	putpmegspace(virt, phys);
	Ctx *c;
	ulong tlbphys;
	Pmeg *pm, *f, **l;

	if(p->upage->va != (USERADDR|(p->pid&0xFFFF)) && p->pid != 0)
		panic("mapstack %s %d %lux 0x%lux 0x%lux",
			p->text, p->pid, p->upage, p->upage->pa, p->upage->va);

	/* free a pmeg if a process needs one */
	if(m->needpmeg){
		pm = (Pmeg*)m->plist;
		if(c = pm->ctx){
			/* remove from context list */
			l = &c->pmeg;
			for(f = *l; f; f = f->cnext){
				if(f == pm){
					*l = f->cnext;
					break;
				}
				l = &f->cnext;
			}
	
			/* flush cache & remove mappings */
			putctx(c);
			freepmeg(pm, 1);
		}
		m->needpmeg = 0;
	}

	/* give up our context if it is unclean */
	if(p->newtlb){
		c = p->ctxonmach[m->machno];
		if(c)
			freectx(c, 1);
		p->newtlb = 0;
	}

	/* set to this proc's context */
	c = p->ctxonmach[m->machno];
	if(c == 0)
		allocctx(p);
	else
		putctx(c);

	/* make sure there's a mapping for u-> */
	tlbphys = PPN(p->upage->pa)|PTEVALID|PTEWRITE|PTEKERNEL|PTEMAINMEM;
	putpmegspace(USERADDR, tlbphys);
	u = (User*)USERADDR;
}

void
flushmmu(void)
{
	Ctx *c;
	Pmeg *p;

	splhi();
	u->p->newtlb = 1;
	mapstack(u->p);
	c = u->p->ctxonmach[m->machno];
	if(c == 0)
		panic("flushmmu");
	while(p = c->pmeg){
		c->pmeg = p->cnext;
		freepmeg(p, 1);
		mkfirst(&m->plist, p);
	}
	spllo();
}

void
cacheinit(void)
invalidateu(void)
{
	int i;

	putcontext(0);
	/*
	 * Initialize cache by clearing the valid bit
	 * (along with the others) in all cache entries
	 */
	for(i=0; i<conf.vacsize; i+=conf.vaclinesize)
		putsysspace(CACHETAGS+i, 0);

	/*
	 * Turn cache on
	 */
	putenab(getenab()|ENABCACHE); /**/
	putpme(USERADDR, INVALIDPTE, 1);
}

struct
{
	Lock;
	KMap	*free;
	KMap	arena[IOSEGSIZE/BY2PG];
}kmapalloc;

void
kmapinit(void)
{
	KMap *k;
	int i;
	ulong va;

	kmapalloc.free = 0;
	k = kmapalloc.arena;
	for(i=0; i<(IOEND-IOSEGM0)/BY2PG; i++,k++){
		k->va = IOSEGM0+i*BY2PG;
	for(va = IOSEGM; va < IOSEGM + IOSEGSIZE; va += BY2PG, k++){
		k->va = va;
		kunmap(k);
	}
}


@@ 346,11 584,23 @@ kmappa(ulong pa, ulong flag)
	unlock(&kmapalloc);
	k->pa = pa;
	s = splhi();
	putpmeg(k->va, PPN(pa)|PTEVALID|PTEKERNEL|PTEWRITE|flag);
	putpme(k->va, PPN(pa)|PTEVALID|PTEKERNEL|PTEWRITE|flag, 1);
	splx(s);
	return k;
}


void
kunmap(KMap *k)
{
	k->pa = 0;
	lock(&kmapalloc);
	k->next = kmapalloc.free;
	kmapalloc.free = k;
	putpme(k->va, INVALIDPTE, 1);
	unlock(&kmapalloc);
}

ulong
kmapregion(ulong pa, ulong n, ulong flag)
{


@@ 383,185 633,3 @@ kmap(Page *pg)
	return kmappa(pg->pa, PTEMAINMEM|PTENOCACHE);
}

KMap*
kmapperm(Page *pg)
{
	/*
	 * Here we know it's a permanent entry and can be cached.
	 */
	return kmappa(pg->pa, PTEMAINMEM);
}

void
kunmap(KMap *k)
{
	k->pa = 0;
	lock(&kmapalloc);
	k->next = kmapalloc.free;
	kmapalloc.free = k;
	putpmeg(k->va, INVALIDPTE);
	unlock(&kmapalloc);
}

void
invalidateu(void)
{
	putpmeg(USERADDR, INVALIDPTE);
}

/*
 * Compile MMU code for this machine, since the MMU can only
 * be addressed from parameterless machine instructions.
 * What's wrong with MMUs you can talk to from C?
 */

/* op3 */
#define	LD	0
#define	ADD	0
#define	OR	2
#define	LDA	16
#define	LDUBA	17
#define	STA	20
#define	STBA	21
#define	JMPL	56
/* op2 */
#define	SETHI	4

void	*compileconst(int, ulong, int);	/* value to/from constant address */
void	*compileldaddr(int, int);	/* value from parameter address */
void	*compilestaddr(int, int);	/* value to parameter address */
void	*compile16(ulong, int);		/* 16 stores of zero */
void	*compile1(ulong, int);		/* 1 stores of zero */

#define	ret()	{*codep++ = (2<<30)|(0<<25)|(JMPL<<19)|(15<<14)|(1<<13)|8;}
#define	nop()	{*codep++ = (0<<30)|(0<<25)|(SETHI<<22)|(0>>10);}

void
compile(void)
{
	putcontext = compileconst(STBA, CONTEXT, 2);
	getenab = compileconst(LDUBA, ENAB, 2);
	putenab = compileconst(STBA, ENAB, 2);
	putpmegspace = compilestaddr(STA, 4);
	putsysspace = compilestaddr(STA, 2);
	getsysspace = compileldaddr(LDA, 2);
	if(conf.ss2){
		flushpg = compile1(BY2PG, 6);
		flushcx = compile16(conf.vaclinesize, 7);
	}else{
		flushpg = compile16(conf.vaclinesize, 0xD);
		flushcx = compile16(conf.vaclinesize, 0xE);
	}
}

void
parameter(int param, int reg)
{
	param += 1;	/* 0th parameter is 1st word on stack */
	param *= 4;
	/* LD #param(R1), Rreg */
	*codep++ = (3<<30)|(reg<<25)|(LD<<19)|(1<<14)|(1<<13)|param;
}

void
constant(ulong c, int reg)
{
	*codep++ = (0<<30)|(reg<<25)|(SETHI<<22)|(c>>10);
	if(c & 0x3FF)
		*codep++ = (2<<30)|(reg<<25)|(OR<<19)|(reg<<14)|(1<<13)|(c&0x3FF);
}

/*
 * void f(int c) { *(word*,asi)addr = c } for stores
 * ulong f(void)  { return *(word*,asi)addr } for loads
 */
void*
compileconst(int op3, ulong addr, int asi)
{
	void *a;

	a = codep;
	constant(addr, 8);	/* MOVW $CONSTANT, R8 */
	ret();			/* JMPL 8(R15), R0 */
	/* in delay slot 	   st or ld R7, (R8+R0, asi)	*/
	*codep++ = (3<<30)|(7<<25)|(op3<<19)|(8<<14)|(asi<<5);
	return a;
}

/*
 * ulong f(ulong addr)  { return *(word*,asi)addr }
 */
void*
compileldaddr(int op3, int asi)
{
	void *a;

	a = codep;
	ret();			/* JMPL 8(R15), R0 */
	/* in delay slot 	   ld (R7+R0, asi), R7	*/
	*codep++ = (3<<30)|(7<<25)|(op3<<19)|(7<<14)|(asi<<5);
	return a;
}

/*
 * void f(ulong addr, int c) { *(word*,asi)addr = c }
 */
void*
compilestaddr(int op3, int asi)
{
	void *a;

	a = codep;
	parameter(1, 8);	/* MOVW (4*1)(FP), R8 */
	ret();			/* JMPL 8(R15), R0 */
	/* in delay slot 	   st R8, (R7+R0, asi)	*/
	*codep++ = (3<<30)|(8<<25)|(op3<<19)|(7<<14)|(asi<<5);
	return a;
}

/*
 * ulong f(ulong addr) { *addr=0; addr+=offset; return addr}
 * offset can be anything
 */
void*
compile1(ulong offset, int asi)
{
	void *a;

	a = codep;
	/* ST R0, (R7+R0, asi)	*/
	*codep++ = (3<<30)|(0<<25)|(STA<<19)|(7<<14)|(asi<<5);
	if(offset < (1<<12)){
		ret();			/* JMPL 8(R15), R0 */
		/* in delay slot ADD $offset, R7 */
		*codep++ = (2<<30)|(7<<25)|(ADD<<19)|(7<<14)|(1<<13)|offset;
	}else{
		constant(offset, 8);
		ret();			/* JMPL 8(R15), R0 */
		/* in delay slot ADD R8, R7 */
		*codep++ = (2<<30)|(7<<25)|(ADD<<19)|(7<<14)|(0<<13)|8;
	}
	return a;
}

/*
 * ulong f(ulong addr) { for(i=0;i<16;i++) {*addr=0; addr+=offset}; return addr}
 * offset must be less than 1<<12
 */
void*
compile16(ulong offset, int asi)
{
	void *a;
	int i;

	a = codep;
	for(i=0; i<16; i++){
		/* ST R0, (R7+R0, asi)	*/
		*codep++ = (3<<30)|(0<<25)|(STA<<19)|(7<<14)|(asi<<5);
		/* ADD $offset, R7 */
		*codep++ = (2<<30)|(7<<25)|(ADD<<19)|(7<<14)|(1<<13)|offset;
	}
	ret();			/* JMPL 8(R15), R0 */
	nop();
	return a;
}