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