xref: /plan9-contrib/sys/src/9/pc/sdvirtio.c (revision 505d3296342d501146b75d234a2da4157d515ac7)
1*505d3296SDavid du Colombier /*
2*505d3296SDavid du Colombier  * virtio ethernet driver implementing the legacy interface:
3*505d3296SDavid du Colombier  * http://docs.oasis-open.org/virtio/virtio/v1.0/virtio-v1.0.html
4*505d3296SDavid du Colombier  */
5*505d3296SDavid du Colombier #include "u.h"
6*505d3296SDavid du Colombier #include "../port/lib.h"
7*505d3296SDavid du Colombier #include "mem.h"
8*505d3296SDavid du Colombier #include "dat.h"
9*505d3296SDavid du Colombier #include "fns.h"
10*505d3296SDavid du Colombier #include "io.h"
11*505d3296SDavid du Colombier #include "ureg.h"
12*505d3296SDavid du Colombier #include "../port/error.h"
13*505d3296SDavid du Colombier 
14*505d3296SDavid du Colombier #include "../port/sd.h"
15*505d3296SDavid du Colombier 
16*505d3296SDavid du Colombier typedef struct Vring Vring;
17*505d3296SDavid du Colombier typedef struct Vdesc Vdesc;
18*505d3296SDavid du Colombier typedef struct Vused Vused;
19*505d3296SDavid du Colombier typedef struct Vqueue Vqueue;
20*505d3296SDavid du Colombier typedef struct Vdev Vdev;
21*505d3296SDavid du Colombier 
22*505d3296SDavid du Colombier typedef struct ScsiCfg ScsiCfg;
23*505d3296SDavid du Colombier 
24*505d3296SDavid du Colombier /* device types */
25*505d3296SDavid du Colombier enum {
26*505d3296SDavid du Colombier 	TypBlk	= 2,
27*505d3296SDavid du Colombier 	TypSCSI	= 8,
28*505d3296SDavid du Colombier };
29*505d3296SDavid du Colombier 
30*505d3296SDavid du Colombier /* status flags */
31*505d3296SDavid du Colombier enum {
32*505d3296SDavid du Colombier 	Acknowledge = 1,
33*505d3296SDavid du Colombier 	Driver = 2,
34*505d3296SDavid du Colombier 	DriverOk = 4,
35*505d3296SDavid du Colombier 	Failed = 0x80,
36*505d3296SDavid du Colombier };
37*505d3296SDavid du Colombier 
38*505d3296SDavid du Colombier /* virtio ports */
39*505d3296SDavid du Colombier enum {
40*505d3296SDavid du Colombier 	Devfeat = 0,
41*505d3296SDavid du Colombier 	Drvfeat = 4,
42*505d3296SDavid du Colombier 	Qaddr = 8,
43*505d3296SDavid du Colombier 	Qsize = 12,
44*505d3296SDavid du Colombier 	Qselect = 14,
45*505d3296SDavid du Colombier 	Qnotify = 16,
46*505d3296SDavid du Colombier 	Status = 18,
47*505d3296SDavid du Colombier 	Isr = 19,
48*505d3296SDavid du Colombier 
49*505d3296SDavid du Colombier 	Devspec = 20,
50*505d3296SDavid du Colombier };
51*505d3296SDavid du Colombier 
52*505d3296SDavid du Colombier /* descriptor flags */
53*505d3296SDavid du Colombier enum {
54*505d3296SDavid du Colombier 	Next = 1,
55*505d3296SDavid du Colombier 	Write = 2,
56*505d3296SDavid du Colombier 	Indirect = 4,
57*505d3296SDavid du Colombier };
58*505d3296SDavid du Colombier 
59*505d3296SDavid du Colombier /* struct sizes */
60*505d3296SDavid du Colombier enum {
61*505d3296SDavid du Colombier 	VringSize = 4,
62*505d3296SDavid du Colombier };
63*505d3296SDavid du Colombier 
64*505d3296SDavid du Colombier struct Vring
65*505d3296SDavid du Colombier {
66*505d3296SDavid du Colombier 	u16int	flags;
67*505d3296SDavid du Colombier 	u16int	idx;
68*505d3296SDavid du Colombier };
69*505d3296SDavid du Colombier 
70*505d3296SDavid du Colombier struct Vdesc
71*505d3296SDavid du Colombier {
72*505d3296SDavid du Colombier 	u64int	addr;
73*505d3296SDavid du Colombier 	u32int	len;
74*505d3296SDavid du Colombier 	u16int	flags;
75*505d3296SDavid du Colombier 	u16int	next;
76*505d3296SDavid du Colombier };
77*505d3296SDavid du Colombier 
78*505d3296SDavid du Colombier struct Vused
79*505d3296SDavid du Colombier {
80*505d3296SDavid du Colombier 	u32int	id;
81*505d3296SDavid du Colombier 	u32int	len;
82*505d3296SDavid du Colombier };
83*505d3296SDavid du Colombier 
84*505d3296SDavid du Colombier struct Vqueue
85*505d3296SDavid du Colombier {
86*505d3296SDavid du Colombier 	Lock;
87*505d3296SDavid du Colombier 
88*505d3296SDavid du Colombier 	Vdev	*dev;
89*505d3296SDavid du Colombier 	int	idx;
90*505d3296SDavid du Colombier 
91*505d3296SDavid du Colombier 	int	size;
92*505d3296SDavid du Colombier 
93*505d3296SDavid du Colombier 	int	free;
94*505d3296SDavid du Colombier 	int	nfree;
95*505d3296SDavid du Colombier 
96*505d3296SDavid du Colombier 	Vdesc	*desc;
97*505d3296SDavid du Colombier 
98*505d3296SDavid du Colombier 	Vring	*avail;
99*505d3296SDavid du Colombier 	u16int	*availent;
100*505d3296SDavid du Colombier 	u16int	*availevent;
101*505d3296SDavid du Colombier 
102*505d3296SDavid du Colombier 	Vring	*used;
103*505d3296SDavid du Colombier 	Vused	*usedent;
104*505d3296SDavid du Colombier 	u16int	*usedevent;
105*505d3296SDavid du Colombier 	u16int	lastused;
106*505d3296SDavid du Colombier 
107*505d3296SDavid du Colombier 	void	*rock[];
108*505d3296SDavid du Colombier };
109*505d3296SDavid du Colombier 
110*505d3296SDavid du Colombier struct Vdev
111*505d3296SDavid du Colombier {
112*505d3296SDavid du Colombier 	int	typ;
113*505d3296SDavid du Colombier 
114*505d3296SDavid du Colombier 	Pcidev	*pci;
115*505d3296SDavid du Colombier 
116*505d3296SDavid du Colombier 	ulong	port;
117*505d3296SDavid du Colombier 	ulong	feat;
118*505d3296SDavid du Colombier 
119*505d3296SDavid du Colombier 	int	nqueue;
120*505d3296SDavid du Colombier 	Vqueue	*queue[16];
121*505d3296SDavid du Colombier 
122*505d3296SDavid du Colombier 	void	*cfg;	/* device specific config (for scsi) */
123*505d3296SDavid du Colombier 
124*505d3296SDavid du Colombier 	Vdev	*next;
125*505d3296SDavid du Colombier };
126*505d3296SDavid du Colombier 
127*505d3296SDavid du Colombier enum {
128*505d3296SDavid du Colombier 	CDBSIZE		= 32,
129*505d3296SDavid du Colombier 	SENSESIZE	= 96,
130*505d3296SDavid du Colombier };
131*505d3296SDavid du Colombier 
132*505d3296SDavid du Colombier struct ScsiCfg
133*505d3296SDavid du Colombier {
134*505d3296SDavid du Colombier 	u32int	num_queues;
135*505d3296SDavid du Colombier 	u32int	seg_max;
136*505d3296SDavid du Colombier 	u32int	max_sectors;
137*505d3296SDavid du Colombier 	u32int	cmd_per_lun;
138*505d3296SDavid du Colombier 	u32int	event_info_size;
139*505d3296SDavid du Colombier 	u32int	sense_size;
140*505d3296SDavid du Colombier 	u32int	cdb_size;
141*505d3296SDavid du Colombier 	u16int	max_channel;
142*505d3296SDavid du Colombier 	u16int	max_target;
143*505d3296SDavid du Colombier 	u32int	max_lun;
144*505d3296SDavid du Colombier };
145*505d3296SDavid du Colombier 
146*505d3296SDavid du Colombier static Vqueue*
mkvqueue(int size)147*505d3296SDavid du Colombier mkvqueue(int size)
148*505d3296SDavid du Colombier {
149*505d3296SDavid du Colombier 	Vqueue *q;
150*505d3296SDavid du Colombier 	uchar *p;
151*505d3296SDavid du Colombier 	int i;
152*505d3296SDavid du Colombier 
153*505d3296SDavid du Colombier 	q = malloc(sizeof(*q) + sizeof(void*)*size);
154*505d3296SDavid du Colombier 	p = mallocalign(
155*505d3296SDavid du Colombier 		PGROUND(sizeof(Vdesc)*size +
156*505d3296SDavid du Colombier 			VringSize +
157*505d3296SDavid du Colombier 			sizeof(u16int)*size +
158*505d3296SDavid du Colombier 			sizeof(u16int)) +
159*505d3296SDavid du Colombier 		PGROUND(VringSize +
160*505d3296SDavid du Colombier 			sizeof(Vused)*size +
161*505d3296SDavid du Colombier 			sizeof(u16int)),
162*505d3296SDavid du Colombier 		BY2PG, 0, 0);
163*505d3296SDavid du Colombier 	if(p == nil || q == nil){
164*505d3296SDavid du Colombier 		print("virtio: no memory for Vqueue\n");
165*505d3296SDavid du Colombier 		free(p);
166*505d3296SDavid du Colombier 		free(q);
167*505d3296SDavid du Colombier 		return nil;
168*505d3296SDavid du Colombier 	}
169*505d3296SDavid du Colombier 
170*505d3296SDavid du Colombier 	q->desc = (void*)p;
171*505d3296SDavid du Colombier 	p += sizeof(Vdesc)*size;
172*505d3296SDavid du Colombier 	q->avail = (void*)p;
173*505d3296SDavid du Colombier 	p += VringSize;
174*505d3296SDavid du Colombier 	q->availent = (void*)p;
175*505d3296SDavid du Colombier 	p += sizeof(u16int)*size;
176*505d3296SDavid du Colombier 	q->availevent = (void*)p;
177*505d3296SDavid du Colombier 	p += sizeof(u16int);
178*505d3296SDavid du Colombier 
179*505d3296SDavid du Colombier 	p = (uchar*)PGROUND((uintptr)p);
180*505d3296SDavid du Colombier 	q->used = (void*)p;
181*505d3296SDavid du Colombier 	p += VringSize;
182*505d3296SDavid du Colombier 	q->usedent = (void*)p;
183*505d3296SDavid du Colombier 	p += sizeof(Vused)*size;
184*505d3296SDavid du Colombier 	q->usedevent = (void*)p;
185*505d3296SDavid du Colombier 
186*505d3296SDavid du Colombier 	q->free = -1;
187*505d3296SDavid du Colombier 	q->nfree = q->size = size;
188*505d3296SDavid du Colombier 	for(i=0; i<size; i++){
189*505d3296SDavid du Colombier 		q->desc[i].next = q->free;
190*505d3296SDavid du Colombier 		q->free = i;
191*505d3296SDavid du Colombier 	}
192*505d3296SDavid du Colombier 
193*505d3296SDavid du Colombier 	return q;
194*505d3296SDavid du Colombier }
195*505d3296SDavid du Colombier 
196*505d3296SDavid du Colombier static Vdev*
viopnpdevs(int typ)197*505d3296SDavid du Colombier viopnpdevs(int typ)
198*505d3296SDavid du Colombier {
199*505d3296SDavid du Colombier 	Vdev *vd, *h, *t;
200*505d3296SDavid du Colombier 	Vqueue *q;
201*505d3296SDavid du Colombier 	Pcidev *p;
202*505d3296SDavid du Colombier 	int n, i, size;
203*505d3296SDavid du Colombier 
204*505d3296SDavid du Colombier 	h = t = nil;
205*505d3296SDavid du Colombier 	for(p = nil; p = pcimatch(p, 0x1AF4, 0);){
206*505d3296SDavid du Colombier 		if((p->did < 0x1000) || (p->did > 0x103F))
207*505d3296SDavid du Colombier 			continue;
208*505d3296SDavid du Colombier 		if(p->rid != 0)
209*505d3296SDavid du Colombier 			continue;
210*505d3296SDavid du Colombier 		//if((p->mem[0].bar & 1) == 0) /* zero on gce */
211*505d3296SDavid du Colombier 		//	continue;
212*505d3296SDavid du Colombier 		if(pcicfgr16(p, 0x2E) != typ)
213*505d3296SDavid du Colombier 			continue;
214*505d3296SDavid du Colombier 		if((vd = malloc(sizeof(*vd))) == nil){
215*505d3296SDavid du Colombier 			print("virtio: no memory for Vdev\n");
216*505d3296SDavid du Colombier 			break;
217*505d3296SDavid du Colombier 		}
218*505d3296SDavid du Colombier 		vd->port = p->mem[0].bar & ~3;
219*505d3296SDavid du Colombier 		size = p->mem[0].size;
220*505d3296SDavid du Colombier 		if(vd->port == 0){ /* gce */
221*505d3296SDavid du Colombier 			vd->port = 0xc000;
222*505d3296SDavid du Colombier 			size = 0x40;
223*505d3296SDavid du Colombier 		}
224*505d3296SDavid du Colombier 		if(ioalloc(vd->port, size, 0, "virtio") < 0){
225*505d3296SDavid du Colombier 			print("virtio: port %lux in use\n", vd->port);
226*505d3296SDavid du Colombier 			free(vd);
227*505d3296SDavid du Colombier 			continue;
228*505d3296SDavid du Colombier 		}
229*505d3296SDavid du Colombier 		vd->typ = typ;
230*505d3296SDavid du Colombier 		vd->pci = p;
231*505d3296SDavid du Colombier 
232*505d3296SDavid du Colombier 		/* reset */
233*505d3296SDavid du Colombier 		outb(vd->port+Status, 0);
234*505d3296SDavid du Colombier 
235*505d3296SDavid du Colombier 		vd->feat = inl(vd->port+Devfeat);
236*505d3296SDavid du Colombier 		outb(vd->port+Status, Acknowledge|Driver);
237*505d3296SDavid du Colombier 		for(i=0; i<nelem(vd->queue); i++){
238*505d3296SDavid du Colombier 			outs(vd->port+Qselect, i);
239*505d3296SDavid du Colombier 			n = ins(vd->port+Qsize);
240*505d3296SDavid du Colombier 			if(n == 0 || (n & (n-1)) != 0)
241*505d3296SDavid du Colombier 				break;
242*505d3296SDavid du Colombier 			if((q = mkvqueue(n)) == nil)
243*505d3296SDavid du Colombier 				break;
244*505d3296SDavid du Colombier 			q->dev = vd;
245*505d3296SDavid du Colombier 			q->idx = i;
246*505d3296SDavid du Colombier 			vd->queue[i] = q;
247*505d3296SDavid du Colombier 			coherence();
248*505d3296SDavid du Colombier 			outl(vd->port+Qaddr, PADDR(vd->queue[i]->desc)/BY2PG);
249*505d3296SDavid du Colombier 		}
250*505d3296SDavid du Colombier 		vd->nqueue = i;
251*505d3296SDavid du Colombier 
252*505d3296SDavid du Colombier 		if(h == nil)
253*505d3296SDavid du Colombier 			h = vd;
254*505d3296SDavid du Colombier 		else
255*505d3296SDavid du Colombier 			t->next = vd;
256*505d3296SDavid du Colombier 		t = vd;
257*505d3296SDavid du Colombier 	}
258*505d3296SDavid du Colombier 
259*505d3296SDavid du Colombier 	return h;
260*505d3296SDavid du Colombier }
261*505d3296SDavid du Colombier 
262*505d3296SDavid du Colombier struct Rock {
263*505d3296SDavid du Colombier 	int done;
264*505d3296SDavid du Colombier 	Rendez *sleep;
265*505d3296SDavid du Colombier };
266*505d3296SDavid du Colombier 
267*505d3296SDavid du Colombier static void
vqinterrupt(Vqueue * q)268*505d3296SDavid du Colombier vqinterrupt(Vqueue *q)
269*505d3296SDavid du Colombier {
270*505d3296SDavid du Colombier 	int id, free, m;
271*505d3296SDavid du Colombier 	struct Rock *r;
272*505d3296SDavid du Colombier 	Rendez *z;
273*505d3296SDavid du Colombier 
274*505d3296SDavid du Colombier 	m = q->size-1;
275*505d3296SDavid du Colombier 
276*505d3296SDavid du Colombier 	ilock(q);
277*505d3296SDavid du Colombier 	while((q->lastused ^ q->used->idx) & m){
278*505d3296SDavid du Colombier 		id = q->usedent[q->lastused++ & m].id;
279*505d3296SDavid du Colombier 		if(r = q->rock[id]){
280*505d3296SDavid du Colombier 			q->rock[id] = nil;
281*505d3296SDavid du Colombier 			z = r->sleep;
282*505d3296SDavid du Colombier 			r->done = 1;	/* hands off */
283*505d3296SDavid du Colombier 			if(z != nil)
284*505d3296SDavid du Colombier 				wakeup(z);
285*505d3296SDavid du Colombier 		}
286*505d3296SDavid du Colombier 		do {
287*505d3296SDavid du Colombier 			free = id;
288*505d3296SDavid du Colombier 			id = q->desc[free].next;
289*505d3296SDavid du Colombier 			q->desc[free].next = q->free;
290*505d3296SDavid du Colombier 			q->free = free;
291*505d3296SDavid du Colombier 			q->nfree++;
292*505d3296SDavid du Colombier 		} while(q->desc[free].flags & Next);
293*505d3296SDavid du Colombier 	}
294*505d3296SDavid du Colombier 	iunlock(q);
295*505d3296SDavid du Colombier }
296*505d3296SDavid du Colombier 
297*505d3296SDavid du Colombier static void
viointerrupt(Ureg *,void * arg)298*505d3296SDavid du Colombier viointerrupt(Ureg *, void *arg)
299*505d3296SDavid du Colombier {
300*505d3296SDavid du Colombier 	Vdev *vd = arg;
301*505d3296SDavid du Colombier 
302*505d3296SDavid du Colombier 	if(inb(vd->port+Isr) & 1)
303*505d3296SDavid du Colombier 		vqinterrupt(vd->queue[vd->typ == TypSCSI ? 2 : 0]);
304*505d3296SDavid du Colombier }
305*505d3296SDavid du Colombier 
306*505d3296SDavid du Colombier static int
viodone(void * arg)307*505d3296SDavid du Colombier viodone(void *arg)
308*505d3296SDavid du Colombier {
309*505d3296SDavid du Colombier 	return ((struct Rock*)arg)->done;
310*505d3296SDavid du Colombier }
311*505d3296SDavid du Colombier 
312*505d3296SDavid du Colombier static void
vqio(Vqueue * q,int head)313*505d3296SDavid du Colombier vqio(Vqueue *q, int head)
314*505d3296SDavid du Colombier {
315*505d3296SDavid du Colombier 	struct Rock rock;
316*505d3296SDavid du Colombier 
317*505d3296SDavid du Colombier 	rock.done = 0;
318*505d3296SDavid du Colombier 	rock.sleep = &up->sleep;
319*505d3296SDavid du Colombier 	q->rock[head] = &rock;
320*505d3296SDavid du Colombier 	q->availent[q->avail->idx & (q->size-1)] = head;
321*505d3296SDavid du Colombier 	coherence();
322*505d3296SDavid du Colombier 	q->avail->idx++;
323*505d3296SDavid du Colombier 	iunlock(q);
324*505d3296SDavid du Colombier 	if((q->used->flags & 1) == 0)
325*505d3296SDavid du Colombier 		outs(q->dev->port+Qnotify, q->idx);
326*505d3296SDavid du Colombier 	while(!rock.done){
327*505d3296SDavid du Colombier 		while(waserror())
328*505d3296SDavid du Colombier 			;
329*505d3296SDavid du Colombier 		tsleep(rock.sleep, viodone, &rock, 1000);
330*505d3296SDavid du Colombier 		poperror();
331*505d3296SDavid du Colombier 
332*505d3296SDavid du Colombier 		if(!rock.done)
333*505d3296SDavid du Colombier 			vqinterrupt(q);
334*505d3296SDavid du Colombier 	}
335*505d3296SDavid du Colombier }
336*505d3296SDavid du Colombier 
337*505d3296SDavid du Colombier static int
vioblkreq(Vdev * vd,int typ,void * a,long count,long secsize,uvlong lba)338*505d3296SDavid du Colombier vioblkreq(Vdev *vd, int typ, void *a, long count, long secsize, uvlong lba)
339*505d3296SDavid du Colombier {
340*505d3296SDavid du Colombier 	int need, free, head;
341*505d3296SDavid du Colombier 	Vqueue *q;
342*505d3296SDavid du Colombier 	Vdesc *d;
343*505d3296SDavid du Colombier 
344*505d3296SDavid du Colombier 	u8int status;
345*505d3296SDavid du Colombier 	struct Vioblkreqhdr {
346*505d3296SDavid du Colombier 		u32int	typ;
347*505d3296SDavid du Colombier 		u32int	prio;
348*505d3296SDavid du Colombier 		u64int	lba;
349*505d3296SDavid du Colombier 	} req;
350*505d3296SDavid du Colombier 
351*505d3296SDavid du Colombier 	need = 2;
352*505d3296SDavid du Colombier 	if(a != nil)
353*505d3296SDavid du Colombier 		need = 3;
354*505d3296SDavid du Colombier 
355*505d3296SDavid du Colombier 	status = -1;
356*505d3296SDavid du Colombier 	req.typ = typ;
357*505d3296SDavid du Colombier 	req.prio = 0;
358*505d3296SDavid du Colombier 	req.lba = lba;
359*505d3296SDavid du Colombier 
360*505d3296SDavid du Colombier 	q = vd->queue[0];
361*505d3296SDavid du Colombier 	ilock(q);
362*505d3296SDavid du Colombier 	while(q->nfree < need){
363*505d3296SDavid du Colombier 		iunlock(q);
364*505d3296SDavid du Colombier 
365*505d3296SDavid du Colombier 		if(!waserror())
366*505d3296SDavid du Colombier 			tsleep(&up->sleep, return0, 0, 500);
367*505d3296SDavid du Colombier 		poperror();
368*505d3296SDavid du Colombier 
369*505d3296SDavid du Colombier 		ilock(q);
370*505d3296SDavid du Colombier 	}
371*505d3296SDavid du Colombier 
372*505d3296SDavid du Colombier 	head = free = q->free;
373*505d3296SDavid du Colombier 
374*505d3296SDavid du Colombier 	d = &q->desc[free]; free = d->next;
375*505d3296SDavid du Colombier 	d->addr = PADDR(&req);
376*505d3296SDavid du Colombier 	d->len = sizeof(req);
377*505d3296SDavid du Colombier 	d->flags = Next;
378*505d3296SDavid du Colombier 
379*505d3296SDavid du Colombier 	if(a != nil){
380*505d3296SDavid du Colombier 		d = &q->desc[free]; free = d->next;
381*505d3296SDavid du Colombier 		d->addr = PADDR(a);
382*505d3296SDavid du Colombier 		d->len = secsize*count;
383*505d3296SDavid du Colombier 		d->flags = typ ? Next : (Write|Next);
384*505d3296SDavid du Colombier 	}
385*505d3296SDavid du Colombier 
386*505d3296SDavid du Colombier 	d = &q->desc[free]; free = d->next;
387*505d3296SDavid du Colombier 	d->addr = PADDR(&status);
388*505d3296SDavid du Colombier 	d->len = sizeof(status);
389*505d3296SDavid du Colombier 	d->flags = Write;
390*505d3296SDavid du Colombier 
391*505d3296SDavid du Colombier 	q->free = free;
392*505d3296SDavid du Colombier 	q->nfree -= need;
393*505d3296SDavid du Colombier 
394*505d3296SDavid du Colombier 	/* queue io, unlock and wait for completion */
395*505d3296SDavid du Colombier 	vqio(q, head);
396*505d3296SDavid du Colombier 
397*505d3296SDavid du Colombier 	return status;
398*505d3296SDavid du Colombier }
399*505d3296SDavid du Colombier 
400*505d3296SDavid du Colombier static int
vioscsireq(SDreq * r)401*505d3296SDavid du Colombier vioscsireq(SDreq *r)
402*505d3296SDavid du Colombier {
403*505d3296SDavid du Colombier 	u8int resp[4+4+2+2+SENSESIZE];
404*505d3296SDavid du Colombier 	u8int req[8+8+3+CDBSIZE];
405*505d3296SDavid du Colombier 	int free, head;
406*505d3296SDavid du Colombier 	u32int len;
407*505d3296SDavid du Colombier 	Vqueue *q;
408*505d3296SDavid du Colombier 	Vdesc *d;
409*505d3296SDavid du Colombier 	Vdev *vd;
410*505d3296SDavid du Colombier 	SDunit *u;
411*505d3296SDavid du Colombier 	ScsiCfg *cfg;
412*505d3296SDavid du Colombier 
413*505d3296SDavid du Colombier 	u = r->unit;
414*505d3296SDavid du Colombier 	vd = u->dev->ctlr;
415*505d3296SDavid du Colombier 	cfg = vd->cfg;
416*505d3296SDavid du Colombier 
417*505d3296SDavid du Colombier 	memset(resp, 0, sizeof(resp));
418*505d3296SDavid du Colombier 	memset(req, 0, sizeof(req));
419*505d3296SDavid du Colombier 	req[0] = 1;
420*505d3296SDavid du Colombier 	req[1] = u->subno;
421*505d3296SDavid du Colombier 	req[2] = r->lun>>8;
422*505d3296SDavid du Colombier 	req[3] = r->lun&0xFF;
423*505d3296SDavid du Colombier 	*(u64int*)(&req[8]) = (uintptr)r;
424*505d3296SDavid du Colombier 
425*505d3296SDavid du Colombier 	memmove(&req[8+8+3], r->cmd, r->clen);
426*505d3296SDavid du Colombier 
427*505d3296SDavid du Colombier 	q = vd->queue[2];
428*505d3296SDavid du Colombier 	ilock(q);
429*505d3296SDavid du Colombier 	while(q->nfree < 3){
430*505d3296SDavid du Colombier 		iunlock(q);
431*505d3296SDavid du Colombier 
432*505d3296SDavid du Colombier 		if(!waserror())
433*505d3296SDavid du Colombier 			tsleep(&up->sleep, return0, 0, 500);
434*505d3296SDavid du Colombier 		poperror();
435*505d3296SDavid du Colombier 
436*505d3296SDavid du Colombier 		ilock(q);
437*505d3296SDavid du Colombier 	}
438*505d3296SDavid du Colombier 
439*505d3296SDavid du Colombier 	head = free = q->free;
440*505d3296SDavid du Colombier 
441*505d3296SDavid du Colombier 	d = &q->desc[free]; free = d->next;
442*505d3296SDavid du Colombier 	d->addr = PADDR(req);
443*505d3296SDavid du Colombier 	d->len = 8+8+3+cfg->cdb_size;
444*505d3296SDavid du Colombier 	d->flags = Next;
445*505d3296SDavid du Colombier 
446*505d3296SDavid du Colombier 	if(r->write && r->dlen > 0){
447*505d3296SDavid du Colombier 		d = &q->desc[free]; free = d->next;
448*505d3296SDavid du Colombier 		d->addr = PADDR(r->data);
449*505d3296SDavid du Colombier 		d->len = r->dlen;
450*505d3296SDavid du Colombier 		d->flags = Next;
451*505d3296SDavid du Colombier 	}
452*505d3296SDavid du Colombier 
453*505d3296SDavid du Colombier 	d = &q->desc[free]; free = d->next;
454*505d3296SDavid du Colombier 	d->addr = PADDR(resp);
455*505d3296SDavid du Colombier 	d->len = 4+4+2+2+cfg->sense_size;
456*505d3296SDavid du Colombier 	d->flags = Write;
457*505d3296SDavid du Colombier 
458*505d3296SDavid du Colombier 	if(!r->write && r->dlen > 0){
459*505d3296SDavid du Colombier 		d->flags |= Next;
460*505d3296SDavid du Colombier 
461*505d3296SDavid du Colombier 		d = &q->desc[free]; free = d->next;
462*505d3296SDavid du Colombier 		d->addr = PADDR(r->data);
463*505d3296SDavid du Colombier 		d->len = r->dlen;
464*505d3296SDavid du Colombier 		d->flags = Write;
465*505d3296SDavid du Colombier 	}
466*505d3296SDavid du Colombier 
467*505d3296SDavid du Colombier 	q->free = free;
468*505d3296SDavid du Colombier 	q->nfree -= 2 + (r->dlen > 0);
469*505d3296SDavid du Colombier 
470*505d3296SDavid du Colombier 	/* queue io, unlock and wait for completion */
471*505d3296SDavid du Colombier 	vqio(q, head);
472*505d3296SDavid du Colombier 
473*505d3296SDavid du Colombier 	/* response+status */
474*505d3296SDavid du Colombier 	r->status = resp[10];
475*505d3296SDavid du Colombier 	if(resp[11] != 0)
476*505d3296SDavid du Colombier 		r->status = SDcheck;
477*505d3296SDavid du Colombier 
478*505d3296SDavid du Colombier 	/* sense_len */
479*505d3296SDavid du Colombier 	len = *((u32int*)&resp[0]);
480*505d3296SDavid du Colombier 	if(len > 0){
481*505d3296SDavid du Colombier 		if(len > sizeof(r->sense))
482*505d3296SDavid du Colombier 			len = sizeof(r->sense);
483*505d3296SDavid du Colombier 		memmove(r->sense, &resp[4+4+2+2], len);
484*505d3296SDavid du Colombier 		r->flags |= SDvalidsense;
485*505d3296SDavid du Colombier 	}
486*505d3296SDavid du Colombier 
487*505d3296SDavid du Colombier 	/* data residue */
488*505d3296SDavid du Colombier 	len = *((u32int*)&resp[4]);
489*505d3296SDavid du Colombier 	if(len > r->dlen)
490*505d3296SDavid du Colombier 		r->rlen = 0;
491*505d3296SDavid du Colombier 	else
492*505d3296SDavid du Colombier 		r->rlen = r->dlen - len;
493*505d3296SDavid du Colombier 
494*505d3296SDavid du Colombier 	return r->status;
495*505d3296SDavid du Colombier 
496*505d3296SDavid du Colombier }
497*505d3296SDavid du Colombier 
498*505d3296SDavid du Colombier static long
viobio(SDunit * u,int lun,int write,void * a,long count,uvlong lba)499*505d3296SDavid du Colombier viobio(SDunit *u, int lun, int write, void *a, long count, uvlong lba)
500*505d3296SDavid du Colombier {
501*505d3296SDavid du Colombier 	long ss, cc, max, ret;
502*505d3296SDavid du Colombier 	Vdev *vd;
503*505d3296SDavid du Colombier 
504*505d3296SDavid du Colombier 	vd = u->dev->ctlr;
505*505d3296SDavid du Colombier 	if(vd->typ == TypSCSI)
506*505d3296SDavid du Colombier 		return scsibio(u, lun, write, a, count, lba);
507*505d3296SDavid du Colombier 
508*505d3296SDavid du Colombier 	max = 32;
509*505d3296SDavid du Colombier 	ss = u->secsize;
510*505d3296SDavid du Colombier 	ret = 0;
511*505d3296SDavid du Colombier 	while(count > 0){
512*505d3296SDavid du Colombier 		if((cc = count) > max)
513*505d3296SDavid du Colombier 			cc = max;
514*505d3296SDavid du Colombier 		if(vioblkreq(vd, write != 0, (uchar*)a + ret, cc, ss, lba) != 0)
515*505d3296SDavid du Colombier 			error(Eio);
516*505d3296SDavid du Colombier 		ret += cc*ss;
517*505d3296SDavid du Colombier 		count -= cc;
518*505d3296SDavid du Colombier 		lba += cc;
519*505d3296SDavid du Colombier 	}
520*505d3296SDavid du Colombier 	return ret;
521*505d3296SDavid du Colombier }
522*505d3296SDavid du Colombier 
523*505d3296SDavid du Colombier enum {
524*505d3296SDavid du Colombier 	SDread,
525*505d3296SDavid du Colombier 	SDwrite,
526*505d3296SDavid du Colombier };
527*505d3296SDavid du Colombier 
528*505d3296SDavid du Colombier static int
viorio(SDreq * r)529*505d3296SDavid du Colombier viorio(SDreq *r)
530*505d3296SDavid du Colombier {
531*505d3296SDavid du Colombier 	int i, count, rw;
532*505d3296SDavid du Colombier 	uvlong lba;
533*505d3296SDavid du Colombier 	SDunit *u;
534*505d3296SDavid du Colombier 	Vdev *vd;
535*505d3296SDavid du Colombier 
536*505d3296SDavid du Colombier 	u = r->unit;
537*505d3296SDavid du Colombier 	vd = u->dev->ctlr;
538*505d3296SDavid du Colombier 	if(vd->typ == TypSCSI)
539*505d3296SDavid du Colombier 		return vioscsireq(r);
540*505d3296SDavid du Colombier 	if(r->cmd[0] == 0x35 || r->cmd[0] == 0x91){
541*505d3296SDavid du Colombier 		if(vioblkreq(vd, 4, nil, 0, 0, 0) != 0)
542*505d3296SDavid du Colombier 			return sdsetsense(r, SDcheck, 3, 0xc, 2);
543*505d3296SDavid du Colombier 		return sdsetsense(r, SDok, 0, 0, 0);
544*505d3296SDavid du Colombier 	}
545*505d3296SDavid du Colombier 	if((i = sdfakescsi(r, nil, 0)) != SDnostatus)
546*505d3296SDavid du Colombier 		return r->status = i;
547*505d3296SDavid du Colombier 	if((i = sdfakescsirw(r, &lba, &count, &rw)) != SDnostatus)
548*505d3296SDavid du Colombier 		return i;
549*505d3296SDavid du Colombier 	r->rlen = viobio(u, r->lun, rw == SDwrite, r->data, count, lba);
550*505d3296SDavid du Colombier 	return r->status = SDok;
551*505d3296SDavid du Colombier }
552*505d3296SDavid du Colombier 
553*505d3296SDavid du Colombier static int
vioonline(SDunit * u)554*505d3296SDavid du Colombier vioonline(SDunit *u)
555*505d3296SDavid du Colombier {
556*505d3296SDavid du Colombier 	uvlong cap;
557*505d3296SDavid du Colombier 	Vdev *vd;
558*505d3296SDavid du Colombier 
559*505d3296SDavid du Colombier 	vd = u->dev->ctlr;
560*505d3296SDavid du Colombier 	if(vd->typ == TypSCSI)
561*505d3296SDavid du Colombier 		return scsionline(u);
562*505d3296SDavid du Colombier 
563*505d3296SDavid du Colombier 	cap = inl(vd->port+Devspec+4);
564*505d3296SDavid du Colombier 	cap <<= 32;
565*505d3296SDavid du Colombier 	cap |= inl(vd->port+Devspec);
566*505d3296SDavid du Colombier 	if(u->sectors != cap){
567*505d3296SDavid du Colombier 		u->sectors = cap;
568*505d3296SDavid du Colombier 		u->secsize = 512;
569*505d3296SDavid du Colombier 		return 2;
570*505d3296SDavid du Colombier 	}
571*505d3296SDavid du Colombier 	return 1;
572*505d3296SDavid du Colombier }
573*505d3296SDavid du Colombier 
574*505d3296SDavid du Colombier static int
vioverify(SDunit * u)575*505d3296SDavid du Colombier vioverify(SDunit *u)
576*505d3296SDavid du Colombier {
577*505d3296SDavid du Colombier 	Vdev *vd;
578*505d3296SDavid du Colombier 
579*505d3296SDavid du Colombier 	vd = u->dev->ctlr;
580*505d3296SDavid du Colombier 	if(vd->typ == TypSCSI)
581*505d3296SDavid du Colombier 		return scsiverify(u);
582*505d3296SDavid du Colombier 
583*505d3296SDavid du Colombier 	return 1;
584*505d3296SDavid du Colombier }
585*505d3296SDavid du Colombier 
586*505d3296SDavid du Colombier SDifc sdvirtioifc;
587*505d3296SDavid du Colombier 
588*505d3296SDavid du Colombier static int
vioenable(SDev * sd)589*505d3296SDavid du Colombier vioenable(SDev *sd)
590*505d3296SDavid du Colombier {
591*505d3296SDavid du Colombier 	char name[32];
592*505d3296SDavid du Colombier 	Vdev *vd;
593*505d3296SDavid du Colombier 
594*505d3296SDavid du Colombier 	vd = sd->ctlr;
595*505d3296SDavid du Colombier 	pcisetbme(vd->pci);
596*505d3296SDavid du Colombier 	snprint(name, sizeof(name), "%s (%s)", sd->name, sd->ifc->name);
597*505d3296SDavid du Colombier 	intrenable(vd->pci->intl, viointerrupt, vd, vd->pci->tbdf, name);
598*505d3296SDavid du Colombier 	outb(vd->port+Status, inb(vd->port+Status) | DriverOk);
599*505d3296SDavid du Colombier 	return 1;
600*505d3296SDavid du Colombier }
601*505d3296SDavid du Colombier 
602*505d3296SDavid du Colombier static int
viodisable(SDev * sd)603*505d3296SDavid du Colombier viodisable(SDev *sd)
604*505d3296SDavid du Colombier {
605*505d3296SDavid du Colombier 	char name[32];
606*505d3296SDavid du Colombier 	Vdev *vd;
607*505d3296SDavid du Colombier 
608*505d3296SDavid du Colombier 	vd = sd->ctlr;
609*505d3296SDavid du Colombier 	snprint(name, sizeof(name), "%s (%s)", sd->name, sd->ifc->name);
610*505d3296SDavid du Colombier 	intrdisable(vd->pci->intl, viointerrupt, vd, vd->pci->tbdf, name);
611*505d3296SDavid du Colombier 	pciclrbme(vd->pci);
612*505d3296SDavid du Colombier 	return 1;
613*505d3296SDavid du Colombier }
614*505d3296SDavid du Colombier 
615*505d3296SDavid du Colombier static SDev*
viopnp(void)616*505d3296SDavid du Colombier viopnp(void)
617*505d3296SDavid du Colombier {
618*505d3296SDavid du Colombier 	SDev *s, *h, *t;
619*505d3296SDavid du Colombier 	Vdev *vd;
620*505d3296SDavid du Colombier 	int id;
621*505d3296SDavid du Colombier 
622*505d3296SDavid du Colombier 	h = t = nil;
623*505d3296SDavid du Colombier 
624*505d3296SDavid du Colombier 	id = 'F';
625*505d3296SDavid du Colombier 	for(vd =  viopnpdevs(TypBlk); vd; vd = vd->next){
626*505d3296SDavid du Colombier 		if(vd->nqueue != 1)
627*505d3296SDavid du Colombier 			continue;
628*505d3296SDavid du Colombier 
629*505d3296SDavid du Colombier 		if((s = malloc(sizeof(*s))) == nil)
630*505d3296SDavid du Colombier 			break;
631*505d3296SDavid du Colombier 		s->ctlr = vd;
632*505d3296SDavid du Colombier 		s->idno = id++;
633*505d3296SDavid du Colombier 		s->ifc = &sdvirtioifc;
634*505d3296SDavid du Colombier 		s->nunit = 1;
635*505d3296SDavid du Colombier 		if(h)
636*505d3296SDavid du Colombier 			t->next = s;
637*505d3296SDavid du Colombier 		else
638*505d3296SDavid du Colombier 			h = s;
639*505d3296SDavid du Colombier 		t = s;
640*505d3296SDavid du Colombier 	}
641*505d3296SDavid du Colombier 
642*505d3296SDavid du Colombier 	id = '0';
643*505d3296SDavid du Colombier 	for(vd = viopnpdevs(TypSCSI); vd; vd = vd->next){
644*505d3296SDavid du Colombier 		ScsiCfg *cfg;
645*505d3296SDavid du Colombier 
646*505d3296SDavid du Colombier 		if(vd->nqueue < 3)
647*505d3296SDavid du Colombier 			continue;
648*505d3296SDavid du Colombier 
649*505d3296SDavid du Colombier 		if((cfg = malloc(sizeof(*cfg))) == nil)
650*505d3296SDavid du Colombier 			break;
651*505d3296SDavid du Colombier 		cfg->num_queues = inl(vd->port+Devspec+4*0);
652*505d3296SDavid du Colombier 		cfg->seg_max = inl(vd->port+Devspec+4*1);
653*505d3296SDavid du Colombier 		cfg->max_sectors = inl(vd->port+Devspec+4*2);
654*505d3296SDavid du Colombier 		cfg->cmd_per_lun = inl(vd->port+Devspec+4*3);
655*505d3296SDavid du Colombier 		cfg->event_info_size = inl(vd->port+Devspec+4*4);
656*505d3296SDavid du Colombier 		cfg->sense_size = inl(vd->port+Devspec+4*5);
657*505d3296SDavid du Colombier 		cfg->cdb_size = inl(vd->port+Devspec+4*6);
658*505d3296SDavid du Colombier 		cfg->max_channel = ins(vd->port+Devspec+4*7);
659*505d3296SDavid du Colombier 		cfg->max_target = ins(vd->port+Devspec+4*7+2);
660*505d3296SDavid du Colombier 		cfg->max_lun = inl(vd->port+Devspec+4*8);
661*505d3296SDavid du Colombier 
662*505d3296SDavid du Colombier 		if(cfg->max_target == 0){
663*505d3296SDavid du Colombier 			free(cfg);
664*505d3296SDavid du Colombier 			continue;
665*505d3296SDavid du Colombier 		}
666*505d3296SDavid du Colombier 		if((cfg->cdb_size > CDBSIZE) || (cfg->sense_size > SENSESIZE)){
667*505d3296SDavid du Colombier 			print("sdvirtio: cdb %ud or sense size %ud too big\n",
668*505d3296SDavid du Colombier 				cfg->cdb_size, cfg->sense_size);
669*505d3296SDavid du Colombier 			free(cfg);
670*505d3296SDavid du Colombier 			continue;
671*505d3296SDavid du Colombier 		}
672*505d3296SDavid du Colombier 		vd->cfg = cfg;
673*505d3296SDavid du Colombier 
674*505d3296SDavid du Colombier 		if((s = malloc(sizeof(*s))) == nil)
675*505d3296SDavid du Colombier 			break;
676*505d3296SDavid du Colombier 		s->ctlr = vd;
677*505d3296SDavid du Colombier 		s->idno = id++;
678*505d3296SDavid du Colombier 		s->ifc = &sdvirtioifc;
679*505d3296SDavid du Colombier 		s->nunit = cfg->max_target;
680*505d3296SDavid du Colombier 		if(h)
681*505d3296SDavid du Colombier 			t->next = s;
682*505d3296SDavid du Colombier 		else
683*505d3296SDavid du Colombier 			h = s;
684*505d3296SDavid du Colombier 		t = s;
685*505d3296SDavid du Colombier 	}
686*505d3296SDavid du Colombier 
687*505d3296SDavid du Colombier 	return h;
688*505d3296SDavid du Colombier }
689*505d3296SDavid du Colombier 
690*505d3296SDavid du Colombier SDifc sdvirtioifc = {
691*505d3296SDavid du Colombier 	"virtio",			/* name */
692*505d3296SDavid du Colombier 
693*505d3296SDavid du Colombier 	viopnp,				/* pnp */
694*505d3296SDavid du Colombier 	nil,				/* legacy */
695*505d3296SDavid du Colombier 	vioenable,			/* enable */
696*505d3296SDavid du Colombier 	viodisable,			/* disable */
697*505d3296SDavid du Colombier 
698*505d3296SDavid du Colombier 	vioverify,			/* verify */
699*505d3296SDavid du Colombier 	vioonline,			/* online */
700*505d3296SDavid du Colombier 	viorio,				/* rio */
701*505d3296SDavid du Colombier 	nil,				/* rctl */
702*505d3296SDavid du Colombier 	nil,				/* wctl */
703*505d3296SDavid du Colombier 
704*505d3296SDavid du Colombier 	viobio,				/* bio */
705*505d3296SDavid du Colombier 	nil,				/* probe */
706*505d3296SDavid du Colombier 	nil,				/* clear */
707*505d3296SDavid du Colombier 	nil,				/* rtopctl */
708*505d3296SDavid du Colombier 	nil,				/* wtopctl */
709*505d3296SDavid du Colombier };
710