]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/pc/sdvirtio.c
sdide: never timeout or retry scsi commands from the controller driver
[plan9front.git] / sys / src / 9 / pc / sdvirtio.c
1 #include "u.h"
2 #include "../port/lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6 #include "io.h"
7 #include "ureg.h"
8 #include "../port/error.h"
9
10 #include "../port/sd.h"
11
12 typedef struct Vring Vring;
13 typedef struct Vdesc Vdesc;
14 typedef struct Vused Vused;
15 typedef struct Vqueue Vqueue;
16 typedef struct Vdev Vdev;
17
18 /* status flags */
19 enum {
20         Acknowledge = 1,
21         Driver = 2,
22         DriverOk = 4,
23         Failed = 0x80,
24 };
25
26 /* virtio ports */
27 enum {
28         Devfeat = 0,
29         Drvfeat = 4,
30         Qaddr = 8,
31         Qsize = 12,
32         Qselect = 14,
33         Qnotify = 16,
34         Status = 18,
35         Isr = 19,
36
37         Devspec = 20,
38 };
39
40 /* descriptor flags */
41 enum {
42         Next = 1,
43         Write = 2,
44         Indirect = 4,
45 };
46
47 /* struct sizes */
48 enum {
49         VringSize = 4,
50 };      
51
52 struct Vring
53 {
54         u16int  flags;
55         u16int  idx;
56 };
57
58 struct Vdesc
59 {
60         u64int  addr;
61         u32int  len;
62         u16int  flags;
63         u16int  next;
64 };
65
66 struct Vused
67 {
68         u32int  id;
69         u32int  len;
70 };
71
72 struct Vqueue
73 {
74         Lock;
75         int     size;
76
77         int     free;
78         int     nfree;
79
80         Vdesc   *desc;
81
82         Vring   *avail;
83         u16int  *availent;
84         u16int  *availevent;
85
86         Vring   *used;
87         Vused   *usedent;
88         u16int  *usedevent;
89         u16int  lastused;
90
91         void    *rock[];
92 };
93
94 struct Vdev
95 {
96         int     typ;
97
98         Pcidev  *pci;
99
100         ulong   port;
101         ulong   feat;
102
103         int     nqueue;
104         Vqueue  *queue[16];
105
106         Vdev    *next;
107 };
108
109 static Vqueue*
110 mkvqueue(int size)
111 {
112         Vqueue *q;
113         uchar *p;
114         int i;
115
116         q = malloc(sizeof(*q) + sizeof(void*)*size);
117         p = mallocalign(
118                 PGROUND(sizeof(Vdesc)*size + 
119                         VringSize + 
120                         sizeof(u16int)*size + 
121                         sizeof(u16int)) +
122                 PGROUND(VringSize + 
123                         sizeof(Vused)*size + 
124                         sizeof(u16int)), 
125                 BY2PG, 0, 0);
126         if(p == nil || q == nil){
127                 print("virtio: no memory for Vqueue\n");
128                 free(p);
129                 free(q);
130                 return nil;
131         }
132
133         q->desc = (void*)p;
134         p += sizeof(Vdesc)*size;
135         q->avail = (void*)p;
136         p += VringSize;
137         q->availent = (void*)p;
138         p += sizeof(u16int)*size;
139         q->availevent = (void*)p;
140         p += sizeof(u16int);
141
142         p = (uchar*)PGROUND((uintptr)p);
143         q->used = (void*)p;
144         p += VringSize;
145         q->usedent = (void*)p;
146         p += sizeof(Vused)*size;
147         q->usedevent = (void*)p;
148
149         q->free = -1;
150         q->nfree = q->size = size;
151         for(i=0; i<size; i++){
152                 q->desc[i].next = q->free;
153                 q->free = i;
154         }
155
156         return q;
157 }
158
159 static Vdev*
160 viopnpdevs(int typ)
161 {
162         Vdev *vd, *h, *t;
163         Pcidev *p;
164         int n, i;
165
166         h = t = nil;
167         for(p = nil; p = pcimatch(p, 0, 0);){
168                 if(p->vid != 0x1AF4)
169                         continue;
170                 if((p->did < 0x1000) || (p->did >= 0x1040))
171                         continue;
172                 if(p->rid != 0)
173                         continue;
174                 if(pcicfgr16(p, 0x2E) != typ)
175                         continue;
176                 if((vd = malloc(sizeof(*vd))) == nil){
177                         print("virtio: no memory for Vdev\n");
178                         break;
179                 }
180                 vd->port = p->mem[0].bar & ~0x1;
181                 if(ioalloc(vd->port, p->mem[0].size, 0, "virtio") < 0){
182                         print("virtio: port %lux in use\n", vd->port);
183                         free(vd);
184                         continue;
185                 }
186                 vd->typ = typ;
187                 vd->pci = p;
188
189                 /* reset */
190                 outb(vd->port+Status, 0);
191
192                 vd->feat = inl(vd->port+Devfeat);
193                 outb(vd->port+Status, Acknowledge|Driver);
194                 for(i=0; i<nelem(vd->queue); i++){
195                         outs(vd->port+Qselect, i);
196                         n = ins(vd->port+Qsize);
197                         if(n == 0 || (n & (n-1)) != 0)
198                                 break;
199                         if((vd->queue[i] = mkvqueue(n)) == nil)
200                                 break;
201                         coherence();
202                         outl(vd->port+Qaddr, PADDR(vd->queue[i]->desc)/BY2PG);
203                 }
204                 vd->nqueue = i;
205         
206                 if(h == nil)
207                         h = vd;
208                 else
209                         t->next = vd;
210                 t = vd;
211         }
212
213         return h;
214 }
215
216 struct Rock {
217         int done;
218         Rendez *sleep;
219 };
220
221 static void
222 viointerrupt(Ureg *, void *arg)
223 {
224         int id, free, m;
225         struct Rock *r;
226         Rendez *z;
227         Vqueue *q;
228         Vdev *vd;
229
230         vd = arg;
231         if(inb(vd->port+Isr) & 1){
232                 q = vd->queue[0];
233                 m = q->size-1;
234
235                 ilock(q);
236                 while((q->lastused ^ q->used->idx) & m){
237                         id = q->usedent[q->lastused++ & m].id;
238                         if(r = q->rock[id]){
239                                 q->rock[id] = nil;
240                                 z = r->sleep;
241                                 r->done = 1;    /* hands off */
242                                 if(z != nil)
243                                         wakeup(z);
244                         }
245                         do {
246                                 free = id;
247                                 id = q->desc[free].next;
248                                 q->desc[free].next = q->free;
249                                 q->free = free;
250                                 q->nfree++;
251                         } while(q->desc[free].flags & Next);
252                 }
253                 iunlock(q);
254         }
255 }
256
257 static int
258 viodone(void *arg)
259 {
260         return ((struct Rock*)arg)->done;
261 }
262
263 static int
264 vioreq(Vdev *vd, int typ, void *a, long count, long secsize, uvlong lba)
265 {
266         struct Rock rock;
267         int free, head;
268         Vqueue *q;
269         Vdesc *d;
270
271         u8int status;
272         struct Vioreqhdr {
273                 u32int  typ;
274                 u32int  prio;
275                 u64int  lba;
276         } req;
277
278         status = 0;
279         req.typ = typ;
280         req.prio = 0;
281         req.lba = lba;
282
283         rock.done = 0;
284         rock.sleep = &up->sleep;
285
286         q = vd->queue[0];
287         ilock(q);
288         while(q->nfree < 3){
289                 iunlock(q);
290
291                 if(!waserror())
292                         tsleep(&up->sleep, return0, 0, 500);
293                 poperror();
294
295                 ilock(q);
296         }
297
298         head = free = q->free;
299
300         d = &q->desc[free]; free = d->next;
301         d->addr = PADDR(&req);
302         d->len = sizeof(req);
303         d->flags = Next;
304
305         d = &q->desc[free]; free = d->next;
306         d->addr = PADDR(a);
307         d->len = secsize*count;
308         d->flags = typ ? Next : (Write|Next);
309
310         d = &q->desc[free]; free = d->next;
311         d->addr = PADDR(&status);
312         d->len = sizeof(status);
313         d->flags = Write;
314
315         q->free = free;
316         q->nfree -= 3;
317
318         q->rock[head] = &rock;
319
320         coherence();
321         q->availent[q->avail->idx++ & (q->size-1)] = head;
322         coherence();
323         outs(vd->port+Qnotify, 0);
324         iunlock(q);
325
326         while(!rock.done){
327                 while(waserror())
328                         ;
329                 tsleep(rock.sleep, viodone, &rock, 1000);
330                 poperror();
331
332                 if(!rock.done)
333                         viointerrupt(nil, vd);
334         }
335
336         return status;
337 }
338
339 static long
340 viobio(SDunit *u, int, int write, void *a, long count, uvlong lba)
341 {
342         long ss, cc, max, ret;
343         Vdev *vd;
344
345         max = 32;
346         ss = u->secsize;
347         vd = u->dev->ctlr;
348
349         ret = 0;
350         while(count > 0){
351                 if((cc = count) > max)
352                         cc = max;
353                 if(vioreq(vd, write != 0, (uchar*)a + ret, cc, ss, lba) != 0)
354                         error(Eio);
355                 ret += cc*ss;
356                 count -= cc;
357                 lba += cc;
358         }
359
360         return ret;
361 }
362
363 static int
364 viorio(SDreq *r)
365 {
366         int i, count, rw;
367         uvlong lba;
368         SDunit *u;
369
370         u = r->unit;
371         if(r->cmd[0] == 0x35 || r->cmd[0] == 0x91){
372                 if(vioreq(u->dev->ctlr, 4, nil, 0, 0, 0) != 0)
373                         return sdsetsense(r, SDcheck, 3, 0xc, 2);
374                 return sdsetsense(r, SDok, 0, 0, 0);
375         }
376         if((i = sdfakescsi(r)) != SDnostatus)
377                 return r->status = i;
378         if((i = sdfakescsirw(r, &lba, &count, &rw)) != SDnostatus)
379                 return i;
380         r->rlen = viobio(u, r->lun, rw == SDwrite, r->data, count, lba);
381         return r->status = SDok;
382 }
383
384 static int
385 vioonline(SDunit *u)
386 {
387         uvlong cap;
388         Vdev *vd;
389
390         vd = u->dev->ctlr;
391         cap = inl(vd->port+Devspec+4);
392         cap <<= 32;
393         cap |= inl(vd->port+Devspec);
394         if(u->sectors != cap){
395                 u->sectors = cap;
396                 u->secsize = 512;
397                 return 2;
398         }
399         return 1;
400 }
401
402 static int
403 vioverify(SDunit *)
404 {
405         return 1;
406 }
407
408 SDifc sdvirtioifc;
409
410 static SDev*
411 viopnp(void)
412 {
413         SDev *s, *h, *t;
414         Vdev *vd;
415         int id;
416         
417         id = 'F';
418         h = t = nil;
419         for(vd =  viopnpdevs(2); vd; vd = vd->next){
420                 if(vd->nqueue != 1)
421                         continue;
422
423                 intrenable(vd->pci->intl, viointerrupt, vd, vd->pci->tbdf, "virtio");
424                 outb(vd->port+Status, inb(vd->port+Status) | DriverOk);
425
426                 if((s = malloc(sizeof(*s))) == nil)
427                         break;
428                 s->ctlr = vd;
429                 s->idno = id++;
430                 s->ifc = &sdvirtioifc;
431                 s->nunit = 1;
432                 if(h)
433                         t->next = s;
434                 else
435                         h = s;
436                 t = s;
437         }
438
439         return h;
440 }
441
442 SDifc sdvirtioifc = {
443         "virtio",                       /* name */
444
445         viopnp,                         /* pnp */
446         nil,                            /* legacy */
447         nil,                            /* enable */
448         nil,                            /* disable */
449
450         vioverify,                      /* verify */
451         vioonline,                      /* online */
452         viorio,                         /* rio */
453         nil,                            /* rctl */
454         nil,                            /* wctl */
455
456         viobio,                         /* bio */
457         nil,                            /* probe */
458         nil,                            /* clear */
459         nil,                            /* rtopctl */
460         nil,                            /* wtopctl */
461 };