7 #include <ip.h> /* parseether() */
8 #include <libsec.h> /* genrandom() */
10 typedef struct VIODev VIODev;
11 typedef struct VIOQueue VIOQueue;
12 typedef struct VIOBuf VIOBuf;
13 typedef struct VIONetDev VIONetDev;
14 typedef struct VIOBlkDev VIOBlkDev;
22 DRIVEROK = 4, /* devstat */
40 u8int (*desc)[16], *avail, *used;
43 u16int availidx, usedidx;
44 void (*notify)(VIOQueue*);
62 u64int macbloom, multibloom;
72 u32int devfeat, guestfeat;
74 u8int devstat, isrstat;
77 u32int (*io)(int, u16int, u32int, int, VIODev *);
78 void (*reset)(VIODev *);
92 val = (uintptr)((void**)arg)[1];
97 pciirq(d->pci, d->isrstat);
102 vioirq(VIODev *d, int val)
107 v = emalloc(sizeof(void*)*2);
110 sendnotif(vioirq_, v);
114 checkdesc(VIOQueue *q, int i)
117 vmerror("virtio device %#x: invalid next pointer %d in queue (size %d), ignoring descriptor", q->d->pci->bdf, i, q->size);
124 viogetbuf(VIOQueue *q, int wait)
127 VIOBuf *b, *rb, **bp;
132 while((q->d->devstat & DRIVEROK) == 0 || q->desc == nil || (gidx = GET16(q->avail, 2), gidx == q->availidx)){
139 dp = checkdesc(q, GET16(q->avail, 4 + 2 * (q->availidx % q->size)));
143 b = emalloc(sizeof(VIOBuf));
145 b->idx = (u8int(*)[16])dp - q->desc;
146 b->addr = GET64(dp, 0);
147 b->len = GET32(dp, 8);
148 b->flags = GET16(dp, 12);
149 b->p = gptr(b->addr, b->len);
151 vmerror("virtio device %#x: invalid buffer pointer %#p in queue, ignoring descriptor", q->d->pci->bdf, (void*)b->addr);
158 if((b->flags & BUFCHAIN) == 0) break;
159 dp = checkdesc(q, GET16(dp, 14));
163 if(rb == nil) goto waitloop;
179 if((q->d->devstat & DRIVEROK) == 0){
184 vmerror("virtio device %#x: address was set to an invalid value while holding buffer", q->d->pci->bdf);
186 p = q->used + 4 + 8 * (q->usedidx % q->size);
187 PUT32(p, 4, b->wptr);
189 PUT16(q->used, 2, ++q->usedidx);
191 if(--q->livebuf <= 0)
192 rwakeup(&q->livebufrend);
194 if(q->avail != nil && (GET16(q->avail, 0) & USEDNOIRQ) == 0)
205 vioqread(VIOBuf *b, void *v, ulong n)
216 if(rc >= n) return rc;
218 if(c == nil) return rc;
219 if((c->flags & BUFWR) == 0){
220 if(p < c->len) break;
226 if(m > n - rc) m = n - rc;
227 memmove(v, (u8int*)c->p + p, m);
235 vioqwrite(VIOBuf *b, void *v, ulong n)
246 if(rc >= n) return rc;
248 if(c == nil) return rc;
249 if((c->flags & BUFWR) != 0){
250 if(p < c->len) break;
256 if(m > n - rc) m = n - rc;
257 memmove((u8int*)c->p + p, v, m);
265 vioqrem(VIOBuf *b, int wr)
271 p = wr ? b->wptr : b->rptr;
272 for(c = b;; c = c->next){
273 if(c == nil) return 0;
274 if(((c->flags & BUFWR) != 0) == wr){
275 if(p < c->len) break;
280 for(c = c->next; c != nil; c = c->next)
281 if(((c->flags & BUFWR) != 0) == wr)
287 vioqaddrset(VIOQueue *q, u64int addr)
293 sz1 = -(-(18 * q->size + 4) & -4096);
294 sz = sz1 + (-(-(8 * q->size + 6) & -4096));
297 vmerror("virtio device %#x: attempt to set queue to invalid address %#p", q->d->pci->bdf, (void *) addr);
306 q->avail = (u8int*)p + 16 * q->size;
307 q->used = (u8int*)p + sz1;
314 vioqreset(VIOQueue *q)
325 viodevstatset(VIODev *v, u32int val)
335 for(i = 0; i < v->nqu; i++){
337 while(v->qu[i].livebuf > 0)
338 rsleep(&v->qu[i].livebufrend);
339 vioqreset(&v->qu[i]);
343 for(i = 0; i < v->nqu; i++)
344 v->qu[i].notify(&v->qu[i]);
349 vioio(int isin, u16int port, u32int val, int sz, void *vp)
353 static char whinebuf[32];
356 switch(isin << 16 | port){
357 case 0x4: v->guestfeat = val; return 0;
358 case 0x8: if(v->qsel < v->nqu) vioqaddrset(&v->qu[v->qsel], val); return 0;
359 case 0xe: v->qsel = val; return 0;
360 case 0x10: if(val < v->nqu) v->qu[val].notify(&v->qu[val]); return 0;
361 case 0x12: viodevstatset(v, val); return 0;
362 case 0x10000: return v->devfeat;
363 case 0x10004: return v->guestfeat;
364 case 0x10008: return v->qsel >= v->nqu ? 0 : v->qu[v->qsel].addr;
365 case 0x1000c: return v->qsel >= v->nqu ? 0 : v->qu[v->qsel].size;
366 case 0x1000e: return v->qsel;
367 case 0x10010: return 0;
368 case 0x10012: return v->devstat;
369 case 0x10013: rc = v->isrstat; vioirq(v, 0); return rc;
371 if(port >= 20 && v->io != nil)
372 return v->io(isin, port - 20, val, sz, v);
373 snprint(whinebuf, sizeof(whinebuf), "virtio device %6x", v->pci->bdf);
374 return iowhine(isin, port, val, sz, whinebuf);
378 mkviodev(u16int devid, u32int pciclass, u32int subid, int queues)
382 d = emalloc(sizeof(VIODev));
383 d->pci = mkpcidev(allocbdf(), devid << 16 | 0x1AF4, pciclass << 8, 1);
384 d->pci->subid = subid << 16;
385 mkpcibar(d->pci, BARIO, 0, 256, vioio, d);
386 d->qu = emalloc(queues * sizeof(VIOQueue));
392 viowakeup(VIOQueue *q)
400 mkvioqueue(VIODev *d, int sz, void (*fn)(VIOQueue*))
404 assert(sz > 0 && sz <= 32768 && (sz & sz - 1) == 0 && fn != nil && d->nqu < d->allocqu);
405 q = d->qu + d->nqu++;
407 q->livebufrend.l = q;
416 bloomhash(u8int *mac)
421 x ^= mac[0] >> 6 ^ mac[1] << 2;
422 x ^= mac[1] >> 4 ^ mac[2] << 4;
425 x ^= mac[3] >> 6 ^ mac[4] << 2;
426 x ^= mac[4] >> 4 ^ mac[5] << 4;
432 viomacok(VIODev *d, u8int *mac)
434 static u8int bcast[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
436 if((d->net.flags & VNETPROMISC) != 0) return 1;
437 if((mac[0] & 1) == 0){
438 if((d->net.flags & (VNETNOUNI|VNETALLUNI)) != 0)
439 return (d->net.flags & VNETNOUNI) == 0;
440 if(memcmp(mac, d->net.mac, 6) == 0) return 1;
441 if(d->net.macbloom == 0) return 0;
442 return d->net.macbloom >> bloomhash(mac) & 1;
443 }else if(memcmp(mac, bcast, 6) == 0)
444 return (d->net.flags & VNETNOBCAST) == 0;
446 if((d->net.flags & (VNETNOMULTI|VNETALLMULTI)) != 0)
447 return (d->net.flags & VNETNOMULTI) == 0;
448 if(d->net.multibloom == 0) return 0;
449 return d->net.multibloom >> bloomhash(mac) & 1;
454 vionetrproc(void *vp)
463 threadsetname("vionetrproc");
466 memset(rxhead, 0, sizeof(rxhead));
468 rc = read(v->net.readfd, rxbuf, sizeof(rxbuf));
470 vmerror("read(vionetrproc): eof");
471 threadexits("read: eof");
474 vmerror("read(vionetrproc): %r");
475 threadexits("read: %r");
478 vmerror("vionetrproc: short packet received (len=%d)", rc);
481 if(!viomacok(v, rxbuf))
483 vb = viogetbuf(q, 1);
485 vmerror("viogetbuf: %r");
488 vioqwrite(vb, rxhead, sizeof(rxhead));
489 vioqwrite(vb, rxbuf, rc);
495 vionetwproc(void *vp)
505 threadsetname("vionetwproc");
509 vb = viogetbuf(q, 1);
511 vmerror("viogetbuf: %r");
512 threadexits("viogetbuf: %r");
514 vioqread(vb, txhead, sizeof(txhead));
515 len = vioqread(vb, txbuf+10, sizeof(txbuf)-10);
516 if(len == sizeof(txbuf)-10){
517 vmerror("virtio net: ignoring excessively long packet");
522 /* openbsd ends up sending lots of zero length packets sometimes */
524 vmerror("virtio net: ignoring short packet (length=%d)", len);
527 }else if(len < 60){ /* openbsd doesn't seem to know about ethernet minimum packet lengths either */
528 memset(txbuf + 10 + len, 0, 60 - len);
531 if((v->net.flags & VNETHEADER) != 0){
543 rc = write(v->net.writefd, txbuf, len + 10);
545 rc = write(v->net.writefd, txbuf + 10, len);
548 vmerror("write(vionetwproc): %r");
552 vmerror("write(vionetwproc): incomplete write (%d < %d)", rc, len);
559 vionetio(int isin, u16int port, u32int val, int sz, VIODev *v)
561 switch(isin << 16 | port){
562 case 0x10000: case 0x10001: case 0x10002: case 0x10003:
563 return GET32(v->net.mac, 0) >> (port & 3) * 8;
564 case 0x10004: case 0x10005: case 0x10006: case 0x10007:
565 return (GET16(v->net.mac, 4) | 1 << 16) >> (port & 3) * 8;
567 return iowhine(isin, port, val, sz, "virtio net");
571 vionettables(VIODev *d, VIOBuf *b)
580 for(i = 0; i < 2; i++){
581 if(vioqread(b, buf, 4) < 4)
585 if(vioqread(b, mac, 6) < 6)
587 bloom[i] |= 1ULL<<bloomhash(mac);
590 d->net.macbloom = bloom[0];
591 d->net.multibloom = bloom[1];
596 vionetcmd(VIOQueue *q)
600 u8int cmd[2], buf[6];
605 for(; b = viogetbuf(q, 0), b != nil; vioputbuf(b)){
606 if(vioqread(b, cmd, 2) < 2){
608 vioqwrite(b, &ack, 1);
612 switch(cmd[0] << 8 | cmd[1]){
613 case 0x0000: fl = VNETPROMISC; goto flag;
614 case 0x0001: fl = VNETALLMULTI; goto flag;
615 case 0x0002: fl = VNETALLUNI; goto flag;
616 case 0x0003: fl = VNETNOMULTI; goto flag;
617 case 0x0004: fl = VNETNOUNI; goto flag;
618 case 0x0005: fl = VNETNOBCAST; goto flag;
620 if(vioqread(b, buf, 1) < 1) ack = 1;
621 else if(buf[0] == 1) d->net.flags |= fl;
622 else if(buf[0] == 0) d->net.flags &= ~fl;
625 case 0x0100: /* MAC_TABLE_SET */
626 ack = vionettables(d, b);
628 case 0x0101: /* MAC_ADDR_SET */
629 if(vioqread(b, buf, 6) < 6) ack = 1;
630 else memmove(d->net.mac, buf, 6);
635 vioqwrite(b, &ack, 1);
640 vionetreset(VIODev *d)
642 d->net.flags &= VNETHEADER;
644 d->net.multibloom = 0;
654 enum { VNETFILE = 1 };
659 if(strncmp(net, "hdr!", 4) == 0){
662 }else if(strncmp(net, "file!", 5) == 0){
665 }else if(strncmp(net, "ea:", 3) == 0){
666 net = strchr(ea = net+3, '!');
668 werrstr("missing: !");
674 if((flags & VNETFILE) != 0){
676 fd = open(net, ORDWR);
677 if(fd < 0) return -1;
679 fd = dial(netmkaddr("-1", net, nil), nil, nil, &cfd);
680 if(fd < 0) return -1;
682 write(cfd, "promiscuous", 11);
683 write(cfd, "bridge", 6);
687 d = mkviodev(0x1000, 0x020000, 1, 3);
688 mkvioqueue(d, 1024, viowakeup);
689 mkvioqueue(d, 1024, viowakeup);
690 mkvioqueue(d, 32, vionetcmd);
691 if(ea == nil || parseether(d->net.mac, ea)){
692 genrandom(d->net.mac, 6);
693 d->net.mac[0] = d->net.mac[0] & ~1 | 2;
695 d->net.flags = flags;
696 d->devfeat = 1<<5|1<<16|1<<17|1<<18|1<<20;
698 d->reset = vionetreset;
699 d->net.readfd = d->net.writefd = fd;
700 proccreate(vionetrproc, d, 8192);
701 proccreate(vionetwproc, d, 8192);
706 vioblkio(int isin, u16int port, u32int val, int sz, VIODev *v)
708 switch(isin << 16 | port){
709 case 0x10000: case 0x10001: case 0x10002: case 0x10003:
710 return (u32int)v->blk.size >> (port & 3) * 8;
711 case 0x10004: case 0x10005: case 0x10006: case 0x10007:
712 return (u32int)(v->blk.size >> 32) >> (port & 3) * 8;
714 return iowhine(isin, port, val, sz, "virtio blk");
729 threadsetname("vioblkproc");
735 vmerror("vioblkproc: viogetbuf: %r");
736 threadexits("vioblkproc: viogetbuf: %r");
739 if(vioqread(b, cmd, sizeof(cmd)) < sizeof(cmd)) goto nope;
740 addr = GET64(cmd, 8);
741 switch(GET32(cmd, 0)){
743 n = vioqrem(b, 1) - 1;
744 if(n < 0 || addr * 512 + n > v->blk.size * 512){
748 seek(v->blk.fd, addr << 9, 0);
749 for(; n > 0; n -= rc){
752 rc = read(v->blk.fd, buf, rc);
753 if(rc < 0) vmerror("read(vioblkproc): %r");
758 vioqwrite(b, buf, rc);
763 if(addr * 512 + n > v->blk.size * 512){
767 seek(v->blk.fd, addr << 9, 0);
768 for(; n > 0; n -= rc){
769 m = vioqread(b, buf, sizeof(buf));
770 rc = write(v->blk.fd, buf, m);
771 if(rc < 0) vmerror("write(vioblkproc): %r");
782 vioqwrite(b, &ack, 1);
793 fd = open(fn, ORDWR);
794 if(fd < 0) return -1;
795 d = mkviodev(0x1000, 0x018000, 2, 1);
796 mkvioqueue(d, 32, viowakeup);
799 d->blk.size = seek(fd, 0, 2) >> 9;
800 proccreate(vioblkproc, d, 16384);