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