#include "io.h"
#include "../port/error.h"
#include "../port/netif.h"
-#include "etherif.h"
+#include "../port/etherif.h"
/*
* virtio ethernet driver
Fmac = (1<<5),
Fstatus = (1<<16),
Fctrlvq = (1<<17),
+ Fctrlrx = (1<<18),
/* vring used flags */
Unonotify = 1,
Vused *usedent;
u16int *usedevent;
u16int lastused;
+
+ uint nintr;
+ uint nnote;
};
struct Ctlr {
int nqueue;
/* virtioether has 3 queues: rx, tx and ctl */
- Vqueue *queue[3];
-
- /* MAC address */
- uchar ea[Eaddrlen];
+ Vqueue queue[3];
};
static Ctlr *ctlrhead;
return q->lastused != q->used->idx;
}
+static void
+vqnotify(Ctlr *ctlr, int x)
+{
+ Vqueue *q;
+
+ coherence();
+ q = &ctlr->queue[x];
+ if(q->used->flags & Unonotify)
+ return;
+ q->nnote++;
+ outs(ctlr->port+Qnotify, x);
+}
+
static void
txproc(void *v)
{
edev = v;
ctlr = edev->ctlr;
- q = ctlr->queue[Vtxq];
+ q = &ctlr->queue[Vtxq];
header = smalloc(VheaderSize);
blocks = smalloc(sizeof(Block*) * (q->qsize/2));
q->desc[j].flags = 0;
}
- q->used->flags &= ~Rnointerrupt;
+ q->avail->flags &= ~Rnointerrupt;
while(waserror())
;
q->desc[j].len = BLEN(b);
coherence();
q->avail->idx++;
- outs(ctlr->port+Qnotify, Vtxq);
+ vqnotify(ctlr, Vtxq);
}
pexit("ether out queue closed", 1);
edev = v;
ctlr = edev->ctlr;
- q = ctlr->queue[Vrxq];
+ q = &ctlr->queue[Vrxq];
header = smalloc(VheaderSize);
blocks = smalloc(sizeof(Block*) * (q->qsize/2));
q->desc[j].flags = Dwrite;
}
- q->used->flags &= ~Rnointerrupt;
+ q->avail->flags &= ~Rnointerrupt;
while(waserror())
;
q->desc[j].len = BALLOC(b);
coherence();
q->avail->idx++;
- outs(ctlr->port+Qnotify, Vrxq);
} while(q->avail->idx != q->used->idx);
+ vqnotify(ctlr, Vrxq);
/* wait for any packets to complete */
if(!vhasroom(q))
blocks[i] = nil;
- b->wp = b->rp + u->len;
- etheriq(edev, b, 1);
+ b->wp = b->rp + u->len - VheaderSize;
+ etheriq(edev, b);
q->lastused++;
}
}
int i;
ctlr = edev->ctlr;
- q = ctlr->queue[Vctlq];
- if(q == nil || q->qsize < 3)
+ q = &ctlr->queue[Vctlq];
+ if(q->qsize < 3)
return -1;
qlock(&ctlr->ctllock);
q->availent[i] = 0;
coherence();
- q->used->flags &= ~Rnointerrupt;
+ q->avail->flags &= ~Rnointerrupt;
q->avail->idx++;
- outs(ctlr->port+Qnotify, Vctlq);
+ vqnotify(ctlr, Vctlq);
while(!vhasroom(q))
sleep(q, vhasroom, q);
q->lastused = q->used->idx;
- q->used->flags |= Rnointerrupt;
+ q->avail->flags |= Rnointerrupt;
qunlock(&ctlr->ctllock);
poperror();
ctlr = edev->ctlr;
if(inb(ctlr->port+Qisr) & 1){
for(i = 0; i < ctlr->nqueue; i++){
- q = ctlr->queue[i];
- if(vhasroom(q))
+ q = &ctlr->queue[i];
+ if(vhasroom(q)){
+ q->nintr++;
wakeup(q);
+ }
}
}
}
l = snprint(p, READSTR, "devfeat %32.32lub\n", ctlr->feat);
l += snprint(p+l, READSTR-l, "drvfeat %32.32lub\n", inl(ctlr->port+Qdrvfeat));
l += snprint(p+l, READSTR-l, "devstatus %8.8ub\n", inb(ctlr->port+Qstatus));
- l += snprint(p+l, READSTR-l, "isr %8.8ub\n", inb(ctlr->port+Qisr));
- l += snprint(p+l, READSTR-l, "netstatus %8.8ub\n", inb(ctlr->port+Qnetstatus));
+ if(ctlr->feat & Fstatus)
+ l += snprint(p+l, READSTR-l, "netstatus %8.8ub\n", inb(ctlr->port+Qnetstatus));
for(i = 0; i < ctlr->nqueue; i++){
- q = ctlr->queue[i];
- l += snprint(p+l, READSTR-l, "vq%d %#p size %d avail->idx %d used->idx %d lastused %hud\n",
- i, q, q->qsize, q->avail->idx, q->used->idx, q->lastused);
+ q = &ctlr->queue[i];
+ l += snprint(p+l, READSTR-l,
+ "vq%d %#p size %d avail->idx %d used->idx %d lastused %hud nintr %ud nnote %ud\n",
+ i, q, q->qsize, q->avail->idx, q->used->idx, q->lastused, q->nintr, q->nnote);
}
n = readstr(offset, a, n, p);
{
Ctlr *ctlr = edev->ctlr;
outb(ctlr->port+Qstatus, 0);
+ pciclrbme(ctlr->pcidev);
}
static void
+ VPGROUND(sizeof(u16int)*3 + VusedSize*size);
}
-static Vqueue*
-mkqueue(int size)
+static int
+initqueue(Vqueue *q, int size)
{
- Vqueue *q;
uchar *p;
/* §2.4: Queue Size value is always a power of 2 and <= 32768 */
assert(!(size & (size - 1)) && size <= 32768);
- q = mallocz(sizeof(Vqueue), 1);
p = mallocalign(queuesize(size), VBY2PG, 0, 0);
- if(p == nil || q == nil){
+ if(p == nil){
print("ethervirtio: no memory for Vqueue\n");
free(p);
- free(q);
- return nil;
+ return -1;
}
q->desc = (void*)p;
q->lastused = q->avail->idx = q->used->idx = 0;
- /* disable interrupts
- * virtio spec says we still get interrupts if
- * VnotifyEmpty is set in Drvfeat */
- q->used->flags |= Rnointerrupt;
+ q->avail->flags |= Rnointerrupt;
- return q;
+ return 0;
}
static Ctlr*
for(p = nil; p = pcimatch(p, 0, 0);){
if(p->vid != 0x1AF4)
continue;
- /* the two possible DIDs for virtio-net
+ /* the two possible DIDs for virtio-net */
if(p->did != 0x1000 && p->did != 0x1041)
continue;
/* non-transitional devices will have a revision > 0 */
/* non-transitional device will have typ+0x40 */
if(pcicfgr16(p, 0x2E) != typ)
continue;
- if((c = malloc(sizeof(Ctlr))) == nil){
+ if((c = mallocz(sizeof(Ctlr), 1)) == nil){
print("ethervirtio: no memory for Ctlr\n");
break;
}
c->port = p->mem[0].bar & ~0x1;
-
if(ioalloc(c->port, p->mem[0].size, 0, "ethervirtio") < 0){
print("ethervirtio: port %ux in use\n", c->port);
free(c);
c->typ = typ;
c->pcidev = p;
+ pcienable(p);
c->id = (p->did<<16)|p->vid;
/* §3.1.2 Legacy Device Initialization */
outb(c->port+Qstatus, 0);
-
outb(c->port+Qstatus, Sacknowledge|Sdriver);
+ /* negotiate feature bits */
c->feat = inl(c->port+Qdevfeat);
-
- if((c->feat & (Fmac|Fstatus|Fctrlvq)) != (Fmac|Fstatus|Fctrlvq)){
- print("ethervirtio: feature mismatch %32.32lub\n", c->feat);
- outb(c->port+Qstatus, Sfailed);
- iofree(c->port);
- free(c);
- continue;
- }
-
- outl(c->port+Qdrvfeat, Fmac|Fstatus|Fctrlvq);
-
- /* part of the 1.0 spec, not used in legacy */
- /*
- outb(vd->port+Status, inb(vd->port+Status) | FeatureOk);
- i = inb(vd->port+Status);
- if(!(i & FeatureOk)){
- print("ethervirtio: feature mismatch %32.32lub\n", vd->feat);
- outb(vd->port+Status, Failed);
- iofree(vd->port);
- free(vd);
- continue;
- }
- */
+ outl(c->port+Qdrvfeat, c->feat & (Fmac|Fstatus|Fctrlvq|Fctrlrx));
/* §4.1.5.1.4 Virtqueue Configuration */
for(i=0; i<nelem(c->queue); i++){
outs(c->port+Qselect, i);
n = ins(c->port+Qsize);
if(n == 0 || (n & (n-1)) != 0){
- c->queue[i] = nil;
+ if(i < 2)
+ print("ethervirtio: queue %d has invalid size %d\n", i, n);
break;
}
- if((c->queue[i] = mkqueue(n)) == nil)
+ if(initqueue(&c->queue[i], n) < 0)
break;
coherence();
- outl(c->port+Qaddr, PADDR(c->queue[i]->desc)/VBY2PG);
+ outl(c->port+Qaddr, PADDR(c->queue[i].desc)/VBY2PG);
}
- c->nqueue = i;
+ if(i < 2){
+ print("ethervirtio: no queues\n");
+ pcidisable(p);
+ free(c);
+ continue;
+ }
+ c->nqueue = i;
- /* read virtio mac */
- for(i = 0; i < Eaddrlen; i++)
- c->ea[i] = inb(c->port+Qmac+i);
-
if(h == nil)
h = c;
else
static int
reset(Ether* edev)
{
+ static uchar zeros[Eaddrlen];
Ctlr *ctlr;
+ int i;
- if(ctlrhead == nil) {
+ if(ctlrhead == nil)
ctlrhead = pciprobe(1);
- }
for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){
if(ctlr->active)
edev->mbps = 1000;
edev->link = 1;
- memmove(edev->ea, ctlr->ea, Eaddrlen);
+ if((ctlr->feat & Fmac) != 0 && memcmp(edev->ea, zeros, Eaddrlen) == 0){
+ for(i = 0; i < Eaddrlen; i++)
+ edev->ea[i] = inb(ctlr->port+Qmac+i);
+ } else {
+ for(i = 0; i < Eaddrlen; i++)
+ outb(ctlr->port+Qmac+i, edev->ea[i]);
+ }
edev->arg = edev;
edev->attach = attach;
edev->shutdown = shutdown;
+ edev->ifstat = ifstat;
- edev->interrupt = interrupt;
+ if((ctlr->feat & (Fctrlvq|Fctrlrx)) == (Fctrlvq|Fctrlrx)){
+ edev->multicast = multicast;
+ edev->promiscuous = promiscuous;
+ }
- edev->ifstat = ifstat;
- edev->multicast = multicast;
- edev->promiscuous = promiscuous;
+ pcisetbme(ctlr->pcidev);
+ intrenable(edev->irq, interrupt, edev, edev->tbdf, edev->name);
return 0;
}
void
ethervirtiolink(void)
{
- addethercard("ethervirtio", reset);
+ addethercard("virtio", reset);
}