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