2 #include "../port/lib.h"
8 #include "../port/error.h"
10 #include "../port/sd.h"
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;
40 /* descriptor flags */
116 q = malloc(sizeof(*q) + sizeof(void*)*size);
118 PGROUND(sizeof(Vdesc)*size +
120 sizeof(u16int)*size +
126 if(p == nil || q == nil){
127 print("virtio: no memory for Vqueue\n");
134 p += sizeof(Vdesc)*size;
137 q->availent = (void*)p;
138 p += sizeof(u16int)*size;
139 q->availevent = (void*)p;
142 p = (uchar*)PGROUND((uintptr)p);
145 q->usedent = (void*)p;
146 p += sizeof(Vused)*size;
147 q->usedevent = (void*)p;
150 q->nfree = q->size = size;
151 for(i=0; i<size; i++){
152 q->desc[i].next = q->free;
167 for(p = nil; p = pcimatch(p, 0, 0);){
170 if((p->did < 0x1000) || (p->did >= 0x1040))
174 if(pcicfgr16(p, 0x2E) != typ)
176 if((vd = malloc(sizeof(*vd))) == nil){
177 print("virtio: no memory for Vdev\n");
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);
190 outb(vd->port+Status, 0);
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)
199 if((vd->queue[i] = mkvqueue(n)) == nil)
202 outl(vd->port+Qaddr, PADDR(vd->queue[i]->desc)/BY2PG);
222 viointerrupt(Ureg *, void *arg)
231 if(inb(vd->port+Isr) & 1){
236 while((q->lastused ^ q->used->idx) & m){
237 id = q->usedent[q->lastused++ & m].id;
241 r->done = 1; /* hands off */
247 id = q->desc[free].next;
248 q->desc[free].next = q->free;
251 } while(q->desc[free].flags & Next);
260 return ((struct Rock*)arg)->done;
264 vioreq(Vdev *vd, int typ, void *a, long count, long secsize, uvlong lba)
284 rock.sleep = &up->sleep;
292 tsleep(&up->sleep, return0, 0, 500);
298 head = free = q->free;
300 d = &q->desc[free]; free = d->next;
301 d->addr = PADDR(&req);
302 d->len = sizeof(req);
305 d = &q->desc[free]; free = d->next;
307 d->len = secsize*count;
308 d->flags = typ ? Next : (Write|Next);
310 d = &q->desc[free]; free = d->next;
311 d->addr = PADDR(&status);
312 d->len = sizeof(status);
318 q->rock[head] = &rock;
321 q->availent[q->avail->idx++ & (q->size-1)] = head;
323 outs(vd->port+Qnotify, 0);
329 tsleep(rock.sleep, viodone, &rock, 1000);
333 viointerrupt(nil, vd);
340 viobio(SDunit *u, int, int write, void *a, long count, uvlong lba)
342 long ss, cc, max, ret;
351 if((cc = count) > max)
353 if(vioreq(vd, write != 0, (uchar*)a + ret, cc, ss, lba) != 0)
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);
376 if((i = sdfakescsi(r)) != SDnostatus)
377 return r->status = i;
378 if((i = sdfakescsirw(r, &lba, &count, &rw)) != SDnostatus)
380 r->rlen = viobio(u, r->lun, rw == SDwrite, r->data, count, lba);
381 return r->status = SDok;
391 cap = inl(vd->port+Devspec+4);
393 cap |= inl(vd->port+Devspec);
394 if(u->sectors != cap){
419 for(vd = viopnpdevs(2); vd; vd = vd->next){
423 intrenable(vd->pci->intl, viointerrupt, vd, vd->pci->tbdf, "virtio");
424 outb(vd->port+Status, inb(vd->port+Status) | DriverOk);
426 if((s = malloc(sizeof(*s))) == nil)
430 s->ifc = &sdvirtioifc;
442 SDifc sdvirtioifc = {
450 vioverify, /* verify */
451 vioonline, /* online */