Browse Source

sys/src/boot/vt4: import Xilinx Virtex-4 boot loader from Bell Labs (thanks Geoff Collyer)

David du Colombier 3 years ago
parent
commit
a31993f34b

+ 22 - 0
sys/src/boot/vt4/archvt4.c

@@ -0,0 +1,22 @@
+/* virtex 4 dependencies */
+#include "include.h"
+
+uvlong myhz = 300000000;	/* fixed 300MHz */
+uchar mymac[Eaddrlen] = { 0x00, 0x0A, 0x35, 0x01, 0x8B, 0xB1 };
+
+void
+clrmchk(void)
+{
+	putesr(0);			/* clears machine check */
+}
+
+/*
+ * virtex 4 systems always have 128MB.
+ */
+uintptr
+memsize(void)
+{
+	uintptr sz = 128*MB;
+
+	return securemem? MEMTOP(sz): sz;
+}

+ 464 - 0
sys/src/boot/vt4/boot.c

@@ -0,0 +1,464 @@
+#include "include.h"
+#include "ip.h"
+#include "/sys/src/libmach/elf.h"
+
+int	tftpupload(char *name, void *p, int len);
+
+extern ulong cpuentry;
+
+static uchar elfident[7] = {
+	'\177', 'E', 'L', 'F', '\1', '\1', '\1'
+};
+static Ehdr ehdr, rehdr;
+static Phdr *phdr;
+static int curphdr;
+static ulong curoff;
+static ulong elftotal;
+
+static long (*swal)(long);
+static ushort (*swab)(ushort);
+
+/*
+ * big-endian short
+ */
+ushort
+beswab(ushort s)
+{
+	uchar *p;
+
+	p = (uchar*)&s;
+	return (p[0]<<8) | p[1];
+}
+
+/*
+ * big-endian long
+ */
+long
+beswal(long l)
+{
+	uchar *p;
+
+	p = (uchar*)&l;
+	return (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3];
+}
+
+/*
+ * little-endian short
+ */
+ushort
+leswab(ushort s)
+{
+	uchar *p;
+
+	p = (uchar*)&s;
+	return (p[1]<<8) | p[0];
+}
+
+/*
+ * little-endian long
+ */
+long
+leswal(long l)
+{
+	uchar *p;
+
+	p = (uchar*)&l;
+	return (p[3]<<24) | (p[2]<<16) | (p[1]<<8) | p[0];
+}
+
+/*
+ * Convert header to canonical form
+ */
+static void
+hswal(long *lp, int n, long (*swap) (long))
+{
+	while (n--) {
+		*lp = (*swap) (*lp);
+		lp++;
+	}
+}
+
+static int
+readehdr(Boot *b)
+{
+	int i;
+
+	/* bitswap the header according to the DATA format */
+	if(ehdr.ident[CLASS] != ELFCLASS32) {
+		print("bad ELF class - not 32 bit\n");
+		return 0;
+	}
+	if(ehdr.ident[DATA] == ELFDATA2LSB) {
+		swab = leswab;
+		swal = leswal;
+	} else if(ehdr.ident[DATA] == ELFDATA2MSB) {
+		swab = beswab;
+		swal = beswal;
+	} else {
+		print("bad ELF encoding - not big or little endian\n");
+		return 0;
+	}
+	memmove(&rehdr, &ehdr, sizeof(Ehdr));
+
+	ehdr.type = swab(ehdr.type);
+	ehdr.machine = swab(ehdr.machine);
+	ehdr.version = swal(ehdr.version);
+	ehdr.elfentry = swal(ehdr.elfentry);
+	ehdr.phoff = swal(ehdr.phoff);
+	ehdr.shoff = swal(ehdr.shoff);
+	ehdr.flags = swal(ehdr.flags);
+	ehdr.ehsize = swab(ehdr.ehsize);
+	ehdr.phentsize = swab(ehdr.phentsize);
+	ehdr.phnum = swab(ehdr.phnum);
+	ehdr.shentsize = swab(ehdr.shentsize);
+	ehdr.shnum = swab(ehdr.shnum);
+	ehdr.shstrndx = swab(ehdr.shstrndx);
+	if(ehdr.type != EXEC || ehdr.version != CURRENT)
+		return 0;
+	if(ehdr.phentsize != sizeof(Phdr))
+		return 0;
+
+	if(debug)
+		print("readehdr OK entry 0x%lux\n", ehdr.elfentry);
+
+	curoff = sizeof(Ehdr);
+	i = ehdr.phoff+ehdr.phentsize*ehdr.phnum - curoff;
+	b->state = READPHDR;
+	b->bp = (char*)malloc(i);
+	b->wp = b->bp;
+	b->ep = b->wp + i;
+	phdr = (Phdr*)(b->bp + ehdr.phoff-sizeof(Ehdr));
+	if(debug)
+		print("phdr...");
+
+	return 1;
+}
+
+static int
+nextphdr(Boot *b)
+{
+	Phdr *php;
+	ulong entry, offset;
+	char *paddr;
+
+	if(debug)
+		print("readedata %d\n", curphdr);
+
+	for(; curphdr < ehdr.phnum; curphdr++){
+		php = phdr+curphdr;
+		if(php->type != LOAD)
+			continue;
+		offset = php->offset;
+		paddr = (char*)PADDR(php->paddr);
+		if(offset < curoff){
+			/*
+			 * Can't (be bothered to) rewind the
+			 * input, it might be from tftp. If we
+			 * did then we could boot FreeBSD kernels
+			 * too maybe.
+			 */
+			return 0;
+		}
+		if(php->offset > curoff){
+			b->state = READEPAD;
+			b->bp = (char*)malloc(offset - curoff);
+			b->wp = b->bp;
+			b->ep = b->wp + offset - curoff;
+			if(debug)
+				print("nextphdr %lud...\n", offset - curoff);
+			return 1;
+		}
+		b->state = READEDATA;
+		b->bp = paddr;
+		b->wp = b->bp;
+		b->ep = b->wp+php->filesz;
+		print("%ud+", php->filesz);
+		elftotal += php->filesz;
+		if(debug)
+			print("nextphdr %ud@0x%p\n", php->filesz, paddr);
+
+		return 1;
+	}
+
+	if(curphdr != 0){
+		print("=%lud\n", elftotal);
+		b->state = TRYBOOT;
+		entry = ehdr.elfentry & ~0xF0000000;
+		PLLONG(b->exec.entry, entry);
+		return 1;
+	}
+
+	return 0;
+}
+
+static int
+readepad(Boot *b)
+{
+	Phdr *php;
+
+	php = phdr+curphdr;
+	if(debug)
+		print("readepad %d\n", curphdr);
+	curoff = php->offset;
+
+	return nextphdr(b);
+}
+
+static int
+readedata(Boot *b)
+{
+	Phdr *php;
+
+	php = phdr+curphdr;
+	if(debug)
+		print("readedata %d\n", curphdr);
+	if(php->filesz < php->memsz){
+		print("%lud",  php->memsz-php->filesz);
+		elftotal += php->memsz-php->filesz;
+		memset((char*)(PADDR(php->paddr)+php->filesz), 0, php->memsz-php->filesz);
+	}
+	curoff = php->offset+php->filesz;
+	curphdr++;
+
+	return nextphdr(b);
+}
+
+static int
+readphdr(Boot *b)
+{
+	Phdr *php;
+
+	php = phdr;
+	hswal((long*)php, ehdr.phentsize*ehdr.phnum/sizeof(long), swal);
+	if(debug)
+		print("phdr curoff %lud vaddr 0x%lux paddr 0x%lux\n",
+			curoff, php->vaddr, php->paddr);
+
+	curoff = ehdr.phoff+ehdr.phentsize*ehdr.phnum;
+	curphdr = 0;
+
+	return nextphdr(b);
+}
+
+static ulong chksum, length;
+
+static void
+sum(void *vp, int len)
+{
+	unsigned long *p;
+
+	if (len % sizeof(int) != 0) {
+		print("sum: len %d not a multiple of word size\n",
+			len);
+		return;
+	}
+	p = vp;
+	len /= sizeof *p;
+	while (len-- > 0)
+		chksum += *p++;
+}
+
+static int
+addbytes(char **dbuf, char *edbuf, char **sbuf, char *esbuf)
+{
+	int n;
+	static ulong *start;
+
+	n = edbuf - *dbuf;
+	if(n <= 0)
+		return 0;
+	if(n > esbuf - *sbuf)
+		n = esbuf - *sbuf;
+	if(n <= 0)
+		return -1;
+
+	memmove(*dbuf, *sbuf, n);
+	coherence();
+
+	/* verify that the copy worked */
+	if (memcmp(*dbuf, *sbuf, n) != 0)
+		panic("addbytes: memmove copied %d bytes wrong from %#p to %#p",
+			n, *sbuf, *dbuf);
+	*sbuf += n;
+	*dbuf += n;
+	return edbuf - *dbuf;
+}
+
+int
+bootpass(Boot *b, void *vbuf, int nbuf)
+{
+	char *buf, *ebuf;
+	Exec *ep;
+	ulong entry, data, text, bss;
+
+	SET(text, data);
+	USED(text, data);
+	if(b->state == FAILED)
+		return FAIL;
+
+	if(nbuf == 0)
+		goto Endofinput;
+
+	buf = vbuf;
+	ebuf = buf+nbuf;
+	while(addbytes(&b->wp, b->ep, &buf, ebuf) == 0) {
+		switch(b->state) {
+		case INITKERNEL:
+			b->state = READEXEC;
+			b->bp = (char*)&b->exec;
+			b->wp = b->bp;
+			b->ep = b->bp+sizeof(Exec);
+			break;
+		case READEXEC:
+			ep = &b->exec;
+			if(GLLONG(ep->magic) == Q_MAGIC) {
+				b->state = READ9TEXT;
+				b->bp = (char*)PADDR(GLLONG(ep->entry));
+				b->wp = b->bp;
+				b->ep = b->wp+GLLONG(ep->text);
+				print("%lud", GLLONG(ep->text));
+				break;
+			}
+
+			/*
+			 * Check for ELF.
+			 */
+			if(memcmp(b->bp, elfident, 4) == 0){
+				b->state = READEHDR;
+				b->bp = (char*)&ehdr;
+				b->wp = b->bp;
+				b->ep = b->wp + sizeof(Ehdr);
+				memmove(b->bp, &b->exec, sizeof(Exec));
+				b->wp += sizeof(Exec);
+				print("elf...");
+				break;
+			}
+
+			print("bad kernel format\n");
+			b->state = FAILED;
+			return FAIL;
+
+		case READ9TEXT:
+			ep = &b->exec;
+			b->state = READ9DATA;
+			b->bp = (char*)UTROUND(PADDR(GLLONG(ep->entry)) +
+				GLLONG(ep->text));
+			b->wp = b->bp;
+			b->ep = b->wp + GLLONG(ep->data);
+			{
+				/*
+				 * fill the gap between text and first page
+				 * of data.
+				 */
+				int wds;
+				ulong *dst = (ulong *)(PADDR(GLLONG(ep->entry))+
+					GLLONG(ep->text));
+
+				for (wds = (ulong *)b->bp - dst; wds-- > 0;
+				    dst++)
+					*dst = (uintptr)dst;
+			}
+			print("+%ld", GLLONG(ep->data));
+			length = b->ep - (char *)PADDR(GLLONG(ep->entry));
+			break;
+	
+		case READ9DATA:
+			ep = &b->exec;
+			bss = GLLONG(ep->bss);
+			print("+%ld=%ld\n",
+				bss, GLLONG(ep->text)+GLLONG(ep->data)+bss);
+			b->state = TRYBOOT;
+			return ENOUGH;
+
+		case READEHDR:
+			if(!readehdr(b)){
+				print("readehdr failed\n");
+				b->state = FAILED;
+				return FAIL;
+			}
+			break;
+
+		case READPHDR:
+			if(!readphdr(b)){
+				b->state = FAILED;
+				return FAIL;
+			}
+			break;
+
+		case READEPAD:
+			if(!readepad(b)){
+				b->state = FAILED;
+				return FAIL;
+			}
+			break;
+
+		case READEDATA:
+			if(!readedata(b)){
+				b->state = FAILED;
+				return FAIL;
+			}
+			if(b->state == TRYBOOT)
+				return ENOUGH;
+			break;
+
+		case TRYBOOT:
+		case READGZIP:
+			return ENOUGH;
+
+		case READ9LOAD:
+		case INIT9LOAD:
+			panic("9load");
+
+		default:
+			panic("bootstate");
+		}
+	}
+	return MORE;
+
+
+Endofinput:
+	/* end of input */
+	switch(b->state) {
+	case INITKERNEL:
+	case READEXEC:
+	case READ9TEXT:
+	case READ9DATA:
+	case READEHDR:
+	case READPHDR:
+	case READEPAD:
+	case READEDATA:
+		print("premature EOF\n");
+		b->state = FAILED;
+		return FAIL;
+	
+	case TRYBOOT:
+		delay(100);
+		syncall();
+		entry = GLLONG(b->exec.entry);
+		dcflush(PADDR(entry), 4*1024*1024);		/* HACK */
+
+		sum((void *)PADDR(entry), length);
+		print("checksum: %#luX (on length %lud)\n", chksum, length);
+
+		print("entry: 0x%lux\n", entry);
+		delay(20);
+
+		cpuentry = entry;		/* for second cpu's use */
+		coherence();
+		dcflush((uintptr)&cpuentry, sizeof cpuentry);
+
+		warp9(PADDR(entry));
+
+		b->state = FAILED;
+		return FAIL;
+
+	case INIT9LOAD:
+	case READ9LOAD:
+		panic("end 9load");
+
+	default:
+		panic("bootdone");
+	}
+	b->state = FAILED;
+	return FAIL;
+}

+ 779 - 0
sys/src/boot/vt4/bootp.c

@@ -0,0 +1,779 @@
+#include "include.h"
+#include "ip.h"
+#include "fs.h"
+
+#define INIPATHLEN	64
+
+typedef struct Pxether Pxether;
+static struct Pxether {
+	Fs	fs;
+	char	ini[INIPATHLEN];
+} *pxether;
+
+extern int debug;
+extern int debugload;
+extern char *persist;
+
+uchar broadcast[Eaddrlen] = {
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+};
+
+static ushort tftpport = 5000;
+static int Id = 1;
+static int ctlrinuse;
+static Netaddr myaddr;
+static Netaddr server;
+
+typedef struct {
+	uchar	header[4];
+	uchar	data[Segsize];
+} Tftp;
+static Tftp *tftpbp;
+
+static void
+hnputs(uchar *ptr, ushort val)
+{
+	ptr[0] = val>>8;
+	ptr[1] = val;
+}
+
+static void
+hnputl(uchar *ptr, ulong val)
+{
+	ptr[0] = val>>24;
+	ptr[1] = val>>16;
+	ptr[2] = val>>8;
+	ptr[3] = val;
+}
+
+static ulong
+nhgetl(uchar *ptr)
+{
+	return ((ptr[0]<<24) | (ptr[1]<<16) | (ptr[2]<<8) | ptr[3]);
+}
+
+static ushort
+nhgets(uchar *ptr)
+{
+	return ((ptr[0]<<8) | ptr[1]);
+}
+
+static	short	endian	= 1;
+static	char*	aendian	= (char*)&endian;
+#define	LITTLE	*aendian
+
+static ushort
+ptcl_csum(void *a, int len)
+{
+	uchar *addr;
+	ulong t1, t2;
+	ulong losum, hisum, mdsum, x;
+
+	addr = a;
+	losum = 0;
+	hisum = 0;
+	mdsum = 0;
+
+	x = 0;
+	if((ulong)addr & 1) {
+		if(len) {
+			hisum += addr[0];
+			len--;
+			addr++;
+		}
+		x = 1;
+	}
+	while(len >= 16) {
+		t1 = *(ushort*)(addr+0);
+		t2 = *(ushort*)(addr+2);	mdsum += t1;
+		t1 = *(ushort*)(addr+4);	mdsum += t2;
+		t2 = *(ushort*)(addr+6);	mdsum += t1;
+		t1 = *(ushort*)(addr+8);	mdsum += t2;
+		t2 = *(ushort*)(addr+10);	mdsum += t1;
+		t1 = *(ushort*)(addr+12);	mdsum += t2;
+		t2 = *(ushort*)(addr+14);	mdsum += t1;
+		mdsum += t2;
+		len -= 16;
+		addr += 16;
+	}
+	while(len >= 2) {
+		mdsum += *(ushort*)addr;
+		len -= 2;
+		addr += 2;
+	}
+	if(x) {
+		if(len)
+			losum += addr[0];
+		if(LITTLE)
+			losum += mdsum;
+		else
+			hisum += mdsum;
+	} else {
+		if(len)
+			hisum += addr[0];
+		if(LITTLE)
+			hisum += mdsum;
+		else
+			losum += mdsum;
+	}
+
+	losum += hisum >> 8;
+	losum += (hisum & 0xff) << 8;
+	while(hisum = losum>>16)
+		losum = hisum + (losum & 0xffff);
+
+	return ~losum;
+}
+
+static ushort
+ip_csum(uchar *addr)
+{
+	int len;
+	ulong sum = 0;
+
+	len = (addr[0]&0xf)<<2;
+
+	while(len > 0) {
+		sum += addr[0]<<8 | addr[1] ;
+		len -= 2;
+		addr += 2;
+	}
+
+	sum = (sum & 0xffff) + (sum >> 16);
+	sum = (sum & 0xffff) + (sum >> 16);
+	return (sum^0xffff);
+}
+
+static void
+udpsend(int ctlrno, Netaddr *a, void *data, int dlen)
+{
+	Udphdr *uh;
+	Etherhdr *ip;
+	Etherpkt pkt;
+	int len, ptcllen;
+
+	uh = (Udphdr*)&pkt;
+
+	memset(uh, 0, sizeof(Etherpkt));
+	memmove(uh->udpcksum+sizeof(uh->udpcksum), data, dlen);
+
+	/*
+	 * UDP portion
+	 */
+	ptcllen = dlen + (UDP_HDRSIZE-UDP_PHDRSIZE);
+	uh->ttl = 0;
+	uh->udpproto = IP_UDPPROTO;
+	uh->frag[0] = 0;
+	uh->frag[1] = 0;
+	hnputs(uh->udpplen, ptcllen);
+	hnputl(uh->udpsrc, myaddr.ip);
+	hnputs(uh->udpsport, myaddr.port);
+	hnputl(uh->udpdst, a->ip);
+	hnputs(uh->udpdport, a->port);
+	hnputs(uh->udplen, ptcllen);
+	uh->udpcksum[0] = 0;
+	uh->udpcksum[1] = 0;
+	dlen = (dlen+1)&~1;
+	hnputs(uh->udpcksum, ptcl_csum(&uh->ttl, dlen+UDP_HDRSIZE));
+
+	/*
+	 * IP portion
+	 */
+	ip = (Etherhdr*)&pkt;
+	len = UDP_EHSIZE+UDP_HDRSIZE+dlen;		/* non-descriptive names */
+	ip->vihl = IP_VER|IP_HLEN;
+	ip->tos = 0;
+	ip->ttl = 255;
+	hnputs(ip->length, len-ETHER_HDR);
+	hnputs(ip->id, Id++);
+	ip->frag[0] = 0;
+	ip->frag[1] = 0;
+	ip->cksum[0] = 0;
+	ip->cksum[1] = 0;
+	hnputs(ip->cksum, ip_csum(&ip->vihl));
+
+	/*
+	 * Ethernet MAC portion
+	 */
+	hnputs(ip->type, ET_IP);
+	memmove(ip->d, a->ea, sizeof(ip->d));
+
+if(debug) {
+	print("udpsend ");
+}
+	/*
+	 * if packet is too short, make it longer rather than relying
+	 * on ethernet interface or lower layers to pad it.
+	 */
+	if (len < ETHERMINTU)
+		len = ETHERMINTU;
+	ethertxpkt(ctlrno, &pkt, len, Timeout);
+}
+
+static void
+nak(int ctlrno, Netaddr *a, int code, char *msg, int report)
+{
+	int n;
+	char buf[128];
+
+	buf[0] = 0;
+	buf[1] = Tftp_ERROR;
+	buf[2] = 0;
+	buf[3] = code;
+	strecpy(buf+4, buf + sizeof buf, msg);
+	n = strlen(msg) + 4 + 1;
+	udpsend(ctlrno, a, buf, n);
+	if(report)
+		print("\ntftp: error(%d): %s\n", code, msg);
+}
+
+void
+dump(void *vaddr, int words)
+{
+	ulong *addr;
+
+	addr = vaddr;
+	while (words-- > 0)
+		print("%.8lux%c", *addr++, words % 8 == 0? '\n': ' ');
+}
+
+static int
+udprecv(int ctlrno, Netaddr *a, void *data, int dlen)
+{
+	int n, len;
+	ushort csm;
+	Udphdr *h;
+	ulong addr, timo;
+	Etherpkt pkt;
+	static int rxactive;
+
+	if(rxactive == 0)
+		timo = 1000;
+	else
+		timo = Timeout;
+	timo += TK2MS(m->ticks);
+	while(timo > TK2MS(m->ticks)){
+		n = etherrxpkt(ctlrno, &pkt, timo - TK2MS(m->ticks));
+		if(n <= 0)
+			continue;
+
+		h = (Udphdr*)&pkt;
+		if(debug)
+			print("udprecv %E to %E...\n", h->s, h->d);
+
+		if(nhgets(h->type) != ET_IP) {
+			if(debug)
+				print("not ip...");
+			continue;
+		}
+
+		if(ip_csum(&h->vihl)) {
+			print("ip chksum error\n");
+			continue;
+		}
+		if(h->vihl != (IP_VER|IP_HLEN)) {
+			print("ip bad vers/hlen\n");
+			continue;
+		}
+
+		if(h->udpproto != IP_UDPPROTO) {
+			if(debug)
+				print("not udp (%d)...", h->udpproto);
+			continue;
+		}
+
+		if(debug)
+			print("okay udp...");
+
+		h->ttl = 0;
+		len = nhgets(h->udplen);
+		hnputs(h->udpplen, len);
+
+		if(nhgets(h->udpcksum)) {
+			csm = ptcl_csum(&h->ttl, len+UDP_PHDRSIZE);
+			if(csm != 0) {
+				print("udp chksum error csum #%4ux len %d\n",
+					csm, n);
+				break;
+			}
+		}
+
+		if(a->port != 0 && nhgets(h->udpsport) != a->port) {
+			if(debug)
+				print("udpport %ux not %ux\n",
+					nhgets(h->udpsport), a->port);
+			continue;
+		}
+
+		addr = nhgetl(h->udpsrc);
+		if(a->ip != Bcastip && a->ip != addr) {
+			if(debug)
+				print("bad ip %lux not %lux\n", addr, a->ip);
+			continue;
+		}
+
+		len -= UDP_HDRSIZE-UDP_PHDRSIZE;
+		if(len > dlen) {
+			print("udp: packet too big: %d > %d; from addr %E\n",
+				len, dlen, h->udpsrc);
+			continue;
+		}
+
+		memmove(data, h->udpcksum+sizeof(h->udpcksum), len);
+		a->ip = addr;
+		a->port = nhgets(h->udpsport);
+		memmove(a->ea, pkt.s, sizeof(a->ea));
+
+		rxactive = 1;
+		return len;
+	}
+
+	return 0;
+}
+
+static int tftpblockno;
+
+/*
+ * format of a request packet, from the RFC:
+ *
+            2 bytes     string    1 byte     string   1 byte
+            ------------------------------------------------
+           | Opcode |  Filename  |   0  |    Mode    |   0  |
+            ------------------------------------------------
+ */
+static int
+tftpopen(int ctlrno, Netaddr *a, char *name, Tftp *tftp, int op)
+{
+	int i, len, rlen, oport;
+	char *end;
+	static char buf[Segsize+2];	/* reduce stack use */
+
+	ctlrinuse = ctlrno;
+	buf[0] = 0;
+	buf[1] = op;
+	end = seprint(buf+2, buf + sizeof buf, "%s", name) + 1;
+	end = seprint(end, buf + sizeof buf, "octet") + 1;
+	len = end - buf;
+
+	oport = a->port;
+	for(i = 0; i < 5; i++){
+		a->port = oport;
+		udpsend(ctlrno, a, buf, len);
+		a->port = 0;
+		if((rlen = udprecv(ctlrno, a, tftp, sizeof(Tftp))) < sizeof(tftp->header))
+			continue;
+
+		switch((tftp->header[0]<<8)|tftp->header[1]){
+		case Tftp_ERROR:
+			print("tftpopen: error (%d): %s\n",
+				(tftp->header[2]<<8)|tftp->header[3],
+				(char*)tftp->data);
+			return -1;
+		case Tftp_DATA:
+			/* this should only happen when opening to read */
+			tftpblockno = 1;
+			len = (tftp->header[2]<<8)|tftp->header[3];
+			if(len != tftpblockno){
+				print("tftpopen: block error: %d\n", len);
+				nak(ctlrno, a, 1, "block error", 0);
+				return -1;
+			}
+			rlen -= sizeof(tftp->header);
+			if(rlen < Segsize){
+				/* ACK last block now, in case we don't later */
+				buf[0] = 0;
+				buf[1] = Tftp_ACK;
+				buf[2] = tftpblockno>>8;
+				buf[3] = tftpblockno;
+				udpsend(ctlrno, a, buf, sizeof(tftp->header));
+			}
+			return rlen;
+		case Tftp_ACK:
+			/* this should only happen when opening to write */
+			len = (tftp->header[2]<<8)|tftp->header[3];
+			if(len != 0){
+				print("tftpopen: block # error: %d != 0\n",
+					len);
+				nak(ctlrno, a, 1, "block # error", 0);
+				return -1;
+			}
+			return 0;
+		}
+	}
+
+	print("tftpopen: failed to connect to server\n");
+	return -1;
+}
+
+static int
+tftpread(int ctlrno, Netaddr *a, Tftp *tftp, int dlen)
+{
+	uchar buf[4];
+	int try, blockno, len;
+
+	dlen += sizeof(tftp->header);
+
+	for(try = 0; try < 10; try++) {
+		buf[0] = 0;
+		buf[1] = Tftp_ACK;
+		buf[2] = tftpblockno>>8;
+		buf[3] = tftpblockno;
+
+		udpsend(ctlrno, a, buf, sizeof(buf));
+		len = udprecv(ctlrno, a, tftp, dlen);
+		if(len <= sizeof(tftp->header)){
+			if(debug)
+				print("tftpread: too short %d <= %d\n",
+					len, sizeof(tftp->header));
+			continue;
+		}
+		blockno = (tftp->header[2]<<8)|tftp->header[3];
+		if(blockno <= tftpblockno){
+			if(debug)
+				print("tftpread: blkno %d <= %d\n",
+					blockno, tftpblockno);
+			continue;
+		}
+
+		if(blockno == tftpblockno+1) {
+			tftpblockno++;
+			if(len < dlen) {	/* last packet; send final ack */
+				tftpblockno++;
+				buf[0] = 0;
+				buf[1] = Tftp_ACK;
+				buf[2] = tftpblockno>>8;
+				buf[3] = tftpblockno;
+				udpsend(ctlrno, a, buf, sizeof(buf));
+			}
+			return len-sizeof(tftp->header);
+		}
+		print("tftpread: block error: %d, expected %d\n",
+			blockno, tftpblockno+1);
+	}
+
+	return -1;
+}
+
+static int
+bootpopen(int ctlrno, char *file, Bootp *rep, int dotftpopen)
+{
+	Bootp req;
+	int i, n;
+	uchar *ea;
+	char name[128], *filename, *sysname;
+
+	if (debugload)
+		print("bootpopen: ether%d!%s...", ctlrno, file);
+	if((ea = etheraddr(ctlrno)) == 0){
+		print("invalid ctlrno %d\n", ctlrno);
+		return -1;
+	}
+
+	filename = 0;
+	sysname = 0;
+	if(file && *file){
+		strecpy(name, name + sizeof name, file);
+		if(filename = strchr(name, '!')){
+			sysname = name;
+			*filename++ = 0;
+		}
+		else
+			filename = name;
+	}
+
+	memset(&req, 0, sizeof(req));
+	req.op = Bootrequest;
+	req.htype = 1;			/* ethernet */
+	req.hlen = Eaddrlen;		/* ethernet */
+	memmove(req.chaddr, ea, Eaddrlen);
+	if(filename != nil)
+		strncpy(req.file, filename, sizeof(req.file));
+	if(sysname != nil)
+		strncpy(req.sname, sysname, sizeof(req.sname));
+
+	myaddr.ip = 0;
+	myaddr.port = BPportsrc;
+	memmove(myaddr.ea, ea, Eaddrlen);
+
+	etherrxflush(ctlrno);
+	for(i = 0; i < 10; i++) {
+		server.ip = Bcastip;
+		server.port = BPportdst;
+		memmove(server.ea, broadcast, sizeof(server.ea));
+		udpsend(ctlrno, &server, &req, sizeof(req));
+		if(udprecv(ctlrno, &server, rep, sizeof(*rep)) <= 0)
+			continue;
+		if(memcmp(req.chaddr, rep->chaddr, Eaddrlen))
+			continue;
+		if(rep->htype != 1 || rep->hlen != Eaddrlen)
+			continue;
+		if(sysname == 0 || strcmp(sysname, rep->sname) == 0)
+			break;
+	}
+	if(i >= 10) {
+		print("bootp on ether%d for %s timed out\n", ctlrno, file);
+		return -1;
+	}
+
+	if(!dotftpopen)
+		return 0;
+
+	if(filename == 0 || *filename == 0){
+		if(strcmp(rep->file, "/386/9pxeload") == 0)
+			return -1;
+		filename = rep->file;
+	}
+
+	if(rep->sname[0] != '\0')
+		 print("%s ", rep->sname);
+	print("(%d.%d.%d.%d!%d): %s\n",
+		rep->siaddr[0],
+		rep->siaddr[1],
+		rep->siaddr[2],
+		rep->siaddr[3],
+		server.port,
+		filename);
+
+	myaddr.ip = nhgetl(rep->yiaddr);
+	myaddr.port = tftpport++;
+	server.ip = nhgetl(rep->siaddr);
+	server.port = TFTPport;
+
+	if((n = tftpopen(ctlrno, &server, filename, tftpbp, Tftp_READ)) < 0)
+		return -1;
+
+	return n;
+}
+
+int
+bootpboot(int ctlrno, char *file, Boot *b)
+{
+	int n;
+	Bootp rep;
+
+	if (tftpbp == nil)
+		tftpbp = malloc(sizeof *tftpbp);
+	if((n = bootpopen(ctlrno, file, &rep, 1)) < 0)
+		return -1;
+
+	while(bootpass(b, tftpbp->data, n) == MORE){
+		n = tftpread(ctlrno, &server, tftpbp, sizeof(tftpbp->data));
+		if(n < sizeof(tftpbp->data))
+			break;
+	}
+
+	if(0 < n && n < sizeof(tftpbp->data))	/* got to end of file */
+		bootpass(b, tftpbp->data, n);
+	else
+		nak(ctlrno, &server, 3, "ok", 0);	/* tftpclose to abort transfer */
+	bootpass(b, nil, 0);	/* boot if possible */
+	return -1;
+}
+
+static vlong
+pxediskseek(Fs*, vlong)
+{
+	return -1;
+}
+
+static long
+pxediskread(Fs*, void*, long)
+{
+	return -1;
+}
+
+static long
+pxeread(File* f, void* va, long len)
+{
+	int n;
+	Bootp rep;
+	char *p, *v;
+
+	if (pxether == nil)
+		pxether = malloc(MaxEther * sizeof *pxether);
+	if((n = bootpopen(f->fs->dev, pxether[f->fs->dev].ini, &rep, 1)) < 0)
+		return -1;
+
+	p = v = va;
+	while(n > 0) {
+		if((p-v)+n > len)
+			n = len - (p-v);
+		memmove(p, tftpbp->data, n);
+		p += n;
+		if(n != Segsize)
+			break;
+		if((n = tftpread(f->fs->dev, &server, tftpbp, sizeof(tftpbp->data))) < 0)
+			return -1;
+	}
+	return p-v;
+}
+
+static int
+pxewalk(File* f, char* name)
+{
+	Bootp rep;
+	char *ini;
+
+	switch(f->walked){
+	default:
+		return -1;
+	case 0:
+		if(strcmp(name, "cfg") == 0){
+			f->walked = 1;
+			return 1;
+		}
+		break;
+	case 1:
+		if(strcmp(name, "pxe") == 0){
+			f->walked = 2;
+			return 1;
+		}
+		break;
+	case 2:
+		if(strcmp(name, "%E") != 0)
+			break;
+		f->walked = 3;
+
+		if(bootpopen(f->fs->dev, nil, &rep, 0) < 0)
+			return 0;
+
+		if (pxether == nil)
+			pxether = malloc(MaxEther * sizeof *pxether);
+		ini = pxether[f->fs->dev].ini;
+		/* use our mac address instead of relying on a bootp answer */
+		seprint(ini, ini+INIPATHLEN, "/cfg/pxe/%E", (uchar *)myaddr.ea);
+		f->path = ini;
+
+		return 1;
+	}
+	return 0;
+}
+
+void*
+pxegetfspart(int ctlrno, char* part, int)
+{
+	Fs *fs;
+
+	if(!pxe || strcmp(part, "*") != 0 || ctlrno >= MaxEther ||
+	    iniread && getconf("*pxeini") != nil)
+		return nil;
+
+	if (pxether == nil)
+		pxether = malloc(MaxEther * sizeof *pxether);
+	fs = &pxether[ctlrno].fs;
+	fs->dev = ctlrno;
+	fs->diskread = pxediskread;
+	fs->diskseek = pxediskseek;
+
+	fs->read = pxeread;
+	fs->walk = pxewalk;
+
+	fs->root.fs = fs;
+	fs->root.walked = 0;
+
+	return fs;
+}
+
+/*
+ * tftp upload, for memory dumps and the like.
+ */
+
+void
+sendfile(int ctlrno, Netaddr *a, void *p, int len, char *name)
+{
+	int ackblock, block, ret, rexmit, rlen, n, txtry, rxl;
+	short op;
+	char *mem, *emem;
+	static uchar ack[1024], buf[Segsize+4];	/* reduce stack use */
+
+	block = 0;
+	rexmit = 0;
+	n = 0;
+	mem = p;
+	emem = mem + len;
+	for(txtry = 0; txtry < 10;) {
+		if(rexmit == 0) {
+			block++;
+			buf[0] = 0;
+			buf[1] = Tftp_DATA;
+			buf[2] = block>>8;
+			buf[3] = block;
+			if (mem < emem) {
+				if (emem - mem < Segsize)
+					n = emem - mem;
+				else
+					n = Segsize;
+				memmove(buf+4, mem, n);
+				mem += n;
+			} else
+				n = 0;
+			txtry = 0;
+		} else {
+			print("rexmit %d bytes to %s:%d\n", 4+n, name, block);
+			txtry++;
+		}
+
+		/* write buf to network */
+		udpsend(ctlrno, a, buf, 4+n);
+		ret = 4+n;
+
+		for(rxl = 0; rxl < 10; rxl++) {
+			rexmit = 0;
+			/* read ack from network */
+			memset(ack, 0, 32);
+			rlen = udprecv(ctlrno, a, ack, sizeof ack);
+			if(rlen < sizeof tftpbp->header) {
+				rexmit = 1;
+				print("reply too small\n");
+				break;
+			}
+			op = ack[0]<<8 | ack[1];
+			if(op == Tftp_ERROR) {
+				print("sendfile: tftp error\n");
+				break;
+			}
+			if (op != Tftp_ACK) {
+				print("expected ACK!\n");
+				continue;
+			}
+			ackblock = ack[2]<<8 | ack[3];
+			if(ackblock == block)
+				break;
+			else if(ackblock == 0xffff) {
+				rexmit = 1;
+				break;
+			} else
+				print("ack not for last block sent, "
+					"awaiting another\n");
+		}
+		if (rxl >= 10)
+			print("never got ack for block %d\n", block);
+		if(ret != Segsize+4 && rexmit == 0) {
+			print(" done.\n");
+			break;
+		}
+		if (0 && rexmit == 0)
+			print(".");
+	}
+	if (txtry >= 5)
+		print("too many rexmits\n");
+}
+
+int
+tftpupload(char *name, void *p, int len)
+{
+	int n;
+
+	if (tftpbp == nil)
+		tftpbp = malloc(sizeof *tftpbp);
+
+	/* assume myaddr and server are still set from downloading */
+	myaddr.port = tftpport++;
+	server.port = TFTPport;
+
+	n = tftpopen(ctlrinuse, &server, name, tftpbp, Tftp_WRITE);
+	if(n < 0)
+		return -1;
+	sendfile(ctlrinuse, &server, p, len, name);
+	return 0;
+}

+ 144 - 0
sys/src/boot/vt4/clock.c

@@ -0,0 +1,144 @@
+/* virtex[45] ppc4xx clock */
+#include "include.h"
+
+uvlong clockintrs;
+
+void
+delay(int l)
+{
+	ulong i, j;
+
+	j = m->delayloop;
+	while(l-- > 0)
+		for(i=0; i < j; i++)
+			;
+}
+
+void
+microdelay(int l)
+{
+	ulong i;
+
+	l *= m->delayloop;
+	l /= 1000;
+	if(l <= 0)
+		l = 1;
+	for(i = 0; i < l; i++)
+		;
+}
+
+enum {
+	Timebase = 1,	/* system clock cycles per time base cycle */
+
+	Wp17=	0<<30,	/* watchdog period (2^x clocks) */
+	Wp21=	1<<30,
+	Wp25=	2<<30,
+	Wp29=	3<<30,
+	Wrnone=	0<<28,	/* no watchdog reset */
+	Wrcore=	1<<28,	/* core reset */
+	Wrchip=	2<<28,	/* chip reset */
+	Wrsys=	3<<28,	/* system reset */
+	Wie=		1<<27,	/* watchdog interrupt enable */
+	Pie=		1<<26,	/* enable PIT interrupt */
+	Fit9=		0<<24,	/* fit period (2^x clocks) */
+	Fit13=	1<<24,
+	Fit17=	2<<24,
+	Fit21=	3<<24,
+	Fie=		1<<23,	/* fit interrupt enable */
+	Are=		1<<22,	/* auto reload enable */
+};
+
+void
+prcpuid(void)
+{
+	ulong pvr;
+	static char xilinx[] = "Xilinx ";
+	static char ppc[] = "PowerPC";
+
+	pvr = getpvr();
+	m->cputype = pvr >> 16;
+	print("cpu%d: %ldMHz %#lux (", m->machno, m->cpuhz/1000000, pvr);
+	switch (m->cputype) {
+	case 0x1291: case 0x4011: case 0x41f1: case 0x5091: case 0x5121:
+		print("%s 405", ppc);
+		break;
+	case 0x2001:			/* 200 is Xilinx, 1 is ppc405 */
+		print(xilinx);
+		switch (pvr & ~0xfff) {
+		case 0x20010000:
+			print("Virtex-II Pro %s 405", ppc);
+			break;
+		case 0x20011000:
+			print("Virtex 4 FX %s 405D5X2", ppc);
+			break;
+		default:
+			print("%s 405", ppc);
+			break;
+		}
+		break;
+	case 0x7ff2:
+		print(xilinx);
+		if ((pvr & ~0xf) == 0x7ff21910)
+			print("Virtex 5 FXT %s 440X5", ppc);
+		else
+			print("%s 440", ppc);
+		break;
+	default:
+		print(ppc);
+		break;
+	}
+	print(")\n");
+}
+
+void
+clockinit(void)
+{
+	int s;
+	long x;
+	vlong now;
+
+	s = splhi();
+	m->clockgen = m->cpuhz = myhz;
+	m->delayloop = m->cpuhz/1000;		/* initial estimate */
+	do {
+		x = gettbl();
+		delay(10);
+		x = gettbl() - x;
+	} while(x < 0);
+
+	/*
+	 *  fix count
+	 */
+	assert(x != 0);
+	m->delayloop = ((vlong)m->delayloop * (10*(vlong)m->clockgen/1000)) /
+		(x*Timebase);
+	if((int)m->delayloop <= 0)
+		m->delayloop = 20000;
+
+	x = (m->clockgen/Timebase)/HZ;
+// print("initial PIT %ld\n", x);
+	putpit(x);
+	puttsr(~0);
+	puttcr(Pie|Are);
+	coherence();
+	splx(s);
+
+	now = m->fastclock;
+	x = 50000000UL;
+	while (now == m->fastclock && x-- > 0)
+		;
+	if (now == m->fastclock)
+		print("clock is NOT ticking\n");
+}
+
+void
+clockintr(Ureg *ureg)
+{
+	/* PIT was set to reload automatically */
+	puttsr(~0);
+	m->fastclock++;
+	dcflush(PTR2UINT(&m->fastclock), sizeof m->fastclock); /* seems needed */
+	clockintrs++;
+	dcflush(PTR2UINT(&clockintrs), sizeof clockintrs);  /* seems needed */
+	timerintr(ureg);
+}

+ 483 - 0
sys/src/boot/vt4/conf.c

@@ -0,0 +1,483 @@
+#include "include.h"
+#include "fs.h"
+
+static char *confname[MAXCONF];
+static char *confval[MAXCONF];
+static int nconf;
+
+extern char **ini;
+
+typedef struct {
+	char*	name;
+	int	start;
+	int	end;
+} Mblock;
+
+typedef struct {
+	char*	tag;
+	Mblock*	mb;
+} Mitem;
+
+static Mblock mblock[MAXCONF];
+static int nmblock;
+static Mitem mitem[MAXCONF];
+static int nmitem;
+static char* mdefault;
+static char mdefaultbuf[10];
+static int mtimeout;
+
+static char*
+comma(char* line, char** residue)
+{
+	char *q, *r;
+
+	if((q = strchr(line, ',')) != nil){
+		*q++ = 0;
+		if(*q == ' ')
+			q++;
+	}
+	*residue = q;
+
+	if((r = strchr(line, ' ')) != nil)
+		*r = 0;
+
+	if(*line == ' ')
+		line++;
+	return line;
+}
+
+static Mblock*
+findblock(char* name, char** residue)
+{
+	int i;
+	char *p;
+
+	p = comma(name, residue);
+	for(i = 0; i < nmblock; i++){
+		if(strcmp(p, mblock[i].name) == 0)
+			return &mblock[i];
+	}
+	return nil;
+}
+
+static Mitem*
+finditem(char* name, char** residue)
+{
+	int i;
+	char *p;
+
+	p = comma(name, residue);
+	for(i = 0; i < nmitem; i++){
+		if(strcmp(p, mitem[i].mb->name) == 0)
+			return &mitem[i];
+	}
+	return nil;
+}
+
+static void
+parsemenu(char* str, char* scratch, int len)
+{
+	Mitem *mi;
+	Mblock *mb, *menu, *nmb;
+	char buf[20], *p, *q, *line[MAXCONF];
+	int i, inblock, n, show;
+
+	inblock = 0;
+	menu = nil;
+	memmove(scratch, str, len);
+	n = getfields(scratch, line, MAXCONF, '\n');
+	if(n >= MAXCONF)
+		print("warning: possibly too many lines in plan9.ini\n");
+	nmb = &mblock[nmblock];
+	for(i = 0; i < n; i++){
+		p = line[i];
+		if(inblock && *p == '['){
+			nmb->end = i;
+			if(strcmp(nmb->name, "menu") == 0)
+				menu = nmb;
+			nmblock++;
+			nmb++;
+			inblock = 0;
+		}
+		if(*p == '['){
+			if(nmblock == 0 && i != 0){
+				nmb->name = "common";
+				nmb->start = 0;
+				nmb->end = i;
+				nmblock++;
+				nmb++;
+			}
+			q = strchr(p+1, ']');
+			if(q == nil || *(q+1) != 0){
+				print("malformed menu block header - %s\n", p);
+				return;
+			}
+			*q = 0;
+			nmb->name = p+1;
+			nmb->start = i+1;
+			inblock = 1;
+		}
+	}
+
+	if(inblock){
+		(nmb++)->end = i;
+		nmblock++;
+	}
+	if(menu == nil)
+		return;
+	if(nmblock < 2){
+		print("incomplete menu specification\n");
+		return;
+	}
+
+	for(i = menu->start; i < menu->end; i++){
+		p = line[i];
+		if(cistrncmp(p, "menuitem=", 9) == 0){
+			p += 9;
+			if((mb = findblock(p, &q)) == nil){
+				print("no block for menuitem %s\n", p);
+				return;
+			}
+			if(q != nil)
+				mitem[nmitem].tag = q;
+			else
+				mitem[nmitem].tag = mb->name;
+			mitem[nmitem].mb = mb;
+			nmitem++;
+		}
+		else if(cistrncmp(p, "menudefault=", 12) == 0){
+			p += 12;
+			if((mi = finditem(p, &q)) == nil){
+				print("no item for menudefault %s\n", p);
+				return;
+			}
+			if(q != nil)
+				mtimeout = strtol(q, 0, 0);
+			seprint(mdefaultbuf, mdefaultbuf + sizeof mdefaultbuf,
+				"%ld", mi-mitem+1);
+			mdefault = mdefaultbuf;
+		}
+		else if(cistrncmp(p, "menuconsole=", 12) == 0){
+			p += 12;
+			p = comma(p, &q);
+			consinit(p, q);
+		}
+		else{
+			print("invalid line in [menu] block - %s\n", p);
+			return;
+		}
+	}
+
+again:
+	print("\nPlan 9 Startup Menu:\n====================\n");
+	for(i = 0; i < nmitem; i++)
+		print("    %d. %s\n", i+1, mitem[i].tag);
+	for(;;){
+		getstr("Selection", buf, sizeof(buf), mdefault, mtimeout);
+		mtimeout = 0;
+		i = strtol(buf, &p, 0)-1;
+		if(i < 0 || i >= nmitem)
+			goto again;
+		switch(*p){
+		case 'p':
+		case 'P':
+			show = 1;
+			print("\n");
+			break;
+		case 0:
+			show = 0;
+			break;
+		default:
+			continue;
+			
+		}
+		mi = &mitem[i];
+	
+		p = seprint(str, str + len, "menuitem=%s\n", mi->mb->name);
+		for(i = 0; i < nmblock; i++){
+			mb = &mblock[i];
+			if(mi->mb != mb && cistrcmp(mb->name, "common") != 0)
+				continue;
+			for(n = mb->start; n < mb->end; n++)
+				p = seprint(p, str + len, "%s\n", line[n]);
+		}
+
+		if(show){
+			for(q = str; q < p; q += i){
+				if((i = print(q)) <= 0)
+					break;
+			}
+			goto again;
+		}
+		break;
+	}
+	print("\n");
+}
+
+char*
+getconf(char *name)
+{
+	int i, n, nmatch;
+	char buf[20];
+
+	nmatch = 0;
+	for(i = 0; i < nconf; i++)
+		if(cistrcmp(confname[i], name) == 0)
+			nmatch++;
+
+	switch(nmatch) {
+	default:
+		print("\n");
+		nmatch = 0;
+		for(i = 0; i < nconf; i++)
+			if(cistrcmp(confname[i], name) == 0)
+				print("%d. %s\n", ++nmatch, confval[i]);
+		print("%d. none of the above\n", ++nmatch);
+		do {
+			getstr(name, buf, sizeof(buf), nil, 0);
+			n = strtoul(buf, 0, 10);
+		} while(n < 1 || n > nmatch);
+
+		for(i = 0; i < nconf; i++)
+			if(cistrcmp(confname[i], name) == 0)
+				if(--n == 0)
+					return confval[i];
+		break;
+
+	case 1:
+		for(i = 0; i < nconf; i++)
+			if(cistrcmp(confname[i], name) == 0)
+				return confval[i];
+		break;
+
+	case 0:
+		break;
+	}
+	return nil;
+}
+
+void
+addconf(char *fmt, ...)
+{
+	va_list arg;
+
+	va_start(arg, fmt);
+	vseprint(BOOTARGS+strlen(BOOTARGS), BOOTARGS+BOOTARGSLEN, fmt, arg);
+	va_end(arg);
+}
+
+void
+changeconf(char *fmt, ...)
+{
+	va_list arg;
+	char *p, *q, pref[20], buf[128];
+
+	va_start(arg, fmt);
+	vseprint(buf, buf+sizeof buf, fmt, arg);
+	va_end(arg);
+	strncpy(pref+1, buf, 19);
+	pref[19] = '\0';
+	if(p = strchr(pref, '='))
+		*(p+1) = '\0';
+	else
+		print("warning: did not change %s in plan9.ini\n", buf);
+
+	/* find old line by looking for \nwhat= */
+	pref[0] = '\n';
+	if(strncmp(BOOTARGS, pref+1, strlen(pref+1)) == 0)
+		p = BOOTARGS;
+	else if(p = strstr(BOOTARGS, pref))
+		p++;
+	else
+		p = nil;
+
+	/* move rest of args up, deleting what= line. */
+	if(p != nil && (q = strchr(p, '\n')) != nil)
+		memmove(p, q+1, strlen(q+1)+1);
+
+	/* add replacement to end */
+	addconf("%s", buf);
+}
+
+/*
+ *  read configuration file
+ */
+static char inibuf[BOOTARGSLEN];
+static char id[8] = "ZORT 0\r\n";
+
+int
+dotini(Fs *fs)
+{
+	File rc;
+	int blankline, i, incomment, inspace, n;
+	char *cp, *p, *q, *line[MAXCONF];
+
+	if(fswalk(fs, *ini, &rc) <= 0)
+		return -1;
+
+	cp = inibuf;
+	*cp = 0;
+	n = fsread(&rc, cp, BOOTARGSLEN-1);
+	if(n <= 0)
+		return -1;
+
+	cp[n] = 0;
+
+	/*
+	 * Strip out '\r', change '\t' -> ' '.
+	 * Change runs of spaces into single spaces.
+	 * Strip out trailing spaces, blank lines.
+	 *
+	 * We do this before we make the copy so that if we 
+	 * need to change the copy, it is already fairly clean.
+	 * The main need is in the case when plan9.ini has been
+	 * padded with lots of trailing spaces, as is the case 
+	 * for those created during a distribution install.
+	 */
+	p = cp;
+	blankline = 1;
+	incomment = inspace = 0;
+	for(q = cp; *q; q++){
+		if(*q == '\r')
+			continue;
+		if(*q == '\t')
+			*q = ' ';
+		if(*q == ' '){
+			inspace = 1;
+			continue;
+		}
+		if(*q == '\n'){
+			if(!blankline){
+				if(!incomment)
+					*p++ = '\n';
+				blankline = 1;
+			}
+			incomment = inspace = 0;
+			continue;
+		}
+		if(inspace){
+			if(!blankline && !incomment)
+				*p++ = ' ';
+			inspace = 0;
+		}
+		if(blankline && *q == '#')
+			incomment = 1;
+		blankline = 0;
+		if(!incomment)
+			*p++ = *q;	
+	}
+	if(p > cp && p[-1] != '\n')
+		*p++ = '\n';
+	*p++ = 0;
+	n = p-cp;
+
+	parsemenu(cp, BOOTARGS, n);
+
+	/*
+	 * Keep a copy.
+	 * We could change this to pass the parsed strings
+	 * to the booted programme instead of the raw
+	 * string, then it only gets done once.
+	 */
+	if(strncmp(cp, id, sizeof(id))){
+		memmove(BOOTARGS, id, sizeof(id));
+		if(n+1+sizeof(id) >= BOOTARGSLEN)
+			n -= sizeof(id);
+		memmove(BOOTARGS+sizeof(id), cp, n+1);
+	}
+	else
+		memmove(BOOTARGS, cp, n+1);
+
+	n = getfields(cp, line, MAXCONF, '\n');
+	for(i = 0; i < n; i++){
+		cp = strchr(line[i], '=');
+		if(cp == 0)
+			continue;
+		*cp++ = 0;
+		if(cp - line[i] >= NAMELEN+1)
+			*(line[i]+NAMELEN-1) = 0;
+		confname[nconf] = line[i];
+		confval[nconf] = cp;
+		nconf++;
+	}
+	return 0;
+}
+
+static int
+parseether(uchar *to, char *from)
+{
+	char nip[4];
+	char *p;
+	int i;
+
+	p = from;
+	while(*p == ' ')
+		++p;
+	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;
+}
+
+int
+isaconfig(char *class, int ctlrno, ISAConf *isa)
+{
+	char cc[NAMELEN], *p, *q, *r;
+	int n;
+
+	seprint(cc, cc + sizeof cc, "%s%d", class, ctlrno);
+	for(n = 0; n < nconf; n++){
+		if(cistrncmp(confname[n], cc, NAMELEN))
+			continue;
+		isa->nopt = 0;
+		p = confval[n];
+		while(*p){
+			while(*p == ' ' || *p == '\t')
+				p++;
+			if(*p == '\0')
+				break;
+			if(cistrncmp(p, "type=", 5) == 0){
+				p += 5;
+				for(q = isa->type; q < &isa->type[NAMELEN-1]; q++){
+					if(*p == '\0' || *p == ' ' || *p == '\t')
+						break;
+					*q = *p++;
+				}
+				*q = '\0';
+			}
+			else if(cistrncmp(p, "port=", 5) == 0)
+				isa->port = strtoul(p+5, &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, "ea=", 3) == 0){
+				if(parseether(isa->ea, p+3) == -1)
+					memset(isa->ea, 0, 6);
+			}
+			else if(isa->nopt < NISAOPT){
+				r = isa->opt[isa->nopt];
+				while(*p && *p != ' ' && *p != '\t'){
+					*r++ = *p++;
+					if(r-isa->opt[isa->nopt] >= ISAOPTLEN-1)
+						break;
+				}
+				*r = '\0';
+				isa->nopt++;
+			}
+			while(*p && *p != ' ' && *p != '\t')
+				p++;
+		}
+		return 1;
+	}
+	return 0;
+}

+ 185 - 0
sys/src/boot/vt4/console.c

@@ -0,0 +1,185 @@
+#include "include.h"
+
+#define uartputs vuartputs
+#define uartgets vuartgets
+#define uartputc vuartputc
+
+static int useuart = 1;
+
+int debug;
+
+void
+kbdinit(void)
+{
+}
+
+void
+consinit(char* name, char* speed)
+{
+	int port;
+
+	if(name == nil || cistrcmp(name, "cga") == 0)
+		return;
+	port = strtoul(name, 0, 0);
+	if(port < 0 || port > 1)
+		return;
+	USED(speed);
+
+	uartputs("\n", 1);
+}
+
+void
+consdrain(void)
+{
+//	if(useuart)
+//		uartdrain();
+}
+
+static void
+consputs(char* s, int n)
+{
+	if(useuart)
+		uartputs(s, n);
+}
+
+void
+warp86(char* s, ulong)
+{
+	if(s != nil)
+		print(s);
+	spllo();
+	consdrain();
+	print("Takes a licking and keeps on ticking...\n");
+	splhi();
+	for(;;)
+		;
+}
+
+static int
+getline(char *buf, int size, int timeout)
+{
+	int c, i=0;
+//	ulong start;
+	char echo;
+
+	USED(timeout);
+	for (;;) {
+//		start = m->ticks;
+//		do{
+//			/* timeout seconds to first char */
+//			if(timeout && ((m->ticks - start) > timeout*HZ))
+//				return -2;
+//			c = consiq.getc(&consiq);
+//		}while(c == -1);
+		c = vuartgetc();
+//		timeout = 0;
+
+		if(c == '\r')
+			c = '\n'; 		/* turn carriage return into newline */
+		if(c == '\177')
+			c = '\010';		/* turn delete into backspace */
+		if(c == '\025')
+			echo = '\n';		/* echo ^U as a newline */
+		else
+			echo = c;
+		consputs(&echo, 1);
+
+		if(c == '\010'){
+			if(i > 0)
+				i--; /* bs deletes last character */
+			continue;
+		}
+		/* a newline ends a line */
+		if (c == '\n')
+			break;
+		/* ^U wipes out the line */
+		if (c =='\025')
+			return -1;
+		if(i == size)
+			return size;
+		buf[i++] = c;
+	}
+	buf[i] = 0;
+	return i;
+}
+
+int
+getstr(char *prompt, char *buf, int size, char *def, int timeout)
+{
+	int len, isdefault;
+	char pbuf[PRINTSIZE];
+
+	buf[0] = 0;
+	isdefault = (def && *def);
+	if(isdefault == 0){
+		timeout = 0;
+		seprint(pbuf, pbuf + sizeof pbuf, "%s: ", prompt);
+	}
+	else if(timeout)
+		seprint(pbuf, pbuf + sizeof pbuf, "%s[default==%s (%ds timeout)]: ",
+			prompt, def, timeout);
+	else
+		seprint(pbuf, pbuf + sizeof pbuf, "%s[default==%s]: ",
+			prompt, def);
+	for (;;) {
+		print(pbuf);
+		consdrain();
+		len = getline(buf, size, timeout);
+		switch(len){
+		case 0:
+			/* RETURN */
+			if(isdefault)
+				break;
+			continue;
+		case -1:
+			/* ^U typed */
+			continue;
+		case -2:
+			/* timeout, use default */
+			consputs("\n", 1);
+			len = 0;
+			break;
+		default:
+			break;
+		}
+		if(len >= size){
+			print("line too long\n");
+			continue;
+		}
+		break;
+	}
+	if(len == 0 && isdefault)
+		strecpy(buf, buf + size, def);
+	return 0;
+}
+
+void
+panic(char *fmt, ...)
+{
+	int n;
+	va_list arg;
+	char buf[PRINTSIZE];
+
+	strecpy(buf, buf + sizeof buf, "panic: ");
+	va_start(arg, fmt);
+	n = vseprint(buf+7, buf+sizeof(buf)-7, fmt, arg) - buf;
+	va_end(arg);
+	buf[n] = '\n';
+	consputs(buf, n+1);
+
+	if (securemem) {
+		n = qtmerrfmt(buf, sizeof buf);
+		consputs(buf, n+1);
+	}
+
+//	splhi(); for(;;);
+	if(etherdetach)
+		etherdetach();
+
+//	consputs("\nPress almost any key to reset...", 32);
+	spllo();
+//	while(consiq.getc(&consiq) == -1)
+//		;
+	vuartgetc();
+	warp86(nil, 0);
+}

+ 232 - 0
sys/src/boot/vt4/data.h

@@ -0,0 +1,232 @@
+enum {
+	PRINTSIZE = 256,
+};
+
+#define		MS2NS(n) (((vlong)(n))*1000000LL)
+#define		TK2MS(x) ((x)*(1000/HZ))
+
+#define MB	(1024*1024)
+
+/*
+ * 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 3584 bytes available at CONFADDR on the PC.
+ *
+ * The low-level boot routines in l.s leave data for us at CONFADDR,
+ * which we pick up before reading the plan9.ini file.
+ */
+#define BOOTLINELEN	64
+#define BOOTARGS	((char*)(CONFADDR+BOOTLINELEN))
+#define	BOOTARGSLEN	1024	/* size reduction */
+#define	MAXCONF		15	/* from 100; size reduction */
+
+/*
+ * intc bits, as of 18 aug 2009.
+ * specific to rae's virtex4 design
+ * vanilla design defines Intuarttx.
+ */
+enum {
+	Bitllfifo,
+	Bittemac,
+	Bitdma,
+	Bitdma2,
+	Bituart,
+	Bitmiiphy,
+	Bitqtmmacfail,			/* qtm only */
+	Bitqtmdraminit,			/* qtm only */
+
+	Intllfifo=1<<Bitllfifo,		/* local-link FIFO */
+	Inttemac= 1<<Bittemac,
+	Intdma	= 1<<Bitdma,
+	Intdma2	= 1<<Bitdma2,
+	Intuart = 1<<Bituart,
+	Intmiiphy = 1<<Bitmiiphy,
+	Intqtmmacfail= 1<<Bitqtmmacfail,
+	Intqtmdraminit= 1<<Bitqtmdraminit,
+};
+
+typedef struct Boot Boot;
+typedef struct File File;
+typedef struct Fs Fs;
+typedef struct Lock Lock;
+typedef union Mach Mach;
+typedef struct Medium Medium;
+typedef u32int Mreg;				/* Msr - bloody UART */
+typedef struct Proc Proc;
+typedef struct Timer Timer;
+typedef struct Type Type;
+typedef struct Ureg Ureg;
+
+struct Lock
+{
+	uint	key;
+	ulong	sr;
+	ulong	pc;
+//	Proc	*p;
+	Mach	*m;
+	ushort	isilock;
+	long	lockcycles;
+};
+
+
+typedef union Mach {
+	struct {
+		int	machno;
+		void*	up;		/* offset known in assember */
+
+		/* ordering from here on irrelevant */
+
+		ulong	ticks;		/* of the clock since boot time */
+		long	oscclk;		/* oscillator frequency (MHz) */
+		long	cpuhz;		/* general system clock (cycles) */
+		long	clockgen;	/* clock generator frequency (cycles) */
+		long	vcohz;
+		long	pllhz;
+		long	plbhz;
+		long	opbhz;
+		long	epbhz;
+		long	pcihz;
+		int	cputype;
+		ulong	delayloop;
+//		uvlong	cyclefreq;	/* frequency of user readable cycle clock */
+		uvlong	fastclock;
+		int	intr;
+	};
+//	uintptr	stack[MACHSIZE/sizeof(uintptr)];	/* now in dram */
+} Mach;
+
+extern Mach* machptr[MAXMACH];
+
+#define	MACHP(n)	machptr[n]
+
+extern register Mach* m;
+extern register void* up;
+
+/*
+ * rest copied from /sys/src/boot/pc/dat.h:
+ */
+
+typedef struct Medium Medium;
+typedef struct Boot Boot;
+
+enum {					/* type */
+	Tether		= 0,
+	Tany		= -1,
+};
+
+enum {					/* name and flag */
+	Fnone		= 0x00,
+
+	Nfs		= 0x00,
+	Ffs		= (1<<Nfs),
+	Nboot		= 0x01,
+	Fboot		= (1<<Nboot),
+	Nbootp		= 0x02,
+	Fbootp		= (1<<Nbootp),
+	NName		= 3,
+
+	Fany		= Fbootp|Fboot|Ffs,
+
+	Fini		= 0x10,
+	Fprobe		= 0x80,
+};
+
+typedef struct Type {
+	ushort	type;
+	ushort	flag;
+	int	(*init)(void);
+	void	(*initdev)(int, char*);
+	void*	(*getfspart)(int, char*, int);	/* actually returns Dos* */
+	void	(*addconf)(int);
+	int	(*boot)(int, char*, Boot*);
+	void	(*printdevs)(int);
+	char**	parts;
+	char**	inis;
+	int	mask;
+	Medium*	media;
+} Type;
+
+extern void (*etherdetach)(void);
+
+#define	_MAGIC(f, b)	((f)|((((4*(b))+0)*(b))+7))
+#define	Q_MAGIC		_MAGIC(0, 21)		/* powerpc */
+
+typedef struct Exec Exec;
+struct	Exec
+{
+	uchar	magic[4];		/* magic number */
+	uchar	text[4];	 	/* size of text segment */
+	uchar	data[4];	 	/* size of initialized data */
+	uchar	bss[4];	  		/* size of uninitialized data */
+	uchar	syms[4];	 	/* size of symbol table */
+	uchar	entry[4];	 	/* entry point */
+	uchar	spsz[4];		/* size of sp/pc offset table */
+	uchar	pcsz[4];		/* size of pc/line number table */
+};
+
+typedef struct Block Block;
+struct Block {
+	Block*	next;
+	uchar*	rp;			/* first unconsumed byte */
+	uchar*	wp;			/* first empty byte */
+	uchar*	lim;			/* 1 past the end of the buffer */
+	uchar*	base;			/* start of the buffer */
+	ulong	flag;
+};
+#define BLEN(s)	((s)->wp - (s)->rp)
+
+enum {
+	Eaddrlen	= 6,
+	/* next two exclude 4-byte ether CRC */
+	ETHERMINTU	= 60,		/* minimum transmit size */
+	ETHERMAXTU	= 1514,		/* maximum transmit size */
+	ETHERHDRSIZE	= 14,		/* size of an ethernet header */
+
+	MaxEther	= 1,	/* from 6; size reduction */
+};
+
+typedef struct {
+	uchar	d[Eaddrlen];
+	uchar	s[Eaddrlen];
+	uchar	type[2];
+	uchar	data[1500];
+	uchar	crc[4];
+} Etherpkt;
+
+enum {	/* returned by bootpass */
+	MORE, ENOUGH, FAIL
+};
+enum {
+	INITKERNEL,
+	READEXEC,
+	READ9TEXT,
+	READ9DATA,
+	READGZIP,
+	READEHDR,
+	READPHDR,
+	READEPAD,
+	READEDATA,
+	TRYBOOT,
+	INIT9LOAD,
+	READ9LOAD,
+	FAILED
+};
+
+struct Boot {
+	int state;
+
+	Exec exec;
+	char *bp;	/* base ptr */
+	char *wp;	/* write ptr */
+	char *ep;	/* end ptr */
+};
+
+extern uvlong	clockintrs;
+extern int	debug;
+extern char	*defaultpartition;
+extern int	iniread;
+extern uvlong	myhz;
+extern int	pxe;
+extern int	securemem;
+extern int	vga;

+ 1 - 0
sys/src/boot/vt4/define.h

@@ -0,0 +1 @@
+#undef PARANOID

+ 251 - 0
sys/src/boot/vt4/ether.c

@@ -0,0 +1,251 @@
+#include "include.h"
+#include "ip.h"
+
+static Ether *ether;
+
+int debug;
+
+int	temacreset(Ether*);
+int	plbtemacreset(Ether*);
+int	isaconfig(char*, int, ISAConf*);
+
+typedef struct Ethercard Ethercard;
+struct Ethercard {
+	char	*type;
+	int	(*reset)(Ether*);
+	int	noprobe;
+} ethercards[] = {
+	{ "temac", temacreset, 0, },
+	{ 0, }
+};
+
+static void xetherdetach(void);
+
+int
+etherinit(void)		/* called from probe() */
+{
+	Ether *ctlr;
+	int ctlrno, i, mask, n, x;
+
+	fmtinstall('E', eipfmt);
+	fmtinstall('V', eipfmt);
+
+	etherdetach = xetherdetach;
+	mask = 0;
+	ether = malloc(MaxEther * sizeof *ether);
+	for(ctlrno = 0; ctlrno < MaxEther; ctlrno++){
+		ctlr = &ether[ctlrno];
+		memset(ctlr, 0, sizeof(Ether));
+//		if(isaconfig("ether", ctlrno, ctlr) == 0)
+//			continue;
+
+		for(n = 0; ethercards[n].type; n++){
+			Ethercard *ecp;
+
+			ecp = &ethercards[n];
+			if (1) {
+				if(ecp->noprobe)
+					continue;
+				memset(ctlr, 0, sizeof(Ether));
+//				strecpy(ctlr->type, &ctlr->type[NAMELEN],
+//					ecp->type);
+			}
+//			else if(cistrcmp(ecp->type, ctlr->type) != 0)
+//				continue;
+			ctlr->ctlrno = ctlrno;
+
+			x = splhi();
+			if((*ecp->reset)(ctlr)){
+				splx(x);
+				continue;
+			}
+
+			ctlr->state = 1;		/* card found */
+			mask |= 1<<ctlrno;
+//			setvec(VectorPIC + ctlr->irq, ctlr->interrupt, ctlr);
+			intrenable(Inttemac, ctlr->interrupt);
+
+			print("ether#%d: port 0x%lux", ctlr->ctlrno, ctlr->port);
+			if(ctlr->mem)
+				print(" addr 0x%luX", ctlr->mem & ~KZERO);
+			if(ctlr->size)
+				print(" size 0x%luX", ctlr->size);
+			print(": %E\n", ctlr->ea);
+		
+			if(ctlr->nrb == 0)
+				ctlr->nrb = Nrb;
+			ctlr->rb = ialloc(sizeof(RingBuf)*ctlr->nrb, 0);
+			if(ctlr->ntb == 0)
+				ctlr->ntb = Ntb;
+			ctlr->tb = ialloc(sizeof(RingBuf)*ctlr->ntb, 0);
+
+			ctlr->rh = 0;
+			ctlr->ri = 0;
+			for(i = 0; i < ctlr->nrb; i++)
+				ctlr->rb[i].owner = Interface;
+		
+			ctlr->th = 0;
+			ctlr->ti = 0;
+			for(i = 0; i < ctlr->ntb; i++)
+				ctlr->tb[i].owner = Host;
+
+			splx(x);
+			break;
+		}
+	}
+
+	return mask;
+}
+
+void
+etherinitdev(int i, char *s)
+{
+	seprint(s, s + NAMELEN, "ether%d", i);
+}
+
+void
+etherprintdevs(int i)
+{
+	print(" ether%d", i);
+}
+
+static Ether*
+attach(int ctlrno)
+{
+	Ether *ctlr;
+
+	if(ctlrno >= MaxEther || ether[ctlrno].state == 0)
+		return 0;
+
+	ctlr = &ether[ctlrno];
+	if(ctlr->state == 1){		/* card found? */
+		ctlr->state = 2;	/* attaching */
+		(*ctlr->attach)(ctlr);
+	}
+
+	return ctlr;
+}
+
+static void
+xetherdetach(void)
+{
+	Ether *ctlr;
+	int ctlrno, x;
+
+	x = splhi();
+	for(ctlrno = 0; ctlrno < MaxEther; ctlrno++){
+		ctlr = &ether[ctlrno];
+		if(ctlr->detach && ctlr->state != 0)	/* found | attaching? */
+			ctlr->detach(ctlr);
+	}
+	splx(x);
+}
+
+uchar*
+etheraddr(int ctlrno)
+{
+	Ether *ctlr;
+
+	if((ctlr = attach(ctlrno)) == 0)
+		return 0;
+
+	return ctlr->ea;
+}
+
+/* wait for owner of RingBuf to change from `owner' */
+static int
+wait(RingBuf* ring, uchar owner, int timo)
+{
+	ulong start;
+
+	start = m->ticks;
+	while(TK2MS(m->ticks - start) < timo)
+		if(ring->owner != owner)
+			return 1;
+	return 0;
+}
+
+int
+etherrxpkt(int ctlrno, Etherpkt* pkt, int timo)
+{
+	int n;
+	Ether *ctlr;
+	RingBuf *ring;
+
+	if((ctlr = attach(ctlrno)) == 0)
+		return 0;
+
+	ring = &ctlr->rb[ctlr->rh];
+	if(wait(ring, Interface, timo) == 0){
+		if(debug)
+			print("ether%d: rx timeout\n", ctlrno);
+		return 0;
+	}
+
+	n = ring->len;
+	memmove(pkt, ring->pkt, n);
+	coherence();
+	ring->owner = Interface;
+	ctlr->rh = NEXT(ctlr->rh, ctlr->nrb);
+
+	return n;
+}
+
+int
+etherrxflush(int ctlrno)
+{
+	int n;
+	Ether *ctlr;
+	RingBuf *ring;
+
+	if((ctlr = attach(ctlrno)) == 0)
+		return 0;
+
+	n = 0;
+	for(;;){
+		ring = &ctlr->rb[ctlr->rh];
+		if(wait(ring, Interface, 100) == 0)
+			break;
+
+		ring->owner = Interface;
+		ctlr->rh = NEXT(ctlr->rh, ctlr->nrb);
+		n++;
+	}
+
+	return n;
+}
+
+int
+ethertxpkt(int ctlrno, Etherpkt* pkt, int len, int)
+{
+	Ether *ctlr;
+	RingBuf *ring;
+	int s;
+
+	if((ctlr = attach(ctlrno)) == 0)
+		return 0;
+
+	ring = &ctlr->tb[ctlr->th];
+	if(wait(ring, Interface, 1000) == 0){
+		print("ether%d: tx buffer timeout\n", ctlrno);
+		return 0;
+	}
+
+	memmove(pkt->s, ctlr->ea, Eaddrlen);
+	if(debug)
+		print("%E to %E...\n", pkt->s, pkt->d);
+	memmove(ring->pkt, pkt, len);
+	if(len < ETHERMINTU){
+		memset(ring->pkt+len, 0, ETHERMINTU-len);
+		len = ETHERMINTU;
+	}
+	ring->len = len;
+	ring->owner = Interface;
+	ctlr->th = NEXT(ctlr->th, ctlr->ntb);
+	coherence();
+	s = splhi();
+	(*ctlr->transmit)(ctlr);
+	splx(s);
+
+	return 1;
+}

+ 68 - 0
sys/src/boot/vt4/etherif.h

@@ -0,0 +1,68 @@
+typedef struct RingBuf {
+	uchar	owner;
+	uchar	unused;
+	ushort	len;
+	uchar	pkt[sizeof(Etherpkt)];
+} RingBuf;
+
+enum {
+	Host		= 0,		/* buffer owned by host */
+	Interface	= 1,		/* buffer owned by card */
+
+	Nrb		= 32,		/* default number of receive buffers */
+	Ntb		= 8,		/* default number of transmit buffers */
+};
+
+/*
+ *  a parsed .ini line
+ */
+#define ISAOPTLEN	32
+#define NISAOPT		8
+#define NAMELEN		28
+
+typedef struct  ISAConf {
+	char	type[NAMELEN];
+	ulong	port;
+//	ulong	irq;
+	ulong	mem;
+	ulong	size;
+	uchar	ea[6];
+
+	int	nopt;
+	char	opt[NISAOPT][ISAOPTLEN];
+} ISAConf;
+
+#define	CONFADDR	(0x2200)		/* above ppc vectors */
+#define BOOTLINE	((char*)CONFADDR)
+
+typedef struct Ether Ether;
+struct Ether {
+	ISAConf;			/* hardware info */
+	ushort	ctlrno;
+	ushort	state;			/* 0: unfound, 1: found, 2: attaching */
+
+	void	(*attach)(Ether*);	/* filled in by reset routine */
+	void	(*transmit)(Ether*);
+	int	(*interrupt)(ulong bit);
+	void	(*detach)(Ether*);
+	void	*ctlr;
+
+	ushort	nrb;			/* number of software receive buffers */
+	ushort	ntb;			/* number of software transmit buffers */
+	RingBuf *rb;			/* software receive buffers */
+	RingBuf *tb;			/* software transmit buffers */
+
+	ushort	rh;			/* first receive buffer belonging to host */
+	ushort	ri;			/* first receive buffer belonging to card */	
+
+	ushort	th;			/* first transmit buffer belonging to host */	
+	ushort	ti;			/* first transmit buffer belonging to card */
+	ushort	tbusy;			/* transmitter is busy */
+	ushort	mbps;			/* zero means link down */	
+};
+
+extern void etherrloop(Ether*, Etherpkt*, long);
+extern void addethercard(char*, int(*)(Ether*));
+
+#define NEXT(x, l)	(((x)+1)%(l))
+#define PREV(x, l)	(((x) == 0) ? (l)-1: (x)-1)

+ 354 - 0
sys/src/boot/vt4/ethertemac.c

@@ -0,0 +1,354 @@
+/*
+ * Xilinx Temacs Ethernet driver.
+ * It uses the Local Link FIFOs.
+ * There are two interfaces per Temacs controller.
+ * Half-duplex is not supported by hardware.
+ */
+#include "include.h"
+
+enum {
+	/* fixed by hardware */
+	Nifcs = 2,
+
+	/* tunable parameters; see below for more */
+	Nrde = 64,
+	Ntde = 4,
+};
+enum {				/* directly-addressible registers' bits */
+	/* raf register */
+	Htrst	= 1<<0,		/* hard temac reset (both ifcs) */
+	Mcstrej	= 1<<1,		/* reject received multicast dest addr */
+	Bcstrej	= 1<<2,		/* reject received broadcast dest addr */
+
+	/* is, ip, ie register */
+	Hardacscmplt = 1<<0,	/* hard register access complete */
+	Autoneg	= 1<<1,		/* auto-negotiation complete */
+	Rxcmplt	= 1<<2,		/* receive complete */
+	Rxrject	= 1<<3,		/* receive frame rejected */
+	Rxfifoovr = 1<<4,	/* receive fifo overrun */
+	Txcmplt	= 1<<5,		/* transmit complete */
+	Rxdcmlock = 1<<6,	/* receive DCM lock (ready for use) */
+	Mgtrdy	= 1<<7,		/* mgt ready (new in 1.01b)
+
+	/* ctl register */
+	Wen	= 1<<15,	/* write instead of read */
+
+	/* ctl register address codes; select other registers */
+	Rcw0	= 0x200,	/* receive configuration */
+	Rcw1	= 0x240,
+	Tc	= 0x280,	/* tx config */
+	Fcc	= 0x2c0,	/* flow control */
+	Emmc	= 0x300,	/* ethernet mac mode config */
+	Phyc	= 0x320,	/* rgmii/sgmii config */
+	Mc	= 0x340,	/* mgmt config */
+	Uaw0	= 0x380,	/* unicast addr word 0 (low-order) */
+	Uaw1	= 0x384,	/* unicast addr word 1 (high-order) */
+	Maw0	= 0x388,	/* multicast addr word 0 (low) */
+	Maw1	= 0x38c,	/* multicast addr word 1 (high + more) */
+	Afm	= 0x390,	/* addr filter mode */
+	Tis	= 0x3a0,	/* intr status */
+	Tie	= 0x3a4,	/* intr enable */
+	Miimwd	= 0x3b0,	/* mii mgmt write data */
+	Miimai	= 0x3b4,	/* mii mgmt access initiate */
+
+	/* rdy register */
+	Fabrrr	= 1<<0,		/* fabric read ready */
+	Miimrr	= 1<<1,		/* mii mgmt read ready */
+	Miimwr	= 1<<2,		/* mii mgmt write ready */
+	Afrr	= 1<<3,		/* addr filter read ready */
+	Afwr	= 1<<4,		/* addr filter write ready */
+	Cfgrr	= 1<<5,		/* config reg read ready */
+	Cfgwr	= 1<<6,		/* config reg write ready */
+	Hardacsrdy = 1<<16,	/* hard reg access ready */
+};
+enum {				/* indirectly-addressible registers' bits */
+	/* Rcw1 register */
+	Rst	= 1<<31,	/* reset */
+	Jum	= 1<<30,	/* jumbo frame enable */
+	Fcs	= 1<<29,	/* in-band fcs enable */
+	Rx	= 1<<28,	/* rx enable */
+	Vlan	= 1<<27,	/* vlan frame enable */
+	Hd	= 1<<26,	/* half-duplex mode (must be 0) */
+	Ltdis	= 1<<25,	/* length/type field valid check disable */
+
+	/* Tc register.  same as Rcw1 but Rx->Tx, Ltdis->Ifg */
+	Tx	= Rx,		/* tx enable */
+	Ifg	= Ltdis,	/* inter-frame gap adjustment enable */
+
+	/* Fcc register */
+	Fctx	= 1<<30,	/* tx flow control enable */
+	Fcrx	= 1<<29,	/* rx flow control enable */
+
+	/* Emmc register */
+	Linkspeed = 3<<30,	/* field */
+	Ls1000	= 2<<30,	/* Gb */
+	Ls100	= 1<<30,	/* 100Mb */
+	Ls10	= 0<<30,	/* 10Mb */
+	Rgmii	= 1<<29,	/* rgmii mode enable */
+	Sgmii	= 1<<28,	/* sgmii mode enable */
+	Gpcs	= 1<<27,	/* 1000base-x mode enable */
+	Hostifen= 1<<26,	/* host interface enable */
+	Tx16	= 1<<25,	/* tx 16-bit (vs 8-bit) data ifc enable (0) */
+	Rx16	= 1<<24,	/* rx 16-bit (vs 8-bit) data ifc enable (0) */
+
+	/* Phyc register.  sgmii link speed is Emmc's Linkspeed. */
+	Rgmiills = 3<<2,	/* field */
+	Rls1000	= 2<<2,		/* Gb */
+	Rls100	= 1<<2,		/* 100Mb */
+	Rls10	= 0<<2,		/* 10Mb */
+	Rgmiihd	= 1<<1,		/* half-duplex */
+	Rgmiilink = 1<<0,	/* rgmii link (is up) */
+
+	/* Mc register */
+	Mdioen	= 1<<6,		/* mdio (mii mgmt) enable */
+
+	/* Maw1 register */
+	Rnw	= 1<<23,	/* multicast addr table reg read (vs write) */
+	Addr	= 3<<16,	/* field */
+
+	/* Afm register */
+	Pm	= 1<<31,	/* promiscuous mode */
+
+	/* Tis, Tie register (*rst->*en) */
+	Fabrrst	= 1<<0,		/* fabric read intr sts (read done) */
+	Miimrst	= 1<<1,		/* mii mgmt read intr sts (read done) */
+	Miimwst	= 1<<2,		/* mii mgmt write intr sts (write done) */
+	Afrst	= 1<<3,		/* addr filter read intr sts (read done) */
+	Afwst	= 1<<4,		/* addr filter write intr sts (write done) */
+	Cfgrst	= 1<<5,		/* config read intr sts (read done) */
+	Cfgwst	= 1<<6,		/* config write intr sts (write done) */
+};
+
+enum {
+	/* tunable parameters */
+	Defmbps	= 1000,		/* default Mb/s */
+	Defls	= Ls1000,	/* must match Defmbps */
+};
+
+typedef struct Temacsw Temacsw;
+typedef struct Temacregs Temacregs;
+struct Temacregs {
+	ulong	raf;		/* reset & addr filter */
+	ulong	tpf;		/* tx pause frame */
+	ulong	ifgp;		/* tx inter-frame gap adjustment */
+	ulong	is;		/* intr status */
+	ulong	ip;		/* intr pending */
+	ulong	ie;		/* intr enable */
+	ulong	pad[2];
+
+	ulong	msw;		/* msw data; shared by ifcs */
+	ulong	lsw;		/* lsw data; shared */
+	ulong	ctl;		/* control; shared */
+	ulong	rdy;		/* ready status */
+	ulong	pad2[4];
+};
+struct Temacsw {
+	Temacregs *regs;
+};
+
+extern uchar mymac[Eaddrlen];
+
+static Ether *ethers[1];	/* only first ether is connected to a fifo */
+static Lock shreglck;		/* protects shared registers */
+
+static void	transmit(Ether *ether);
+
+static void
+getready(Temacregs *trp)
+{
+	while ((trp->rdy & Hardacsrdy) == 0)
+		;
+}
+
+static ulong
+rdindir(Temacregs *trp, unsigned code)
+{
+	ulong val;
+
+	ilock(&shreglck);
+	getready(trp);
+	trp->ctl = code;
+	coherence();
+
+	getready(trp);
+	val = trp->lsw;
+	iunlock(&shreglck);
+	return val;
+}
+
+static int
+wrindir(Temacregs *trp, unsigned code, ulong val)
+{
+	ilock(&shreglck);
+	getready(trp);
+	trp->lsw = val;
+	coherence();
+	trp->ctl = Wen | code;
+	coherence();
+
+	getready(trp);
+	iunlock(&shreglck);
+	return 0;
+}
+
+static int
+interrupt(ulong bit)
+{
+	int e, r, sts;
+	Ether *ether;
+	Temacsw *ctlr;
+
+	r = 0;
+	for (e = 0; e < MaxEther; e++) {
+		ether = ethers[e];
+		if (ether == nil)
+			continue;
+		ctlr = ether->ctlr;
+		sts = ctlr->regs->is;
+		if (sts)
+			r = 1;
+		ctlr->regs->is = sts;	/* extinguish intr source */
+		coherence();
+		sts &= ~(Rxcmplt | Txcmplt | Rxdcmlock | Mgtrdy);
+		if (sts)
+			iprint("ethertemac: sts %#ux\n", sts);
+	}
+	if (r)
+		intrack(bit);
+	return r;
+}
+
+static void
+reset(Ether *ether)
+{
+	Temacsw *ctlr;
+	Temacregs *trp;
+
+	ctlr = ether->ctlr;
+	trp = ctlr->regs;
+	trp->ie = 0;
+	coherence();
+	/* don't use raf to reset: that resets both interfaces */
+	wrindir(trp, Tc,   Rst);
+	while (rdindir(trp, Tc) & Rst)
+		;
+	wrindir(trp, Rcw1, Rst);
+	while (rdindir(trp, Rcw1) & Rst)
+		;
+	llfiforeset();
+}
+
+static void
+attach(Ether *)
+{
+}
+
+static void
+transmit(Ether *ether)
+{
+	RingBuf *tb;
+
+	if (ether->tbusy)
+		return;
+	tb = &ether->tb[ether->ti];
+	if (tb->owner != Interface)
+		return;
+	llfifotransmit(tb->pkt, tb->len);
+	coherence();
+	tb->owner = Host;
+	coherence();
+	ether->ti = NEXT(ether->ti, ether->ntb);
+	coherence();
+}
+
+static void
+detach(Ether *ether)
+{
+	reset(ether);
+}
+
+int
+temacreset(Ether* ether)
+{
+	int i;
+	ulong ealo, eahi;
+	uvlong ea;
+	Temacsw *ctlr;
+	Temacregs *trp;
+
+	if ((unsigned)ether->ctlrno >= nelem(ethers) || ethers[ether->ctlrno])
+		return -1;		/* already probed & found */
+	trp = (Temacregs *)Temac + ether->ctlrno;
+	if (probeaddr((uintptr)trp) < 0)
+		return -1;
+
+	ethers[ether->ctlrno] = ether;
+	ether->ctlr = ctlr = malloc(sizeof *ctlr);
+	ctlr->regs = trp;
+
+	/*
+	 * Determine media.
+	 */
+	ether->mbps = Defmbps;
+//	ether->mbps = media(ether, 1);
+
+	/*
+	 * Initialise descriptor rings, ethernet address.
+	 */
+	ether->nrb = Nrde;
+	ether->ntb = Ntde;
+	ether->rb = malloc(Nrde * sizeof(RingBuf));
+	ether->tb = malloc(Ntde * sizeof(RingBuf));
+	ether->port = Temac;
+
+	reset(ether);
+	delay(1);
+
+	llfifoinit(ether);
+
+	wrindir(trp, Mc, Mdioen | 29);	/* 29 is divisor; see p.47 of ds537 */
+	delay(100);			/* guess */
+
+	/*
+	 * mac addr is stored little-endian in longs in Uaw[01].
+	 * default address is rubbish.
+	 */
+	memmove(ether->ea, mymac, Eaddrlen);
+	ea = 0;
+	for (i = 0; i < Eaddrlen; i++)
+		ea |= (uvlong)mymac[i] << (i * 8);
+	wrindir(trp, Uaw0, (ulong)ea);
+	wrindir(trp, Uaw1, (ulong)(ea >> 32));
+	ealo = rdindir(trp, Uaw0);
+	eahi = rdindir(trp, Uaw1) & 0xffff;
+	if (ealo != (ulong)ea || eahi != (ulong)(ea >> 32))
+		panic("temac mac address wouldn't set, got %lux %lux",
+			eahi, ealo);
+
+	/*
+	 * admit broadcast packets too
+	 */
+	wrindir(trp, Maw0, ~0ul);
+	wrindir(trp, Maw1, 0xffff);	/* write to mat reg 0 */
+
+	wrindir(trp, Afm, 0);		/* not promiscuous */
+	wrindir(trp, Tc, Tx);
+	wrindir(trp, Emmc, Defls);
+
+/*	intrenable(Inttemac, interrupt); /* done by ether.c */
+	trp->ie = Rxrject | Rxfifoovr;	/* just errors */
+	coherence();
+
+	wrindir(trp, Tc,   Tx);
+	wrindir(trp, Rcw1, Rx);
+
+	/*
+	 * Linkage to the generic ethernet driver.
+	 */
+	ether->attach = attach;
+	ether->transmit = transmit;
+	ether->interrupt = interrupt;
+	ether->detach = detach;
+
+	return 0;
+}

+ 23 - 0
sys/src/boot/vt4/fakeqtm.c

@@ -0,0 +1,23 @@
+#include "include.h"
+
+int
+qtmerrfmt(char *, int)
+{
+	return 0;
+}
+
+void
+qtmerrtest(char *)
+{
+}
+
+void
+qtmerrtestaddr(ulong)
+{
+}
+
+int
+qtmmemreset(void)
+{
+	return 0;
+}

+ 94 - 0
sys/src/boot/vt4/fs.c

@@ -0,0 +1,94 @@
+#include "include.h"
+#include "fs.h"
+
+/*
+ *  grab next element from a path, return the pointer to unprocessed portion of
+ *  path.
+ */
+char *
+nextelem(char *path, char *elem)
+{
+	int i;
+
+	while(*path == '/')
+		path++;
+	if(*path==0 || *path==' ')
+		return 0;
+	for(i=0; *path!='\0' && *path!='/' && *path!=' '; i++){
+		if(i==NAMELEN){
+			print("name component too long\n");
+			return 0;
+		}
+		*elem++ = *path++;
+	}
+	*elem = '\0';
+	return path;
+}
+
+int
+fswalk(Fs *fs, char *path, File *f)
+{
+	char element[NAMELEN];
+
+	*f = fs->root;
+	if(BADPTR(fs->walk))
+		panic("fswalk bad pointer fs->walk");
+
+	f->path = path;
+	while(path = nextelem(path, element)){
+		switch(fs->walk(f, element)){
+		case -1:
+			return -1;
+		case 0:
+			return 0;
+		}
+	}
+	return 1;
+}
+
+enum {
+	Bufsize = 8192,
+};
+
+/*
+ *  boot
+ */
+int
+fsboot(Fs *fs, char *path, Boot *b)
+{
+	File file;
+	long n;
+	static char *buf;
+
+	if (buf == nil)
+		buf = malloc(Bufsize + 1);
+	switch(fswalk(fs, path, &file)){
+	case -1:
+		print("error walking to %s\n", path);
+		return -1;
+	case 0:
+		print("%s not found\n", path);
+		return -1;
+	case 1:
+		print("found %s\n", path);
+		break;
+	}
+
+	while((n = fsread(&file, buf, Bufsize)) > 0) {
+		if(bootpass(b, buf, n) != MORE)
+			break;
+	}
+
+	bootpass(b, nil, 0);	/* tries boot */
+	return -1;
+}
+
+int
+fsread(File *file, void *a, long n)
+{
+	if(BADPTR(file->fs))
+		panic("bad pointer file->fs in fsread");
+	if(BADPTR(file->fs->read))
+		panic("bad pointer file->fs->read in fsread");
+	return file->fs->read(file, a, n);
+}

+ 22 - 0
sys/src/boot/vt4/fs.h

@@ -0,0 +1,22 @@
+struct File{
+	int	walked;
+	Fs	*fs;
+	char	*path;
+};
+
+struct Fs{
+	int	dev;				/* device id */
+	long	(*diskread)(Fs*, void*, long);	/* disk read routine */
+	vlong	(*diskseek)(Fs*, vlong);	/* disk seek routine */
+	long	(*read)(File*, void*, long);
+	int	(*walk)(File*, char*);
+	File	root;
+};
+
+extern int chatty;
+extern int dotini(Fs*);
+extern int fswalk(Fs*, char*, File*);
+extern int fsread(File*, void*, long);
+extern int fsboot(Fs*, char*, Boot*);
+
+#define BADPTR(x) (0 && (ulong)x < 0x80000000)

+ 9 - 0
sys/src/boot/vt4/include.h

@@ -0,0 +1,9 @@
+#include "u.h"
+#include "libc.h"
+#include "ureg.h"
+#include <thread.h>
+#include "ppc405.h"
+#include "define.h"
+#include "data.h"
+#include "etherif.h"
+#include "prototype.h"

+ 101 - 0
sys/src/boot/vt4/intr.c

@@ -0,0 +1,101 @@
+/* Xilink XPS interrupt controller */
+
+#include "include.h"
+
+enum {
+	/* mer bits */
+	Merme	= 1<<0,		/* master enable */
+	Merhie	= 1<<1,		/* hw intr enable */
+
+	Maxintrs = 8,		/* from 32; size reduction */
+};
+
+typedef struct {
+	ulong	isr;		/* status */
+	ulong	ipr;		/* pending (ro) */
+	ulong	ier;		/* enable */
+	ulong	iar;		/* acknowledge (wo) */
+	ulong	sieb;		/* set ie bits; avoid */
+	ulong	cieb;		/* clear ie bits; avoid */
+	ulong	ivr;		/* vector; silly */
+	ulong	mer;		/* master enable */
+} Intregs;
+
+typedef struct {
+	ulong	bit;
+	int	(*func)(ulong);
+} Intr;
+
+Intregs *irp = (Intregs *)Intctlr;
+Intr intrs[Maxintrs];
+Intr *nextintr;
+
+static uvlong extintrs;
+
+/* called from trap to poll for external interrupts */
+void
+intr(Ureg *)
+{
+	int handled;
+	Intr *ip;
+
+	extintrs++;
+	dcflush(PTR2UINT(&extintrs), sizeof extintrs);	/* seems needed */
+	handled = 0;
+	for (ip = intrs; ip->bit != 0; ip++)
+		handled |= ip->func(ip->bit);
+	if (!handled)
+		print("interrupt with no handler\n");
+}
+
+void
+intrinit(void)
+{
+	intrack(~0);			/* clear dregs */
+	putesr(0);			/* clears machine check */
+	coherence();
+
+	irp->mer = Merme | Merhie;
+	irp->ier = 0;			/* clear any pending spurious intrs */
+	coherence();
+	nextintr = intrs;		/* touches sram, not dram */
+	nextintr->bit = 0;
+	coherence();
+
+	intrack(~0);			/* clear dregs */
+	putesr(0);			/* clears machine check */
+	coherence();
+}
+
+/* register func as the interrupt-service routine for bit */
+void
+intrenable(ulong bit, int (*func)(ulong))
+{
+	Intr *ip;
+
+	if (func == nil)
+		return;
+	assert(nextintr < intrs + nelem(intrs));
+	assert(bit);
+	for (ip = intrs; ip->bit != 0; ip++)
+		if (bit == ip->bit) {
+			iprint("handler for intr bit %#lux already "
+				"registered\n", bit);
+			return;
+		}
+	nextintr->bit = bit;
+	nextintr->func = func;
+	nextintr++;
+	coherence();
+	irp->ier |= bit;
+	coherence();
+}
+
+void
+intrack(ulong bit)
+{
+	if (bit) {
+		irp->iar = bit;
+		coherence();
+	}
+}

+ 100 - 0
sys/src/boot/vt4/ip.h

@@ -0,0 +1,100 @@
+typedef struct Udphdr Udphdr;
+struct Udphdr
+{
+	uchar	d[6];		/* Ethernet destination */
+	uchar	s[6];		/* Ethernet source */
+	uchar	type[2];	/* Ethernet packet type */
+
+	uchar	vihl;		/* Version and header length */
+	uchar	tos;		/* Type of service */
+	uchar	length[2];	/* packet length */
+	uchar	id[2];		/* Identification */
+	uchar	frag[2];	/* Fragment information */
+
+	/* Udp pseudo ip really starts here */
+	uchar	ttl;	
+	uchar	udpproto;	/* Protocol */
+	uchar	udpplen[2];	/* Header plus data length */
+	uchar	udpsrc[4];	/* Ip source */
+	uchar	udpdst[4];	/* Ip destination */
+	uchar	udpsport[2];	/* Source port */
+	uchar	udpdport[2];	/* Destination port */
+	uchar	udplen[2];	/* data length */
+	uchar	udpcksum[2];	/* Checksum */
+};
+
+typedef struct Etherhdr Etherhdr;
+struct Etherhdr
+{
+	uchar	d[6];
+	uchar	s[6];
+	uchar	type[2];
+
+	/* Now we have the ip fields */
+	uchar	vihl;		/* Version and header length */
+	uchar	tos;		/* Type of service */
+	uchar	length[2];	/* packet length */
+	uchar	id[2];		/* Identification */
+	uchar	frag[2];	/* Fragment information */
+	uchar	ttl;		/* Time to live */
+	uchar	proto;		/* Protocol */
+	uchar	cksum[2];	/* Header checksum */
+	uchar	src[4];		/* Ip source */
+	uchar	dst[4];		/* Ip destination */
+};
+
+enum
+{
+	IP_VER		= 0x40,
+	IP_HLEN		= 0x05,			
+ 	UDP_EHSIZE	= 22,
+	UDP_PHDRSIZE	= 12,
+	UDP_HDRSIZE	= 20,
+	ETHER_HDR	= 14,
+	IP_UDPPROTO	= 17,
+	ET_IP		= 0x800,
+	Bcastip		= 0xffffffff,
+	BPportsrc	= 68,
+	BPportdst	= 67,
+	TFTPport	= 69,
+	Timeout		= 2000,	/* milliseconds; was 5000 */
+	Bootrequest 	= 1,
+	Bootreply   	= 2,
+	Tftp_READ	= 1,
+	Tftp_WRITE	= 2,
+	Tftp_DATA	= 3,
+	Tftp_ACK	= 4,
+	Tftp_ERROR	= 5,
+	Segsize		= 512,
+	TFTPSZ		= Segsize+10,
+};
+
+typedef struct Bootp Bootp;
+struct Bootp
+{
+	uchar	op;		/* opcode */
+	uchar	htype;		/* hardware type */
+	uchar	hlen;		/* hardware address len */
+	uchar	hops;		/* hops */
+	uchar	xid[4];		/* a random number */
+	uchar	secs[2];	/* elapsed since client started booting */
+	uchar	pad[2];
+	uchar	ciaddr[4];	/* client IP address (client tells server) */
+	uchar	yiaddr[4];	/* client IP address (server tells client) */
+	uchar	siaddr[4];	/* server IP address */
+	uchar	giaddr[4];	/* gateway IP address */
+	uchar	chaddr[16];	/* client hardware address */
+	char	sname[64];	/* server host name (optional) */
+	char	file[128];	/* boot file name */
+	char	vend[128];	/* vendor-specific goo */
+};
+
+typedef struct Netaddr Netaddr;
+struct Netaddr
+{
+	ulong	ip;
+	ushort	port;
+	char	ea[Eaddrlen];
+};
+
+extern int	eipfmt(Fmt*);

+ 684 - 0
sys/src/boot/vt4/l.s

@@ -0,0 +1,684 @@
+/* virtex4 ppc405 machine assist */
+#include "ppc405.h"
+#include "define.h"
+
+/* special instruction definitions */
+#define	BDNZ	BC	16,0,
+#define	BDNE	BC	0,2,
+
+#define	TBRL	268	/* read time base lower in MFTB */
+#define	TBRU	269	/* read time base upper in MFTB */
+#define	MFTB(tbr,d)	WORD	$((31<<26)|((d)<<21)|((tbr&0x1f)<<16)|(((tbr>>5)&0x1f)<<11)|(371<<1))
+
+#define	TLBIA		WORD	$((31<<26)|(370<<1))
+#define	TLBSYNC		WORD	$((31<<26)|(566<<1))
+	
+/* 400 models; perhaps others */
+#define	ICCCI(a,b)	WORD	$((31<<26)|((a)<<16)|((b)<<11)|(966<<1))
+#define	DCCCI(a,b)	WORD	$((31<<26)|((a)<<16)|((b)<<11)|(454<<1))
+/* these follow the source -> dest ordering */
+#define	DCREAD(s,t)	WORD	$((31<<26)|((t)<<21)|((s)<<11)|(486<<1))
+#define	TLBRELO(a,t)	WORD	$((31<<26)|((t)<<21)|((a)<<16)|(1<<11)|(946<<1))
+#define	TLBREHI(a,t)	WORD	$((31<<26)|((t)<<21)|((a)<<16)|(0<<11)|(946<<1))
+#define	TLBWELO(s,a)	WORD	$((31<<26)|((s)<<21)|((a)<<16)|(1<<11)|(978<<1))
+#define	TLBWEHI(s,a)	WORD	$((31<<26)|((s)<<21)|((a)<<16)|(0<<11)|(978<<1))
+#define	TLBSXF(a,b,t)	WORD	$((31<<26)|((t)<<21)|((a)<<16)|((b)<<11)|(914<<1))
+#define	TLBSXCC(a,b,t)	WORD	$((31<<26)|((t)<<21)|((a)<<16)|((b)<<11)|(914<<1)|1)
+#define	WRTMSR_EE(s)	WORD	$((31<<26)|((s)<<21)|(131<<1))
+#define	WRTMSR_EEI(e)	WORD	$((31<<26)|((e)<<15)|(163<<1))
+
+/* on some models mtmsr doesn't synchronise enough (eg, 603e) */
+#define	MSRSYNC	SYNC; ISYNC
+#define MSYNC	MSRSYNC
+
+/*
+ * on the 400 series, the prefetcher madly fetches across RFI, sys call,
+ * and others; use BR 0(PC) to stop it.
+ */
+#define	RFI	WORD	$((19<<26)|(50<<1)); BR 0(PC)
+#define	RFCI	WORD	$((19<<26)|(51<<1)); BR 0(PC)
+
+#define MFCCR0(r) WORD $((31<<26) | ((r)<<21) | (0x1d<<11) | (0x13<<16) | (339<<1))
+#define MTCCR0(r) WORD $((31<<26) | ((r)<<21) | (0x1d<<11) | (0x13<<16) | (467<<1))
+
+/* print progress character.  steps on R7 and R8, needs SB set. */
+#define PROG(c)	MOVW $(Uartlite+4), R7; MOVW $(c), R8; MOVW R8, 0(R7); SYNC	
+
+	NOSCHED
+
+TEXT start<>(SB), 1, $-4
+	/* virtex4 CR 203746 patch for ppc405 errata cpu_213 */
+	MFCCR0(3)
+	OR	$0x50000000, R3
+	MTCCR0(3)
+
+	XORCC	R0, R0				/* from now on R0 == 0 */
+	MOVW	R0, CR
+
+	MOVW	R0, SPR(SPR_ESR)
+	/*
+	 * setup MSR
+	 * turn off interrupts & mmu
+	 * use 0x000 as exception prefix
+	 * enable machine check
+	 */
+	MOVW	$(MSR_ME), R1
+	ISYNC
+	MOVW	R1, MSR
+	MSYNC
+	ISYNC
+
+	/* setup SB for pre mmu */
+	MOVW	$setSB(SB), R2		/* SB until mmu on */
+
+PROG('\r')
+PROG('\n')
+
+	/*
+	 * Invalidate the caches.
+	 */
+//	ICCCI(0, 0)
+	MOVW	R0, SPR(SPR_ICCR)
+	ICCCI(0, 2)  /* errata cpu_121 reveals that EA is used; we'll use SB */
+	ISYNC
+	DCCCI(0, 0)
+	MSYNC
+
+	MOVW	$((DCACHEWAYSIZE/DCACHELINESZ)-1), R3
+	MOVW	R3, CTR
+	MOVW	R0, R3
+dcinv:
+	DCCCI(0,3)
+	ADD	$32, R3
+	BDNZ	dcinv
+
+	/*
+	 * cache is write-through; no user-defined 0; big endian throughout.
+	 * start with caches off until we have zeroed all of memory once.
+	 */
+	MOVW	$~0, R3
+	MOVW	R3, SPR(SPR_DCWR)	/* write-through everywhere*/
+	/* starting from the high bit, each bit represents 128MB */
+	MOVW	R0, R3			/* region bits */
+	MOVW	R3, SPR(SPR_DCCR)
+	MOVW	R3, SPR(SPR_ICCR)
+	ISYNC
+	MOVW	R0, SPR(SPR_SU0R)
+	MOVW	R0, SPR(SPR_SLER)
+	ISYNC
+
+	NOR	R3, R3		/* no speculative access in uncached mem */
+	MOVW	R3, SPR(SPR_SGR)
+	ISYNC
+
+	/*
+	 * set other system configuration values
+	 */
+	MOVW	R0, SPR(SPR_PIT)
+	MOVW	$~0, R3
+	MOVW	R3, SPR(SPR_TSR)
+
+
+	/* run the boot loader with the mmu off */
+
+	/*
+	 * invalidate the caches again to flush any addresses
+	 * below KZERO
+	 */
+	ICCCI(0, 0)
+	ISYNC
+
+	/*
+	 * Set up SB, vector space (16KiB, 64KiB aligned),
+	 * extern registers (m->, up->) and stack.
+	 * Mach (and stack) will be cleared along with the
+	 * rest of BSS below if this is CPU#0.
+	 * Memstart is the first free memory location
+	 * after the kernel.
+	 */
+	MOVW	$setSB(SB), R2			/* (SB) */
+
+PROG('P')
+PROG('l')
+
+	MOVW	$PHYSSRAM, R6			/* vectors at bottom of sram */
+	MOVW	R6, SPR(SPR_EVPR)
+
+	/* only one cpu, # zero */
+	/* sizeof(Mach) is currently 19*4 = 76 bytes */
+	MOVW	R6, R(MACH)			/* m-> before 1st vector */
+
+	MOVW	R0, R(USER)			/* up-> */
+	MOVW	$0xfffffffc, R1		/* put stack in sram temporarily */
+
+_CPU0:						/* boot processor */
+	MOVW	$edata-4(SB), R3
+	MOVW	R0, R4
+	SUB	$8, R4				/* sram end, below JMP */
+_clrbss:					/* clear BSS */
+	MOVWU	R0, 4(R3)
+	CMP	R3, R4
+	BNE	_clrbss
+
+	MOVW	R0, memstart(SB)	/* start of unused memory: dram */
+	MOVW	R6, vectorbase(SB) /* 64KiB aligned vector base, for trapinit */
+
+PROG('a')
+PROG('n')
+PROG(' ')
+	BL	main(SB)
+	BR	0(PC)
+	RETURN
+
+TEXT	cacheson(SB), 1, $-4
+	/* cache is write-through; no user-defined 0; big endian throughout */
+	MOVW	$~0, R3
+	MOVW	R3, SPR(SPR_DCWR)	/* write-through everywhere*/
+	/*
+	 * cache bottom 128MB (dram) & top 128MB (sram), but not I/O reg.s.
+	 * starting from the high bit, each bit represents another 128MB.
+	 */
+	MOVW	$(1<<31 | 1<<0), R3
+	MOVW	R3, SPR(SPR_DCCR)
+	MOVW	R3, SPR(SPR_ICCR)
+	ISYNC
+	MOVW	R0, SPR(SPR_SU0R)
+	MOVW	R0, SPR(SPR_SLER)
+	ISYNC
+
+	MOVW	R3, R4
+	NOR	R3, R3		/* no speculative access in uncached mem */
+	MOVW	R3, SPR(SPR_SGR)
+	ISYNC
+	MOVW	R4, R3		/* return value: true iff caches on */
+	RETURN
+
+TEXT	splhi(SB), 1, $-4
+	MOVW	MSR, R3
+	WRTMSR_EEI(0)
+//	MOVW	LR, R31
+//	MOVW	R31, 4(R(MACH))	/* save PC in m->splpc */
+	RETURN
+
+TEXT	splx(SB), 1, $-4
+//	MOVW	LR, R31
+//	MOVW	R31, 4(R(MACH))	/* save PC in m->splpc */
+	/* fall though */
+
+TEXT	splxpc(SB), 1, $-4
+	WRTMSR_EE(3)
+	RETURN
+
+TEXT	spllo(SB), 1, $-4
+	MOVW	MSR, R3
+	WRTMSR_EEI(1)
+	RETURN
+
+TEXT	spldone(SB), 1, $-4
+	RETURN
+
+TEXT	islo(SB), 1, $-4
+	MOVW	MSR, R3
+	RLWNM	$0, R3, $MSR_EE, R3
+	RETURN
+
+TEXT dcbi(SB), 1, $-4				/* dcbi(addr) */
+	DCBI	(R3)
+	RETURN
+
+TEXT	icflush(SB), 1, $-4	/* icflush(virtaddr, count) */
+	MOVW	n+4(FP), R4
+	RLWNM	$0, R3, $~(ICACHELINESZ-1), R5
+	SUB	R5, R3
+	ADD	R3, R4
+	ADD	$(ICACHELINESZ-1), R4
+	SRAW	$ICACHELINELOG, R4
+	MOVW	R4, CTR
+icf0:	ICBI	(R5)
+	ADD	$ICACHELINESZ, R5
+	BDNZ	icf0
+	ISYNC
+	RETURN
+
+TEXT sync(SB), 1, $-4				/* sync() */
+	SYNC
+	RETURN
+
+TEXT dcflush(SB), 1, $-4			/* dcflush(virtaddr, count) */
+	MOVW	n+4(FP), R4
+	RLWNM	$0, R3, $~(DCACHELINESZ-1), R5
+	CMP	R4, $0
+	BLE	dcf1
+	SUB	R5, R3
+	ADD	R3, R4
+	ADD	$(DCACHELINESZ-1), R4
+	SRAW	$DCACHELINELOG, R4
+	MOVW	R4, CTR
+dcf0:
+	DCBF	(R5)
+	ADD	$DCACHELINESZ, R5
+	BDNZ	dcf0
+dcf1:
+	SYNC
+	RETURN
+
+/* copied from ../vt5/l.s; hope it's right */
+TEXT	cachesinvalidate(SB), 1, $-4
+	ICCCI(0, 2) /* errata cpu_121 reveals that EA is used; we'll use SB */
+	DCCCI(0, 2) /* dcache must not be in use (or just needs to be clean?) */
+	MSYNC
+	RETURN
+
+TEXT	getpit(SB), 1, $0
+	MOVW	SPR(SPR_PIT), R3
+	RETURN
+
+TEXT	putpit(SB), 1, $0
+	MOVW	R3, SPR(SPR_PIT)
+	RETURN
+
+TEXT	putpid(SB), 1, $0
+	MOVW	R3, SPR(SPR_PID)
+	RETURN
+
+TEXT	getpid(SB), 1, $0
+	MOVW	SPR(SPR_PID), R3
+	RETURN
+
+/* 405s have no PIR, so use low bits of PVR, which rae can set. */
+TEXT	getpir(SB), 1, $-4
+	MOVW	SPR(SPR_PVR), R3
+	ANDCC	$017, R3
+	RETURN
+
+TEXT	gettbl(SB), 1, $0
+	MFTB(TBRL, 3)
+	RETURN
+
+TEXT	gettbu(SB), 1, $0
+	MFTB(TBRU, 3)
+	RETURN
+
+TEXT	gettsr(SB), 1, $0
+	MOVW	SPR(SPR_TSR), R3
+	RETURN
+
+TEXT	puttsr(SB), 1, $0
+	MOVW	R3, SPR(SPR_TSR)
+	RETURN
+
+TEXT	gettcr(SB), 1, $0
+	MOVW	SPR(SPR_TCR), R3
+	RETURN
+
+TEXT	puttcr(SB), 1, $0
+	MOVW	R3, SPR(SPR_TCR)
+	RETURN
+
+TEXT	getpvr(SB), 1, $0
+	MOVW	SPR(SPR_PVR), R3
+	RETURN
+
+TEXT	getmsr(SB), 1, $0
+	MOVW	MSR, R3
+	RETURN
+
+TEXT	putmsr(SB), 1, $0
+	SYNC
+	MOVW	R3, MSR
+	MSRSYNC
+	RETURN
+
+TEXT	getesr(SB), 1, $0
+	MOVW	SPR(SPR_ESR), R3
+	RETURN
+
+TEXT	putesr(SB), 1, $0
+	MOVW	R3, SPR(SPR_ESR)
+	RETURN
+
+TEXT	putevpr(SB), 1, $0
+	MOVW	R3, SPR(SPR_EVPR)
+	RETURN
+
+TEXT	setsp(SB), 1, $0
+	MOVW	R3, R1
+	RETURN
+
+TEXT	getdear(SB), 1, $0
+	MOVW	SPR(SPR_DEAR), R3
+	RETURN
+
+TEXT	tas32(SB), 1, $0
+	SYNC
+	MOVW	R3, R4
+	MOVW	$0xdead,R5
+tas1:
+	MSYNC
+	LWAR	(R4), R3
+	CMP	R3, $0
+	BNE	tas0
+	DCBT	(R4)				/* fix 405 errata cpu_210 */
+	STWCCC	R5, (R4)
+	BNE	tas1
+tas0:
+	SYNC
+	ISYNC
+	RETURN
+
+TEXT	eieio(SB), 1, $0
+	EIEIO
+	RETURN
+
+TEXT	syncall(SB), 1, $0
+	SYNC
+	ISYNC
+	RETURN
+
+TEXT _xinc(SB), 1, $0			/* void _xinc(long *); */
+	MOVW	R3, R4
+xincloop:
+	LWAR	(R4), R3
+	ADD	$1, R3
+	DCBT	(R4)				/* fix 405 errata cpu_210 */
+	STWCCC	R3, (R4)
+	BNE	xincloop
+	RETURN
+
+TEXT _xdec(SB), 1, $0			/* long _xdec(long *); */
+	MOVW	R3, R4
+xdecloop:
+	LWAR	(R4), R3
+	ADD	$-1, R3
+	DCBT	(R4)				/* fix 405 errata cpu_210 */
+	STWCCC	R3, (R4)
+	BNE	xdecloop
+	RETURN
+
+
+#define SPR_CSRR0	0x03a		/* Critical Save/Restore Register 0 */
+#define SPR_CSRR1	0x03b		/* Critical Save/Restore Register 1 */
+//#define SPR_DEAR	0x03d		/* Data Error Address Register */
+
+#define SPR_SPRG4R	0x104		/* SPR general 4; user/supervisor R */
+#define SPR_SPRG5R	0x105		/* SPR general 5; user/supervisor R */
+#define SPR_SPRG6R	0x106		/* SPR general 6; user/supervisor R */
+#define SPR_SPRG7R	0x107		/* SPR general 7; user/supervisor R */
+#define SPR_SPRG4W	0x114		/* SPR General 4; supervisor W */
+#define SPR_SPRG5W	0x115		/* SPR General 5; supervisor W  */
+#define SPR_SPRG6W	0x116		/* SPR General 6; supervisor W  */
+#define SPR_SPRG7W	0x117		/* SPR General 7; supervisor W */
+
+#define SPR_MCSRR0	0x23a
+#define SPR_MCSRR1	0x23b
+
+#define	SAVER0		SPR_SPRG0	/* shorthand use in save/restore */
+#define	SAVER1		SPR_SPRG1
+#define	SAVELR		SPR_SPRG2
+#define	SAVEXX		SPR_SPRG3
+
+#define	UREGSPACE	(UREGSIZE+8)
+
+#define RTBL		28		/* time stamp tracing */
+
+/*
+ * the 405 does not follow Book E: traps turn the mmu off.
+ * the following code has been executed at the exception
+ * vector location already:
+ *	MOVW	R0, SPR(SAVER0)
+ *	(critical interrupts disabled in MSR, using R0)
+ *	MOVW	LR, R0
+ *	MOVW	R0, SPR(SAVELR) 
+ *	BL	trapvec(SB)
+ */
+TEXT	trapvec(SB), 1, $-4
+	MOVW	LR, R0
+	MOVW	R0, SPR(SAVEXX)			/* save interrupt vector offset */
+trapcommon:					/* entry point for machine checks */
+	MOVW	R1, SPR(SAVER1)			/* save stack pointer */
+
+	/* did we come from user space? */
+	MOVW	SPR(SPR_SRR1), R0
+	MOVW	CR, R1
+	MOVW	R0, CR
+	BC	4,17,ktrap			/* if MSR[PR]=0, we are in kernel space */
+
+	/* was user mode, switch to kernel stack and context */
+	MOVW	R1, CR
+	MOVW	SPR(SPR_SPRG7R), R1		/* up->kstack+KSTACK-UREGSPACE, set in touser and sysrforkret */
+	MFTB(TBRL, RTBL)
+	BL	saveureg(SB)
+
+//	MOVW	$mach0(SB), R(MACH)		/* FIX FIX FIX */
+//	MOVW	8(R(MACH)), R(USER)		/* FIX FIX FIX */
+//try this:
+/* 405s have no PIR; could use PVR */
+//	MOVW	SPR(SPR_PIR), R4		/* PIN */
+//	SLW	$2, R4				/* offset into pointer array */
+	MOVW	$0, R4				/* assume cpu 0 */
+	MOVW	$machptr(SB), R(MACH)		/* pointer array */
+	ADD	R4, R(MACH)			/* pointer to array element */
+	MOVW	(R(MACH)), R(MACH)		/* m-> */
+	MOVW	8(R(MACH)), R(USER)		/* up-> */
+
+	BL	trap(SB)
+	BR	restoreureg
+
+ktrap:
+	/* was kernel mode, R(MACH) and R(USER) already set */
+	MOVW	R1, CR
+	MOVW	SPR(SAVER1), R1
+	SUB	$UREGSPACE, R1		/* push onto current kernel stack */
+	BL	saveureg(SB)
+	BL	trap(SB)
+
+restoreureg:
+	MOVMW	48(R1), R2		/* r2:r31 */
+	/* defer R1, R0 */
+	MOVW	36(R1), R0
+	MOVW	R0, CTR
+	MOVW	32(R1), R0
+	MOVW	R0, XER
+	MOVW	28(R1), R0
+	MOVW	R0, CR	/* CR */
+	MOVW	24(R1), R0
+	MOVW	R0, LR
+	MOVW	20(R1), R0
+	MOVW	R0, SPR(SPR_SPRG7W)	/* kstack for traps from user space */
+	MOVW	16(R1), R0
+	MOVW	R0, SPR(SPR_SRR0)	/* old PC */
+	MOVW	12(R1), R0
+	RLWNM	$0, R0, $~MSR_WE, R0	/* remove wait state */
+	MOVW	R0, SPR(SPR_SRR1)	/* old MSR */
+	/* cause, skip */
+	MOVW	40(R1), R0
+	MOVW	44(R1), R1		/* old SP */
+	SYNC				/* fix 405 errata cpu_210 */
+	RFI
+
+/*
+ * machine check.
+ * make it look like the others.
+ * it's safe to destroy SPR_SRR0/1 because they can only be in
+ * use if a critical interrupt has interrupted a non-critical interrupt
+ * before it has had a chance to block critical interrupts,
+ * but no recoverable machine checks can occur during a critical interrupt,
+ * so the lost state doesn't matter.
+ */
+TEXT	trapmvec(SB), 1, $-4
+	MOVW	LR, R0
+	MOVW	R0, SPR(SAVEXX)
+	MOVW	SPR(SPR_MCSRR0), R0		/* PC or excepting insn */
+	MOVW	R0, SPR(SPR_SRR0)
+	MOVW	SPR(SPR_MCSRR1), R0		/* old MSR */
+	MOVW	R0, SPR(SPR_SRR1)
+	BR	trapcommon
+
+/*
+ * external interrupts (non-critical)
+ */
+TEXT	intrvec(SB), 1, $-4
+	MOVW	LR, R0
+	MOVW	R0, SPR(SAVEXX)			/* save interrupt vector offset */
+	MOVW	R1, SPR(SAVER1)			/* save stack pointer */
+
+	/* did we come from user space? */
+	MOVW	SPR(SPR_SRR1), R0
+	MOVW	CR, R1
+	MOVW	R0, CR
+	BC	4,17,intr1			/* if MSR[PR]=0, we are in kernel space */
+
+	/* was user mode, switch to kernel stack and context */
+	MOVW	R1, CR
+	MOVW	SPR(SPR_SPRG7R), R1		/* up->kstack+KSTACK-UREGSPACE, set in touser and sysrforkret */
+	BL	saveureg(SB)
+
+//	MOVW	$mach0(SB), R(MACH)		/* FIX FIX FIX */
+//	MOVW	8(R(MACH)), R(USER)
+//try this:
+/* 405s have no PIR */
+//	MOVW	SPR(SPR_PIR), R4		/* PIN */
+//	SLW	$2, R4				/* offset into pointer array */
+	MOVW	$0, R4				/* assume cpu 0 */
+	MOVW	$machptr(SB), R(MACH)		/* pointer array */
+	ADD	R4, R(MACH)			/* pointer to array element */
+	MOVW	(R(MACH)), R(MACH)		/* m-> */
+	MOVW	8(R(MACH)), R(USER)		/* up-> */
+
+	BL	intr(SB)
+	BR	restoreureg
+
+intr1:
+	/* was kernel mode, R(MACH) and R(USER) already set */
+	MOVW	R1, CR
+	MOVW	SPR(SAVER1), R1
+	SUB	$UREGSPACE, R1		/* push onto current kernel stack */
+	BL	saveureg(SB)
+	BL	intr(SB)
+	BR	restoreureg
+
+/*
+ * critical interrupt
+ */
+TEXT	critintrvec(SB), 1, $-4
+	MOVW	LR, R0
+	MOVW	R0, SPR(SAVEXX)
+	MOVW	R1, SPR(SAVER1)		/* save stack pointer */
+
+	/* did we come from user space? */
+	MOVW	SPR(SPR_CSRR1), R0
+	MOVW	CR, R1
+	MOVW	R0, CR
+	BC	4,16,kintrintr		/* if MSR[EE]=0, kernel was interrupted at start of intrvec */
+	BC	4,17,kcintr1		/* if MSR[PR]=0, we are in kernel space */
+
+ucintr:
+	/* was user mode or intrvec interrupted: switch to kernel stack and context */
+	MOVW	R1, CR
+	MOVW	SPR(SPR_SPRG7R), R1		/* up->kstack+KSTACK-UREGSPACE, set in touser and sysrforkret */
+	BL	saveureg(SB)
+
+//	MOVW	$mach0(SB), R(MACH)		/* FIX FIX FIX */
+//	MOVW	8(R(MACH)), R(USER)
+//try this:
+/* 405s have no PIR */
+//	MOVW	SPR(SPR_PIR), R4		/* PIN */
+//	SLW	$2, R4				/* offset into pointer array */
+	MOVW	$0, R4				/* assume cpu 0 */
+	MOVW	$machptr(SB), R(MACH)		/* pointer array */
+	ADD	R4, R(MACH)			/* pointer to array element */
+	MOVW	(R(MACH)), R(MACH)		/* m-> */
+	MOVW	8(R(MACH)), R(USER)		/* up-> */
+
+	BR	cintrcomm
+
+kintrintr:
+	/* kernel mode, and EE off, so kernel intrvec interrupted, but was previous mode kernel or user? */
+	MOVW	SPR(SPR_SRR1), R0
+	MOVW	R0, CR
+	BC	(4+8),17,ucintr	/* MSR[PR]=1, we were in user space, need set up */
+
+kcintr1:
+	/*  was kernel mode and external interrupts enabled, R(MACH) and R(USER) already set */
+	MOVW	R1, CR
+	MOVW	SPR(SAVER1), R1
+	SUB	$UREGSPACE, R1	/* push onto current kernel stack */
+	BL	saveureg(SB)
+
+cintrcomm:
+	/* special part of Ureg for critical interrupts only (using Ureg.dcmp, Ureg.icmp, Ureg.dmiss) */
+	MOVW	SPR(SPR_SPRG6R), R4	/* critical interrupt saves volatile R0 in SPRG6 */
+	MOVW	R4, (160+8)(R1)
+	MOVW	SPR(SPR_CSRR0), R4	/* store critical interrupt pc */
+	MOVW	R4, (164+8)(R1)
+	MOVW	SPR(SPR_CSRR1), R4	/* critical interrupt msr */
+	MOVW	R4, (168+8)(R1)
+
+	BL	intr(SB)
+
+	/* first restore usual part of Ureg */
+	MOVMW	48(R1), R2	/* r2:r31 */
+	/* defer R1, R0 */
+	MOVW	40(R1), R0
+	MOVW	R0, SPR(SAVER0)		/* restore normal r0 save */
+	MOVW	36(R1), R0
+	MOVW	R0, CTR
+	MOVW	32(R1), R0
+	MOVW	R0, XER
+	MOVW	28(R1), R0
+	MOVW	R0, CR	/* CR */
+	MOVW	24(R1), R0
+	MOVW	R0, LR
+	MOVW	20(R1), R0
+	MOVW	R0, SPR(SPR_SPRG7W)	/* kstack for traps from user space */
+	MOVW	16(R1), R0
+	MOVW	R0, SPR(SPR_SRR0)	/* saved normal PC */
+	MOVW	12(R1), R0
+	MOVW	R0, SPR(SPR_SRR1)	/* saved normal MSR */
+
+	/* restore special bits for critical interrupts */
+	MOVW	(164+8)(R1), R0		/* critical interrupt's saved pc */
+	MOVW	R0, SPR(SPR_CSRR0)
+	MOVW	(168+8)(R1), R0
+	RLWNM	$0, R0, $~MSR_WE, R0	/* remove wait state */
+	MOVW	R0, SPR(SPR_CSRR1)	
+
+	/* cause, skip */
+	MOVW	(160+8)(R1), R0		/* critical interrupt's saved R0 */
+	MOVW	44(R1), R1		/* old SP */
+	RFCI
+	
+/*
+ * enter with stack set and mapped.
+ * on return, SB (R2) has been set, and R3 has the Ureg*,
+ * the MMU has been re-enabled, kernel text and PC are in KSEG,
+ * Stack (R1), R(MACH) and R(USER) are set by caller, if required.
+ */
+TEXT	saveureg(SB), 1, $-4
+	MOVMW	R2, 48(R1)			/* save gprs r2 to r31 */
+	MOVW	$setSB(SB), R2
+	MOVW	SPR(SAVER1), R4
+	MOVW	R4, 44(R1)
+	MOVW	SPR(SAVER0), R5
+	MOVW	R5, 40(R1)
+	MOVW	CTR, R6
+	MOVW	R6, 36(R1)
+	MOVW	XER, R4
+	MOVW	R4, 32(R1)
+	MOVW	CR, R5
+	MOVW	R5, 28(R1)
+	MOVW	SPR(SAVELR), R6			/* LR */
+	MOVW	R6, 24(R1)
+	MOVW	SPR(SPR_SPRG7R), R6		/* up->kstack+KSTACK-UREGSPACE */
+	MOVW	R6, 20(R1)
+	MOVW	SPR(SPR_SRR0), R0
+	MOVW	R0, 16(R1)			/* PC of excepting insn (or next insn) */
+	MOVW	SPR(SPR_SRR1), R0
+	MOVW	R0, 12(R1)			/* old MSR */
+	MOVW	SPR(SAVEXX), R0
+	MOVW	R0, 8(R1)			/* cause/vector */
+	ADD	$8, R1, R3			/* Ureg* */
+	DCBT	(R1)				/* fix 405 errata cpu_210 */
+	STWCCC	R3, (R1)			/* break any pending reservations */
+	MOVW	$0, R0				/* compiler/linker expect R0 to be zero */
+	RETURN

+ 102 - 0
sys/src/boot/vt4/libc.h

@@ -0,0 +1,102 @@
+/*
+ * functions (possibly) linked in, complete, from libc.
+ */
+#define nelem(x)	(sizeof(x)/sizeof((x)[0]))
+#define offsetof(s, m)	(ulong)(&(((s*)0)->m))
+#define assert(x)	if(x){}else _assert("x")
+
+/*
+ * mem routines
+ */
+extern void* memset(void*, int, ulong);
+extern int memcmp(void*, void*, ulong);
+extern void* memmove(void*, void*, ulong);
+
+/*
+ * string routines
+ */
+extern int cistrcmp(char *, char *);
+extern int cistrncmp(char *, char *, int);
+extern char *strchr(char *, int);
+extern int strcmp(char *, char *);
+extern char* strecpy(char*, char*, char*);
+extern long strlen(char*);
+extern int strncmp(char *, char *, int);
+extern char* strncpy(char*, char*, long);
+extern char* strstr(char *, char *);
+extern int tokenize(char*, char**, int);
+
+/*
+ * malloc
+ */
+extern void free(void*);
+extern void* malloc(ulong);
+extern void* mallocalign(ulong, ulong, long, ulong);
+extern int mallocinit(void*, ulong);
+
+/*
+ * print routines
+ */
+typedef struct Fmt Fmt;
+struct Fmt {
+	uchar	runes;			/* output buffer is runes or chars? */
+	void*	start;			/* of buffer */
+	void*	to;			/* current place in the buffer */
+	void*	stop;			/* end of the buffer; overwritten if flush fails */
+	int	(*flush)(Fmt*);		/* called when to == stop */
+	void*	farg;			/* to make flush a closure */
+	int	nfmt;			/* num chars formatted so far */
+	va_list	args;			/* args passed to dofmt */
+	int	r;			/* % format Rune */
+	int	width;
+	int	prec;
+	ulong	flags;
+};
+
+extern int print(char*, ...);
+extern char* seprint(char*, char*, char*, ...);
+extern char* vseprint(char*, char*, char*, va_list);
+
+#pragma	varargck	argpos	print		1
+#pragma	varargck	argpos	seprint		3
+
+#pragma	varargck	type	"lld"	vlong
+#pragma	varargck	type	"llx"	vlong
+#pragma	varargck	type	"lld"	uvlong
+#pragma	varargck	type	"llx"	uvlong
+#pragma	varargck	type	"ld"	long
+#pragma	varargck	type	"lx"	long
+#pragma	varargck	type	"ld"	ulong
+#pragma	varargck	type	"lx"	ulong
+#pragma	varargck	type	"d"	int
+#pragma	varargck	type	"x"	int
+#pragma	varargck	type	"c"	int
+#pragma	varargck	type	"C"	int
+#pragma	varargck	type	"d"	uint
+#pragma	varargck	type	"x"	uint
+#pragma	varargck	type	"c"	uint
+#pragma	varargck	type	"C"	uint
+#pragma	varargck	type	"s"	char*
+#pragma	varargck	type	"q"	char*
+#pragma	varargck	type	"S"	Rune*
+#pragma	varargck	type	"%"	void
+#pragma	varargck	type	"p"	uintptr
+#pragma	varargck	type	"p"	void*
+#pragma	varargck	flag	','
+#pragma	varargck	type	"E"	uchar*	/* eipfmt */
+#pragma	varargck	type	"V"	uchar*	/* eipfmt */
+
+extern int fmtinstall(int, int (*)(Fmt*));
+extern int dofmt(Fmt*, char*);
+
+/*
+ * one-of-a-kind
+ */
+extern void _assert(char*);
+extern uintptr getcallerpc(void*);
+extern long strtol(char*, char**, int);
+extern ulong strtoul(char*, char**, int);
+extern	void	longjmp(jmp_buf, int);
+extern	int	setjmp(jmp_buf);
+
+extern char etext[], edata[], end[];

+ 211 - 0
sys/src/boot/vt4/llfifo.c

@@ -0,0 +1,211 @@
+/*
+ * Xilinx Local Link FIFOs for Temac, in pairs (rx and tx).
+ */
+#include "include.h"
+
+enum {
+	Reset	= 0xa5,		/* magic [tr]dfr & llr value */
+
+	/* dmacr; copied from dma.c */
+	Sinc	= 1<<31,	/* source increment */
+	Dinc	= 1<<30,	/* dest increment */
+
+	/* field masks */
+
+	Bytecnt	= (1<<11) - 1,
+	Wordcnt	= (1<<9) - 1,
+};
+
+enum {				/* register's bits */
+	/* isr, ier registers (*?e->*ee) */
+	Rpure	= 1<<31,	/* rx packet underrun read error */
+	Rpore	= 1<<30,	/* rx packet overrun read error */
+	Rpue	= 1<<29,	/* rx packet underrun error */
+	Tpoe	= 1<<28,	/* tx packet overrun error */
+	Tc	= 1<<27,	/* tx complete */
+	Rc	= 1<<26,	/* rx complete */
+	Tse	= 1<<25,	/* tx size error */
+	Trc	= 1<<24,	/* tx reset complete */
+	Rrc	= 1<<23,	/* rx reset complete */
+};
+
+typedef struct Llfiforegs Llfiforegs;
+typedef struct Llfifosw Llfifosw;
+
+struct Llfiforegs {
+	ulong	isr;		/* intr status */
+	ulong	ier;		/* intr enable */
+
+	ulong	tdfr;		/* tx data fifo reset */
+	ulong	tdfv;		/* tx data fifo vacancy (words free) */
+	ulong	tdfd;		/* tx data fifo write port */
+	ulong	tlf;		/* tx length fifo */
+
+	ulong	rdfr;		/* rx data fifo reset */
+	ulong	rdfo;		/* rx data fifo occupancy */
+	ulong	rdfd;		/* rx data fifo read port */
+	ulong	rlf;		/* tx length fifo */
+
+	ulong	llr;		/* locallink reset */
+};
+struct Llfifosw {
+	Llfiforegs *regs;
+};
+
+static Llfiforegs *frp = (Llfiforegs *)Llfifo;
+static Ether *llether;
+
+/*
+ * as of dma controller v2, keyhole operations are on ulongs,
+ * but otherwise it's as if memmove were used.
+ * addresses need not be word-aligned, though registers are.
+ */
+static void
+fifocpy(void *vdest, void *vsrc, uint bytes, ulong flags)
+{
+	int words;
+	ulong *dest, *dstbuf, *src;
+	/* +2*BY2WD is slop for alignment */
+	static uchar buf[ETHERMAXTU+8+2*BY2WD];
+
+	dest = vdest;
+	src = vsrc;
+	assert(bytes <= sizeof buf);
+	words = bytes / BY2WD;
+	if (bytes % BY2WD != 0)
+		words++;
+
+	switch (flags & (Sinc | Dinc)) {
+	case Sinc | Dinc:
+		memmove(vdest, vsrc, bytes);
+		break;
+	case Sinc:				/* mem to register */
+		src = (ulong *)ROUNDUP((uvlong)buf, BY2WD);
+		memmove(src, vsrc, bytes);	/* ensure src alignment */
+		assert((uintptr)src  % BY2WD == 0);
+		assert((uintptr)dest % BY2WD == 0);
+		while (words-- > 0)
+			*dest = *src++;
+		break;
+	case Dinc:				/* register to mem */
+		dest = dstbuf = (ulong *)ROUNDUP((uvlong)buf, BY2WD);
+		assert((uintptr)src  % BY2WD == 0);
+		assert((uintptr)dest % BY2WD == 0);
+		while (words-- > 0)
+			*dest++ = *src;
+		memmove(vdest, dstbuf, bytes);	/* ensure dest alignment */
+		break;
+	case 0:				/* register-to-null or vice versa */
+		while (words-- > 0)
+			*dest = *src;
+		break;
+	}
+}
+
+static void
+discardinpkt(int len)		/* discard the rx fifo's packet */
+{
+	ulong null;
+
+	fifocpy(&null, &frp->rdfd, len, 0);
+	coherence();
+}
+
+int
+llfifointr(ulong bit)
+{
+	ulong len, sts;
+	Ether *ether;
+	RingBuf *rb;
+	static uchar zaddrs[Eaddrlen * 2];
+
+	sts = frp->isr;
+	if (sts == 0)
+		return 0;			/* not for me */
+	ether = llether;
+	/* it's important to drain all packets in the rx fifo */
+	while ((frp->rdfo & Wordcnt) != 0) {
+		assert((frp->rdfo & ~Wordcnt) == 0);
+		len = frp->rlf & Bytecnt;	/* read rlf from fifo */
+		assert((len & ~Bytecnt) == 0);
+		assert(len > 0 && len <= ETHERMAXTU);
+		rb = &ether->rb[ether->ri];
+		if (rb->owner == Interface) {
+			/* from rx fifo into ring buffer */
+			fifocpy(rb->pkt, &frp->rdfd, len, Dinc);
+			if (memcmp(rb->pkt, zaddrs, sizeof zaddrs) == 0) {
+				iprint("ether header with all-zero mac "
+					"addresses\n");
+				continue;
+			}
+			rb->len = len;
+			rb->owner = Host;
+			coherence();
+			ether->ri = NEXT(ether->ri, ether->nrb);
+			coherence();
+		} else {
+			discardinpkt(len);
+			/* not too informative during booting */
+			iprint("llfifo: no buffer for input pkt\n");
+		}
+	}
+
+	if (sts & Tc)
+		ether->tbusy = 0;
+	ether->transmit(ether);
+
+	frp->isr = sts;			/* extinguish intr source */
+	coherence();
+
+	intrack(bit);
+	sts &= ~(Tc | Rc);
+	if (sts)
+		iprint("llfifo isr %#lux\n", sts);
+	return 1;
+}
+
+void
+llfiforeset(void)
+{
+	frp->tdfr = Reset;
+	frp->rdfr = Reset;
+	coherence();
+	while ((frp->isr & (Trc | Rrc)) != (Trc | Rrc))
+		;
+}
+
+void
+llfifoinit(Ether *ether)
+{
+	llether = ether;
+	frp->ier = 0;
+	frp->isr = frp->isr;	/* extinguish intr source */
+	coherence();
+
+	intrenable(Intllfifo, llfifointr);
+	coherence();
+	frp->ier = Rc | Tc;
+	coherence();
+}
+
+void
+llfifotransmit(uchar *ubuf, unsigned len)
+{
+	int wds;
+
+	llether->tbusy = 1;
+
+	assert(len <= ETHERMAXTU);
+	wds = ROUNDUP(len, BY2WD) / BY2WD;
+
+	/* wait for tx fifo to drain */
+	while ((frp->tdfv & Wordcnt) < wds)
+		;
+
+	/* to tx fifo */
+	assert((frp->tdfv & ~Wordcnt) == 0);
+	fifocpy(&frp->tdfd, ubuf, len, Sinc);
+	coherence();
+	frp->tlf = len;			/* send packet in tx fifo to ether */
+	coherence();
+}

+ 840 - 0
sys/src/boot/vt4/load.c

@@ -0,0 +1,840 @@
+#include "include.h"
+#include "fs.h"
+
+enum {
+	Dontprint,
+	Print,
+
+	Datamagic	= 0xfacebabe,
+};
+
+int securemem;
+
+static char *etherparts[] = { "*", 0 };
+static char *etherinis[] = {
+	"/cfg/pxe/%E",
+	0
+};
+
+Type types[] = {
+	{	Tether,
+		Fini|Fbootp,
+		etherinit, etherinitdev,
+		pxegetfspart, 0, bootpboot,
+		etherprintdevs,
+		etherparts,
+		etherinis,
+	},
+};
+
+static char *typenm[] = {
+	[Tether]	"ether",
+};
+
+typedef struct Mode Mode;
+
+enum {
+	Dany		= -1,
+	Nmedia		= 2,		/* size reduction; was 16 */
+};
+
+enum {					/* mode */
+	Mauto,
+	Mlocal,
+	Manual,
+	NMode,
+};
+
+typedef struct Medium Medium;
+struct Medium {
+	Type*	type;
+	ushort	flag;
+	ushort	dev;
+	char	name[NAMELEN];
+
+	Fs	*inifs;
+	char	*part;
+	char	*ini;
+
+	Medium*	next;
+};
+
+typedef struct Mode {
+	char*	name;
+	ushort	mode;
+} Mode;
+
+extern char bdata[], edata[], end[], etext[];
+
+static Medium media[Nmedia];
+static Medium *curmedium = media;
+
+static Mode modes[NMode+1] = {
+	[Mauto]		{ "auto",   Mauto,  },
+	[Mlocal]	{ "local",  Mlocal, },
+	[Manual]	{ "manual", Manual, },
+};
+
+Mach* machptr[MAXMACH];
+
+ulong cpuentry = 0;
+
+char **ini;
+char *defaultpartition;
+char *persist;
+
+int debugload;
+int iniread;
+int pxe = 1;
+int scsi0port;
+int vga;
+
+uintptr memstart;				/* set before main called */
+uintptr vectorbase;				/* set before main called */
+
+static uintptr memsz;
+
+static Medium*
+parse(char *line, char **file)
+{
+	char *p;
+	Type *tp;
+	Medium *mp;
+
+	if(p = strchr(line, '!')) {
+		*p++ = 0;
+		*file = p;
+	} else
+		*file = "";
+
+	tp = types;
+	for(mp = tp->media; mp; mp = mp->next)
+		if(strcmp(mp->name, line) == 0)
+			return mp;
+	if(p)
+		*--p = '!';
+	return nil;
+}
+
+static int
+boot(Medium *mp, char *file)
+{
+	Type *tp;
+	Medium *xmp;
+	static int didaddconf;
+	Boot b;
+
+	memset(&b, 0, sizeof b);
+	b.state = INITKERNEL;
+
+	if(didaddconf == 0) {
+		didaddconf = 1;
+		tp = types;
+		if(tp->addconf)
+			for(xmp = tp->media; xmp; xmp = xmp->next)
+				(*tp->addconf)(xmp->dev);
+	}
+
+	seprint(BOOTLINE, BOOTLINE + BOOTLINELEN, "%s!%s", mp->name, file);
+	print("booting %s!%s\n", mp->name, file);
+	return (*mp->type->boot)(mp->dev, file, &b);
+}
+
+static Medium*
+allocm(Type *tp)
+{
+	Medium **l;
+
+	if(curmedium >= &media[Nmedia])
+		return 0;
+
+	for(l = &tp->media; *l; l = &(*l)->next)
+		;
+	*l = curmedium++;
+	return *l;
+}
+
+Medium*
+probe(int type, int flag, int dev)
+{
+	Type *tp;
+	int i;
+	Medium *mp;
+	File f;
+	Fs *fs;
+	char **partp;
+
+	tp = types;
+	if(type != Tany && type != tp->type)
+		return 0;
+
+	if(flag != Fnone)
+		for(mp = tp->media; mp; mp = mp->next)
+			if((flag & mp->flag) && (dev == Dany || dev == mp->dev))
+				return mp;
+	if((tp->flag & Fprobe) == 0){
+		tp->flag |= Fprobe;
+		tp->mask = (*tp->init)();
+	}
+
+	for(i = 0; tp->mask; i++){
+		if((tp->mask & (1<<i)) == 0)
+			continue;
+		tp->mask &= ~(1<<i);
+
+		if((mp = allocm(tp)) == 0)
+			continue;
+
+		mp->dev = i;
+		mp->flag = tp->flag;
+		mp->type = tp;
+		(*tp->initdev)(i, mp->name);
+
+		if(mp->flag & Fini){
+			mp->flag &= ~Fini;
+			for(partp = tp->parts; *partp; partp++){
+				if((fs = (*tp->getfspart)(i, *partp, 0)) == nil)
+					continue;
+
+				for(ini = tp->inis; *ini; ini++)
+					if(fswalk(fs, *ini, &f) > 0){
+						mp->inifs = fs;
+						mp->part = *partp;
+						mp->ini = f.path;
+						mp->flag |= Fini;
+						goto Break2;
+					}
+			}
+		}
+	Break2:
+		if((flag & mp->flag) && (dev == Dany || dev == i))
+			return mp;
+	}
+	return 0;
+}
+
+enum {
+	Kilo = 1024,
+};
+
+/* make sure we don't get the write system call from libc */
+long
+write(int fd, void *buf, long nbytes)
+{
+	USED(fd);
+	vuartputs(buf, nbytes);
+	return nbytes;
+}
+
+/*
+ * write zero words to an entire cache line (the one containing addr).
+ * does not flush the data cache.
+ */
+void
+cacheline0(uintptr addr)
+{
+	ulong *sp, *endmem;
+
+	addr &= ~(DCACHELINESZ - 1);
+	endmem = (ulong *)(addr + DCACHELINESZ);
+	for (sp = (ulong *)addr; sp < endmem; sp++)
+		*sp = 0;
+//	coherence();
+}
+
+/* force the four qtm write buffers to be retired to dram by filling them. */
+void
+flushwrbufs(void)
+{
+	if (!securemem)
+		return;
+	cacheline0(PHYSDRAM);
+	cacheline0(PHYSDRAM + DCACHELINESZ);
+	cacheline0(PHYSDRAM + 2*DCACHELINESZ);
+	cacheline0(PHYSDRAM + 3*DCACHELINESZ);
+	coherence();
+}
+
+static void
+vfyzeros(ulong *addr, ulong *end)
+{
+	ulong wd;
+	ulong *sp;
+
+	for (sp = addr; sp < end; sp++) {
+		wd = *sp;
+		if (wd != 0) {
+			PROG('?')
+			panic("bad dram: %#p read back as %#lux not 0", sp, wd);
+		}
+	}
+}
+
+static int
+memreset(void)
+{
+	int i, cnt;
+	uintptr addr, endmem;
+
+	cnt = 0;
+	if (securemem)
+		cnt = qtmmemreset();
+	else
+		/* normal dram init. should take 100—250 ms. */
+		for (i = 10*1000*1000; i-- > 0; )
+			cnt++;
+PROG('t')
+	/*
+	 * dram must be done initialising now,
+	 * but qtm needs us to zero it *all* before any other use,
+	 * to set the macs.  even for non-qtm dram, it might be wise
+	 * to ensure that all the ecc bits are set.
+	 */
+	memsz = memsize();		/* may carefully probe qtm dram */
+	qtmerrtest("sizing memory");
+
+	dcflush(PHYSSRAM, (1ULL << 32) - PHYSSRAM);
+	cachesinvalidate();
+
+	endmem = PHYSDRAM + memsz;
+	for (addr = PHYSDRAM; addr < endmem; addr += DCACHELINESZ) {
+		cacheline0(addr);
+		qtmerrtestaddr(addr);
+	}
+	assert(addr == endmem);
+	coherence();
+	flushwrbufs();
+	qtmerrtest("zeroing dram");
+
+#ifdef PARANOID
+	vfyzeros((ulong *)PHYSDRAM, (ulong *)endmem);
+#else
+	vfyzeros((ulong *)(endmem - MB), (ulong *)endmem);
+#endif
+	dcflush(PHYSDRAM, memsz);
+	dcflush(PHYSSRAM, (1ULL << 32) - PHYSSRAM);
+	cachesinvalidate();
+
+	/*
+	 * Hallelujah!  We can finally treat qtm dram just like real memory,
+	 * except that the last (partial) page is funny and should be avoided
+	 * after initialising it.
+	 * It happens even with caches off, so it's a bit hard to explain
+	 * why it should be funny.
+	 */
+	memsz &= ~(BY2PG - 1);
+	endmem = PHYSDRAM + memsz;
+
+#ifdef PARANOID
+	vfyzeros((ulong *)PHYSDRAM, (ulong *)endmem);
+#else
+	vfyzeros((ulong *)PHYSDRAM, (ulong *)(PHYSDRAM + MB));
+	vfyzeros((ulong *)(endmem - MB), (ulong *)endmem);
+#endif
+	qtmerrtest("reading back dram");
+	return cnt;
+}
+
+static void
+memtest(void)
+{
+	ulong wd;
+	ulong *sp, *endmem;
+
+	/*
+	 * verify that (possibly secure) dram is more or less working.
+	 * write address of each word into that word.
+	 */
+#ifdef PARANOID
+	endmem = (ulong *)(PHYSDRAM + memsz);
+#else
+	endmem = (ulong *)(PHYSDRAM + MB);		/* just the first MB */
+#endif
+	for (sp = (ulong *)PHYSDRAM; sp < endmem; sp++)
+		*sp = (ulong)sp;
+	coherence();
+	/* no need to flush caches, caches are off */
+
+	/*
+	 * now verify that each word contains its address.
+	 */
+	for (sp = (ulong *)PHYSDRAM; sp < endmem; sp++) {
+		wd = *sp;
+		if (wd != (ulong)sp) {
+			PROG('?')
+			panic("bad dram: %#p read back as %#lux", sp, wd);
+		}
+	}
+PROG('t')
+/*	memset((void *)PHYSDRAM, 0, memsz);	/* good hygiene? */
+}
+
+enum {
+	Testbase = PHYSDRAM + 0x4000,
+};
+
+void
+meminit(void)
+{
+	int i;
+	uchar *memc;
+	ulong *mem;
+
+//	iprint("sanity: writing...");
+	mem  = (ulong *)Testbase;
+	memc = (uchar *)Testbase;
+
+	memset(mem, 0252, Kilo);
+	coherence();
+	dcflush(Testbase, Kilo);
+
+//	iprint("reading...");
+	for (i = 0; i < Kilo; i++)
+		if (memc[i] != 0252)
+			panic("dram not retaining data");
+
+//	iprint("zeroing...");
+	memset(mem, '\0', Kilo);
+	coherence();
+	dcflush(Testbase, Kilo);
+	if (*mem)
+		panic("zeroed dram not zero");
+//	iprint("\n");
+}
+
+static int idx;
+static char numb[32];
+
+static void
+gendigs(ulong n)
+{
+	int dig;
+
+	do {
+		dig = n % 16;
+		if (dig > 9)
+			numb[idx--] = 'A' + dig - 10;
+		else
+			numb[idx--] = '0' + dig;
+	} while ((n /= 16) > 0);
+}
+
+static void
+prnum(ulong n)
+{
+//	PROG(' ')
+	idx = nelem(numb) - 1;
+	gendigs(n);
+	for (; idx < nelem(numb); idx++)
+		PROG(numb[idx])
+	PROG('\n')
+	PROG('\r')
+}
+
+extern uintptr vectorbase;
+
+void
+main(void)
+{
+	int flag, i, j, mode, tried;
+	char def[2*NAMELEN], line[80], *p, *file;
+	Medium *mp;
+	Type *tp;
+	static int savcnt, caching;
+	static ulong vfy = Datamagic;
+
+	/*
+	 * all cpus start executing at reset address, thus start of sram.
+	 * cpu0 loads the kernel;
+	 * all others just jump to the (by now) loaded kernel.
+	 */
+	/* "\r\nPlan " already printed by l.s */
+	if (getpir() != 0) {
+		for (j = 0; j < 6; j++)
+			for (i = 2*1000*1000*1000; i > 0; i--)
+				;
+
+		cachesinvalidate();
+		while(cpuentry == 0)
+			cachesinvalidate();
+
+		for (j = 0; j < 6; j++)
+			for (i = 2*1000*1000*1000; i > 0; i--)
+				;
+		warp9(PADDR(cpuentry));
+	}
+
+	/*
+	 * we may have to realign the data segment; apparently ql -R4096
+	 * does not pad the text segment.
+	 */
+	if (vfy != Datamagic)
+		memmove(bdata, etext, edata - bdata);
+	if (vfy != Datamagic) {
+		PROG('?')
+		panic("misaligned data segment");
+	}
+//	memset(edata, 0, end - edata);		/* zero bss */
+
+PROG('9')
+	/*
+	 * trap vectors are in sram, so we don't have to wait for dram
+	 * to become ready to initialise them.
+	 */
+	trapinit();
+PROG(' ')
+	securemem = (probeaddr(Qtm) >= 0);
+	if (securemem)
+		PROG('q')
+	else
+		PROG('n')
+PROG(' ')
+
+	/*
+	 * the stack is now at top of sram, and entry to main just pushed
+	 * stuff onto it.  the new stack will be at the top of dram,
+	 * when dram finishes initialising itself.
+	 */
+PROG('B')
+PROG('o')
+PROG('o')
+	/* do voodoo to make dram usable; prints "t".  sets memsz. */
+	savcnt = memreset();
+PROG('s')
+	memtest();			/* also prints "t" */
+	intrinit();
+
+	caching = cacheson();
+
+	/*
+	 * switch to the dram stack just below the end of dram.
+	 * have to allow enough room for main's local variables,
+	 * to avoid address faults.
+	 */
+
+PROG('r')
+	setsp(memsz - BY2PG);
+
+PROG('a')
+	meminit();
+	/* memory is now more or less normal from a software perspective. */
+
+	memset(m, 0, sizeof(Mach));
+	m->machno = 0;
+	m->up = nil;
+	MACHP(0) = m;
+
+	/*
+	 * the Mach struct is now initialised, so we can print safely.
+	 */
+
+//	print("\nPlan 9 bootstrap");	/* already printed, 1 char at a time */
+	print("p");
+//	print("\n%d iterations waiting for %s init done\n",
+//		savcnt, (securemem? "qtm": "dram"));
+	if (securemem)
+		print("; secure memory");
+	print("; caches %s\n", (caching? "on": "off"));
+	print("\n");
+	if ((uintptr)end < PHYSSRAM)
+		panic("too big; end %#p before sram @ %#ux", end, PHYSSRAM);
+	print("memory found: %,lud (%lux)\n", memsz, memsz);
+
+	spllo();
+	clockinit();
+	prcpuid();
+//	etherinit();			/* probe() calls this */
+	kbdinit();
+
+	/*
+	 * find and read plan9.ini, setting configuration variables.
+	 */
+	tp = types;
+//	debug = debugload = 1;		// DEBUG
+	if(pxe && (mp = probe(tp->type, Fini, Dany)) && mp->flag & Fini){
+		if (debug)
+			print("using %s!%s!%s\n", mp->name, mp->part, mp->ini);
+//		iniread = !dotini(mp->inifs);
+	}
+
+	/*
+	 * we should now have read plan9.ini, if any.
+	 */
+	persist = getconf("*bootppersist");
+
+	tried = 0;
+	mode = Mauto;
+
+	p = getconf("bootfile");
+	if(p != 0) {
+		mode = Manual;
+		for(i = 0; i < NMode; i++){
+			if(strcmp(p, modes[i].name) == 0){
+				mode = modes[i].mode;
+				goto done;
+			}
+		}
+		if((mp = parse(p, &file)) == nil)
+			print("Unknown boot device: %s\n", p);
+		else
+			tried = boot(mp, file);
+	}
+done:
+	if(tried == 0 && mode != Manual){
+		flag = Fany;
+		if(mode == Mlocal)
+			flag &= ~Fbootp;
+		if((mp = probe(Tany, flag, Dany)))
+			boot(mp, "");
+		if (debugload)
+			print("end auto probe\n");
+	}
+
+	def[0] = 0;
+	if(p = getconf("bootdef"))
+		strecpy(def, def + sizeof def, p);
+	flag = 0;
+	tp = types;
+	for(mp = tp->media; mp; mp = mp->next){
+		if(flag == 0){
+			flag = 1;
+			print("Boot devices:");
+		}
+		(*tp->printdevs)(mp->dev);
+	}
+	if(flag)
+		print("\n");
+
+	/*
+	 * e.g., *bootppersist=ether0
+	 *
+	 * previously, we looped in bootpopen if we were pxeload or if
+	 * *bootppersist was set.  that doesn't work well for pxeload where
+	 * bootp will never succeed on the first interface but only on another
+	 * interface.
+	 */
+//print("boot mode %s\n", modes[mode].name);
+	if (mode == Mauto && persist != nil &&
+	    (mp = parse(persist, &file)) != nil) {
+		boot(mp, file);
+		print("pausing before retry...");
+		delay(30*1000);
+		print("\n");
+	}
+
+	for(;;){
+		if(getstr("boot from", line, sizeof(line), def,
+		    (mode != Manual)*15) >= 0)
+			if(mp = parse(line, &file))
+				boot(mp, file);
+		def[0] = 0;
+	}
+}
+
+int
+getfields(char *lp, char **fields, int n, char sep)
+{
+	int i;
+
+	for(i = 0; lp && *lp && i < n; i++){
+		while(*lp == sep)
+			*lp++ = 0;
+		if(*lp == 0)
+			break;
+		fields[i] = lp;
+		while(*lp && *lp != sep){
+			if(*lp == '\\' && *(lp+1) == '\n')
+				*lp++ = ' ';
+			lp++;
+		}
+	}
+	return i;
+}
+
+int
+cistrcmp(char *a, char *b)
+{
+	int ac, bc;
+
+	for(;;){
+		ac = *a++;
+		bc = *b++;
+
+		if(ac >= 'A' && ac <= 'Z')
+			ac = 'a' + (ac - 'A');
+		if(bc >= 'A' && bc <= 'Z')
+			bc = 'a' + (bc - 'A');
+		ac -= bc;
+		if(ac)
+			return ac;
+		if(bc == 0)
+			break;
+	}
+	return 0;
+}
+
+int
+cistrncmp(char *a, char *b, int n)
+{
+	unsigned ac, bc;
+
+	while(n > 0){
+		ac = *a++;
+		bc = *b++;
+		n--;
+
+		if(ac >= 'A' && ac <= 'Z')
+			ac = 'a' + (ac - 'A');
+		if(bc >= 'A' && bc <= 'Z')
+			bc = 'a' + (bc - 'A');
+
+		ac -= bc;
+		if(ac)
+			return ac;
+		if(bc == 0)
+			break;
+	}
+
+	return 0;
+}
+
+#define PSTART		(8*MB)		/* start allocating here */
+#define PEND		(100*MB)	/* below stack */
+
+static ulong palloc = PSTART;
+
+void*
+ialloc(ulong n, int align)
+{
+	ulong p;
+	int a;
+
+	assert(palloc >= PSTART);
+	p = palloc;
+	if(align <= 0)
+		align = 4;
+	if(a = n % align)
+		n += align - a;
+	if(a = p % align)
+		p += align - a;
+
+	palloc = p+n;
+	if(palloc > PEND)
+		panic("ialloc(%lud, %d) called from %#p",
+			n, align, getcallerpc(&n));
+	return memset((void*)p, 0, n);
+}
+
+void*
+xspanalloc(ulong size, int align, ulong span)
+{
+	ulong a, v;
+
+	if((palloc + (size+align+span)) > PEND)
+		panic("xspanalloc(%lud, %d, 0x%lux) called from %#p",
+			size, align, span, getcallerpc(&size));
+
+	a = (ulong)ialloc(size+align+span, 0);
+
+	if(span > 2)
+		v = (a + span) & ~(span-1);
+	else
+		v = a;
+
+	if(align > 1)
+		v = (v + align) & ~(align-1);
+
+	return (void*)v;
+}
+
+static Block *allocbp;
+
+Block*
+allocb(int size)
+{
+	Block *bp, **lbp;
+	ulong addr;
+
+	lbp = &allocbp;
+	for(bp = *lbp; bp; bp = bp->next){
+		if((bp->lim - bp->base) >= size){
+			*lbp = bp->next;
+			break;
+		}
+		lbp = &bp->next;
+	}
+	if(bp == 0){
+		if((palloc + (sizeof(Block)+size+64)) > PEND)
+			panic("allocb(%d) called from %#p",
+				size, getcallerpc(&size));
+		bp = ialloc(sizeof(Block)+size+64, 0);
+		addr = (ulong)bp;
+		addr = ROUNDUP(addr + sizeof(Block), 8);
+		bp->base = (uchar*)addr;
+		bp->lim = ((uchar*)bp) + sizeof(Block)+size+64;
+	}
+
+	if(bp->flag)
+		panic("allocb reuse");
+
+	bp->rp = bp->base;
+	bp->wp = bp->rp;
+	bp->next = 0;
+	bp->flag = 1;
+
+	return bp;
+}
+
+void
+freeb(Block* bp)
+{
+	bp->next = allocbp;
+	allocbp = bp;
+
+	bp->flag = 0;
+}
+
+void (*etherdetach)(void);
+
+void
+warp9(ulong entry)
+{
+	ulong inst;
+	ulong *mem;
+
+	if(etherdetach)
+		etherdetach();
+//	consdrain();
+	delay(10);
+
+	splhi();
+	trapdisable();
+	coherence();
+	syncall();
+
+	qtmerrtest("syncs before kernel entry");
+	syncall();
+	putmsr(getmsr() & ~MSR_ME);	/* disable machine check traps */
+	syncall();
+	clrmchk();
+	syncall();
+
+	mem = (ulong *)PADDR(entry);
+	inst = *mem;
+	if (inst == 0)
+		panic("word at entry addr is zero, kernel not loaded");
+	/*
+	 * This is where to push things on the stack to
+	 * boot *BSD systems, e.g.
+	 * (*((void (*)(void*, void*, void*, void*, ulong, ulong))PADDR(entry)))
+	 *	(0, 0, 0, 0, 8196, 640);
+	 * will enable NetBSD boot (the real memory size needs to
+	 * go in the 5th argument).
+	 */
+	coherence();
+	syncall();
+	(*(void (*)(void))mem)();
+
+	for (;;)
+		;
+}

+ 76 - 0
sys/src/boot/vt4/malloc.c

@@ -0,0 +1,76 @@
+/* simple memory allocation */
+#include "include.h"
+
+#undef malloc
+#undef free
+
+void *
+malloc(ulong n)
+{
+	return ialloc(n, 8);
+}
+
+void
+free(void *)
+{
+}
+
+void*
+mallocz(ulong size, int clr)
+{
+	void *v;
+
+	v = malloc(size);
+	if(clr && v != nil)
+		memset(v, 0, size);
+	return v;
+}
+
+void*
+realloc(void *v, ulong size)
+{
+	USED(v, size);
+	panic("realloc called");
+	return 0;
+}
+
+void*
+calloc(ulong n, ulong szelem)
+{
+	return mallocz(n * szelem, 1);
+}
+
+void
+setmalloctag(void *v, ulong pc)
+{
+	USED(v, pc);
+}
+
+void
+setrealloctag(void *v, ulong pc)
+{
+	USED(v, pc);
+}
+
+ulong
+getmalloctag(void *v)
+{
+	USED(v);
+	assert(0);
+	return ~0;
+}
+
+ulong
+getrealloctag(void *v)
+{
+	USED(v);
+	assert(0);
+	return ~0;
+}
+
+void*
+malloctopoolblock(void *v)
+{
+	USED(v);
+	return nil;
+}

+ 196 - 0
sys/src/boot/vt4/mem.h

@@ -0,0 +1,196 @@
+/*
+ * Memory and machine-specific definitions.  Used in C and assembler.
+ */
+#define KiB		1024u			/* Kibi 0x0000000000000400 */
+#define MiB		1048576u		/* Mebi 0x0000000000100000 */
+#define GiB		1073741824u		/* Gibi 000000000040000000 */
+#define TiB		1099511627776ull	/* Tebi 0x0000010000000000 */
+#define PiB		1125899906842624ull	/* Pebi 0x0004000000000000 */
+#define EiB		1152921504606846976ull	/* Exbi 0x1000000000000000 */
+
+#define HOWMANY(x, y)	(((x)+((y)-1))/(y))
+#define ROUNDUP(x, y)	(HOWMANY((x), (y))*(y))
+#define ROUNDDN(x, y)	(((x)/(y))*(y))
+#define MIN(a, b)	((a) < (b)? (a): (b))
+#define MAX(a, b)	((a) > (b)? (a): (b))
+
+/*
+ * Sizes
+ */
+#define BI2BY		8			/* bits per byte */
+#define BY2V		8			/* bytes per vlong */
+#define BY2SE		4			/* bytes per stack element */
+#define BY2WD		4			/* bytes per int */
+#define BY2PG		4096			/* bytes per page */
+#define PGSHIFT		12			/* log(BY2PG) */
+#define PGROUND(s)	ROUNDUP(s, BY2PG)
+#define UTROUND(t)	ROUNDUP((t), 0x100000)
+#define STACKALIGN(sp)	((sp) & ~7)		/* bug: assure with alloc */
+
+#define ICACHESIZE	16384			/* 0, 4, 8, 16, or 32 KB */
+#define ICACHEWAYSIZE	(ICACHESIZE/2)		/* 2-way set associative */
+#define ICACHELINELOG	5			/* 8 words (4 bytes) per line */
+#define ICACHELINESZ	(1<<ICACHELINELOG)
+
+#define DCACHESIZE	16384			/* 0, 4, 8, 16, or 32 KB */
+#define DCACHEWAYSIZE	(DCACHESIZE/2)		/* 2-way set associative */
+#define DCACHELINELOG	5			/* 8 words (4 bytes) per line */
+#define DCACHELINESZ	(1<<DCACHELINELOG)
+
+#define BLOCKALIGN	DCACHELINESZ		/* for ../port/allocb.c */
+
+#define MAXMACH		1			/* max # cpus system can run */
+#define MACHSIZE	BY2PG
+
+/*
+ * Time
+ */
+#define HZ		100			/* clock frequency */
+#define MHz		1000000
+#define TK2SEC(t)	((t)/HZ)		/* ticks to seconds */
+
+/*
+ * IBM bit field order
+ * used only to derive bit mask for interrupt vector numbers
+ */
+#define IBIT(n)	(1UL<<(31-(n)))
+
+/*
+ * Bit encodings for Machine State Register (MSR)
+ */
+#define MSR_AP	0x02000000	/* auxiliary processor available */
+#define MSR_APE	0x00080000	/* APU exception enable */
+#define MSR_WE	0x00040000	/* wait state enable */
+#define MSR_CE	0x00020000	/* critical interrupt enable */
+#define MSR_EE	0x00008000	/* enable external/decrementer interrupts */
+#define MSR_PR	0x00004000	/* =1, user mode */
+#define MSR_FP	0x00002000	/* floating-point available */
+#define MSR_ME	0x00001000	/* enable machine check exceptions */
+#define MSR_FE0	0x00000800	/* floating-point exception mode 0 */
+#define MSR_DWE	0x00000400	/* debug wait enable */
+#define MSR_DE	0x00000200	/* debug interrupts enable */
+#define MSR_FE1	0x00000100	/* floating-point exception mode 1 */
+#define MSR_IR	0x00000020	/* enable instruction address translation */
+#define MSR_DR	0x00000010	/* enable data address translation */
+
+/* state in user mode */
+#define UMSR	(MSR_PR|MSR_DE|MSR_CE|MSR_EE|MSR_IR|MSR_DR)
+
+/*
+ * Exception Syndrome Register (ESR)
+ */
+#define ESR_MCI	0x80000000	/* instruction machine check */
+#define ESR_PIL	0x08000000	/* program interrupt: illegal instruction */
+#define ESR_PPR	0x04000000	/* program interrupt: privileged */
+#define ESR_PTR	0x02000000	/* program interrupt: trap with successful compare */
+#define ESR_PEU	0x01000000	/* program interrupt: unimplemented APU/FPU operation */
+#define ESR_DST	0x00800000	/* data storage interrupt: store fault */
+#define ESR_DIZ	0x00400000	/* data/instruction storage interrupt: zone fault */
+#define ESR_PFP	0x00080000	/* program interrupt: FPU interrupt occurred */
+#define ESR_PAP	0x00040000	/* program interrupt: APU interrupt occurred */
+#define ESR_U0F	0x00008000	/* data storage interrupt: u0 fault */
+
+/*
+ * Interrupt vector offsets
+ */
+#define INT_RESET	0x0100		/* Critical input interrupt */
+#define INT_MCHECK	0x0200		/* Machine check */
+#define INT_DSI		0x0300		/* Data storage interrupt */
+#define INT_ISI		0x0400		/* Instruction storage interrupt */
+#define INT_EI		0x0500		/* External interrupt */
+#define INT_ALIGN	0x0600		/* Alignment */
+#define INT_PROG	0x0700		/* Program */
+#define INT_FPU		0x0800		/* FPU unavailable */
+#define INT_DEC		0x0900		/* UNUSED on 405? */
+#define INT_SYSCALL	0x0C00		/* System call */
+#define INT_TRACE	0x0D00		/* UNUSED on 405? */
+#define INT_FPA		0x0E00		/* UNUSED on 405? */
+#define INT_APU		0x0F20		/* APU unavailable */
+#define INT_PIT		0x1000		/* PIT interrupt */
+#define INT_FIT		0x1010		/* FIT interrupt */
+#define INT_WDT		0x1020		/* Watchdog timer */
+#define INT_DMISS	0x1100		/* Data TLB miss */
+#define INT_IMISS	0x1200		/* Instruction TLB miss */
+#define INT_DEBUG	0x2000		/* Debug */
+
+/*
+ * Magic registers
+ */
+#define MACH		30		/* R30 is m-> */
+#define USER		29		/* R29 is up-> */
+
+/*
+ * Virtual MMU
+ */
+#define PTEMAPMEM	(1024*1024)
+#define PTEPERTAB	(PTEMAPMEM/BY2PG)
+#define SEGMAPSIZE	1984
+#define SSEGMAPSIZE	16
+#define PPN(x)		((x)&~(BY2PG-1))
+
+#define PTEVALID	(1<<0)
+#define PTEWRITE	(1<<1)
+#define PTERONLY	(0<<1)
+#define PTEUNCACHED	(1<<2)
+
+/*
+ * Physical MMU
+ */
+#define NTLB		64	/* number of entries */
+#define NTLBPID		256	/* number of hardware pids (0 = global) */
+
+/* TLBHI */
+#define TLBEPN(x)	((x) & ~0x3FF)
+#define TLB1K		(0<<7)
+#define TLB4K		(1<<7)
+#define TLB16K		(2<<7)
+#define TLB64K		(3<<7)
+#define TLB256K		(4<<7)
+#define TLB1MB		(5<<7)
+#define TLB4MB		(6<<7)
+#define TLB16MB		(7<<7)
+#define TLBVALID	(1<<6)
+#define TLBLE		(1<<5)	/* little-endian */
+#define TLBU0		(1<<4)	/* user-defined attribute */
+
+/* TLBLO */
+#define TLBRPN(x)	((x) & ~0x3FF)
+#define TLBEX		(1<<9)	/* execute enable */
+#define TLBWR		(1<<8)	/* write enable */
+#define TLBZONE(x)	((x)<<4)
+#define TLBW		(1<<3)	/* write-through */
+#define TLBI		(1<<2)	/* cache inhibit */
+#define TLBM		(1<<1)	/* memory coherent */
+#define TLBG		(1<<0)	/* guarded */
+
+/*
+ * software TLB (for quick reload by [id]tlbmiss)
+ */
+#define STLBLOG		10
+#define STLBSIZE	(1<<STLBLOG)
+
+/*
+ * Address spaces
+ */
+#define KSEG0		0x80000000
+#define KSEG1		0xA0000000		/* uncached alias for KZERO */
+#define KSEGM		0xE0000000		/* mask to check segment */
+#define KZERO		KSEG0			/* base of kernel address space */
+#define KTZERO		(KZERO+0x4000)	/* first address in kernel text */
+
+#define TSTKTOP		KZERO			/* top of temporary stack */
+#define TSTKSIZ		100
+
+#define UZERO		0			/* base of user address space */
+#define UTZERO		(UZERO+BY2PG)		/* first address in user text */
+#define USTKTOP		(TSTKTOP-TSTKSIZ*BY2PG)	/* byte just beyond user stack */
+
+#define KSTACK		8192			/* Size of kernel stack */
+
+#define OCMZERO	0x40000000			/* on-chip memory (virtual and physical--see 405EP p 5-1) */
+#define USTKSIZE	(4*1024*1024)		/* size of user stack */
+#define UREGSIZE	((8+40)*4)
+
+#include "physmem.h"
+
+#define getpgcolor(a)	0		/* ../port/page.c */

+ 55 - 0
sys/src/boot/vt4/mkenum

@@ -0,0 +1,55 @@
+#!/bin/rc
+
+awk '
+BEGIN{
+	oargc = 0;
+	for(argc = 1; argc < ARGC; argc++){
+		if(ARGV[argc] !~ /^-.+/ || ARGV[argc] ~ /--/)
+			break;
+		if(ARGV[argc] != "-D")
+			oargv[ARGV[argc]] = oargc++;
+		else
+			DEBUG = 1;
+		ARGV[argc] = "";
+	}
+}
+
+/^enum([ \t]*{|$)/{
+	inenum = 1;
+	if(DEBUG)
+		printf "inenum = 1\n";
+	next;
+}
+
+inenum && /^};$/{
+	if(DEBUG)
+		printf "inenum = 0\n";
+	inenum = 0;
+}
+
+inenum && $0 ~ /^[ \t]+[_A-Za-z][_0-9A-Za-z]+[ \t]+=[ \t]+[0-9A-Z_a-z()<> ]+,/{
+	tab = "\t";
+	if(length($1) < 8)
+		sep = tab tab;
+	else
+		sep = tab;
+	split($3, a, ",");
+	printf "#define %s%s%s", $1, sep, a[1];
+	if(match($0, /\/\*.*\*\/$/)){
+		len = length(a[1]);
+		sep = "";
+		while(len < 24){
+			sep = sep tab;
+			len += 8;
+		}
+		printf "%s%s", sep, substr($0, RSTART);
+	}
+	printf "\n"
+}
+
+/^$/{
+	printf "\n";
+}
+
+END{
+}' $*

+ 56 - 0
sys/src/boot/vt4/mkfile

@@ -0,0 +1,56 @@
+CONF=virtex4
+CONFLIST=virtex4
+BASE=0xfffe2100		# first location after vectors; see PHYSSRAM
+
+objtype=power
+</$objtype/mkfile
+p=b
+
+CFLAGS=$CFLAGS -I.
+AFLAGS=	-I.
+HFILES=\
+	data.h\
+	define.h\
+	etherif.h\
+	fs.h\
+	include.h\
+	ip.h\
+	libc.h\
+	mem.h\
+	physmem.h\
+	portdata.h\
+	portprototype.h\
+	ppc405.h\
+	prototype.h\
+	qtm.h\
+
+all:VQ:	$p$CONF
+
+<| awk -f parse $CONF
+<portmkfile
+
+OBJ=$MACH $DEVS $PORT
+
+${p}$CONF:D:	$OBJ mkfile $CONF
+	$LD -o $target.elf -aH6 -l -T$BASE -R4 $OBJ $LIB >$target.list
+	$LD -o $target		-l -T$BASE -R4 $OBJ $LIB
+	size $target
+	nm -n $target | tail -5
+	nm -n $target >$target.names
+
+$OBJ: $HFILES
+
+${objtype}a.h:D:	$objtype.h
+	rc mkenum $objtype.h > $target
+
+install:V:	$p$CONF
+	cp -x $p$CONF $p$CONF.elf /power
+	{ 9fs lookout  && cp -x $p$CONF $p$CONF.elf /n/lookout/power } &
+	{ 9fs piestand && cp -x $p$CONF $p$CONF.elf /n/piestand/power } &
+	{ 9fs slocum &&
+	cp $p$CONF^* /n/slocum/home/rae/hbsr/ml410_ddr2 &&
+	cp $p$CONF^* /n/slocum/home/rae/hbsr/ml410_qtm } &
+	wait
+
+nuke clean:V:
+	rm -f *.[$OS] *.out *.acid *.elf $p$CONF^*

+ 12 - 0
sys/src/boot/vt4/mkfilelist

@@ -0,0 +1,12 @@
+#!/bin/rc
+
+rfork e
+switch($#*){
+case 1
+	RE=`{echo *.c | sed 's/ /|/g; s/.*/^(&)$/'}
+	LIST=`{builtin cd $1; ls *.c | grep -v ''$RE''}
+	echo $LIST | sed 's/\.c//g; s/ +/|/g'
+case *
+	exit 'usage'
+}
+exit ''

+ 18 - 0
sys/src/boot/vt4/nolock.c

@@ -0,0 +1,18 @@
+#include "include.h"
+
+void
+lock(Lock* l)
+{
+	for(;;){
+		while(l->key)
+			;
+		if(TAS(&l->key) == 0)
+			return;
+	}
+}
+
+void
+unlock(Lock* l)
+{
+	l->key = 0;
+}

+ 536 - 0
sys/src/boot/vt4/parse

@@ -0,0 +1,536 @@
+BEGIN{
+	oargc = 0;
+	for(argc = 1; argc < ARGC; argc++){
+		if(ARGV[argc] !~ /^-.+/ || ARGV[argc] ~ /--/)
+			break;
+		if(ARGV[argc] != "-D")
+			oargv[ARGV[argc]] = oargc++;
+		else
+			DEBUG = 1;
+		ARGV[argc] = "";
+	}
+
+	objtype = ENVIRON["objtype"];
+
+	while(getline > 0){
+		if(/^[ \t]*$/ || /^#/)
+			continue;
+
+		if(/^[^ \t]/){
+			#section[$1] = 0;
+			tag = $1;
+		}
+		if(!tag)
+			continue;
+		sub(/^[ \t]*/, "");
+		line[tag, section[tag]++] = $0;
+	}
+
+	o = "";
+	if(!oargc || ("-mkdevlist" in oargv)){
+		s = mkdevlist();
+		if(!("-mkdevlist" in oargv) || (oargc > 1))
+			s = "DEVS=" s;
+		o = o s "\n";
+	}
+	if((!oargc || ("-mkmach" in oargv)) && (objtype in section)){
+		s = mkmach();
+		if(!("-mkmach" in oargv) || (oargc > 1))
+			s = "MACH=" s;
+		o = o s "\n";
+	}
+	if((!oargc || ("-mklib" in oargv)) && ("lib" in section)){
+		s = mklib();
+		if(!("-mklib" in oargv) || (oargc > 1))
+			s = "LIB=" s;
+		o = o s "\n";
+	}
+	if((!oargc || ("-mkport" in oargv) ) && ("port" in section)){
+		s = mkport();
+		if(!("-mkport" in oargv) || (oargc > 1))
+			s = "PORT=" s;
+		o = o s "\n";
+	}
+	if("dbgflg" in section){
+		for(i = 1; i < section["dbgflg"]; i++){
+			n = split(line["dbgflg", i], a);
+			if(n < 2 || n > 4 || a[2] !~ /'[a-zA-Z]'/)
+				continue;
+			if(n > 2 && a[3] !~ /'[a-zA-Z]'/)
+				continue;
+			if(n == 4 && (a[4] < 1 || a[4] >= 128))
+				continue;
+			dbgc[a[1]] = a[2];
+			if(n == 4)
+				dbgflg[a[3]] = a[4];
+			else if(n == 3)
+				dbgflg[a[3]] = 1;
+		}
+	}
+	if((!oargc || ("-mkrules" in oargv)) && ("dir" in section)){
+		o = o mkrules(".", exists, a, c, "-I.");
+		for(i = 1; i < section["dir"]; i++){
+			n = split(line["dir", i], a);
+			dir = "../" a[1];
+			if(n == 1)
+				a[2] = "-I.";
+			s = a[2];
+			o = o mkrules(dir, exists, a, c, s);
+			l = listolate(a, "|");
+			if(l != ""){
+				o = o "^(" l ")\\.$O:R:	" dir "/\\1.s\n";
+				o = o "\t$AS $AFLAGS " s " " dir "/$stem1.s\n";
+			}
+			l = listolate(c, "|");
+			if(l != ""){
+				o = o "^(" l ")\\.$O:R:	" dir "/\\1.c\n";
+				o = o "\t$CC $CFLAGS " s " " dir "/$stem1.c\n";
+			}
+		}
+	}
+	if((!oargc || ("-mkrootrules" in oargv)) && ("rootdir" in section)){
+		mkrootrules(name, cname, src);
+		s = ARGV[argc] ".root.s:D:";
+		for(i = 1; i < section["rootdir"]; i++)
+			s = s " " src[i];
+		s = s "\n\tmkrootall\\\n";
+		for(i = 1; i < section["rootdir"]; i++)
+			s = s "\t\t" name[i] " " cname[i] " " src[i] "\\\n";
+		s = s "\t>$target\n";
+		if(section["rootdir"] > 1)
+			o = o s;
+	}
+	if((!oargc || ("-mkrrrules" in oargv)) && ("rr" in section)){
+		n = split(line["rr", 0], a);
+		if(n == 1)
+			a[2] = ARGV[argc] ".proto";
+		s = "$CONF.rr:\tmkrr $CONF " a[2] "\n";
+		s = s "\tmkrr $CONF " a[2] "\n";
+		for(i = 1; i < section["rr"]; i++)
+			s = s "$CONF.rr:\t" line["rr", i] "\n";
+		o = o s;
+	}
+	if("-mkdevc" in oargv)
+		o = o mkdevc();
+	if("-mkerrstr" in oargv)
+		o = o mkerrstr();
+	if("-mksystab" in oargv)
+		o = o mksystab();
+	if("-mkbootconf" in oargv)
+		o = o mkbootconf();
+
+	#
+	# to do:
+	#	bootmkfile
+	#	mkrootall (can it be done at all?)
+	#
+	printf o;
+
+	exit 0;
+}
+
+function mkbootconf(				a, n, s, t, u, c, d, p, r){
+	s = "#include <u.h>\n";
+	s = s "#include <libc.h>\n\n";
+	s = s "#include \"../boot/boot.h\"\n\n";
+	s = s "Method method[] = {\n";
+
+	c = "0";
+	d = "#S/sdC0/";
+	p = "boot";
+	r = "/root";
+
+	for(i = 0; i < section["boot"]; i++){		# NOTE: start at 0
+		n = split(line["boot", i], a);
+		if(a[1] == "boot"){
+			if(a[2] == "cpu"){
+				c = "1";
+				if(n == 4 && a[3] == "boot")
+					d = a[4];
+			}
+			else if(a[2] == "rootdir" && n == 3)
+				r = a[3];
+			else if(a[2] ~ /^(bboot|dosboot|romboot)$/){
+				c = "1";
+				p = a[2];
+			}
+			else if(a[2] == "boot" && n == 3)
+				d = a[3];
+			continue;
+		}
+		s = s "\t{ \"" a[1] "\", config" a[1] ", connect" a[1] ", ";
+		t = "nil";
+		if(n > 1){
+			u = line["boot", i];
+			if(sub(/^[_A-Za-z][_A-Za-z0-9]*[ \t]*/, "", u)){
+				if(match(u, /^".*"$/))
+					u = substr(u, RSTART+1, RLENGTH-2);
+				t = "\"" u "\"";
+			}
+		}
+		s = s t ", },\n";
+	}
+	s = s "\t{ nil },\n};\n\n";
+	s = s "int cpuflag = " c ";\n";
+	s = s "char* rootdir = \"" r "\";\n";
+	s = s "char* bootdisk = \"" d "\";\n";
+	s = s "extern void " p "(int, char**);\n\n";
+	s = s "void\nmain(int argc, char **argv)\n";
+	s = s "{\n\t" p "(argc, argv);\n}\n"
+
+	t = "int (*cfs)(int) = 0;\n";
+	for(i = 1; i < section["rootdir"]; i++){
+		if($1 !~ /\/bin\/cfs$/)
+			continue;
+		t = "int (*cfs)(int) = cache;\n";
+		break;
+	}
+	s = s t;
+
+	return s;
+}
+
+function mksystab(					a, i, f, n, s, t){
+	s = "#include \"/sys/src/libc/9syscall/sys.h\"\n\n";
+	s = s "typedef void Syscall(Ar0*, va_list);\n\n";
+
+	t = "";
+	while(getline < "/sys/src/libc/9syscall/sys.h"){
+		if($1 != "#define" || NF != 3)
+			continue;
+
+		f = "sys" tolower($2);
+		if($2 == "SYSR1")
+			f = "sysr1";
+		if($2 == "RENDEZVOUS")
+			n = "Rendez";
+		else if($2 == "BRK_")
+			n = "Brk";
+		else
+			n = substr($2, 1, 1) tolower(substr($2, 2));
+
+		s = s "Syscall " f ";\n";
+		t = t "\t[" $2 "]\t";
+		if(length($2) < 6)
+			t = t "\t";
+		t = t "{ \"" n "\", " f ", ";
+		#
+		# if($1 ~ "(FVERSION|FSTAT|STAT)")
+		#	t = t "{ .u = 0 } },\n";
+		# else
+		#
+		if($1 ~ "(BIND|MOUNT|FWSTAT|WSTAT)")
+			t = t "{ .l = 0 } },\n";
+		else if($1 ~ "(EXEC|RENDEZVOUS|SEGBRK|SEGATTACH)")
+			t = t "{ .v = (void*)-1 } },\n";
+		else
+			t = t "{ .i = -1 } },\n";
+	}
+	if("syscall" in section){
+		for(i = 1; i < section["syscall"]; i++){
+			if(split(line["syscall", i], a) != 8)
+				continue;
+			if(line["syscall", i] !~ /#define.*{ \.[ilpuv] = .* }$/)
+				continue;
+
+			f = "sys" tolower(a[2]);
+			n = substr(a[2], 1, 1) tolower(substr(a[2], 2));
+
+			s = s "\nSyscall " f ";\n";
+			t = t a[1] " " a[2] "\t" a[3] "\n\t[" a[2] "]\t";
+			if(length(a[2]) < 6)
+				t = t "\t";
+			split(line["syscall", i], a, "{");
+			t = t "{ \"" n "\", " f ", {" a[2] " },\n";
+		}
+	}
+	s = s "struct {\n\tchar*\tn;\n\tSyscall*f;\n\tAr0\tr;\n}";
+	s = s " systab[] = {\n" t "};\n\nint nsyscall = nelem(systab);\n";
+
+	return s;
+}
+
+function mkerrstr(					a, s){
+	FS="[ \t;]+";
+	while(getline < "error.h"){
+		split($0, a, /\/\* | \*\//);
+		s = s $2 " " $3 " = \"" a[2] "\";\n";
+	}
+	FS=" ";
+
+	return s;
+}
+
+function mkdevc(		a, d, i, m, n, s, t, u, name, cname){
+	s = "#include \"u.h\"\n";
+	s = s "#include \"lib.h\"\n";
+	s = s "#include \"mem.h\"\n";
+	s = s "#include \"dat.h\"\n";
+	s = s "#include \"fns.h\"\n";
+	s = s "#include \"error.h\"\n\n";
+	s = s "#include \"io.h\"\n\n";
+
+	t = "";
+	for(i = 1; i < section["dev"]; i++){
+		split(line["dev", i], a);
+		s = s "extern Dev " a[1] "devtab;\n";
+		t = t "\t&" a[1] "devtab,\n";
+		d[a[1]]++;
+	}
+	s = s "Dev* devtab[] = {\n" t "\tnil,\n};\n\n";
+
+	mkrootrules(name, cname, m);
+	t = "";
+	for(i = 1; i < section["rootdir"]; i++){
+		s = s "extern uchar " cname[i] "code[];\n";
+		s = s "extern usize " cname[i] "len;\n";
+		t = t "\taddbootfile(\"" name[i] "\", " cname[i] "code, " cname[i] "len);\n";
+	}
+	for(i = 1; i < section["link"]; i++){
+		split(line["link", i], a);
+		s = s "extern void " a[1] "link(void);\n";
+		t = t "\t" a[1] "link();\n";
+	}
+	s = s "void\nlinks(void)\n{\n" t "}\n\n";
+
+	if("ip" in d && "ip" in section){
+		t = "";
+		s = s "#include \"../ip/ip.h\"\n";
+		for(i = 1; i < section["ip"]; i++){
+			split(line["ip", i], a);
+			s = s "extern void " a[1] "init(Fs*);\n";
+			t = t "\t" a[1] "init,\n";
+		}
+		s = s "void (*ipprotoinit[])(Fs*) = {\n" t "\tnil,\n};\n\n";
+	}
+
+	if("sd" in d && "sd" in section){
+		t = "";
+		s = s "#include \"sd.h\"\n";
+		for(i = 1; i < section["sd"]; i++){
+			split(line["sd", i], a);
+			s = s "extern SDifc " a[1] "ifc;\n";
+			t = t  "\t&" a[1] "ifc,\n";
+		}
+		s = s "SDifc* sdifc[] = {\n" t "\tnil,\n};\n\n";
+	}
+
+	if("uart" in d && "uart" in section){
+		t = "";
+		for(i = 1; i < section["uart"]; i++){
+			split(line["uart", i], a);
+			a[1] = substr(a[1], 5, length(a[1])-4) "physuart";
+			s = s "extern PhysUart " a[1] ";\n";
+			t = t  "\t&" a[1] ",\n";
+		}
+		s = s "PhysUart* physuart[] = {\n" t "\tnil,\n};\n\n";
+	}
+
+	t = "";
+	n = 0;
+	if("physseg" in section){
+		for(i = 1; i < section["physseg"]; i++){
+			u = line["physseg", i];
+			if(u ~ /^\.[_A-Za-z][_A-Za-z0-9]*/)
+				t = t "\t";
+			t = t "\t" u "\n";
+			if(sub(/.*\.pgalloc.*=[^_A-Za-z]*/, "", u)){
+				if(match(u, /^[_A-Za-z][_A-Za-z0-9]*/)){
+					u = substr(u, RSTART, RLENGTH);
+					s = s "extern Page *(*" u ")(Segment*, uintptr);\n";
+				}
+			}
+			else if(sub(/.*\.pgfree.*=[^_A-Za-z]*/, "", u)){
+				if(match(u, /^[_A-Za-z][_A-Za-z0-9]*/)){
+					u = substr(u, RSTART, RLENGTH);
+					s = s "extern void (*" u ")(Page*);\n";
+				}
+			}
+			if(match(u, /}/))
+				n++;
+		}
+	}
+	s = s "Physseg physseg[" n+8 "] = {\n";
+	s = s "\t{\t.attr\t= SG_SHARED,\n";
+	s = s "\t\t.name\t= \"shared\",\n";
+	s = s "\t\t.size\t= SEGMAXSIZE,\n\t},\n";
+	s = s "\t{\t.attr\t= SG_BSS,\n";
+	s = s "\t\t.name\t= \"memory\",\n";
+	s = s "\t\t.size\t= SEGMAXSIZE,\n\t},\n";
+	s = s t "};\nint nphysseg = " n+8 ";\n\n";
+
+	s = s "char dbgflg[256]";
+	t = "";
+	for(u in dbgflg)
+		t = t "\t[" u "]\t" dbgflg[u] ",\n";
+	if(t != "")
+		s = s " = {\n" t "}";
+	s = s ";\n\n";
+
+	for(i in m)
+		delete m[i];
+
+	for(i = 1; i < section["misc"]; i++){
+		split(line["misc", i], a);
+		m[a[1]] = line["misc", i];
+	}
+	if("cache" in m){
+		s = s "extern void cinit(void);\n";
+		s = s "extern void copen(Chan*);\n";
+		s = s "extern int cread(Chan*, uchar*, int, vlong);\n";
+		s = s "extern void cupdate(Chan*, uchar*, int, vlong);\n";
+		s = s "extern void cwrite(Chan*, uchar*, int, vlong);\n\n";
+		s = s "void (*mfcinit)(void) = cinit;\n";
+		s = s "void (*mfcopen)(Chan*) = copen;\n";
+		s = s "int (*mfcread)(Chan*, uchar*, int, vlong) = cread;\n";
+		s = s "void (*mfcupdate)(Chan*, uchar*, int, vlong) = cupdate;\n";
+		s = s "void (*mfcwrite)(Chan*, uchar*, int, vlong) = cwrite;\n\n";
+	}
+	else{
+		s = s "void (*mfcinit)(void) = nil;\n";
+		s = s "void (*mfcopen)(Chan*) = nil;\n";
+		s = s "int (*mfcread)(Chan*, uchar*, int, vlong) = nil;\n";
+		s = s "void (*mfcupdate)(Chan*, uchar*, int, vlong) = nil;\n";
+		s = s "void (*mfcwrite)(Chan*, uchar*, int, vlong) = nil;\n\n";
+	}
+	if(!("rdb" in misc)){
+		s = s "void\n";
+		s = s "rdb(void)\n";
+		s = s "{\n";
+		s = s "\tsplhi();\n";
+		s = s "\tiprint(\"rdb...not installed\\n\");\n";
+		s = s "\tfor(;;);\n";
+		s = s "}\n\n";
+	}
+
+	if("conf" in section){
+		for(i = 1; i < section["conf"]; i++)
+			s = s line["conf", i] "\n";
+		s = s "\n";
+	}
+	t = ".";
+	while("pwd" | getline > 0){
+		if($0 ~ /^\//)
+			t = $0;
+	}
+	s = s "char* conffile = \"" t "/" ARGV[argc] "\";\n";
+	s = s "ulong kerndate = KERNDATE;\n";
+
+	return s;
+}
+
+function mkrootrules(name, cname, src,			a, i, n){
+	for(i = 1; i < section["rootdir"]; i++){
+		n = split(line["rootdir", i], a);
+		if(n >= 2)
+			name[i] = a[2];
+		else
+			name[i] = a[1];
+		sub(/.*\//, "", name[i]);
+		cname[i] = a[1];
+		gsub(/[^a-zA-Z0-9_]/, "_", cname[i]);
+		src[i] = a[1];
+	}
+}
+
+function mkrules(dir, exists, ameta, cmeta, flags,		f, i, s, t){
+	for(i in ameta)
+		delete ameta[i];
+	for(i in cmeta)
+		delete cmeta[i];
+
+	s = "";
+	while("cd " dir "; /bin/ls *.[cs]" | getline > 0){
+		if($0 !~ /^[A-Za-z0-9]*\.[cs]$/)
+			continue;
+		f = $0;
+		if(!sub(/\.[cs]$/, ""))
+			continue;
+		if($0 in exists)
+			continue;
+		exists[$0] = dir;
+		if(f ~ /\.c$/){
+			if(!($0 in dbgc)){
+				cmeta[$0]++;
+				continue;
+			}
+			t = "$CC $CFLAGS " flags;
+		}
+		else{
+			if(!($0 in dbgc)){
+				ameta[$0]++;
+				continue;
+			}
+			t = "$AS $AFLAGS " flags;
+		}
+		s = s $0 ".$O:\t" dir "/" f "\n";
+		s = s "\t" t " -D'_DBGC_='" dbgc[$0] "'' " dir "/" f "\n";
+	}
+	return s;
+}
+
+function mkport(					array){
+	arrayify(array, "port", "", ".$O", 1);
+
+	return listolate(array, " ");
+}
+
+function mklib(						array){
+	arrayify(array, "lib", "/$objtype/lib/", ".a", 1);
+
+	return listolate(array," ");
+}
+
+function mkmach(					a, i, s){
+	s = "";
+	for(i = 1; i < section[objtype]; i++){
+		if(!split(line[objtype, i], a))
+			continue;
+		if(s == "")
+			s = a[1] ".$O";
+		else
+			s = s " " a[1] ".$O";
+	}
+
+	return s;
+}
+
+function mkdevlist(					a, array, i, j, n, s){
+	for(s in section){
+		if(line[s, 0] !~ /[ \t]\+dev[^_A-Za-z0-9]*/)
+			continue;
+		if(s == "dev")
+			arrayify(array, s, "dev", ".$O", 1);
+		else if(s == objtype)
+			arrayify(array, s, "", ".$O", 0);
+		else
+			arrayify(array, s, "", ".$O", 1);
+	}
+
+	return listolate(array, " ");
+}
+
+function listolate(array, sep,				a, s){
+	s = "";
+	for(a in array){
+		if(s == "")
+			s = a;
+		else
+			s = a sep s;
+	}
+
+	return s;
+}
+
+function arrayify(array, tag, prefix, suffix, one,	a, i, j, n){
+	for(i = 1; i < section[tag]; i++){
+		n = split(line[tag, i], a);
+		if(one)
+			array[prefix a[1] suffix]++;
+		for(j = 2; j <= n; j++){
+			if(a[$j] ~ /[+=-].*/)
+				continue;
+			array[a[j] suffix]++;
+		}
+	}
+}

+ 33 - 0
sys/src/boot/vt4/physmem.h

@@ -0,0 +1,33 @@
+/*
+ * Memory-mapped IO
+ */
+
+/*
+ * virtex4 system loses top 1/9th of 128MB to ECC in the secure memory system.
+ * if the mac boundary is not a cache-line boundary (multiple of 32),
+ * don't touch anything in the last cache-line (round mac boundary down
+ * to a multiple of 32).  MEMTOP computes the first untouchable address.
+ */
+#define MEMTOP(phys)	(((((phys)/DCACHELINESZ)*8)/9) * DCACHELINESZ)
+#define MAXMEM		(128*MB)
+
+/* memory map for rae's virtex4 design */
+#define	PHYSDRAM	0
+#define PHYSSRAM	0xfffe0000	/* 128K long, in top 128M */
+
+#define	Io		0xf0000000	/* ~512K of IO registers */
+#define Uartlite	0xf0000000
+#define Gpio		0xf0010000
+#define Intctlr		0xf0020000
+#define Temac		0xf0030000
+#define Llfifo		0xf0040000
+#define Dmactlr		0xf0050000
+#define Dmactlr2	0xf0060000
+/*
+ * if these devices exist in a given hardware configuration,
+ * they will be at these addresses.
+ */
+#define Qtm		0xf0070000	/* encrypted memory control */
+#define Mpmc		0xf0080000	/* multi-port memory controller */
+/* setting low bit interrupts cpu0; don't set Hie */
+#define Intctlr2	0xf0090000	/* sw interrupt controller */

+ 23 - 0
sys/src/boot/vt4/portclock.c

@@ -0,0 +1,23 @@
+/* portable clock code */
+#include "include.h"
+
+ulong intrcount[MAXMACH];
+
+void
+hzclock(void)
+{
+	m->ticks++;
+	dcflush(PTR2UINT(&m->ticks), sizeof m->ticks);
+}
+
+void
+timerintr(Ureg *)
+{
+	intrcount[m->machno]++;
+	hzclock();
+}
+
+void
+timersinit(void)
+{
+}

+ 16 - 0
sys/src/boot/vt4/portmkfile

@@ -0,0 +1,16 @@
+%.$O:		%.s
+	$AS $AFLAGS $stem.s
+
+%.$O:		%.c
+	$CC $CFLAGS $stem.c
+
+%.$O:		include.h
+%.$O:		/$objtype/include/u.h
+%.$O:		libc.h
+%.$O:		define.h
+%.$O:		data.h portdata.h
+%.$O:		prototype.h portprototype.h
+%.$O:		glue.h
+
+clean:V:
+	rm -f *.[$OS] *.out *.m *.acid $p$CONF *.elf

+ 2 - 0
sys/src/boot/vt4/portprototype.h

@@ -0,0 +1,2 @@
+extern void	ilock(Lock*);
+extern void	iunlock(Lock*);

+ 64 - 0
sys/src/boot/vt4/ppc405.h

@@ -0,0 +1,64 @@
+/*
+ * PowerPC 405 definitions
+ */
+#include "mem.h"
+
+#undef	PGROUND
+#define PGROUND(s)	ROUNDUP(s, MiB)
+
+/*
+ * Special Purpose Registers of interest here
+ */
+
+#define SPR_SRR0	26		/* Save/Restore Register 0 */
+#define SPR_SRR1	27		/* Save/Restore Register 1 */
+#define SPR_TBU		85		/* Time Base Upper */
+#define SPR_SPRG0	272		/* SPR General 0 */
+#define SPR_SPRG1	273		/* SPR General 1 */
+#define SPR_SPRG2	274		/* SPR General 2 */
+#define SPR_SPRG3	275		/* SPR General 3 */
+#define SPR_TBL		284		/* Time Base Lower */
+#define	SPR_PVR		287		/* Processor Version */
+#define SPR_IVOR(n)	(400+(n))	/* Interrupt Vector Offset 0-15 */
+#define SPR_ZPR		944		/* Zone Protection Register */
+#define SPR_PID		945		/* Process Identity */
+#define SPR_MMUCR	946		/* MMU Control */
+#define SPR_CCR0	947		/* Core Configuration Register 0 */
+#define SPR_IAC3	948		/* Instruction Address Compare 3 */
+#define SPR_IAC4	949		/* Instruction Address Compare 4 */
+#define SPR_DVC1	950		/* Data Value Compare 1 */
+#define SPR_DVC2	951		/* Data Value Compare 2 */
+#define SPR_SGR		953		/* Store Guarded Register */
+#define SPR_DCWR	954		/* Data Cache Write-through Register */
+#define SPR_SLER	955		/* Storage Little Endian Register */
+#define SPR_SU0R	956		/* Storage User-defined 0 Register */
+#define SPR_DBCR1	957		/* Debug Control Register 1 */
+#define SPR_ICDBDR	979		/* Instruction Cache Debug Data Register */
+#define SPR_ESR		980		/* Exception Syndrome Register */
+#define SPR_DEAR	981		/* Data Error Address Register */
+#define SPR_EVPR	982		/* Exception Vector Prefix Register */
+#define SPR_TSR		984		/* Time Status Register */
+#define SPR_TCR		986		/* Time Control Register */
+#define SPR_PIT		987		/* Programmable Interval Timer */
+#define SPR_SRR2	990		/* Save/Restore Register 2 */
+#define SPR_SRR3	991		/* Save/Restore Register 3 */
+#define SPR_DBSR	1008		/* Debug Status Register */
+#define SPR_DBCR0	1010		/* Debug Control Register 0 */
+#define SPR_IAC1	1012		/* Instruction Address Compare 1 */
+#define SPR_IAC2	1013		/* Instruction Address Compare 2 */
+#define SPR_DAC1	1014		/* Data Address Compare 1 */
+#define SPR_DAC2	1015		/* Data Address Compare 2 */
+#define SPR_DCCR	1018		/* Data Cache Cachability Register */
+#define SPR_ICCR	1019		/* Instruction Cache Cachability Register */
+
+/*
+ * Bit encodings for Memory Management Unit Control Register (MMUCR)
+ */
+#define MMUCR_STS	0x00010000	/* search translation space */
+#define MMUCR_IULXE	0x00040000	/* instruction cache unlock exception enable */
+#define MMUCR_DULXE	0x00080000	/* data cache unlock exception enable */
+#define MMUCR_U3L2SWOAE	0x00100000	/* U3 L3 store without allocate enable */
+#define MMUCR_U2SWOAE	0x00200000	/* U2 store without allocate enable */
+#define MMUCR_U1TE	0x00400000	/* U1 transient enable */
+#define MMUCR_SWOA	0x01000000	/* store without allocate */
+#define MMUCR_L2SWOA	0x02000000	/* L2 store without allocate */

+ 38 - 0
sys/src/boot/vt4/print.c

@@ -0,0 +1,38 @@
+#include "include.h"
+
+int
+print(char* fmt, ...)
+{
+	int n;
+	va_list args;
+	char buf[PRINTSIZE];
+
+	va_start(args, fmt);
+	n = vseprint(buf, buf+sizeof(buf), fmt, args) - buf;
+	va_end(args);
+
+	vuartputs(buf, n);
+	return n;
+}
+
+void
+_fmtlock(void)
+{
+}
+
+void
+_fmtunlock(void)
+{
+}
+
+int
+_efgfmt(Fmt*)
+{
+	return -1;
+}
+
+int
+errfmt(Fmt*)
+{
+	return -1;
+}

+ 196 - 0
sys/src/boot/vt4/prototype.h

@@ -0,0 +1,196 @@
+#define PTR2UINT(p)	((uintptr)(p))
+#define UINT2PTR(i)	((void*)(i))
+
+/* physical == virtual in our world! */
+#define PADDR(a)	((ulong)(a) & ~0xF0000000)
+
+#define	GSHORT(p)	(((p)[1]<<8)|(p)[0])
+#define	GLONG(p)	((GSHORT(p+2)<<16)|GSHORT(p))
+#define	GLSHORT(p)	(((p)[0]<<8)|(p)[1])
+#define	GLLONG(p)	(((ulong)GLSHORT(p)<<16)|GLSHORT(p+2))
+#define	PLLONG(p,v)	(p)[3]=(v);(p)[2]=(v)>>8;(p)[1]=(v)>>16;(p)[0]=(v)>>24
+
+#define mallocalign(n, a, o, s)	ialloc((n), (a))
+
+/* emergency debugging printing, from uart.c */
+#define Uarttxfifo	((ulong *)(Uartlite + 4))
+#define PROG(c)		{ coherence(); *Uarttxfifo = (uchar)(c); coherence(); }
+
+/*
+ * l32p.s
+ */
+void	cachesinvalidate(void);
+void	dcbi(void*);
+void	dcflush(uintptr, usize);
+void	eieio(void);
+u32int	esrget(void);
+void	esrput(u32int);
+// char*	getconf(char*);
+#define getconf(s) nil
+u32int	getccr0(void);
+u32int	getdar(void);
+u32int	getdear(void);
+u32int	getdec(void);
+u32int	getesr(void);
+u32int	getmsr(void);
+u32int	getpid(void);
+u32int	getpir(void);
+u32int	getpit(void);
+u32int	getpvr(void);
+u32int	gettbl(void);
+u32int	gettsr(void);
+void	icflush(uintptr, usize);
+int	islo(void);
+void	microdelay(int);
+u32int	mmucrget(void);
+void	mmucrput(u32int);
+void	putdec(ulong);
+void	putesr(ulong);
+void	putevpr(ulong);
+void	putmsr(u32int);
+void	putpid(u32int);
+void	putpit(u32int);
+void	putsdr1(u32int);
+void	puttcr(u32int);
+void	puttsr(u32int);
+void	setsp(uintptr);
+void	spldone(void);
+int	splhi(void);
+int	spllo(void);
+void	splx(int);
+void	splxpc(int);
+void	sync(void);
+void	syncall(void);
+int	tas32(uint*);
+void	trapvec(void);
+int	_xdec(long *);
+int	_xinc(long *);
+
+/*
+ * trap.c
+ */
+void	trapdisable(void);
+void	trapinit(void);
+
+/*
+ * intr.c
+ */
+void	intr(Ureg *ur);
+void	intrack(ulong);
+void	intrenable(ulong bit, int (*func)(ulong));
+void	intrinit(void);
+ulong	lddbg(void);
+
+/*
+ * uart.c
+ */
+int	vuartgetc(void);
+void	vuartputc(int c);
+int	vuartputs(char *s, int len);
+
+/*
+ * clock.c
+ */
+void	clockinit(void);
+void	clockintr(Ureg *ureg);
+void	delay(int);
+void	prcpuid(void);
+void	timerintr(Ureg*);
+
+/*
+ * dma.c
+ */
+void	dmacpy(void *dest, void *src, ulong len, ulong flags);
+void	dmainit(void);
+void	dmastart(void *dest, void *src, ulong len, ulong flags);
+void	dmawait(void);
+
+/*
+ * ether.c
+ */
+uchar	*etheraddr(int ctlrno);
+int	etherinit(void);
+void	etherinitdev(int, char*);
+void	etherprintdevs(int);
+int	etherrxflush(int ctlrno);
+int	etherrxpkt(int ctlrno, Etherpkt* pkt, int timo);
+int	ethertxpkt(int ctlrno, Etherpkt* pkt, int len, int);
+
+/*
+ * llfifo.c
+ */
+void	llfifoinit(Ether *);
+int	llfifointr(ulong bit);
+void	llfiforeset(void);
+void	llfifotransmit(uchar *ubuf, unsigned len);
+
+/*
+ * load.c
+ */
+int	getfields(char*, char**, int, char);
+void*	ialloc(ulong, int);
+
+/*
+ * boot.c
+ */
+int	bootpass(Boot *b, void *vbuf, int nbuf);
+
+/*
+ * bootp.c
+ */
+int	bootpboot(int, char*, Boot*);
+void*	pxegetfspart(int, char*, int);
+
+/*
+ * conf.c
+ */
+int	dotini(Fs *fs);
+
+/*
+ * console.c
+ */
+void	consinit(char*, char*);
+int	getstr(char*, char*, int, char*, int);
+void	kbdinit(void);
+void	warp86(char*, ulong);
+void	warp9(ulong);
+
+/*
+ * inflate.c
+ */
+int	gunzip(uchar*, int, uchar*, int);
+
+/*
+ * misc.
+ */
+
+void	ilock(Lock*);
+void	iunlock(Lock*);
+int	lock(Lock*);
+void	unlock(Lock*);
+
+void	panic(char *fmt, ...);
+
+#define TAS(l)		tas32(l)
+
+#define iprint		print
+
+#define coherence()	eieio()
+#define exit(n)		for(;;) ;
+
+void	cacheline0(uintptr addr);
+ulong	cacheson(void);
+void	clrmchk(void);
+void	dump(void *, int);
+void	dumpregs(Ureg *);
+void	flushwrbufs(void);
+uintptr	memsize(void);
+vlong	probeaddr(uintptr addr);
+/*
+ * qtm.c
+ */
+int	qtmerrfmt(char *, int);
+void	qtmerrtest(char *);
+void	qtmerrtestaddr(ulong);
+int	qtmmemreset(void);
+vlong	qtmprobeaddr(uintptr addr);

+ 210 - 0
sys/src/boot/vt4/taslock.c

@@ -0,0 +1,210 @@
+#include "include.h"
+
+#undef LOCKCYCLES
+
+#ifdef LOCKCYCLES
+uvlong maxlockcycles;
+uvlong maxilockcycles;
+ulong maxlockpc;
+ulong maxilockpc;
+#endif
+
+struct
+{
+	ulong	locks;
+	ulong	glare;
+	ulong	inglare;
+} lockstats;
+
+static void
+inccnt(Ref *r)
+{
+	_xinc(&r->ref);
+}
+
+static int
+deccnt(Ref *r)
+{
+	int x;
+
+	x = _xdec(&r->ref);
+	assert(x >= 0);
+//	if(x < 0)
+//		panic("deccnt pc=%#p", getcallerpc(&r));
+	return x;
+}
+
+static void
+dumplockmem(char *tag, Lock *l)
+{
+	uchar *cp;
+	int i;
+
+	iprint("%s: ", tag);
+	cp = (uchar*)l;
+	for(i = 0; i < 64; i++)
+		iprint("%2.2ux ", cp[i]);
+	iprint("\n");
+}
+
+void
+lockloop(Lock *l, uintptr pc)
+{
+	print("lock %#p loop key %#ux pc %#p\n", l, l->key, pc);
+}
+
+int
+lock(Lock *l)
+{
+	int i;
+	uintptr pc;
+
+	pc = getcallerpc(&l);
+
+	lockstats.locks++;
+	if(TAS(&l->key) == 0){
+		l->pc = pc;
+		l->isilock = 0;
+#ifdef LOCKCYCLES
+		cycles(&l->lockcycles);
+#endif
+		return 0;
+	}
+
+	lockstats.glare++;
+	for(;;){
+		lockstats.inglare++;
+		i = 0;
+		while(l->key){
+			if(i++ > 100000000){
+				i = 0;
+				lockloop(l, pc);
+			}
+		}
+		if(TAS(&l->key) == 0){
+			l->pc = pc;
+			l->isilock = 0;
+#ifdef LOCKCYCLES
+			cycles(&l->lockcycles);
+#endif
+			return 1;
+		}
+	}
+}
+
+void
+ilock(Lock *l)
+{
+	ulong sr;
+	uintptr pc;
+
+	pc = getcallerpc(&l);
+	lockstats.locks++;
+
+	sr = splhi();
+	if(TAS(&l->key) != 0){
+		lockstats.glare++;
+		/*
+		 * Cannot also check l->pc and l->m here because
+		 * they might just not be set yet, or the lock might 
+		 * even have been let go.
+		 */
+		if(!l->isilock){
+			dumplockmem("ilock:", l);
+			print("corrupt ilock %#p pc=%#p m=%#p isilock=%d", 
+				l, l->pc, l->m, l->isilock);
+			assert(0);
+		}
+		if(l->m == MACHP(m->machno)) {
+			print("ilock: deadlock on cpu%d pc=%#p lockpc=%#p\n", 
+				m->machno, pc, l->pc);
+			assert(0);
+		}
+		for(;;){
+			lockstats.inglare++;
+			splx(sr);
+			while(l->key)
+				;
+			sr = splhi();
+			if(TAS(&l->key) == 0)
+				goto acquire;
+		}
+	}
+acquire:
+//	m->ilockdepth++;
+	l->sr = sr;
+	l->pc = pc;
+	l->isilock = 1;
+	l->m = MACHP(m->machno);
+#ifdef LOCKCYCLES
+	cycles(&l->lockcycles);
+#endif
+}
+
+int
+canlock(Lock *l)
+{
+	if(TAS(&l->key))
+		return 0;
+
+	l->pc = getcallerpc(&l);
+	l->m = MACHP(m->machno);
+	l->isilock = 0;
+#ifdef LOCKCYCLES
+	cycles(&l->lockcycles);
+#endif
+	return 1;
+}
+
+void
+unlock(Lock *l)
+{
+#ifdef LOCKCYCLES
+	uvlong x;
+	cycles(&x);
+	l->lockcycles = x - l->lockcycles;
+	if(l->lockcycles > maxlockcycles){
+		maxlockcycles = l->lockcycles;
+		maxlockpc = l->pc;
+	}
+#endif
+
+	if(l->key == 0)
+		print("unlock: not locked: pc %#p\n", getcallerpc(&l));
+	if(l->isilock)
+		print("unlock of ilock: pc %#p, held by %#p\n",
+			getcallerpc(&l), l->pc);
+	l->m = nil;
+	l->key = 0;
+	coherence();
+}
+
+void
+iunlock(Lock *l)
+{
+	ulong sr;
+
+#ifdef LOCKCYCLES
+	uvlong x;
+	cycles(&x);
+	l->lockcycles = x - l->lockcycles;
+	if(l->lockcycles > maxilockcycles){
+		maxilockcycles = l->lockcycles;
+		maxilockpc = l->pc;
+	}
+#endif
+
+	if(l->key == 0)
+		print("iunlock: not locked: pc %#p\n", getcallerpc(&l));
+	if(!l->isilock)
+		print("iunlock of lock: pc %#p, held by %#p\n", getcallerpc(&l), l->pc);
+	if(islo())
+		print("iunlock while lo: pc %#p, held by %#p\n", getcallerpc(&l), l->pc);
+
+	sr = l->sr;
+	l->m = nil;
+	l->key = 0;
+	coherence();
+//	m->ilockdepth--;
+	splx(sr);
+}

+ 324 - 0
sys/src/boot/vt4/trap.c

@@ -0,0 +1,324 @@
+/*
+ * power pc 405 traps
+ */
+#include "include.h"
+
+static struct {
+	ulong off;
+	char *name;
+} intcause[] = {
+	{ INT_RESET,	"system reset" },
+	{ INT_MCHECK,	"machine check" },
+	{ INT_DSI,	"data access" },
+	{ INT_ISI,	"instruction access" },
+	{ INT_EI,	"external interrupt" },
+	{ INT_ALIGN,	"alignment" },
+	{ INT_PROG,	"program exception" },
+	{ INT_FPU,	"floating-point unavailable" },
+	{ INT_DEC,	"decrementer" },
+	{ INT_SYSCALL,	"system call" },
+	{ INT_TRACE,	"trace trap" },
+	{ INT_FPA,	"floating point unavailable" },
+	{ INT_APU,	"auxiliary processor unavailable" },
+	{ INT_PIT,	"programmable interval timer interrrupt" },
+	{ INT_FIT,	"fixed interval timer interrupt" },
+	{ INT_WDT,	"watch dog timer interrupt" },
+	{ INT_DMISS,	"data TLB miss" },
+	{ INT_IMISS,	"instruction TLB miss" },
+	{ INT_DEBUG,	"debug interrupt" },
+	{ 0,		"unknown interrupt" }
+};
+
+static char *excname(ulong, u32int);
+
+char *regname[]={
+	"CAUSE",	"SRR1",
+	"PC",		"GOK",
+	"LR",		"CR",
+	"XER",	"CTR",
+	"R0",		"R1",
+	"R2",		"R3",
+	"R4",		"R5",
+	"R6",		"R7",
+	"R8",		"R9",
+	"R10",	"R11",
+	"R12",	"R13",
+	"R14",	"R15",
+	"R16",	"R17",
+	"R18",	"R19",
+	"R20",	"R21",
+	"R22",	"R23",
+	"R24",	"R25",
+	"R26",	"R27",
+	"R28",	"R29",
+	"R30",	"R31",
+};
+
+static int probing, trapped;
+static jmp_buf probenv;
+
+static void
+sethvec(uintptr v, void (*r)(void))
+{
+	u32int *vp;
+	ulong o, ra;
+	long d;
+
+	vp = UINT2PTR(v);
+	vp[0] = 0x7c1043a6;			/* MOVW R0, SPR(SPRG0) */
+	vp[1] = 0x7c0802a6;			/* MOVW LR, R0 */
+	vp[2] = 0x7c1243a6;			/* MOVW R0, SPR(SPRG2) */
+	d = (uchar*)r - (uchar*)&vp[3];
+	o = (ulong)d >> 25;
+	if(o != 0 && o != 0x7F){
+		/* a branch too far: running from ROM */
+		ra = (ulong)r;
+		vp[3] = (15<<26)|(ra>>16);	/* MOVW $r&~0xFFFF, R0 */
+		vp[4] = (24<<26)|(ra&0xFFFF);	/* OR $r&0xFFFF, R0 */
+		vp[5] = 0x7c0803a6;		/* MOVW	R0, LR */
+		vp[6] = 0x4e800021;		/* BL (LR) */
+	}
+	else
+		vp[3] = (18<<26)|(d&0x3FFFFFC)|1;	/* BL (relative) */
+	dcflush(PTR2UINT(vp), 8*sizeof(u32int));
+}
+
+static void
+sethvec2(uintptr v, void (*r)(void))
+{
+	u32int *vp;
+	long d;
+
+	vp = UINT2PTR(v);
+	d = (uchar*)r - (uchar*)&vp[0];
+	vp[0] = (18<<26)|(d & 0x3FFFFFC);	/* B (relative) */
+	dcflush(PTR2UINT(vp), sizeof(*vp));
+}
+
+extern uintptr vectorbase;
+
+void
+trapinit(void)
+{
+	uintptr e, s;
+
+	putesr(0);				/* clears machine check */
+	coherence();
+
+	/*
+	 * set all exceptions to trap
+	 */
+	s = vectorbase + 0x100;		/* Mach is at vectorbase */
+	/* 0x2000 is last documented 405 vector */
+	for(e = s+0x2100; s < e; s += 0x100)
+		sethvec(s, trapvec);
+	coherence();
+
+	/*
+	 * set exception handlers
+	 */
+//	sethvec(vectorbase + INT_RESET, trapcritvec);
+//	sethvec(vectorbase + INT_MCHECK, trapcritvec);
+	sethvec(vectorbase + INT_DSI, trapvec);
+	sethvec(vectorbase + INT_ISI, trapvec);
+	sethvec(vectorbase + INT_EI, trapvec); /* external non-critical intrs */
+	sethvec(vectorbase + INT_ALIGN, trapvec);
+	sethvec(vectorbase + INT_PROG, trapvec);
+	sethvec(vectorbase + INT_FPU, trapvec);
+	sethvec(vectorbase + INT_SYSCALL, trapvec);
+	sethvec(vectorbase + INT_APU, trapvec);
+	sethvec(vectorbase + INT_PIT, trapvec);
+	sethvec(vectorbase + INT_FIT, trapvec);
+//	sethvec(vectorbase + INT_WDT, trapcritvec);
+//	sethvec2(vectorbase + INT_DMISS, dtlbmiss);
+//	sethvec2(vectorbase + INT_IMISS, itlbmiss);
+//	sethvec(vectorbase + INT_DEBUG, trapcritvec);
+	syncall();
+
+	/* l.s already set EVPR */
+	putmsr(getmsr() | MSR_CE | MSR_ME);	/* no MSR_EE here */
+	coherence();
+}
+
+void
+trapdisable(void)
+{
+	putmsr(getmsr() & ~( MSR_EE | MSR_CE | MSR_ME)); /* MSR_DE as well? */
+}
+
+static char*
+excname(ulong ivoff, u32int esr)
+{
+	int i;
+
+	if(ivoff == INT_PROG){
+		if(esr & ESR_PIL)
+			return "illegal instruction";
+		if(esr & ESR_PPR)
+			return "privileged";
+		if(esr & ESR_PTR)
+			return "trap with successful compare";
+		if(esr & ESR_PEU)
+			return "unimplemented APU/FPU";
+		if(esr & ESR_PAP)
+			return "APU exception";
+		if(esr & ESR_U0F)
+			return "data storage: u0 fault";
+	}
+	for(i=0; intcause[i].off != 0; i++)
+		if(intcause[i].off == ivoff)
+			break;
+	return intcause[i].name;
+}
+
+void
+dumpstack(void)
+{
+	/* sorry.  stack is in dram, not Mach now */
+}
+
+void
+dumpregs(Ureg *ureg)
+{
+	int i;
+	uintptr *l;
+
+	iprint("cpu%d: registers for boot\n", m->machno);
+
+	l = &ureg->cause;
+	for(i = 0; i < nelem(regname); i += 2, l += 2)
+		iprint("%s\t%.8p\t%s\t%.8p\n", regname[i], l[0], regname[i+1], l[1]);
+}
+
+static void
+faultpower(Ureg *ureg, ulong addr, int read)
+{
+	USED(read);
+	dumpregs(ureg);
+	panic("boot fault: 0x%lux", addr);
+}
+
+void
+trap(Ureg *ur)
+{
+	int ecode;
+	u32int esr;
+
+	ur->cause &= 0xFFE0;
+	ecode = ur->cause;
+	if(ecode < 0 || ecode >= 0x2000)
+		ecode = 0x3000;
+	esr = getesr();
+	putesr(0);
+
+	switch(ecode){
+	case INT_SYSCALL:
+		panic("syscall in boot: srr1 %#4.4luX pc %#p\n",
+			ur->srr1, ur->pc);
+
+	case INT_PIT:
+		clockintr(ur);
+		break;
+
+	case INT_MCHECK:
+		if (probing) {
+			trapped++;
+			longjmp(probenv, 1);
+		}
+		if(esr & ESR_MCI){
+			//iprint("mcheck-mci %lux\n", ur->pc);
+			faultpower(ur, ur->pc, 1);
+			break;
+		}
+		//iprint("mcheck %#lux esr=%#ux dear=%#ux\n", ur->pc, esr, getdear());
+		ur->pc -= 4;	/* back up to faulting instruction */
+		/* fall through */
+	case INT_DSI:
+	case INT_DMISS:
+		faultpower(ur, getdear(), !(esr&ESR_DST));
+		break;
+
+	case INT_ISI:
+	case INT_IMISS:
+		faultpower(ur, ur->pc, 1);
+		break;
+
+	case INT_EI:
+		intr(ur);
+		break;
+
+	case INT_PROG:
+	default:
+		print("boot %s pc=%#lux\n", excname(ecode, esr), ur->pc);
+		dumpregs(ur);
+		dumpstack();
+		if(m->machno == 0)
+			spllo();
+		exit(1);
+	}
+	splhi();
+}
+
+static Lock fltlck;
+
+vlong
+probeaddr(uintptr addr)
+{
+	vlong v;
+
+	ilock(&fltlck);
+	probing = 1;
+	/*
+	 * using setjmp & longjmp is a sleazy hack; see ../../9k/vt4/trap.c
+	 * for a less sleazy hack.
+	 */
+	if (setjmp(probenv) == 0)
+		v = *(ulong *)addr;	/* this may cause a fault */
+	else
+		v = -1;
+	probing = 0;
+	iunlock(&fltlck);
+	return v;
+}
+
+vlong
+qtmprobeaddr(uintptr addr)
+{
+	int i;
+	vlong v, junk;
+	vlong *ptr;
+
+	ilock(&fltlck);
+	probing = 1;
+	trapped = 0;
+	v = 0;
+	/*
+	 * using setjmp & longjmp is a sleazy hack; see ../../9k/vt4/trap.c
+	 * for a less sleazy hack.
+	 */
+	if (setjmp(probenv) == 0) {
+		syncall();
+		clrmchk();
+		syncall();
+
+		ptr = (vlong *)addr;
+		for (i = 0; !trapped && !(getesr() & ESR_MCI) &&
+		    i < DCACHELINESZ/sizeof *ptr; i++) {
+			ptr[i] = 0;
+			coherence();
+		}
+		junk = ptr[0];
+		USED(junk);
+	} else
+		v = -1;
+
+	syncall();
+	if(getesr() & ESR_MCI)
+		v = -1;
+	syncall();
+	clrmchk();
+	syncall();
+	probing = 0;
+	iunlock(&fltlck);
+	return v;
+}

+ 64 - 0
sys/src/boot/vt4/uart.c

@@ -0,0 +1,64 @@
+/* xilinx uartlite driver (polling) */
+#include "include.h"
+
+enum {
+	/* UART Lite register offsets */
+	Rxfifo		= 0,		/* receive FIFO, read only */
+	Txfifo		= 4,		/* transmit FIFO, write only */
+	Status		= 8,		/* status register, read only */
+	Ctl		= 12,		/* control reg, write only */
+
+	/* control register bit positions */
+	Ctlintrena	= 0x10,		/* enable interrupt */
+	Rxfiforst	= 0x02,		/* reset receive FIFO */
+	Txfiforst	= 0x01,		/* reset transmit FIFO */
+
+	/* status register bit positions */
+	Parerr		= 0x80,
+	Framerr		= 0x40,
+	Overrunerr	= 0x20,
+	Stsintrena	= 0x10,		/* interrupt enabled */
+	Txfifofull	= 0x08,		/* transmit FIFO full */
+	Txfifoempty	= 0x04,		/* transmit FIFO empty */
+	Rxfifofull	= 0x02,		/* receive FIFO full */
+	Rxfifohasdata	= 0x01,		/* data in receive FIFO */
+
+	FIFO_SIZE	= 16,		/* bytes in tx or rx fifo */
+};
+
+#define Uartctl		((ulong *)(Uartlite + Ctl))
+#define Uartstatus	((ulong *)(Uartlite + Status))
+#define Uartrxfifo	((ulong *)(Uartlite + Rxfifo))
+/* #define Uarttxfifo	((ulong *)(Uartlite + Txfifo)) /* see prototype.h */
+
+void
+vuartputc(int c)
+{
+	int i;
+
+	coherence();
+	i = 2000000;		/* allow > 1.04 ms per char @ 9600 baud */
+	while(*Uartstatus & Txfifofull && i-- > 0)
+		;
+	*Uarttxfifo = (uchar)c;
+	coherence();
+}
+
+int
+vuartputs(char *s, int len)
+{
+	while (len-- > 0) {
+		if (*s == '\n')
+			vuartputc('\r');
+		vuartputc(*s++);
+	}
+	return len;
+}
+
+int
+vuartgetc(void)
+{
+	while((*Uartstatus & Rxfifohasdata) == 0)
+		;
+	return (uchar)*Uartrxfifo;
+}

+ 26 - 0
sys/src/boot/vt4/virtex4

@@ -0,0 +1,26 @@
+# rae's virtex4 ml410 boot
+power
+	l
+	load
+	trap
+	clock
+	archvt4
+	intr
+	uart
+	ethertemac
+	llfifo
+	fakeqtm
+# port stuff
+	portclock
+	malloc
+	print
+	console
+	taslock
+	ether
+	boot
+	fs
+	bootp
+
+lib
+	libip
+	libc

+ 18 - 0
sys/src/boot/vt4/words

@@ -0,0 +1,18 @@
+This bootstrap is intended to load a Plan 9 kernel via bootp and tftp
+into a Xilinx Virtex 4 ML410 board.  It shares code with the ML510
+boot in ../vt5.  The mkfile generates an elf file which is then fed
+into the Xilinx CAD tool to generate a bitstream for loading into the
+FPGA on the ML410.
+
+This bootstrap will almost certainly not work for you without some
+changes.  In particular, the addresses of the I/O registers in
+physmem.h will have to be changed to match yours and you'll need to
+make sure that they are mapped if you have more than 512K of them.
+
+This bootstrap does not use dma but in principle it could, by
+replacing calls to fifocpy with calls to dmacpy.  dma is one more
+thing that could go wrong, so we ended up using the cpu to copy words
+to and from ethernet fifos.
+
+This code runs in high memory (above 0xfffe0000) and its global data
+is thus in sram.  On the Virtex 4, the bootstrap runs with the MMU off.