diff options
author | aiju <devnull@localhost> | 2017-06-18 22:17:35 +0000 |
---|---|---|
committer | aiju <devnull@localhost> | 2017-06-18 22:17:35 +0000 |
commit | 2806a34ec0152cbf8ac0e2198bda111a9b1273bd (patch) | |
tree | 788b51551953265bd54211d44b674f7ed7b05d45 /sys/src/cmd/vmx | |
parent | ed040d676ad95858211ec8b5f43e8713692fdeb2 (diff) |
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
Diffstat (limited to 'sys/src/cmd/vmx')
-rw-r--r-- | sys/src/cmd/vmx/exith.c | 73 | ||||
-rw-r--r-- | sys/src/cmd/vmx/io.c | 68 | ||||
-rw-r--r-- | sys/src/cmd/vmx/ksetup.c | 205 | ||||
-rw-r--r-- | sys/src/cmd/vmx/vesa.c | 1 | ||||
-rw-r--r-- | sys/src/cmd/vmx/vga.c | 4 | ||||
-rw-r--r-- | sys/src/cmd/vmx/virtio.c | 59 |
6 files changed, 370 insertions, 40 deletions
diff --git a/sys/src/cmd/vmx/exith.c b/sys/src/cmd/vmx/exith.c index f68566607..0c27bc9bd 100644 --- a/sys/src/cmd/vmx/exith.c +++ b/sys/src/cmd/vmx/exith.c @@ -14,6 +14,13 @@ struct ExitInfo { 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 @@ cpuid(ExitInfo *ei) 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 @@ rdwrmsr(ExitInfo *ei) 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 @@ rdwrmsr(ExitInfo *ei) 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 @@ movdr(ExitInfo *ei) 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 @@ static ExitType etypes[] = { {".wrmsr", rdwrmsr}, {".movdr", movdr}, {"#db", dbgexc}, + {"movcr", movcr}, }; void diff --git a/sys/src/cmd/vmx/io.c b/sys/src/cmd/vmx/io.c index 507dda972..0ba1233af 100644 --- a/sys/src/cmd/vmx/io.c +++ b/sys/src/cmd/vmx/io.c @@ -363,6 +363,7 @@ picio(int isin, u16int port, u32int val, int sz, void *) p->init = 4; picupdate(p); return 0; + case 0: case 4: p->imr = val; picupdate(p); @@ -412,10 +413,12 @@ struct PITChannel { 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 @@ settimer(vlong targ) 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 @@ pitadvance(void) 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 @@ pitadvance(void) 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 @@ pitadvance(void) 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 @@ pitsetreload(int n, int hi, u8int v) 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 @@ pitio(int isin, u16int port, u32int val, int sz, void *) 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 @@ pitio(int isin, u16int port, u32int val, int sz, void *) 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 @@ struct I8042 { 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 @@ mousecmd(u8int val) 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 @@ i8042io(int isin, u16int port, u32int val, int sz, void *) 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 @@ IOHandler handlers[] = { 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 @@ IOHandler handlers[] = { 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 */ diff --git a/sys/src/cmd/vmx/ksetup.c b/sys/src/cmd/vmx/ksetup.c index 9b8b54baf..3dac6de8b 100644 --- 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 @@ tryelf(void) 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 @@ loadkernel(char *fn) 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); } diff --git a/sys/src/cmd/vmx/vesa.c b/sys/src/cmd/vmx/vesa.c index 96d8ef83f..35c7a0d38 100644 --- a/sys/src/cmd/vmx/vesa.c +++ b/sys/src/cmd/vmx/vesa.c @@ -258,6 +258,7 @@ vesamodeget(int addr, u16int mode, u16int *retv) 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; diff --git a/sys/src/cmd/vmx/vga.c b/sys/src/cmd/vmx/vga.c index 4880a1566..edd141c46 100644 --- a/sys/src/cmd/vmx/vga.c +++ b/sys/src/cmd/vmx/vga.c @@ -152,6 +152,10 @@ vgaio(int isin, u16int port, u32int val, int sz, void *) { 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){ diff --git a/sys/src/cmd/vmx/virtio.c b/sys/src/cmd/vmx/virtio.c index 345c94b3c..d4d573fc7 100644 --- a/sys/src/cmd/vmx/virtio.c +++ b/sys/src/cmd/vmx/virtio.c @@ -53,6 +53,8 @@ struct VIONetDev { VNETNOMULTI = 8, VNETNOUNI = 16, VNETNOBCAST = 32, + + VNETHEADER = 1<<31, } flags; u64int macbloom, multibloom; }; @@ -447,6 +449,7 @@ vionetrproc(void *vp) 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 @@ vionetwproc(void *vp) 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 @@ vionetwproc(void *vp) 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 @@ vionetwproc(void *vp) 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 @@ vionetcmd(VIOQueue *q) void vionetreset(VIODev *d) { - d->net.flags = 0; + d->net.flags &= VNETHEADER; d->net.macbloom = 0; d->net.multibloom = 0; } @@ -618,10 +636,30 @@ mkvionet(char *net) 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 @@ mkvionet(char *net) 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; |