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