2 #include "../port/lib.h"
7 #include "../port/error.h"
8 #include "../port/netif.h"
9 #include "../port/etherif.h"
12 * virtio ethernet driver
13 * http://docs.oasis-open.org/virtio/virtio/v1.0/virtio-v1.0.html
16 typedef struct Vring Vring;
17 typedef struct Vdesc Vdesc;
18 typedef struct Vused Vused;
19 typedef struct Vheader Vheader;
20 typedef struct Vqueue Vqueue;
21 typedef struct Ctlr Ctlr;
24 /* §2.1 Device Status Field */
31 /* §4.1.4.8 Legacy Interfaces: A Note on PCI Device Layout */
43 /* flags in Qnetstatus */
53 /* vring used flags */
55 /* vring avail flags */
58 /* descriptor flags */
69 /* §4.1.5.1.4.1 says pages are 4096 bytes
70 * for the purposes of the driver.
73 #define VPGROUND(s) ROUND(s, VBY2PG)
79 /* class/cmd for Vctlq */
84 CmdMacTableSet = 0x00,
120 /* §2.4 Virtqueues */
159 /* virtioether has 3 queues: rx, tx and ctl */
163 static Ctlr *ctlrhead;
169 return q->lastused != q->used->idx;
173 vqnotify(Ctlr *ctlr, int x)
179 if(q->used->flags & Unonotify)
182 outs(ctlr->port+Qnotify, x);
199 q = &ctlr->queue[Vtxq];
201 header = smalloc(VheaderSize);
202 blocks = smalloc(sizeof(Block*) * (q->qsize/2));
204 for(i = 0; i < q->qsize/2; i++){
206 q->desc[j].addr = PADDR(header);
207 q->desc[j].len = VheaderSize;
208 q->desc[j].next = j | 1;
209 q->desc[j].flags = Dnext;
211 q->availent[i] = q->availent[i + q->qsize/2] = j;
215 q->desc[j].flags = 0;
218 q->avail->flags &= ~Rnointerrupt;
223 while((b = qbread(edev->oq, 1000000)) != nil){
225 /* retire completed packets */
226 while((i = q->lastused) != q->used->idx){
227 u = &q->usedent[i & q->qmask];
228 i = (u->id & q->qmask) >> 1;
236 /* have free slot? */
237 i = q->avail->idx & (q->qmask >> 1);
241 /* ring full, wait and retry */
243 sleep(q, vhasroom, q);
246 /* slot is free, fill in descriptor */
249 q->desc[j].addr = PADDR(b->rp);
250 q->desc[j].len = BLEN(b);
253 vqnotify(ctlr, Vtxq);
256 pexit("ether out queue closed", 1);
273 q = &ctlr->queue[Vrxq];
275 header = smalloc(VheaderSize);
276 blocks = smalloc(sizeof(Block*) * (q->qsize/2));
278 for(i = 0; i < q->qsize/2; i++){
280 q->desc[j].addr = PADDR(header);
281 q->desc[j].len = VheaderSize;
282 q->desc[j].next = j | 1;
283 q->desc[j].flags = Dwrite|Dnext;
285 q->availent[i] = q->availent[i + q->qsize/2] = j;
289 q->desc[j].flags = Dwrite;
292 q->avail->flags &= ~Rnointerrupt;
298 /* replenish receive ring */
300 i = q->avail->idx & (q->qmask >> 1);
303 if((b = iallocb(ETHERMAXTU)) == nil)
307 q->desc[j].addr = PADDR(b->rp);
308 q->desc[j].len = BALLOC(b);
311 } while(q->avail->idx != q->used->idx);
312 vqnotify(ctlr, Vrxq);
314 /* wait for any packets to complete */
316 sleep(q, vhasroom, q);
318 /* retire completed packets */
319 while((i = q->lastused) != q->used->idx) {
320 u = &q->usedent[i & q->qmask];
321 i = (u->id & q->qmask) >> 1;
322 if((b = blocks[i]) == nil)
327 b->wp = b->rp + u->len - VheaderSize;
335 vctlcmd(Ether *edev, uchar class, uchar cmd, uchar *data, int ndata)
337 uchar hdr[2], ack[1];
344 q = &ctlr->queue[Vctlq];
348 qlock(&ctlr->ctllock);
357 d->addr = PADDR(hdr);
358 d->len = sizeof(hdr);
362 d->addr = PADDR(data);
367 d->addr = PADDR(ack);
368 d->len = sizeof(ack);
372 i = q->avail->idx & q->qmask;
376 q->avail->flags &= ~Rnointerrupt;
378 vqnotify(ctlr, Vctlq);
380 sleep(q, vhasroom, q);
381 q->lastused = q->used->idx;
382 q->avail->flags |= Rnointerrupt;
384 qunlock(&ctlr->ctllock);
388 print("#l%d: vctlcmd: %ux.%ux -> %ux\n", edev->ctlrno, class, cmd, ack[0]);
394 interrupt(Ureg*, void* arg)
403 if(inb(ctlr->port+Qisr) & 1){
404 for(i = 0; i < ctlr->nqueue; i++){
426 outb(ctlr->port+Qstatus, inb(ctlr->port+Qstatus) | Sdriverok);
429 snprint(name, sizeof name, "#l%drx", edev->ctlrno);
430 kproc(name, rxproc, edev);
431 snprint(name, sizeof name, "#l%dtx", edev->ctlrno);
432 kproc(name, txproc, edev);
438 ifstat(Ether *edev, void *a, long n, ulong offset)
447 p = smalloc(READSTR);
449 l = snprint(p, READSTR, "devfeat %32.32lub\n", ctlr->feat);
450 l += snprint(p+l, READSTR-l, "drvfeat %32.32lub\n", inl(ctlr->port+Qdrvfeat));
451 l += snprint(p+l, READSTR-l, "devstatus %8.8ub\n", inb(ctlr->port+Qstatus));
452 if(ctlr->feat & Fstatus)
453 l += snprint(p+l, READSTR-l, "netstatus %8.8ub\n", inb(ctlr->port+Qnetstatus));
455 for(i = 0; i < ctlr->nqueue; i++){
457 l += snprint(p+l, READSTR-l,
458 "vq%d %#p size %d avail->idx %d used->idx %d lastused %hud nintr %ud nnote %ud\n",
459 i, q, q->qsize, q->avail->idx, q->used->idx, q->lastused, q->nintr, q->nnote);
462 n = readstr(offset, a, n, p);
469 shutdown(Ether* edev)
471 Ctlr *ctlr = edev->ctlr;
472 outb(ctlr->port+Qstatus, 0);
473 pciclrbme(ctlr->pcidev);
477 promiscuous(void *arg, int on)
483 vctlcmd(edev, CtrlRx, CmdPromisc, b, sizeof(b));
487 multicast(void *arg, uchar*, int)
492 b[0] = edev->nmaddr > 0;
493 vctlcmd(edev, CtrlRx, CmdAllmulti, b, sizeof(b));
496 /* §2.4.2 Legacy Interfaces: A Note on Virtqueue Layout */
498 queuesize(ulong size)
500 return VPGROUND(VdescSize*size + sizeof(u16int)*(3+size))
501 + VPGROUND(sizeof(u16int)*3 + VusedSize*size);
505 initqueue(Vqueue *q, int size)
509 /* §2.4: Queue Size value is always a power of 2 and <= 32768 */
510 assert(!(size & (size - 1)) && size <= 32768);
512 p = mallocalign(queuesize(size), VBY2PG, 0, 0);
514 print("ethervirtio: no memory for Vqueue\n");
523 q->availent = (void*)p;
524 p += sizeof(u16int)*size;
525 q->availevent = (void*)p;
528 p = (uchar*)VPGROUND((uintptr)p);
531 q->usedent = (void*)p;
533 q->usedevent = (void*)p;
536 q->qmask = q->qsize - 1;
538 q->lastused = q->avail->idx = q->used->idx = 0;
540 q->avail->flags |= Rnointerrupt;
554 /* §4.1.2 PCI Device Discovery */
555 for(p = nil; p = pcimatch(p, 0, 0);){
558 /* the two possible DIDs for virtio-net */
559 if(p->did != 0x1000 && p->did != 0x1041)
561 /* non-transitional devices will have a revision > 0 */
564 /* non-transitional device will have typ+0x40 */
565 if(pcicfgr16(p, 0x2E) != typ)
567 if((c = mallocz(sizeof(Ctlr), 1)) == nil){
568 print("ethervirtio: no memory for Ctlr\n");
572 c->port = p->mem[0].bar & ~0x1;
573 if(ioalloc(c->port, p->mem[0].size, 0, "ethervirtio") < 0){
574 print("ethervirtio: port %ux in use\n", c->port);
582 c->id = (p->did<<16)|p->vid;
584 /* §3.1.2 Legacy Device Initialization */
585 outb(c->port+Qstatus, 0);
586 outb(c->port+Qstatus, Sacknowledge|Sdriver);
588 /* negotiate feature bits */
589 c->feat = inl(c->port+Qdevfeat);
590 outl(c->port+Qdrvfeat, c->feat & (Fmac|Fstatus|Fctrlvq|Fctrlrx));
592 /* §4.1.5.1.4 Virtqueue Configuration */
593 for(i=0; i<nelem(c->queue); i++){
594 outs(c->port+Qselect, i);
595 n = ins(c->port+Qsize);
596 if(n == 0 || (n & (n-1)) != 0){
598 print("ethervirtio: queue %d has invalid size %d\n", i, n);
601 if(initqueue(&c->queue[i], n) < 0)
604 outl(c->port+Qaddr, PADDR(c->queue[i].desc)/VBY2PG);
607 print("ethervirtio: no queues\n");
628 static uchar zeros[Eaddrlen];
633 ctlrhead = pciprobe(1);
635 for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){
638 if(edev->port == 0 || edev->port == ctlr->port){
648 edev->port = ctlr->port;
649 edev->irq = ctlr->pcidev->intl;
650 edev->tbdf = ctlr->pcidev->tbdf;
654 if((ctlr->feat & Fmac) != 0 && memcmp(edev->ea, zeros, Eaddrlen) == 0){
655 for(i = 0; i < Eaddrlen; i++)
656 edev->ea[i] = inb(ctlr->port+Qmac+i);
658 for(i = 0; i < Eaddrlen; i++)
659 outb(ctlr->port+Qmac+i, edev->ea[i]);
664 edev->attach = attach;
665 edev->shutdown = shutdown;
666 edev->ifstat = ifstat;
668 if((ctlr->feat & (Fctrlvq|Fctrlrx)) == (Fctrlvq|Fctrlrx)){
669 edev->multicast = multicast;
670 edev->promiscuous = promiscuous;
673 pcisetbme(ctlr->pcidev);
674 intrenable(edev->irq, interrupt, edev, edev->tbdf, edev->name);
680 ethervirtiolink(void)
682 addethercard("virtio", reset);