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