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