xref: /plan9-contrib/sys/src/9/pc/ethervirtio10.c (revision 2fb7fabba558c4600b978310f1825e051ff9bed9)
1*2fb7fabbSDavid du Colombier /*
2*2fb7fabbSDavid du Colombier  * virtio 1.0 ethernet driver
3*2fb7fabbSDavid du Colombier  * http://docs.oasis-open.org/virtio/virtio/v1.0/virtio-v1.0.html
4*2fb7fabbSDavid du Colombier  *
5*2fb7fabbSDavid du Colombier  * In contrast to ethervirtio.c, this driver handles the non-legacy
6*2fb7fabbSDavid du Colombier  * interface for virtio ethernet which uses mmio for all register accesses
7*2fb7fabbSDavid du Colombier  * and requires a laborate pci capability structure dance to get working.
8*2fb7fabbSDavid du Colombier  *
9*2fb7fabbSDavid du Colombier  * It is kind of pointless as it is most likely slower than
10*2fb7fabbSDavid du Colombier  * port i/o (harder to emulate on the pc platform).
11*2fb7fabbSDavid du Colombier  *
12*2fb7fabbSDavid du Colombier  * The reason why this driver is needed it is that vultr set the
13*2fb7fabbSDavid du Colombier  * disable-legacy=on option in the -device parameter for qemu
14*2fb7fabbSDavid du Colombier  * on their hypervisor.
15*2fb7fabbSDavid du Colombier  */
16*2fb7fabbSDavid du Colombier #include "u.h"
17*2fb7fabbSDavid du Colombier #include "../port/lib.h"
18*2fb7fabbSDavid du Colombier #include "mem.h"
19*2fb7fabbSDavid du Colombier #include "dat.h"
20*2fb7fabbSDavid du Colombier #include "fns.h"
21*2fb7fabbSDavid du Colombier #include "io.h"
22*2fb7fabbSDavid du Colombier #include "../port/error.h"
23*2fb7fabbSDavid du Colombier #include "../port/netif.h"
24*2fb7fabbSDavid du Colombier #include "etherif.h"
25*2fb7fabbSDavid du Colombier 
26*2fb7fabbSDavid du Colombier typedef struct Vconfig Vconfig;
27*2fb7fabbSDavid du Colombier typedef struct Vnetcfg Vnetcfg;
28*2fb7fabbSDavid du Colombier 
29*2fb7fabbSDavid du Colombier typedef struct Vring Vring;
30*2fb7fabbSDavid du Colombier typedef struct Vdesc Vdesc;
31*2fb7fabbSDavid du Colombier typedef struct Vused Vused;
32*2fb7fabbSDavid du Colombier typedef struct Vheader Vheader;
33*2fb7fabbSDavid du Colombier typedef struct Vqueue Vqueue;
34*2fb7fabbSDavid du Colombier 
35*2fb7fabbSDavid du Colombier typedef struct Ctlr Ctlr;
36*2fb7fabbSDavid du Colombier 
37*2fb7fabbSDavid du Colombier enum {
38*2fb7fabbSDavid du Colombier 	/* §2.1 Device Status Field */
39*2fb7fabbSDavid du Colombier 	Sacknowledge = 1,
40*2fb7fabbSDavid du Colombier 	Sdriver = 2,
41*2fb7fabbSDavid du Colombier 	Sdriverok = 4,
42*2fb7fabbSDavid du Colombier 	Sfeaturesok = 8,
43*2fb7fabbSDavid du Colombier 	Sfailed = 128,
44*2fb7fabbSDavid du Colombier 
45*2fb7fabbSDavid du Colombier 	/* flags in Qnetstatus */
46*2fb7fabbSDavid du Colombier 	Nlinkup = (1<<0),
47*2fb7fabbSDavid du Colombier 	Nannounce = (1<<1),
48*2fb7fabbSDavid du Colombier 
49*2fb7fabbSDavid du Colombier 	/* feat[0] bits */
50*2fb7fabbSDavid du Colombier 	Fmac = 1<<5,
51*2fb7fabbSDavid du Colombier 	Fstatus = 1<<16,
52*2fb7fabbSDavid du Colombier 	Fctrlvq = 1<<17,
53*2fb7fabbSDavid du Colombier 	Fctrlrx = 1<<18,
54*2fb7fabbSDavid du Colombier 
55*2fb7fabbSDavid du Colombier 	/* feat[1] bits */
56*2fb7fabbSDavid du Colombier 	Fversion1 = 1<<(32-32),
57*2fb7fabbSDavid du Colombier 
58*2fb7fabbSDavid du Colombier 	/* vring used flags */
59*2fb7fabbSDavid du Colombier 	Unonotify = 1,
60*2fb7fabbSDavid du Colombier 	/* vring avail flags */
61*2fb7fabbSDavid du Colombier 	Rnointerrupt = 1,
62*2fb7fabbSDavid du Colombier 
63*2fb7fabbSDavid du Colombier 	/* descriptor flags */
64*2fb7fabbSDavid du Colombier 	Dnext = 1,
65*2fb7fabbSDavid du Colombier 	Dwrite = 2,
66*2fb7fabbSDavid du Colombier 	Dindirect = 4,
67*2fb7fabbSDavid du Colombier 
68*2fb7fabbSDavid du Colombier 	/* struct sizes */
69*2fb7fabbSDavid du Colombier 	VringSize = 4,
70*2fb7fabbSDavid du Colombier 	VdescSize = 16,
71*2fb7fabbSDavid du Colombier 	VusedSize = 8,
72*2fb7fabbSDavid du Colombier 	VheaderSize = 12,
73*2fb7fabbSDavid du Colombier 
74*2fb7fabbSDavid du Colombier 	Vrxq	= 0,
75*2fb7fabbSDavid du Colombier 	Vtxq	= 1,
76*2fb7fabbSDavid du Colombier 	Vctlq	= 2,
77*2fb7fabbSDavid du Colombier 
78*2fb7fabbSDavid du Colombier 	/* class/cmd for Vctlq */
79*2fb7fabbSDavid du Colombier 	CtrlRx	= 0x00,
80*2fb7fabbSDavid du Colombier 		CmdPromisc	= 0x00,
81*2fb7fabbSDavid du Colombier 		CmdAllmulti	= 0x01,
82*2fb7fabbSDavid du Colombier 	CtrlMac	= 0x01,
83*2fb7fabbSDavid du Colombier 		CmdMacTableSet	= 0x00,
84*2fb7fabbSDavid du Colombier 	CtrlVlan= 0x02,
85*2fb7fabbSDavid du Colombier 		CmdVlanAdd	= 0x00,
86*2fb7fabbSDavid du Colombier 		CmdVlanDel	= 0x01,
87*2fb7fabbSDavid du Colombier };
88*2fb7fabbSDavid du Colombier 
89*2fb7fabbSDavid du Colombier struct Vconfig {
90*2fb7fabbSDavid du Colombier 	u32int	devfeatsel;
91*2fb7fabbSDavid du Colombier 	u32int	devfeat;
92*2fb7fabbSDavid du Colombier 	u32int	drvfeatsel;
93*2fb7fabbSDavid du Colombier 	u32int	drvfeat;
94*2fb7fabbSDavid du Colombier 
95*2fb7fabbSDavid du Colombier 	u16int	msixcfg;
96*2fb7fabbSDavid du Colombier 	u16int	nqueues;
97*2fb7fabbSDavid du Colombier 
98*2fb7fabbSDavid du Colombier 	u8int	status;
99*2fb7fabbSDavid du Colombier 	u8int	cfggen;
100*2fb7fabbSDavid du Colombier 	u16int	queuesel;
101*2fb7fabbSDavid du Colombier 
102*2fb7fabbSDavid du Colombier 	u16int	queuesize;
103*2fb7fabbSDavid du Colombier 	u16int	queuemsixvect;
104*2fb7fabbSDavid du Colombier 
105*2fb7fabbSDavid du Colombier 	u16int	queueenable;
106*2fb7fabbSDavid du Colombier 	u16int	queuenotifyoff;
107*2fb7fabbSDavid du Colombier 
108*2fb7fabbSDavid du Colombier 	u64int	queuedesc;
109*2fb7fabbSDavid du Colombier 	u64int	queueavail;
110*2fb7fabbSDavid du Colombier 	u64int	queueused;
111*2fb7fabbSDavid du Colombier };
112*2fb7fabbSDavid du Colombier 
113*2fb7fabbSDavid du Colombier struct Vnetcfg
114*2fb7fabbSDavid du Colombier {
115*2fb7fabbSDavid du Colombier 	u16int	mac0;
116*2fb7fabbSDavid du Colombier 	u16int	mac1;
117*2fb7fabbSDavid du Colombier 	u16int	mac2;
118*2fb7fabbSDavid du Colombier 	u16int	status;
119*2fb7fabbSDavid du Colombier 	u16int	maxqueuepairs;
120*2fb7fabbSDavid du Colombier 	u16int	mtu;
121*2fb7fabbSDavid du Colombier };
122*2fb7fabbSDavid du Colombier 
123*2fb7fabbSDavid du Colombier struct Vring
124*2fb7fabbSDavid du Colombier {
125*2fb7fabbSDavid du Colombier 	u16int	flags;
126*2fb7fabbSDavid du Colombier 	u16int	idx;
127*2fb7fabbSDavid du Colombier };
128*2fb7fabbSDavid du Colombier 
129*2fb7fabbSDavid du Colombier struct Vdesc
130*2fb7fabbSDavid du Colombier {
131*2fb7fabbSDavid du Colombier 	u64int	addr;
132*2fb7fabbSDavid du Colombier 	u32int	len;
133*2fb7fabbSDavid du Colombier 	u16int	flags;
134*2fb7fabbSDavid du Colombier 	u16int	next;
135*2fb7fabbSDavid du Colombier };
136*2fb7fabbSDavid du Colombier 
137*2fb7fabbSDavid du Colombier struct Vused
138*2fb7fabbSDavid du Colombier {
139*2fb7fabbSDavid du Colombier 	u32int	id;
140*2fb7fabbSDavid du Colombier 	u32int	len;
141*2fb7fabbSDavid du Colombier };
142*2fb7fabbSDavid du Colombier 
143*2fb7fabbSDavid du Colombier struct Vheader
144*2fb7fabbSDavid du Colombier {
145*2fb7fabbSDavid du Colombier 	u8int	flags;
146*2fb7fabbSDavid du Colombier 	u8int	segtype;
147*2fb7fabbSDavid du Colombier 	u16int	hlen;
148*2fb7fabbSDavid du Colombier 	u16int	seglen;
149*2fb7fabbSDavid du Colombier 	u16int	csumstart;
150*2fb7fabbSDavid du Colombier 	u16int	csumend;
151*2fb7fabbSDavid du Colombier };
152*2fb7fabbSDavid du Colombier 
153*2fb7fabbSDavid du Colombier struct Vqueue
154*2fb7fabbSDavid du Colombier {
155*2fb7fabbSDavid du Colombier 	Rendez;
156*2fb7fabbSDavid du Colombier 
157*2fb7fabbSDavid du Colombier 	uint	qsize;
158*2fb7fabbSDavid du Colombier 	uint	qmask;
159*2fb7fabbSDavid du Colombier 
160*2fb7fabbSDavid du Colombier 	Vdesc	*desc;
161*2fb7fabbSDavid du Colombier 
162*2fb7fabbSDavid du Colombier 	Vring	*avail;
163*2fb7fabbSDavid du Colombier 	u16int	*availent;
164*2fb7fabbSDavid du Colombier 	u16int	*availevent;
165*2fb7fabbSDavid du Colombier 
166*2fb7fabbSDavid du Colombier 	Vring	*used;
167*2fb7fabbSDavid du Colombier 	Vused	*usedent;
168*2fb7fabbSDavid du Colombier 	u16int	*usedevent;
169*2fb7fabbSDavid du Colombier 	u16int	lastused;
170*2fb7fabbSDavid du Colombier 
171*2fb7fabbSDavid du Colombier 	uint	nintr;
172*2fb7fabbSDavid du Colombier 	uint	nnote;
173*2fb7fabbSDavid du Colombier 
174*2fb7fabbSDavid du Colombier 	/* notify register */
175*2fb7fabbSDavid du Colombier 	void	*notify;
176*2fb7fabbSDavid du Colombier };
177*2fb7fabbSDavid du Colombier 
178*2fb7fabbSDavid du Colombier struct Ctlr {
179*2fb7fabbSDavid du Colombier 	Lock;
180*2fb7fabbSDavid du Colombier 
181*2fb7fabbSDavid du Colombier 	QLock	ctllock;
182*2fb7fabbSDavid du Colombier 
183*2fb7fabbSDavid du Colombier 	int	attached;
184*2fb7fabbSDavid du Colombier 
185*2fb7fabbSDavid du Colombier 	/* registers */
186*2fb7fabbSDavid du Colombier 	Vconfig	*cfg;
187*2fb7fabbSDavid du Colombier 	Vnetcfg *dev;
188*2fb7fabbSDavid du Colombier 	u8int	*isr;
189*2fb7fabbSDavid du Colombier 	u8int	*notify;
190*2fb7fabbSDavid du Colombier 	u32int	notifyoffmult;
191*2fb7fabbSDavid du Colombier 
192*2fb7fabbSDavid du Colombier 	uvlong	port;
193*2fb7fabbSDavid du Colombier 	Pcidev	*pcidev;
194*2fb7fabbSDavid du Colombier 	Ctlr	*next;
195*2fb7fabbSDavid du Colombier 	int	active;
196*2fb7fabbSDavid du Colombier 	ulong	feat[2];
197*2fb7fabbSDavid du Colombier 	int	nqueue;
198*2fb7fabbSDavid du Colombier 
199*2fb7fabbSDavid du Colombier 	/* virtioether has 3 queues: rx, tx and ctl */
200*2fb7fabbSDavid du Colombier 	Vqueue	queue[3];
201*2fb7fabbSDavid du Colombier };
202*2fb7fabbSDavid du Colombier 
203*2fb7fabbSDavid du Colombier static Ctlr *ctlrhead;
204*2fb7fabbSDavid du Colombier 
205*2fb7fabbSDavid du Colombier static int
vhasroom(void * v)206*2fb7fabbSDavid du Colombier vhasroom(void *v)
207*2fb7fabbSDavid du Colombier {
208*2fb7fabbSDavid du Colombier 	Vqueue *q = v;
209*2fb7fabbSDavid du Colombier 	return q->lastused != q->used->idx;
210*2fb7fabbSDavid du Colombier }
211*2fb7fabbSDavid du Colombier 
212*2fb7fabbSDavid du Colombier static void
vqnotify(Ctlr * ctlr,int x)213*2fb7fabbSDavid du Colombier vqnotify(Ctlr *ctlr, int x)
214*2fb7fabbSDavid du Colombier {
215*2fb7fabbSDavid du Colombier 	Vqueue *q;
216*2fb7fabbSDavid du Colombier 
217*2fb7fabbSDavid du Colombier 	coherence();
218*2fb7fabbSDavid du Colombier 	q = &ctlr->queue[x];
219*2fb7fabbSDavid du Colombier 	if(q->used->flags & Unonotify)
220*2fb7fabbSDavid du Colombier 		return;
221*2fb7fabbSDavid du Colombier 	q->nnote++;
222*2fb7fabbSDavid du Colombier 	*((u16int*)q->notify) = x;
223*2fb7fabbSDavid du Colombier }
224*2fb7fabbSDavid du Colombier 
225*2fb7fabbSDavid du Colombier static void
txproc(void * v)226*2fb7fabbSDavid du Colombier txproc(void *v)
227*2fb7fabbSDavid du Colombier {
228*2fb7fabbSDavid du Colombier 	Vheader *header;
229*2fb7fabbSDavid du Colombier 	Block **blocks;
230*2fb7fabbSDavid du Colombier 	Ether *edev;
231*2fb7fabbSDavid du Colombier 	Ctlr *ctlr;
232*2fb7fabbSDavid du Colombier 	Vqueue *q;
233*2fb7fabbSDavid du Colombier 	Vused *u;
234*2fb7fabbSDavid du Colombier 	Block *b;
235*2fb7fabbSDavid du Colombier 	int i, j;
236*2fb7fabbSDavid du Colombier 
237*2fb7fabbSDavid du Colombier 	edev = v;
238*2fb7fabbSDavid du Colombier 	ctlr = edev->ctlr;
239*2fb7fabbSDavid du Colombier 	q = &ctlr->queue[Vtxq];
240*2fb7fabbSDavid du Colombier 
241*2fb7fabbSDavid du Colombier 	header = smalloc(VheaderSize);
242*2fb7fabbSDavid du Colombier 	blocks = smalloc(sizeof(Block*) * (q->qsize/2));
243*2fb7fabbSDavid du Colombier 
244*2fb7fabbSDavid du Colombier 	for(i = 0; i < q->qsize/2; i++){
245*2fb7fabbSDavid du Colombier 		j = i << 1;
246*2fb7fabbSDavid du Colombier 		q->desc[j].addr = PADDR(header);
247*2fb7fabbSDavid du Colombier 		q->desc[j].len = VheaderSize;
248*2fb7fabbSDavid du Colombier 		q->desc[j].next = j | 1;
249*2fb7fabbSDavid du Colombier 		q->desc[j].flags = Dnext;
250*2fb7fabbSDavid du Colombier 
251*2fb7fabbSDavid du Colombier 		q->availent[i] = q->availent[i + q->qsize/2] = j;
252*2fb7fabbSDavid du Colombier 
253*2fb7fabbSDavid du Colombier 		j |= 1;
254*2fb7fabbSDavid du Colombier 		q->desc[j].next = 0;
255*2fb7fabbSDavid du Colombier 		q->desc[j].flags = 0;
256*2fb7fabbSDavid du Colombier 	}
257*2fb7fabbSDavid du Colombier 
258*2fb7fabbSDavid du Colombier 	q->avail->flags &= ~Rnointerrupt;
259*2fb7fabbSDavid du Colombier 
260*2fb7fabbSDavid du Colombier 	while(waserror())
261*2fb7fabbSDavid du Colombier 		;
262*2fb7fabbSDavid du Colombier 
263*2fb7fabbSDavid du Colombier 	while((b = qbread(edev->oq, 1000000)) != nil){
264*2fb7fabbSDavid du Colombier 		for(;;){
265*2fb7fabbSDavid du Colombier 			/* retire completed packets */
266*2fb7fabbSDavid du Colombier 			while((i = q->lastused) != q->used->idx){
267*2fb7fabbSDavid du Colombier 				u = &q->usedent[i & q->qmask];
268*2fb7fabbSDavid du Colombier 				i = (u->id & q->qmask) >> 1;
269*2fb7fabbSDavid du Colombier 				if(blocks[i] == nil)
270*2fb7fabbSDavid du Colombier 					break;
271*2fb7fabbSDavid du Colombier 				freeb(blocks[i]);
272*2fb7fabbSDavid du Colombier 				blocks[i] = nil;
273*2fb7fabbSDavid du Colombier 				q->lastused++;
274*2fb7fabbSDavid du Colombier 			}
275*2fb7fabbSDavid du Colombier 
276*2fb7fabbSDavid du Colombier 			/* have free slot? */
277*2fb7fabbSDavid du Colombier 			i = q->avail->idx & (q->qmask >> 1);
278*2fb7fabbSDavid du Colombier 			if(blocks[i] == nil)
279*2fb7fabbSDavid du Colombier 				break;
280*2fb7fabbSDavid du Colombier 
281*2fb7fabbSDavid du Colombier 			/* ring full, wait and retry */
282*2fb7fabbSDavid du Colombier 			if(!vhasroom(q))
283*2fb7fabbSDavid du Colombier 				sleep(q, vhasroom, q);
284*2fb7fabbSDavid du Colombier 		}
285*2fb7fabbSDavid du Colombier 
286*2fb7fabbSDavid du Colombier 		/* slot is free, fill in descriptor */
287*2fb7fabbSDavid du Colombier 		blocks[i] = b;
288*2fb7fabbSDavid du Colombier 		j = (i << 1) | 1;
289*2fb7fabbSDavid du Colombier 		q->desc[j].addr = PADDR(b->rp);
290*2fb7fabbSDavid du Colombier 		q->desc[j].len = BLEN(b);
291*2fb7fabbSDavid du Colombier 		coherence();
292*2fb7fabbSDavid du Colombier 		q->avail->idx++;
293*2fb7fabbSDavid du Colombier 		vqnotify(ctlr, Vtxq);
294*2fb7fabbSDavid du Colombier 	}
295*2fb7fabbSDavid du Colombier 
296*2fb7fabbSDavid du Colombier 	pexit("ether out queue closed", 1);
297*2fb7fabbSDavid du Colombier }
298*2fb7fabbSDavid du Colombier 
299*2fb7fabbSDavid du Colombier static void
rxproc(void * v)300*2fb7fabbSDavid du Colombier rxproc(void *v)
301*2fb7fabbSDavid du Colombier {
302*2fb7fabbSDavid du Colombier 	Vheader *header;
303*2fb7fabbSDavid du Colombier 	Block **blocks;
304*2fb7fabbSDavid du Colombier 	Ether *edev;
305*2fb7fabbSDavid du Colombier 	Ctlr *ctlr;
306*2fb7fabbSDavid du Colombier 	Vqueue *q;
307*2fb7fabbSDavid du Colombier 	Vused *u;
308*2fb7fabbSDavid du Colombier 	Block *b;
309*2fb7fabbSDavid du Colombier 	int i, j;
310*2fb7fabbSDavid du Colombier 
311*2fb7fabbSDavid du Colombier 	edev = v;
312*2fb7fabbSDavid du Colombier 	ctlr = edev->ctlr;
313*2fb7fabbSDavid du Colombier 	q = &ctlr->queue[Vrxq];
314*2fb7fabbSDavid du Colombier 
315*2fb7fabbSDavid du Colombier 	header = smalloc(VheaderSize);
316*2fb7fabbSDavid du Colombier 	blocks = smalloc(sizeof(Block*) * (q->qsize/2));
317*2fb7fabbSDavid du Colombier 
318*2fb7fabbSDavid du Colombier 	for(i = 0; i < q->qsize/2; i++){
319*2fb7fabbSDavid du Colombier 		j = i << 1;
320*2fb7fabbSDavid du Colombier 		q->desc[j].addr = PADDR(header);
321*2fb7fabbSDavid du Colombier 		q->desc[j].len = VheaderSize;
322*2fb7fabbSDavid du Colombier 		q->desc[j].next = j | 1;
323*2fb7fabbSDavid du Colombier 		q->desc[j].flags = Dwrite|Dnext;
324*2fb7fabbSDavid du Colombier 
325*2fb7fabbSDavid du Colombier 		q->availent[i] = q->availent[i + q->qsize/2] = j;
326*2fb7fabbSDavid du Colombier 
327*2fb7fabbSDavid du Colombier 		j |= 1;
328*2fb7fabbSDavid du Colombier 		q->desc[j].next = 0;
329*2fb7fabbSDavid du Colombier 		q->desc[j].flags = Dwrite;
330*2fb7fabbSDavid du Colombier 	}
331*2fb7fabbSDavid du Colombier 
332*2fb7fabbSDavid du Colombier 	q->avail->flags &= ~Rnointerrupt;
333*2fb7fabbSDavid du Colombier 
334*2fb7fabbSDavid du Colombier 	while(waserror())
335*2fb7fabbSDavid du Colombier 		;
336*2fb7fabbSDavid du Colombier 
337*2fb7fabbSDavid du Colombier 	for(;;){
338*2fb7fabbSDavid du Colombier 		/* replenish receive ring */
339*2fb7fabbSDavid du Colombier 		do {
340*2fb7fabbSDavid du Colombier 			i = q->avail->idx & (q->qmask >> 1);
341*2fb7fabbSDavid du Colombier 			if(blocks[i] != nil)
342*2fb7fabbSDavid du Colombier 				break;
343*2fb7fabbSDavid du Colombier 			if((b = iallocb(ETHERMAXTU)) == nil)
344*2fb7fabbSDavid du Colombier 				break;
345*2fb7fabbSDavid du Colombier 			blocks[i] = b;
346*2fb7fabbSDavid du Colombier 			j = (i << 1) | 1;
347*2fb7fabbSDavid du Colombier 			q->desc[j].addr = PADDR(b->rp);
348*2fb7fabbSDavid du Colombier 			q->desc[j].len = BALLOC(b);
349*2fb7fabbSDavid du Colombier 			coherence();
350*2fb7fabbSDavid du Colombier 			q->avail->idx++;
351*2fb7fabbSDavid du Colombier 		} while(q->avail->idx != q->used->idx);
352*2fb7fabbSDavid du Colombier 		vqnotify(ctlr, Vrxq);
353*2fb7fabbSDavid du Colombier 
354*2fb7fabbSDavid du Colombier 		/* wait for any packets to complete */
355*2fb7fabbSDavid du Colombier 		if(!vhasroom(q))
356*2fb7fabbSDavid du Colombier 			sleep(q, vhasroom, q);
357*2fb7fabbSDavid du Colombier 
358*2fb7fabbSDavid du Colombier 		/* retire completed packets */
359*2fb7fabbSDavid du Colombier 		while((i = q->lastused) != q->used->idx) {
360*2fb7fabbSDavid du Colombier 			u = &q->usedent[i & q->qmask];
361*2fb7fabbSDavid du Colombier 			i = (u->id & q->qmask) >> 1;
362*2fb7fabbSDavid du Colombier 			if((b = blocks[i]) == nil)
363*2fb7fabbSDavid du Colombier 				break;
364*2fb7fabbSDavid du Colombier 
365*2fb7fabbSDavid du Colombier 			blocks[i] = nil;
366*2fb7fabbSDavid du Colombier 			b->wp = b->rp + u->len - VheaderSize;
367*2fb7fabbSDavid du Colombier 			etheriq(edev, b, 1);
368*2fb7fabbSDavid du Colombier 			q->lastused++;
369*2fb7fabbSDavid du Colombier 		}
370*2fb7fabbSDavid du Colombier 	}
371*2fb7fabbSDavid du Colombier }
372*2fb7fabbSDavid du Colombier 
373*2fb7fabbSDavid du Colombier static int
vctlcmd(Ether * edev,uchar class,uchar cmd,uchar * data,int ndata)374*2fb7fabbSDavid du Colombier vctlcmd(Ether *edev, uchar class, uchar cmd, uchar *data, int ndata)
375*2fb7fabbSDavid du Colombier {
376*2fb7fabbSDavid du Colombier 	uchar hdr[2], ack[1];
377*2fb7fabbSDavid du Colombier 	Ctlr *ctlr;
378*2fb7fabbSDavid du Colombier 	Vqueue *q;
379*2fb7fabbSDavid du Colombier 	Vdesc *d;
380*2fb7fabbSDavid du Colombier 	int i;
381*2fb7fabbSDavid du Colombier 
382*2fb7fabbSDavid du Colombier 	ctlr = edev->ctlr;
383*2fb7fabbSDavid du Colombier 	q = &ctlr->queue[Vctlq];
384*2fb7fabbSDavid du Colombier 	if(q->qsize < 3)
385*2fb7fabbSDavid du Colombier 		return -1;
386*2fb7fabbSDavid du Colombier 
387*2fb7fabbSDavid du Colombier 	qlock(&ctlr->ctllock);
388*2fb7fabbSDavid du Colombier 	while(waserror())
389*2fb7fabbSDavid du Colombier 		;
390*2fb7fabbSDavid du Colombier 
391*2fb7fabbSDavid du Colombier 	ack[0] = 0x55;
392*2fb7fabbSDavid du Colombier 	hdr[0] = class;
393*2fb7fabbSDavid du Colombier 	hdr[1] = cmd;
394*2fb7fabbSDavid du Colombier 
395*2fb7fabbSDavid du Colombier 	d = &q->desc[0];
396*2fb7fabbSDavid du Colombier 	d->addr = PADDR(hdr);
397*2fb7fabbSDavid du Colombier 	d->len = sizeof(hdr);
398*2fb7fabbSDavid du Colombier 	d->next = 1;
399*2fb7fabbSDavid du Colombier 	d->flags = Dnext;
400*2fb7fabbSDavid du Colombier 	d++;
401*2fb7fabbSDavid du Colombier 	d->addr = PADDR(data);
402*2fb7fabbSDavid du Colombier 	d->len = ndata;
403*2fb7fabbSDavid du Colombier 	d->next = 2;
404*2fb7fabbSDavid du Colombier 	d->flags = Dnext;
405*2fb7fabbSDavid du Colombier 	d++;
406*2fb7fabbSDavid du Colombier 	d->addr = PADDR(ack);
407*2fb7fabbSDavid du Colombier 	d->len = sizeof(ack);
408*2fb7fabbSDavid du Colombier 	d->next = 0;
409*2fb7fabbSDavid du Colombier 	d->flags = Dwrite;
410*2fb7fabbSDavid du Colombier 
411*2fb7fabbSDavid du Colombier 	i = q->avail->idx & q->qmask;
412*2fb7fabbSDavid du Colombier 	q->availent[i] = 0;
413*2fb7fabbSDavid du Colombier 	coherence();
414*2fb7fabbSDavid du Colombier 
415*2fb7fabbSDavid du Colombier 	q->avail->flags &= ~Rnointerrupt;
416*2fb7fabbSDavid du Colombier 	q->avail->idx++;
417*2fb7fabbSDavid du Colombier 	vqnotify(ctlr, Vctlq);
418*2fb7fabbSDavid du Colombier 	while(!vhasroom(q))
419*2fb7fabbSDavid du Colombier 		sleep(q, vhasroom, q);
420*2fb7fabbSDavid du Colombier 	q->lastused = q->used->idx;
421*2fb7fabbSDavid du Colombier 	q->avail->flags |= Rnointerrupt;
422*2fb7fabbSDavid du Colombier 
423*2fb7fabbSDavid du Colombier 	qunlock(&ctlr->ctllock);
424*2fb7fabbSDavid du Colombier 	poperror();
425*2fb7fabbSDavid du Colombier 
426*2fb7fabbSDavid du Colombier 	if(ack[0] != 0)
427*2fb7fabbSDavid du Colombier 		print("#l%d: vctlcmd: %ux.%ux -> %ux\n", edev->ctlrno, class, cmd, ack[0]);
428*2fb7fabbSDavid du Colombier 
429*2fb7fabbSDavid du Colombier 	return ack[0];
430*2fb7fabbSDavid du Colombier }
431*2fb7fabbSDavid du Colombier 
432*2fb7fabbSDavid du Colombier static void
interrupt(Ureg *,void * arg)433*2fb7fabbSDavid du Colombier interrupt(Ureg*, void* arg)
434*2fb7fabbSDavid du Colombier {
435*2fb7fabbSDavid du Colombier 	Ether *edev;
436*2fb7fabbSDavid du Colombier 	Ctlr *ctlr;
437*2fb7fabbSDavid du Colombier 	Vqueue *q;
438*2fb7fabbSDavid du Colombier 	int i;
439*2fb7fabbSDavid du Colombier 
440*2fb7fabbSDavid du Colombier 	edev = arg;
441*2fb7fabbSDavid du Colombier 	ctlr = edev->ctlr;
442*2fb7fabbSDavid du Colombier 	if(*ctlr->isr & 1){
443*2fb7fabbSDavid du Colombier 		for(i = 0; i < ctlr->nqueue; i++){
444*2fb7fabbSDavid du Colombier 			q = &ctlr->queue[i];
445*2fb7fabbSDavid du Colombier 			if(vhasroom(q)){
446*2fb7fabbSDavid du Colombier 				q->nintr++;
447*2fb7fabbSDavid du Colombier 				wakeup(q);
448*2fb7fabbSDavid du Colombier 			}
449*2fb7fabbSDavid du Colombier 		}
450*2fb7fabbSDavid du Colombier 	}
451*2fb7fabbSDavid du Colombier }
452*2fb7fabbSDavid du Colombier 
453*2fb7fabbSDavid du Colombier static void
attach(Ether * edev)454*2fb7fabbSDavid du Colombier attach(Ether* edev)
455*2fb7fabbSDavid du Colombier {
456*2fb7fabbSDavid du Colombier 	char name[KNAMELEN];
457*2fb7fabbSDavid du Colombier 	Ctlr* ctlr;
458*2fb7fabbSDavid du Colombier 	int i;
459*2fb7fabbSDavid du Colombier 
460*2fb7fabbSDavid du Colombier 	ctlr = edev->ctlr;
461*2fb7fabbSDavid du Colombier 	ilock(ctlr);
462*2fb7fabbSDavid du Colombier 	if(ctlr->attached){
463*2fb7fabbSDavid du Colombier 		iunlock(ctlr);
464*2fb7fabbSDavid du Colombier 		return;
465*2fb7fabbSDavid du Colombier 	}
466*2fb7fabbSDavid du Colombier 	ctlr->attached = 1;
467*2fb7fabbSDavid du Colombier 
468*2fb7fabbSDavid du Colombier 	/* enable the queues */
469*2fb7fabbSDavid du Colombier 	for(i = 0; i < ctlr->nqueue; i++){
470*2fb7fabbSDavid du Colombier 		ctlr->cfg->queuesel = i;
471*2fb7fabbSDavid du Colombier 		ctlr->cfg->queueenable = 1;
472*2fb7fabbSDavid du Colombier 	}
473*2fb7fabbSDavid du Colombier 
474*2fb7fabbSDavid du Colombier 	/* driver is ready */
475*2fb7fabbSDavid du Colombier 	ctlr->cfg->status |= Sdriverok;
476*2fb7fabbSDavid du Colombier 
477*2fb7fabbSDavid du Colombier 	iunlock(ctlr);
478*2fb7fabbSDavid du Colombier 
479*2fb7fabbSDavid du Colombier 	/* start kprocs */
480*2fb7fabbSDavid du Colombier 	snprint(name, sizeof name, "#l%drx", edev->ctlrno);
481*2fb7fabbSDavid du Colombier 	kproc(name, rxproc, edev);
482*2fb7fabbSDavid du Colombier 	snprint(name, sizeof name, "#l%dtx", edev->ctlrno);
483*2fb7fabbSDavid du Colombier 	kproc(name, txproc, edev);
484*2fb7fabbSDavid du Colombier }
485*2fb7fabbSDavid du Colombier 
486*2fb7fabbSDavid du Colombier static long
ifstat(Ether * edev,void * a,long n,ulong offset)487*2fb7fabbSDavid du Colombier ifstat(Ether *edev, void *a, long n, ulong offset)
488*2fb7fabbSDavid du Colombier {
489*2fb7fabbSDavid du Colombier 	int i, l;
490*2fb7fabbSDavid du Colombier 	char *p;
491*2fb7fabbSDavid du Colombier 	Ctlr *ctlr;
492*2fb7fabbSDavid du Colombier 	Vqueue *q;
493*2fb7fabbSDavid du Colombier 
494*2fb7fabbSDavid du Colombier 	ctlr = edev->ctlr;
495*2fb7fabbSDavid du Colombier 
496*2fb7fabbSDavid du Colombier 	p = smalloc(READSTR);
497*2fb7fabbSDavid du Colombier 
498*2fb7fabbSDavid du Colombier 	l = snprint(p, READSTR, "devfeat %32.32luX %32.32luX\n", ctlr->feat[1], ctlr->feat[0]);
499*2fb7fabbSDavid du Colombier 	l += snprint(p+l, READSTR-l, "devstatus %8.8uX\n", ctlr->cfg->status);
500*2fb7fabbSDavid du Colombier 
501*2fb7fabbSDavid du Colombier 	for(i = 0; i < ctlr->nqueue; i++){
502*2fb7fabbSDavid du Colombier 		q = &ctlr->queue[i];
503*2fb7fabbSDavid du Colombier 		l += snprint(p+l, READSTR-l,
504*2fb7fabbSDavid du Colombier 			"vq%d %#p size %d avail->idx %d used->idx %d lastused %hud nintr %ud nnote %ud\n",
505*2fb7fabbSDavid du Colombier 			i, q, q->qsize, q->avail->idx, q->used->idx, q->lastused, q->nintr, q->nnote);
506*2fb7fabbSDavid du Colombier 	}
507*2fb7fabbSDavid du Colombier 
508*2fb7fabbSDavid du Colombier 	n = readstr(offset, a, n, p);
509*2fb7fabbSDavid du Colombier 	free(p);
510*2fb7fabbSDavid du Colombier 
511*2fb7fabbSDavid du Colombier 	return n;
512*2fb7fabbSDavid du Colombier }
513*2fb7fabbSDavid du Colombier 
514*2fb7fabbSDavid du Colombier static void
shutdown(Ether * edev)515*2fb7fabbSDavid du Colombier shutdown(Ether* edev)
516*2fb7fabbSDavid du Colombier {
517*2fb7fabbSDavid du Colombier 	Ctlr *ctlr = edev->ctlr;
518*2fb7fabbSDavid du Colombier 
519*2fb7fabbSDavid du Colombier 	coherence();
520*2fb7fabbSDavid du Colombier 	ctlr->cfg->status = 0;
521*2fb7fabbSDavid du Colombier 	coherence();
522*2fb7fabbSDavid du Colombier 
523*2fb7fabbSDavid du Colombier 	pciclrbme(ctlr->pcidev);
524*2fb7fabbSDavid du Colombier }
525*2fb7fabbSDavid du Colombier 
526*2fb7fabbSDavid du Colombier static void
promiscuous(void * arg,int on)527*2fb7fabbSDavid du Colombier promiscuous(void *arg, int on)
528*2fb7fabbSDavid du Colombier {
529*2fb7fabbSDavid du Colombier 	Ether *edev = arg;
530*2fb7fabbSDavid du Colombier 	uchar b[1];
531*2fb7fabbSDavid du Colombier 
532*2fb7fabbSDavid du Colombier 	b[0] = on != 0;
533*2fb7fabbSDavid du Colombier 	vctlcmd(edev, CtrlRx, CmdPromisc, b, sizeof(b));
534*2fb7fabbSDavid du Colombier }
535*2fb7fabbSDavid du Colombier 
536*2fb7fabbSDavid du Colombier static void
multicast(void * arg,uchar *,int)537*2fb7fabbSDavid du Colombier multicast(void *arg, uchar*, int)
538*2fb7fabbSDavid du Colombier {
539*2fb7fabbSDavid du Colombier 	Ether *edev = arg;
540*2fb7fabbSDavid du Colombier 	uchar b[1];
541*2fb7fabbSDavid du Colombier 
542*2fb7fabbSDavid du Colombier 	b[0] = edev->nmaddr > 0;
543*2fb7fabbSDavid du Colombier 	vctlcmd(edev, CtrlRx, CmdAllmulti, b, sizeof(b));
544*2fb7fabbSDavid du Colombier }
545*2fb7fabbSDavid du Colombier 
546*2fb7fabbSDavid du Colombier static int
initqueue(Vqueue * q,int size)547*2fb7fabbSDavid du Colombier initqueue(Vqueue *q, int size)
548*2fb7fabbSDavid du Colombier {
549*2fb7fabbSDavid du Colombier 	uchar *p;
550*2fb7fabbSDavid du Colombier 
551*2fb7fabbSDavid du Colombier 	q->desc = mallocalign(VdescSize*size, 16, 0, 0);
552*2fb7fabbSDavid du Colombier 	if(q->desc == nil)
553*2fb7fabbSDavid du Colombier 		return -1;
554*2fb7fabbSDavid du Colombier 	p = mallocalign(VringSize + 2*size + 2, 2, 0, 0);
555*2fb7fabbSDavid du Colombier 	if(p == nil){
556*2fb7fabbSDavid du Colombier FreeDesc:
557*2fb7fabbSDavid du Colombier 		free(q->desc);
558*2fb7fabbSDavid du Colombier 		q->desc = nil;
559*2fb7fabbSDavid du Colombier 		return -1;
560*2fb7fabbSDavid du Colombier 	}
561*2fb7fabbSDavid du Colombier 	q->avail = (void*)p;
562*2fb7fabbSDavid du Colombier 	p += VringSize;
563*2fb7fabbSDavid du Colombier 	q->availent = (void*)p;
564*2fb7fabbSDavid du Colombier 	p += sizeof(u16int)*size;
565*2fb7fabbSDavid du Colombier 	q->availevent = (void*)p;
566*2fb7fabbSDavid du Colombier 	p = mallocalign(VringSize + VusedSize*size + 2, 4, 0, 0);
567*2fb7fabbSDavid du Colombier 	if(p == nil){
568*2fb7fabbSDavid du Colombier 		free(q->avail);
569*2fb7fabbSDavid du Colombier 		q->avail = nil;
570*2fb7fabbSDavid du Colombier 		goto FreeDesc;
571*2fb7fabbSDavid du Colombier 	}
572*2fb7fabbSDavid du Colombier 	q->used = (void*)p;
573*2fb7fabbSDavid du Colombier 	p += VringSize;
574*2fb7fabbSDavid du Colombier 	q->usedent = (void*)p;
575*2fb7fabbSDavid du Colombier 	p += VusedSize*size;
576*2fb7fabbSDavid du Colombier 	q->usedevent = (void*)p;
577*2fb7fabbSDavid du Colombier 
578*2fb7fabbSDavid du Colombier 	q->qsize = size;
579*2fb7fabbSDavid du Colombier 	q->qmask = q->qsize - 1;
580*2fb7fabbSDavid du Colombier 
581*2fb7fabbSDavid du Colombier 	q->lastused = q->avail->idx = q->used->idx = 0;
582*2fb7fabbSDavid du Colombier 
583*2fb7fabbSDavid du Colombier 	q->avail->flags |= Rnointerrupt;
584*2fb7fabbSDavid du Colombier 
585*2fb7fabbSDavid du Colombier 	return 0;
586*2fb7fabbSDavid du Colombier }
587*2fb7fabbSDavid du Colombier 
588*2fb7fabbSDavid du Colombier static int
matchvirtiocfgcap(Pcidev * p,int cap,int off,int typ)589*2fb7fabbSDavid du Colombier matchvirtiocfgcap(Pcidev *p, int cap, int off, int typ)
590*2fb7fabbSDavid du Colombier {
591*2fb7fabbSDavid du Colombier 	int bar;
592*2fb7fabbSDavid du Colombier 
593*2fb7fabbSDavid du Colombier 	if(cap != 9 || pcicfgr8(p, off+3) != typ)
594*2fb7fabbSDavid du Colombier 		return 1;
595*2fb7fabbSDavid du Colombier 
596*2fb7fabbSDavid du Colombier 	/* skip invalid or non memory bars */
597*2fb7fabbSDavid du Colombier 	bar = pcicfgr8(p, off+4);
598*2fb7fabbSDavid du Colombier 	if(bar < 0 || bar >= nelem(p->mem)
599*2fb7fabbSDavid du Colombier 	|| p->mem[bar].size == 0
600*2fb7fabbSDavid du Colombier 	|| (p->mem[bar].bar & 3) != 0)
601*2fb7fabbSDavid du Colombier 		return 1;
602*2fb7fabbSDavid du Colombier 
603*2fb7fabbSDavid du Colombier 	return 0;
604*2fb7fabbSDavid du Colombier }
605*2fb7fabbSDavid du Colombier 
606*2fb7fabbSDavid du Colombier static int
virtiocap(Pcidev * p,int typ)607*2fb7fabbSDavid du Colombier virtiocap(Pcidev *p, int typ)
608*2fb7fabbSDavid du Colombier {
609*2fb7fabbSDavid du Colombier 	return pcienumcaps(p, matchvirtiocfgcap, typ);
610*2fb7fabbSDavid du Colombier }
611*2fb7fabbSDavid du Colombier 
612*2fb7fabbSDavid du Colombier static void*
virtiomapregs(Pcidev * p,int cap,int size)613*2fb7fabbSDavid du Colombier virtiomapregs(Pcidev *p, int cap, int size)
614*2fb7fabbSDavid du Colombier {
615*2fb7fabbSDavid du Colombier 	int bar, len;
616*2fb7fabbSDavid du Colombier 	uvlong addr;
617*2fb7fabbSDavid du Colombier 
618*2fb7fabbSDavid du Colombier 	if(cap < 0)
619*2fb7fabbSDavid du Colombier 		return nil;
620*2fb7fabbSDavid du Colombier 	bar = pcicfgr8(p, cap+4) % nelem(p->mem);
621*2fb7fabbSDavid du Colombier 	addr = pcicfgr32(p, cap+8);
622*2fb7fabbSDavid du Colombier 	len = pcicfgr32(p, cap+12);
623*2fb7fabbSDavid du Colombier 	if(size <= 0)
624*2fb7fabbSDavid du Colombier 		size = len;
625*2fb7fabbSDavid du Colombier 	else if(len < size)
626*2fb7fabbSDavid du Colombier 		return nil;
627*2fb7fabbSDavid du Colombier 	if(addr+len > p->mem[bar].size)
628*2fb7fabbSDavid du Colombier 		return nil;
629*2fb7fabbSDavid du Colombier 	addr += p->mem[bar].bar & ~0xFULL;
630*2fb7fabbSDavid du Colombier 	return vmap(addr, size);
631*2fb7fabbSDavid du Colombier }
632*2fb7fabbSDavid du Colombier 
633*2fb7fabbSDavid du Colombier static Ctlr*
pciprobe(void)634*2fb7fabbSDavid du Colombier pciprobe(void)
635*2fb7fabbSDavid du Colombier {
636*2fb7fabbSDavid du Colombier 	Ctlr *c, *h, *t;
637*2fb7fabbSDavid du Colombier 	Pcidev *p;
638*2fb7fabbSDavid du Colombier 	Vconfig *cfg;
639*2fb7fabbSDavid du Colombier 	int bar, cap, n, i;
640*2fb7fabbSDavid du Colombier 
641*2fb7fabbSDavid du Colombier 	h = t = nil;
642*2fb7fabbSDavid du Colombier 
643*2fb7fabbSDavid du Colombier 	/* §4.1.2 PCI Device Discovery */
644*2fb7fabbSDavid du Colombier 	for(p = nil; p = pcimatch(p, 0x1AF4, 0x1041);){
645*2fb7fabbSDavid du Colombier 		/* non-transitional devices will have a revision > 0 */
646*2fb7fabbSDavid du Colombier 		if(p->rid == 0)
647*2fb7fabbSDavid du Colombier 			continue;
648*2fb7fabbSDavid du Colombier 		if((cap = virtiocap(p, 1)) < 0)
649*2fb7fabbSDavid du Colombier 			continue;
650*2fb7fabbSDavid du Colombier 		bar = pcicfgr8(p, cap+4) % nelem(p->mem);
651*2fb7fabbSDavid du Colombier 		cfg = virtiomapregs(p, cap, sizeof(Vconfig));
652*2fb7fabbSDavid du Colombier 		if(cfg == nil)
653*2fb7fabbSDavid du Colombier 			continue;
654*2fb7fabbSDavid du Colombier 		if((c = mallocz(sizeof(Ctlr), 1)) == nil){
655*2fb7fabbSDavid du Colombier 			print("ethervirtio: no memory for Ctlr\n");
656*2fb7fabbSDavid du Colombier 			break;
657*2fb7fabbSDavid du Colombier 		}
658*2fb7fabbSDavid du Colombier 		c->cfg = cfg;
659*2fb7fabbSDavid du Colombier 		c->pcidev = p;
660*2fb7fabbSDavid du Colombier 		c->port = p->mem[bar].bar & ~0xFULL;
661*2fb7fabbSDavid du Colombier 
662*2fb7fabbSDavid du Colombier 		c->dev = virtiomapregs(p, virtiocap(p, 4), sizeof(Vnetcfg));
663*2fb7fabbSDavid du Colombier 		if(c->dev == nil)
664*2fb7fabbSDavid du Colombier 			goto Baddev;
665*2fb7fabbSDavid du Colombier 		c->isr = virtiomapregs(p, virtiocap(p, 3), 0);
666*2fb7fabbSDavid du Colombier 		if(c->isr == nil)
667*2fb7fabbSDavid du Colombier 			goto Baddev;
668*2fb7fabbSDavid du Colombier 		cap = virtiocap(p, 2);
669*2fb7fabbSDavid du Colombier 		c->notify = virtiomapregs(p, cap, 0);
670*2fb7fabbSDavid du Colombier 		if(c->notify == nil)
671*2fb7fabbSDavid du Colombier 			goto Baddev;
672*2fb7fabbSDavid du Colombier 		c->notifyoffmult = pcicfgr32(p, cap+16);
673*2fb7fabbSDavid du Colombier 
674*2fb7fabbSDavid du Colombier 		/* device reset */
675*2fb7fabbSDavid du Colombier 		coherence();
676*2fb7fabbSDavid du Colombier 		cfg->status = 0;
677*2fb7fabbSDavid du Colombier 		while(cfg->status != 0)
678*2fb7fabbSDavid du Colombier 			delay(1);
679*2fb7fabbSDavid du Colombier 		cfg->status = Sacknowledge|Sdriver;
680*2fb7fabbSDavid du Colombier 
681*2fb7fabbSDavid du Colombier 		/* negotiate feature bits */
682*2fb7fabbSDavid du Colombier 		cfg->devfeatsel = 1;
683*2fb7fabbSDavid du Colombier 		c->feat[1] = cfg->devfeat;
684*2fb7fabbSDavid du Colombier 
685*2fb7fabbSDavid du Colombier 		cfg->devfeatsel = 0;
686*2fb7fabbSDavid du Colombier 		c->feat[0] = cfg->devfeat;
687*2fb7fabbSDavid du Colombier 
688*2fb7fabbSDavid du Colombier 		cfg->drvfeatsel = 1;
689*2fb7fabbSDavid du Colombier 		cfg->drvfeat = c->feat[1] & Fversion1;
690*2fb7fabbSDavid du Colombier 
691*2fb7fabbSDavid du Colombier 		cfg->drvfeatsel = 0;
692*2fb7fabbSDavid du Colombier 		cfg->drvfeat = c->feat[0] & (Fmac|Fctrlvq|Fctrlrx);
693*2fb7fabbSDavid du Colombier 
694*2fb7fabbSDavid du Colombier 		cfg->status |= Sfeaturesok;
695*2fb7fabbSDavid du Colombier 
696*2fb7fabbSDavid du Colombier 		for(i=0; i<nelem(c->queue); i++){
697*2fb7fabbSDavid du Colombier 			cfg->queuesel = i;
698*2fb7fabbSDavid du Colombier 			n = cfg->queuesize;
699*2fb7fabbSDavid du Colombier 			if(n == 0 || (n & (n-1)) != 0){
700*2fb7fabbSDavid du Colombier 				if(i < 2)
701*2fb7fabbSDavid du Colombier 					print("ethervirtio: queue %d has invalid size %d\n", i, n);
702*2fb7fabbSDavid du Colombier 				break;
703*2fb7fabbSDavid du Colombier 			}
704*2fb7fabbSDavid du Colombier 			if(initqueue(&c->queue[i], n) < 0)
705*2fb7fabbSDavid du Colombier 				break;
706*2fb7fabbSDavid du Colombier 			c->queue[i].notify = c->notify + c->notifyoffmult * cfg->queuenotifyoff;
707*2fb7fabbSDavid du Colombier 			coherence();
708*2fb7fabbSDavid du Colombier 			cfg->queuedesc = PADDR(c->queue[i].desc);
709*2fb7fabbSDavid du Colombier 			cfg->queueavail = PADDR(c->queue[i].avail);
710*2fb7fabbSDavid du Colombier 			cfg->queueused = PADDR(c->queue[i].used);
711*2fb7fabbSDavid du Colombier 		}
712*2fb7fabbSDavid du Colombier 		if(i < 2){
713*2fb7fabbSDavid du Colombier 			print("ethervirtio: no queues\n");
714*2fb7fabbSDavid du Colombier Baddev:
715*2fb7fabbSDavid du Colombier 			/* TODO, vunmap */
716*2fb7fabbSDavid du Colombier 			free(c);
717*2fb7fabbSDavid du Colombier 			continue;
718*2fb7fabbSDavid du Colombier 		}
719*2fb7fabbSDavid du Colombier 		c->nqueue = i;
720*2fb7fabbSDavid du Colombier 
721*2fb7fabbSDavid du Colombier 		if(h == nil)
722*2fb7fabbSDavid du Colombier 			h = c;
723*2fb7fabbSDavid du Colombier 		else
724*2fb7fabbSDavid du Colombier 			t->next = c;
725*2fb7fabbSDavid du Colombier 		t = c;
726*2fb7fabbSDavid du Colombier 	}
727*2fb7fabbSDavid du Colombier 
728*2fb7fabbSDavid du Colombier 	return h;
729*2fb7fabbSDavid du Colombier }
730*2fb7fabbSDavid du Colombier 
731*2fb7fabbSDavid du Colombier 
732*2fb7fabbSDavid du Colombier static int
reset(Ether * edev)733*2fb7fabbSDavid du Colombier reset(Ether* edev)
734*2fb7fabbSDavid du Colombier {
735*2fb7fabbSDavid du Colombier 	static uchar zeros[Eaddrlen];
736*2fb7fabbSDavid du Colombier 	Ctlr *ctlr;
737*2fb7fabbSDavid du Colombier 	int i;
738*2fb7fabbSDavid du Colombier 
739*2fb7fabbSDavid du Colombier 	if(ctlrhead == nil)
740*2fb7fabbSDavid du Colombier 		ctlrhead = pciprobe();
741*2fb7fabbSDavid du Colombier 
742*2fb7fabbSDavid du Colombier 	for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){
743*2fb7fabbSDavid du Colombier 		if(ctlr->active)
744*2fb7fabbSDavid du Colombier 			continue;
745*2fb7fabbSDavid du Colombier 		if(edev->port == 0 || edev->port == ctlr->port){
746*2fb7fabbSDavid du Colombier 			ctlr->active = 1;
747*2fb7fabbSDavid du Colombier 			break;
748*2fb7fabbSDavid du Colombier 		}
749*2fb7fabbSDavid du Colombier 	}
750*2fb7fabbSDavid du Colombier 
751*2fb7fabbSDavid du Colombier 	if(ctlr == nil)
752*2fb7fabbSDavid du Colombier 		return -1;
753*2fb7fabbSDavid du Colombier 
754*2fb7fabbSDavid du Colombier 	edev->ctlr = ctlr;
755*2fb7fabbSDavid du Colombier 	edev->port = ctlr->port;
756*2fb7fabbSDavid du Colombier 	edev->irq = ctlr->pcidev->intl;
757*2fb7fabbSDavid du Colombier 	edev->tbdf = ctlr->pcidev->tbdf;
758*2fb7fabbSDavid du Colombier 	edev->mbps = 1000;
759*2fb7fabbSDavid du Colombier 	edev->link = 1;
760*2fb7fabbSDavid du Colombier 
761*2fb7fabbSDavid du Colombier 	if((ctlr->feat[0] & Fmac) != 0 && memcmp(edev->ea, zeros, Eaddrlen) == 0){
762*2fb7fabbSDavid du Colombier 		for(i = 0; i < Eaddrlen; i++)
763*2fb7fabbSDavid du Colombier 			edev->ea[i] = ((uchar*)ctlr->dev)[i];
764*2fb7fabbSDavid du Colombier 	} else {
765*2fb7fabbSDavid du Colombier 		for(i = 0; i < Eaddrlen; i++)
766*2fb7fabbSDavid du Colombier 			((uchar*)ctlr->dev)[i] = edev->ea[i];
767*2fb7fabbSDavid du Colombier 	}
768*2fb7fabbSDavid du Colombier 
769*2fb7fabbSDavid du Colombier 	edev->arg = edev;
770*2fb7fabbSDavid du Colombier 
771*2fb7fabbSDavid du Colombier 	edev->attach = attach;
772*2fb7fabbSDavid du Colombier 	edev->shutdown = shutdown;
773*2fb7fabbSDavid du Colombier 	edev->ifstat = ifstat;
774*2fb7fabbSDavid du Colombier 
775*2fb7fabbSDavid du Colombier 	if((ctlr->feat[0] & (Fctrlvq|Fctrlrx)) == (Fctrlvq|Fctrlrx)){
776*2fb7fabbSDavid du Colombier 		edev->multicast = multicast;
777*2fb7fabbSDavid du Colombier 		edev->promiscuous = promiscuous;
778*2fb7fabbSDavid du Colombier 	}
779*2fb7fabbSDavid du Colombier 
780*2fb7fabbSDavid du Colombier 	pcisetbme(ctlr->pcidev);
781*2fb7fabbSDavid du Colombier 	intrenable(edev->irq, interrupt, edev, edev->tbdf, edev->name);
782*2fb7fabbSDavid du Colombier 
783*2fb7fabbSDavid du Colombier 	return 0;
784*2fb7fabbSDavid du Colombier }
785*2fb7fabbSDavid du Colombier 
786*2fb7fabbSDavid du Colombier void
ethervirtio10link(void)787*2fb7fabbSDavid du Colombier ethervirtio10link(void)
788*2fb7fabbSDavid du Colombier {
789*2fb7fabbSDavid du Colombier 	addethercard("virtio10", reset);
790*2fb7fabbSDavid du Colombier }
791