~kris/9p

9hist

1a3a798a350908cbeee28e3fbce55d514ae40240 — David du Colombier 35 years ago 5679305
Plan 9 from Bell Labs 1991-06-07
M gnot/dat.h => gnot/dat.h +0 -34
@@ 1,8 1,6 @@
typedef struct Conf	Conf;
typedef struct FFrame	FFrame;
typedef struct FPsave	FPsave;
typedef struct IOQ	IOQ;
typedef struct KIOQ	KIOQ;
typedef struct KMap	KMap;
typedef struct Label	Label;
typedef struct Lock	Lock;


@@ 120,38 118,6 @@ struct KMap
};
#define	VA(k)	((k)->va)

/*
 *  character based IO (mouse, keyboard, console screen)
 */
#define NQ	4096
struct IOQ
{
	Lock;
	uchar	buf[NQ];
	uchar	*in;
	uchar	*out;
	int	state;
	Rendez	r;
	union{
		void	(*puts)(IOQ*, void*, int);	/* output */
		int	(*putc)(IOQ*, int);		/* input */
	};
	void	*ptr;
};
struct KIOQ
{
	QLock;
	IOQ;
	int	repeat;
	int	c;
	int	count;
};

extern IOQ	lineq;
extern IOQ	printq;
extern IOQ	mouseq;
extern KIOQ	kbdq;

struct Mach
{
	int	machno;			/* physical id of processor */

M gnot/fns.h => gnot/fns.h +0 -23
@@ 57,26 57,3 @@ int	splduart(void);
int	tas(char*);
void	touser(void);
#define	waserror()	(u->nerrlab++, setlabel(&u->errlab[u->nerrlab-1]))

/*
 *  for queues (to go into portfns.h)
 */
int	cangetc(void*);
int	canputc(void*);
int	getc(IOQ*);
int	gets(IOQ*, void*, int);
void	initq(IOQ*);
int	putc(IOQ*, int);
void	puts(IOQ*, void*, int);

/*
 *  for devcons (to go into portfns.h)
 */
void	kbdclock(void);
void	kbdrepeat(int);
int	kbdputc(IOQ*, int);
int	kprint(char*, ...);
int	mouseputc(IOQ*, int);
void	printinit(void);
void	putstr(char*);
void	putstrn(char*, long);

M port/devcons.c => port/devcons.c +269 -310
@@ 8,90 8,35 @@

#include	"devtab.h"

static struct
{
	Lock;
	uchar	buf[4000];
	uchar	*in;
	uchar	*out;
	int	printing;
	int	c;
}printq;

typedef struct IOQ	IOQ;

#define	NQ	4096
struct IOQ{
	union{
		Lock;
		QLock;
	};
	uchar	buf[NQ];
	uchar	*in;
	uchar	*out;
	int	state;
	Rendez	r;
};

IOQ	kbdq;		/* qlock to getc; interrupt putc's */
IOQ	lineq;		/* lock to getc; interrupt putc's */

#define SYSLOGMAGIC	0xdeadbeaf
#define SYSLOG		((Syslog *)(UNCACHED | 0x1B00))
typedef struct Syslog	Syslog;
struct Syslog
{
	ulong	magic;
	int	wrapped;
	char	*next;
	char	buf[2*1024];
};


void
printinit(void)
{

	printq.in = printq.buf;
	printq.out = printq.buf;
	lock(&printq);		/* allocate lock */
	unlock(&printq);
struct {
	IOQ;			/* lock to klogputs */
	QLock;			/* qlock to getc */
}	klogq;

	kbdq.in = kbdq.buf;
	kbdq.out = kbdq.buf;
	lineq.in = lineq.buf;
	lineq.out = lineq.buf;
	qlock(&kbdq);		/* allocate qlock */
	qunlock(&kbdq);
	lock(&lineq);		/* allocate lock */
	unlock(&lineq);
IOQ	lineq;			/* lock to getc; interrupt putc's */
IOQ	printq;
IOQ	mouseq;
KIOQ	kbdq;

	duartinit();
}
Ref	raw;		/* whether kbd i/o is raw (rcons is open) */

/*
 * Put a string on the console.
 * n bytes of s are guaranteed to fit in the buffer and is ready to print.
 * Must be called splhi() and with printq locked.
 *  init the queues and set the output routine
 */
void
puts(char *s, int n)
printinit(void)
{
	if(!printq.printing){
		printq.printing = 1;
		printq.c = *s++;
		n--;
	}
	memmove(printq.in, s, n);
	printq.in += n;
	if(printq.in >= printq.buf+sizeof(printq.buf))
		printq.in = printq.buf;
	initq(&printq);
	initq(&lineq);
	initq(&kbdq);
	kbdq.putc = kbdputc;
	initq(&klogq);
	initq(&mouseq);
	mouseq.putc = mouseputc;
}

/*
 * Print a string on the console.  This is the high level routine
 * with a queue to the interrupt handler.  BUG: There is no check against
 * overflow.
 *   Print a string on the console.  Convert \n to \r\n
 */
void
putstrn(char *str, int n)


@@ 99,39 44,59 @@ putstrn(char *str, int n)
	int s, c, m;
	char *t;

	s = splhi();
	lock(&printq);
	syslog(str, n);
	while(n > 0){
		if(*str == '\n')
			puts("\r", 1);
		m = printq.buf+sizeof(printq.buf) - printq.in;
		if(n < m)
			m = n;
		if(printq.puts && *str=='\n')
			(*printq.puts)(&printq, "\r", 1);
		m = n;
		t = memchr(str+1, '\n', m-1);
		if(t)
			if(t-str < m)
				m = t - str;
		puts(str, m);
		if(printq.puts)
			(*printq.puts)(&printq, str, m);
		screenputs(str, m);
		n -= m;
		str += m;
	}
	unlock(&printq);
	splx(s);
}

int
cangetc(IOQ *q)
/*
 *   Print a string in the kernel log.  Ignore overflow.
 */
void
klogputs(char *str, long n)
{
	return q->in != q->out;
	int s, m;
	uchar *nextin;

	s = splhi();
	lock(&klogq);
	while(n){
		m = &klogq.buf[NQ] - klogq.in;
		if(m > n)
			m = n;
		memmove(klogq.in, str, m);
		n -= m;
		str += m;
		nextin = klogq.in + m;
		if(nextin >= &klogq.buf[NQ])
			klogq.in = klogq.buf;
		else
			klogq.in = nextin;
	}
	unlock(&klogq);
	splx(s);
	wakeup(&klogq.r);
}

int
isbrkc(IOQ *q)
isbrkc(KIOQ *q)
{
	uchar *p;

	for(p=q->out; p!=q->in; ){
		if(raw.ref)
			return 1;
		if(*p==0x04 || *p=='\n')
			return 1;
		p++;


@@ 142,19 107,6 @@ isbrkc(IOQ *q)
}

int
getc(IOQ *q)
{
	int c;

	if(q->in == q->out)
		return -1;
	c = *q->out++;
	if(q->out == q->buf+sizeof(q->buf))
		q->out = q->buf;
	return c;
}

int
sprint(char *s, char *fmt, ...)
{
	return doprint(s, s+PRINTSIZE, fmt, (&fmt+1)) - s;


@@ 171,6 123,17 @@ print(char *fmt, ...)
	return n;
}

int
kprint(char *fmt, ...)
{
	char buf[PRINTSIZE];
	int n;

	n = doprint(buf, buf+sizeof(buf), fmt, (&fmt+1)) - buf;
	klogputs(buf, n);
	return n;
}

void
panic(char *fmt, ...)
{


@@ 212,55 175,50 @@ pprint(char *fmt, ...)
void
prflush(void)
{
	while(printq.printing)
		delay(100);
}

/*
 * Get character to print at interrupt time.
 * Always called splhi from proc 0.
 */
int
conschar(void)
{
	uchar *p;
	int c;

	lock(&printq);
	p = printq.out;
	if(p == printq.in){
		printq.printing = 0;
		c = -1;
	}else{
		c = *p++;
		if(p >= printq.buf+sizeof(printq.buf))
			p = printq.buf;
		printq.out = p;
	}
	unlock(&printq);
	return c;
	while(printq.in != printq.out) ;
}

void
echo(int c)
{
	char ch;
	static int ctrlt;

	/*
	 * ^t hack BUG
	 * ^p hack
	 */
	if(c == 0x10)
		panic("^p");
	if(c == 0x14)
		DEBUG();
	/*
	 * ^t hack BUG
	 */
	if(ctrlt == 2){
		ctrlt = 0;
		switch(c){
		case 0x14:
			break;	/* pass it on */
		case 'm':
			mntdump();
			return;
		case 'p':
			DEBUG();
			return;
		case 'q':
			dumpqueues();
			return;
		case 'r':
			exit();
			break;
		}
	}else if(c == 0x14){
		ctrlt++;
		return;
	}
	ctrlt = 0;
	if(raw.ref)
		return;
	if(c == 0x15)
		putstrn("^U\n", 3);
	if(c == 0x16)
		dumpqueues();
	if(c == 0xe)
		nonettoggle();
	if(c == 0xc)
		lancetoggle();
	else{
		ch = c;
		putstrn(&ch, 1);


@@ 271,31 229,38 @@ echo(int c)
 * Put character into read queue at interrupt time.
 * Always called splhi from proc 0.
 */
void
kbdchar(int c)
int
kbdputc(IOQ *q, int ch)
{
	if(c == 0)	/* NULs cause trouble */
		return;
	if(c == '\r')
		c = '\n';
	echo(c);
	*kbdq.in++ = c;
	echo(ch);
	kbdq.c = ch;
	*kbdq.in++ = ch;
	if(kbdq.in == kbdq.buf+sizeof(kbdq.buf))
		kbdq.in = kbdq.buf;
	if(c=='\n' || c==0x04)
	if(raw.ref || ch=='\n' || ch==0x04)
		wakeup(&kbdq.r);
	return 0;
}

void
printslave(void)
kbdrepeat(int rep)
{
	int c;
	kbdq.repeat = rep;
	kbdq.count = 0;
}

	c = printq.c;
	if(c){
		printq.c = 0;
		duartxmit(c);
void
kbdclock(void)
{
	if(kbdq.repeat == 0)
		return;
	if(kbdq.repeat==1 && ++kbdq.count>HZ){
		kbdq.repeat = 2;
		kbdq.count = 0;
		return;
	}
	if(++kbdq.count&1)
		kbdputc(&kbdq, kbdq.c);
}

int


@@ 311,31 276,37 @@ enum{
	Qdir,
	Qcons,
	Qcputime,
	Qlog,
	Qlights,
	Qnoise,
	Qnull,
/*	Qpanic, /**/
	Qpgrpid,
	Qpid,
	Qppid,
	Qrcons,
	Qtime,
	Quser,
	Qvmereset,
	Qklog,
	Qmsec,
	Qclock,
	Qsysstat,
};

Dirtab consdir[]={
	"cons",		{Qcons},	0,	0600,
	"cputime",	{Qcputime},	72,	0600,
	"log",		{Qlog},		BY2PG-8,	0600,
	"null",		{Qnull},	0,	0600,
/*	"panic",	{Qpanic},	0,	0666, /**/
	"pgrpid",	{Qpgrpid},	12,	0600,
	"pid",		{Qpid},		12,	0600,
	"ppid",		{Qppid},	12,	0600,
	"time",		{Qtime},	12,	0600,
	"user",		{Quser},	0,	0600,
	"vmereset",	{Qvmereset},	0,	0600,
	"sysstat",	{Qsysstat},	0,	0600,
	"cons",		{Qcons},	0,		0600,
	"cputime",	{Qcputime},	6*NUMSIZE,	0600,
	"lights",	{Qlights},	0,		0600,
	"noise",	{Qnoise},	0,		0600,
	"null",		{Qnull},	0,		0600,
	"pgrpid",	{Qpgrpid},	NUMSIZE,	0600,
	"pid",		{Qpid},		NUMSIZE,	0600,
	"ppid",		{Qppid},	NUMSIZE,	0600,
	"rcons",	{Qrcons},	0,		0600,
	"time",		{Qtime},	NUMSIZE,	0600,
	"user",		{Quser},	0,		0600,
	"klog",		{Qklog},	0,		0400,
	"msec",		{Qmsec},	NUMSIZE,	0400,
	"clock",	{Qclock},	2*NUMSIZE,	0400,
	"sysstat",	{Qsysstat},	0,		0600,
};

#define	NCONS	(sizeof consdir/sizeof(Dirtab))


@@ 415,12 386,29 @@ consstat(Chan *c, char *dp)
Chan*
consopen(Chan *c, int omode)
{
	if(c->qid.path==Quser && omode==(OWRITE|OTRUNC)){
		/* truncate? */
		if(strcmp(u->p->pgrp->user, "bootes") == 0)	/* BUG */
			u->p->pgrp->user[0] = 0;
		else
			error(Eperm);
	int ch;

	switch(c->qid.path){
	case Quser:
		if(omode==(OWRITE|OTRUNC)){
			/* truncate? */
			if(strcmp(u->p->pgrp->user, "bootes") == 0)	/* BUG */
				u->p->pgrp->user[0] = 0;
			else
				error(Eperm);
		}
		break;
	case Qrcons:
		if(incref(&raw) == 1){
			lock(&lineq);
			while((ch=getc(&kbdq)) != -1){
				*lineq.in++ = ch;
				if(lineq.in == lineq.buf+sizeof(lineq.buf))
					lineq.in = lineq.buf;
			}
			unlock(&lineq);
		}
		break;
	}
	return devopen(c, omode, consdir, NCONS, devgen);
}


@@ 434,8 422,11 @@ conscreate(Chan *c, char *name, int omode, ulong perm)
void
consclose(Chan *c)
{
	if(c->qid.path==Qrcons && (c->flag&COPEN))
		decref(&raw);
}


long
consread(Chan *c, void *buf, long n, ulong offset)
{


@@ 445,7 436,7 @@ consread(Chan *c, void *buf, long n, ulong offset)
	char *cbuf = buf;
	char *user;
	int userlen;
	char tmp[6*NUMSIZE], xbuf[512];
	char tmp[6*NUMSIZE], xbuf[1024];
	Mach *mp;

	if(n <= 0)


@@ 454,6 445,7 @@ consread(Chan *c, void *buf, long n, ulong offset)
	case Qdir:
		return devdirread(c, buf, n, consdir, NCONS, devgen);

	case Qrcons:
	case Qcons:
		qlock(&kbdq);
		if(waserror()){


@@ 461,9 453,14 @@ consread(Chan *c, void *buf, long n, ulong offset)
			nexterror();
		}
		while(!cangetc(&lineq)){
			sleep(&kbdq.r, (int(*)(void*))isbrkc, &kbdq);
			sleep(&kbdq.r, isbrkc, &kbdq);
			do{
				lock(&lineq);
				ch = getc(&kbdq);
				if(raw.ref){
					unlock(&lineq);
					goto Default;
				}
				switch(ch){
				case '\b':
					if(lineq.in != lineq.out){


@@ 475,17 472,21 @@ consread(Chan *c, void *buf, long n, ulong offset)
				case 0x15:
					lineq.in = lineq.out;
					break;
				Default:
				default:
					*lineq.in++ = ch;
					if(lineq.in == lineq.buf+sizeof(lineq.buf))
					lineq.in = lineq.buf;
					*lineq.in = ch;
					if(lineq.in >= lineq.buf+sizeof(lineq.buf)-1)
						lineq.in = lineq.buf;
					else
						lineq.in++;
				}
			}while(ch!='\n' && ch!=0x04);
				unlock(&lineq);
			}while(raw.ref==0 && ch!='\n' && ch!=0x04);
		}
		i = 0;
		while(n>0){
		while(n > 0){
			ch = getc(&lineq);
			if(ch == 0x04 || ch == -1)
			if(ch==-1 || (raw.ref==0 && ch==0x04))
				break;
			i++;
			*cbuf++ = ch;


@@ 523,11 524,42 @@ consread(Chan *c, void *buf, long n, ulong offset)
	case Qtime:
		return readnum(offset, buf, n, boottime+TK2SEC(MACHP(0)->ticks), 12);

	case Qclock:
		k = offset;
		if(k >= 2*NUMSIZE)
			return 0;
		if(k+n > 2*NUMSIZE)
			n = 2*NUMSIZE - k;
		readnum(0, tmp, NUMSIZE, MACHP(0)->ticks, NUMSIZE);
		readnum(0, tmp+NUMSIZE, NUMSIZE, HZ, NUMSIZE);
		memmove(buf, tmp+k, n);
		return n;

	case Quser:
		return readstr(offset, buf, n, u->p->pgrp->user);

	case Qlog:
		return readlog(offset, buf, n);
	case Qnull:
		return 0;

	case Qklog:
		qlock(&klogq);
		if(waserror()){
			qunlock(&klogq);
			nexterror();
		}
		while(!cangetc(&klogq))
			sleep(&klogq.r, cangetc, &klogq);
		for(i=0; i<n; i++){
			if((ch=getc(&klogq)) == -1)
				break;
			*cbuf++ = ch;
		}
		poperror();
		qunlock(&klogq);
		return i;

	case Qmsec:
		return readnum(offset, buf, n, TK2MS(MACHP(0)->ticks), NUMSIZE);

	case Qsysstat:
		j = 0;


@@ 536,13 568,10 @@ consread(Chan *c, void *buf, long n, ulong offset)
				mp = MACHP(id);
				j += sprint(&xbuf[j], "%d %d %d %d %d %d %d %d\n",
					id, mp->cs, mp->intr, mp->syscall, mp->pfault,
					    mp->tlbfault, mp->tlbpurge, mp->spinlock);
					    mp->tlbfault, mp->tlbpurge, m->spinlock);
			}
		}

		return readstr(offset, buf, n, xbuf);
	case Qnull:
		return 0;

	default:
		panic("consread %lux\n", c->qid);


@@ 550,17 579,60 @@ consread(Chan *c, void *buf, long n, ulong offset)
	}
}

void
conslights(char *a, int n)
{
	int l;
	char line[128];
	char *lp;
	int c;

	lp = line;
	while(n--){
		*lp++ = c = *a++;
		if(c=='\n' || n==0 || lp==&line[sizeof(line)-1])
			break;
	}
	*lp = 0;
	lights(strtoul(line, 0, 0));
}

void
consnoise(char *a, int n)
{
	int freq;
	int duration;
	char line[128];
	char *lp;
	int c;

	lp = line;
	while(n--){
		*lp++ = c = *a++;
		if(c=='\n' || n==0 || lp==&line[sizeof(line)-1]){
			*lp = 0;
			freq = strtoul(line, &lp, 0);
			while(*lp==' ' || *lp=='\t')
				lp++;
			duration = strtoul(lp, &lp, 0);
			buzz(freq, duration);
			lp = line;
		}
	}
}

long
conswrite(Chan *c, void *va, long n, ulong offset)
{
	char cbuf[64];
	char buf[256];
	long l, m;
	int id;
	char *a = va;
	Mach *mp;
	int id;

	switch(c->qid.path){
	case Qrcons:
	case Qcons:
		/*
		 * Damn. Can't page fault in putstrn, so copy the data locally.


@@ 607,11 679,14 @@ conswrite(Chan *c, void *va, long n, ulong offset)
	case Qnull:
		break;

	case Qvmereset:
		if(strcmp(u->p->pgrp->user, "bootes") != 0)
			error(Eperm);
		vmereset();
	case Qnoise:
		consnoise(a, n);
		break;

	case Qlights:
		conslights(a, n);
		break;

	case Qsysstat:
		for(id = 0; id < 32; id++) {
			if(active.machs & (1<<id)) {


@@ 625,8 700,8 @@ conswrite(Chan *c, void *va, long n, ulong offset)
				mp->spinlock = 0;
			}
		}

		break;

	default:
		error(Egreg);
	}


@@ 644,119 719,3 @@ conswstat(Chan *c, char *dp)
{
	error(Eperm);
}

/*
 *  kernel based system log, passed between crashes
 */
void
sysloginit(void)
{
	Syslog *s;
	int i;

	s = SYSLOG;
	if(s->magic!=SYSLOGMAGIC || s->next>=&s->buf[sizeof(s->buf)] || s->next<s->buf){
		s->wrapped = 0;
		s->next = s->buf;
		s->magic = SYSLOGMAGIC;
	}
}

void
syslog(char *p, int n)
{
	Syslog *s;
	char *end;
	int m;

	sysloginit();
	s = SYSLOG;
	end = &s->buf[sizeof(s->buf)];
	while(n){
		if(s->next + n > end)
			m = end - s->next;
		else
			m = n;
		memmove(s->next, p, m);
		s->next += m;
		p += m;
		n -= m;
		if(s->next >= end){
			s->wrapped = 1;
			s->next = s->buf;
		}
	}
	wbflush();
}

long
readlog(ulong off, char *buf, ulong len)
{
	Syslog *s;
	char *end;
	char *start;
	int n, m;
	char *p;

	n = len;
	p = buf;

	sysloginit();
	s = SYSLOG;
	end = &s->buf[sizeof(s->buf)];

	if(s->wrapped){
		start = s->next;
		m = sizeof(s->buf);
	} else {
		start = s->buf;
		m = s->next - start;
	}

	if(off > m)
		return 0;
	if(off + n > m)
		n = m - off;
	start += off;
	if(start > end)
		start -= sizeof(s->buf);

	while(n > 0){
		if(start + n > end)
			m = end - start;
		else
			m = n;
		memmove(p, start, m);
		start += m;
		p += m;
		n -= m;
		if(start >= end)
			start = s->buf;
	}

	return p-buf;
}

/*
 *  Read and write every byte of the log.  This seems to ensure that
 *  on reboot, the bytes will really be in memory.  I don't understand -- presotto
 */
void
flushsyslog(void)
{
	Syslog *s;
	char *p, *end;
	int x;

	s = SYSLOG;
	end = &s->buf[sizeof(s->buf)];

	x = splhi();
	lock(&printq);
	for(p = s->buf; p < end; p++)
		*p = *p;
	unlock(&printq);
	splx(x);

	wbflush();
}

M port/devscc.c => port/devscc.c +0 -3
@@ 284,7 284,6 @@ sccintr0(SCC *sp, uchar x)
	IOQ *cq;

	if(x & ExtPendB){
		/*kprint("scc %d: %2.2ux\n", sp-scc, *sp->ptr);*/
		sccwrreg(sp, 0, ResExtPend);
	}
	if(x & RxPendB){


@@ 305,10 304,8 @@ sccintr0(SCC *sp, uchar x)
		cq = sp->oq;
		lock(cq);
		ch = getc(cq);
		/*kprint("<%2.2ux>", ch);*/
		onepointseven();
		if(ch < 0){
			/*kprint("<done>", ch);*/
			sccwrreg(sp, 0, ResTxPend);
			sp->printing = 0;
			wakeup(&cq->r);

M port/fault.c => port/fault.c +22 -10
@@ 9,7 9,7 @@
int
fault(ulong addr, int read)
{
	ulong mmuvirt, mmuphys = 0, n;
	ulong mmuvirt, mmuphys, uncache = 0, n;
	Seg *s;
	PTE *opte, *pte, *npte;
	Orig *o;


@@ 67,7 67,11 @@ fault(ulong addr, int read)
			/* Make a new bss or lock segment page */
			if(s - u->p->seg == LSEG) {
				pte->page = lkpage(o, addr);
				mmuphys = PTEUNCACHE;
				if(pte->page == 0) {
					unlock(o);
					return -1;
				}
				uncache = PTEUNCACHED;
			}
			else
				pte->page = newpage(0, o, addr);


@@ 115,9 119,12 @@ fault(ulong addr, int read)
	/*
	 * Copy on reference (conf.copymode==1) or write (conf.copymode==0)
	 */
	if((o->flag & OWRPERM) && (conf.copymode || !read)
	if((o->flag & (OWRPERM|OSHARED))==OWRPERM && (conf.copymode || !read)
	&& ((head && ((o->flag&OPURE) || o->nproc>1))
	    || (!head && pte->page->ref>1))){

		if(!(o->flag&OISMEM))
			panic("copy on ref/wr to non memory");
		touched = 1;
		/*
		 * Look for the easy way out: are we the last non-modified?


@@ 163,6 170,7 @@ fault(ulong addr, int read)
		 * increment on the mod list which must be removed
		 */
		if(zeroed){
			pg->ref--;
			o->npage--;
			opte->page = 0;
		}else{		/* copy page */


@@ 174,25 182,27 @@ fault(ulong addr, int read)
			kunmap(k1);
			if(pg->ref <= 1)
				panic("pg->ref <= 1");
			pg->ref--;
		}
		pg->ref--;

    easy:
		mmuphys |= PTEWRITE;
		mmuphys = PTEWRITE;
	}else{
		mmuphys |= PTERONLY;
		mmuphys = PTERONLY;
		if(o->flag & OWRPERM)
			if(o->flag & OPURE){
				if(!head && pte->page->ref==1)
					mmuphys |= PTEWRITE;
					mmuphys = PTEWRITE;
			}else
				if((head && o->nproc==1)
	  			  || (!head && pte->page->ref==1))
					mmuphys |= PTEWRITE;
					mmuphys = PTEWRITE;
	}
	mmuvirt = addr;
	mmuphys |= PPN(pte->page->pa) | PTEVALID;
	usepage(pte->page, 1);
	mmuphys |= PPN(pte->page->pa) | PTEVALID | uncache;
	if(o->flag&OISMEM)
		usepage(pte->page, 1);

	if(pte->page->va != addr)
		panic("wrong addr in tail %lux %lux", pte->page->va, addr);
	if(pte->proc && pte->proc != u->p){


@@ 200,9 210,11 @@ fault(ulong addr, int read)
		print("u->p %lux pte->proc %lux\n", u->p, pte->proc);
		panic("addr %lux seg %d wrong proc in tail", addr, s-u->p->seg);
	}

	unlock(o);
	if(touched == 0)
		m->tlbfault++;

	putmmu(mmuvirt, mmuphys);
	return 0;
}

M port/page.c => port/page.c +4 -4
@@ 334,6 334,7 @@ loop:
		o->nproc = 1;
		o->npage = 0;
		o->chan = c;
		o->freepg = unusepage;
		if(c){
			o->type = c->type;
			o->qid = c->qid;


@@ 428,8 429,6 @@ forkmod(Seg *old, Seg *new, Proc *p)
	ppte = 0;
	pte = old->mod;
	while(pte){
if(pte->page==0) panic("forkmod zero page");
if(pte->proc != u->p) panic("forkmod wrong page");
		npte = newmod(o);
		npte->proc = p;
		npte->page = pte->page;


@@ 579,15 578,16 @@ freepage(Orig *o, int dolock)
	int i;

	pte = o->pte;
	for(i=0; i<o->npte; i++,pte++)
	for(i=0; i<o->npte; i++,pte++) {
		if(pg = pte->page){	/* assign = */
			if(pg->ref == 1){
				unusepage(pg, dolock);
				(*o->freepg)(pg, dolock);
				pte->page = 0;
				pg->o = 0;
			}
			pg->ref--;
		}
	}
	o->npage = 0;
}


M port/portdat.h => port/portdat.h +37 -1
@@ 9,6 9,8 @@ typedef struct Env	Env;
typedef struct Envp	Envp;
typedef struct Envval	Envval;
typedef struct Etherpkt	Etherpkt;
typedef struct IOQ	IOQ;
typedef struct KIOQ	KIOQ;
typedef struct List	List;
typedef struct Mount	Mount;
typedef struct Mtab	Mtab;


@@ 199,6 201,37 @@ struct Etherpkt {
#define	ETHERMAXTU	1514	/* maximum transmit size */
#define ETHERHDRSIZE	14	/* size of an ethernet header */

/*
 *  character based IO (mouse, keyboard, console screen)
 */
#define NQ	4096
struct IOQ
{
	Lock;
	uchar	buf[NQ];
	uchar	*in;
	uchar	*out;
	int	state;
	Rendez	r;
	union{
		void	(*puts)(IOQ*, void*, int);	/* output */
		int	(*putc)(IOQ*, int);		/* input */
	};
	void	*ptr;
};
struct KIOQ
{
	QLock;
	IOQ;
	int	repeat;
	int	c;
	int	count;
};
extern IOQ	lineq;
extern IOQ	printq;
extern IOQ	mouseq;
extern KIOQ	kbdq;

struct Mount
{
	Ref;				/* also used as a lock when playing lists */


@@ 229,6 262,8 @@ struct Note
#define	OWRPERM	0x01	/* write permission */
#define	OPURE	0x02	/* original data mustn't be written */
#define	OCACHED	0x04	/* cached; don't discard on exit */
#define OISMEM	0x08	/* origin contains real memory */
#define OSHARED	0x10	/* origin does not copy on ref/wr */

struct Orig
{


@@ 247,6 282,8 @@ struct Orig
	Qid	mqid;
	ulong	minca;			/* base of region in chan */
	ulong	maxca;			/* end of region in chan */
	void	(*freepg)(Page*, int);	/* how to free pages for this origin */
	Page	(*allocpg)(int, Orig*, ulong);
};

struct Page


@@ 298,7 335,6 @@ struct Seg
	ulong	maxva;			/* va of last pte */
	PTE	*mod;			/* list of modified pte's */
	ulong	endseg;			/* segments end */
	ulong	pad[2]; /**/
};

/*

M port/portfns.h => port/portfns.h +20 -1
@@ 10,8 10,10 @@ int	blen(Block *);
int	bround(Block *, int);
void	buzz(int, int);
void	cancel(Alarm*);
int	cangetc(void*);
int	canlock(Lock*);
int	canqlock(QLock*);
int	canputc(void*);
void	chandevinit(void);
void	chandevreset(void);
void	chaninit(void);


@@ 65,19 67,27 @@ void	freepage(Orig*, int);
void	freepte(Orig*);
void	freesegs(int);
Block*	getb(Blist*);
int	getc(IOQ*);
int	getfields(char*, char**, int, char);
Block*	getq(Queue*);
int	gets(IOQ*, void*, int);
void	gotolabel(Label*);
void	growpte(Orig*, ulong);
void*	ialloc(ulong, int);
long	ibrk(ulong, int);
int	incref(Ref*);
void	initq(IOQ*);
void	insert(List**, List*, List*);
void	invalidateu(void);
void	isdir(Chan*);
void	kbdclock(void);
void	kbdrepeat(int);
int	kbdputc(IOQ*, int);
int	kprint(char*, ...);
void	kproc(char*, void(*)(void*), void*);
void	lights(int);
Page*	lkpage(Orig*, ulong);
void	lkpgfree(Page*, int);
void	lock(Lock*);
void	lockinit(void);
Orig*	lookorig(ulong, ulong, int, Chan*);


@@ 86,6 96,7 @@ void	mapstack(Proc*);
void	mklockseg(Seg*);
void	mmurelease(Proc*);
int	mount(Chan*, Chan*, int);
int	mouseputc(IOQ*, int);
Chan*	namec(char*, int, int, ulong);
Alarm*	newalarm(void);
Chan*	newchan(void);


@@ 112,7 123,11 @@ Pgrp*	pgrptab(int);
#define	poperror()	u->nerrlab--
int	postnote(Proc*, int, char*, int);
int	pprint(char*, ...);
void	printslave(void);
void	printinit(void);
int	putc(IOQ*, int);
void	putstr(char*);
void	putstrn(char*, long);
void	puts(IOQ*, void*, int);
ulong 	procalarm(ulong);
void	procinit0(void);
Proc*	proctab(int);


@@ 131,6 146,10 @@ void	resched(char*);
int	return0(void*);
Proc*	runproc(void);
void	savefpregs(FPsave*);
void	sccintr(void);
void	sccputs(IOQ*, char*, int);
void	sccsetup(void*);
void	sccspecial(int, IOQ*, IOQ*, int);
void	schedinit(void);
void	sched(void);
long	seconds(void);

M port/sysproc.c => port/sysproc.c +7 -7
@@ 66,7 66,7 @@ sysfork(ulong *arg)
	usp = s->minva & (BY2PG-1);	/* just low bits */
	s->maxva = USTKTOP;
	n = (s->maxva-s->minva)>>PGSHIFT;
	s->o = neworig(s->minva, n, OWRPERM, 0);
	s->o = neworig(s->minva, n, OWRPERM|OISMEM, 0);
	lock(s->o);
	/*
	 * Only part of last stack page


@@ 250,7 250,7 @@ sysexec(ulong *arg)

	s = &p->seg[ESEG];
	s->proc = p;
	s->o = neworig(TSTKTOP-(spage<<PGSHIFT), spage, OWRPERM, 0);
	s->o = neworig(TSTKTOP-(spage<<PGSHIFT), spage, OWRPERM|OISMEM, 0);
	s->minva = s->o->va;
	s->maxva = TSTKTOP;



@@ 295,9 295,9 @@ sysexec(ulong *arg)
	 */
	s = &p->seg[TSEG];
	s->proc = p;
	o = lookorig(UTZERO, (t-UTZERO)>>PGSHIFT, OCACHED, tc);
	o = lookorig(UTZERO, (t-UTZERO)>>PGSHIFT, OCACHED|OISMEM, tc);
	if(o == 0){
		o = neworig(UTZERO, (t-UTZERO)>>PGSHIFT, OCACHED, tc);
		o = neworig(UTZERO, (t-UTZERO)>>PGSHIFT, OCACHED|OISMEM, tc);
		o->minca = 0;
		o->maxca = sizeof(Exec)+exec.text;
	}


@@ 311,9 311,9 @@ sysexec(ulong *arg)
	 */
	s = &p->seg[DSEG];
	s->proc = p;
	o = lookorig(t, (d-t)>>PGSHIFT, OWRPERM|OPURE|OCACHED, tc);
	o = lookorig(t, (d-t)>>PGSHIFT, OWRPERM|OPURE|OCACHED|OISMEM, tc);
	if(o == 0){
		o = neworig(t, (d-t)>>PGSHIFT, OWRPERM|OPURE|OCACHED, tc);
		o = neworig(t, (d-t)>>PGSHIFT, OWRPERM|OPURE|OCACHED|OISMEM, tc);
		o->minca = p->seg[TSEG].o->maxca;
		o->maxca = o->minca + exec.data;
	}


@@ 327,7 327,7 @@ sysexec(ulong *arg)
	 */
	s = &p->seg[BSEG];
	s->proc = p;
	o = neworig(d, (b-d)>>PGSHIFT, OWRPERM, 0);
	o = neworig(d, (b-d)>>PGSHIFT, OWRPERM|OISMEM, 0);
	o->minca = 0;
	o->maxca = 0;
	s->o = o;

M power/clock.c => power/clock.c +1 -1
@@ 101,8 101,8 @@ clock(Ureg *ur)
						p->time[p->insyscall]++;
				}
			}
			printslave();
		}
		duartslave();
		if(active.exiting && active.machs&(1<<m->machno)){
			print("someone's exiting\n");
			exit();

A power/devduart.c => power/devduart.c +836 -0
@@ 0,0 1,836 @@
#include	"u.h"
#include	"lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"io.h"

int	duartacr;
int	duartimr;

void	duartputs(IOQ*, char*, int);
void	iprint(char*, ...);

#define	PAD	15	/* registers are well-spaced */

/*
 * Register set for half the duart.  There are really two sets.
 */
struct Duart{
	uchar	mr1_2,		/* Mode Register Channels 1 & 2 */
		pad0[PAD];
	uchar	sr_csr,		/* Status Register/Clock Select Register */
		pad1[PAD];
	uchar	cmnd,		/* Command Register */
		pad2[PAD];
	uchar	data,		/* RX Holding / TX Holding Register */
		pad3[PAD];
	uchar	ipc_acr,	/* Input Port Change/Aux. Control Register */
		pad4[PAD];
	uchar	is_imr,		/* Interrupt Status/Interrupt Mask Register */
		pad5[PAD];
	uchar	ctur,		/* Counter/Timer Upper Register */
		pad6[PAD];
	uchar	ctlr,		/* Counter/Timer Lower Register */
		pad7[PAD];
};
#define	ppcr	is_imr		/* in the second register set */

#define DBD75		0
#define DBD110		1
#define DBD38400	2
#define DBD150		3
#define DBD300		4
#define DBD600		5
#define DBD1200		6
#define DBD2000		7
#define DBD2400		8
#define DBD4800		9
#define DBD1800		10
#define DBD9600		11
#define DBD19200	12

enum{
	CHAR_ERR	=0x00,	/* MR1x - Mode Register 1 */
	EVEN_PAR	=0x00,
	ODD_PAR		=0x04,
	NO_PAR		=0x10,
	CBITS8		=0x03,
	CBITS7		=0x02,
	CBITS6		=0x01,
	CBITS5		=0x00,
	NORM_OP		=0x00,	/* MR2x - Mode Register 2 */
	TWOSTOPB	=0x0F,
	ONESTOPB	=0x07,
	ENB_RX		=0x01,	/* CRx - Command Register */
	DIS_RX		=0x02,
	ENB_TX		=0x04,
	DIS_TX		=0x08,
	RESET_MR 	=0x10,
	RESET_RCV  	=0x20,
	RESET_TRANS  	=0x30,
	RESET_ERR  	=0x40,
	RESET_BCH	=0x50,
	STRT_BRK	=0x60,
	STOP_BRK	=0x70,
	RCV_RDY		=0x01,	/* SRx - Channel Status Register */
	FIFOFULL	=0x02,
	XMT_RDY		=0x04,
	XMT_EMT		=0x08,
	OVR_ERR		=0x10,
	PAR_ERR		=0x20,
	FRM_ERR		=0x40,
	RCVD_BRK	=0x80,
	IM_IPC		=0x80,	/* IMRx/ISRx - Interrupt Mask/Interrupt Status */
	IM_DBB		=0x40,
	IM_RRDYB	=0x20,
	IM_XRDYB	=0x10,
	IM_CRDY		=0x08,
	IM_DBA		=0x04,
	IM_RRDYA	=0x02,
	IM_XRDYA	=0x01,
	BD38400		=0xCC|0x0000,
	BD19200		=0xCC|0x0100,
	BD9600		=0xBB|0x0000,
	BD4800		=0x99|0x0000,
	BD2400		=0x88|0x0000,
	BD1200		=0x66|0x0000,
	BD300		=0x44|0x0000,

	Maxport		=8,
};

/*
 *  requests to perform on a duart
 */
enum {
	Dnone=	0,
	Dbaud,
	Dbreak,
	Ddtr,
	Dprint,
	Dena,
	Dstate,
};

/*
 *  software info for a serial duart interface
 */
typedef struct Duartport	Duartport;
struct Duartport
{
	QLock;
	int	printing;	/* true if printing */
	Duart	*duart;		/* device */
	int	inited;
	int	c;		/* character to restart output */
	int	op;		/* operation requested */
	int	val;		/* value of operation */
	Rendez	opr;		/* waiot here for op to complete */

	/* console interface */
	int	nostream;	/* can't use the stream interface */
	IOQ	*iq;		/* input character queue */
	IOQ	*oq;		/* output character queue */

	/* stream interface */
	Queue	*wq;		/* write queue */
	Rendez	r;		/* kproc waiting for input */
	Alarm	*a;		/* alarm for waking the kernel process */
	int	delay;		/* between character input and waking kproc */
 	int	kstarted;	/* kproc started */
	uchar	delim[256/8];	/* characters that act as delimiters */
};
Duartport	duartport[Maxport];	/* max possible */

/*
 *  configure a duart port, default is 9600 baud, 8 bits/char, 1 stop bit,
 *  no parity
 */
void
duartsetup(Duartport *dp)
{
	Duart *duart;

	duart = dp->duart;

	duart->cmnd = RESET_RCV|DIS_TX|DIS_RX;
	duart->cmnd = RESET_TRANS;
	duart->cmnd = RESET_ERR;
	duart->cmnd = STOP_BRK;

	duart->ipc_acr = 0x80;		/* baud-rate set 2 */
	duart->cmnd = RESET_MR;
	duart->mr1_2 = NO_PAR|CBITS8;
	duart->mr1_2 = ONESTOPB;
	duart->sr_csr = (DBD9600<<4)|DBD9600;
	duart->is_imr = IM_RRDYA|IM_XRDYA;
	duart->cmnd = ENB_TX|ENB_RX;
}

/*
 *  init the duart on the current processor
 */
void
duartinit(void)
{
	Duartport *dp;

	dp = &duartport[2*m->machno];
	if(dp->inited)
		return;
	dp->inited = 1;

	dp->duart = DUARTREG;
	duartsetup(dp);
	dp++;
	dp->duart = DUARTREG+1;
	duartsetup(dp);
}

/*
 *  enable a duart port
 */
void
duartenable(Duartport *dp)
{
	dp->duart->cmnd = ENB_TX|ENB_RX;
}

void
duartenable0(void)
{
	DUARTREG->cmnd = ENB_TX|ENB_RX;
}

void
duartbaud(Duartport *dp, int b)
{
	int x = 0;

	switch(b){
	case 38400:
		x = BD38400;
		break;
	case 19200:
		x = BD19200;
		break;
	case 9600:
		x = BD9600;
		break;
	case 4800:
		x = BD4800;
		break;
	case 2400:
		x = BD2400;
		break;
	case 1200:
		x = BD1200;
		break;
	case 300:
		x = BD300;
		break;
	default:
		errors("bad baud rate");
	}
	if(x & 0x0100)
		dp->duart->ipc_acr = duartacr |= 0x80;
	else
		dp->duart->ipc_acr = duartacr &= ~0x80;
	dp->duart->sr_csr = x;
}

void
duartdtr(Duartport *dp, int val)
{
	if (val)
		dp->duart->ctlr=0x01;
	else
		dp->duart->ctur=0x01;
}

void
duartbreak(Duartport *dp, int val)
{
	Duart *duart;

	duart = dp->duart;
	if (val){
		duart->is_imr = duartimr &= ~IM_XRDYB;
		duart->cmnd = STRT_BRK|ENB_TX;
	} else {
		duart->cmnd = STOP_BRK|ENB_TX;
		duart->is_imr = duartimr |= IM_XRDYB;
	}
}

/*
 *  do anything requested for this CPU's duarts
 */
void
duartslave0(Duartport *dp)
{
	switch(dp->op){
	case Ddtr:
		duartbaud(dp, dp->val);
		break;
	case Dbaud:
		duartdtr(dp, dp->val);
		break;
	case Dbreak:
		duartbreak(dp, dp->val);
		break;
	case Dprint:
		dp->duart->cmnd = ENB_TX;
		dp->duart->data = dp->val;
		break;
	case Dena:
		duartenable(dp);
		break;
	case Dstate:
		dp->val = dp->duart->is_imr;
		break;
	}
	dp->op = Dnone;
	wakeup(&dp->opr);
}
void
duartslave(void)
{
	Duartport *dp;

	dp = &duartport[2*m->machno];
	if(dp->op != Dnone)
		duartslave0(dp);
	dp++;
	if(dp->op != Dnone)
		duartslave0(dp);
}

duartrintr(Duartport *dp)
{
	Duart *duart;
	IOQ *cq;
	int status;
	char ch;

	duart = dp->duart;
	status = duart->sr_csr;
	ch = duart->data;
	if(status & (FRM_ERR|OVR_ERR|PAR_ERR))
		duart->cmnd = RESET_ERR;

	cq = dp->iq;
	if(cq->putc)
		(*cq->putc)(cq, ch);
	else {
		putc(cq, ch);
		if(dp->delim[ch/8] & (1<<(ch&7)) )
			wakeup(&cq->r);
	}
}

duartxintr(Duartport *dp)
{
	Duart *duart;
	IOQ *cq;
	char ch;

	cq = dp->oq;
	lock(cq);
	ch = getc(cq);
	duart = dp->duart;
	if(ch < 0){
		dp->printing = 0;
		wakeup(&cq->r);
		duart->cmnd = DIS_TX;
	} else
		duart->data = ch;
	unlock(cq);
}

void
duartintr(void)
{
	int cause, status, c;
	Duart *duart;
	Duartport *dp;

	dp = &duartport[2*m->machno];
	duart = dp->duart;
	cause = duart->is_imr;
	/*
	 * I can guess your interrupt.
	 */
	/*
	 * Is it 1?
	 */
	if(cause & IM_RRDYA)
		duartrintr(dp);
	/*
	 * Is it 2?
	 */
	if(cause & IM_XRDYA)
		duartxintr(dp);
	/*
	 * Is it 3?
	 */
	if(cause & IM_RRDYB)
		duartrintr(dp+1);
	/*
	 * Is it 4?
	 */
	if(cause & IM_XRDYB)
		duartxintr(dp+1);
}

/*
 *  processor 0 only
 */
int
duartrawputc(int c)
{
	Duart *duart;
	int i;

	duart = DUARTREG;
	if(c == '\n')
		duartrawputc('\r');
	duart->cmnd = ENB_TX;
	i = 0;
	while((duart->sr_csr&XMT_RDY) == 0)
		if(++i >= 1000000){
			duartsetup(&duartport[0]);
			for(i=0; i<100000; i++)
				;
			break;
		}
	duart->data = c;
	if(c == '\n')
		for(i=0; i<100000; i++)
			;
	return c;
}
void
duartrawputs(char *s)
{
	int i;
	while(*s){
		duartrawputc(*s++);
	}
	for(i=0; i < 1000000; i++)
		;
}
void
iprint(char *fmt, ...)
{
	char buf[1024];
	long *arg;

	arg = (long*)(&fmt+1);
	sprint(buf, fmt, *arg, *(arg+1), *(arg+2), *(arg+3));
	duartrawputs(buf);
}

/*
 *  Queue n characters for output; if queue is full, we lose characters.
 *  Get the output going if it isn't already.
 */
void
duartputs(IOQ *cq, char *s, int n)
{
	int ch, x;
	Duartport *dp;
	Duart *duart;

	x = splhi();
	lock(cq);
	puts(cq, s, n);
	dp = cq->ptr;
	if(dp->printing == 0){
		ch = getc(cq);
		if(ch >= 0){
			dp->printing = 1;
			dp->val = ch;
			dp->op = Dprint;
		}
	}
	unlock(cq);
	splx(x);
}

/*
 *  set up an duart port as something other than a stream
 */
void
duartspecial(int port, IOQ *oq, IOQ *iq, int baud)
{
	Duartport *dp = &duartport[port];

	dp->nostream = 1;
	if(oq){
		dp->oq = oq;
		dp->oq->puts = duartputs;
		dp->oq->ptr = dp;
	}
	if(iq){
		dp->iq = iq;
		dp->iq->ptr = dp;
	}
	duartenable(dp);
	duartbaud(dp, baud);
}

static void	duarttimer(Alarm*);
static int	duartputc(IOQ *, int);
static void	duartstopen(Queue*, Stream*);
static void	duartstclose(Queue*);
static void	duartoput(Queue*, Block*);
static void	duartkproc(void *);
Qinfo duartinfo =
{
	nullput,
	duartoput,
	duartstopen,
	duartstclose,
	"duart"
};

/*
 *  wakeup the helper process to do input
 */
static void
duarttimer(Alarm *a)
{
	Duartport *dp = a->arg;

	cancel(a);
	dp->a = 0;
	wakeup(&dp->iq->r);
}

static int
duartputc(IOQ *cq, int ch)
{
	Duartport *dp = cq->ptr; int r;

	r = putc(cq, ch);

	/*
	 *  pass upstream within dp->delay milliseconds
	 */
	if(dp->a==0){
		if(dp->delay == 0)
			wakeup(&cq->r);
		else
			dp->a = alarm(dp->delay, duarttimer, dp);
	}
	return r;
}

static void
duartstopen(Queue *q, Stream *s)
{
	Duartport *dp;
	char name[NAMELEN];

	dp = &duartport[s->id];

	qlock(dp);
	dp->wq = WR(q);
	WR(q)->ptr = dp;
	RD(q)->ptr = dp;
	dp->delay = 64;
	dp->iq->putc = duartputc;
	qunlock(dp);

	/* start with all characters as delimiters */
	memset(dp->delim, 1, sizeof(dp->delim));
	
	if(dp->kstarted == 0){
		dp->kstarted = 1;
		sprint(name, "duart%d", s->id);
		kproc(name, duartkproc, dp);
	}
}

static void
duartstclose(Queue *q)
{
	Duartport *dp = q->ptr;

	qlock(dp);
	dp->wq = 0;
	dp->iq->putc = 0;
	WR(q)->ptr = 0;
	RD(q)->ptr = 0;
	qunlock(dp);
}

static int
opdone(void *x)
{
	Duartport *dp = x;

	return dp->op == Dnone;
}
static void
duartoput(Queue *q, Block *bp)
{
	Duartport *dp = q->ptr;
	IOQ *cq;
	int n, m;

	if(dp == 0){
		freeb(bp);
		return;
	}
	cq = dp->oq;
	if(waserror()){
		freeb(bp);
		nexterror();
	}
	if(bp->type == M_CTL){
		qlock(dp);
		while (cangetc(cq))	/* let output drain */
			sleep(&cq->r, cangetc, cq);
		n = strtoul((char *)(bp->rptr+1), 0, 0);
		switch(*bp->rptr){
		case 'B':
		case 'b':
			dp->val = n;
			dp->op = Dbaud;
			sleep(&dp->opr, opdone, dp);
			break;
		case 'D':
		case 'd':
			dp->val = n;
			dp->op = Ddtr;
			sleep(&dp->opr, opdone, dp);
			break;
		case 'K':
		case 'k':
			dp->val = 1;
			dp->op = Dbreak;
			sleep(&dp->opr, opdone, dp);
			tsleep(&dp->opr, return0, 0, n);
			dp->val = 0;
			dp->op = Dbreak;
			sleep(&dp->opr, opdone, dp);
			break;
		case 'R':
		case 'r':
			/* can't control? */
			break;
		case 'W':
		case 'w':
			if(n>=0 && n<1000)
				dp->delay = n;
			break;
		}
		qunlock(dp);
	}else while((m = BLEN(bp)) > 0){
		while ((n = canputc(cq)) == 0){
			kprint(" duartoput: sleeping\n");
			sleep(&cq->r, canputc, cq);
		}
		if(n > m)
			n = m;
		(*cq->puts)(cq, bp->rptr, n);
		bp->rptr += n;
	}
	freeb(bp);
	poperror();
}

/*
 *  process to send bytes upstream for a port
 */
static void
duartkproc(void *a)
{
	Duartport *dp = a;
	IOQ *cq = dp->iq;
	Block *bp;
	int n;

loop:
	while ((n = cangetc(cq)) == 0)
		sleep(&cq->r, cangetc, cq);
	qlock(dp);
	if(dp->wq == 0){
		cq->out = cq->in;
	}else{
		bp = allocb(n);
		bp->flags |= S_DELIM;
		bp->wptr += gets(cq, bp->wptr, n);
		PUTNEXT(RD(dp->wq), bp);
	}
	qunlock(dp);
	goto loop;
}

Dirtab *duartdir;
int nduartport;

/*
 *  allocate the queues if no one else has
 */
void
duartreset(void)
{
	Duartport *dp;
	int i;

	/*
 	 *  allocate the directory and fill it in
	 */
	nduartport = 2*conf.nmach;
	duartdir = ialloc(nduartport*2*sizeof(Dirtab), 0);
	for(i = 0; i < nduartport; i++){
		sprint(duartdir[2*i].name, "tty%d", i);
		sprint(duartdir[2*i+1].name, "tty%dctl", i);
		duartdir[2*i].length = duartdir[2*i+1].length = 0;
		duartdir[2*i].perm = duartdir[2*i+1].perm = 0666;
		duartdir[2*i].qid.path = STREAMQID(i, Sdataqid);
		duartdir[2*i+1].qid.path = STREAMQID(i, Sctlqid);
	}

	/*
	 *  allocate queues for any stream interfaces
	 */
	for(dp = duartport; dp < &duartport[nduartport]; dp++){
		if(dp->nostream)
			continue;

		dp->iq = ialloc(sizeof(IOQ), 0);
		initq(dp->iq);
		dp->iq->ptr = dp;

		dp->oq = ialloc(sizeof(IOQ), 0);
		initq(dp->oq);
		dp->oq->ptr = dp;
		dp->oq->puts = duartputs;
	}
}

Chan*
duartattach(char *spec)
{
	return devattach('t', spec);
}

Chan*
duartclone(Chan *c, Chan *nc)
{
	return devclone(c, nc);
}

int
duartwalk(Chan *c, char *name)
{
	return devwalk(c, name, duartdir, 2*nduartport, devgen);
}

void
duartstat(Chan *c, char *dp)
{
	switch(STREAMTYPE(c->qid.path)){
	case Sdataqid:
		streamstat(c, dp, "tty0");
		break;
	default:
		devstat(c, dp, duartdir, 2*nduartport, devgen);
		break;
	}
}

Chan*
duartopen(Chan *c, int omode)
{
	Duartport *dp;

	switch(STREAMTYPE(c->qid.path)){
	case Sdataqid:
	case Sctlqid:
		dp = &duartport[STREAMID(c->qid.path)];
		break;
	default:
		dp = 0;
		break;
	}

	if(dp && dp->nostream)
		errors("in use");

	if((c->qid.path & CHDIR) == 0)
		streamopen(c, &duartinfo);
	return devopen(c, omode, duartdir, 2*nduartport, devgen);
}

void
duartcreate(Chan *c, char *name, int omode, ulong perm)
{
	errors("only God can create a tty");
}

void
duartclose(Chan *c)
{
	if(c->stream)
		streamclose(c);
}

long
duartread(Chan *c, void *buf, long n, ulong offset)
{
	Duartport *dp;

	if(c->qid.path&CHDIR)
		return devdirread(c, buf, n, duartdir, 2*nduartport, devgen);

	switch(STREAMTYPE(c->qid.path)){
	case Sdataqid:
		return streamread(c, buf, n);
	case Sctlqid:
		if(offset)
			return 0;
		dp = &duartport[STREAMID(c->qid.path)];
		qlock(dp);
		dp->op = Dstate;
		sleep(&dp->opr, opdone, dp);
		*(uchar *)buf = dp->val;
		qunlock(dp);
		return 1;
	}

	errors("no such duart type");
}

long
duartwrite(Chan *c, void *va, long n, ulong offset)
{
	return streamwrite(c, va, n, 0);
}

void
duartremove(Chan *c)
{
	errors("can't remove a duart");
}

void
duartwstat(Chan *c, char *dp)
{
	errors("can't write a duart");
}

int
duartactive(void)
{
	int i;

	for(i = 0; i < nduartport; i++)
		if(duartport[i].printing)
			return 1;
	return 0;                                                                         }

D power/duart.c => power/duart.c +0 -220
@@ 1,220 0,0 @@
#include	"u.h"
#include	"lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"io.h"

#define	PAD	15	/* registers are well-spaced */

/*
 * Register set for half the duart.  There are really two sets.
 */
struct Duart{
	uchar	mr1_2,		/* Mode Register Channels 1 & 2 */
		pad0[PAD];
	uchar	sr_csr,		/* Status Register/Clock Select Register */
		pad1[PAD];
	uchar	cmnd,		/* Command Register */
		pad2[PAD];
	uchar	data,		/* RX Holding / TX Holding Register */
		pad3[PAD];
	uchar	ipc_acr,	/* Input Port Change/Aux. Control Register */
		pad4[PAD];
	uchar	is_imr,		/* Interrupt Status/Interrupt Mask Register */
		pad5[PAD];
	uchar	ctur,		/* Counter/Timer Upper Register */
		pad6[PAD];
	uchar	ctlr,		/* Counter/Timer Lower Register */
		pad7[PAD];
};
#define	ppcr	is_imr		/* in the second register set */

#define DBD75		0
#define DBD110		1
#define DBD38400	2
#define DBD150		3
#define DBD300		4
#define DBD600		5
#define DBD1200		6
#define DBD2000		7
#define DBD2400		8
#define DBD4800		9
#define DBD1800		10
#define DBD9600		11
#define DBD19200	12

enum{
	CHAR_ERR	=0x00,	/* MR1x - Mode Register 1 */
	EVEN_PAR	=0x00,
	ODD_PAR		=0x04,
	NO_PAR		=0x10,
	CBITS8		=0x03,
	CBITS7		=0x02,
	CBITS6		=0x01,
	CBITS5		=0x00,
	NORM_OP		=0x00,	/* MR2x - Mode Register 2 */
	TWOSTOPB	=0x0F,
	ONESTOPB	=0x07,
	ENB_RX		=0x01,	/* CRx - Command Register */
	DIS_RX		=0x02,
	ENB_TX		=0x04,
	DIS_TX		=0x08,
	RESET_MR 	=0x10,
	RESET_RCV  	=0x20,
	RESET_TRANS  	=0x30,
	RESET_ERR  	=0x40,
	RESET_BCH	=0x50,
	STRT_BRK	=0x60,
	STOP_BRK	=0x70,
	RCV_RDY		=0x01,	/* SRx - Channel Status Register */
	FIFOFULL	=0x02,
	XMT_RDY		=0x04,
	XMT_EMT		=0x08,
	OVR_ERR		=0x10,
	PAR_ERR		=0x20,
	FRM_ERR		=0x40,
	RCVD_BRK	=0x80,
	IM_IPC		=0x80,	/* IMRx/ISRx - Interrupt Mask/Interrupt Status */
	IM_DBB		=0x40,
	IM_RRDYB	=0x20,
	IM_XRDYB	=0x10,
	IM_CRDY		=0x08,
	IM_DBA		=0x04,
	IM_RRDYA	=0x02,
	IM_XRDYA	=0x01,
};

void
duartinit(void)
{
	Duart *duart;

	duart = DUARTREG;

	duart->cmnd = RESET_RCV|DIS_TX|DIS_RX;
	duart->cmnd = RESET_TRANS;
	duart->cmnd = RESET_ERR;
	duart->cmnd = STOP_BRK;

	duart->ipc_acr = 0x80;		/* baud-rate set 2 */
	duart->cmnd = RESET_MR;
	duart->mr1_2 = NO_PAR|CBITS8;
	duart->mr1_2 = ONESTOPB;
	duart->sr_csr = (DBD9600<<4)|DBD9600;
	duart->is_imr = IM_RRDYA|IM_XRDYA;
	duart->cmnd = ENB_TX|ENB_RX;
}

void
duartreset(void)
{
	DUARTREG->cmnd = ENB_TX|ENB_RX;
}

void
duartintr(void)
{
	int cause, status, c;
	Duart *duart;

	duart = DUARTREG;
	cause = duart->is_imr;
	/*
	 * I can guess your interrupt.
	 */
	/*
	 * Is it 1?
	 */
	if(cause & IM_RRDYA){
		/* keyboard input interrupt */
		status = duart->sr_csr;
		c = duart->data;
		if(status & (FRM_ERR|OVR_ERR|PAR_ERR))
			duart->cmnd = RESET_ERR;
		kbdchar(c);
	}
	/*
	 * Is it 2?
	 */
	if(cause & IM_XRDYA){
		c = conschar();
		if(c == -1)
			duart->cmnd = DIS_TX;
		else
			duart->data = c;
	}
}
/*
int
duartgetc(void)
{
	Duart *duart;
	int c;

	duart = DUARTREG;
	do; while ((duart->sr_csr & RCV_RDY) == 0);
	c = duart->data&0177;
	consputc(c);
	if(c == '\r'){
		c = '\n';
		consputc(c);
	}
	return c;
}
*/

int
duartputc(int c)
{
	Duart *duart;
	int i;

	duart = DUARTREG;
	if(c == '\n')
		duartputc('\r');
	duart->cmnd = ENB_TX;
	i = 0;
	while((duart->sr_csr&XMT_RDY) == 0)
		if(++i >= 1000000){
			duartinit();
			for(i=0; i<100000; i++)
				;
			break;
		}
	duart->data = c;
	if(c == '\n')
		for(i=0; i<100000; i++)
			;
	return c;
}

void
duartxmit(int c)
{
	Duart *duart;

	duart = DUARTREG;
	duart->cmnd = ENB_TX;
	duart->data = c;
}

void
duartputs(char *s)
{
	int i;
	while(*s){
		duartputc(*s++);
	}
	for(i=0; i < 1000000; i++)
		;
}

void
iprint(char *s, void *a, void *b, void *c, void *d)
{
	char buf[1024];

	sprint(buf, s, a, b, c, d);
	duartputs(buf);
}

M power/fns.h => power/fns.h +5 -13
@@ 7,16 7,12 @@ ulong	confeval(char*);
void	confread(void);
void	confprint(void);
void	confset(char*);
int	consactive(void);
int	conschar(void);
void	consoff(void);
int	consputc(int);
void	duartspecial(int, IOQ*, IOQ*, int);
int	duartactive(void);
void	duartenable0(void);
void	duartinit(void);
void	duartintr(void);
int	duartputc(int);
void	duartputs(char*);
void	duartreset(void);
void	duartxmit(int);
void	duartslave(void);
void	evenaddr(ulong);
void	faultmips(Ureg*, int, int);
ulong	fcr31(void);


@@ 28,19 24,16 @@ ulong	gettlbvirt(int);
void	gotopc(ulong);
void	icflush(void *, int);
void	ioboardinit(void);
void	kbdchar(int);
void	intr(Ureg*);
void	lanceintr(void);
void	lanceparity(void);
void	lancesetup(Lance*);
void	launchinit(void);
void	launch(int);
void	lights(int);
void	newstart(void);
int	newtlbpid(Proc*);
void	novme(int);
void	online(void);
void	printinit(void);
Block*	prepend(Block*, int);
void	prflush(void);
#define procsetup(p)	((p)->fpstate = FPinit)


@@ 53,12 46,11 @@ void	puttlb(ulong, ulong);
void	puttlbx(int, ulong, ulong);
int	readlog(ulong, char*, ulong);
void	restfpregs(FPsave*, ulong);
#define	screenputs
void	setvmevec(int, void (*)(int));
void	sinit(void);
uchar*	smap(int, uchar*);
void	sunmap(int, uchar*);
void	syslog(char*, int);
void	sysloginit(void);
void	tlbinit(void);
Block*	tolance(Block*, int);
void	touser(void*);

M power/l.s => power/l.s +0 -3
@@ 160,10 160,8 @@ TEXT	gotopc(SB), $8

TEXT	puttlb(SB), $4

	JAL	splhi(SB)
	MOVW	0(FP), R2
	MOVW	4(FP), R3
	MOVW	R1, 4(SP)
	MOVW	R2, M(TLBVIRT)
	MOVW	R3, M(TLBPHYS)
	NOOP


@@ 173,7 171,6 @@ TEXT	puttlb(SB), $4
	BGEZ	R4, index
	TLBWR
	NOOP
	JAL	splx(SB)
	RET

index:

M power/lock.c => power/lock.c +18 -4
@@ 15,6 15,7 @@

#define	SEMPERPG	64		/* hardware semaphores per page */
#define NSEMPG		1024
#define ULOCKPG		512

struct
{


@@ 22,6 23,7 @@ struct
	ulong	*nextsem;		/* next one to allocate */
	int	nsem;			/* at SEMPERPG, jump to next page */
	uchar	bmap[NSEMPG];		/* allocation map */
	int	ulockpg;		/* count of user lock available */
}semalloc;

Page lkpgheader[NSEMPG];


@@ 32,6 34,7 @@ lockinit(void)
	memset(semalloc.bmap, 0, sizeof(semalloc.bmap));
	semalloc.bmap[0] = 1;

	semalloc.ulockpg = ULOCKPG;
	semalloc.lock.sbsem = SBSEM;
	semalloc.nextsem = SBSEM+1;
	semalloc.nsem = 1;


@@ 63,6 66,11 @@ lkpage(Orig *o, ulong va)
	int i;

	lock(&semalloc.lock);
	if(--semalloc.ulockpg < 0) {
		semalloc.ulockpg++;
		unlock(&semalloc.lock);
		return 0;
	}
	top = &semalloc.bmap[NSEMPG];
	for(p = semalloc.bmap; *p && p < top; p++)
		;


@@ 72,23 80,28 @@ lkpage(Orig *o, ulong va)
	*p = 1;
	i = p-semalloc.bmap;
	pg = &lkpgheader[i];
	pg->pa = (ulong)((i*WD2PG) + SBSEM);
	pg->pa = (ulong)((i*WD2PG) + SBSEM) & ~UNCACHED;
	pg->va = va;
	pg->o = o;
	pg->ref = 1;

	unlock(&semalloc.lock);
	return pg;
}

void
lkpgfree(ulong *lpa)
lkpgfree(Page *pg, int dolock)
{
	uchar *p;

	p = &semalloc.bmap[(lpa-SBSEM)/WD2PG];
	lock(&semalloc.lock);
	p = &semalloc.bmap[((pg->pa|UNCACHED)-(ulong)SBSEM)/BY2PG];
	if(!*p)
		panic("lkpgfree");
	*p = 0;
	
	semalloc.ulockpg++;
	unlock(&semalloc.lock);
}

#define PCOFF -9


@@ 171,9 184,10 @@ mklockseg(Seg *s)
	Orig *o;

	s->proc = u->p;
	o = neworig(LKSEGBASE, 0, OWRPERM|OPURE, 0);
	o = neworig(LKSEGBASE, 0, OWRPERM|OSHARED, 0);
	o->minca = 0;
	o->maxca = 0;
	o->freepg = lkpgfree;
	s->o = o;
	s->minva = LKSEGBASE;
	s->maxva = LKSEGBASE;

M power/main.c => power/main.c +18 -2
@@ 57,6 57,7 @@ main(void)
	arginit();
	lockinit();
	printinit();
	duartspecial(0, &printq, &kbdq, 9600);
	tlbinit();
	vecinit();
	procinit0();


@@ 83,6 84,7 @@ machinit(void)
	memset(m, 0, sizeof(Mach));
	m->machno = n;
	m->stb = &stlb[n][0];
	duartinit();
}

void


@@ 370,13 372,13 @@ exit(void)
	unlock(&active);
	spllo();
	print("cpu %d exiting\n", m->machno);
	while(active.machs || consactive())
	while(active.machs || duartactive())
		for(i=0; i<1000; i++)
			;
	splhi();
	for(i=0; i<2000000; i++)
		;
	duartreset();
	duartenable0();
	firmware();
}



@@ 819,3 821,17 @@ lanceparity(void)
	MODEREG->promenet &= ~4;
	MODEREG->promenet |= 4;
}

/*
 *  for the sake of a single devcons.c
 */
void
buzz(int f, int d)
{
}
int
mouseputc(IOQ *q, int c)
{
	return 0;
}


M power/mem.h => power/mem.h +1 -1
@@ 96,7 96,7 @@
#define	PTEGLOBL	(1<<8)
#define	PTEVALID	(1<<9)
#define	PTEWRITE	(1<<10)
#define PTEUNCACHE	(1<<11)
#define PTEUNCACHED	(1<<11)
#define PTERONLY	0
#define	PTEPID(n)	((n)<<6)
#define TLBPID(n)	(((n)>>6)&0x3F)

M ss/dat.h => ss/dat.h +0 -35
@@ 212,38 212,3 @@ struct
	short	machs;
	short	exiting;
}active;

/*
 *  character based IO (mouse, keyboard, console screen)
 */
typedef struct IOQ	IOQ;
typedef struct KIOQ	KIOQ;
#define NQ	4096
struct IOQ
{
	Lock;
	uchar	buf[NQ];
	uchar	*in;
	uchar	*out;
	int	state;
	Rendez	r;
	union{
		void	(*puts)(IOQ*, void*, int);	/* output */
		int	(*putc)(IOQ*, int);		/* input */
	};
	void	*ptr;
};
struct KIOQ
{
	QLock;
	IOQ;
	int	repeat;
	int	c;
	int	count;
};

extern IOQ	lineq;
extern IOQ	printq;
extern IOQ	mouseq;
extern KIOQ	kbdq;


M ss/fns.h => ss/fns.h +1 -31
@@ 59,6 59,7 @@ void	restartprint(Alarm*);
void	restfpregs(FPsave*);
void	screeninit(void);
void	screenputc(int);
void	screenputs(char*, int);
void	spldone(void);
ulong	swap1(ulong*);
void	touser(ulong);


@@ 66,34 67,3 @@ void	trap(Ureg*);
void	trapinit(void);
#define	wbflush()	/* mips compatibility */
#define	waserror()	(u->nerrlab++, setlabel(&u->errlab[u->nerrlab-1]))

/*
 *  for queues (to go into portfns.h)
 */
int	cangetc(void*);
int	canputc(void*);
int	getc(IOQ*);
int	gets(IOQ*, void*, int);
void	initq(IOQ*);
int	putc(IOQ*, int);
void	puts(IOQ*, void*, int);

/*
 *  for the scc (to go into portfns.h)
 */
void	sccintr(void);
void	sccputs(IOQ*, char*, int);
void	sccsetup(void*);
void	sccspecial(int, IOQ*, IOQ*, int);

/*
 *  for devcons (to go into portfns.h)
 */
void	kbdclock(void);
void	kbdrepeat(int);
int	kbdputc(IOQ*, int);
int	kprint(char*, ...);
int	mouseputc(IOQ*, int);
void	printinit(void);
void	putstr(char*);
void	putstrn(char*, long);

M ss/mem.h => ss/mem.h +1 -0
@@ 92,6 92,7 @@
#define	PTEIO		(1<<26)
#define	PTEACCESS	(1<<25)
#define	PTEMODIFY	(1<<24)
#define PTEUNCACHED	0

#define	INVALIDPTE	0
#define	PPN(pa)		((pa>>12)&0xFFFF)