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