2 * virtio 1.0 disk driver
3 * http://docs.oasis-open.org/virtio/virtio/v1.0/virtio-v1.0.html
5 * In contrast to sdvirtio.c, this driver handles the non-legacy
6 * interface for virtio disk which uses mmio for all register accesses
7 * and requires a laborate pci capability structure dance to get working.
9 * It is kind of pointless as it is most likely slower than
10 * port i/o (harder to emulate on the pc platform).
12 * The reason why this driver is needed it is that vultr set the
13 * disable-legacy=on option in the -device parameter for qemu
14 * on their hypervisor.
17 #include "../port/lib.h"
22 #include "../port/pci.h"
24 #include "../port/error.h"
26 #include "../port/sd.h"
28 typedef struct Vscsidev Vscsidev;
29 typedef struct Vblkdev Vblkdev;
31 typedef struct Vconfig Vconfig;
32 typedef struct Vring Vring;
33 typedef struct Vdesc Vdesc;
34 typedef struct Vused Vused;
35 typedef struct Vqueue Vqueue;
36 typedef struct Vdev Vdev;
53 /* descriptor flags */
77 u32int event_info_size;
104 u16int queuemsixvect;
107 u16int queuenotifyoff;
173 void *dev; /* device specific config (for scsi) */
179 u32int notifyoffmult;
191 q = malloc(sizeof(*q) + sizeof(void*)*size);
193 PGROUND(sizeof(Vdesc)*size +
195 sizeof(u16int)*size +
201 if(p == nil || q == nil){
202 print("virtio: no memory for Vqueue\n");
209 p += sizeof(Vdesc)*size;
212 q->availent = (void*)p;
213 p += sizeof(u16int)*size;
214 q->availevent = (void*)p;
217 p = (uchar*)PGROUND((uintptr)p);
220 q->usedent = (void*)p;
221 p += sizeof(Vused)*size;
222 q->usedevent = (void*)p;
225 q->nfree = q->size = size;
226 for(i=0; i<size; i++){
227 q->desc[i].next = q->free;
235 matchvirtiocfgcap(Pcidev *p, int cap, int off, int typ)
239 if(cap != 9 || pcicfgr8(p, off+3) != typ)
242 /* skip invalid or non memory bars */
243 bar = pcicfgr8(p, off+4);
244 if(bar < 0 || bar >= nelem(p->mem)
245 || p->mem[bar].size == 0
246 || (p->mem[bar].bar & 3) != 0)
253 virtiocap(Pcidev *p, int typ)
255 return pcienumcaps(p, matchvirtiocfgcap, typ);
259 virtiomapregs(Pcidev *p, int cap, int size)
266 bar = pcicfgr8(p, cap+4) % nelem(p->mem);
267 addr = pcicfgr32(p, cap+8);
268 len = pcicfgr32(p, cap+12);
273 if(addr+len > p->mem[bar].size)
275 addr += p->mem[bar].bar & ~0xFULL;
276 return vmap(addr, size);
290 for(p = nil; p = pcimatch(p, 0x1AF4, 0x1040+typ);){
293 if((cap = virtiocap(p, 1)) < 0)
295 bar = pcicfgr8(p, cap+4) % nelem(p->mem);
296 cfg = virtiomapregs(p, cap, sizeof(Vconfig));
299 if((vd = malloc(sizeof(*vd))) == nil){
300 print("virtio: no memory for Vdev\n");
303 vd->port = p->mem[bar].bar & ~0xFULL;
309 vd->isr = virtiomapregs(p, virtiocap(p, 3), 0);
317 cap = virtiocap(p, 2);
318 vd->notify = virtiomapregs(p, cap, 0);
319 if(vd->notify == nil)
321 vd->notifyoffmult = pcicfgr32(p, cap+16);
325 while(cfg->status != 0)
327 cfg->status = Acknowledge|Driver;
329 /* negotiate feature bits */
331 vd->feat[1] = cfg->devfeat;
333 vd->feat[0] = cfg->devfeat;
335 cfg->drvfeat = vd->feat[1] & 1;
339 for(i=0; i<nelem(vd->queue); i++){
342 if(n == 0 || (n & (n-1)) != 0)
344 if((q = mkvqueue(n)) == nil)
346 q->notify = vd->notify + vd->notifyoffmult * cfg->queuenotifyoff;
351 cfg->queuedesc = PADDR(q->desc);
352 cfg->queueavail = PADDR(q->avail);
353 cfg->queueused = PADDR(q->used);
373 vqinterrupt(Vqueue *q)
382 while((q->lastused ^ q->used->idx) & m){
383 id = q->usedent[q->lastused++ & m].id;
387 r->done = 1; /* hands off */
393 id = q->desc[free].next;
394 q->desc[free].next = q->free;
397 } while(q->desc[free].flags & Next);
403 viointerrupt(Ureg *, void *arg)
408 vqinterrupt(vd->queue[vd->typ == TypSCSI ? 2 : 0]);
414 return ((struct Rock*)arg)->done;
418 vqio(Vqueue *q, int head)
423 rock.sleep = &up->sleep;
424 q->rock[head] = &rock;
425 q->availent[q->avail->idx & (q->size-1)] = head;
429 if((q->used->flags & 1) == 0)
430 *((u16int*)q->notify) = q->idx;
434 tsleep(rock.sleep, viodone, &rock, 1000);
443 vioblkreq(Vdev *vd, int typ, void *a, long count, long secsize, uvlong lba)
445 int need, free, head;
450 struct Vioblkreqhdr {
467 while(q->nfree < need){
471 tsleep(&up->sleep, return0, 0, 500);
477 head = free = q->free;
479 d = &q->desc[free]; free = d->next;
480 d->addr = PADDR(&req);
481 d->len = sizeof(req);
485 d = &q->desc[free]; free = d->next;
487 d->len = secsize*count;
488 d->flags = typ ? Next : (Write|Next);
491 d = &q->desc[free]; free = d->next;
492 d->addr = PADDR(&status);
493 d->len = sizeof(status);
499 /* queue io, unlock and wait for completion */
508 u8int resp[4+4+2+2+SENSESIZE];
509 u8int req[8+8+3+CDBSIZE];
522 memset(resp, 0, sizeof(resp));
523 memset(req, 0, sizeof(req));
527 req[3] = r->lun&0xFF;
528 *(u64int*)(&req[8]) = (uintptr)r;
530 memmove(&req[8+8+3], r->cmd, r->clen);
538 tsleep(&up->sleep, return0, 0, 500);
544 head = free = q->free;
546 d = &q->desc[free]; free = d->next;
547 d->addr = PADDR(req);
548 d->len = 8+8+3+scsi->cdb_size;
551 if(r->write && r->dlen > 0){
552 d = &q->desc[free]; free = d->next;
553 d->addr = PADDR(r->data);
558 d = &q->desc[free]; free = d->next;
559 d->addr = PADDR(resp);
560 d->len = 4+4+2+2+scsi->sense_size;
563 if(!r->write && r->dlen > 0){
566 d = &q->desc[free]; free = d->next;
567 d->addr = PADDR(r->data);
573 q->nfree -= 2 + (r->dlen > 0);
575 /* queue io, unlock and wait for completion */
578 /* response+status */
579 r->status = resp[10];
584 len = *((u32int*)&resp[0]);
586 if(len > sizeof(r->sense))
587 len = sizeof(r->sense);
588 memmove(r->sense, &resp[4+4+2+2], len);
589 r->flags |= SDvalidsense;
593 len = *((u32int*)&resp[4]);
597 r->rlen = r->dlen - len;
604 viobio(SDunit *u, int lun, int write, void *a, long count, uvlong lba)
606 long ss, cc, max, ret;
610 if(vd->typ == TypSCSI)
611 return scsibio(u, lun, write, a, count, lba);
617 if((cc = count) > max)
619 if(vioblkreq(vd, write != 0, (uchar*)a + ret, cc, ss, lba) != 0)
638 if(vd->typ == TypSCSI)
639 return vioscsireq(r);
640 if(r->cmd[0] == 0x35 || r->cmd[0] == 0x91){
641 if(vioblkreq(vd, 4, nil, 0, 0, 0) != 0)
642 return sdsetsense(r, SDcheck, 3, 0xc, 2);
643 return sdsetsense(r, SDok, 0, 0, 0);
645 if((i = sdfakescsi(r)) != SDnostatus)
646 return r->status = i;
647 if((i = sdfakescsirw(r, &lba, &count, &rw)) != SDnostatus)
649 r->rlen = viobio(u, r->lun, rw == SDwrite, r->data, count, lba);
650 return r->status = SDok;
661 if(vd->typ == TypSCSI)
662 return scsionline(u);
666 if(u->sectors != cap){
680 if(vd->typ == TypSCSI)
681 return scsiverify(u);
697 snprint(name, sizeof(name), "%s (%s)", sd->name, sd->ifc->name);
698 intrenable(vd->pci->intl, viointerrupt, vd, vd->pci->tbdf, name);
701 vd->cfg->status |= DriverOk;
702 for(i = 0; i < vd->nqueue; i++){
703 vd->cfg->queuesel = i;
704 vd->cfg->queueenable = 1;
717 snprint(name, sizeof(name), "%s (%s)", sd->name, sd->ifc->name);
718 intrdisable(vd->pci->intl, viointerrupt, vd, vd->pci->tbdf, name);
733 for(vd = viopnpdevs(TypBlk); vd; vd = vd->next){
737 if((vd->dev = virtiomapregs(vd->pci, virtiocap(vd->pci, 4), sizeof(Vblkdev))) == nil)
739 if((s = malloc(sizeof(*s))) == nil)
743 s->ifc = &sdvirtio10ifc;
753 for(vd = viopnpdevs(TypSCSI); vd; vd = vd->next){
759 if((scsi = virtiomapregs(vd->pci, virtiocap(vd->pci, 4), sizeof(Vscsidev))) == nil)
761 if(scsi->max_target == 0){
762 vunmap(scsi, sizeof(Vscsidev));
765 if((scsi->cdb_size > CDBSIZE) || (scsi->sense_size > SENSESIZE)){
766 print("sdvirtio: cdb %ud or sense size %ud too big\n",
767 scsi->cdb_size, scsi->sense_size);
768 vunmap(scsi, sizeof(Vscsidev));
773 if((s = malloc(sizeof(*s))) == nil)
777 s->ifc = &sdvirtio10ifc;
778 s->nunit = scsi->max_target;
789 SDifc sdvirtio10ifc = {
790 "virtio10", /* name */
794 vioenable, /* enable */
795 viodisable, /* disable */
797 vioverify, /* verify */
798 vioonline, /* online */