shithub: riscv

Download patch

ref: 2806a34ec0152cbf8ac0e2198bda111a9b1273bd
parent: ed040d676ad95858211ec8b5f43e8713692fdeb2
author: aiju <devnull@localhost>
date: Sun Jun 18 18:17:35 EDT 2017

vmx(1): linux kernel loading; PIT fixes to support linux; support VGA 0x3D4 word writes; support sending virtio ethernet packets to a file and prepending snoopy headers

--- a/sys/src/cmd/vmx/exith.c
+++ b/sys/src/cmd/vmx/exith.c
@@ -14,6 +14,13 @@
 	u32int ilen, iinfo;
 };
 
+static char *x86reg[16] = {
+	RAX, RCX, RDX, RBX,
+	RSP, RBP, RSI, RDI,
+	R8, R9, R10, R11,
+	R12, R13, R14, R15
+};
+
 static void
 skipinstr(ExitInfo *ei)
 {
@@ -242,7 +249,7 @@
 		break;
 	case 2: goto literal; /* cache stuff */
 	case 3: goto zero; /* processor serial number */
-	case 4: goto literal; /* cache stuff */
+	case 4: goto zero; /* cache stuff */
 	case 5: goto zero; /* monitor/mwait */
 	case 6: goto zero; /* thermal management */
 	case 7: goto zero; /* more features */
@@ -305,6 +312,7 @@
 		if(rd) val = rget("efer");
 		else rset("efer", val);
 		break;
+	case 0x8B: val = 0; break; /* microcode update */
 	default:
 		if(rd){
 			vmerror("read from unknown MSR %#ux ignored", cx);
@@ -323,12 +331,6 @@
 static void
 movdr(ExitInfo *ei)
 {
-	static char *reg[16] = {
-		RAX, RCX, RDX, RBX,
-		RSP, RBP, RSI, RDI,
-		R8, R9, R10, R11,
-		R12, R13, R14, R15
-	};
 	static char *dr[8] = { "dr0", "dr1", "dr2", "dr3", nil, nil, "dr6", "dr7" };
 	int q;
 	
@@ -338,13 +340,65 @@
 		return;
 	}
 	if((q & 16) != 0)
-		rset(reg[q >> 8 & 15], rget(dr[q & 7]));
+		rset(x86reg[q >> 8 & 15], rget(dr[q & 7]));
 	else
-		rset(dr[q & 7], rget(reg[q >> 8 & 15]));
+		rset(dr[q & 7], rget(x86reg[q >> 8 & 15]));
 	skipinstr(ei);
 }
 
 static void
+movcr(ExitInfo *ei)
+{
+	u32int q;
+	
+	q = ei->qual;
+	switch(q & 15){
+	case 0:
+		switch(q >> 4 & 3){
+		case 0:
+			vmdebug("illegal CR0 write, value %#ux", rget(x86reg[q >> 8 & 15]));
+			rset("cr0real", rget(x86reg[q >> 8 & 15]));
+			skipinstr(ei);
+			break;
+		case 1:
+			vmerror("shouldn't happen: trap on MOV from CR0");
+			rset(x86reg[q >> 8 & 15], rget("cr0fake"));
+			skipinstr(ei);
+			break;
+		case 2:
+			vmerror("shouldn't happen: trap on CLTS");
+			rset("cr0real", rget("cr0real") & ~8);
+			skipinstr(ei);
+			break;
+		case 3:
+			vmerror("LMSW handler unimplemented");
+			postexc("#ud", NOERRC);
+		}
+		break;
+	case 4:
+		switch(ei->qual >> 4 & 3){
+		case 0:
+			vmdebug("illegal CR4 write, value %#ux", rget(x86reg[q >> 8 & 15]));
+			rset("cr4real", rget(x86reg[q >> 8 & 15]));
+			skipinstr(ei);
+			break;
+		case 1:
+			vmerror("shouldn't happen: trap on MOV from CR4");
+			rset(x86reg[q >> 8 & 15], rget("cr3fake"));
+			skipinstr(ei);
+			break;
+		default:
+			vmerror("unknown CR4 operation %d", q);
+			postexc("#ud", NOERRC);
+		}
+		break;
+	default:
+		vmerror("access to unknown control register CR%d", ei->qual & 15);
+		postexc("#ud", NOERRC);
+	}
+}
+
+static void
 dbgexc(ExitInfo *ei)
 {
 	rset("dr6", rget("dr6") | ei->qual);
@@ -380,6 +434,7 @@
 	{".wrmsr", rdwrmsr},
 	{".movdr", movdr},
 	{"#db", dbgexc},
+	{"movcr", movcr},
 };
 
 void
--- a/sys/src/cmd/vmx/io.c
+++ b/sys/src/cmd/vmx/io.c
@@ -363,6 +363,7 @@
 			p->init = 4;
 			picupdate(p);
 			return 0;
+		case 0:
 		case 4:
 			p->imr = val;
 			picupdate(p); 
@@ -412,10 +413,12 @@
 	enum { READLO, READHI, READLATLO, READLATHI } readstate;
 	u8int writestate;
 	vlong lastnsec;
+	u8int output;
 };
 PITChannel pit[3] = {
 	[0] { .state 1 },
 };
+u8int port61;
 enum { PERIOD = 838 };
 
 void
@@ -437,6 +440,18 @@
 		threadint(timerid);
 }
 
+static void
+pitout(int n, int v)
+{
+	if(n == 0)
+		irqline(0, v);
+	switch(v){
+	case IRQLLOHI: case 1: pit[n].output = 1; break;
+	case 0: pit[n].output = 0; break;
+	case IRQLTOGGLE: pit[n].output ^= 1; break;
+	}
+}
+
 void
 pitadvance(void)
 {
@@ -455,11 +470,11 @@
 		case 0:
 			if(p->state != 0){
 				nc = t / PERIOD;
-				if(p->count <= nc && i == 0)
-					irqline(0, 1);
+				if(p->count <= nc)
+					pitout(i, 1);
 				p->count -= nc;
 				p->lastnsec -= t % PERIOD;
-				if(i == 0 && (pic[0].lines & 1<<0) == 0)
+				if(!p->output)
 					settimer(p->lastnsec + p->count * PERIOD);
 			}
 			break;
@@ -474,8 +489,7 @@
 					nc -= p->count - 1;
 					nc %= rel;
 					p->count = rel - nc + 1;
-					if(i == 0)
-						irqline(0, IRQLLOHI);
+					pitout(i, IRQLLOHI);
 				}
 				p->lastnsec -= t % PERIOD;
 				settimer(p->lastnsec + p->count * PERIOD);
@@ -492,8 +506,7 @@
 					nc -= p->count;
 					nc %= rel;
 					p->count = rel - nc;
-					if(i == 0)
-						irqline(0, IRQLTOGGLE);
+					pitout(i, IRQLTOGGLE);
 				}
 				p->lastnsec -= t % PERIOD;
 				settimer(p->lastnsec + p->count / 2 * PERIOD);
@@ -515,8 +528,7 @@
 		p->reload = p->reload & 0xff00 | v;
 	switch(p->mode){
 	case 0:
-		if(n == 0)
-			irqline(0, 0);
+		pitout(n, 0);
 		if(p->access != 3 || hi){
 			p->count = p->reload;
 			p->state = 1;
@@ -569,6 +581,7 @@
 			return pit[n].latch >> 8;
 		}
 		return 0;
+	case 0x10061: return port61 | pit[2].output << 5;
 	case 0x40:
 	case 0x41:
 	case 0x42:
@@ -608,16 +621,18 @@
 			pit[n].readstate = pit[n].access == 1 ? READHI : READLO;
 			pit[n].writestate = pit[n].access == 1 ? READHI : READLO;
 			pit[n].lastnsec = nsec();
-			if(n == 0)
-				switch(pit[n].mode){
-				case 0:
-					irqline(0, 0);
-					break;
-				default:
-					irqline(0, 1);
-				}
+			switch(pit[n].mode){
+			case 0:
+				pitout(n, 0);
+				break;
+			default:
+				pitout(n, 1);
+			}
 		}
 		return 0;
+	case 0x61:
+		port61 = port61 & 0xf0 | val & 0x0f;
+		return 0;
 	}
 	return iowhine(isin, port, val, sz, "pit");
 }
@@ -628,7 +643,7 @@
 	int cmd;
 	u16int buf; /* |0x100 == kbd, |0x200 == mouse, |0x400 == cmd */
 } i8042 = {
-	.cfg 0x34,
+	.cfg 0x74,
 	.stat 0x10,
 	.oport 0x01,
 	.cmd -1,
@@ -809,7 +824,13 @@
 			break;
 		case 0xe7: mouseputc(0xfa); mouse.scaling21 = 1; break; /* set 2:1 scaling */
 		case 0xe6: mouseputc(0xfa); mouse.scaling21 = 0; break; /* set 1:1 scaling */
-		default: vmerror("unknown mouse command %#ux", val); mouseputc(0xfc);
+		
+		case 0x88: case 0x00: case 0x0a: /* sentelic & cypress */
+		case 0xe1: /* trackpoint */
+			mouseputc(0xfe);
+			break;
+
+		default: vmerror("unknown mouse command %#ux", val); mouseputc(0xfe);
 		}
 	}
 	i8042kick(nil);
@@ -894,6 +915,12 @@
 		case 0x60: case 0xd1: case 0xd2: case 0xd3: case 0xd4:
 			i8042.cmd = val;
 			return 0;
+		case 0xf0: case 0xf2: case 0xf4: case 0xf6: /* pulse reset line */
+		case 0xf8: case 0xfa: case 0xfc: case 0xfe:
+			sysfatal("i8042: system reset");
+		case 0xf1: case 0xf3: case 0xf5: case 0xf7: /* no-op */
+		case 0xf9: case 0xfb: case 0xfd: case 0xff:
+			return 0;
 		}
 		vmerror("unknown i8042 command %#ux", val);
 		return 0;
@@ -1204,6 +1231,7 @@
 	0x70, 0x71, rtcio, nil,
 	0xa0, 0xa1, picio, nil,
 	0x60, 0x60, i8042io, nil,
+	0x61, 0x61, pitio, nil, /* pc speaker */
 	0x64, 0x64, i8042io, nil,
 	0x2f8, 0x2ff, uartio, nil,
 	0x3b0, 0x3bb, vgaio, nil,
@@ -1219,7 +1247,7 @@
 	0x3f6, 0x3f6, ideio, nil, /* ide primary (aux) */
 	0x3f0, 0x3f7, fdcio, nil, /* floppy */
 
-	0x061, 0x061, nopio, nil, /* pc speaker */
+	0x080, 0x080, nopio, nil, /* dma -- used by linux for delay by dummy write */
 	0x084, 0x084, nopio, nil, /* dma -- used by openbsd for delay by dummy read */
 	0x100, 0x110, nopio, nil, /* elnk3 */
 	0x240, 0x25f, nopio, nil, /* ne2000 */
--- a/sys/src/cmd/vmx/ksetup.c
+++ b/sys/src/cmd/vmx/ksetup.c
@@ -1,6 +1,7 @@
 #include <u.h>
 #include <libc.h>
 #include <thread.h>
+#include <draw.h>
 #include <libsec.h>
 #include "dat.h"
 #include "fns.h"
@@ -687,6 +688,208 @@
 	return 0;
 }
 
+static void
+linuxbootmod(char *fn, void *zp, u32int kend)
+{
+	u32int addr;
+	uintptr memend;
+	int fd;
+	vlong sz;
+	void *v;
+	int rc;
+	
+	fd = open(fn, OREAD);
+	if(fd < 0) sysfatal("linux: initrd: open: %r");
+	sz = seek(fd, 0, 2);
+	if(sz < 0) sysfatal("linux: initrd: seek: %r");
+	if(sz == 0) sysfatal("linux: empty initrd");
+	addr = GET32(zp, 0x22c);
+	memend = (1<<20) + gavail(gptr(1<<20, 0));
+	if(addr >= memend) addr = memend - 1;
+	if((addr - (sz - 1) & -4) < kend) sysfatal("linux: no room for initrd");
+	addr = addr - (sz - 1) & -4;
+	print("%#ux %#ux\n", addr, (u32int)sz);
+	v = gptr(addr, sz);
+	if(v == nil) sysfatal("linux: initrd: gptr failed");
+	seek(fd, 0, 0);
+	rc = readn(fd, v, sz);
+	if(rc < 0) sysfatal("linux: initrd: read: %r");
+	if(rc < sz) sysfatal("linux: initrd: short read");
+	close(fd);
+	PUT32(zp, 0x218, addr);
+	PUT32(zp, 0x21C, sz);
+}
+
+static void
+linuxscreeninfo(void *zp)
+{
+	extern VgaMode *curmode, textmode;
+	extern uintptr fbaddr, fbsz;
+	uintptr extmem;
+	int i, p, s;
+	
+	extmem = gavail(gptr(1<<20, 0)) >> 10;
+	if(extmem >= 65535) extmem = 65535;
+	PUT16(zp, 0x02, extmem);
+	
+	if(curmode == nil) return;
+	if(curmode == &textmode){
+		PUT8(zp, 0x06, 3); /* mode 3 */
+		PUT8(zp, 0x07, 80); /* 80 cols */
+		PUT8(zp, 0x0e, 25); /* 25 rows */
+		PUT8(zp, 0x0f, 0x22); /* VGA */
+		PUT16(zp, 0x10, 16); /* characters are 16 pixels high */
+	}else{
+		PUT8(zp, 0x0f, 0x23); /* VESA linear framebuffer */
+		PUT16(zp, 0x12, curmode->w);
+		PUT16(zp, 0x14, curmode->h);
+		PUT16(zp, 0x16, chantodepth(curmode->chan));
+		PUT32(zp, 0x18, fbaddr);
+		PUT32(zp, 0x1C, fbsz);
+		PUT16(zp, 0x24, curmode->hbytes);
+		for(i = 0, p = 0; i < 4; i++){
+			s = curmode->chan >> 8 * i & 15;
+			if(s == 0) continue;
+			switch(curmode->chan >> 8 * i + 4 & 15){
+			case CRed: PUT16(zp, 0x26, s | p << 8); break;
+			case CGreen: PUT16(zp, 0x28, s | p << 8); break;
+			case CBlue: PUT16(zp, 0x2a, s | p << 8); break;
+			case CAlpha: case CIgnore:  PUT16(zp, 0x2c, s | p << 8); ; break;
+			}
+			p += s;
+		}
+		PUT16(zp, 0x34, 1<<0|1<<1|1<<3|1<<4|1<<5|1<<6|1<<7); /* attributes */
+	}
+}
+
+enum {
+	GDTRW = 2<<8,
+	GDTRX = 10<<8,
+	GDTS = 1<<12,
+	GDTP = 1<<15,
+	GDT64 = 1<<21,
+	GDT32 = 1<<22,
+	GDTG = 1<<23,
+};
+#define GDTLIM0(l) ((l) & 0x0ffff)
+#define GDTLIM1(l) ((l) & 0xf0000)
+#define GDTBASE0(b) ((b) << 16)
+#define GDTBASE1(b) ((b) >> 16 & 0xff | (b) & 0xff000000)
+
+static void
+linuxgdt(void *v)
+{
+	u32int base;
+	
+	base = gpa(v);
+	rset("gdtrbase", base);
+	v = pack(v, "ii", 0, 0);
+	v = pack(v, "ii", 0, 0);
+	v = pack(v, "ii", GDTLIM0(-1) | GDTBASE0(0), GDTLIM1(-1) | GDTBASE1(0) | GDTRX | GDTG | GDTS | GDTP | GDT32);
+	v = pack(v, "ii", GDTLIM0(-1) | GDTBASE0(0), GDTLIM1(-1) | GDTBASE1(0) | GDTRW | GDTG | GDTS | GDTP | GDT32);
+	rset("gdtrlimit", gpa(v) - base - 1);
+	rset("cs", 0x10);
+	rset("ds", 0x18);
+	rset("es", 0x18);
+	rset("ss", 0x18);
+}
+
+static void
+linuxe820(uchar *zp)
+{
+	Region *r;
+	uchar *v;
+	uvlong s, e;
+	int n;
+	
+	v = zp + 0x2d0;
+	v = pack(v, "vvi", (uvlong)0, (uvlong)0xa0000, BIOS_MAP_FREE);
+	n = 1;
+	for(r = mmap; r != nil; r = r->next){
+		s = r->start;
+		e = r->end;
+		if(s < (1<<20)) s = 1<<20;
+		if(e <= s || r->type == REGFB) continue;
+		v = pack(v, "vvi", s, e - s, isusermem(r) ? BIOS_MAP_FREE : BIOS_MAP_RES);
+		n++;
+	}
+	PUT8(zp, 0x1e8, n);
+}
+
+static int
+trylinux(void)
+{
+	char buf[1024];
+	u8int loadflags;
+	u16int version;
+	uchar *zp;
+	void *v;
+	u32int ncmdline, cmdlinemax, syssize, setupsects;
+	
+	seek(fd, 0, 0);
+	if(readn(fd, buf, sizeof(buf)) < 1024) return 0;
+	if(GET16(buf, 0x1FE) != 0xAA55 || GET32(buf, 0x202) != 0x53726448) return 0;
+	version = GET16(buf, 0x206);
+	if(version < 0x206){
+		vmerror("linux: kernel too old (boot protocol version %d.%.2d, needs to be 2.06 or newer)", version >> 8, version & 0xff);
+		return 0;
+	}
+	loadflags = GET8(buf, 0x211);
+	if((loadflags & 1) == 0){
+		vmerror("linux: zImage is not supported");
+		return 0;
+	}
+	zp = gptr(0x1000, 0x1000);
+	if(zp == nil) sysfatal("linux: gptr for zeropage failed");
+	rset(RSI, 0x1000);
+	memset(zp, 0, 0x1000);
+	memmove(zp + 0x1f1, buf + 0x1f1, 0x202 + GET8(buf, 0x201) - 0x1f1);
+	setupsects = GET8(zp, 0x1F1);
+	if(setupsects == 0) setupsects = 4;
+	syssize = GET32(zp, 0x1F4);
+	cmdlinemax = GET32(zp, 0x238);
+	
+	v = gptr(1<<20, syssize << 4);
+	if(v == nil) sysfatal("linux: not enough room for kernel");
+	epreadn(v, syssize << 4, (setupsects + 1) * 512, "trylinux");
+	
+	v = gptr(0x20000, 1);
+	if(v == nil) sysfatal("linux: gptr for cmdline failed");
+	ncmdline = putcmdline(v);
+	if(ncmdline == 0)
+		*(uchar*)v = 0;
+	else
+		if(ncmdline - 1 > cmdlinemax) sysfatal("linux: cmdline too long (%d > %d)", ncmdline, cmdlinemax);
+	PUT32(zp, 0x228, 0x20000);
+	
+	switch(bootmodn){
+	case 0: break;
+	default:
+		vmerror("linux: ignoring extra boot modules (only one supported)");
+		/* wet floor */
+	case 1:
+		linuxbootmod(*bootmod, zp, (1<<20) + (syssize << 4));
+	}
+	
+	linuxscreeninfo(zp);
+	v = gptr(0x3000, 256);
+	if(v == nil) sysfatal("linux: gptr for gdt failed");
+	linuxgdt(v);
+	
+	linuxe820(zp);
+	
+	PUT16(zp, 0x1FA, 0xffff);
+	PUT8(zp, 0x210, 0xFF); /* bootloader ID */
+	PUT8(zp, 0x211, loadflags | 0x80); /* kernel can use heap */
+
+	PUT32(zp, 0x224, 0xfe00); /* kernel can use full segment */
+	rset(RPC, GET32(zp, 0x214));
+	rset(RBP, 0);
+	rset(RDI, 0);
+	rset(RBX, 0);
+	return 1;
+}
+
 void
 loadkernel(char *fn)
 {
@@ -694,7 +897,7 @@
 	if(fd < 0) sysfatal("open: %r");
 	if(readn(fd, hdr, sizeof(hdr)) <= 0)
 		sysfatal("readn: %r");
-	if(!trymultiboot() && !tryelf())
+	if(!trymultiboot() && !tryelf() && !trylinux())
 		sysfatal("%s: unknown format", fn);
 	close(fd);
 }
--- a/sys/src/cmd/vmx/vesa.c
+++ b/sys/src/cmd/vmx/vesa.c
@@ -258,6 +258,7 @@
 	pos = 0;
 	for(i = 0; i < 4; i++){
 		s = p->chan >> 8 * i & 15;
+		if(s == 0) continue;
 		switch(p->chan >> 8 * i + 4 & 15){
 		case CRed: nred = s; pred = pos; break;
 		case CGreen: ngreen = s; pgreen = pos; break;
--- a/sys/src/cmd/vmx/vga.c
+++ b/sys/src/cmd/vmx/vga.c
@@ -152,6 +152,10 @@
 {
 	u32int m;
 
+	if(port == 0x3d4 && sz == 2 && !isin){
+		vgaio(0, 0x3d4, (u8int)val, 1, nil);
+		return vgaio(0, 0x3d5, (u8int)(val >> 8), 1, nil);
+	}
 	if(sz != 1) vmdebug("vga: non-byte access to port %#ux, sz=%d", port, sz);
 	val = (u8int) val;
 	switch(isin << 16 | port){
--- a/sys/src/cmd/vmx/virtio.c
+++ b/sys/src/cmd/vmx/virtio.c
@@ -53,6 +53,8 @@
 		VNETNOMULTI = 8,
 		VNETNOUNI = 16,
 		VNETNOBCAST = 32,
+		
+		VNETHEADER = 1<<31,
 	} flags;
 	u64int macbloom, multibloom;
 };
@@ -447,6 +449,7 @@
 	threadsetname("vionetrproc");
 	v = vp;
 	q = &v->qu[0];
+	memset(rxhead, 0, sizeof(rxhead));
 	for(;;){
 		rc = read(v->net.readfd, rxbuf, sizeof(rxbuf));
 		if(rc == 0){
@@ -481,8 +484,9 @@
 	VIOQueue *q;
 	VIOBuf *vb;
 	uchar txhead[10];
-	uchar txbuf[1600];
+	uchar txbuf[1610];
 	int rc, len;
+	uvlong ns;
 	
 	threadsetname("vionetwproc");
 	v = vp;
@@ -494,8 +498,8 @@
 			threadexits("viogetbuf: %r");
 		}
 		vioqread(vb, txhead, sizeof(txhead));
-		len = vioqread(vb, txbuf, sizeof(txbuf));
-		if(len == sizeof(txbuf)){
+		len = vioqread(vb, txbuf+10, sizeof(txbuf)-10);
+		if(len == sizeof(txbuf)-10){
 			vmerror("virtio net: ignoring excessively long packet");
 			vioputbuf(vb);
 			continue;
@@ -507,10 +511,24 @@
 			vioputbuf(vb);
 			continue;
 		}else if(len < 60){ /* openbsd doesn't seem to know about ethernet minimum packet lengths either */
-			memset(txbuf + len, 0, 60 - len);
+			memset(txbuf + 10 + len, 0, 60 - len);
 			len = 60;
-		}	
-		rc = write(v->net.writefd, txbuf, len);
+		}
+		if((v->net.flags & VNETHEADER) != 0){
+			txbuf[0] = len  >> 8;
+			txbuf[1] = len;
+			ns = nsec();
+			txbuf[2] = ns >> 56;
+			txbuf[3] = ns >> 48;
+			txbuf[4] = ns >> 40;
+			txbuf[5] = ns >> 32;
+			txbuf[6] = ns >> 24;
+			txbuf[7] = ns >> 16;
+			txbuf[8] = ns >> 8;
+			txbuf[9] = ns;
+			rc = write(v->net.writefd, txbuf, len + 10);
+		}else
+			rc = write(v->net.writefd, txbuf + 10, len);
 		vioputbuf(vb);
 		if(rc < 0){
 			vmerror("write(vionetwproc): %r");
@@ -607,7 +625,7 @@
 void
 vionetreset(VIODev *d)
 {
-	d->net.flags = 0;
+	d->net.flags &= VNETHEADER;
 	d->net.macbloom = 0;
 	d->net.multibloom = 0;
 }
@@ -618,10 +636,30 @@
 	int fd, cfd;
 	VIODev *d;
 	int i;
+	int flags;
+	enum { VNETFILE = 1 };
 
-	fd = dial(netmkaddr("-1", net, nil), nil, nil, &cfd);
-	if(fd < 0) return -1;
-	if(cfd >= 0) fprint(cfd, "promiscuous");
+	flags = 0;
+	for(;;){
+		if(strncmp(net, "hdr!", 4) == 0){
+			net += 4;
+			flags |= VNETHEADER;
+		}else if(strncmp(net, "file!", 5) == 0){
+			net += 5;
+			flags |= VNETFILE;
+		}else
+			break;
+	}
+	if((flags & VNETFILE) != 0){
+		flags &= ~VNETFILE;
+		fd = open(net, ORDWR);
+		if(fd < 0) return -1;
+	}else{
+		fd = dial(netmkaddr("-1", net, nil), nil, nil, &cfd);
+		if(fd < 0) return -1;
+		if(cfd >= 0) fprint(cfd, "promiscuous");
+	}
+	
 	d = mkviodev(0x1000, 0x020000, 1);
 	mkvioqueue(d, 1024, viowakeup);
 	mkvioqueue(d, 1024, viowakeup);
@@ -629,6 +667,7 @@
 	for(i = 0; i < 6; i++)
 		d->net.mac[i] = rand();
 	d->net.mac[0] = d->net.mac[0] & ~1 | 2;
+	d->net.flags = flags;
 	d->devfeat = 1<<5|1<<16|1<<17|1<<18|1<<20;
 	d->io = vionetio;
 	d->reset = vionetreset;