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