2 #include "../port/lib.h"
7 #include "../port/error.h"
8 #include "../port/netif.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 */
52 /* vring used flags */
54 /* vring avail flags */
57 /* descriptor flags */
68 /* §4.1.5.1.4.1 says pages are 4096 bytes
69 * for the purposes of the driver.
72 #define VPGROUND(s) ROUND(s, VBY2PG)
78 /* class/cmd for Vctlq */
83 CmdMacTableSet = 0x00,
119 /* §2.4 Virtqueues */
155 /* virtioether has 3 queues: rx, tx and ctl */
162 static Ctlr *ctlrhead;
168 return q->lastused != q->used->idx;
185 q = ctlr->queue[Vtxq];
187 header = smalloc(VheaderSize);
188 blocks = smalloc(sizeof(Block*) * (q->qsize/2));
190 for(i = 0; i < q->qsize/2; i++){
192 q->desc[j].addr = PADDR(header);
193 q->desc[j].len = VheaderSize;
194 q->desc[j].next = j | 1;
195 q->desc[j].flags = Dnext;
197 q->availent[i] = q->availent[i + q->qsize/2] = j;
201 q->desc[j].flags = 0;
204 q->used->flags &= ~Rnointerrupt;
209 while((b = qbread(edev->oq, 1000000)) != nil){
211 /* retire completed packets */
212 while((i = q->lastused) != q->used->idx){
213 u = &q->usedent[i & q->qmask];
214 i = (u->id & q->qmask) >> 1;
222 /* have free slot? */
223 i = q->avail->idx & (q->qmask >> 1);
227 /* ring full, wait and retry */
229 sleep(q, vhasroom, q);
232 /* slot is free, fill in descriptor */
235 q->desc[j].addr = PADDR(b->rp);
236 q->desc[j].len = BLEN(b);
239 outs(ctlr->port+Qnotify, Vtxq);
242 pexit("ether out queue closed", 1);
259 q = ctlr->queue[Vrxq];
261 header = smalloc(VheaderSize);
262 blocks = smalloc(sizeof(Block*) * (q->qsize/2));
264 for(i = 0; i < q->qsize/2; i++){
266 q->desc[j].addr = PADDR(header);
267 q->desc[j].len = VheaderSize;
268 q->desc[j].next = j | 1;
269 q->desc[j].flags = Dwrite|Dnext;
271 q->availent[i] = q->availent[i + q->qsize/2] = j;
275 q->desc[j].flags = Dwrite;
278 q->used->flags &= ~Rnointerrupt;
284 /* replenish receive ring */
286 i = q->avail->idx & (q->qmask >> 1);
289 if((b = iallocb(ETHERMAXTU)) == nil)
293 q->desc[j].addr = PADDR(b->rp);
294 q->desc[j].len = BALLOC(b);
297 outs(ctlr->port+Qnotify, Vrxq);
298 } while(q->avail->idx != q->used->idx);
300 /* wait for any packets to complete */
302 sleep(q, vhasroom, q);
304 /* retire completed packets */
305 while((i = q->lastused) != q->used->idx) {
306 u = &q->usedent[i & q->qmask];
307 i = (u->id & q->qmask) >> 1;
308 if((b = blocks[i]) == nil)
313 b->wp = b->rp + u->len;
321 vctlcmd(Ether *edev, uchar class, uchar cmd, uchar *data, int ndata)
323 uchar hdr[2], ack[1];
330 q = ctlr->queue[Vctlq];
331 if(q == nil || q->qsize < 3)
334 qlock(&ctlr->ctllock);
343 d->addr = PADDR(hdr);
344 d->len = sizeof(hdr);
348 d->addr = PADDR(data);
353 d->addr = PADDR(ack);
354 d->len = sizeof(ack);
358 i = q->avail->idx & q->qmask;
362 q->used->flags &= ~Rnointerrupt;
364 outs(ctlr->port+Qnotify, Vctlq);
366 sleep(q, vhasroom, q);
367 q->lastused = q->used->idx;
368 q->used->flags |= Rnointerrupt;
370 qunlock(&ctlr->ctllock);
374 print("#l%d: vctlcmd: %ux.%ux -> %ux\n", edev->ctlrno, class, cmd, ack[0]);
380 interrupt(Ureg*, void* arg)
389 if(inb(ctlr->port+Qisr) & 1){
390 for(i = 0; i < ctlr->nqueue; i++){
410 outb(ctlr->port+Qstatus, inb(ctlr->port+Qstatus) | Sdriverok);
413 snprint(name, sizeof name, "#l%drx", edev->ctlrno);
414 kproc(name, rxproc, edev);
415 snprint(name, sizeof name, "#l%dtx", edev->ctlrno);
416 kproc(name, txproc, edev);
422 ifstat(Ether *edev, void *a, long n, ulong offset)
431 p = smalloc(READSTR);
433 l = snprint(p, READSTR, "devfeat %32.32lub\n", ctlr->feat);
434 l += snprint(p+l, READSTR-l, "drvfeat %32.32lub\n", inl(ctlr->port+Qdrvfeat));
435 l += snprint(p+l, READSTR-l, "devstatus %8.8ub\n", inb(ctlr->port+Qstatus));
436 l += snprint(p+l, READSTR-l, "isr %8.8ub\n", inb(ctlr->port+Qisr));
437 l += snprint(p+l, READSTR-l, "netstatus %8.8ub\n", inb(ctlr->port+Qnetstatus));
439 for(i = 0; i < ctlr->nqueue; i++){
441 l += snprint(p+l, READSTR-l, "vq%d %#p size %d avail->idx %d used->idx %d lastused %hud\n",
442 i, q, q->qsize, q->avail->idx, q->used->idx, q->lastused);
445 n = readstr(offset, a, n, p);
452 shutdown(Ether* edev)
454 Ctlr *ctlr = edev->ctlr;
455 outb(ctlr->port+Qstatus, 0);
459 promiscuous(void *arg, int on)
465 vctlcmd(edev, CtrlRx, CmdPromisc, b, sizeof(b));
469 multicast(void *arg, uchar*, int)
474 b[0] = edev->nmaddr > 0;
475 vctlcmd(edev, CtrlRx, CmdAllmulti, b, sizeof(b));
478 /* §2.4.2 Legacy Interfaces: A Note on Virtqueue Layout */
480 queuesize(ulong size)
482 return VPGROUND(VdescSize*size + sizeof(u16int)*(3+size))
483 + VPGROUND(sizeof(u16int)*3 + VusedSize*size);
492 /* §2.4: Queue Size value is always a power of 2 and <= 32768 */
493 assert(!(size & (size - 1)) && size <= 32768);
495 q = mallocz(sizeof(Vqueue), 1);
496 p = mallocalign(queuesize(size), VBY2PG, 0, 0);
497 if(p == nil || q == nil){
498 print("ethervirtio: no memory for Vqueue\n");
508 q->availent = (void*)p;
509 p += sizeof(u16int)*size;
510 q->availevent = (void*)p;
513 p = (uchar*)VPGROUND((uintptr)p);
516 q->usedent = (void*)p;
518 q->usedevent = (void*)p;
521 q->qmask = q->qsize - 1;
523 q->lastused = q->avail->idx = q->used->idx = 0;
525 /* disable interrupts
526 * virtio spec says we still get interrupts if
527 * VnotifyEmpty is set in Drvfeat */
528 q->used->flags |= Rnointerrupt;
542 /* §4.1.2 PCI Device Discovery */
543 for(p = nil; p = pcimatch(p, 0, 0);){
546 /* the two possible DIDs for virtio-net
547 if(p->did != 0x1000 && p->did != 0x1041)
549 /* non-transitional devices will have a revision > 0 */
552 /* non-transitional device will have typ+0x40 */
553 if(pcicfgr16(p, 0x2E) != typ)
555 if((c = malloc(sizeof(Ctlr))) == nil){
556 print("ethervirtio: no memory for Ctlr\n");
560 c->port = p->mem[0].bar & ~0x1;
562 if(ioalloc(c->port, p->mem[0].size, 0, "ethervirtio") < 0){
563 print("ethervirtio: port %ux in use\n", c->port);
570 c->id = (p->did<<16)|p->vid;
572 /* §3.1.2 Legacy Device Initialization */
573 outb(c->port+Qstatus, 0);
575 outb(c->port+Qstatus, Sacknowledge|Sdriver);
577 c->feat = inl(c->port+Qdevfeat);
579 if((c->feat & (Fmac|Fstatus|Fctrlvq)) != (Fmac|Fstatus|Fctrlvq)){
580 print("ethervirtio: feature mismatch %32.32lub\n", c->feat);
581 outb(c->port+Qstatus, Sfailed);
587 outl(c->port+Qdrvfeat, Fmac|Fstatus|Fctrlvq);
589 /* part of the 1.0 spec, not used in legacy */
591 outb(vd->port+Status, inb(vd->port+Status) | FeatureOk);
592 i = inb(vd->port+Status);
593 if(!(i & FeatureOk)){
594 print("ethervirtio: feature mismatch %32.32lub\n", vd->feat);
595 outb(vd->port+Status, Failed);
602 /* §4.1.5.1.4 Virtqueue Configuration */
603 for(i=0; i<nelem(c->queue); i++){
604 outs(c->port+Qselect, i);
605 n = ins(c->port+Qsize);
606 if(n == 0 || (n & (n-1)) != 0){
610 if((c->queue[i] = mkqueue(n)) == nil)
613 outl(c->port+Qaddr, PADDR(c->queue[i]->desc)/VBY2PG);
617 /* read virtio mac */
618 for(i = 0; i < Eaddrlen; i++)
619 c->ea[i] = inb(c->port+Qmac+i);
637 if(ctlrhead == nil) {
638 ctlrhead = pciprobe(1);
641 for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){
644 if(edev->port == 0 || edev->port == ctlr->port){
654 edev->port = ctlr->port;
655 edev->irq = ctlr->pcidev->intl;
656 edev->tbdf = ctlr->pcidev->tbdf;
660 memmove(edev->ea, ctlr->ea, Eaddrlen);
664 edev->attach = attach;
665 edev->shutdown = shutdown;
667 edev->interrupt = interrupt;
669 edev->ifstat = ifstat;
670 edev->multicast = multicast;
671 edev->promiscuous = promiscuous;
677 ethervirtiolink(void)
679 addethercard("ethervirtio", reset);