~kris/9p

9hist

f322baa2e764a610c48ec19a242d4f4503054f59 — David du Colombier 24 years ago 78ef0ba
Plan 9 from Bell Labs 2002-01-09
M alphapc/devarch.c => alphapc/devarch.c +2 -1
@@ 372,7 372,8 @@ Dev archdevtab = {
	"arch",

	devreset,
	devinit,
	devinit,	
	devshutdown,
	archattach,
	archwalk,
	archstat,

M alphapc/devether.c => alphapc/devether.c +1 -0
@@ 431,6 431,7 @@ Dev etherdevtab = {

	etherreset,
	devinit,
	devshutdown,
	etherattach,
	etherwalk,
	etherstat,

M alphapc/devvga.c => alphapc/devvga.c +2 -1
@@ 393,7 393,8 @@ Dev vgadevtab = {
	"vga",

	vgareset,
	devinit,
	devinit,	
	devshutdown,
	vgaattach,
	vgawalk,
	vgastat,

M alphapc/main.c => alphapc/main.c +14 -6
@@ 145,16 145,16 @@ init0(void)
	chandevinit();

	if(!waserror()){
		ksetenv("cputype", "alpha");
		ksetenv("cputype", "alpha", 0);
		sprint(tstr, "alpha %s alphapc", conffile);
		ksetenv("terminal", tstr);
		ksetenv("terminal", tstr, 0);
		if(cpuserver)
			ksetenv("service", "cpu");
			ksetenv("service", "cpu", 0);
		else
			ksetenv("service", "terminal");
			ksetenv("service", "terminal", 0);
		for(i = 0; i < nconf; i++)
			if(confname[i] && confname[i][0] != '*')
				ksetenv(confname[i], confval[i]);
			if(confname[i])
				ksetenv(confname[i], confval[i], 1);
		poperror();
	}



@@ 258,6 258,14 @@ procsave(Proc *p)
	 */
}

/* still too do */
void
reboot(void*, void*, ulong)
{
	exit(0);
}


void
exit(int)
{

M bitsy/devether.c => bitsy/devether.c +1 -0
@@ 445,6 445,7 @@ Dev etherdevtab = {

	etherreset,
	devinit,
	devshutdown,
	etherattach,
	etherwalk,
	etherstat,

M bitsy/devflash.c => bitsy/devflash.c +1 -0
@@ 617,6 617,7 @@ Dev flashdevtab = {

	devreset,
	flashinit,
	devshutdown,
	flashattach,
	flashwalk,
	flashstat,

M bitsy/devpcmcia.c => bitsy/devpcmcia.c +1 -0
@@ 386,6 386,7 @@ Dev pcmciadevtab = {

	pcmciareset,
	devinit,
	devshutdown,
	pcmciaattach,
	pcmciawalk,
	pcmciastat,

M bitsy/devpenmouse.c => bitsy/devpenmouse.c +1 -0
@@ 430,6 430,7 @@ Dev penmousedevtab = {

	penmousereset,
	penmouseinit,
	devshutdown,
	penmouseattach,
	penmousewalk,
	penmousestat,

M bitsy/devsac.c => bitsy/devsac.c +1 -0
@@ 576,6 576,7 @@ Dev sacdevtab = {

	devreset,
	sacinit,
	devshutdown,
	sacattach,
	sacwalk,
	sacstat,

M bitsy/devuda1341.c => bitsy/devuda1341.c +1 -0
@@ 1382,6 1382,7 @@ Dev uda1341devtab = {

	devreset,
	audioinit,
	devshutdown,
	audioattach,
	audiowalk,
	audiostat,

M bitsy/devµc.c => bitsy/devµc.c +1 -0
@@ 417,6 417,7 @@ Dev µcdevtab = {

	devreset,
	µcinit,
	devshutdown,
	µcattach,
	µcwalk,
	µcstat,

M bitsy/main.c => bitsy/main.c +12 -4
@@ 51,6 51,14 @@ main(void)
	schedinit();
}

/* need to do better */
void
reboot(void*, void*, ulong)
{
	exit(0);
}


/*
 *  exit kernel either on a panic or user request
 */


@@ 94,12 102,12 @@ init0(void)
	chandevinit();

	if(!waserror()){
		ksetenv("terminal", "bitsy");
		ksetenv("cputype", "arm");
		ksetenv("terminal", "bitsy", 0);
		ksetenv("cputype", "arm", 0);
		if(cpuserver)
			ksetenv("service", "cpu");
			ksetenv("service", "cpu", 0);
		else
			ksetenv("service", "terminal");
			ksetenv("service", "terminal", 0);
		poperror();
	}
	kproc("alarm", alarmkproc, 0);

M boot/boot.h => boot/boot.h +2 -7
@@ 53,24 53,19 @@ extern int	parsefields(char*, char**, int, char*);

/* methods */
extern void	configil(Method*);
extern int	authil(void);
extern int	connectil(void);

extern void	configtcp(Method*);
extern int	authtcp(void);
extern int	connecttcp(void);

extern void	configlocal(Method*);
extern int	authlocal(void);
extern int	connectlocal(void);

extern void	configsac(Method*);
extern int	authsac(void);
extern int	connectsac(void);

extern void	configpaq(Method*);
extern int	authpaq(void);
extern int	connectpaq(void);
extern void	configrc(Method*);
extern int	connectrc(void);

/* hack for passing authentication address */
extern char	*authaddr;

M boot/local.c => boot/local.c +0 -6
@@ 49,12 49,6 @@ configlocal(Method *mp)
}

int
authlocal(void)
{
	return -1;
}

int
connectlocal(void)
{
	int i, p[2];

A boot/rc.c => boot/rc.c +17 -0
@@ 0,0 1,17 @@
#include <u.h>
#include <libc.h>
#include <../boot/boot.h>

void
configrc(Method *)
{
	execl("/rc", "/rc", "-m", "/rcmain", "-i", 0);
	fatal("rc");
}

int
connectrc(void)
{
	// does not get here
	return -1;
}

M boot/sac.c => boot/sac.c +0 -9
@@ 2,8 2,6 @@
#include <libc.h>
#include <../boot/boot.h>

void configip(char* dev);

// HACK - take over from boot since file system is not
// available on a pipe



@@ 43,13 41,6 @@ configsac(Method *mp)
}

int
authsac(void)
{
	// does not get here
	return -1;
}

int
connectsac(void)
{
	// does not get here

M ip/devip.c => ip/devip.c +2 -8
@@ 259,11 259,7 @@ ipreset(void)
{
	nullmediumlink();
	pktmediumlink();
}

static void
ipinit(void)
{
	fmtinstall('i', eipconv);
	fmtinstall('I', eipconv);
	fmtinstall('E', eipconv);


@@ 1079,9 1075,6 @@ ipwrite(Chan* ch, void *v, long n, vlong off)
			free(cb);
			nexterror();
		}
		if(cb->nf < 1)
			error("short control request");

		ct = lookupcmd(cb, ipctlmsg, nelem(ipctlmsg));
		switch(ct->index){
		case CMconnect:


@@ 1171,7 1164,8 @@ Dev ipdevtab = {
	"ip",

	ipreset,
	ipinit,
	devinit,
	devshutdown,
	ipattach,
	ipwalk,
	ipstat,

M mtx/devarch.c => mtx/devarch.c +1 -0
@@ 372,6 372,7 @@ Dev archdevtab = {

	devreset,
	devinit,
	devshutdown,
	archattach,
	archwalk,
	archstat,

A mtx/devether.c => mtx/devether.c +467 -0
@@ 0,0 1,467 @@
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
#include "ureg.h"
#include "../port/error.h"
#include "../port/netif.h"

#include "etherif.h"

static Ether *etherxx[MaxEther];

Chan*
etherattach(char* spec)
{
	ulong ctlrno;
	char *p;
	Chan *chan;

	ctlrno = 0;
	if(spec && *spec){
		ctlrno = strtoul(spec, &p, 0);
		if((ctlrno == 0 && p == spec) || *p || (ctlrno >= MaxEther))
			error(Ebadarg);
	}
	if(etherxx[ctlrno] == 0)
		error(Enodev);

	chan = devattach('l', spec);
	chan->dev = ctlrno;
	if(etherxx[ctlrno]->attach)
		etherxx[ctlrno]->attach(etherxx[ctlrno]);
	return chan;
}

static Walkqid*
etherwalk(Chan* chan, Chan* nchan, char** name, int nname)
{
	return netifwalk(etherxx[chan->dev], chan, nchan, name, nname);
}

static int
etherstat(Chan* chan, uchar* dp, int n)
{
	return netifstat(etherxx[chan->dev], chan, dp, n);
}

static Chan*
etheropen(Chan* chan, int omode)
{
	return netifopen(etherxx[chan->dev], chan, omode);
}

static void
ethercreate(Chan*, char*, int, ulong)
{
}

static void
etherclose(Chan* chan)
{
	netifclose(etherxx[chan->dev], chan);
}

static long
etherread(Chan* chan, void* buf, long n, vlong off)
{
	Ether *ether;
	ulong offset = off;

	ether = etherxx[chan->dev];
	if((chan->qid.type & QTDIR) == 0 && ether->ifstat){
		/*
		 * With some controllers it is necessary to reach
		 * into the chip to extract statistics.
		 */
		if(NETTYPE(chan->qid.path) == Nifstatqid)
			return ether->ifstat(ether, buf, n, offset);
		else if(NETTYPE(chan->qid.path) == Nstatqid)
			ether->ifstat(ether, buf, 0, offset);
	}

	return netifread(ether, chan, buf, n, offset);
}

static Block*
etherbread(Chan* chan, long n, ulong offset)
{
	return netifbread(etherxx[chan->dev], chan, n, offset);
}

static int
etherwstat(Chan* chan, uchar* dp, int n)
{
	return netifwstat(etherxx[chan->dev], chan, dp, n);
}

static void
etherrtrace(Netfile* f, Etherpkt* pkt, int len)
{
	int i, n;
	Block *bp;

	if(qwindow(f->in) <= 0)
		return;
	if(len > 58)
		n = 58;
	else
		n = len;
	bp = iallocb(64);
	if(bp == nil)
		return;
	memmove(bp->wp, pkt->d, n);
	i = TK2MS(MACHP(0)->ticks);
	bp->wp[58] = len>>8;
	bp->wp[59] = len;
	bp->wp[60] = i>>24;
	bp->wp[61] = i>>16;
	bp->wp[62] = i>>8;
	bp->wp[63] = i;
	bp->wp += 64;
	qpass(f->in, bp);
}

Block*
etheriq(Ether* ether, Block* bp, int fromwire)
{
	Etherpkt *pkt;
	ushort type;
	int len, multi, tome, fromme;
	Netfile **ep, *f, **fp, *fx;
	Block *xbp;

	ether->inpackets++;

	pkt = (Etherpkt*)bp->rp;
	len = BLEN(bp);
	type = (pkt->type[0]<<8)|pkt->type[1];
	fx = 0;
	ep = &ether->f[Ntypes];

	multi = pkt->d[0] & 1;
	/* check for valid multcast addresses */
	if(multi && memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) && ether->prom == 0){
		if(!activemulti(ether, pkt->d, sizeof(pkt->d))){
			if(fromwire){
				freeb(bp);
				bp = 0;
			}
			return bp;
		}
	}

	/* is it for me? */
	tome = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0;
	fromme = memcmp(pkt->s, ether->ea, sizeof(pkt->s)) == 0;

	/*
	 * Multiplex the packet to all the connections which want it.
	 * If the packet is not to be used subsequently (fromwire != 0),
	 * attempt to simply pass it into one of the connections, thereby
	 * saving a copy of the data (usual case hopefully).
	 */
	for(fp = ether->f; fp < ep; fp++){
		if(f = *fp)
		if(f->type == type || f->type < 0)
		if(tome || multi || f->prom){
			/* Don't want to hear bridged packets */
			if(f->bridge && !fromwire && !fromme)
				continue;
			if(!f->headersonly){
				if(fromwire && fx == 0)
					fx = f;
				else if(xbp = iallocb(len)){
					memmove(xbp->wp, pkt, len);
					xbp->wp += len;
					qpass(f->in, xbp);
				}
				else
					ether->soverflows++;
			}
			else
				etherrtrace(f, pkt, len);
		}
	}

	if(fx){
		if(qpass(fx->in, bp) < 0)
			ether->soverflows++;
		return 0;
	}
	if(fromwire){
		freeb(bp);
		return 0;
	}

	return bp;
}

static int
etheroq(Ether* ether, Block* bp)
{
	int len, loopback, s;
	Etherpkt *pkt;

	ether->outpackets++;

	/*
	 * Check if the packet has to be placed back onto the input queue,
	 * i.e. if it's a loopback or broadcast packet or the interface is
	 * in promiscuous mode.
	 * If it's a loopback packet indicate to etheriq that the data isn't
	 * needed and return, etheriq will pass-on or free the block.
	 * To enable bridging to work, only packets that were originated
	 * by this interface are fed back.
	 */
	pkt = (Etherpkt*)bp->rp;
	len = BLEN(bp);
	loopback = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0;
	if(loopback || memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) == 0 || ether->prom){
		s = splhi();
		etheriq(ether, bp, 0);
		splx(s);
	}

	if(!loopback){
		qbwrite(ether->oq, bp);
		ether->transmit(ether);
	} else
		freeb(bp);

	return len;
}

static long
etherwrite(Chan* chan, void* buf, long n, vlong)
{
	Ether *ether;
	Block *bp;
	int nn;

	ether = etherxx[chan->dev];
	if(NETTYPE(chan->qid.path) != Ndataqid) {
		nn = netifwrite(ether, chan, buf, n);
		if(nn >= 0)
			return nn;

		if(ether->ctl!=nil)
			return ether->ctl(ether,buf,n);
			
		error(Ebadctl);
	}

	if(n > ether->maxmtu)
		error(Etoobig);
	if(n < ether->minmtu)
		error(Etoosmall);

	bp = allocb(n);
	if(waserror()){
		freeb(bp);
		nexterror();
	}
	memmove(bp->rp, buf, n);
	memmove(bp->rp+Eaddrlen, ether->ea, Eaddrlen);
	poperror();
	bp->wp += n;

	return etheroq(ether, bp);
}

static long
etherbwrite(Chan* chan, Block* bp, ulong)
{
	Ether *ether;
	long n;

	n = BLEN(bp);
	if(NETTYPE(chan->qid.path) != Ndataqid){
		if(waserror()) {
			freeb(bp);
			nexterror();
		}
		n = etherwrite(chan, bp->rp, n, 0);
		poperror();
		freeb(bp);
		return n;
	}
	ether = etherxx[chan->dev];

	if(n > ether->maxmtu){
		freeb(bp);
		error(Etoobig);
	}
	if(n < ether->minmtu){
		freeb(bp);
		error(Etoosmall);
	}

	return etheroq(ether, bp);
}

static struct {
	char*	type;
	int	(*reset)(Ether*);
} cards[MaxEther+1];

void
addethercard(char* t, int (*r)(Ether*))
{
	static int ncard;

	if(ncard == MaxEther)
		panic("too many ether cards");
	cards[ncard].type = t;
	cards[ncard].reset = r;
	ncard++;
}

int
parseether(uchar *to, char *from)
{
	char nip[4];
	char *p;
	int i;

	p = from;
	for(i = 0; i < 6; i++){
		if(*p == 0)
			return -1;
		nip[0] = *p++;
		if(*p == 0)
			return -1;
		nip[1] = *p++;
		nip[2] = 0;
		to[i] = strtoul(nip, 0, 16);
		if(*p == ':')
			p++;
	}
	return 0;
}

static void
etherreset(void)
{
	Ether *ether;
	int i, n, ctlrno;
	char name[32], buf[128];

	for(ether = 0, ctlrno = 0; ctlrno < MaxEther; ctlrno++){
		if(ether == 0)
			ether = malloc(sizeof(Ether));
		memset(ether, 0, sizeof(Ether));
		ether->ctlrno = ctlrno;
		ether->tbdf = BUSUNKNOWN;
		ether->mbps = 10;
		ether->minmtu = ETHERMINTU;
		ether->maxmtu = ETHERMAXTU;
		if(isaconfig("ether", ctlrno, ether) == 0)
			continue;
		for(n = 0; cards[n].type; n++){
			if(cistrcmp(cards[n].type, ether->type))
				continue;
			for(i = 0; i < ether->nopt; i++){
				if(strncmp(ether->opt[i], "ea=", 3))
					continue;
				if(parseether(ether->ea, &ether->opt[i][3]) == -1)
					memset(ether->ea, 0, Eaddrlen);
			}	
			if(cards[n].reset(ether))
				break;

			/*
			 * IRQ2 doesn't really exist, it's used to gang the interrupt
			 * controllers together. A device set to IRQ2 will appear on
			 * the second interrupt controller as IRQ9.
			 */
			if(ether->irq == 2)
				ether->irq = 9;
			snprint(name, sizeof(name), "ether%d", ctlrno);

			/*
			 * If ether->irq is 0, it is a hack to indicate no interrupt
			 * used by ethersink.
			 */
			if(ether->irq > 0)
				intrenable(ether->irq, ether->interrupt, ether, ether->tbdf, name);

			i = sprint(buf, "#l%d: %s: %dMbps port 0x%luX irq %lud",
				ctlrno, ether->type, ether->mbps, ether->port, ether->irq);
			if(ether->mem)
				i += sprint(buf+i, " addr 0x%luX", PADDR(ether->mem));
			if(ether->size)
				i += sprint(buf+i, " size 0x%luX", ether->size);
			i += sprint(buf+i, ": %2.2uX%2.2uX%2.2uX%2.2uX%2.2uX%2.2uX",
				ether->ea[0], ether->ea[1], ether->ea[2],
				ether->ea[3], ether->ea[4], ether->ea[5]);
			sprint(buf+i, "\n");
			print(buf);

			if(ether->mbps >= 100){
				netifinit(ether, name, Ntypes, 256*1024);
				if(ether->oq == 0)
					ether->oq = qopen(256*1024, 1, 0, 0);
			}
			else{
				netifinit(ether, name, Ntypes, 65*1024);
				if(ether->oq == 0)
					ether->oq = qopen(65*1024, 1, 0, 0);
			}
			if(ether->oq == 0)
				panic("etherreset %s", name);
			ether->alen = Eaddrlen;
			memmove(ether->addr, ether->ea, Eaddrlen);
			memset(ether->bcast, 0xFF, Eaddrlen);

			etherxx[ctlrno] = ether;
			ether = 0;
			break;
		}
	}
	if(ether)
		free(ether);
}

#define POLY 0xedb88320

/* really slow 32 bit crc for ethers */
ulong
ethercrc(uchar *p, int len)
{
	int i, j;
	ulong crc, b;

	crc = 0xffffffff;
	for(i = 0; i < len; i++){
		b = *p++;
		for(j = 0; j < 8; j++){
			crc = (crc>>1) ^ (((crc^b) & 1) ? POLY : 0);
			b >>= 1;
		}
	}
	return crc;
}

Dev etherdevtab = {
	'l',
	"ether",

	etherreset,
	devinit,
	devshutdown,
	etherattach,
	etherwalk,
	etherstat,
	etheropen,
	ethercreate,
	etherclose,
	etherread,
	etherbread,
	etherwrite,
	etherbwrite,
	devremove,
	etherwstat,
};

A mtx/ether2114x.c => mtx/ether2114x.c +1671 -0
@@ 0,0 1,1671 @@
/*
 * Digital Semiconductor DECchip 2114x PCI Fast Ethernet LAN Controller.
 * To do:
 *	thresholds;
 *	ring sizing;
 *	handle more error conditions;
 *	tidy setup packet mess;
 *	push initialisation back to attach;
 *	full SROM decoding.
 */
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
#include "../port/error.h"
#include "../port/netif.h"

#include "etherif.h"

#define DEBUG		(1)
#define debug		if(DEBUG)print

enum {
	Nrde		= 64,
	Ntde		= 64,
};

#define Rbsz		ROUNDUP(sizeof(Etherpkt)+4, 4)

enum {					/* CRS0 - Bus Mode */
	Swr		= 0x00000001,	/* Software Reset */
	Bar		= 0x00000002,	/* Bus Arbitration */
	Dsl		= 0x0000007C,	/* Descriptor Skip Length (field) */
	Ble		= 0x00000080,	/* Big/Little Endian */
	Pbl		= 0x00003F00,	/* Programmable Burst Length (field) */
	Cal		= 0x0000C000,	/* Cache Alignment (field) */
	Cal8		= 0x00004000,	/* 8 longword boundary alignment */
	Cal16		= 0x00008000,	/* 16 longword boundary alignment */
	Cal32		= 0x0000C000,	/* 32 longword boundary alignment */
	Tap		= 0x000E0000,	/* Transmit Automatic Polling (field) */
	Dbo		= 0x00100000,	/* Descriptor Byte Ordering Mode */
	Rml		= 0x00200000,	/* Read Multiple */
}; 

enum {					/* CSR[57] - Status and Interrupt Enable */
	Ti		= 0x00000001,	/* Transmit Interrupt */
	Tps		= 0x00000002,	/* Transmit Process Stopped */
	Tu		= 0x00000004,	/* Transmit buffer Unavailable */
	Tjt		= 0x00000008,	/* Transmit Jabber Timeout */
	Unf		= 0x00000020,	/* transmit UNderFlow */
	Ri		= 0x00000040,	/* Receive Interrupt */
	Ru		= 0x00000080,	/* Receive buffer Unavailable */
	Rps		= 0x00000100,	/* Receive Process Stopped */
	Rwt		= 0x00000200,	/* Receive Watchdog Timeout */
	Eti		= 0x00000400,	/* Early Transmit Interrupt */
	Gte		= 0x00000800,	/* General purpose Timer Expired */
	Fbe		= 0x00002000,	/* Fatal Bit Error */
	Ais		= 0x00008000,	/* Abnormal Interrupt Summary */
	Nis		= 0x00010000,	/* Normal Interrupt Summary */
	Rs		= 0x000E0000,	/* Receive process State (field) */
	Ts		= 0x00700000,	/* Transmit process State (field) */
	Eb		= 0x03800000,	/* Error bits */
};

enum {					/* CSR6 - Operating Mode */
	Hp		= 0x00000001,	/* Hash/Perfect receive filtering mode */
	Sr		= 0x00000002,	/* Start/stop Receive */
	Ho		= 0x00000004,	/* Hash-Only filtering mode */
	Pb		= 0x00000008,	/* Pass Bad frames */
	If		= 0x00000010,	/* Inverse Filtering */
	Sb		= 0x00000020,	/* Start/stop Backoff counter */
	Pr		= 0x00000040,	/* Promiscuous Mode */
	Pm		= 0x00000080,	/* Pass all Multicast */
	Fd		= 0x00000200,	/* Full Duplex mode */
	Om		= 0x00000C00,	/* Operating Mode (field) */
	Fc		= 0x00001000,	/* Force Collision */
	St		= 0x00002000,	/* Start/stop Transmission Command */
	Tr		= 0x0000C000,	/* ThReshold control bits (field) */
	Tr128		= 0x00000000,
	Tr256		= 0x00004000,
	Tr512		= 0x00008000,
	Tr1024		= 0x0000C000,
	Ca		= 0x00020000,	/* CApture effect enable */
	Ps		= 0x00040000,	/* Port Select */
	Hbd		= 0x00080000,	/* HeartBeat Disable */
	Imm		= 0x00100000,	/* IMMediate mode */
	Sf		= 0x00200000,	/* Store and Forward */
	Ttm		= 0x00400000,	/* Transmit Threshold Mode */
	Pcs		= 0x00800000,	/* PCS function */
	Scr		= 0x01000000,	/* SCRambler mode */
	Mbo		= 0x02000000,	/* Must Be One */
	Ra		= 0x40000000,	/* Receive All */
	Sc		= 0x80000000,	/* Special Capture effect enable */

	TrMODE		= Tr512,	/* default transmission threshold */
};

enum {					/* CSR9 - ROM and MII Management */
	Scs		= 0x00000001,	/* serial ROM chip select */
	Sclk		= 0x00000002,	/* serial ROM clock */
	Sdi		= 0x00000004,	/* serial ROM data in */
	Sdo		= 0x00000008,	/* serial ROM data out */
	Ss		= 0x00000800,	/* serial ROM select */
	Wr		= 0x00002000,	/* write */
	Rd		= 0x00004000,	/* read */

	Mdc		= 0x00010000,	/* MII management clock */
	Mdo		= 0x00020000,	/* MII management write data */
	Mii		= 0x00040000,	/* MII management operation mode (W) */
	Mdi		= 0x00080000,	/* MII management data in */
};

enum {					/* CSR12 - General-Purpose Port */
	Gpc		= 0x00000100,	/* General Purpose Control */
};

typedef struct Des {
	int	status;
	int	control;
	ulong	addr;
	Block*	bp;
} Des;

enum {					/* status */
	Of		= 0x00000001,	/* Rx: OverFlow */
	Ce		= 0x00000002,	/* Rx: CRC Error */
	Db		= 0x00000004,	/* Rx: Dribbling Bit */
	Re		= 0x00000008,	/* Rx: Report on MII Error */
	Rw		= 0x00000010,	/* Rx: Receive Watchdog */
	Ft		= 0x00000020,	/* Rx: Frame Type */
	Cs		= 0x00000040,	/* Rx: Collision Seen */
	Tl		= 0x00000080,	/* Rx: Frame too Long */
	Ls		= 0x00000100,	/* Rx: Last deScriptor */
	Fs		= 0x00000200,	/* Rx: First deScriptor */
	Mf		= 0x00000400,	/* Rx: Multicast Frame */
	Rf		= 0x00000800,	/* Rx: Runt Frame */
	Dt		= 0x00003000,	/* Rx: Data Type (field) */
	De		= 0x00004000,	/* Rx: Descriptor Error */
	Fl		= 0x3FFF0000,	/* Rx: Frame Length (field) */
	Ff		= 0x40000000,	/* Rx: Filtering Fail */

	Def		= 0x00000001,	/* Tx: DEFerred */
	Uf		= 0x00000002,	/* Tx: UnderFlow error */
	Lf		= 0x00000004,	/* Tx: Link Fail report */
	Cc		= 0x00000078,	/* Tx: Collision Count (field) */
	Hf		= 0x00000080,	/* Tx: Heartbeat Fail */
	Ec		= 0x00000100,	/* Tx: Excessive Collisions */
	Lc		= 0x00000200,	/* Tx: Late Collision */
	Nc		= 0x00000400,	/* Tx: No Carrier */
	Lo		= 0x00000800,	/* Tx: LOss of carrier */
	To		= 0x00004000,	/* Tx: Transmission jabber timeOut */

	Es		= 0x00008000,	/* [RT]x: Error Summary */
	Own		= 0x80000000,	/* [RT]x: OWN bit */
};

enum {					/* control */
	Bs1		= 0x000007FF,	/* [RT]x: Buffer 1 Size */
	Bs2		= 0x003FF800,	/* [RT]x: Buffer 2 Size */

	Ch		= 0x01000000,	/* [RT]x: second address CHained */
	Er		= 0x02000000,	/* [RT]x: End of Ring */

	Ft0		= 0x00400000,	/* Tx: Filtering Type 0 */
	Dpd		= 0x00800000,	/* Tx: Disabled PaDding */
	Ac		= 0x04000000,	/* Tx: Add CRC disable */
	Set		= 0x08000000,	/* Tx: SETup packet */
	Ft1		= 0x10000000,	/* Tx: Filtering Type 1 */
	Fseg		= 0x20000000,	/* Tx: First SEGment */
	Lseg		= 0x40000000,	/* Tx: Last SEGment */
	Ic		= 0x80000000,	/* Tx: Interrupt on Completion */
};

enum {					/* PHY registers */
	Bmcr		= 0,		/* Basic Mode Control */
	Bmsr		= 1,		/* Basic Mode Status */
	Phyidr1		= 2,		/* PHY Identifier #1 */
	Phyidr2		= 3,		/* PHY Identifier #2 */
	Anar		= 4,		/* Auto-Negotiation Advertisment */
	Anlpar		= 5,		/* Auto-Negotiation Link Partner Ability */
	Aner		= 6,		/* Auto-Negotiation Expansion */
};

enum {					/* Variants */
	Tulip0		= (0x0009<<16)|0x1011,
	Tulip3		= (0x0019<<16)|0x1011,
	Pnic		= (0x0002<<16)|0x11AD,
	Pnic2		= (0xC115<<16)|0x11AD,
};

typedef struct Ctlr Ctlr;
typedef struct Ctlr {
	int	port;
	Pcidev*	pcidev;
	Ctlr*	next;
	int	active;
	int	id;			/* (pcidev->did<<16)|pcidev->vid */

	uchar*	srom;
	int	sromsz;			/* address size in bits */
	uchar*	sromea;			/* MAC address */
	uchar*	leaf;
	int	sct;			/* selected connection type */
	int	k;			/* info block count */
	uchar*	infoblock[16];
	int	sctk;			/* sct block index */
	int	curk;			/* current block index */
	uchar*	type5block;

	int	phy[32];		/* logical to physical map */
	int	phyreset;		/* reset bitmap */
	int	curphyad;
	int	fdx;
	int	ttm;

	uchar	fd;			/* option */
	int	medium;			/* option */

	int	csr6;			/* CSR6 - operating mode */
	int	mask;			/* CSR[57] - interrupt mask */
	int	mbps;

	Lock	lock;

	Des*	rdr;			/* receive descriptor ring */
	int	nrdr;			/* size of rdr */
	int	rdrx;			/* index into rdr */

	Lock	tlock;
	Des*	tdr;			/* transmit descriptor ring */
	int	ntdr;			/* size of tdr */
	int	tdrh;			/* host index into tdr */
	int	tdri;			/* interface index into tdr */
	int	ntq;			/* descriptors active */
	int	ntqmax;
	Block*	setupbp;

	ulong	of;			/* receive statistics */
	ulong	ce;
	ulong	cs;
	ulong	tl;
	ulong	rf;
	ulong	de;

	ulong	ru;
	ulong	rps;
	ulong	rwt;

	ulong	uf;			/* transmit statistics */
	ulong	ec;
	ulong	lc;
	ulong	nc;
	ulong	lo;
	ulong	to;

	ulong	tps;
	ulong	tu;
	ulong	tjt;
	ulong	unf;
} Ctlr;

static Ctlr* ctlrhead;
static Ctlr* ctlrtail;

#define csr32r(c, r)	(inl((c)->port+((r)*8)))
#define csr32w(c, r, l)	(outl((c)->port+((r)*8), (ulong)(l)))

static void
promiscuous(void* arg, int on)
{
	Ctlr *ctlr;

	ctlr = ((Ether*)arg)->ctlr;
	ilock(&ctlr->lock);
	if(on)
		ctlr->csr6 |= Pr;
	else
		ctlr->csr6 &= ~Pr;
	csr32w(ctlr, 6, ctlr->csr6);
	iunlock(&ctlr->lock);
}

static void
attach(Ether* ether)
{
	Ctlr *ctlr;

	ctlr = ether->ctlr;
	ilock(&ctlr->lock);
	if(!(ctlr->csr6 & Sr)){
		ctlr->csr6 |= Sr;
		csr32w(ctlr, 6, ctlr->csr6);
	}
	iunlock(&ctlr->lock);
}

static long
ifstat(Ether* ether, void* a, long n, ulong offset)
{
	Ctlr *ctlr;
	char *buf, *p;
	int i, l, len;

	ctlr = ether->ctlr;

	ether->crcs = ctlr->ce;
	ether->frames = ctlr->rf+ctlr->cs;
	ether->buffs = ctlr->de+ctlr->tl;
	ether->overflows = ctlr->of;

	if(n == 0)
		return 0;

	p = malloc(READSTR);
	l = snprint(p, READSTR, "Overflow: %lud\n", ctlr->of);
	l += snprint(p+l, READSTR-l, "Ru: %lud\n", ctlr->ru);
	l += snprint(p+l, READSTR-l, "Rps: %lud\n", ctlr->rps);
	l += snprint(p+l, READSTR-l, "Rwt: %lud\n", ctlr->rwt);
	l += snprint(p+l, READSTR-l, "Tps: %lud\n", ctlr->tps);
	l += snprint(p+l, READSTR-l, "Tu: %lud\n", ctlr->tu);
	l += snprint(p+l, READSTR-l, "Tjt: %lud\n", ctlr->tjt);
	l += snprint(p+l, READSTR-l, "Unf: %lud\n", ctlr->unf);
	l += snprint(p+l, READSTR-l, "CRC Error: %lud\n", ctlr->ce);
	l += snprint(p+l, READSTR-l, "Collision Seen: %lud\n", ctlr->cs);
	l += snprint(p+l, READSTR-l, "Frame Too Long: %lud\n", ctlr->tl);
	l += snprint(p+l, READSTR-l, "Runt Frame: %lud\n", ctlr->rf);
	l += snprint(p+l, READSTR-l, "Descriptor Error: %lud\n", ctlr->de);
	l += snprint(p+l, READSTR-l, "Underflow Error: %lud\n", ctlr->uf);
	l += snprint(p+l, READSTR-l, "Excessive Collisions: %lud\n", ctlr->ec);
	l += snprint(p+l, READSTR-l, "Late Collision: %lud\n", ctlr->lc);
	l += snprint(p+l, READSTR-l, "No Carrier: %lud\n", ctlr->nc);
	l += snprint(p+l, READSTR-l, "Loss of Carrier: %lud\n", ctlr->lo);
	l += snprint(p+l, READSTR-l, "Transmit Jabber Timeout: %lud\n",
		ctlr->to);
	l += snprint(p+l, READSTR-l, "csr6: %luX %uX\n", csr32r(ctlr, 6),
		ctlr->csr6);
	snprint(p+l, READSTR-l, "ntqmax: %d\n", ctlr->ntqmax);
	ctlr->ntqmax = 0;
	buf = a;
	len = readstr(offset, buf, n, p);
	if(offset > l)
		offset -= l;
	else
		offset = 0;
	buf += len;
	n -= len;

	l = snprint(p, READSTR, "srom:");
	for(i = 0; i < (1<<(ctlr->sromsz)*sizeof(ushort)); i++){
		if(i && ((i & 0x0F) == 0))
			l += snprint(p+l, READSTR-l, "\n     ");
		l += snprint(p+l, READSTR-l, " %2.2uX", ctlr->srom[i]);
	}

	snprint(p+l, READSTR-l, "\n");
	len += readstr(offset, buf, n, p);
	free(p);

	return len;
}

static void
txstart(Ether* ether)
{
	Ctlr *ctlr;
	Block *bp;
	Des *des;
	int control;

	ctlr = ether->ctlr;
	while(ctlr->ntq < (ctlr->ntdr-1)){
		if(ctlr->setupbp){
			bp = ctlr->setupbp;
			ctlr->setupbp = 0;
			control = Ic|Set|BLEN(bp);
		}
		else{
			bp = qget(ether->oq);
			if(bp == nil)
				break;
			control = Ic|Lseg|Fseg|BLEN(bp);
		}

		ctlr->tdr[PREV(ctlr->tdrh, ctlr->ntdr)].control &= ~Ic;
		des = &ctlr->tdr[ctlr->tdrh];
		des->bp = bp;
		des->addr = PCIWADDR(bp->rp);
		des->control |= control;
		ctlr->ntq++;
		coherence();
		des->status = Own;
		csr32w(ctlr, 1, 0);
		ctlr->tdrh = NEXT(ctlr->tdrh, ctlr->ntdr);
	}

	if(ctlr->ntq > ctlr->ntqmax)
		ctlr->ntqmax = ctlr->ntq;
}

static void
transmit(Ether* ether)
{
	Ctlr *ctlr;

	ctlr = ether->ctlr;
	ilock(&ctlr->tlock);
	txstart(ether);
	iunlock(&ctlr->tlock);
}

static void
interrupt(Ureg*, void* arg)
{
	Ctlr *ctlr;
	Ether *ether;
	int len, status;
	Des *des;
	Block *bp;

print("ether intr\n");
	ether = arg;
	ctlr = ether->ctlr;

	while((status = csr32r(ctlr, 5)) & (Nis|Ais)){
		/*
		 * Acknowledge the interrupts and mask-out
		 * the ones that are implicitly handled.
		 */
		csr32w(ctlr, 5, status);
		status &= (ctlr->mask & ~(Nis|Ti));

		if(status & Ais){
			if(status & Tps)
				ctlr->tps++;
			if(status & Tu)
				ctlr->tu++;
			if(status & Tjt)
				ctlr->tjt++;
			if(status & Ru)
				ctlr->ru++;
			if(status & Rps)
				ctlr->rps++;
			if(status & Rwt)
				ctlr->rwt++;
			status &= ~(Ais|Rwt|Rps|Ru|Tjt|Tu|Tps);
		}

		/*
		 * Received packets.
		 */
		if(status & Ri){
			des = &ctlr->rdr[ctlr->rdrx];
			while(!(des->status & Own)){
				if(des->status & Es){
					if(des->status & Of)
						ctlr->of++;
					if(des->status & Ce)
						ctlr->ce++;
					if(des->status & Cs)
						ctlr->cs++;
					if(des->status & Tl)
						ctlr->tl++;
					if(des->status & Rf)
						ctlr->rf++;
					if(des->status & De)
						ctlr->de++;
				}
				else if(bp = iallocb(Rbsz)){
					len = ((des->status & Fl)>>16)-4;
					des->bp->wp = des->bp->rp+len;
					etheriq(ether, des->bp, 1);
					des->bp = bp;
					des->addr = PCIWADDR(bp->rp);
				}

				des->control &= Er;
				des->control |= Rbsz;
				coherence();
				des->status = Own;

				ctlr->rdrx = NEXT(ctlr->rdrx, ctlr->nrdr);
				des = &ctlr->rdr[ctlr->rdrx];
			}
			status &= ~Ri;
		}

		/*
		 * Check the transmit side:
		 *	check for Transmit Underflow and Adjust
		 *	the threshold upwards;
		 *	free any transmitted buffers and try to
		 *	top-up the ring.
		 */
		if(status & Unf){
			ctlr->unf++;
			ilock(&ctlr->lock);
			csr32w(ctlr, 6, ctlr->csr6 & ~St);
			switch(ctlr->csr6 & Tr){
			case Tr128:
				len = Tr256;
				break;
			case Tr256:
				len = Tr512;
				break;
			case Tr512:
				len = Tr1024;
				break;
			default:
			case Tr1024:
				len = Sf;
				break;
			}
			ctlr->csr6 = (ctlr->csr6 & ~Tr)|len;
			csr32w(ctlr, 6, ctlr->csr6);
			iunlock(&ctlr->lock);
			csr32w(ctlr, 5, Tps);
			status &= ~(Unf|Tps);
		}

		ilock(&ctlr->tlock);
		while(ctlr->ntq){
			des = &ctlr->tdr[ctlr->tdri];
			if(des->status & Own)
				break;

			if(des->status & Es){
				if(des->status & Uf)
					ctlr->uf++;
				if(des->status & Ec)
					ctlr->ec++;
				if(des->status & Lc)
					ctlr->lc++;
				if(des->status & Nc)
					ctlr->nc++;
				if(des->status & Lo)
					ctlr->lo++;
				if(des->status & To)
					ctlr->to++;
				ether->oerrs++;
			}

			freeb(des->bp);
			des->control &= Er;

			ctlr->ntq--;
			ctlr->tdri = NEXT(ctlr->tdri, ctlr->ntdr);
		}
		txstart(ether);
		iunlock(&ctlr->tlock);

		/*
		 * Anything left not catered for?
		 */
		if(status)
			panic("#l%d: status %8.8uX\n", ether->ctlrno, status);
	}
}

static void
ctlrinit(Ether* ether)
{
	Ctlr *ctlr;
	Des *des;
	Block *bp;
	int i;
	uchar bi[Eaddrlen*2];

	ctlr = ether->ctlr;

	/*
	 * Allocate and initialise the receive ring;
	 * allocate and initialise the transmit ring;
	 * unmask interrupts and start the transmit side;
	 * create and post a setup packet to initialise
	 * the physical ethernet address.
	 */
	ctlr->rdr = xspanalloc(ctlr->nrdr*sizeof(Des), 8*sizeof(ulong), 0);
	for(des = ctlr->rdr; des < &ctlr->rdr[ctlr->nrdr]; des++){
		des->bp = iallocb(Rbsz);
		if(des->bp == nil)
			panic("can't allocate ethernet receive ring\n");
		des->status = Own;
		des->control = Rbsz;
		des->addr = PCIWADDR(des->bp->rp);
	}
	ctlr->rdr[ctlr->nrdr-1].control |= Er;
	ctlr->rdrx = 0;
	csr32w(ctlr, 3, PCIWADDR(ctlr->rdr));

	ctlr->tdr = xspanalloc(ctlr->ntdr*sizeof(Des), 8*sizeof(ulong), 0);
	ctlr->tdr[ctlr->ntdr-1].control |= Er;
	ctlr->tdrh = 0;
	ctlr->tdri = 0;
	csr32w(ctlr, 4, PCIWADDR(ctlr->tdr));

	/*
	 * Clear any bits in the Status Register (CSR5) as
	 * the PNIC has a different reset value from a true 2114x.
	 */
	ctlr->mask = Nis|Ais|Fbe|Rwt|Rps|Ru|Ri|Unf|Tjt|Tps|Ti;
	csr32w(ctlr, 5, ctlr->mask);
	csr32w(ctlr, 7, ctlr->mask);
	ctlr->csr6 |= St;
	csr32w(ctlr, 6, ctlr->csr6);

	for(i = 0; i < Eaddrlen/2; i++){
		bi[i*4] = ether->ea[i*2];
		bi[i*4+1] = ether->ea[i*2+1];
		bi[i*4+2] = ether->ea[i*2+1];
		bi[i*4+3] = ether->ea[i*2];
	}
	bp = iallocb(Eaddrlen*2*16);
	if(bp == nil)
		panic("can't allocate ethernet setup buffer\n");
	memset(bp->rp, 0xFF, sizeof(bi));
	for(i = sizeof(bi); i < sizeof(bi)*16; i += sizeof(bi))
		memmove(bp->rp+i, bi, sizeof(bi));
	bp->wp += sizeof(bi)*16;

	ctlr->setupbp = bp;
	ether->oq = qopen(256*1024, 1, 0, 0);
	transmit(ether);
}

static void
csr9w(Ctlr* ctlr, int data)
{
	csr32w(ctlr, 9, data);
	microdelay(1);
}

static int
miimdi(Ctlr* ctlr, int n)
{
	int data, i;

	/*
	 * Read n bits from the MII Management Register.
	 */
	data = 0;
	for(i = n-1; i >= 0; i--){
		if(csr32r(ctlr, 9) & Mdi)
			data |= (1<<i);
		csr9w(ctlr, Mii|Mdc);
		csr9w(ctlr, Mii);
	}
	csr9w(ctlr, 0);

	return data;
}

static void
miimdo(Ctlr* ctlr, int bits, int n)
{
	int i, mdo;

	/*
	 * Write n bits to the MII Management Register.
	 */
	for(i = n-1; i >= 0; i--){
		if(bits & (1<<i))
			mdo = Mdo;
		else
			mdo = 0;
		csr9w(ctlr, mdo);
		csr9w(ctlr, mdo|Mdc);
		csr9w(ctlr, mdo);
	}
}

static int
miir(Ctlr* ctlr, int phyad, int regad)
{
	int data, i;

	if(ctlr->id == Pnic){
		i = 1000;
		csr32w(ctlr, 20, 0x60020000|(phyad<<23)|(regad<<18));
		do{
			microdelay(1);
			data = csr32r(ctlr, 20);
		}while((data & 0x80000000) && --i);

		if(i == 0)
			return -1;
		return data & 0xFFFF;
	}

	/*
	 * Preamble;
	 * ST+OP+PHYAD+REGAD;
	 * TA + 16 data bits.
	 */
	miimdo(ctlr, 0xFFFFFFFF, 32);
	miimdo(ctlr, 0x1800|(phyad<<5)|regad, 14);
	data = miimdi(ctlr, 18);

	if(data & 0x10000)
		return -1;

	return data & 0xFFFF;
}

static void
miiw(Ctlr* ctlr, int phyad, int regad, int data)
{
	/*
	 * Preamble;
	 * ST+OP+PHYAD+REGAD+TA + 16 data bits;
	 * Z.
	 */
	miimdo(ctlr, 0xFFFFFFFF, 32);
	data &= 0xFFFF;
	data |= (0x05<<(5+5+2+16))|(phyad<<(5+2+16))|(regad<<(2+16))|(0x02<<16);
	miimdo(ctlr, data, 32);
	csr9w(ctlr, Mdc);
	csr9w(ctlr, 0);
}

static int
sromr(Ctlr* ctlr, int r)
{
	int i, op, data, size;

	if(ctlr->id == Pnic){
		i = 1000;
		csr32w(ctlr, 19, 0x600|r);
		do{
			microdelay(1);
			data = csr32r(ctlr, 19);
		}while((data & 0x80000000) && --i);

		if(ctlr->sromsz == 0)
			ctlr->sromsz = 6;

		return csr32r(ctlr, 9) & 0xFFFF;
	}

	/*
	 * This sequence for reading a 16-bit register 'r'
	 * in the EEPROM is taken straight from Section
	 * 7.4 of the 21140 Hardware Reference Manual.
	 */
reread:
	csr9w(ctlr, Rd|Ss);
	csr9w(ctlr, Rd|Ss|Scs);
	csr9w(ctlr, Rd|Ss|Sclk|Scs);
	csr9w(ctlr, Rd|Ss);

	op = 0x06;
	for(i = 3-1; i >= 0; i--){
		data = Rd|Ss|(((op>>i) & 0x01)<<2)|Scs;
		csr9w(ctlr, data);
		csr9w(ctlr, data|Sclk);
		csr9w(ctlr, data);
	}

	/*
	 * First time through must work out the EEPROM size.
	 */
	if((size = ctlr->sromsz) == 0)
		size = 8;

	for(size = size-1; size >= 0; size--){
		data = Rd|Ss|(((r>>size) & 0x01)<<2)|Scs;
		csr9w(ctlr, data);
		csr9w(ctlr, data|Sclk);
		csr9w(ctlr, data);
		microdelay(1);
		if(!(csr32r(ctlr, 9) & Sdo))
			break;
	}

	data = 0;
	for(i = 16-1; i >= 0; i--){
		csr9w(ctlr, Rd|Ss|Sclk|Scs);
		if(csr32r(ctlr, 9) & Sdo)
			data |= (1<<i);
		csr9w(ctlr, Rd|Ss|Scs);
	}

	csr9w(ctlr, 0);

	if(ctlr->sromsz == 0){
		ctlr->sromsz = 8-size;
		goto reread;
	}

	return data & 0xFFFF;
}

static void
softreset(Ctlr* ctlr)
{
	/*
	 * Soft-reset the controller and initialise bus mode.
	 * Delay should be >= 50 PCI cycles (2×S @ 25MHz).
	 */
	csr32w(ctlr, 0, Swr);
	microdelay(10);
	csr32w(ctlr, 0, Rml|Cal16);
	delay(1);
}

static int
type5block(Ctlr* ctlr, uchar* block)
{
	int csr15, i, len;

	/*
	 * Reset or GPR sequence. Reset should be once only,
	 * before the GPR sequence.
	 * Note 'block' is not a pointer to the block head but
	 * a pointer to the data in the block starting at the
	 * reset length value so type5block can be used for the
	 * sequences contained in type 1 and type 3 blocks.
	 * The SROM docs state the 21140 type 5 block is the
	 * same as that for the 21143, but the two controllers
	 * use different registers and sequence-element lengths
	 * so the 21140 code here is a guess for a real type 5
	 * sequence.
	 */
	len = *block++;
	if(ctlr->id != Tulip3){
		for(i = 0; i < len; i++){
			csr32w(ctlr, 12, *block);
			block++;
		}
		return len;
	}

	for(i = 0; i < len; i++){
		csr15 = *block++<<16;
		csr15 |= *block++<<24;
		csr32w(ctlr, 15, csr15);
		debug("%8.8uX ", csr15);
	}
	return 2*len;
}

static int
typephylink(Ctlr* ctlr, uchar*)
{
	int an, bmcr, bmsr, csr6, x;

	/*
	 * Fail if
	 *	auto-negotiataion enabled but not complete;
	 *	no valid link established.
	 */
	bmcr = miir(ctlr, ctlr->curphyad, Bmcr);
	miir(ctlr, ctlr->curphyad, Bmsr);
	bmsr = miir(ctlr, ctlr->curphyad, Bmsr);
	debug("bmcr 0x%2.2uX bmsr 0x%2.2uX\n", bmcr, bmsr);
	if(((bmcr & 0x1000) && !(bmsr & 0x0020)) || !(bmsr & 0x0004))
		return 0;

	if(bmcr & 0x1000){
		an = miir(ctlr, ctlr->curphyad, Anar);
		an &= miir(ctlr, ctlr->curphyad, Anlpar) & 0x3E0;
		debug("an 0x%2.uX 0x%2.2uX 0x%2.2uX\n",
	    		miir(ctlr, ctlr->curphyad, Anar),
			miir(ctlr, ctlr->curphyad, Anlpar),
			an);
	
		if(an & 0x0100)
			x = 0x4000;
		else if(an & 0x0080)
			x = 0x2000;
		else if(an & 0x0040)
			x = 0x1000;
		else if(an & 0x0020)
			x = 0x0800;
		else
			x = 0;
	}
	else if((bmcr & 0x2100) == 0x2100)
		x = 0x4000;
	else if(bmcr & 0x2000){
		/*
		 * If FD capable, force it if necessary.
		 */
		if((bmsr & 0x4000) && ctlr->fd){
			miiw(ctlr, ctlr->curphyad, Bmcr, 0x2100);
			x = 0x4000;
		}
		else
			x = 0x2000;
	}
	else if(bmcr & 0x0100)
		x = 0x1000;
	else
		x = 0x0800;

	csr6 = Sc|Mbo|Hbd|Ps|Ca|Sb|TrMODE;
	if(ctlr->fdx & x)
		csr6 |= Fd;
	if(ctlr->ttm & x)
		csr6 |= Ttm;
	debug("csr6 0x%8.8uX 0x%8.8uX 0x%8.8luX\n",
		csr6, ctlr->csr6, csr32r(ctlr, 6));
	if(csr6 != ctlr->csr6){
		ctlr->csr6 = csr6;
		csr32w(ctlr, 6, csr6);
	}

	return 1;
}

static int
typephymode(Ctlr* ctlr, uchar* block, int wait)
{
	uchar *p;
	int len, mc, nway, phyx, timeo;

	if(DEBUG){
		int i;

		len = (block[0] & ~0x80)+1;
		for(i = 0; i < len; i++)
			debug("%2.2uX ", block[i]);
		debug("\n");
	}

	if(block[1] == 1)
		len = 1;
	else if(block[1] == 3)
		len = 2;
	else
		return -1;

	/*
	 * Snarf the media capabilities, nway advertisment,
	 * FDX and TTM bitmaps.
	 */
	p = &block[5+len*block[3]+len*block[4+len*block[3]]];
	mc = *p++;
	mc |= *p++<<8;
	nway = *p++;
	nway |= *p++<<8;
	ctlr->fdx = *p++;
	ctlr->fdx |= *p++<<8;
	ctlr->ttm = *p++;
	ctlr->ttm |= *p<<8;
	debug("mc %4.4uX nway %4.4uX fdx %4.4uX ttm %4.4uX\n",
		mc, nway, ctlr->fdx, ctlr->ttm);
	USED(mc);

	phyx = block[2];
	ctlr->curphyad = ctlr->phy[phyx];

	ctlr->csr6 = 0;//Sc|Mbo|Hbd|Ps|Ca|Sb|TrMODE;
	//csr32w(ctlr, 6, ctlr->csr6);
	if(typephylink(ctlr, block))
		return 0;

	if(!(ctlr->phyreset & (1<<phyx))){
		debug("reset seq: len %d: ", block[3]);
		if(ctlr->type5block)
			type5block(ctlr, &ctlr->type5block[2]);
		else
			type5block(ctlr, &block[4+len*block[3]]);
		debug("\n");
		ctlr->phyreset |= (1<<phyx);
	}

	/*
	 * GPR sequence.
	 */
	debug("gpr seq: len %d: ", block[3]);
	type5block(ctlr, &block[3]);
	debug("\n");

	ctlr->csr6 = 0;//Sc|Mbo|Hbd|Ps|Ca|Sb|TrMODE;
	//csr32w(ctlr, 6, ctlr->csr6);
	if(typephylink(ctlr, block))
		return 0;

	/*
	 * Turn off auto-negotiation, set the auto-negotiation
	 * advertisment register then start the auto-negotiation
	 * process again.
	 */
	miiw(ctlr, ctlr->curphyad, Bmcr, 0);
	miiw(ctlr, ctlr->curphyad, Anar, nway|1);
	miiw(ctlr, ctlr->curphyad, Bmcr, 0x1000);

	if(!wait)
		return 0;

	for(timeo = 0; timeo < 30; timeo++){
		if(typephylink(ctlr, block))
			return 0;
		delay(100);
	}

	return -1;
}

static int
typesymmode(Ctlr *ctlr, uchar *block, int wait)
{
	uint gpmode, gpdata, command;

	USED(wait);
	gpmode = block[3] | ((uint) block[4] << 8);
	gpdata = block[5] | ((uint) block[6] << 8);
	command = (block[7] | ((uint) block[8] << 8)) & 0x71;
	if (command & 0x8000) {
		print("ether2114x.c: FIXME: handle type 4 mode blocks where cmd.active_invalid != 0\n");
		return -1;
	}
	csr32w(ctlr, 15, gpmode);
	csr32w(ctlr, 15, gpdata);
	ctlr->csr6 = (command & 0x71) << 18;
	csr32w(ctlr, 6, ctlr->csr6);
	return 0;
}

static int
type2mode(Ctlr* ctlr, uchar* block, int)
{
	uchar *p;
	int csr6, csr13, csr14, csr15, gpc, gpd;

	csr6 = Sc|Mbo|Ca|Sb|TrMODE;
	debug("type2mode: medium 0x%2.2uX\n", block[2]);

	/*
	 * Don't attempt full-duplex
	 * unless explicitly requested.
	 */
	if((block[2] & 0x3F) == 0x04){	/* 10BASE-TFD */
		if(!ctlr->fd)
			return -1;
		csr6 |= Fd;
	}

	/*
	 * Operating mode programming values from the datasheet
	 * unless media specific data is explicitly given.
	 */
	p = &block[3];
	if(block[2] & 0x40){
		csr13 = (block[4]<<8)|block[3];
		csr14 = (block[6]<<8)|block[5];
		csr15 = (block[8]<<8)|block[7];
		p += 6;
	}
	else switch(block[2] & 0x3F){
	default:
		return -1;
	case 0x00:			/* 10BASE-T */
		csr13 = 0x00000001;
		csr14 = 0x00007F3F;
		csr15 = 0x00000008;
		break;
	case 0x01:			/* 10BASE-2 */
		csr13 = 0x00000009;
		csr14 = 0x00000705;
		csr15 = 0x00000006;
		break;
	case 0x02:			/* 10BASE-5 (AUI) */
		csr13 = 0x00000009;
		csr14 = 0x00000705;
		csr15 = 0x0000000E;
		break;
	case 0x04:			/* 10BASE-TFD */
		csr13 = 0x00000001;
		csr14 = 0x00007F3D;
		csr15 = 0x00000008;
		break;
	}
	gpc = *p++<<16;
	gpc |= *p++<<24;
	gpd = *p++<<16;
	gpd |= *p<<24;

	csr32w(ctlr, 13, 0);
	csr32w(ctlr, 14, csr14);
	csr32w(ctlr, 15, gpc|csr15);
	delay(10);
	csr32w(ctlr, 15, gpd|csr15);
	csr32w(ctlr, 13, csr13);

	ctlr->csr6 = csr6;
	csr32w(ctlr, 6, ctlr->csr6);

	debug("type2mode: csr13 %8.8uX csr14 %8.8uX csr15 %8.8uX\n",
		csr13, csr14, csr15);
	debug("type2mode: gpc %8.8uX gpd %8.8uX csr6 %8.8uX\n",
		gpc, gpd, csr6);

	return 0;
}

static int
type0link(Ctlr* ctlr, uchar* block)
{
	int m, polarity, sense;

	m = (block[3]<<8)|block[2];
	sense = 1<<((m & 0x000E)>>1);
	if(m & 0x0080)
		polarity = sense;
	else
		polarity = 0;

	return (csr32r(ctlr, 12) & sense)^polarity;
}

static int
type0mode(Ctlr* ctlr, uchar* block, int wait)
{
	int csr6, m, timeo;

	csr6 = Sc|Mbo|Hbd|Ca|Sb|TrMODE;
debug("type0: medium 0x%uX, fd %d: 0x%2.2uX 0x%2.2uX 0x%2.2uX 0x%2.2uX\n",
    ctlr->medium, ctlr->fd, block[0], block[1], block[2], block[3]); 
	switch(block[0]){
	default:
		break;

	case 0x04:			/* 10BASE-TFD */
	case 0x05:			/* 100BASE-TXFD */
	case 0x08:			/* 100BASE-FXFD */
		/*
		 * Don't attempt full-duplex
		 * unless explicitly requested.
		 */
		if(!ctlr->fd)
			return -1;
		csr6 |= Fd;
		break;
	}

	m = (block[3]<<8)|block[2];
	if(m & 0x0001)
		csr6 |= Ps;
	if(m & 0x0010)
		csr6 |= Ttm;
	if(m & 0x0020)
		csr6 |= Pcs;
	if(m & 0x0040)
		csr6 |= Scr;

	csr32w(ctlr, 12, block[1]);
	microdelay(10);
	csr32w(ctlr, 6, csr6);
	ctlr->csr6 = csr6;

	if(!wait)
		return 0;

	for(timeo = 0; timeo < 30; timeo++){
		if(type0link(ctlr, block))
			return 0;
		delay(100);
	}

	return -1;
}

static int
mediaxx(Ether* ether, int wait)
{
	Ctlr* ctlr;
	uchar *block;

	ctlr = ether->ctlr;
	block = ctlr->infoblock[ctlr->curk];
	if(block[0] & 0x80){
		switch(block[1]){
		default:
			return -1;
		case 0:
			if(ctlr->medium >= 0 && block[2] != ctlr->medium)
				return 0;
/* need this test? */	if(ctlr->sct != 0x0800 && (ctlr->sct & 0x3F) != block[2])
				return 0;
			if(type0mode(ctlr, block+2, wait))
				return 0;
			break;
		case 1:
			if(typephymode(ctlr, block, wait))
				return 0;
			break;
		case 2:
			debug("type2: medium %d block[2] %d\n",
				ctlr->medium, block[2]);
			if(ctlr->medium >= 0 && ((block[2] & 0x3F) != ctlr->medium))
				return 0;
			if(type2mode(ctlr, block, wait))
				return 0;
			break;
		case 3:
			if(typephymode(ctlr, block, wait))
				return 0;
			break;
		case 4:
			debug("type4: medium %d block[2] %d\n",
				ctlr->medium, block[2]);
			if(ctlr->medium >= 0 && ((block[2] & 0x3F) != ctlr->medium))
				return 0;
			if(typesymmode(ctlr, block, wait))
				return 0;
			break;
		}
	}
	else{
		if(ctlr->medium >= 0 && block[0] != ctlr->medium)
			return 0;
/* need this test? */if(ctlr->sct != 0x0800 && (ctlr->sct & 0x3F) != block[0])
			return 0;
		if(type0mode(ctlr, block, wait))
			return 0;
	}

	if(ctlr->csr6){
		if(!(ctlr->csr6 & Ps) || (ctlr->csr6 & Ttm))
			return 10;
		return 100;
	}

	return 0;
}

static int
media(Ether* ether, int wait)
{
	Ctlr* ctlr;
	int k, mbps;

	ctlr = ether->ctlr;
	for(k = 0; k < ctlr->k; k++){
		mbps = mediaxx(ether, wait);
		if(mbps > 0)
			return mbps;
		if(ctlr->curk == 0)
			ctlr->curk = ctlr->k-1;
		else
			ctlr->curk--;
	}

	return 0;
}

static char* mediatable[9] = {
	"10BASE-T",				/* TP */
	"10BASE-2",				/* BNC */
	"10BASE-5",				/* AUI */
	"100BASE-TX",
	"10BASE-TFD",
	"100BASE-TXFD",
	"100BASE-T4",
	"100BASE-FX",
	"100BASE-FXFD",
};

static uchar en1207[] = {		/* Accton EN1207-COMBO */
	0x00, 0x00, 0xE8,		/* [0]  vendor ethernet code */
	0x00,				/* [3]  spare */

	0x00, 0x08,			/* [4]  connection (LSB+MSB = 0x0800) */
	0x1F,				/* [6]  general purpose control */
	2,				/* [7]  block count */

	0x00,				/* [8]  media code (10BASE-TX) */
	0x0B,				/* [9]  general purpose port data */
	0x9E, 0x00,			/* [10] command (LSB+MSB = 0x009E) */

	0x03,				/* [8]  media code (100BASE-TX) */
	0x1B,				/* [9]  general purpose port data */
	0x6D, 0x00,			/* [10] command (LSB+MSB = 0x006D) */

					/* There is 10BASE-2 as well, but... */
};

static uchar ana6910fx[] = {		/* Adaptec (Cogent) ANA-6910FX */
	0x00, 0x00, 0x92,		/* [0]  vendor ethernet code */
	0x00,				/* [3]  spare */

	0x00, 0x08,			/* [4]  connection (LSB+MSB = 0x0800) */
	0x3F,				/* [6]  general purpose control */
	1,				/* [7]  block count */

	0x07,				/* [8]  media code (100BASE-FX) */
	0x03,				/* [9]  general purpose port data */
	0x2D, 0x00			/* [10] command (LSB+MSB = 0x000D) */
};

static uchar smc9332[] = {		/* SMC 9332 */
	0x00, 0x00, 0xC0,		/* [0]  vendor ethernet code */
	0x00,				/* [3]  spare */

	0x00, 0x08,			/* [4]  connection (LSB+MSB = 0x0800) */
	0x1F,				/* [6]  general purpose control */
	2,				/* [7]  block count */

	0x00,				/* [8]  media code (10BASE-TX) */
	0x00,				/* [9]  general purpose port data */
	0x9E, 0x00,			/* [10] command (LSB+MSB = 0x009E) */

	0x03,				/* [8]  media code (100BASE-TX) */
	0x09,				/* [9]  general purpose port data */
	0x6D, 0x00,			/* [10] command (LSB+MSB = 0x006D) */
};

static uchar* leaf21140[] = {
	en1207,				/* Accton EN1207-COMBO */
	ana6910fx,			/* Adaptec (Cogent) ANA-6910FX */
	smc9332,			/* SMC 9332 */
	nil,
};

/*
 * Copied to ctlr->srom at offset 20.
 */
static uchar leafpnic[] = {
	0x00, 0x00, 0x00, 0x00,		/* MAC address */
	0x00, 0x00,
	0x00,				/* controller 0 device number */
	0x1E, 0x00,			/* controller 0 info leaf offset */
	0x00,				/* reserved */
	0x00, 0x08,			/* selected connection type */
	0x00,				/* general purpose control */
	0x01,				/* block count */

	0x8C,				/* format indicator and count */
	0x01,				/* block type */
	0x00,				/* PHY number */
	0x00,				/* GPR sequence length */
	0x00,				/* reset sequence length */
	0x00, 0x78,			/* media capabilities */
	0xE0, 0x01,			/* Nway advertisment */
	0x00, 0x50,			/* FDX bitmap */
	0x00, 0x18,			/* TTM bitmap */
};

static int
srom(Ctlr* ctlr)
{
	int i, k, oui, phy, x;
	uchar *p;

	/*
	 * This is a partial decoding of the SROM format described in
	 * 'Digital Semiconductor 21X4 Serial ROM Format, Version 4.05,
	 * 2-Mar-98'. Only the 2114[03] are handled, support for other
	 * controllers can be added as needed.
	 * Do a dummy read first to get the size and allocate ctlr->srom.
	 */
	sromr(ctlr, 0);
	if(ctlr->srom == nil)
		ctlr->srom = malloc((1<<ctlr->sromsz)*sizeof(ushort));
	for(i = 0; i < (1<<ctlr->sromsz); i++){
		x = sromr(ctlr, i);
		ctlr->srom[2*i] = x;
		ctlr->srom[2*i+1] = x>>8;
	}

	/*
	 * There are 2 SROM layouts:
	 *	e.g. Digital EtherWORKS	station address at offset 20;
	 *				this complies with the 21140A SROM
	 *				application note from Digital;
	 * 	e.g. SMC9332		station address at offset 0 followed by
	 *				2 additional bytes, repeated at offset
	 *				6; the 8 bytes are also repeated in
	 *				reverse order at offset 8.
	 * To check which it is, read the SROM and check for the repeating
	 * patterns of the non-compliant cards; if that fails use the one at
	 * offset 20.
	 */
	ctlr->sromea = ctlr->srom;
	for(i = 0; i < 8; i++){
		x = ctlr->srom[i];
		if(x != ctlr->srom[15-i] || x != ctlr->srom[16+i]){
			ctlr->sromea = &ctlr->srom[20];
			break;
		}
	}

	/*
	 * Fake up the SROM for the PNIC.
	 * It looks like a 21140 with a PHY.
	 * The MAC address is byte-swapped in the orginal SROM data.
	 */
	if(ctlr->id == Pnic){
		memmove(&ctlr->srom[20], leafpnic, sizeof(leafpnic));
		for(i = 0; i < Eaddrlen; i += 2){
			ctlr->srom[20+i] = ctlr->srom[i+1];
			ctlr->srom[20+i+1] = ctlr->srom[i];
		}
	}

	/*
	 * Next, try to find the info leaf in the SROM for media detection.
	 * If it's a non-conforming card try to match the vendor ethernet code
	 * and point p at a fake info leaf with compact 21140 entries.
	 */
	if(ctlr->sromea == ctlr->srom){
		p = nil;
		for(i = 0; leaf21140[i] != nil; i++){
			if(memcmp(leaf21140[i], ctlr->sromea, 3) == 0){
				p = &leaf21140[i][4];
				break;
			}
		}
		if(p == nil)
			return -1;
	}
	else
		p = &ctlr->srom[(ctlr->srom[28]<<8)|ctlr->srom[27]];

	/*
	 * Set up the info needed for later media detection.
	 * For the 21140, set the general-purpose mask in CSR12.
	 * The info block entries are stored in order of increasing
	 * precedence, so detection will work backwards through the
	 * stored indexes into ctlr->srom.
	 * If an entry is found which matches the selected connection
	 * type, save the index. Otherwise, start at the last entry.
	 * If any MII entries are found (type 1 and 3 blocks), scan
	 * for PHYs.
	 */
	ctlr->leaf = p;
	ctlr->sct = *p++;
	ctlr->sct |= *p++<<8;
	if(ctlr->id != Tulip3){
		csr32w(ctlr, 12, Gpc|*p++);
		delay(200);
	}
	ctlr->k = *p++;
	if(ctlr->k >= nelem(ctlr->infoblock))
		ctlr->k = nelem(ctlr->infoblock)-1;
	ctlr->sctk = ctlr->k-1;
	phy = 0;
	for(k = 0; k < ctlr->k; k++){
		ctlr->infoblock[k] = p;
		/*
		 * The RAMIX PMC665 has a badly-coded SROM,
		 * hence the test for 21143 and type 3.
		 */
		if((*p & 0x80) || (ctlr->id == Tulip3 && *(p+1) == 3)){
			*p |= 0x80;
			if(*(p+1) == 1 || *(p+1) == 3)
				phy = 1;
			if(*(p+1) == 5)
				ctlr->type5block = p;
			p += (*p & ~0x80)+1;
		}
		else{
			debug("type0: 0x%2.2uX 0x%2.2uX 0x%2.2uX 0x%2.2uX\n",
				p[0], p[1], p[2], p[3]); 
			if(ctlr->sct != 0x0800 && *p == (ctlr->sct & 0xFF))
				ctlr->sctk = k;
			p += 4;
		}
	}
	ctlr->curk = ctlr->sctk;
	debug("sct 0x%uX medium 0x%uX k %d curk %d phy %d\n",
		ctlr->sct, ctlr->medium, ctlr->k, ctlr->curk, phy);

	if(phy){
		x = 0;
		for(k = 0; k < nelem(ctlr->phy); k++){
			if((oui = miir(ctlr, k, 2)) == -1 || oui == 0)
				continue;
			if(DEBUG){
				oui = (oui & 0x3FF)<<6;
				oui |= miir(ctlr, k, 3)>>10;
				miir(ctlr, k, 1);
				debug("phy%d: index %d oui %uX reg1 %uX\n",
					x, k, oui, miir(ctlr, k, 1));
				USED(oui);
			}
			ctlr->phy[x] = k;
		}
	}

	ctlr->fd = 0;
	ctlr->medium = -1;

	return 0;
}

static void
dec2114xpci(void)
{
	Ctlr *ctlr;
	Pcidev *p;
	int x;

	p = nil;
	while(p = pcimatch(p, 0, 0)){
		if(p->ccrb != 0x02 || p->ccru != 0)
			continue;
		switch((p->did<<16)|p->vid){
		default:
			continue;

		case Tulip3:			/* 21143 */
			/*
			 * Exit sleep mode.
			 */
			x = pcicfgr32(p, 0x40);
			x &= ~0xc0000000;
			pcicfgw32(p, 0x40, x);
			/*FALLTHROUGH*/

		case Pnic:			/* PNIC */
		case Pnic2:			/* PNIC-II */
		case Tulip0:			/* 21140 */
			break;
		}

		/*
		 * bar[0] is the I/O port register address and
		 * bar[1] is the memory-mapped register address.
		 */
		ctlr = malloc(sizeof(Ctlr));
		ctlr->port = p->mem[0].bar & ~0x01;
		ctlr->pcidev = p;
		ctlr->id = (p->did<<16)|p->vid;

		if(ioalloc(ctlr->port, p->mem[0].size, 0, "dec2114x") < 0){
			print("dec2114x: port 0x%uX in use\n", ctlr->port);
			free(ctlr);
			continue;
		}

		/*
		 * Some cards (e.g. ANA-6910FX) seem to need the Ps bit
		 * set or they don't always work right after a hardware
		 * reset.
		 */
		csr32w(ctlr, 6, Mbo|Ps);
		softreset(ctlr);

		if(srom(ctlr)){
			iofree(ctlr->port);
			free(ctlr);
			continue;
		}

		switch(ctlr->id){
		default:
			break;

		case Pnic:			/* PNIC */
			/*
			 * Turn off the jabber timer.
			 */
			csr32w(ctlr, 15, 0x00000001);
			break;
		}

		if(ctlrhead != nil)
			ctlrtail->next = ctlr;
		else
			ctlrhead = ctlr;
		ctlrtail = ctlr;
	}
}

static int
reset(Ether* ether)
{
	Ctlr *ctlr;
	int i, x;
	uchar ea[Eaddrlen];
	static int scandone;

	if(scandone == 0){
		dec2114xpci();
		scandone = 1;
	}

	/*
	 * Any adapter matches if no ether->port is supplied,
	 * otherwise the ports must match.
	 */
	for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){
		if(ctlr->active)
			continue;
		if(ether->port == 0 || ether->port == ctlr->port){
			ctlr->active = 1;
			break;
		}
	}
	if(ctlr == nil)
		return -1;

	ether->ctlr = ctlr;
	ether->port = ctlr->port;
	ether->irq = ctlr->pcidev->intl;
	ether->tbdf = ctlr->pcidev->tbdf;

	/*
	 * Check if the adapter's station address is to be overridden.
	 * If not, read it from the EEPROM and set in ether->ea prior to
	 * loading the station address in the hardware.
	 */
	memset(ea, 0, Eaddrlen);
	if(memcmp(ea, ether->ea, Eaddrlen) == 0)
		memmove(ether->ea, ctlr->sromea, Eaddrlen);

	/*
	 * Look for a medium override in case there's no autonegotiation
	 * (no MII) or the autonegotiation fails.
	 */
	for(i = 0; i < ether->nopt; i++){
		if(cistrcmp(ether->opt[i], "FD") == 0){
			ctlr->fd = 1;
			continue;
		}
		for(x = 0; x < nelem(mediatable); x++){
			debug("compare <%s> <%s>\n", mediatable[x],
				ether->opt[i]);
			if(cistrcmp(mediatable[x], ether->opt[i]))
				continue;
			ctlr->medium = x;
	
			switch(ctlr->medium){
			default:
				ctlr->fd = 0;
				break;
	
			case 0x04:		/* 10BASE-TFD */
			case 0x05:		/* 100BASE-TXFD */
			case 0x08:		/* 100BASE-FXFD */
				ctlr->fd = 1;
				break;
			}
			break;
		}
	}

	ether->mbps = media(ether, 1);

	/*
	 * Initialise descriptor rings, ethernet address.
	 */
	ctlr->nrdr = Nrde;
	ctlr->ntdr = Ntde;
	pcisetbme(ctlr->pcidev);
	ctlrinit(ether);

	/*
	 * Linkage to the generic ethernet driver.
	 */
	ether->attach = attach;
	ether->transmit = transmit;
	ether->interrupt = interrupt;
	ether->ifstat = ifstat;

	ether->arg = ether;
	ether->promiscuous = promiscuous;

	return 0;
}

void
ether2114xlink(void)
{
	addethercard("21140",  reset);
	addethercard("2114x",  reset);
}

A mtx/etherif.h => mtx/etherif.h +36 -0
@@ 0,0 1,36 @@
enum {
	MaxEther	= 24,
	Ntypes		= 8,
};

typedef struct Ether Ether;
struct Ether {
	ISAConf;			/* hardware info */

	int	ctlrno;
	int	tbdf;			/* type+busno+devno+funcno */
	int	mbps;			/* Mbps */
	int	minmtu;
	int 	maxmtu;
	uchar	ea[Eaddrlen];

	void	(*attach)(Ether*);	/* filled in by reset routine */
	void	(*transmit)(Ether*);
	void	(*interrupt)(Ureg*, void*);
	long	(*ifstat)(Ether*, void*, long, ulong);
	long 	(*ctl)(Ether*, void*, long); /* custom ctl messages */
	void	*ctlr;

	Queue*	oq;

	Netif;
};

extern Block* etheriq(Ether*, Block*, int);
extern void addethercard(char*, int(*)(Ether*));
extern ulong ethercrc(uchar*, int);

#define NEXT(x, l)	(((x)+1)%(l))
#define PREV(x, l)	(((x) == 0) ? (l)-1: (x)-1)
#define	HOWMANY(x, y)	(((x)+((y)-1))/(y))
#define ROUNDUP(x, y)	(HOWMANY((x), (y))*(y))

M mtx/main.c => mtx/main.c +11 -3
@@ 80,6 80,7 @@ static struct
plan9ini[] =
{
	{ "console", "0" },
	{ "ether0", "type=2114x" },
};

char*


@@ 115,11 116,11 @@ init0(void)
	chandevinit();

	if(!waserror()){
		ksetenv("cputype", "power");
		ksetenv("cputype", "power", 0);
		if(cpuserver)
			ksetenv("service", "cpu");
			ksetenv("service", "cpu", 0);
		else
			ksetenv("service", "terminal");
			ksetenv("service", "terminal", 0);
		
/*
		for(p = confenv; *p; p++) {


@@ 195,6 196,13 @@ userinit(void)
	ready(p);
}

/* still to do */
void
reboot(void*, void*, ulong)
{
	exit(0);
}

void
exit(int ispanic)
{

M mtx/trap.c => mtx/trap.c +3 -0
@@ 24,6 24,7 @@ hwintrenable(Vctl *v)

	irq = v->irq;
	if(BUSTYPE(v->tbdf) == BusPCI) {	/* MPIC? */
print("add PCI intr %d\n", irq);
		if(irq > 15) {
			print("intrenable: pci irq %d out of range\n", v->irq);
			return -1;


@@ 32,6 33,7 @@ hwintrenable(Vctl *v)
		mpicenable(vec, v);
	}
	else {
print("add pic intr %d\n", irq);
		if(irq > MaxIrqPIC) {
			print("intrenable: irq %d out of range\n", v->irq);
			return -1;


@@ 348,6 350,7 @@ intr(Ureg *ureg)
		vno = i8259intack();
		mpiceoi(0);
	}
else print("non-pic %d\n", vno);

	if(vno > nelem(vctl) || (ctl = vctl[vno]) == 0) {
		panic("spurious intr %d", vno);

M pc/apbootstrap.s => pc/apbootstrap.s +3 -6
@@ 15,8 15,6 @@
#define INVD		BYTE $0x0F; BYTE $0x08
#define WBINVD		BYTE $0x0F; BYTE $0x09

#define PADDR(a)	((a) & ~KZERO)

/*
 * Macros for calculating offsets within the page directory base
 * and page tables. Note that these are assembler-specific hence


@@ 64,9 62,8 @@ TEXT _apbootstrap(SB), $0			/* address APBOOTSTRAP+0x14 */
	FARJUMP32(SELECTOR(2, SELGDT, 0), _ap32-KZERO(SB))

/*
 * There's a little mystery here - the Pentium Pro appears to need an identity
 * mmu map for the switch to virtual mode. The manual doesn't say this is necessary
 * and it isn't required on the Pentium.
 * For Pentiums and higher, the code that enables paging must come from
 * pages that are identity mapped. 
 * To this end double map KZERO at virtual 0 and undo the mapping once virtual
 * nirvana has been obtained.
 */


@@ 109,5 106,5 @@ TEXT gdt(SB), $0
	LONG $0xFFFF; LONG $(SEGG|SEGB|(0xF<<16)|SEGP|SEGPL(0)|SEGDATA|SEGW)
	LONG $0xFFFF; LONG $(SEGG|SEGD|(0xF<<16)|SEGP|SEGPL(0)|SEGEXEC|SEGR)
TEXT gdtptr(SB), $0
	WORD	$(3*8)
	WORD	$(3*8-1)
	LONG	$gdt-KZERO(SB)

M pc/apm.c => pc/apm.c +0 -14
@@ 64,19 64,6 @@ setgdt(int sel, ulong base, ulong limit, int flag)
			((base>>16)&0xFF) | SEGP | SEGPL(0) | flag;
}

static void
loadgdt(void)
{
	ulong x;
	ushort ptr[3];

	ptr[0] = sizeof(m->gdt);
	x = (ulong)m->gdt;
	ptr[1] = x & 0xFFFF;
	ptr[2] = (x>>16) & 0xFFFF;
	lgdt(ptr);
}

static	ulong ax, cx, dx, di, ebx, esi;
static Ureg apmu;
static long


@@ 154,7 141,6 @@ apmlink(void)
	setgdt(APMCSEG, ax<<4, ((esi&0xFFFF)-1)&0xFFFF, SEGEXEC|SEGR|SEGD);
	setgdt(APMCSEG16, cx<<4, ((esi>>16)-1)&0xFFFF, SEGEXEC|SEGR);
	setgdt(APMDSEG, dx<<4, (di-1)&0xFFFF, SEGDATA|SEGW|SEGD);
	loadgdt();

	addarchfile("apm", 0660, apmread, apmwrite);


M pc/dat.h => pc/dat.h +1 -1
@@ 84,7 84,7 @@ struct Conf
	ulong	copymode;	/* 0 is copy on write, 1 is copy on reference */
	ulong	ialloc;		/* max interrupt time allocation in bytes */
	ulong	pipeqsize;	/* size in bytes of pipe queues */
	int	nuart;	/* number of uart devices */
	int	nuart;		/* number of uart devices */
};

/*

M pc/devarch.c => pc/devarch.c +1 -0
@@ 461,6 461,7 @@ Dev archdevtab = {

	devreset,
	devinit,
	devshutdown,
	archattach,
	archwalk,
	archstat,

M pc/devether.c => pc/devether.c +20 -0
@@ 425,6 425,25 @@ etherreset(void)
		free(ether);
}

static void
ethershutdown(void)
{
	Ether *ether;
	int i;

	for(i = 0; i < MaxEther; i++){
		ether = etherxx[i];
		if(ether == nil)
			continue;
		if(ether->shutdown == nil) {
			print("#l%d: no shutdown fuction\n", i);
			continue;
		}
		(*ether->shutdown)(ether);
	}
}


#define POLY 0xedb88320

/* really slow 32 bit crc for ethers */


@@ 451,6 470,7 @@ Dev etherdevtab = {

	etherreset,
	devinit,
	ethershutdown,
	etherattach,
	etherwalk,
	etherstat,

M pc/devfloppy.c => pc/devfloppy.c +1 -0
@@ 1061,6 1061,7 @@ Dev floppydevtab = {

	floppyreset,
	devinit,
	devshutdown,
	floppyattach,
	floppywalk,
	floppystat,

M pc/devi82365.c => pc/devi82365.c +1 -0
@@ 870,6 870,7 @@ Dev i82365devtab = {

	devreset,
	devinit,
	devshutdown,
	i82365attach,
	i82365walk,
	i82365stat,

M pc/devlml.c => pc/devlml.c +2 -1
@@ 318,7 318,8 @@ Dev lmldevtab = {
	"video",

	lmlreset,
	devinit,
	devinit,	
	devshutdown,
	lmlattach,
	lmlwalk,
	lmlstat,

M pc/devlpt.c => pc/devlpt.c +1 -0
@@ 229,6 229,7 @@ Dev lptdevtab = {

	devreset,
	devinit,
	devshutdown,
	lptattach,
	lptwalk,
	lptstat,

M pc/devpccard.c => pc/devpccard.c +2 -1
@@ 1367,7 1367,8 @@ Dev pccarddevtab = {
	"cardbus",

	devreset,
	devinit,
	devinit,	
	devshutdown,
	pccardattach,
	pccardwalk,
	pccardstat,

M pc/devrtc.c => pc/devrtc.c +1 -0
@@ 308,6 308,7 @@ Dev rtcdevtab = {

	devreset,
	rtcinit,
	devshutdown,
	rtcattach,
	rtcwalk,
	rtcstat,

M pc/devusb.c => pc/devusb.c +1 -0
@@ 2307,6 2307,7 @@ Dev usbdevtab = {

	usbreset,
	usbinit,
	devshutdown,
	usbattach,
	usbwalk,
	usbstat,

M pc/devvga.c => pc/devvga.c +1 -0
@@ 447,6 447,7 @@ Dev vgadevtab = {

	vgareset,
	devinit,
	devshutdown,
	vgaattach,
	vgawalk,
	vgastat,

M pc/ether82543gc.c => pc/ether82543gc.c +8 -0
@@ 861,6 861,13 @@ gc82543detach(Ctlr* ctlr)
		;
}

static void
gc82543shutdown(Ether* ether)
{
print("gc82543shutdown\n");
	gc82543detach(ether->ctlr);
}

static int
gc82543reset(Ctlr* ctlr)
{


@@ 994,6 1001,7 @@ gc82543pnp(Ether* edev)
	edev->transmit = gc82543transmit;
	edev->interrupt = gc82543interrupt;
	edev->ifstat = gc82543ifstat;
	edev->shutdown = gc82543shutdown;

	edev->arg = edev;
	edev->promiscuous = nil;

M pc/ether82557.c => pc/ether82557.c +13 -0
@@ 973,6 973,18 @@ scanphy(Ctlr* ctlr)
	return -1;
}

static void
shutdown(Ether* ether)
{
	Ctlr *ctlr = ether->ctlr;

print("ether82557 shutting down\n");
	csr32w(ctlr, Port, 0);
	delay(1);
	csr8w(ctlr, Interrupt, InterruptM);
}


static int
reset(Ether* ether)
{


@@ 1247,6 1259,7 @@ reset(Ether* ether)
	ether->transmit = transmit;
	ether->interrupt = interrupt;
	ether->ifstat = ifstat;
	ether->shutdown = shutdown;

	ether->promiscuous = promiscuous;
	ether->multicast = multicast;

M pc/etherif.h => pc/etherif.h +1 -0
@@ 19,6 19,7 @@ struct Ether {
	void	(*interrupt)(Ureg*, void*);
	long	(*ifstat)(Ether*, void*, long, ulong);
	long 	(*ctl)(Ether*, void*, long); /* custom ctl messages */
	void	(*shutdown)(Ether*);	/* shutdown hardware before reboot */
	void	*ctlr;

	Queue*	oq;

M pc/floppy.h => pc/floppy.h +0 -1
@@ 164,7 164,6 @@ floppysetup1(FController *fl)
		fl->d[1].dt = equip & 0xf;
		floppysetdef(&fl->d[1]);
	}

	intrenable(IrqFLOPPY, pcfloppyintr, fl, BUSUNKNOWN, "floppy");
}


M pc/fns.h => pc/fns.h +2 -2
@@ 1,7 1,6 @@
#include "../port/portfns.h"

void	aamloop(int);
void	addconf(char*, char*);
Dirtab*	addarchfile(char*, int, long(*)(Chan*,void*,long,vlong), long(*)(Chan*,void*,long,vlong));
//void	addscsilink(char*, Scsiio (*)(int, ISAConf*));
void	archinit(void);


@@ 74,8 73,9 @@ void	lgdt(ushort[3]);
void	lidt(ushort[3]);
void	links(void);
void	ltr(ulong);
void	mach0init(void);
void	mathinit(void);
void	meminit(ulong);
void	meminit(void);
#define mmuflushtlb(pdb) putcr3(pdb)
void	mmuinit(void);
ulong	mmukmap(ulong, ulong, int);

A pc/initcode.s => pc/initcode.s +23 -0
@@ 0,0 1,23 @@
#include "/sys/src/libc/9syscall/sys.h"

TEXT	main(SB),$0

	/*
	 *  exec("/boot", bootv)
	 */
	LEAL	4(SP),AX
	PUSHL	AX
	LEAL	boot(SB),AX
	PUSHL	AX
	PUSHL	$0
	MOVL	$EXEC,AX
	INT	$64

	/*
	 *  should never get here
	 */
here:
	JMP	here

GLOBL	boot+0(SB),$6
DATA	boot+0(SB)/5,$"/boot"

M pc/l.s => pc/l.s +27 -34
@@ 24,29 24,29 @@
#define PTO(a)		(((((a))>>12) & 0x03FF)<<2)

/*
 * Entered here from the bootstrap programme possibly via a jump to 0x00100020, so
 * need to make another jump to set the correct virtual address.
 * In protected mode with paging turned on, the first 4MB of physical memory mapped
 * to KZERO and up.
 * For backwards compatiblity with 9load - should go away when 9load is changed
 * 9load currently sets up the mmu, however the first 16MB of memory is identity
 * mapped, so behave as if the mmu was not setup
 */
TEXT _start0x00100020(SB),$0
	CLI
	MOVL	$_start0x80100020(SB), AX
TEXT _start0x80100020(SB),$0
	MOVL	$_start0x00100020(SB), AX
	ANDL	$~KZERO, AX
	JMP*	AX

/*
 * First check if the bootstrap programme left the first 4MB nicely mapped, otherwise
 * make the basic page tables for processor 0. Four pages are needed for the basic set:
 * a page directory, a page table for mapping the first 4MB of physical memory, and
 * virtual and physical pages for mapping the Mach structure.
 * In protected mode with paging turned off and segment registers setup to linear map all memory.
 * Entered via a jump to 0x00100020, the physical address of the virtual kernel entry point of 0x80100020
 * Make the basic page tables for processor 0. Four pages are needed for the basic set:
 * a page directory, a page table for mapping the first 4MB of physical memory to KZERO,
 * and virtual and physical pages for mapping the Mach structure.
 * The remaining PTEs will be allocated later when memory is sized.
 * An identity mmu map is also needed for the switch to virtual mode.  This
 * identity mapping is removed once the MMU is going and the JMP has been made
 * to virtual memory.
 */
TEXT _start0x80100020(SB), $0
	MOVL	CR3, AX				/* check the page directory base */
	CMPL	AX, $PADDR(CPU0PDB)
	JEQ	_clearbss
TEXT _start0x00100020(SB),$0
	CLI					/* make sure interrupts are off */

	MOVL	$CPU0PDB, DI			/* clear 4 pages for the tables etc. */
	MOVL	$PADDR(CPU0PDB), DI		/* clear 4 pages for the tables etc. */
	XORL	AX, AX
	MOVL	$(4*BY2PG), CX
	SHRL	$2, CX


@@ 54,13 54,13 @@ TEXT _start0x80100020(SB), $0
	CLD
	REP;	STOSL

	MOVL	$CPU0PDB, AX
	MOVL	$PADDR(CPU0PDB), AX
	ADDL	$PDO(KZERO), AX			/* page directory offset for KZERO */
	MOVL	$PADDR(CPU0PTE), (AX)		/* PTE's for 0x80000000 */
	MOVL	$(PTEWRITE|PTEVALID), BX	/* page permissions */
	ORL	BX, (AX)

	MOVL	$CPU0PTE, AX			/* first page of page table */
	MOVL	$PADDR(CPU0PTE), AX		/* first page of page table */
	MOVL	$1024, CX			/* 1024 pages in 4MB */
_setpte:
	MOVL	BX, (AX)


@@ 68,7 68,7 @@ _setpte:
	ADDL	$4, AX
	LOOP	_setpte

	MOVL	$CPU0PTE, AX
	MOVL	$PADDR(CPU0PTE), AX
	ADDL	$PTO(MACHADDR), AX		/* page table entry offset for MACHADDR */
	MOVL	$PADDR(CPU0MACH), (AX)		/* PTE for Mach */
	MOVL	$(PTEWRITE|PTEVALID), BX	/* page permissions */


@@ 76,18 76,12 @@ _setpte:

/*
 * Now ready to use the new map. Make sure the processor options are what is wanted.
 * It is necessary on some processors to follow mode switching with a JMP instruction
 * It is necessary on some processors to immediately follow mode switching with a JMP instruction
 * to clear the prefetch queues.
 * There's a little mystery here - the Pentium Pro appears to need an identity
 * mmu map for the switch to virtual mode. The manual doesn't say this is necessary
 * and it isn't required on the Pentium.
 * To this end double map KZERO at virtual 0 and undo the mapping once virtual
 * nirvana has been attained.
 */
	MOVL	$PADDR(CPU0PDB), CX		/* load address of page directory */
	MOVL	CX, BX
	MOVL	(PDO(KZERO))(BX), DX		/* double-map KZERO at 0 */
	MOVL	DX, (PDO(0))(BX)
	MOVL	(PDO(KZERO))(CX), DX		/* double-map KZERO at 0 */
	MOVL	DX, (PDO(0))(CX)
	MOVL	CX, CR3
	DELAY					/* JMP .+2 */



@@ 95,9 89,9 @@ _setpte:
	ORL	$0x80010000, DX			/* PG|WP */
	ANDL	$~0x6000000A, DX		/* ~(CD|NW|TS|MP) */

	MOVL	$_startpg(SB), AX
	MOVL	$_startpg(SB), AX		/* this is a virtual address */
	MOVL	DX, CR0				/* turn on paging */
	JMP*	AX
	JMP*	AX				/* jump to the virtual nirvana */

/*
 * Basic machine environment set, can clear BSS and create a stack.


@@ 107,9 101,7 @@ _setpte:
 * be initialised here.
 */
TEXT _startpg(SB), $0
	MOVL	CX, AX				/* physical address of PDB */
	ORL	$KZERO, AX
	MOVL	$0, (PDO(0))(AX)		/* undo double-map of KZERO at 0 */
	MOVL	$0, (PDO(0))(CX)		/* undo double-map of KZERO at 0 */
	MOVL	CX, CR3				/* load and flush the mmu */

_clearbss:


@@ 126,6 118,7 @@ _clearbss:
	MOVL	SP, m(SB)			/* initialise global Mach pointer */
	MOVL	$0, 0(SP)			/* initialise m->machno */


	ADDL	$(MACHSIZE-4), SP		/* initialise stack */

/*

M pc/main.c => pc/main.c +135 -80
@@ 7,27 7,28 @@
#include	"ureg.h"
#include	"init.h"
#include	"pool.h"
#include	"reboot.h"

Mach *m;

static  uchar *sp;	/* stack pointer for /boot */

/*
 * Where configuration info is left for the loaded programme.
 * This will turn into a structure as more is done by the boot loader
 * (e.g. why parse the .ini file twice?).
 * There are XXX bytes available at CONFADDR.
 * There are 3584 bytes available at CONFADDR.
 */
#define BOOTLINE	((char*)CONFADDR)
#define BOOTLINELEN	64
#define BOOTARGS	((char*)(CONFADDR+BOOTLINELEN))
#define	BOOTARGSLEN	(4096-0x200-BOOTLINELEN)
#define	MAXCONF		32
#define	MAXCONF		64

char bootdisk[KNAMELEN];
Conf	conf;
char *confname[MAXCONF];
char *confval[MAXCONF];
int nconf;
uchar	*sp;	/* user stack of init proc */

static void
options(void)


@@ 71,18 72,16 @@ options(void)
void
main(void)
{
	conf.nmach = 1;
	MACHP(0) = (Mach*)CPU0MACH;
	m->pdb = (ulong*)CPU0PDB;
	machinit();
	active.machs = 1;
	active.exiting = 0;
	mach0init();
	options();
	ioinit();
	i8250console();

	print("\nPlan 9\n");

	cpuidentify();
	screeninit();
	meminit();
	confinit();
	archinit();
	xinit();


@@ 108,6 107,19 @@ conf.monitor = 1;
}

void
mach0init(void)
{
	conf.nmach = 1;
	MACHP(0) = (Mach*)CPU0MACH;
	m->pdb = (ulong*)CPU0PDB;

	machinit();

	active.machs = 1;
	active.exiting = 0;
}

void
machinit(void)
{
	int machno;


@@ 129,19 141,10 @@ machinit(void)
}

void
ksetterm(char *f)
{
	char buf[2*KNAMELEN];

	sprint(buf, f, conffile);
	ksetenv("terminal", buf);
}

void
init0(void)
{
	int i;
	char tstr[32];
	char buf[2*KNAMELEN];

	up->nerrlab = 0;



@@ 159,17 162,15 @@ init0(void)
	chandevinit();

	if(!waserror()){
		strcpy(tstr, arch->id);
		strcat(tstr, " %s");
		ksetterm(tstr);
		ksetenv("cputype", "386");
		snprint(buf, sizeof(buf), "%s %s", arch->id, conffile);
		ksetenv("terminal", buf, 0);
		ksetenv("cputype", "386", 0);
		if(cpuserver)
			ksetenv("service", "cpu");
			ksetenv("service", "cpu", 0);
		else
			ksetenv("service", "terminal");
			ksetenv("service", "terminal", 0);
		for(i = 0; i < nconf; i++)
			if(confname[i] && confname[i][0] != '*')
				ksetenv(confname[i], confval[i]);
			ksetenv(confname[i], confval[i], 1);
		poperror();
	}
	kproc("alarm", alarmkproc, 0);


@@ 260,6 261,8 @@ bootargs(ulong base)

	ac = 0;
	av[ac++] = pusharg("/386/9dos");

	/* when boot is changed to only use rc, this code can go away */
	cp[BOOTLINELEN-1] = 0;
	buf[0] = 0;
	if(strncmp(cp, "fd", 2) == 0){


@@ 270,13 273,6 @@ bootargs(ulong base)
		av[ac++] = pusharg(buf);
	} else if(strncmp(cp, "ether", 5) == 0)
		av[ac++] = pusharg("-n");
	if(buf[0]){
		cp = strchr(buf, '!');
		if(cp){
			strcpy(bootdisk, cp+1);
			addconf("bootdisk", bootdisk);
		}
	}

	/* 4 byte word align stack */
	sp = (uchar*)((ulong)sp & ~3);


@@ 290,18 286,6 @@ bootargs(ulong base)
	sp += (USTKTOP - BY2PG) - base - sizeof(ulong);
}

Conf	conf;

void
addconf(char *name, char *val)
{
	if(nconf >= MAXCONF)
		return;
	confname[nconf] = name;
	confval[nconf] = val;
	nconf++;
}

char*
getconf(char *name)
{


@@ 313,24 297,47 @@ getconf(char *name)
	return 0;
}

static void
writeconf(void)
{
	char *p, *q;
	int n;

	p = getconfenv();

	if(waserror()) {
		free(p);
		nexterror();
	}

	/* convert to name=value\n format */
	for(q=p; *q; q++) {
		q += strlen(q);
		*q = '=';
		q += strlen(q);
		*q = '\n';
	}
	n = q - p + 1;
	if(n >= BOOTARGSLEN)
		error("kernel configuration too large");
	memset(BOOTLINE, 0, BOOTLINELEN);
	memmove(BOOTARGS, p, n);
	poperror();
	free(p);
}

void
confinit(void)
{
	char *p;
	int userpcnt;
	ulong kpages, maxmem;
	ulong kpages;

	if(p = getconf("*maxmem"))
		maxmem = strtoul(p, 0, 0);
	else
		maxmem = 0;
	if(p = getconf("*kernelpercent"))
		userpcnt = 100 - strtol(p, 0, 0);
	else
		userpcnt = 0;

	meminit(maxmem);

	conf.npage = conf.npage0 + conf.npage1;

	conf.nproc = 100 + ((conf.npage*BY2PG)/MB)*5;


@@ 556,8 563,8 @@ procsave(Proc *p)
	mmuflushtlb(PADDR(m->pdb));
}

void
exit(int ispanic)
static void
shutdown(int ispanic)
{
	int ms, once;



@@ 588,7 595,56 @@ exit(int ispanic)
	}
	else
		delay(1000);
}

void
reboot(void *entry, void *code, ulong size)
{
	void (*f)(ulong, ulong, ulong);
	ulong *pdb;

	writeconf();

	shutdown(0);

	/*
	 * should be the only processor running now
	 */

	print("shutting down...\n");
	delay(200);

	splhi();

	/* turn off buffered serial console */
	serialoq = nil;

	/* shutdown devices */
	chandevshutdown();

	/*
	 * Modify the machine page table to directly map the low 4MB of memory
	 * This allows the reboot code to turn off the page mapping
	 */
	pdb = m->pdb;
	pdb[PDX(0)] = pdb[PDX(KZERO)];
	mmuflushtlb(PADDR(pdb));

	/* setup reboot trampoline function */
	f = (void*)REBOOTADDR;
	memmove(f, rebootcode, sizeof(rebootcode));

	print("rebooting...\n");

	/* off we go - never to return */
	(*f)(PADDR(entry), PADDR(code), size);
}


void
exit(int ispanic)
{
	shutdown(ispanic);
	arch->reset();
}



@@ 596,33 652,32 @@ int
isaconfig(char *class, int ctlrno, ISAConf *isa)
{
	char cc[32], *p;
	int i, n;
	int i;

	snprint(cc, sizeof cc, "%s%d", class, ctlrno);
	for(n = 0; n < nconf; n++){
		if(cistrcmp(confname[n], cc) != 0)
			continue;
		isa->nopt = tokenize(confval[n], isa->opt, NISAOPT);
		for(i = 0; i < isa->nopt; i++){
			p = isa->opt[i];
			if(cistrncmp(p, "type=", 5) == 0)
				isa->type = p + 5;
			else if(cistrncmp(p, "port=", 5) == 0)
				isa->port = strtoul(p+5, &p, 0);
			else if(cistrncmp(p, "irq=", 4) == 0)
				isa->irq = strtoul(p+4, &p, 0);
			else if(cistrncmp(p, "dma=", 4) == 0)
				isa->dma = strtoul(p+4, &p, 0);
			else if(cistrncmp(p, "mem=", 4) == 0)
				isa->mem = strtoul(p+4, &p, 0);
			else if(cistrncmp(p, "size=", 5) == 0)
				isa->size = strtoul(p+5, &p, 0);
			else if(cistrncmp(p, "freq=", 5) == 0)
				isa->freq = strtoul(p+5, &p, 0);
		}
		return 1;
	p = getconf(cc);
	if(p == nil)
		return 0;

	isa->nopt = tokenize(p, isa->opt, NISAOPT);
	for(i = 0; i < isa->nopt; i++){
		p = isa->opt[i];
		if(cistrncmp(p, "type=", 5) == 0)
			isa->type = p + 5;
		else if(cistrncmp(p, "port=", 5) == 0)
			isa->port = strtoul(p+5, &p, 0);
		else if(cistrncmp(p, "irq=", 4) == 0)
			isa->irq = strtoul(p+4, &p, 0);
		else if(cistrncmp(p, "dma=", 4) == 0)
			isa->dma = strtoul(p+4, &p, 0);
		else if(cistrncmp(p, "mem=", 4) == 0)
			isa->mem = strtoul(p+4, &p, 0);
		else if(cistrncmp(p, "size=", 5) == 0)
			isa->size = strtoul(p+5, &p, 0);
		else if(cistrncmp(p, "freq=", 5) == 0)
			isa->freq = strtoul(p+5, &p, 0);
	}
	return 0;
	return 1;
}

int

M pc/mem.h => pc/mem.h +9 -1
@@ 31,6 31,7 @@
 * Fundamental addresses
 */
#define IDTADDR		0x80000800		/* idt */
#define	REBOOTADDR	0x00001000		/* reboot code - physical address */
#define APBOOTSTRAP	0x80001000		/* AP bootstrap code */
#define CONFADDR	0x80001200		/* info passed from boot loader */
#define CPU0PDB		0x80002000		/* bootstrap processor PDB */


@@ 75,8 76,8 @@
#define SELECTOR(i, t, p)	(((i)<<3) | (t) | (p))

#define NULLSEL	SELECTOR(NULLSEG, SELGDT, 0)
#define KESEL	SELECTOR(KESEG, SELGDT, 0)
#define KDSEL	SELECTOR(KDSEG, SELGDT, 0)
#define KESEL	SELECTOR(KESEG, SELGDT, 0)
#define UESEL	SELECTOR(UESEG, SELGDT, 3)
#define UDSEL	SELECTOR(UDSEG, SELGDT, 3)
#define TSSSEL	SELECTOR(TSSSEG, SELGDT, 0)


@@ 126,4 127,11 @@
#define	PTEUSER		(1<<2)
#define	PTESIZE		(1<<7)

/*
 * Macros for calculating offsets within the page directory base
 * and page tables. 
 */
#define PDX(va)		((((ulong)(va))>>22) & 0x03FF)
#define PTX(va)		((((ulong)(va))>>12) & 0x03FF)

#define getpgcolor(a)	0

M pc/memory.c => pc/memory.c +42 -16
@@ 11,9 11,6 @@

#define MEMDEBUG	0

#define PDX(va)		((((ulong)(va))>>22) & 0x03FF)
#define PTX(va)		((((ulong)(va))>>12) & 0x03FF)

enum {
	MemUPA		= 0,		/* unbacked physical address */
	MemRAM		= 1,		/* physical memory */


@@ 77,9 74,19 @@ static RMap rmapumbrw = {
};

void
memdebug(void)
mapprint(RMap *rmap)
{
	Map *mp;

	print("%s\n", rmap->name);	
	for(mp = rmap->map; mp->size; mp++)
		print("\t%8.8luX %8.8uX %8.8luX\n", mp->addr, mp->size, mp->addr+mp->size);
}


void
memdebug(void)
{
	ulong maxpa, maxpa1, maxpa2;

	if(MEMDEBUG == 0)


@@ 91,14 98,10 @@ memdebug(void)
	print("maxpa = %luX -> %luX, maxpa1 = %luX maxpa2 = %luX\n",
		maxpa, MB+maxpa*KB, maxpa1, maxpa2);

	for(mp = rmapram.map; mp->size; mp++)
		print("%8.8luX %8.8uX %8.8luX\n", mp->addr, mp->size, mp->addr+mp->size);
	for(mp = rmapumb.map; mp->size; mp++)
		print("%8.8luX %8.8uX %8.8luX\n", mp->addr, mp->size, mp->addr+mp->size);
	for(mp = rmapumbrw.map; mp->size; mp++)
		print("%8.8luX %8.8uX %8.8luX\n", mp->addr, mp->size, mp->addr+mp->size);
	for(mp = rmapupa.map; mp->size; mp++)
		print("%8.8luX %8.8uX %8.8luX\n", mp->addr, mp->size, mp->addr+mp->size);
	mapprint(&rmapram);
	mapprint(&rmapumb);
	mapprint(&rmapumbrw);
	mapprint(&rmapupa);
}

void


@@ 263,10 266,23 @@ umbscan(void)
	}
}

int
touch(uchar *p, int n)
{
	int x;
	int i;

	x = 0;
	for(i=0; i<n; i++)
		x += p[i];
	return x;
}


static void
ramscan(ulong maxmem)
{
	ulong *k0, kzero, map, maxpa, pa, *pte, *table, *va, x;
	ulong *k0, kzero, map, maxpa, pa, *pte, *table, *va, x, n;
	int nvalid[NMemType];
	uchar *bda;



@@ 284,11 300,14 @@ ramscan(ulong maxmem)
	 */
	x = PADDR(CPU0MACH+BY2PG);
	bda = (uchar*)KADDR(0x400);
	mapfree(&rmapram, x, ((bda[0x14]<<8)|bda[0x13])*KB-x);
	n = ((bda[0x14]<<8)|bda[0x13])*KB-x;
	mapfree(&rmapram, x, n);
	memset(KADDR(x), 0, n);		/* keep us honest */

	x = PADDR(PGROUND((ulong)end));
	pa = MemMinMB*MB;
	mapfree(&rmapram, x, pa-x);
	memset(KADDR(x), 0, pa-x);		/* keep us honest */

	/*
	 * Check if the extended memory size can be obtained from the CMOS.


@@ 363,7 382,7 @@ ramscan(ulong maxmem)
				pa += BY2PG;
			}while(pa % MB);
			mmuflushtlb(PADDR(m->pdb));
//			memset(va, 0, MB);
			/* memset(va, 0, MB); so damn slow to memset all of memory */
		}
		else if(pa < 16*MB){
			nvalid[MemUMB] += MB/BY2PG;


@@ 418,10 437,17 @@ ramscan(ulong maxmem)
}

void
meminit(ulong maxmem)
meminit(void)
{
	Map *mp, *xmp;
	ulong pa, *pte;
	ulong maxmem;
	char *p;

	if(p = getconf("*maxmem"))
		maxmem = strtoul(p, 0, 0);
	else
		maxmem = 0;

	/*
	 * Set special attributes for memory between 640KB and 1MB:

M pc/mmu.c => pc/mmu.c +18 -6
@@ 10,7 10,7 @@
#define	TSSSEGM(b,p)	{ ((b)<<16)|sizeof(Tss),\
			  ((b)&0xFF000000)|(((b)>>16)&0xFF)|SEGTSS|SEGPL(p)|SEGP }

Segdesc gdt[6] =
Segdesc gdt[NGDT] =
{
[NULLSEG]	{ 0, 0},		/* null descriptor */
[KDSEG]		DATASEGM(0),		/* kernel data/stack */


@@ 20,9 20,6 @@ Segdesc gdt[6] =
[TSSSEG]	TSSSEGM(0,0),		/* tss segment */
};

#define PDX(va)		((((ulong)(va))>>22) & 0x03FF)
#define PTX(va)		((((ulong)(va))>>12) & 0x03FF)

static void
taskswitch(ulong pdb, ulong stack)
{


@@ 40,6 37,21 @@ taskswitch(ulong pdb, ulong stack)
}

void
gdtinit(void)
{
	ulong x;
	ushort ptr[3];

	memmove(m->gdt, gdt, sizeof(m->gdt));

	ptr[0] = sizeof(m->gdt)-1;
	x = (ulong)m->gdt;
	ptr[1] = x & 0xFFFF;
	ptr[2] = (x>>16) & 0xFFFF;
	lgdt(ptr);
}

void
mmuinit(void)
{
	ulong x, *p;


@@ 53,13 65,13 @@ mmuinit(void)
	m->gdt[TSSSEG].d0 = (x<<16)|sizeof(Tss);
	m->gdt[TSSSEG].d1 = (x&0xFF000000)|((x>>16)&0xFF)|SEGTSS|SEGPL(0)|SEGP;

	ptr[0] = sizeof(m->gdt);
	ptr[0] = sizeof(m->gdt)-1;
	x = (ulong)m->gdt;
	ptr[1] = x & 0xFFFF;
	ptr[2] = (x>>16) & 0xFFFF;
	lgdt(ptr);

	ptr[0] = sizeof(Segdesc)*256;
	ptr[0] = sizeof(Segdesc)*256-1;
	x = IDTADDR;
	ptr[1] = x & 0xFFFF;
	ptr[2] = (x>>16) & 0xFFFF;

M pc/mp.c => pc/mp.c +0 -3
@@ 348,9 348,6 @@ checkmtrr(void)
	}
}

#define PDX(va)		((((ulong)(va))>>22) & 0x03FF)
#define PTX(va)		((((ulong)(va))>>12) & 0x03FF)

static void
squidboy(Apic* apic)
{

M pc/pci.c => pc/pci.c +6 -1
@@ 888,6 888,7 @@ pcicfginit(void)

out:
	qunlock(&pcicfginitlock);

}

static int


@@ 1170,8 1171,12 @@ pcireset(void)
	if(pcicfgmode == -1)
		pcicfginit();

	for(p = pcilist; p != nil; p = p->list)
	for(p = pcilist; p != nil; p = p->list) {
		/* don't mess with the bridges */
		if(p->ccrb == 0x06)
			continue;
		pciclrbme(p);
	}
}

void

A pc/reboot.h => pc/reboot.h +7 -0
@@ 0,0 1,7 @@
uchar rebootcode[]={
0x8b,0x7c,0x24,0x04,0x89,0xf8,0x8b,0x74,0x24,0x08,0x8b,0x4c,0x24,0x0c,0x0f,0x20,
0xc2,0x81,0xe2,0xff,0xff,0xff,0x7f,0x0f,0x22,0xc2,0x31,0xd2,0x0f,0x22,0xda,0x39,
0xfe,0x7f,0x08,0x89,0xf2,0x01,0xca,0x39,0xfa,0x7f,0x07,0xfc,0xf3,0xa4,0x09,0xc0,
0xff,0xe0,0x01,0xcf,0x01,0xce,0x4f,0x4e,0xfd,0xf3,0xa4,0xeb,0xf1,

};

A pc/rebootcode.s => pc/rebootcode.s +54 -0
@@ 0,0 1,54 @@
#include "mem.h"

/*
 * Turn off MMU, then memmory the new kernel to its correct location
 * in physical memory.  Then jumps the to start of the kernel.
 */

TEXT	main(SB),$0
	MOVL	p1+0(FP), DI		/* destination */
	MOVL	DI, AX			/* entry point */
	MOVL	p2+4(FP), SI		/* source */
	MOVL	n+8(FP), CX		/* byte count */

/*
 * disable paging
 */
	MOVL	CR0, DX
	ANDL	$~0x80000000, DX		/* ~(PG) */
	MOVL	DX, CR0
	MOVL	$0, DX
	MOVL	DX, CR3

/*
 * the source and destination may overlap.
 * determine whether to copy forward or backwards
 */
	CMPL	SI, DI
	JGT	_forward
	MOVL	SI, DX
	ADDL	CX, DX
	CMPL	DX, DI
	JGT	_back

_forward:
	CLD
	REP;	MOVSB
	JMP	_startkernel

_back:
	ADDL	CX, DI
	ADDL	CX, SI
	SUBL	$1, DI
	SUBL	$1, SI
	STD
	REP;	MOVSB
	JMP	_startkernel
/*
 * JMP to kernel entry point.  Note the true kernel entry point is
 * the virtual address KZERO|AX, but this must wait until
 * the MMU is enabled by the kernel in l.s
 */
_startkernel:
	ORL	AX, AX		/* NOP: avoid link bug */
	JMP*	AX

M pc/uarti8250.c => pc/uarti8250.c +17 -4
@@ 165,7 165,7 @@ i8250status(Uart* uart, void* buf, long n, long offset)
	ier = ctlr->sticky[Ier];
	lcr = ctlr->sticky[Lcr];
	snprint(p, READSTR,
		"b%d c%d d%d e%d l%d m%d p%c r%d s%d i%d\n"
		"b%d c%d d%d e%d l%d m%d p%c r%d s%d i%d ier=%ux\n"
		"dev(%d) type(%d) framing(%d) overruns(%d)%s%s%s%s\n",

		uart->baud,


@@ 178,6 178,7 @@ i8250status(Uart* uart, void* buf, long n, long offset)
		(mcr & Rts) != 0,
		(lcr & Stb) ? 2: 1,
		ctlr->fena,
		ier,

		uart->dev,
		uart->type,


@@ 269,13 270,13 @@ i8250modemctl(Uart* uart, int on)
	ilock(&uart->tlock);
	if(on){
		ctlr->sticky[Ier] |= Ems;
		csr8w(ctlr, Ier, 0);
		csr8w(ctlr, Ier, ctlr->sticky[Ier]);
		uart->modem = 1;
		uart->cts = csr8r(ctlr, Msr) & Cts;
	}
	else{
		ctlr->sticky[Ier] &= ~Ems;
		csr8w(ctlr, Ier, 0);
		csr8w(ctlr, Ier, ctlr->sticky[Ier]);
		uart->modem = 0;
		uart->cts = 1;
	}


@@ 525,7 526,7 @@ i8250disable(Uart* uart)

	ctlr = uart->regs;
	ctlr->sticky[Ier] = 0;
	csr8w(ctlr, Ier, 0);
	csr8w(ctlr, Ier, ctlr->sticky[Ier]);

	if(ctlr->iena != 0){
		intrdisable(ctlr->irq, i8250interrupt, uart, ctlr->tbdf, uart->name);


@@ 562,6 563,18 @@ i8250enable(Uart* uart, int ie)

	(*uart->phys->dtr)(uart, 1);
	(*uart->phys->rts)(uart, 1);


	/*
	 * During startup, the i8259 interrupt controler is reset.
	 * This may result in a lost interrupt from the i8250 uart.
	 * The i8250 thinks the interrupt is still outstanding and does not
	 * generate any further interrupts. To work around this we call the
	 * interrupt handler to clear any pending inerrupt events.
	 * Note this must be donw after setting Ier.
	 */
	if(ie)
		i8250interrupt(nil, uart);
}

void*

M port/chan.c => port/chan.c +12 -0
@@ 208,6 208,18 @@ chandevinit(void)
		devtab[i]->init();
}

void
chandevshutdown(void)
{
	int i;
	
	/* shutdown in reverse order */
	for(i=0; devtab[i] != nil; i++)
		;
	for(i--; i >= 0; i--)
		devtab[i]->shutdown();
}

Chan*
newchan(void)
{

M port/dev.c => port/dev.c +5 -0
@@ 78,6 78,11 @@ devinit(void)
{
}

void
devshutdown(void)
{
}

Chan*
devattach(int tc, char *spec)
{

M port/devaudio.c => port/devaudio.c +1 -0
@@ 1221,6 1221,7 @@ Dev audiodevtab = {

	devreset,
	audioinit,
	devshutdown,
	audioattach,
	audiowalk,
	audiostat,

M port/devbridge.c => port/devbridge.c +1 -0
@@ 1190,6 1190,7 @@ Dev bridgedevtab = {

	devreset,
	bridgeinit,
	devshutdown,
	bridgeattach,
	bridgewalk,
	bridgestat,

M port/devcap.c => port/devcap.c +1 -0
@@ 251,6 251,7 @@ Dev capdevtab = {

	devreset,
	devinit,
	devshutdown,
	capattach,
	capwalk,
	capstat,

M port/devcons.c => port/devcons.c +29 -22
@@ 50,6 50,18 @@ static int	readbintime(char*, int);
static int	writetime(char*, int);
static int	writebintime(char*, int);

enum
{
	CMreboot,
	CMpanic,
};

Cmdtab reboottbl[] =
{
	CMreboot,	"reboot",	0,
	CMpanic,	"panic",	0,
};

void
printinit(void)
{


@@ 868,6 880,8 @@ conswrite(Chan *c, void *va, long n, vlong off)
	int id, fd;
	Chan *swc;
	ulong offset = off;
	Cmdbuf *cb;
	Cmdtab *ct;

	switch((ulong)c->qid.path){
	case Qcons:


@@ 940,21 954,22 @@ conswrite(Chan *c, void *va, long n, vlong off)
	case Qreboot:
		if(!iseve())
			error(Eperm);
		if(strncmp(a, "reboot", 6) == 0){
			print("conswrite: reboot\n");
			exit(0);
		}
		if(strncmp(a, "malloc", 6) == 0){	/* rsc bug */
			a = malloc(2);
			strcpy(a, "hi");
			free(a);
			a = malloc(2);
			strcpy(a, "helo");
			free(a);
			panic("not reached conswrite");
		cb = parsecmd(a, n);

		if(waserror()) {
			free(cb);
			nexterror();
		}
		if(strncmp(a, "panic", 5) == 0)
		ct = lookupcmd(cb, reboottbl, nelem(reboottbl));
		switch(ct->index) {
		case CMreboot:
			rebootcmd(cb->nf-1, cb->f+1);
			break;
		case CMpanic:
			panic("/dev/reboot");
		}
		poperror();
		free(cb);
		break;

	case Qsysstat:


@@ 1009,21 1024,13 @@ conswrite(Chan *c, void *va, long n, vlong off)
	return n;
}

void
setterm(char *f)
{
	char buf[64];

	snprint(buf, sizeof buf, f, conffile);
	ksetenv("terminal", buf);
}

Dev consdevtab = {
	'c',
	"cons",

	devreset,
	consinit,
	devshutdown,
	consattach,
	conswalk,
	consstat,

M port/devdraw.c => port/devdraw.c +1 -0
@@ 1925,6 1925,7 @@ Dev drawdevtab = {

	devreset,
	devinit,
	devshutdown,
	drawattach,
	drawwalk,
	drawstat,

M port/devdup.c => port/devdup.c +1 -0
@@ 130,6 130,7 @@ Dev dupdevtab = {

	devreset,
	devinit,
	devshutdown,
	dupattach,
	dupwalk,
	dupstat,

M port/devenv.c => port/devenv.c +87 -12
@@ 5,12 5,16 @@
#include	"fns.h"
#include	"../port/error.h"


enum
{
	Maxenvsize = 16300,
};

static Egrp	*envgrp(Chan *c);
static int	envwriteable(Chan *c);

static Egrp	confegrp;	/* global environment group containing the kernel configuration */

static int
envgen(Chan *c, char*, Dirtab*, int, int s, Dir *dp)
{


@@ 22,7 26,7 @@ envgen(Chan *c, char*, Dirtab*, int, int s, Dir *dp)
		return 1;
	}

	eg = up->egrp;
	eg = envgrp(c);
	qlock(eg);

	for(e = eg->entries; e && s; e = e->link)


@@ 53,7 57,19 @@ envlookup(Egrp *eg, char *name, ulong qidpath)
static Chan*
envattach(char *spec)
{
	return devattach('e', spec);
	Chan *c;
	Egrp *egrp = nil;

	if(spec && *spec) {
		if(strcmp(spec, "c") == 0)
			egrp = &confegrp;
		if(egrp == nil)
			error(Ebadarg);
	}

	c = devattach('e', spec);
	c->aux = egrp;
	return c;
}

static Walkqid*


@@ 66,7 82,7 @@ static int
envstat(Chan *c, uchar *db, int n)
{
	if(c->qid.type & QTDIR)
		c->qid.vers = up->egrp->vers;
		c->qid.vers = envgrp(c)->vers;
	return devstat(c, db, n, 0, 0, envgen);
}



@@ 76,12 92,14 @@ envopen(Chan *c, int omode)
	Egrp *eg;
	Evalue *e;

	eg = up->egrp;
	eg = envgrp(c);
	if(c->qid.type & QTDIR) {
		if(omode != OREAD)
			error(Eperm);
	}
	else {
		if(omode != OREAD && !envwriteable(c))
			error(Eperm);
		qlock(eg);
		e = envlookup(eg, nil, c->qid.path);
		if(e == 0) {


@@ 112,7 130,7 @@ envcreate(Chan *c, char *name, int omode, ulong)
		error(Eperm);

	omode = openmode(omode);
	eg = up->egrp;
	eg = envgrp(c);

	qlock(eg);
	if(waserror()) {


@@ 151,7 169,7 @@ envremove(Chan *c)
	if(c->qid.type & QTDIR)
		error(Eperm);

	eg = up->egrp;
	eg = envgrp(c);
	qlock(eg);
	l = &eg->entries;
	for(e = *l; e; e = e->link) {


@@ 196,7 214,7 @@ envread(Chan *c, void *a, long n, vlong off)
	if(c->qid.type & QTDIR)
		return devdirread(c, a, n, 0, 0, envgen);

	eg = up->egrp;
	eg = envgrp(c);
	qlock(eg);
	e = envlookup(eg, nil, c->qid.path);
	if(e == 0) {


@@ 230,7 248,7 @@ envwrite(Chan *c, void *a, long n, vlong off)
	if(vend > Maxenvsize)
		error(Etoobig);

	eg = up->egrp;
	eg = envgrp(c);
	qlock(eg);
	e = envlookup(eg, nil, c->qid.path);
	if(e == 0) {


@@ 260,6 278,7 @@ Dev envdevtab = {

	devreset,
	devinit,
	devshutdown,
	envattach,
	envwalk,
	envstat,


@@ 314,17 333,73 @@ closeegrp(Egrp *eg)
	}
}

static Egrp*
envgrp(Chan *c)
{
	if(c->aux == nil)
		return up->egrp;
	return c->aux;
}

static int
envwriteable(Chan *c)
{
	return iseve() || c->aux == nil;
}

/*
 *  to let the kernel set environment variables
 */
void
ksetenv(char *ename, char *eval)
ksetenv(char *ename, char *eval, int conf)
{
	Chan *c;
	char buf[2*KNAMELEN];

	sprint(buf, "#e/%s", ename);
	
	snprint(buf, sizeof(buf), "#e%s/%s", conf?"c":"", ename);
	c = namec(buf, Acreate, OWRITE, 0600);
	devtab[c->type]->write(c, eval, strlen(eval), 0);
	cclose(c);
}

/*
 * Return a copy of configuration environment as a sequence of strings.
 * The strings alternate between name and value.  A zero length name string
 * indicates the end of the list
 */
char *
getconfenv(void)
{
	Egrp *eg = &confegrp;
	Evalue *e;
	char *p, *q;
	int n;

	qlock(eg);
	if(waserror()) {
		qunlock(eg);
		nexterror();
	}
	
	/* determine size */
	n = 0;
	for(e=eg->entries; e; e=e->link)
		n += strlen(e->name) + e->len + 2;
	p = malloc(n + 1);
	if(p == nil)
		error(Enomem);
	q = p;
	for(e=eg->entries; e; e=e->link) {
		strcpy(q, e->name);
		q += strlen(q) + 1;
		memmove(q, e->value, e->len);
		q[e->len] = 0;
		/* move up to the first null */
		q += strlen(q) + 1;
	}
	*q = 0;
	
	poperror();
	qunlock(eg);
	return p;
}

M port/devkprof.c => port/devkprof.c +1 -0
@@ 176,6 176,7 @@ Dev kprofdevtab = {

	devreset,
	kprofinit,
	devshutdown,
	kprofattach,
	kprofwalk,
	kprofstat,

M port/devloopback.c => port/devloopback.c +1 -0
@@ 748,6 748,7 @@ Dev loopbackdevtab = {

	devreset,
	loopbackinit,
	devshutdown,
	loopbackattach,
	loopbackwalk,
	loopbackstat,

M port/devmnt.c => port/devmnt.c +1 -1
@@ 450,7 450,6 @@ mntwalk(Chan *c, Chan *nc, char **name, int nname)
		wq->qid[i] = r->reply.wqid[i];

    Return:

	poperror();
	mntfree(r);
	poperror();


@@ 1142,6 1141,7 @@ Dev mntdevtab = {

	mntreset,
	devinit,
	devshutdown,
	mntattach,
	mntwalk,
	mntstat,

M port/devmouse.c => port/devmouse.c +1 -0
@@ 435,6 435,7 @@ Dev mousedevtab = {

	mousereset,
	mouseinit,
	devshutdown,
	mouseattach,
	mousewalk,
	mousestat,

M port/devpipe.c => port/devpipe.c +1 -0
@@ 375,6 375,7 @@ Dev pipedevtab = {

	devreset,
	pipeinit,
	devshutdown,
	pipeattach,
	pipewalk,
	pipestat,

M port/devpnp.c => port/devpnp.c +1 -0
@@ 624,6 624,7 @@ Dev pnpdevtab = {

	pnpreset,
	devinit,
	devshutdown,
	pnpattach,
	pnpwalk,
	pnpstat,

M port/devproc.c => port/devproc.c +1 -0
@@ 920,6 920,7 @@ Dev procdevtab = {

	devreset,
	procinit,
	devshutdown,
	procattach,
	procwalk,
	procstat,

M port/devroot.c => port/devroot.c +1 -0
@@ 156,6 156,7 @@ Dev rootdevtab = {

	rootreset,
	devinit,
	devshutdown,
	rootattach,
	rootwalk,
	rootstat,

M port/devsd.c => port/devsd.c +1 -0
@@ 1448,6 1448,7 @@ Dev sddevtab = {

	sdreset,
	devinit,
	devshutdown,
	sdattach,
	sdwalk,
	sdstat,

M port/devsdp.c => port/devsdp.c +1 -0
@@ 935,6 935,7 @@ Dev sdpdevtab = {

	devreset,
	sdpinit,
	devshutdown,
	sdpattach,
	sdpwalk,
	sdpstat,

M port/devsrv.c => port/devsrv.c +2 -1
@@ 316,7 316,8 @@ Dev srvdevtab = {
	"srv",

	devreset,
	srvinit,
	srvinit,	
	devshutdown,
	srvattach,
	srvwalk,
	srvstat,

M port/devssl.c => port/devssl.c +1 -0
@@ 1229,6 1229,7 @@ Dev ssldevtab = {

	devreset,
	sslinit,
	devshutdown,
	sslattach,
	sslwalk,
	sslstat,

M port/devtls.c => port/devtls.c +1 -0
@@ 1666,6 1666,7 @@ Dev tlsdevtab = {

	devreset,
	tlsinit,
	devshutdown,
	tlsattach,
	tlswalk,
	tlsstat,

M port/devuart.c => port/devuart.c +1 -0
@@ 509,6 509,7 @@ Dev uartdevtab = {

	uartreset,
	devinit,
	devshutdown,
	uartattach,
	uartwalk,
	uartstat,

M port/portdat.h => port/portdat.h +1 -0
@@ 185,6 185,7 @@ struct Dev

	void	(*reset)(void);
	void	(*init)(void);
	void	(*shutdown)(void);
	Chan*	(*attach)(char*);
	Walkqid*	(*walk)(Chan*, Chan*, char**, int);
	int	(*stat)(Chan*, uchar*, int);

M port/portfns.h => port/portfns.h +6 -1
@@ 25,6 25,7 @@ int		canqlock(QLock*);
int		canrlock(RWlock*);
void		chandevinit(void);
void		chandevreset(void);
void		chandevshutdown(void);
void		chanfree(Chan*);
void		chanrec(Mnt*);
void		checkalarms(void);


@@ 73,6 74,7 @@ void		devpermcheck(char*, ulong, int);
void		devpower(int);
void		devremove(Chan*);
void		devreset(void);
void		devshutdown(void);
int		devstat(Chan*, uchar*, int, Dirtab*, int, Devgen*);
Walkqid*	devwalk(Chan*, Chan*, char**, int, Dirtab*, int, Devgen*);
int		devwstat(Chan*, uchar*, int);


@@ 114,6 116,7 @@ void		getcolor(ulong, ulong*, ulong*, ulong*);
ulong		getmalloctag(void*);
ulong		getrealloctag(void*);
void		gotolabel(Label*);
char*		getconfenv(void);
int		haswaitq(void*);
long		hostdomainwrite(char*, int);
long		hostownerwrite(char*, int);


@@ 142,7 145,7 @@ int		kprint(char*, ...);
void		kproc(char*, void(*)(void*), void*);
void		kprocchild(Proc*, void (*)(void*), void*);
void		(*kproftimer)(ulong);
void		ksetenv(char*, char*);
void		ksetenv(char*, char*, int);
void		kstrcpy(char*, char*, int);
void		kstrdup(char**, char*);
long		latin1(Rune*, int);


@@ 270,6 273,8 @@ void		rdb(void);
int		readnum(ulong, char*, ulong, ulong, int);
int		readstr(ulong, char*, ulong, char*);
void		ready(Proc*);
void		rebootcmd(int, char**);
void		reboot(void*, void*, ulong);
void		relocateseg(Segment*, ulong);
void		renameuser(char*, char*);
void		resched(char*);

A port/rebootcmd.c => port/rebootcmd.c +100 -0
@@ 0,0 1,100 @@
#include	"u.h"
#include	"../port/lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"../port/error.h"
#include	"a.out.h"

static ulong
l2be(long l)
{
	uchar *cp;

	cp = (uchar*)&l;
	return (cp[0]<<24) | (cp[1]<<16) | (cp[2]<<8) | cp[3];
}


static void
readn(Chan *c, void *vp, long n)
{
	char *p = vp;
	long nn;

	while(n > 0) {
		nn = devtab[c->type]->read(c, p, n, c->offset);
		if(nn == 0)
			error(Eshort);
		c->offset += nn;
		p += nn;
		n -= nn;
	}
}

static void
setbootcmd(int argc, char *argv[])
{
	char *buf, *p, *ep;
	int i;

	buf = malloc(1024);
	if(buf == nil)
		error(Enomem);
	p = buf;
	ep = buf + 1024;
	for(i=0; i<argc; i++)
		p = seprint(p, ep, "%q ", argv[i]);
	*p = 0;
	ksetenv("bootcmd", buf, 1);
	free(buf);
}

void
rebootcmd(int argc, char *argv[])
{
	Chan *c;
	Exec exec;
	ulong magic, text, rtext, entry, data, size;
	uchar *p;

	if(argc == 0)
		exit(0);

	c = namec(argv[0], Aopen, OEXEC, 0);
	if(waserror()){
		cclose(c);
		nexterror();
	}

	readn(c, &exec, sizeof(Exec));
	magic = l2be(exec.magic);
	entry = l2be(exec.entry);
	text = l2be(exec.text);
	data = l2be(exec.data);
	if(magic != AOUT_MAGIC)
		error(Ebadexec);

	/* round text out to page boundary */
	rtext = PGROUND(entry+text)-entry;
	size = rtext + data;
	p = malloc(size);
	if(p == nil)
		error(Enomem);

	if(waserror()){
		free(p);
		nexterror();
	}

	memset(p, 0, size);
	readn(c, p, text);
	readn(c, p + rtext, data);

	ksetenv("bootfile", argv[0], 1);
	setbootcmd(argc-1, argv+1);

	reboot((void*)entry, p, size);

	panic("return from reboot!");
}