1*9550e22eSDavid du Colombier /*
2*9550e22eSDavid du Colombier * virtio ethernet driver implementing the legacy interface:
3*9550e22eSDavid du Colombier * http://docs.oasis-open.org/virtio/virtio/v1.0/virtio-v1.0.html
4*9550e22eSDavid du Colombier */
5*9550e22eSDavid du Colombier #include "u.h"
6*9550e22eSDavid du Colombier #include "../port/lib.h"
7*9550e22eSDavid du Colombier #include "mem.h"
8*9550e22eSDavid du Colombier #include "dat.h"
9*9550e22eSDavid du Colombier #include "fns.h"
10*9550e22eSDavid du Colombier #include "io.h"
11*9550e22eSDavid du Colombier #include "../port/error.h"
12*9550e22eSDavid du Colombier #include "../port/netif.h"
13*9550e22eSDavid du Colombier #include "etherif.h"
14*9550e22eSDavid du Colombier
15*9550e22eSDavid du Colombier typedef struct Vring Vring;
16*9550e22eSDavid du Colombier typedef struct Vdesc Vdesc;
17*9550e22eSDavid du Colombier typedef struct Vused Vused;
18*9550e22eSDavid du Colombier typedef struct Vheader Vheader;
19*9550e22eSDavid du Colombier typedef struct Vqueue Vqueue;
20*9550e22eSDavid du Colombier typedef struct Ctlr Ctlr;
21*9550e22eSDavid du Colombier
22*9550e22eSDavid du Colombier enum {
23*9550e22eSDavid du Colombier /* §2.1 Device Status Field */
24*9550e22eSDavid du Colombier Sacknowledge = 1,
25*9550e22eSDavid du Colombier Sdriver = 2,
26*9550e22eSDavid du Colombier Sdriverok = 4,
27*9550e22eSDavid du Colombier Sfeatureok = 8,
28*9550e22eSDavid du Colombier Sfailed = 128,
29*9550e22eSDavid du Colombier
30*9550e22eSDavid du Colombier /* §4.1.4.8 Legacy Interfaces: A Note on PCI Device Layout */
31*9550e22eSDavid du Colombier Qdevfeat = 0,
32*9550e22eSDavid du Colombier Qdrvfeat = 4,
33*9550e22eSDavid du Colombier Qaddr = 8,
34*9550e22eSDavid du Colombier Qsize = 12,
35*9550e22eSDavid du Colombier Qselect = 14,
36*9550e22eSDavid du Colombier Qnotify = 16,
37*9550e22eSDavid du Colombier Qstatus = 18,
38*9550e22eSDavid du Colombier Qisr = 19,
39*9550e22eSDavid du Colombier Qmac = 20,
40*9550e22eSDavid du Colombier Qnetstatus = 26,
41*9550e22eSDavid du Colombier
42*9550e22eSDavid du Colombier /* flags in Qnetstatus */
43*9550e22eSDavid du Colombier Nlinkup = (1<<0),
44*9550e22eSDavid du Colombier Nannounce = (1<<1),
45*9550e22eSDavid du Colombier
46*9550e22eSDavid du Colombier /* feature bits */
47*9550e22eSDavid du Colombier Fmac = (1<<5),
48*9550e22eSDavid du Colombier Fstatus = (1<<16),
49*9550e22eSDavid du Colombier Fctrlvq = (1<<17),
50*9550e22eSDavid du Colombier Fctrlrx = (1<<18),
51*9550e22eSDavid du Colombier
52*9550e22eSDavid du Colombier /* vring used flags */
53*9550e22eSDavid du Colombier Unonotify = 1,
54*9550e22eSDavid du Colombier /* vring avail flags */
55*9550e22eSDavid du Colombier Rnointerrupt = 1,
56*9550e22eSDavid du Colombier
57*9550e22eSDavid du Colombier /* descriptor flags */
58*9550e22eSDavid du Colombier Dnext = 1,
59*9550e22eSDavid du Colombier Dwrite = 2,
60*9550e22eSDavid du Colombier Dindirect = 4,
61*9550e22eSDavid du Colombier
62*9550e22eSDavid du Colombier /* struct sizes */
63*9550e22eSDavid du Colombier VringSize = 4,
64*9550e22eSDavid du Colombier VdescSize = 16,
65*9550e22eSDavid du Colombier VusedSize = 8,
66*9550e22eSDavid du Colombier VheaderSize = 10,
67*9550e22eSDavid du Colombier
68*9550e22eSDavid du Colombier /* §4.1.5.1.4.1 says pages are 4096 bytes
69*9550e22eSDavid du Colombier * for the purposes of the driver.
70*9550e22eSDavid du Colombier */
71*9550e22eSDavid du Colombier VBY2PG = 4096,
72*9550e22eSDavid du Colombier #define VPGROUND(s) ROUND(s, VBY2PG)
73*9550e22eSDavid du Colombier
74*9550e22eSDavid du Colombier Vrxq = 0,
75*9550e22eSDavid du Colombier Vtxq = 1,
76*9550e22eSDavid du Colombier Vctlq = 2,
77*9550e22eSDavid du Colombier
78*9550e22eSDavid du Colombier /* class/cmd for Vctlq */
79*9550e22eSDavid du Colombier CtrlRx = 0x00,
80*9550e22eSDavid du Colombier CmdPromisc = 0x00,
81*9550e22eSDavid du Colombier CmdAllmulti = 0x01,
82*9550e22eSDavid du Colombier CtrlMac = 0x01,
83*9550e22eSDavid du Colombier CmdMacTableSet = 0x00,
84*9550e22eSDavid du Colombier CtrlVlan= 0x02,
85*9550e22eSDavid du Colombier CmdVlanAdd = 0x00,
86*9550e22eSDavid du Colombier CmdVlanDel = 0x01,
87*9550e22eSDavid du Colombier };
88*9550e22eSDavid du Colombier
89*9550e22eSDavid du Colombier struct Vring
90*9550e22eSDavid du Colombier {
91*9550e22eSDavid du Colombier u16int flags;
92*9550e22eSDavid du Colombier u16int idx;
93*9550e22eSDavid du Colombier };
94*9550e22eSDavid du Colombier
95*9550e22eSDavid du Colombier struct Vdesc
96*9550e22eSDavid du Colombier {
97*9550e22eSDavid du Colombier u64int addr;
98*9550e22eSDavid du Colombier u32int len;
99*9550e22eSDavid du Colombier u16int flags;
100*9550e22eSDavid du Colombier u16int next;
101*9550e22eSDavid du Colombier };
102*9550e22eSDavid du Colombier
103*9550e22eSDavid du Colombier struct Vused
104*9550e22eSDavid du Colombier {
105*9550e22eSDavid du Colombier u32int id;
106*9550e22eSDavid du Colombier u32int len;
107*9550e22eSDavid du Colombier };
108*9550e22eSDavid du Colombier
109*9550e22eSDavid du Colombier struct Vheader
110*9550e22eSDavid du Colombier {
111*9550e22eSDavid du Colombier u8int flags;
112*9550e22eSDavid du Colombier u8int segtype;
113*9550e22eSDavid du Colombier u16int hlen;
114*9550e22eSDavid du Colombier u16int seglen;
115*9550e22eSDavid du Colombier u16int csumstart;
116*9550e22eSDavid du Colombier u16int csumend;
117*9550e22eSDavid du Colombier };
118*9550e22eSDavid du Colombier
119*9550e22eSDavid du Colombier /* §2.4 Virtqueues */
120*9550e22eSDavid du Colombier struct Vqueue
121*9550e22eSDavid du Colombier {
122*9550e22eSDavid du Colombier Rendez;
123*9550e22eSDavid du Colombier
124*9550e22eSDavid du Colombier uint qsize;
125*9550e22eSDavid du Colombier uint qmask;
126*9550e22eSDavid du Colombier
127*9550e22eSDavid du Colombier Vdesc *desc;
128*9550e22eSDavid du Colombier
129*9550e22eSDavid du Colombier Vring *avail;
130*9550e22eSDavid du Colombier u16int *availent;
131*9550e22eSDavid du Colombier u16int *availevent;
132*9550e22eSDavid du Colombier
133*9550e22eSDavid du Colombier Vring *used;
134*9550e22eSDavid du Colombier Vused *usedent;
135*9550e22eSDavid du Colombier u16int *usedevent;
136*9550e22eSDavid du Colombier u16int lastused;
137*9550e22eSDavid du Colombier
138*9550e22eSDavid du Colombier uint nintr;
139*9550e22eSDavid du Colombier uint nnote;
140*9550e22eSDavid du Colombier };
141*9550e22eSDavid du Colombier
142*9550e22eSDavid du Colombier struct Ctlr {
143*9550e22eSDavid du Colombier Lock;
144*9550e22eSDavid du Colombier
145*9550e22eSDavid du Colombier QLock ctllock;
146*9550e22eSDavid du Colombier
147*9550e22eSDavid du Colombier int attached;
148*9550e22eSDavid du Colombier
149*9550e22eSDavid du Colombier int port;
150*9550e22eSDavid du Colombier Pcidev *pcidev;
151*9550e22eSDavid du Colombier Ctlr *next;
152*9550e22eSDavid du Colombier int active;
153*9550e22eSDavid du Colombier int id;
154*9550e22eSDavid du Colombier int typ;
155*9550e22eSDavid du Colombier ulong feat;
156*9550e22eSDavid du Colombier int nqueue;
157*9550e22eSDavid du Colombier
158*9550e22eSDavid du Colombier /* virtioether has 3 queues: rx, tx and ctl */
159*9550e22eSDavid du Colombier Vqueue queue[3];
160*9550e22eSDavid du Colombier };
161*9550e22eSDavid du Colombier
162*9550e22eSDavid du Colombier static Ctlr *ctlrhead;
163*9550e22eSDavid du Colombier
164*9550e22eSDavid du Colombier static int
vhasroom(void * v)165*9550e22eSDavid du Colombier vhasroom(void *v)
166*9550e22eSDavid du Colombier {
167*9550e22eSDavid du Colombier Vqueue *q = v;
168*9550e22eSDavid du Colombier return q->lastused != q->used->idx;
169*9550e22eSDavid du Colombier }
170*9550e22eSDavid du Colombier
171*9550e22eSDavid du Colombier static void
vqnotify(Ctlr * ctlr,int x)172*9550e22eSDavid du Colombier vqnotify(Ctlr *ctlr, int x)
173*9550e22eSDavid du Colombier {
174*9550e22eSDavid du Colombier Vqueue *q;
175*9550e22eSDavid du Colombier
176*9550e22eSDavid du Colombier coherence();
177*9550e22eSDavid du Colombier q = &ctlr->queue[x];
178*9550e22eSDavid du Colombier if(q->used->flags & Unonotify)
179*9550e22eSDavid du Colombier return;
180*9550e22eSDavid du Colombier q->nnote++;
181*9550e22eSDavid du Colombier outs(ctlr->port+Qnotify, x);
182*9550e22eSDavid du Colombier }
183*9550e22eSDavid du Colombier
184*9550e22eSDavid du Colombier static void
txproc(void * v)185*9550e22eSDavid du Colombier txproc(void *v)
186*9550e22eSDavid du Colombier {
187*9550e22eSDavid du Colombier Vheader *header;
188*9550e22eSDavid du Colombier Block **blocks;
189*9550e22eSDavid du Colombier Ether *edev;
190*9550e22eSDavid du Colombier Ctlr *ctlr;
191*9550e22eSDavid du Colombier Vqueue *q;
192*9550e22eSDavid du Colombier Vused *u;
193*9550e22eSDavid du Colombier Block *b;
194*9550e22eSDavid du Colombier int i, j;
195*9550e22eSDavid du Colombier
196*9550e22eSDavid du Colombier edev = v;
197*9550e22eSDavid du Colombier ctlr = edev->ctlr;
198*9550e22eSDavid du Colombier q = &ctlr->queue[Vtxq];
199*9550e22eSDavid du Colombier
200*9550e22eSDavid du Colombier header = smalloc(VheaderSize);
201*9550e22eSDavid du Colombier blocks = smalloc(sizeof(Block*) * (q->qsize/2));
202*9550e22eSDavid du Colombier
203*9550e22eSDavid du Colombier for(i = 0; i < q->qsize/2; i++){
204*9550e22eSDavid du Colombier j = i << 1;
205*9550e22eSDavid du Colombier q->desc[j].addr = PADDR(header);
206*9550e22eSDavid du Colombier q->desc[j].len = VheaderSize;
207*9550e22eSDavid du Colombier q->desc[j].next = j | 1;
208*9550e22eSDavid du Colombier q->desc[j].flags = Dnext;
209*9550e22eSDavid du Colombier
210*9550e22eSDavid du Colombier q->availent[i] = q->availent[i + q->qsize/2] = j;
211*9550e22eSDavid du Colombier
212*9550e22eSDavid du Colombier j |= 1;
213*9550e22eSDavid du Colombier q->desc[j].next = 0;
214*9550e22eSDavid du Colombier q->desc[j].flags = 0;
215*9550e22eSDavid du Colombier }
216*9550e22eSDavid du Colombier
217*9550e22eSDavid du Colombier q->avail->flags &= ~Rnointerrupt;
218*9550e22eSDavid du Colombier
219*9550e22eSDavid du Colombier while(waserror())
220*9550e22eSDavid du Colombier ;
221*9550e22eSDavid du Colombier
222*9550e22eSDavid du Colombier while((b = qbread(edev->oq, 1000000)) != nil){
223*9550e22eSDavid du Colombier for(;;){
224*9550e22eSDavid du Colombier /* retire completed packets */
225*9550e22eSDavid du Colombier while((i = q->lastused) != q->used->idx){
226*9550e22eSDavid du Colombier u = &q->usedent[i & q->qmask];
227*9550e22eSDavid du Colombier i = (u->id & q->qmask) >> 1;
228*9550e22eSDavid du Colombier if(blocks[i] == nil)
229*9550e22eSDavid du Colombier break;
230*9550e22eSDavid du Colombier freeb(blocks[i]);
231*9550e22eSDavid du Colombier blocks[i] = nil;
232*9550e22eSDavid du Colombier q->lastused++;
233*9550e22eSDavid du Colombier }
234*9550e22eSDavid du Colombier
235*9550e22eSDavid du Colombier /* have free slot? */
236*9550e22eSDavid du Colombier i = q->avail->idx & (q->qmask >> 1);
237*9550e22eSDavid du Colombier if(blocks[i] == nil)
238*9550e22eSDavid du Colombier break;
239*9550e22eSDavid du Colombier
240*9550e22eSDavid du Colombier /* ring full, wait and retry */
241*9550e22eSDavid du Colombier if(!vhasroom(q))
242*9550e22eSDavid du Colombier sleep(q, vhasroom, q);
243*9550e22eSDavid du Colombier }
244*9550e22eSDavid du Colombier
245*9550e22eSDavid du Colombier /* slot is free, fill in descriptor */
246*9550e22eSDavid du Colombier blocks[i] = b;
247*9550e22eSDavid du Colombier j = (i << 1) | 1;
248*9550e22eSDavid du Colombier q->desc[j].addr = PADDR(b->rp);
249*9550e22eSDavid du Colombier q->desc[j].len = BLEN(b);
250*9550e22eSDavid du Colombier coherence();
251*9550e22eSDavid du Colombier q->avail->idx++;
252*9550e22eSDavid du Colombier vqnotify(ctlr, Vtxq);
253*9550e22eSDavid du Colombier }
254*9550e22eSDavid du Colombier
255*9550e22eSDavid du Colombier pexit("ether out queue closed", 1);
256*9550e22eSDavid du Colombier }
257*9550e22eSDavid du Colombier
258*9550e22eSDavid du Colombier static void
rxproc(void * v)259*9550e22eSDavid du Colombier rxproc(void *v)
260*9550e22eSDavid du Colombier {
261*9550e22eSDavid du Colombier Vheader *header;
262*9550e22eSDavid du Colombier Block **blocks;
263*9550e22eSDavid du Colombier Ether *edev;
264*9550e22eSDavid du Colombier Ctlr *ctlr;
265*9550e22eSDavid du Colombier Vqueue *q;
266*9550e22eSDavid du Colombier Vused *u;
267*9550e22eSDavid du Colombier Block *b;
268*9550e22eSDavid du Colombier int i, j;
269*9550e22eSDavid du Colombier
270*9550e22eSDavid du Colombier edev = v;
271*9550e22eSDavid du Colombier ctlr = edev->ctlr;
272*9550e22eSDavid du Colombier q = &ctlr->queue[Vrxq];
273*9550e22eSDavid du Colombier
274*9550e22eSDavid du Colombier header = smalloc(VheaderSize);
275*9550e22eSDavid du Colombier blocks = smalloc(sizeof(Block*) * (q->qsize/2));
276*9550e22eSDavid du Colombier
277*9550e22eSDavid du Colombier for(i = 0; i < q->qsize/2; i++){
278*9550e22eSDavid du Colombier j = i << 1;
279*9550e22eSDavid du Colombier q->desc[j].addr = PADDR(header);
280*9550e22eSDavid du Colombier q->desc[j].len = VheaderSize;
281*9550e22eSDavid du Colombier q->desc[j].next = j | 1;
282*9550e22eSDavid du Colombier q->desc[j].flags = Dwrite|Dnext;
283*9550e22eSDavid du Colombier
284*9550e22eSDavid du Colombier q->availent[i] = q->availent[i + q->qsize/2] = j;
285*9550e22eSDavid du Colombier
286*9550e22eSDavid du Colombier j |= 1;
287*9550e22eSDavid du Colombier q->desc[j].next = 0;
288*9550e22eSDavid du Colombier q->desc[j].flags = Dwrite;
289*9550e22eSDavid du Colombier }
290*9550e22eSDavid du Colombier
291*9550e22eSDavid du Colombier q->avail->flags &= ~Rnointerrupt;
292*9550e22eSDavid du Colombier
293*9550e22eSDavid du Colombier while(waserror())
294*9550e22eSDavid du Colombier ;
295*9550e22eSDavid du Colombier
296*9550e22eSDavid du Colombier for(;;){
297*9550e22eSDavid du Colombier /* replenish receive ring */
298*9550e22eSDavid du Colombier do {
299*9550e22eSDavid du Colombier i = q->avail->idx & (q->qmask >> 1);
300*9550e22eSDavid du Colombier if(blocks[i] != nil)
301*9550e22eSDavid du Colombier break;
302*9550e22eSDavid du Colombier if((b = iallocb(ETHERMAXTU)) == nil)
303*9550e22eSDavid du Colombier break;
304*9550e22eSDavid du Colombier blocks[i] = b;
305*9550e22eSDavid du Colombier j = (i << 1) | 1;
306*9550e22eSDavid du Colombier q->desc[j].addr = PADDR(b->rp);
307*9550e22eSDavid du Colombier q->desc[j].len = BALLOC(b);
308*9550e22eSDavid du Colombier coherence();
309*9550e22eSDavid du Colombier q->avail->idx++;
310*9550e22eSDavid du Colombier } while(q->avail->idx != q->used->idx);
311*9550e22eSDavid du Colombier vqnotify(ctlr, Vrxq);
312*9550e22eSDavid du Colombier
313*9550e22eSDavid du Colombier /* wait for any packets to complete */
314*9550e22eSDavid du Colombier if(!vhasroom(q))
315*9550e22eSDavid du Colombier sleep(q, vhasroom, q);
316*9550e22eSDavid du Colombier
317*9550e22eSDavid du Colombier /* retire completed packets */
318*9550e22eSDavid du Colombier while((i = q->lastused) != q->used->idx) {
319*9550e22eSDavid du Colombier u = &q->usedent[i & q->qmask];
320*9550e22eSDavid du Colombier i = (u->id & q->qmask) >> 1;
321*9550e22eSDavid du Colombier if((b = blocks[i]) == nil)
322*9550e22eSDavid du Colombier break;
323*9550e22eSDavid du Colombier
324*9550e22eSDavid du Colombier blocks[i] = nil;
325*9550e22eSDavid du Colombier
326*9550e22eSDavid du Colombier b->wp = b->rp + u->len - VheaderSize;
327*9550e22eSDavid du Colombier etheriq(edev, b, 1);
328*9550e22eSDavid du Colombier q->lastused++;
329*9550e22eSDavid du Colombier }
330*9550e22eSDavid du Colombier }
331*9550e22eSDavid du Colombier }
332*9550e22eSDavid du Colombier
333*9550e22eSDavid du Colombier static int
vctlcmd(Ether * edev,uchar class,uchar cmd,uchar * data,int ndata)334*9550e22eSDavid du Colombier vctlcmd(Ether *edev, uchar class, uchar cmd, uchar *data, int ndata)
335*9550e22eSDavid du Colombier {
336*9550e22eSDavid du Colombier uchar hdr[2], ack[1];
337*9550e22eSDavid du Colombier Ctlr *ctlr;
338*9550e22eSDavid du Colombier Vqueue *q;
339*9550e22eSDavid du Colombier Vdesc *d;
340*9550e22eSDavid du Colombier int i;
341*9550e22eSDavid du Colombier
342*9550e22eSDavid du Colombier ctlr = edev->ctlr;
343*9550e22eSDavid du Colombier q = &ctlr->queue[Vctlq];
344*9550e22eSDavid du Colombier if(q->qsize < 3)
345*9550e22eSDavid du Colombier return -1;
346*9550e22eSDavid du Colombier
347*9550e22eSDavid du Colombier qlock(&ctlr->ctllock);
348*9550e22eSDavid du Colombier while(waserror())
349*9550e22eSDavid du Colombier ;
350*9550e22eSDavid du Colombier
351*9550e22eSDavid du Colombier ack[0] = 0x55;
352*9550e22eSDavid du Colombier hdr[0] = class;
353*9550e22eSDavid du Colombier hdr[1] = cmd;
354*9550e22eSDavid du Colombier
355*9550e22eSDavid du Colombier d = &q->desc[0];
356*9550e22eSDavid du Colombier d->addr = PADDR(hdr);
357*9550e22eSDavid du Colombier d->len = sizeof(hdr);
358*9550e22eSDavid du Colombier d->next = 1;
359*9550e22eSDavid du Colombier d->flags = Dnext;
360*9550e22eSDavid du Colombier d++;
361*9550e22eSDavid du Colombier d->addr = PADDR(data);
362*9550e22eSDavid du Colombier d->len = ndata;
363*9550e22eSDavid du Colombier d->next = 2;
364*9550e22eSDavid du Colombier d->flags = Dnext;
365*9550e22eSDavid du Colombier d++;
366*9550e22eSDavid du Colombier d->addr = PADDR(ack);
367*9550e22eSDavid du Colombier d->len = sizeof(ack);
368*9550e22eSDavid du Colombier d->next = 0;
369*9550e22eSDavid du Colombier d->flags = Dwrite;
370*9550e22eSDavid du Colombier
371*9550e22eSDavid du Colombier i = q->avail->idx & q->qmask;
372*9550e22eSDavid du Colombier q->availent[i] = 0;
373*9550e22eSDavid du Colombier coherence();
374*9550e22eSDavid du Colombier
375*9550e22eSDavid du Colombier q->avail->flags &= ~Rnointerrupt;
376*9550e22eSDavid du Colombier q->avail->idx++;
377*9550e22eSDavid du Colombier vqnotify(ctlr, Vctlq);
378*9550e22eSDavid du Colombier while(!vhasroom(q))
379*9550e22eSDavid du Colombier sleep(q, vhasroom, q);
380*9550e22eSDavid du Colombier q->lastused = q->used->idx;
381*9550e22eSDavid du Colombier q->avail->flags |= Rnointerrupt;
382*9550e22eSDavid du Colombier
383*9550e22eSDavid du Colombier qunlock(&ctlr->ctllock);
384*9550e22eSDavid du Colombier poperror();
385*9550e22eSDavid du Colombier
386*9550e22eSDavid du Colombier if(ack[0] != 0)
387*9550e22eSDavid du Colombier print("#l%d: vctlcmd: %ux.%ux -> %ux\n", edev->ctlrno, class, cmd, ack[0]);
388*9550e22eSDavid du Colombier
389*9550e22eSDavid du Colombier return ack[0];
390*9550e22eSDavid du Colombier }
391*9550e22eSDavid du Colombier
392*9550e22eSDavid du Colombier static void
interrupt(Ureg *,void * arg)393*9550e22eSDavid du Colombier interrupt(Ureg*, void* arg)
394*9550e22eSDavid du Colombier {
395*9550e22eSDavid du Colombier Ether *edev;
396*9550e22eSDavid du Colombier Ctlr *ctlr;
397*9550e22eSDavid du Colombier Vqueue *q;
398*9550e22eSDavid du Colombier int i;
399*9550e22eSDavid du Colombier
400*9550e22eSDavid du Colombier edev = arg;
401*9550e22eSDavid du Colombier ctlr = edev->ctlr;
402*9550e22eSDavid du Colombier if(inb(ctlr->port+Qisr) & 1){
403*9550e22eSDavid du Colombier for(i = 0; i < ctlr->nqueue; i++){
404*9550e22eSDavid du Colombier q = &ctlr->queue[i];
405*9550e22eSDavid du Colombier if(vhasroom(q)){
406*9550e22eSDavid du Colombier q->nintr++;
407*9550e22eSDavid du Colombier wakeup(q);
408*9550e22eSDavid du Colombier }
409*9550e22eSDavid du Colombier }
410*9550e22eSDavid du Colombier }
411*9550e22eSDavid du Colombier }
412*9550e22eSDavid du Colombier
413*9550e22eSDavid du Colombier static void
attach(Ether * edev)414*9550e22eSDavid du Colombier attach(Ether* edev)
415*9550e22eSDavid du Colombier {
416*9550e22eSDavid du Colombier char name[KNAMELEN];
417*9550e22eSDavid du Colombier Ctlr* ctlr;
418*9550e22eSDavid du Colombier
419*9550e22eSDavid du Colombier ctlr = edev->ctlr;
420*9550e22eSDavid du Colombier lock(ctlr);
421*9550e22eSDavid du Colombier if(ctlr->attached){
422*9550e22eSDavid du Colombier unlock(ctlr);
423*9550e22eSDavid du Colombier return;
424*9550e22eSDavid du Colombier }
425*9550e22eSDavid du Colombier ctlr->attached = 1;
426*9550e22eSDavid du Colombier unlock(ctlr);
427*9550e22eSDavid du Colombier
428*9550e22eSDavid du Colombier /* ready to go */
429*9550e22eSDavid du Colombier outb(ctlr->port+Qstatus, inb(ctlr->port+Qstatus) | Sdriverok);
430*9550e22eSDavid du Colombier
431*9550e22eSDavid du Colombier /* start kprocs */
432*9550e22eSDavid du Colombier snprint(name, sizeof name, "#l%drx", edev->ctlrno);
433*9550e22eSDavid du Colombier kproc(name, rxproc, edev);
434*9550e22eSDavid du Colombier snprint(name, sizeof name, "#l%dtx", edev->ctlrno);
435*9550e22eSDavid du Colombier kproc(name, txproc, edev);
436*9550e22eSDavid du Colombier }
437*9550e22eSDavid du Colombier
438*9550e22eSDavid du Colombier static long
ifstat(Ether * edev,void * a,long n,ulong offset)439*9550e22eSDavid du Colombier ifstat(Ether *edev, void *a, long n, ulong offset)
440*9550e22eSDavid du Colombier {
441*9550e22eSDavid du Colombier int i, l;
442*9550e22eSDavid du Colombier char *p;
443*9550e22eSDavid du Colombier Ctlr *ctlr;
444*9550e22eSDavid du Colombier Vqueue *q;
445*9550e22eSDavid du Colombier
446*9550e22eSDavid du Colombier ctlr = edev->ctlr;
447*9550e22eSDavid du Colombier
448*9550e22eSDavid du Colombier p = smalloc(READSTR);
449*9550e22eSDavid du Colombier
450*9550e22eSDavid du Colombier l = snprint(p, READSTR, "devfeat %4.4luX\n", ctlr->feat);
451*9550e22eSDavid du Colombier l += snprint(p+l, READSTR-l, "drvfeat %4.4luX\n", inl(ctlr->port+Qdrvfeat));
452*9550e22eSDavid du Colombier l += snprint(p+l, READSTR-l, "devstatus %uX\n", inb(ctlr->port+Qstatus));
453*9550e22eSDavid du Colombier if(ctlr->feat & Fstatus)
454*9550e22eSDavid du Colombier l += snprint(p+l, READSTR-l, "netstatus %uX\n", inb(ctlr->port+Qnetstatus));
455*9550e22eSDavid du Colombier
456*9550e22eSDavid du Colombier for(i = 0; i < ctlr->nqueue; i++){
457*9550e22eSDavid du Colombier q = &ctlr->queue[i];
458*9550e22eSDavid du Colombier l += snprint(p+l, READSTR-l,
459*9550e22eSDavid du Colombier "vq%d %#p size %d avail->idx %d used->idx %d lastused %hud nintr %ud nnote %ud\n",
460*9550e22eSDavid du Colombier i, q, q->qsize, q->avail->idx, q->used->idx, q->lastused, q->nintr, q->nnote);
461*9550e22eSDavid du Colombier }
462*9550e22eSDavid du Colombier
463*9550e22eSDavid du Colombier n = readstr(offset, a, n, p);
464*9550e22eSDavid du Colombier free(p);
465*9550e22eSDavid du Colombier
466*9550e22eSDavid du Colombier return n;
467*9550e22eSDavid du Colombier }
468*9550e22eSDavid du Colombier
469*9550e22eSDavid du Colombier static void
shutdown(Ether * edev)470*9550e22eSDavid du Colombier shutdown(Ether* edev)
471*9550e22eSDavid du Colombier {
472*9550e22eSDavid du Colombier Ctlr *ctlr = edev->ctlr;
473*9550e22eSDavid du Colombier outb(ctlr->port+Qstatus, 0);
474*9550e22eSDavid du Colombier pciclrbme(ctlr->pcidev);
475*9550e22eSDavid du Colombier }
476*9550e22eSDavid du Colombier
477*9550e22eSDavid du Colombier static void
promiscuous(void * arg,int on)478*9550e22eSDavid du Colombier promiscuous(void *arg, int on)
479*9550e22eSDavid du Colombier {
480*9550e22eSDavid du Colombier Ether *edev = arg;
481*9550e22eSDavid du Colombier uchar b[1];
482*9550e22eSDavid du Colombier
483*9550e22eSDavid du Colombier b[0] = on != 0;
484*9550e22eSDavid du Colombier vctlcmd(edev, CtrlRx, CmdPromisc, b, sizeof(b));
485*9550e22eSDavid du Colombier }
486*9550e22eSDavid du Colombier
487*9550e22eSDavid du Colombier static void
multicast(void * arg,uchar *,int)488*9550e22eSDavid du Colombier multicast(void *arg, uchar*, int)
489*9550e22eSDavid du Colombier {
490*9550e22eSDavid du Colombier Ether *edev = arg;
491*9550e22eSDavid du Colombier uchar b[1];
492*9550e22eSDavid du Colombier
493*9550e22eSDavid du Colombier b[0] = edev->nmaddr > 0;
494*9550e22eSDavid du Colombier vctlcmd(edev, CtrlRx, CmdAllmulti, b, sizeof(b));
495*9550e22eSDavid du Colombier }
496*9550e22eSDavid du Colombier
497*9550e22eSDavid du Colombier /* §2.4.2 Legacy Interfaces: A Note on Virtqueue Layout */
498*9550e22eSDavid du Colombier static ulong
queuesize(ulong size)499*9550e22eSDavid du Colombier queuesize(ulong size)
500*9550e22eSDavid du Colombier {
501*9550e22eSDavid du Colombier return VPGROUND(VdescSize*size + sizeof(u16int)*(3+size))
502*9550e22eSDavid du Colombier + VPGROUND(sizeof(u16int)*3 + VusedSize*size);
503*9550e22eSDavid du Colombier }
504*9550e22eSDavid du Colombier
505*9550e22eSDavid du Colombier static int
initqueue(Vqueue * q,int size)506*9550e22eSDavid du Colombier initqueue(Vqueue *q, int size)
507*9550e22eSDavid du Colombier {
508*9550e22eSDavid du Colombier uchar *p;
509*9550e22eSDavid du Colombier
510*9550e22eSDavid du Colombier /* §2.4: Queue Size value is always a power of 2 and <= 32768 */
511*9550e22eSDavid du Colombier assert(!(size & (size - 1)) && size <= 32768);
512*9550e22eSDavid du Colombier
513*9550e22eSDavid du Colombier p = mallocalign(queuesize(size), VBY2PG, 0, 0);
514*9550e22eSDavid du Colombier if(p == nil){
515*9550e22eSDavid du Colombier print("ethervirtio: no memory for Vqueue\n");
516*9550e22eSDavid du Colombier free(p);
517*9550e22eSDavid du Colombier return -1;
518*9550e22eSDavid du Colombier }
519*9550e22eSDavid du Colombier
520*9550e22eSDavid du Colombier q->desc = (void*)p;
521*9550e22eSDavid du Colombier p += VdescSize*size;
522*9550e22eSDavid du Colombier q->avail = (void*)p;
523*9550e22eSDavid du Colombier p += VringSize;
524*9550e22eSDavid du Colombier q->availent = (void*)p;
525*9550e22eSDavid du Colombier p += sizeof(u16int)*size;
526*9550e22eSDavid du Colombier q->availevent = (void*)p;
527*9550e22eSDavid du Colombier p += sizeof(u16int);
528*9550e22eSDavid du Colombier
529*9550e22eSDavid du Colombier p = (uchar*)VPGROUND((uintptr)p);
530*9550e22eSDavid du Colombier q->used = (void*)p;
531*9550e22eSDavid du Colombier p += VringSize;
532*9550e22eSDavid du Colombier q->usedent = (void*)p;
533*9550e22eSDavid du Colombier p += VusedSize*size;
534*9550e22eSDavid du Colombier q->usedevent = (void*)p;
535*9550e22eSDavid du Colombier
536*9550e22eSDavid du Colombier q->qsize = size;
537*9550e22eSDavid du Colombier q->qmask = q->qsize - 1;
538*9550e22eSDavid du Colombier
539*9550e22eSDavid du Colombier q->lastused = q->avail->idx = q->used->idx = 0;
540*9550e22eSDavid du Colombier
541*9550e22eSDavid du Colombier q->avail->flags |= Rnointerrupt;
542*9550e22eSDavid du Colombier
543*9550e22eSDavid du Colombier return 0;
544*9550e22eSDavid du Colombier }
545*9550e22eSDavid du Colombier
546*9550e22eSDavid du Colombier static Ctlr*
pciprobe(int typ)547*9550e22eSDavid du Colombier pciprobe(int typ)
548*9550e22eSDavid du Colombier {
549*9550e22eSDavid du Colombier Ctlr *c, *h, *t;
550*9550e22eSDavid du Colombier Pcidev *p;
551*9550e22eSDavid du Colombier int n, i;
552*9550e22eSDavid du Colombier
553*9550e22eSDavid du Colombier h = t = nil;
554*9550e22eSDavid du Colombier
555*9550e22eSDavid du Colombier /* §4.1.2 PCI Device Discovery */
556*9550e22eSDavid du Colombier for(p = nil; p = pcimatch(p, 0x1AF4, 0);){
557*9550e22eSDavid du Colombier /* the two possible DIDs for virtio-net */
558*9550e22eSDavid du Colombier if(p->did != 0x1000 && p->did != 0x1041)
559*9550e22eSDavid du Colombier continue;
560*9550e22eSDavid du Colombier /*
561*9550e22eSDavid du Colombier * non-transitional devices will have a revision > 0,
562*9550e22eSDavid du Colombier * these are handled by ethervirtio10 driver.
563*9550e22eSDavid du Colombier */
564*9550e22eSDavid du Colombier if(p->rid != 0)
565*9550e22eSDavid du Colombier continue;
566*9550e22eSDavid du Colombier /* first membar needs to be I/O */
567*9550e22eSDavid du Colombier if((p->mem[0].bar & 1) == 0)
568*9550e22eSDavid du Colombier continue;
569*9550e22eSDavid du Colombier /* non-transitional device will have typ+0x40 */
570*9550e22eSDavid du Colombier if(pcicfgr16(p, 0x2E) != typ)
571*9550e22eSDavid du Colombier continue;
572*9550e22eSDavid du Colombier if((c = mallocz(sizeof(Ctlr), 1)) == nil){
573*9550e22eSDavid du Colombier print("ethervirtio: no memory for Ctlr\n");
574*9550e22eSDavid du Colombier break;
575*9550e22eSDavid du Colombier }
576*9550e22eSDavid du Colombier c->port = p->mem[0].bar & ~3;
577*9550e22eSDavid du Colombier if(ioalloc(c->port, p->mem[0].size, 0, "ethervirtio") < 0){
578*9550e22eSDavid du Colombier print("ethervirtio: port %ux in use\n", c->port);
579*9550e22eSDavid du Colombier free(c);
580*9550e22eSDavid du Colombier continue;
581*9550e22eSDavid du Colombier }
582*9550e22eSDavid du Colombier
583*9550e22eSDavid du Colombier c->typ = typ;
584*9550e22eSDavid du Colombier c->pcidev = p;
585*9550e22eSDavid du Colombier c->id = (p->did<<16)|p->vid;
586*9550e22eSDavid du Colombier
587*9550e22eSDavid du Colombier /* §3.1.2 Legacy Device Initialization */
588*9550e22eSDavid du Colombier outb(c->port+Qstatus, 0);
589*9550e22eSDavid du Colombier while(inb(c->port+Qstatus) != 0)
590*9550e22eSDavid du Colombier delay(1);
591*9550e22eSDavid du Colombier outb(c->port+Qstatus, Sacknowledge|Sdriver);
592*9550e22eSDavid du Colombier
593*9550e22eSDavid du Colombier /* negotiate feature bits */
594*9550e22eSDavid du Colombier c->feat = inl(c->port+Qdevfeat);
595*9550e22eSDavid du Colombier outl(c->port+Qdrvfeat, c->feat & (Fmac|Fstatus|Fctrlvq|Fctrlrx));
596*9550e22eSDavid du Colombier
597*9550e22eSDavid du Colombier /* §4.1.5.1.4 Virtqueue Configuration */
598*9550e22eSDavid du Colombier for(i=0; i<nelem(c->queue); i++){
599*9550e22eSDavid du Colombier outs(c->port+Qselect, i);
600*9550e22eSDavid du Colombier n = ins(c->port+Qsize);
601*9550e22eSDavid du Colombier if(n == 0 || (n & (n-1)) != 0){
602*9550e22eSDavid du Colombier if(i < 2)
603*9550e22eSDavid du Colombier print("ethervirtio: queue %d has invalid size %d\n", i, n);
604*9550e22eSDavid du Colombier break;
605*9550e22eSDavid du Colombier }
606*9550e22eSDavid du Colombier if(initqueue(&c->queue[i], n) < 0)
607*9550e22eSDavid du Colombier break;
608*9550e22eSDavid du Colombier coherence();
609*9550e22eSDavid du Colombier outl(c->port+Qaddr, PADDR(c->queue[i].desc)/VBY2PG);
610*9550e22eSDavid du Colombier }
611*9550e22eSDavid du Colombier if(i < 2){
612*9550e22eSDavid du Colombier print("ethervirtio: no queues\n");
613*9550e22eSDavid du Colombier free(c);
614*9550e22eSDavid du Colombier continue;
615*9550e22eSDavid du Colombier }
616*9550e22eSDavid du Colombier c->nqueue = i;
617*9550e22eSDavid du Colombier
618*9550e22eSDavid du Colombier if(h == nil)
619*9550e22eSDavid du Colombier h = c;
620*9550e22eSDavid du Colombier else
621*9550e22eSDavid du Colombier t->next = c;
622*9550e22eSDavid du Colombier t = c;
623*9550e22eSDavid du Colombier }
624*9550e22eSDavid du Colombier
625*9550e22eSDavid du Colombier return h;
626*9550e22eSDavid du Colombier }
627*9550e22eSDavid du Colombier
628*9550e22eSDavid du Colombier
629*9550e22eSDavid du Colombier static int
reset(Ether * edev)630*9550e22eSDavid du Colombier reset(Ether* edev)
631*9550e22eSDavid du Colombier {
632*9550e22eSDavid du Colombier static uchar zeros[Eaddrlen];
633*9550e22eSDavid du Colombier Ctlr *ctlr;
634*9550e22eSDavid du Colombier int i;
635*9550e22eSDavid du Colombier
636*9550e22eSDavid du Colombier if(ctlrhead == nil)
637*9550e22eSDavid du Colombier ctlrhead = pciprobe(1);
638*9550e22eSDavid du Colombier
639*9550e22eSDavid du Colombier for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){
640*9550e22eSDavid du Colombier if(ctlr->active)
641*9550e22eSDavid du Colombier continue;
642*9550e22eSDavid du Colombier if(edev->port == 0 || edev->port == ctlr->port){
643*9550e22eSDavid du Colombier ctlr->active = 1;
644*9550e22eSDavid du Colombier break;
645*9550e22eSDavid du Colombier }
646*9550e22eSDavid du Colombier }
647*9550e22eSDavid du Colombier
648*9550e22eSDavid du Colombier if(ctlr == nil)
649*9550e22eSDavid du Colombier return -1;
650*9550e22eSDavid du Colombier
651*9550e22eSDavid du Colombier edev->ctlr = ctlr;
652*9550e22eSDavid du Colombier edev->port = ctlr->port;
653*9550e22eSDavid du Colombier edev->irq = ctlr->pcidev->intl;
654*9550e22eSDavid du Colombier edev->tbdf = ctlr->pcidev->tbdf;
655*9550e22eSDavid du Colombier edev->mbps = 1000;
656*9550e22eSDavid du Colombier edev->link = 1;
657*9550e22eSDavid du Colombier
658*9550e22eSDavid du Colombier if((ctlr->feat & Fmac) != 0 && memcmp(edev->ea, zeros, Eaddrlen) == 0){
659*9550e22eSDavid du Colombier for(i = 0; i < Eaddrlen; i++)
660*9550e22eSDavid du Colombier edev->ea[i] = inb(ctlr->port+Qmac+i);
661*9550e22eSDavid du Colombier } else {
662*9550e22eSDavid du Colombier for(i = 0; i < Eaddrlen; i++)
663*9550e22eSDavid du Colombier outb(ctlr->port+Qmac+i, edev->ea[i]);
664*9550e22eSDavid du Colombier }
665*9550e22eSDavid du Colombier
666*9550e22eSDavid du Colombier edev->arg = edev;
667*9550e22eSDavid du Colombier
668*9550e22eSDavid du Colombier edev->attach = attach;
669*9550e22eSDavid du Colombier edev->shutdown = shutdown;
670*9550e22eSDavid du Colombier edev->ifstat = ifstat;
671*9550e22eSDavid du Colombier
672*9550e22eSDavid du Colombier if((ctlr->feat & (Fctrlvq|Fctrlrx)) == (Fctrlvq|Fctrlrx)){
673*9550e22eSDavid du Colombier edev->multicast = multicast;
674*9550e22eSDavid du Colombier edev->promiscuous = promiscuous;
675*9550e22eSDavid du Colombier }
676*9550e22eSDavid du Colombier
677*9550e22eSDavid du Colombier pcisetbme(ctlr->pcidev);
678*9550e22eSDavid du Colombier intrenable(edev->irq, interrupt, edev, edev->tbdf, edev->name);
679*9550e22eSDavid du Colombier
680*9550e22eSDavid du Colombier return 0;
681*9550e22eSDavid du Colombier }
682*9550e22eSDavid du Colombier
683*9550e22eSDavid du Colombier void
ethervirtiolink(void)684*9550e22eSDavid du Colombier ethervirtiolink(void)
685*9550e22eSDavid du Colombier {
686*9550e22eSDavid du Colombier addethercard("virtio", reset);
687*9550e22eSDavid du Colombier }
688*9550e22eSDavid du Colombier
689