~kris/9p

9hist

fd5d765be48434f680845f99a399ff352f3e97b5 — David du Colombier 23 years ago fcba2d6
Plan 9 from Bell Labs 2002-11-30
M alphapc/devarch.c => alphapc/devarch.c +40 -4
@@ 401,13 401,49 @@ PCArch archgeneric = {

static char	*sysnames[] =
{
// [26]		"EB164",
[26]		"AlphaPC 164",
[1]		"Alpha Demo. Unit",
[2]		"DEC 4000; Cobra",
[3]		"DEC 7000; Ruby",
[4]		"DEC 3000/500; Flamingo family (TC)",
[6]		"DEC 2000/300; Jensen (EISA/ISA)",
[7]		"DEC 3000/300; Pelican (TC)",
[8]		"Avalon A12; Avalon Multicomputer",
[9]		"DEC 2100/A500; Sable",
[10]		"DEC APXVME/64; AXPvme (VME?)",
[11]		"DEC AXPPCI/33; NoName (PCI/ISA)",
[12]		"DEC 21000; TurboLaser (PCI/EISA)",
[13]		"DEC 2100/A50; Avanti (PCI/ISA)",
[14]		"DEC MUSTANG; Mustang",
[15]		"DEC KN20AA; kn20aa (PCI/EISA)",
[17]		"DEC 1000; Mikasa (PCI/ISA?)",
[19]		"EB66; EB66 (PCI/ISA?)",		// DEC?
[20]		"EB64P; EB64+ (PCI/ISA?)",		// DEC?
[21]		"Alphabook1; Alphabook",
[22]		"DEC 4100; Rawhide (PCI/EISA)",
[23]		"DEC EV45/PBP; Lego",
[24]		"DEC 2100A/A500; Lynx",
[26]		"DEC AlphaPC 164",	// only supported one: "EB164 (PCI/ISA)"
[27]		"DEC 1000A; Noritake",
[28]		"DEC AlphaVME/224; Cortex",
[30]		"DEC 550; Miata (PCI/ISA)",
[32]		"DEC EV56/PBP; Takara",
[33]		"DEC AlphaVME/320; Yukon (VME?)",
[34]		"DEC 6600; MonetGoldrush",
// 200 and up is Alpha Processor Inc. machines
// [201]	"API UP1000; Nautilus",
};

static char	*cpunames[] =
{
[7]		"21164A",
[1]		"EV3",
[2]		"EV4: 21064",
[3]		"Simulation",
[4]		"LCA4: 2106[68]",
[5]		"EV5: 21164",
[6]		"EV45: 21064A",
[7]		"21164A",		/* only supported one: EV56 */
[8]		"EV6: 21264",
[9]		"PCA256: 21164PC",
};

void


@@ 430,7 466,7 @@ cpuidprint(void)
		s = "<unknown>";
		if (hwrpb->systype < nelem(sysnames))
			s = sysnames[hwrpb->systype];
		print("DEC %s (%llux, %llux, %llux)\n", s, hwrpb->systype, hwrpb->sysvar, hwrpb->sysrev);
		print("%s (%llux, %llux, %llux)\n", s, hwrpb->systype, hwrpb->sysvar, hwrpb->sysrev);
	}

	for (i = 0; i < hwrpb->ncpu; i++) {

M alphapc/devether.c => alphapc/devether.c +57 -19
@@ 10,7 10,7 @@

#include "etherif.h"

static Ether *etherxx[MaxEther];
static volatile Ether *etherxx[MaxEther];

Chan*
etherattach(char* spec)


@@ 176,7 176,8 @@ etheriq(Ether* ether, Block* bp, int fromwire)
				else if(xbp = iallocb(len)){
					memmove(xbp->wp, pkt, len);
					xbp->wp += len;
					qpass(f->in, xbp);
					if(qpass(f->in, xbp) < 0)
						ether->soverflows++;
				}
				else
					ether->soverflows++;


@@ 239,21 240,26 @@ etherwrite(Chan* chan, void* buf, long n, vlong)
{
	Ether *ether;
	Block *bp;
	int nn;

	ether = etherxx[chan->dev];
	if(NETTYPE(chan->qid.path) != Ndataqid){

	if(NETTYPE(chan->qid.path) != Ndataqid) {
		nn = netifwrite(ether, chan, buf, n);
		if(nn >= 0)
			return nn;
		if(n == sizeof("nonblocking")-1 && strncmp((char*)buf, "nonblocking", n) == 0){
			qnoblock(ether->oq, 1);
			return n;
		}
		if(ether->ctl!=nil)
			return ether->ctl(ether,buf,n);

		return netifwrite(ether, chan, buf, n);
		error(Ebadctl);
	}

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

	bp = allocb(n);


@@ 276,18 282,23 @@ etherbwrite(Chan* chan, Block* bp, ulong)
	long n;

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

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


@@ 320,7 331,7 @@ parseether(uchar *to, char *from)
	int i;

	p = from;
	for(i = 0; i < 6; i++){
	for(i = 0; i < Eaddrlen; i++){
		if(*p == 0)
			return -1;
		nip[0] = *p++;


@@ 340,7 351,7 @@ etherreset(void)
{
	Ether *ether;
	int i, n, ctlrno;
	char name[32], buf[128];
	char name[32], buf[256];

	for(ether = 0, ctlrno = 0; ctlrno < MaxEther; ctlrno++){
		if(ether == 0)


@@ 359,9 370,9 @@ etherreset(void)
			for(i = 0; i < ether->nopt; i++){
				if(strncmp(ether->opt[i], "ea=", 3))
					continue;
				if(parseether(ether->ea, &ether->opt[i][3]) == -1)
				if(parseether(ether->ea, &ether->opt[i][3]))
					memset(ether->ea, 0, Eaddrlen);
			}	
			}
			if(cards[n].reset(ether))
				break;



@@ 373,10 384,17 @@ etherreset(void)
			if(ether->irq == 2 && BUSTYPE(ether->tbdf) != BusPCI)
				ether->irq = 9;
			snprint(name, sizeof(name), "ether%d", ctlrno);
			intrenable(ether->irq, ether->interrupt, ether, ether->tbdf, name);
			/*
			 * 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);
			i = sprint(buf, "#l%d (%s): %s: %dMbps port 0x%luX irq %lud",
				ctlrno, name, ether->type, ether->mbps,
				ether->port, ether->irq);
			if(ether->mem)
				i += sprint(buf+i, " addr 0x%luX", PADDR(ether->mem));
			if(ether->size)


@@ 412,6 430,26 @@ etherreset(void)
		free(ether);
}

/* imported from ../pc; not ready for use yet */
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 */

M alphapc/etherif.h => alphapc/etherif.h +6 -0
@@ 15,10 15,15 @@ struct Ether {
	uchar	ea[Eaddrlen];

	void	(*attach)(Ether*);	/* filled in by reset routine */
	void	(*detach)(Ether*);	/* NEW, from ../pc */
	void	(*transmit)(Ether*);
	void	(*interrupt)(Ureg*, void*);
	long	(*ifstat)(Ether*, void*, long, ulong);
	long 	(*ctl)(Ether*, void*, long); /* custom ctl messages */
/* START NEW, from ../pc */
	void	(*power)(Ether*, int);	/* power on/off */
	void	(*shutdown)(Ether*);	/* shutdown hardware before reboot */
/* END NEW */
	void	*ctlr;

	Queue*	oq;


@@ 29,6 34,7 @@ struct Ether {
extern Block* etheriq(Ether*, Block*, int);
extern void addethercard(char*, int(*)(Ether*));
extern ulong ethercrc(uchar*, int);
extern int parseether(uchar*, char*);

#define NEXT(x, l)	(((x)+1)%(l))
#define PREV(x, l)	(((x) == 0) ? (l)-1: (x)-1)

M alphapc/main.c => alphapc/main.c +151 -14
@@ 32,6 32,7 @@ options(void)
	cp = bootargs;
	strncpy(cp, bootconf->bootargs, BOOTARGSLEN);
	cp[BOOTARGSLEN-1] = 0;
	/* can't print in this routine, see below in main() */

	/*
	 * Strip out '\r', change '\t' -> ' '.


@@ 60,6 61,18 @@ options(void)
	}
}

/* debugging only */
static void
dumpopts(void)
{
	int i;

	print("dumpopts: found /alpha/conf options at 0x%lux\n",
		bootconf->bootargs);
	for(i = 0; i < nconf; i++)
		print("dumpopts: read %s=%s\n", confname[i], confval[i]);
}

void
main(void)
{


@@ 80,6 93,8 @@ main(void)
	trapinit();
	screeninit();
	printinit();
	/* it's now safe to print */
	/* dumpopts();			/* DEBUG */
	kbdinit();
	i8250console();
	quotefmtinstall();


@@ 102,6 117,26 @@ initfp.fpstatus = 0x68028000;
	schedinit();
}

/* cpu->state bits */
enum {
	Cpubootinprog	= 1,	/* boot in progress */
	Cpucanrestart	= 2,	/* restart possible */
	Cpuavail	= 4,	/* processor available */
	Cpuexists 	= 8,	/* processor present */
	Cpuuserhalted	= 0x10,	/* user halted */
	Cpuctxtokay	= 0x20,	/* context valid */
	Cpupalokay	= 0x40,	/* PALcode valid */
	Cpupalmemokay	= 0x80,	/* PALcode memory valid */
	Cpupalloaded	= 0x100, /* PALcode loaded */
	Cpuhaltmask	= 0xff0000, /* halt request mask */
	Cpuhaltdflt	= 0,
	Cpuhaltsaveexit = 0x10000,
	Cpuhaltcoldboot = 0x20000,
	Cpuhaltwarmboot = 0x30000,
	Cpuhaltstayhalted = 0x40000,
	Cpumustbezero = 0xffffffffff000000,	/* 24:63 -- must be zero */
};

/*
 *  initialize a processor's mach structure.  each processor does this
 *  for itself.


@@ 121,15 156,16 @@ machinit(void)
	active.machs = 1;

	cpu = (Hwcpu*) ((ulong)hwrpb + hwrpb->cpuoff + n*hwrpb->cpulen);
	cpu->state &= ~1;			/* boot in progress - not */
/*	cpu->state |= (4<<16);		/* stay halted */
	cpu->state &= ~Cpubootinprog;
	if (0)
		cpu->state |= Cpuhaltstayhalted;
}

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

	up->nerrlab = 0;



@@ 147,9 183,9 @@ init0(void)
	chandevinit();

	if(!waserror()){
		snprint(buf, sizeof(buf), "alpha %s alphapc", conffile);
		ksetenv("terminal", buf, 0);
		ksetenv("cputype", "alpha", 0);
		sprint(tstr, "alpha %s alphapc", conffile);
		ksetenv("terminal", tstr, 0);
		if(cpuserver)
			ksetenv("service", "cpu", 0);
		else


@@ 254,15 290,104 @@ procsave(Proc *p)
	mmupark();
}

/* still to do */
void
reboot(void*, void*, ulong)
setupboot(int halt)
{
	int n = 0;		// cpu id of primary cpu, not just m->machno
	Hwcpu *cpu = (Hwcpu*)((ulong)hwrpb + hwrpb->cpuoff + n*hwrpb->cpulen);

	cpu->state &= ~(Cpucanrestart | Cpuhaltmask);
	cpu->state |= (halt? Cpuhaltstayhalted: Cpuhaltwarmboot);
}

/* from ../pc */
static void
shutdown(int ispanic)
{
	int ms, once;

	lock(&active);
	if(ispanic)
		active.ispanic = ispanic;
	else if(m->machno == 0 && (active.machs & (1<<m->machno)) == 0)
		active.ispanic = 0;
	once = active.machs & (1<<m->machno);
	active.machs &= ~(1<<m->machno);
	active.exiting = 1;
	unlock(&active);

	if(once)
		print("cpu%d: exiting\n", m->machno);
	spllo();
	for(ms = 5*1000; ms > 0; ms -= TK2MS(2)){
		delay(TK2MS(2));
		if(active.machs == 0 && consactive() == 0)
			break;
	}

	if(active.ispanic && m->machno == 0) {
		if(cpuserver)
			delay(10000);
		else
			for (;;)
				continue;
	} else
		delay(1000);
}

/* from ../pc: */
void
reboot(void *entry, void *code, ulong size)
{
	// writeconf();		// pass kernel environment to next kernel
	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();

#ifdef FUTURE
{
	ulong *pdb;
	/*
	 * 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 */
{
	void (*f)(ulong, ulong, ulong) = (void*)REBOOTADDR;

	memmove(f, rebootcode, sizeof(rebootcode));
#else
	USED(entry, code, size);
#endif

	print("rebooting...\n");
#ifdef FUTURE
	/* off we go - never to return */
	(*f)(PADDR(entry), PADDR(code), size);
}
#endif
	setupboot(0);		// reboot, don't halt
	exit(0);
}

void
exit(int)
exit(int ispanic)
{
	canlock(&active);
	active.machs &= ~(1<<m->machno);


@@ 277,9 402,21 @@ exit(int)

	splhi();
	delay(1000);	/* give serial fifo time to finish flushing */
	if (getconf("*debug") != nil) {
		USED(ispanic);
		delay(60*1000);		/* give us time to read the screen */
	}
	if(arch->coredetach)
		arch->coredetach();
	firmware();
	setupboot(1);			// set up to halt
	for (; ; )
		firmware();

	// on PC is just:
	if (0) {
		shutdown(ispanic);
		// arch->reset();
	}
}

void


@@ 414,7 551,7 @@ void
memholes(void)
{
	Bank *b, *eb;
	

	b = bootconf->bank;
	eb = b+bootconf->nbank;
	while(b < eb) {


@@ 453,10 590,10 @@ getconf(char *name)
{
	int n;

	for(n = 0; n < nconf; n++){
		if(cistrcmp(confname[n], name) == 0)
	for(n = 0; n < nconf; n++)
		if(cistrcmp(confname[n], name) == 0) {
			return confval[n];
	}
		}
	return 0;
}



@@ 501,7 638,7 @@ cistrcmp(char *a, char *b)
	for(;;){
		ac = *a++;
		bc = *b++;
	

		if(ac >= 'A' && ac <= 'Z')
			ac = 'a' + (ac - 'A');
		if(bc >= 'A' && bc <= 'Z')

M alphapc/pci.c => alphapc/pci.c +4 -5
@@ 197,15 197,14 @@ pciscan(int bno, Pcidev** list)
static void
pcicfginit(void)
{
/*	char *p; */
	char *p;

	lock(&pcicfginitlock);
	if(pcicfgmode == -1){
		pcicfgmode = 0;
/*		if(p = getconf("*pcimaxdno"))
			pcimaxdno = strtoul(p, 0, 0); */
pcimaxdno = 15 /* was 20; what is correct value??? */;

		pcimaxdno = 15;		/* was 20; what is correct value??? */
		if(p = getconf("*pcimaxdno"))
			pcimaxdno = strtoul(p, 0, 0);
		pciscan(0, &pciroot);
	}
	unlock(&pcicfginitlock);

M alphapc/random.c => alphapc/random.c +1 -138
@@ 1,138 1,1 @@
#include	"u.h"
#include	"../port/lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"../port/error.h"


struct Rb
{
	QLock;
	Rendez	producer;
	Rendez	consumer;
	ulong	randomcount;
	uchar	buf[1024];
	uchar	*ep;
	uchar	*rp;
	uchar	*wp;
	uchar	next;
	uchar	wakeme;
	ushort	bits;
	ulong	randn;
} rb;

static int
rbnotfull(void*)
{
	int i;

	i = rb.rp - rb.wp;
	return i != 1 && i != (1 - sizeof(rb.buf));
}

static int
rbnotempty(void*)
{
	return rb.wp != rb.rp;
}

void
genrandom(void*)
{
	up->basepri = PriNormal;
	up->priority = up->basepri;

	for(;;){
		for(;;)
			if(++rb.randomcount > 100000)
				break;
		if(anyhigher())
			sched();
		if(!rbnotfull(0))
			sleep(&rb.producer, rbnotfull, 0);
	}
}

/*
 *  produce random bits in a circular buffer
 */
static void
randomclock(void)
{
	if(rb.randomcount == 0 || !rbnotfull(0))
		return;

	rb.bits = (rb.bits<<2) ^ rb.randomcount;
	rb.randomcount = 0;

	rb.next++;
	if(rb.next != 8/2)
		return;
	rb.next = 0;

	*rb.wp ^= rb.bits;
	if(rb.wp+1 == rb.ep)
		rb.wp = rb.buf;
	else
		rb.wp = rb.wp+1;

	if(rb.wakeme)
		wakeup(&rb.consumer);
}

void
randominit(void)
{
	addclock0link(randomclock, 1000/HZ);
	rb.ep = rb.buf + sizeof(rb.buf);
	rb.rp = rb.wp = rb.buf;
	kproc("genrandom", genrandom, 0);
}

/*
 *  consume random bytes from a circular buffer
 */
ulong
randomread(void *xp, ulong n)
{
	uchar *e, *p;
	ulong x;

	p = xp;

	if(waserror()){
		qunlock(&rb);
		nexterror();
	}

	qlock(&rb);
	for(e = p + n; p < e; ){
		if(rb.wp == rb.rp){
			rb.wakeme = 1;
			wakeup(&rb.producer);
			sleep(&rb.consumer, rbnotempty, 0);
			rb.wakeme = 0;
			continue;
		}

		/*
		 *  beating clocks will be precictable if
		 *  they are synchronized.  Use a cheap pseudo
		 *  random number generator to obscure any cycles.
		 */
		x = rb.randn*1103515245 ^ *rb.rp;
		*p++ = rb.randn = x;

		if(rb.rp+1 == rb.ep)
			rb.rp = rb.buf;
		else
			rb.rp = rb.rp+1;
	}
	qunlock(&rb);
	poperror();

	wakeup(&rb.producer);

	return n;
}
#include "../pc/random.c"

M bitsy/main.c => bitsy/main.c +1 -1
@@ 145,7 145,7 @@ bootargs(ulong base)
	 */
	sp = (uchar*)base + BY2PG - sizeof(Sargs);

	bootpath = pusharg("/boot");
	bootpath = pusharg("/boot/boot");
	ac = 0;
	av[ac++] = pusharg("boot");


M boot/bootauth.c => boot/bootauth.c +3 -3
@@ 13,7 13,7 @@ authentication(int cpuflag)
	char *argv[16], **av;
	int ac;

	if(access("/factotum", AEXEC) < 0){
	if(access("/boot/factotum", AEXEC) < 0){
		glenda();
		return;
	}


@@ 38,8 38,8 @@ authentication(int cpuflag)
	case -1:
		fatal("starting factotum: %r");
	case 0:
		exec("/factotum", av);
		fatal("execing /factotum");
		exec("/boot/factotum", av);
		fatal("execing /boot/factotum");
	default:
		break;
	}

M boot/bootcache.c => boot/bootcache.c +3 -3
@@ 10,7 10,7 @@ cache(int fd)
	int argc, i, p[2];
	char *argv[5], bd[32], buf[256], partition[64], *pp;

	if(stat("/cfs", statbuf, sizeof statbuf) < 0)
	if(stat("/boot/cfs", statbuf, sizeof statbuf) < 0)
		return fd;

	*partition = 0;


@@ 63,9 63,9 @@ cache(int fd)
		dup(p[0], 1);
		close(p[0]);
		if(fflag)
			execl("/cfs", "bootcfs", "-rs", "-f", partition, 0);
			execl("/boot/cfs", "bootcfs", "-rs", "-f", partition, 0);
		else
			execl("/cfs", "bootcfs", "-s", "-f", partition, 0);
			execl("/boot/cfs", "bootcfs", "-s", "-f", partition, 0);
		break;
	default:
		close(p[0]);

M boot/bootip.c => boot/bootip.c +1 -1
@@ 60,7 60,7 @@ configip(void)
	case -1:
		fatal("configuring ip: %r");
	case 0:
		exec("/ipconfig", arg);
		exec("/boot/ipconfig", arg);
		fatal("execing /ipconfig");
	default:
		break;

M boot/embed.c => boot/embed.c +3 -3
@@ 28,7 28,7 @@ connectembed(void)
	Dir *dir;
	char **arg, **argp;

	dir = dirstat("/paqfs");
	dir = dirstat("/boot/paqfs");
	if(dir == nil)
		return -1;
	free(dir);


@@ 51,7 51,7 @@ connectembed(void)
	case 0:
		arg = malloc((bargc+5)*sizeof(char*));
		argp = arg;
		*argp++ = "/paqfs";
		*argp++ = "/boot/paqfs";
		*argp++ = "-iv";
		*argp++ = paqfile;
		for(i=1; i<bargc; i++)


@@ 62,7 62,7 @@ connectembed(void)
		dup(p[1], 1);
		close(p[0]);
		close(p[1]);
		exec("/paqfs", arg);
		exec("/boot/paqfs", arg);
		fatal("can't exec paqfs");
	default:
		break;

M boot/local.c => boot/local.c +2 -2
@@ 58,7 58,7 @@ connectlocal(void)
	char **arg, **argp;
	ulong mode;

	if(stat("/kfs", statbuf, sizeof statbuf) < 0)
	if(stat("/boot/kfs", statbuf, sizeof statbuf) < 0)
		return -1;

	dev = disk ? disk : bootdisk;


@@ 100,7 100,7 @@ connectlocal(void)
		dup(p[1], 1);
		close(p[0]);
		close(p[1]);
		exec("/kfs", arg);
		exec("/boot/kfs", arg);
		fatal("can't exec kfs");
	default:
		break;

M boot/paq.c => boot/paq.c +1 -1
@@ 55,7 55,7 @@ connectpaq(void)
		dup(p[1], 1);
		close(p[0]);
		close(p[1]);
		exec("/paqfs", arg);
		exec("/boot/paqfs", arg);
		fatal("can't exec paqfs");
	default:
		break;

M port/devroot.c => port/devroot.c +102 -38
@@ 5,61 5,90 @@
#include	"fns.h"
#include	"../port/error.h"

enum{
	Qdir=	0,
enum
{
	Qdir = 0,
	Qboot = 0x1000,

	Nfiles=32,	/* max root files */
	Nrootfiles = 32,
	Nbootfiles = 16,
};

extern ulong	bootlen;
extern uchar	bootcode[];
typedef struct Dirlist Dirlist;
struct Dirlist
{
	uint base;
	Dirtab *dir;
	uchar **data;
	int ndir;
	int mdir;
};

Dirtab rootdir[Nfiles] = {
	".",	{Qdir, 0, QTDIR},	0,		DMDIR|0555,
static Dirtab rootdir[Nrootfiles] = {
	"#/",		{Qdir, 0, QTDIR},	0,		DMDIR|0555,
	"boot",	{Qboot, 0, QTDIR},	0,		DMDIR|0555,
};
static uchar *rootdata[Nrootfiles];
static Dirlist rootlist = 
{
	0,
	rootdir,
	rootdata,
	2,
	Nrootfiles
};

static uchar	*rootdata[Nfiles];
static int	nroot = 1;
static Dirtab bootdir[Nbootfiles] = {
	"boot",	{Qboot, 0, QTDIR},	0,		DMDIR|0555,
};
static uchar *bootdata[Nbootfiles];
static Dirlist bootlist =
{
	Qboot,
	bootdir,
	bootdata,
	1,
	Nbootfiles
};

/*
 *  add a root file
 *  add a file to the list
 */
static void
addroot(char *name, uchar *contents, ulong len, int perm)
addlist(Dirlist *l, char *name, uchar *contents, ulong len, int perm)
{
	Dirtab *d;

	if(nroot >= Nfiles)
	if(l->ndir >= l->mdir)
		panic("too many root files");
	rootdata[nroot] = contents;
	d = &rootdir[nroot];
	l->data[l->ndir] = contents;
	d = &l->dir[l->ndir];
	strcpy(d->name, name);
	d->length = len;
	d->perm = perm;
	d->qid.type = 0;
	d->qid.vers = 0;
	d->qid.path = nroot+1;
	d->qid.path = ++l->ndir + l->base;
	if(perm & DMDIR)
		d->qid.type |= QTDIR;
	nroot++;
}

/*
 *  add a root file
 */
void
addrootfile(char *name, uchar *contents, ulong len)
addbootfile(char *name, uchar *contents, ulong len)
{
	addroot(name, contents, len, 0555);
	addlist(&bootlist, name, contents, len, 0555);
}

/*
 *  add a root file
 *  add a root directory
 */
static void
addrootdir(char *name)
{
	addroot(name, nil, 0, DMDIR|0555);
	addlist(&rootlist, name, nil, 0, DMDIR|0555);
}

static void


@@ 75,8 104,6 @@ rootreset(void)
	addrootdir("proc");
	addrootdir("root");
	addrootdir("srv");

	addrootfile("boot", bootcode, bootlen);	/* always have a boot file */
}

static Chan*


@@ 85,27 112,57 @@ rootattach(char *spec)
	return devattach('/', spec);
}

static int
rootgen(Chan *c, char *name, Dirtab*, int, int s, Dir *dp)
{
	int t;
	Dirtab *d;
	Dirlist *l;

	switch((int)c->qid.path){
	case Qdir:
		return devgen(c, name, rootlist.dir, rootlist.ndir, s, dp);
	case Qboot:
		if(s == DEVDOTDOT){
			devdir(c, (Qid){Qdir, 0, QTDIR}, "#/", 0, eve, 0555, dp);
			return 1;
		}
		return devgen(c, name, bootlist.dir, bootlist.ndir, s, dp);
	default:
		if((int)c->qid.path < Qboot){
			t = c->qid.path-1;
			l = &rootlist;
		}else{
			t = c->qid.path - Qboot - 1;
			l = &bootlist;
		}
		if(t >= l->ndir)
			return -1;
		if(s != 0)
			return -1;
		d = &l->dir[t];
		devdir(c, d->qid, d->name, d->length, eve, d->perm, dp);
		return 1;
	}
	return -1;
}

static Walkqid*
rootwalk(Chan *c, Chan *nc, char **name, int nname)
{
	return devwalk(c,  nc, name, nname, rootdir, nroot, devgen);
	return devwalk(c,  nc, name, nname, nil, 0, rootgen);
}

static int
rootstat(Chan *c, uchar *dp, int n)
{
	return devstat(c, dp, n, rootdir, nroot, devgen);
	return devstat(c, dp, n, nil, 0, rootgen);
}

static Chan*
rootopen(Chan *c, int omode)
{
	switch((ulong)c->qid.path) {
	default:
		break;
	}

	return devopen(c, omode, rootdir, nroot, devgen);
	return devopen(c, omode, nil, 0, devgen);
}

/*


@@ 121,17 178,26 @@ rootread(Chan *c, void *buf, long n, vlong off)
{
	ulong t;
	Dirtab *d;
	Dirlist *l;
	uchar *data;
	ulong offset = off;

	t = c->qid.path;
	switch(t){
	case Qdir:
		return devdirread(c, buf, n, rootdir, nroot, devgen);
	case Qboot:
		return devdirread(c, buf, n, nil, 0, rootgen);
	}

	if(t<Qboot)
		l = &rootlist;
	else{
		t -= Qboot;
		l = &bootlist;
	}

	d = &rootdir[t-1];
	data = rootdata[t-1];
	d = &l->dir[t-1];
	data = l->data[t-1];
	if(offset >= d->length)
		return 0;
	if(offset+n > d->length)


@@ 141,12 207,9 @@ rootread(Chan *c, void *buf, long n, vlong off)
}

static long
rootwrite(Chan *c, void*, long, vlong)
rootwrite(Chan*, void*, long, vlong)
{
	switch((ulong)c->qid.path){
	default:
		error(Egreg);
	}
	error(Egreg);
	return 0;
}



@@ 170,3 233,4 @@ Dev rootdevtab = {
	devremove,
	devwstat,
};


M port/portfns.h => port/portfns.h +1 -1
@@ 1,7 1,7 @@
void		accounttime(void);
void		addclock0link(void (*)(void), int);
int		addphysseg(Physseg*);
void		addrootfile(char*, uchar*, ulong);
void		addbootfile(char*, uchar*, ulong);
Block*		adjustblock(Block*, int);
void		alarmkproc(void*);
Block*		allocb(int);