2014-12-07 06:13:44 +00:00
|
|
|
#include "u.h"
|
|
|
|
#include "../port/lib.h"
|
|
|
|
#include "mem.h"
|
|
|
|
#include "dat.h"
|
|
|
|
#include "fns.h"
|
|
|
|
#include "io.h"
|
|
|
|
#include "../port/error.h"
|
|
|
|
#include "../port/netif.h"
|
2018-02-11 17:08:03 +00:00
|
|
|
#include "../port/etherif.h"
|
2014-12-07 06:13:44 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* virtio ethernet driver
|
|
|
|
* http://docs.oasis-open.org/virtio/virtio/v1.0/virtio-v1.0.html
|
|
|
|
*/
|
|
|
|
|
|
|
|
typedef struct Vring Vring;
|
|
|
|
typedef struct Vdesc Vdesc;
|
|
|
|
typedef struct Vused Vused;
|
|
|
|
typedef struct Vheader Vheader;
|
|
|
|
typedef struct Vqueue Vqueue;
|
|
|
|
typedef struct Ctlr Ctlr;
|
|
|
|
|
|
|
|
enum {
|
|
|
|
/* §2.1 Device Status Field */
|
|
|
|
Sacknowledge = 1,
|
|
|
|
Sdriver = 2,
|
|
|
|
Sdriverok = 4,
|
|
|
|
Sfeatureok = 8,
|
|
|
|
Sfailed = 128,
|
|
|
|
|
|
|
|
/* §4.1.4.8 Legacy Interfaces: A Note on PCI Device Layout */
|
|
|
|
Qdevfeat = 0,
|
|
|
|
Qdrvfeat = 4,
|
|
|
|
Qaddr = 8,
|
|
|
|
Qsize = 12,
|
|
|
|
Qselect = 14,
|
|
|
|
Qnotify = 16,
|
|
|
|
Qstatus = 18,
|
|
|
|
Qisr = 19,
|
|
|
|
Qmac = 20,
|
|
|
|
Qnetstatus = 26,
|
|
|
|
|
|
|
|
/* flags in Qnetstatus */
|
|
|
|
Nlinkup = (1<<0),
|
|
|
|
Nannounce = (1<<1),
|
|
|
|
|
|
|
|
/* feature bits */
|
|
|
|
Fmac = (1<<5),
|
|
|
|
Fstatus = (1<<16),
|
|
|
|
Fctrlvq = (1<<17),
|
2014-12-08 18:19:53 +00:00
|
|
|
Fctrlrx = (1<<18),
|
2014-12-07 06:13:44 +00:00
|
|
|
|
|
|
|
/* vring used flags */
|
|
|
|
Unonotify = 1,
|
|
|
|
/* vring avail flags */
|
|
|
|
Rnointerrupt = 1,
|
|
|
|
|
|
|
|
/* descriptor flags */
|
|
|
|
Dnext = 1,
|
|
|
|
Dwrite = 2,
|
|
|
|
Dindirect = 4,
|
|
|
|
|
|
|
|
/* struct sizes */
|
|
|
|
VringSize = 4,
|
|
|
|
VdescSize = 16,
|
|
|
|
VusedSize = 8,
|
|
|
|
VheaderSize = 10,
|
|
|
|
|
|
|
|
/* §4.1.5.1.4.1 says pages are 4096 bytes
|
|
|
|
* for the purposes of the driver.
|
|
|
|
*/
|
|
|
|
VBY2PG = 4096,
|
|
|
|
#define VPGROUND(s) ROUND(s, VBY2PG)
|
|
|
|
|
|
|
|
Vrxq = 0,
|
|
|
|
Vtxq = 1,
|
|
|
|
Vctlq = 2,
|
2014-12-07 16:58:51 +00:00
|
|
|
|
|
|
|
/* class/cmd for Vctlq */
|
|
|
|
CtrlRx = 0x00,
|
|
|
|
CmdPromisc = 0x00,
|
|
|
|
CmdAllmulti = 0x01,
|
|
|
|
CtrlMac = 0x01,
|
|
|
|
CmdMacTableSet = 0x00,
|
|
|
|
CtrlVlan= 0x02,
|
|
|
|
CmdVlanAdd = 0x00,
|
|
|
|
CmdVlanDel = 0x01,
|
2014-12-07 06:13:44 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct Vring
|
|
|
|
{
|
|
|
|
u16int flags;
|
|
|
|
u16int idx;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct Vdesc
|
|
|
|
{
|
|
|
|
u64int addr;
|
|
|
|
u32int len;
|
|
|
|
u16int flags;
|
|
|
|
u16int next;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct Vused
|
|
|
|
{
|
|
|
|
u32int id;
|
|
|
|
u32int len;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct Vheader
|
|
|
|
{
|
|
|
|
u8int flags;
|
|
|
|
u8int segtype;
|
|
|
|
u16int hlen;
|
|
|
|
u16int seglen;
|
|
|
|
u16int csumstart;
|
|
|
|
u16int csumend;
|
|
|
|
};
|
|
|
|
|
|
|
|
/* §2.4 Virtqueues */
|
|
|
|
struct Vqueue
|
|
|
|
{
|
|
|
|
Rendez;
|
|
|
|
|
|
|
|
uint qsize;
|
|
|
|
uint qmask;
|
|
|
|
|
|
|
|
Vdesc *desc;
|
|
|
|
|
|
|
|
Vring *avail;
|
|
|
|
u16int *availent;
|
|
|
|
u16int *availevent;
|
|
|
|
|
|
|
|
Vring *used;
|
|
|
|
Vused *usedent;
|
|
|
|
u16int *usedevent;
|
|
|
|
u16int lastused;
|
2014-12-08 18:19:53 +00:00
|
|
|
|
|
|
|
uint nintr;
|
2014-12-09 02:23:53 +00:00
|
|
|
uint nnote;
|
2014-12-07 06:13:44 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct Ctlr {
|
|
|
|
Lock;
|
|
|
|
|
2014-12-07 16:58:51 +00:00
|
|
|
QLock ctllock;
|
2014-12-07 06:13:44 +00:00
|
|
|
|
2014-12-07 16:58:51 +00:00
|
|
|
int attached;
|
|
|
|
|
|
|
|
int port;
|
|
|
|
Pcidev *pcidev;
|
|
|
|
Ctlr *next;
|
|
|
|
int active;
|
|
|
|
int id;
|
|
|
|
int typ;
|
2014-12-07 06:13:44 +00:00
|
|
|
ulong feat;
|
2014-12-07 16:58:51 +00:00
|
|
|
int nqueue;
|
2014-12-07 06:13:44 +00:00
|
|
|
|
|
|
|
/* virtioether has 3 queues: rx, tx and ctl */
|
2014-12-08 18:19:53 +00:00
|
|
|
Vqueue queue[3];
|
2014-12-07 06:13:44 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static Ctlr *ctlrhead;
|
|
|
|
|
|
|
|
static int
|
|
|
|
vhasroom(void *v)
|
|
|
|
{
|
|
|
|
Vqueue *q = v;
|
|
|
|
return q->lastused != q->used->idx;
|
|
|
|
}
|
|
|
|
|
2014-12-09 02:23:53 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2014-12-07 06:13:44 +00:00
|
|
|
static void
|
|
|
|
txproc(void *v)
|
|
|
|
{
|
2014-12-07 16:58:51 +00:00
|
|
|
Vheader *header;
|
|
|
|
Block **blocks;
|
2014-12-07 06:13:44 +00:00
|
|
|
Ether *edev;
|
|
|
|
Ctlr *ctlr;
|
|
|
|
Vqueue *q;
|
|
|
|
Vused *u;
|
|
|
|
Block *b;
|
|
|
|
int i, j;
|
|
|
|
|
|
|
|
edev = v;
|
|
|
|
ctlr = edev->ctlr;
|
2014-12-08 18:19:53 +00:00
|
|
|
q = &ctlr->queue[Vtxq];
|
2014-12-07 06:13:44 +00:00
|
|
|
|
2014-12-07 16:58:51 +00:00
|
|
|
header = smalloc(VheaderSize);
|
|
|
|
blocks = smalloc(sizeof(Block*) * (q->qsize/2));
|
2014-12-07 06:13:44 +00:00
|
|
|
|
|
|
|
for(i = 0; i < q->qsize/2; i++){
|
|
|
|
j = i << 1;
|
2014-12-07 16:58:51 +00:00
|
|
|
q->desc[j].addr = PADDR(header);
|
2014-12-07 06:13:44 +00:00
|
|
|
q->desc[j].len = VheaderSize;
|
|
|
|
q->desc[j].next = j | 1;
|
|
|
|
q->desc[j].flags = Dnext;
|
|
|
|
|
|
|
|
q->availent[i] = q->availent[i + q->qsize/2] = j;
|
|
|
|
|
|
|
|
j |= 1;
|
|
|
|
q->desc[j].next = 0;
|
|
|
|
q->desc[j].flags = 0;
|
|
|
|
}
|
|
|
|
|
2014-12-09 02:23:53 +00:00
|
|
|
q->avail->flags &= ~Rnointerrupt;
|
2014-12-07 06:13:44 +00:00
|
|
|
|
2014-12-07 16:58:51 +00:00
|
|
|
while(waserror())
|
|
|
|
;
|
|
|
|
|
2014-12-07 06:13:44 +00:00
|
|
|
while((b = qbread(edev->oq, 1000000)) != nil){
|
2014-12-07 14:34:53 +00:00
|
|
|
for(;;){
|
|
|
|
/* retire completed packets */
|
|
|
|
while((i = q->lastused) != q->used->idx){
|
|
|
|
u = &q->usedent[i & q->qmask];
|
|
|
|
i = (u->id & q->qmask) >> 1;
|
2014-12-07 16:58:51 +00:00
|
|
|
if(blocks[i] == nil)
|
2014-12-07 14:34:53 +00:00
|
|
|
break;
|
2014-12-07 16:58:51 +00:00
|
|
|
freeb(blocks[i]);
|
|
|
|
blocks[i] = nil;
|
2014-12-07 14:34:53 +00:00
|
|
|
q->lastused++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* have free slot? */
|
|
|
|
i = q->avail->idx & (q->qmask >> 1);
|
2014-12-07 16:58:51 +00:00
|
|
|
if(blocks[i] == nil)
|
2014-12-07 14:34:53 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
/* ring full, wait and retry */
|
2014-12-07 06:13:44 +00:00
|
|
|
if(!vhasroom(q))
|
|
|
|
sleep(q, vhasroom, q);
|
|
|
|
}
|
|
|
|
|
2014-12-07 14:34:53 +00:00
|
|
|
/* slot is free, fill in descriptor */
|
2014-12-07 16:58:51 +00:00
|
|
|
blocks[i] = b;
|
2014-12-07 14:34:53 +00:00
|
|
|
j = (i << 1) | 1;
|
|
|
|
q->desc[j].addr = PADDR(b->rp);
|
|
|
|
q->desc[j].len = BLEN(b);
|
|
|
|
coherence();
|
|
|
|
q->avail->idx++;
|
2014-12-09 02:23:53 +00:00
|
|
|
vqnotify(ctlr, Vtxq);
|
2014-12-07 06:13:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pexit("ether out queue closed", 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
rxproc(void *v)
|
|
|
|
{
|
2014-12-07 16:58:51 +00:00
|
|
|
Vheader *header;
|
|
|
|
Block **blocks;
|
2014-12-07 06:13:44 +00:00
|
|
|
Ether *edev;
|
|
|
|
Ctlr *ctlr;
|
|
|
|
Vqueue *q;
|
|
|
|
Vused *u;
|
|
|
|
Block *b;
|
|
|
|
int i, j;
|
|
|
|
|
|
|
|
edev = v;
|
|
|
|
ctlr = edev->ctlr;
|
2014-12-08 18:19:53 +00:00
|
|
|
q = &ctlr->queue[Vrxq];
|
2014-12-07 06:13:44 +00:00
|
|
|
|
2014-12-07 16:58:51 +00:00
|
|
|
header = smalloc(VheaderSize);
|
|
|
|
blocks = smalloc(sizeof(Block*) * (q->qsize/2));
|
2014-12-07 06:13:44 +00:00
|
|
|
|
|
|
|
for(i = 0; i < q->qsize/2; i++){
|
|
|
|
j = i << 1;
|
2014-12-07 16:58:51 +00:00
|
|
|
q->desc[j].addr = PADDR(header);
|
2014-12-07 06:13:44 +00:00
|
|
|
q->desc[j].len = VheaderSize;
|
|
|
|
q->desc[j].next = j | 1;
|
|
|
|
q->desc[j].flags = Dwrite|Dnext;
|
|
|
|
|
|
|
|
q->availent[i] = q->availent[i + q->qsize/2] = j;
|
|
|
|
|
|
|
|
j |= 1;
|
|
|
|
q->desc[j].next = 0;
|
|
|
|
q->desc[j].flags = Dwrite;
|
|
|
|
}
|
|
|
|
|
2014-12-09 02:23:53 +00:00
|
|
|
q->avail->flags &= ~Rnointerrupt;
|
2014-12-07 06:13:44 +00:00
|
|
|
|
2014-12-07 16:58:51 +00:00
|
|
|
while(waserror())
|
|
|
|
;
|
|
|
|
|
2014-12-07 06:13:44 +00:00
|
|
|
for(;;){
|
|
|
|
/* replenish receive ring */
|
|
|
|
do {
|
|
|
|
i = q->avail->idx & (q->qmask >> 1);
|
2014-12-07 16:58:51 +00:00
|
|
|
if(blocks[i] != nil)
|
2014-12-07 06:13:44 +00:00
|
|
|
break;
|
|
|
|
if((b = iallocb(ETHERMAXTU)) == nil)
|
|
|
|
break;
|
2014-12-07 16:58:51 +00:00
|
|
|
blocks[i] = b;
|
2014-12-07 06:13:44 +00:00
|
|
|
j = (i << 1) | 1;
|
|
|
|
q->desc[j].addr = PADDR(b->rp);
|
|
|
|
q->desc[j].len = BALLOC(b);
|
|
|
|
coherence();
|
|
|
|
q->avail->idx++;
|
|
|
|
} while(q->avail->idx != q->used->idx);
|
2014-12-09 02:23:53 +00:00
|
|
|
vqnotify(ctlr, Vrxq);
|
2014-12-07 06:13:44 +00:00
|
|
|
|
|
|
|
/* wait for any packets to complete */
|
|
|
|
if(!vhasroom(q))
|
|
|
|
sleep(q, vhasroom, q);
|
|
|
|
|
|
|
|
/* retire completed packets */
|
|
|
|
while((i = q->lastused) != q->used->idx) {
|
|
|
|
u = &q->usedent[i & q->qmask];
|
|
|
|
i = (u->id & q->qmask) >> 1;
|
2014-12-07 16:58:51 +00:00
|
|
|
if((b = blocks[i]) == nil)
|
2014-12-07 06:13:44 +00:00
|
|
|
break;
|
|
|
|
|
2014-12-07 16:58:51 +00:00
|
|
|
blocks[i] = nil;
|
2014-12-07 06:13:44 +00:00
|
|
|
|
2017-06-12 11:25:12 +00:00
|
|
|
b->wp = b->rp + u->len - VheaderSize;
|
2018-02-18 18:56:01 +00:00
|
|
|
etheriq(edev, b);
|
2014-12-07 06:13:44 +00:00
|
|
|
q->lastused++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-07 16:58:51 +00:00
|
|
|
static int
|
|
|
|
vctlcmd(Ether *edev, uchar class, uchar cmd, uchar *data, int ndata)
|
|
|
|
{
|
|
|
|
uchar hdr[2], ack[1];
|
|
|
|
Ctlr *ctlr;
|
|
|
|
Vqueue *q;
|
|
|
|
Vdesc *d;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
ctlr = edev->ctlr;
|
2014-12-08 18:19:53 +00:00
|
|
|
q = &ctlr->queue[Vctlq];
|
|
|
|
if(q->qsize < 3)
|
2014-12-07 16:58:51 +00:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
qlock(&ctlr->ctllock);
|
|
|
|
while(waserror())
|
|
|
|
;
|
|
|
|
|
|
|
|
ack[0] = 0x55;
|
|
|
|
hdr[0] = class;
|
|
|
|
hdr[1] = cmd;
|
|
|
|
|
|
|
|
d = &q->desc[0];
|
|
|
|
d->addr = PADDR(hdr);
|
|
|
|
d->len = sizeof(hdr);
|
|
|
|
d->next = 1;
|
|
|
|
d->flags = Dnext;
|
|
|
|
d++;
|
|
|
|
d->addr = PADDR(data);
|
|
|
|
d->len = ndata;
|
|
|
|
d->next = 2;
|
|
|
|
d->flags = Dnext;
|
|
|
|
d++;
|
|
|
|
d->addr = PADDR(ack);
|
|
|
|
d->len = sizeof(ack);
|
|
|
|
d->next = 0;
|
|
|
|
d->flags = Dwrite;
|
|
|
|
|
|
|
|
i = q->avail->idx & q->qmask;
|
|
|
|
q->availent[i] = 0;
|
|
|
|
coherence();
|
|
|
|
|
2014-12-09 02:23:53 +00:00
|
|
|
q->avail->flags &= ~Rnointerrupt;
|
2014-12-07 16:58:51 +00:00
|
|
|
q->avail->idx++;
|
2014-12-09 02:23:53 +00:00
|
|
|
vqnotify(ctlr, Vctlq);
|
2014-12-07 16:58:51 +00:00
|
|
|
while(!vhasroom(q))
|
|
|
|
sleep(q, vhasroom, q);
|
|
|
|
q->lastused = q->used->idx;
|
2014-12-09 02:23:53 +00:00
|
|
|
q->avail->flags |= Rnointerrupt;
|
2014-12-07 16:58:51 +00:00
|
|
|
|
|
|
|
qunlock(&ctlr->ctllock);
|
|
|
|
poperror();
|
|
|
|
|
|
|
|
if(ack[0] != 0)
|
|
|
|
print("#l%d: vctlcmd: %ux.%ux -> %ux\n", edev->ctlrno, class, cmd, ack[0]);
|
|
|
|
|
|
|
|
return ack[0];
|
|
|
|
}
|
|
|
|
|
2014-12-07 06:13:44 +00:00
|
|
|
static void
|
|
|
|
interrupt(Ureg*, void* arg)
|
|
|
|
{
|
|
|
|
Ether *edev;
|
2014-12-07 16:58:51 +00:00
|
|
|
Ctlr *ctlr;
|
2014-12-07 06:13:44 +00:00
|
|
|
Vqueue *q;
|
2014-12-07 16:58:51 +00:00
|
|
|
int i;
|
2014-12-07 06:13:44 +00:00
|
|
|
|
|
|
|
edev = arg;
|
|
|
|
ctlr = edev->ctlr;
|
|
|
|
if(inb(ctlr->port+Qisr) & 1){
|
2014-12-07 16:58:51 +00:00
|
|
|
for(i = 0; i < ctlr->nqueue; i++){
|
2014-12-08 18:19:53 +00:00
|
|
|
q = &ctlr->queue[i];
|
|
|
|
if(vhasroom(q)){
|
|
|
|
q->nintr++;
|
2014-12-07 16:58:51 +00:00
|
|
|
wakeup(q);
|
2014-12-08 18:19:53 +00:00
|
|
|
}
|
2014-12-07 16:58:51 +00:00
|
|
|
}
|
2014-12-07 06:13:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
attach(Ether* edev)
|
|
|
|
{
|
|
|
|
char name[KNAMELEN];
|
|
|
|
Ctlr* ctlr;
|
|
|
|
|
|
|
|
ctlr = edev->ctlr;
|
|
|
|
lock(ctlr);
|
|
|
|
if(!ctlr->attached){
|
|
|
|
ctlr->attached = 1;
|
|
|
|
|
2014-12-07 16:58:51 +00:00
|
|
|
/* ready to go */
|
|
|
|
outb(ctlr->port+Qstatus, inb(ctlr->port+Qstatus) | Sdriverok);
|
|
|
|
|
2014-12-07 06:13:44 +00:00
|
|
|
/* start kprocs */
|
|
|
|
snprint(name, sizeof name, "#l%drx", edev->ctlrno);
|
|
|
|
kproc(name, rxproc, edev);
|
|
|
|
snprint(name, sizeof name, "#l%dtx", edev->ctlrno);
|
|
|
|
kproc(name, txproc, edev);
|
|
|
|
}
|
|
|
|
unlock(ctlr);
|
|
|
|
}
|
|
|
|
|
|
|
|
static long
|
|
|
|
ifstat(Ether *edev, void *a, long n, ulong offset)
|
|
|
|
{
|
|
|
|
int i, l;
|
|
|
|
char *p;
|
|
|
|
Ctlr *ctlr;
|
|
|
|
Vqueue *q;
|
|
|
|
|
|
|
|
ctlr = edev->ctlr;
|
|
|
|
|
|
|
|
p = smalloc(READSTR);
|
|
|
|
|
|
|
|
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));
|
2014-12-08 18:19:53 +00:00
|
|
|
if(ctlr->feat & Fstatus)
|
|
|
|
l += snprint(p+l, READSTR-l, "netstatus %8.8ub\n", inb(ctlr->port+Qnetstatus));
|
2014-12-07 06:13:44 +00:00
|
|
|
|
|
|
|
for(i = 0; i < ctlr->nqueue; i++){
|
2014-12-08 18:19:53 +00:00
|
|
|
q = &ctlr->queue[i];
|
2014-12-09 02:23:53 +00:00
|
|
|
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);
|
2014-12-07 06:13:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
n = readstr(offset, a, n, p);
|
|
|
|
free(p);
|
|
|
|
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2014-12-07 16:58:51 +00:00
|
|
|
shutdown(Ether* edev)
|
2014-12-07 06:13:44 +00:00
|
|
|
{
|
2014-12-07 16:58:51 +00:00
|
|
|
Ctlr *ctlr = edev->ctlr;
|
|
|
|
outb(ctlr->port+Qstatus, 0);
|
2014-12-07 06:13:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2014-12-07 16:58:51 +00:00
|
|
|
promiscuous(void *arg, int on)
|
2014-12-07 06:13:44 +00:00
|
|
|
{
|
2014-12-07 16:58:51 +00:00
|
|
|
Ether *edev = arg;
|
|
|
|
uchar b[1];
|
2014-12-07 06:13:44 +00:00
|
|
|
|
2014-12-07 16:58:51 +00:00
|
|
|
b[0] = on != 0;
|
|
|
|
vctlcmd(edev, CtrlRx, CmdPromisc, b, sizeof(b));
|
2014-12-07 06:13:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
multicast(void *arg, uchar*, int)
|
|
|
|
{
|
2014-12-07 16:58:51 +00:00
|
|
|
Ether *edev = arg;
|
|
|
|
uchar b[1];
|
2014-12-07 06:13:44 +00:00
|
|
|
|
2014-12-07 16:58:51 +00:00
|
|
|
b[0] = edev->nmaddr > 0;
|
|
|
|
vctlcmd(edev, CtrlRx, CmdAllmulti, b, sizeof(b));
|
2014-12-07 06:13:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* §2.4.2 Legacy Interfaces: A Note on Virtqueue Layout */
|
|
|
|
static ulong
|
|
|
|
queuesize(ulong size)
|
|
|
|
{
|
|
|
|
return VPGROUND(VdescSize*size + sizeof(u16int)*(3+size))
|
|
|
|
+ VPGROUND(sizeof(u16int)*3 + VusedSize*size);
|
|
|
|
}
|
|
|
|
|
2014-12-08 18:19:53 +00:00
|
|
|
static int
|
|
|
|
initqueue(Vqueue *q, int size)
|
2014-12-07 06:13:44 +00:00
|
|
|
{
|
|
|
|
uchar *p;
|
|
|
|
|
|
|
|
/* §2.4: Queue Size value is always a power of 2 and <= 32768 */
|
|
|
|
assert(!(size & (size - 1)) && size <= 32768);
|
|
|
|
|
|
|
|
p = mallocalign(queuesize(size), VBY2PG, 0, 0);
|
2014-12-08 18:19:53 +00:00
|
|
|
if(p == nil){
|
2014-12-07 06:13:44 +00:00
|
|
|
print("ethervirtio: no memory for Vqueue\n");
|
|
|
|
free(p);
|
2014-12-08 18:19:53 +00:00
|
|
|
return -1;
|
2014-12-07 06:13:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
q->desc = (void*)p;
|
|
|
|
p += VdescSize*size;
|
|
|
|
q->avail = (void*)p;
|
|
|
|
p += VringSize;
|
|
|
|
q->availent = (void*)p;
|
|
|
|
p += sizeof(u16int)*size;
|
|
|
|
q->availevent = (void*)p;
|
|
|
|
p += sizeof(u16int);
|
|
|
|
|
|
|
|
p = (uchar*)VPGROUND((uintptr)p);
|
|
|
|
q->used = (void*)p;
|
|
|
|
p += VringSize;
|
|
|
|
q->usedent = (void*)p;
|
|
|
|
p += VusedSize*size;
|
|
|
|
q->usedevent = (void*)p;
|
|
|
|
|
|
|
|
q->qsize = size;
|
|
|
|
q->qmask = q->qsize - 1;
|
|
|
|
|
|
|
|
q->lastused = q->avail->idx = q->used->idx = 0;
|
2014-12-09 02:23:53 +00:00
|
|
|
|
|
|
|
q->avail->flags |= Rnointerrupt;
|
2014-12-07 06:13:44 +00:00
|
|
|
|
2014-12-08 18:19:53 +00:00
|
|
|
return 0;
|
2014-12-07 06:13:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static Ctlr*
|
|
|
|
pciprobe(int typ)
|
|
|
|
{
|
|
|
|
Ctlr *c, *h, *t;
|
|
|
|
Pcidev *p;
|
|
|
|
int n, i;
|
|
|
|
|
|
|
|
h = t = nil;
|
|
|
|
|
|
|
|
/* §4.1.2 PCI Device Discovery */
|
|
|
|
for(p = nil; p = pcimatch(p, 0, 0);){
|
|
|
|
if(p->vid != 0x1AF4)
|
|
|
|
continue;
|
2014-12-07 17:20:47 +00:00
|
|
|
/* the two possible DIDs for virtio-net */
|
2014-12-07 06:13:44 +00:00
|
|
|
if(p->did != 0x1000 && p->did != 0x1041)
|
|
|
|
continue;
|
|
|
|
/* non-transitional devices will have a revision > 0 */
|
|
|
|
if(p->rid != 0)
|
|
|
|
continue;
|
|
|
|
/* non-transitional device will have typ+0x40 */
|
|
|
|
if(pcicfgr16(p, 0x2E) != typ)
|
|
|
|
continue;
|
2014-12-08 18:19:53 +00:00
|
|
|
if((c = mallocz(sizeof(Ctlr), 1)) == nil){
|
2014-12-07 06:13:44 +00:00
|
|
|
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);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
c->typ = typ;
|
|
|
|
c->pcidev = 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);
|
|
|
|
|
2014-12-08 18:19:53 +00:00
|
|
|
/* negotiate feature bits */
|
2014-12-07 06:13:44 +00:00
|
|
|
c->feat = inl(c->port+Qdevfeat);
|
2014-12-08 18:19:53 +00:00
|
|
|
outl(c->port+Qdrvfeat, c->feat & (Fmac|Fstatus|Fctrlvq|Fctrlrx));
|
2014-12-07 06:13:44 +00:00
|
|
|
|
|
|
|
/* §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);
|
2017-06-12 11:25:12 +00:00
|
|
|
if(n == 0 || (n & (n-1)) != 0){
|
|
|
|
if(i < 2)
|
|
|
|
print("ethervirtio: queue %d has invalid size %d\n", i, n);
|
2014-12-07 06:13:44 +00:00
|
|
|
break;
|
2017-06-12 11:25:12 +00:00
|
|
|
}
|
2014-12-08 18:19:53 +00:00
|
|
|
if(initqueue(&c->queue[i], n) < 0)
|
2014-12-07 06:13:44 +00:00
|
|
|
break;
|
|
|
|
coherence();
|
2014-12-08 18:19:53 +00:00
|
|
|
outl(c->port+Qaddr, PADDR(c->queue[i].desc)/VBY2PG);
|
2014-12-07 06:13:44 +00:00
|
|
|
}
|
2017-06-12 11:25:12 +00:00
|
|
|
if(i < 2){
|
|
|
|
print("ethervirtio: no queues\n");
|
|
|
|
free(c);
|
|
|
|
continue;
|
|
|
|
}
|
2014-12-08 18:19:53 +00:00
|
|
|
c->nqueue = i;
|
2014-12-07 06:13:44 +00:00
|
|
|
|
|
|
|
if(h == nil)
|
|
|
|
h = c;
|
|
|
|
else
|
|
|
|
t->next = c;
|
|
|
|
t = c;
|
|
|
|
}
|
|
|
|
|
|
|
|
return h;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
reset(Ether* edev)
|
|
|
|
{
|
2014-12-08 18:19:53 +00:00
|
|
|
static uchar zeros[Eaddrlen];
|
2014-12-07 06:13:44 +00:00
|
|
|
Ctlr *ctlr;
|
2014-12-08 18:19:53 +00:00
|
|
|
int i;
|
2014-12-07 06:13:44 +00:00
|
|
|
|
|
|
|
if(ctlrhead == nil) {
|
|
|
|
ctlrhead = pciprobe(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){
|
|
|
|
if(ctlr->active)
|
|
|
|
continue;
|
|
|
|
if(edev->port == 0 || edev->port == ctlr->port){
|
|
|
|
ctlr->active = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(ctlr == nil)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
edev->ctlr = ctlr;
|
|
|
|
edev->port = ctlr->port;
|
|
|
|
edev->irq = ctlr->pcidev->intl;
|
|
|
|
edev->tbdf = ctlr->pcidev->tbdf;
|
|
|
|
edev->mbps = 1000;
|
|
|
|
edev->link = 1;
|
|
|
|
|
2014-12-08 18:19:53 +00:00
|
|
|
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]);
|
|
|
|
}
|
2014-12-07 06:13:44 +00:00
|
|
|
|
|
|
|
edev->arg = edev;
|
|
|
|
|
|
|
|
edev->attach = attach;
|
|
|
|
edev->shutdown = shutdown;
|
|
|
|
edev->ifstat = ifstat;
|
2014-12-08 18:19:53 +00:00
|
|
|
|
|
|
|
if((ctlr->feat & (Fctrlvq|Fctrlrx)) == (Fctrlvq|Fctrlrx)){
|
|
|
|
edev->multicast = multicast;
|
|
|
|
edev->promiscuous = promiscuous;
|
|
|
|
}
|
2014-12-07 06:13:44 +00:00
|
|
|
|
2018-02-11 17:08:03 +00:00
|
|
|
intrenable(edev->irq, interrupt, edev, edev->tbdf, edev->name);
|
|
|
|
|
2014-12-07 06:13:44 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
ethervirtiolink(void)
|
|
|
|
{
|
2014-12-08 18:19:53 +00:00
|
|
|
addethercard("virtio", reset);
|
2014-12-07 06:13:44 +00:00
|
|
|
}
|
|
|
|
|