2 * virtio ethernet driver implementing the legacy interface:
3 * http://docs.oasis-open.org/virtio/virtio/v1.0/virtio-v1.0.html
6 #include "../port/lib.h"
11 #include "../port/pci.h"
13 #include "../port/error.h"
15 #include "../port/sd.h"
17 typedef struct Vring Vring;
18 typedef struct Vdesc Vdesc;
19 typedef struct Vused Vused;
20 typedef struct Vqueue Vqueue;
21 typedef struct Vdev Vdev;
23 typedef struct ScsiCfg ScsiCfg;
53 /* descriptor flags */
123 void *cfg; /* device specific config (for scsi) */
139 u32int event_info_size;
154 q = malloc(sizeof(*q) + sizeof(void*)*size);
156 PGROUND(sizeof(Vdesc)*size +
158 sizeof(u16int)*size +
164 if(p == nil || q == nil){
165 print("virtio: no memory for Vqueue\n");
172 p += sizeof(Vdesc)*size;
175 q->availent = (void*)p;
176 p += sizeof(u16int)*size;
177 q->availevent = (void*)p;
180 p = (uchar*)PGROUND((uintptr)p);
183 q->usedent = (void*)p;
184 p += sizeof(Vused)*size;
185 q->usedevent = (void*)p;
188 q->nfree = q->size = size;
189 for(i=0; i<size; i++){
190 q->desc[i].next = q->free;
206 for(p = nil; p = pcimatch(p, 0x1AF4, 0);){
207 if((p->did < 0x1000) || (p->did > 0x103F))
211 if((p->mem[0].bar & 1) == 0)
213 if(pcicfgr16(p, 0x2E) != typ)
215 if((vd = malloc(sizeof(*vd))) == nil){
216 print("virtio: no memory for Vdev\n");
219 vd->port = p->mem[0].bar & ~3;
220 if(ioalloc(vd->port, p->mem[0].size, 0, "virtio") < 0){
221 print("virtio: port %lux in use\n", vd->port);
230 outb(vd->port+Status, 0);
232 vd->feat = inl(vd->port+Devfeat);
233 outb(vd->port+Status, Acknowledge|Driver);
234 for(i=0; i<nelem(vd->queue); i++){
235 outs(vd->port+Qselect, i);
236 n = ins(vd->port+Qsize);
237 if(n == 0 || (n & (n-1)) != 0)
239 if((q = mkvqueue(n)) == nil)
245 outl(vd->port+Qaddr, PADDR(vd->queue[i]->desc)/BY2PG);
265 vqinterrupt(Vqueue *q)
274 while((q->lastused ^ q->used->idx) & m){
275 id = q->usedent[q->lastused++ & m].id;
279 r->done = 1; /* hands off */
285 id = q->desc[free].next;
286 q->desc[free].next = q->free;
289 } while(q->desc[free].flags & Next);
295 viointerrupt(Ureg *, void *arg)
299 if(inb(vd->port+Isr) & 1)
300 vqinterrupt(vd->queue[vd->typ == TypSCSI ? 2 : 0]);
306 return ((struct Rock*)arg)->done;
310 vqio(Vqueue *q, int head)
315 rock.sleep = &up->sleep;
316 q->rock[head] = &rock;
317 q->availent[q->avail->idx & (q->size-1)] = head;
321 if((q->used->flags & 1) == 0)
322 outs(q->dev->port+Qnotify, q->idx);
326 tsleep(rock.sleep, viodone, &rock, 1000);
335 vioblkreq(Vdev *vd, int typ, void *a, long count, long secsize, uvlong lba)
337 int need, free, head;
342 struct Vioblkreqhdr {
359 while(q->nfree < need){
363 tsleep(&up->sleep, return0, 0, 500);
369 head = free = q->free;
371 d = &q->desc[free]; free = d->next;
372 d->addr = PADDR(&req);
373 d->len = sizeof(req);
377 d = &q->desc[free]; free = d->next;
379 d->len = secsize*count;
380 d->flags = typ ? Next : (Write|Next);
383 d = &q->desc[free]; free = d->next;
384 d->addr = PADDR(&status);
385 d->len = sizeof(status);
391 /* queue io, unlock and wait for completion */
400 u8int resp[4+4+2+2+SENSESIZE];
401 u8int req[8+8+3+CDBSIZE];
414 memset(resp, 0, sizeof(resp));
415 memset(req, 0, sizeof(req));
419 req[3] = r->lun&0xFF;
420 *(u64int*)(&req[8]) = (uintptr)r;
422 memmove(&req[8+8+3], r->cmd, r->clen);
430 tsleep(&up->sleep, return0, 0, 500);
436 head = free = q->free;
438 d = &q->desc[free]; free = d->next;
439 d->addr = PADDR(req);
440 d->len = 8+8+3+cfg->cdb_size;
443 if(r->write && r->dlen > 0){
444 d = &q->desc[free]; free = d->next;
445 d->addr = PADDR(r->data);
450 d = &q->desc[free]; free = d->next;
451 d->addr = PADDR(resp);
452 d->len = 4+4+2+2+cfg->sense_size;
455 if(!r->write && r->dlen > 0){
458 d = &q->desc[free]; free = d->next;
459 d->addr = PADDR(r->data);
465 q->nfree -= 2 + (r->dlen > 0);
467 /* queue io, unlock and wait for completion */
470 /* response+status */
471 r->status = resp[10];
476 len = *((u32int*)&resp[0]);
478 if(len > sizeof(r->sense))
479 len = sizeof(r->sense);
480 memmove(r->sense, &resp[4+4+2+2], len);
481 r->flags |= SDvalidsense;
485 len = *((u32int*)&resp[4]);
489 r->rlen = r->dlen - len;
496 viobio(SDunit *u, int lun, int write, void *a, long count, uvlong lba)
498 long ss, cc, max, ret;
502 if(vd->typ == TypSCSI)
503 return scsibio(u, lun, write, a, count, lba);
509 if((cc = count) > max)
511 if(vioblkreq(vd, write != 0, (uchar*)a + ret, cc, ss, lba) != 0)
530 if(vd->typ == TypSCSI)
531 return vioscsireq(r);
532 if(r->cmd[0] == 0x35 || r->cmd[0] == 0x91){
533 if(vioblkreq(vd, 4, nil, 0, 0, 0) != 0)
534 return sdsetsense(r, SDcheck, 3, 0xc, 2);
535 return sdsetsense(r, SDok, 0, 0, 0);
537 if((i = sdfakescsi(r)) != SDnostatus)
538 return r->status = i;
539 if((i = sdfakescsirw(r, &lba, &count, &rw)) != SDnostatus)
541 r->rlen = viobio(u, r->lun, rw == SDwrite, r->data, count, lba);
542 return r->status = SDok;
552 if(vd->typ == TypSCSI)
553 return scsionline(u);
555 cap = inl(vd->port+Devspec+4);
557 cap |= inl(vd->port+Devspec);
558 if(u->sectors != cap){
572 if(vd->typ == TypSCSI)
573 return scsiverify(u);
588 snprint(name, sizeof(name), "%s (%s)", sd->name, sd->ifc->name);
589 intrenable(vd->pci->intl, viointerrupt, vd, vd->pci->tbdf, name);
590 outb(vd->port+Status, inb(vd->port+Status) | DriverOk);
601 snprint(name, sizeof(name), "%s (%s)", sd->name, sd->ifc->name);
602 intrdisable(vd->pci->intl, viointerrupt, vd, vd->pci->tbdf, name);
617 for(vd = viopnpdevs(TypBlk); vd; vd = vd->next){
621 if((s = malloc(sizeof(*s))) == nil)
625 s->ifc = &sdvirtioifc;
635 for(vd = viopnpdevs(TypSCSI); vd; vd = vd->next){
641 if((cfg = malloc(sizeof(*cfg))) == nil)
643 cfg->num_queues = inl(vd->port+Devspec+4*0);
644 cfg->seg_max = inl(vd->port+Devspec+4*1);
645 cfg->max_sectors = inl(vd->port+Devspec+4*2);
646 cfg->cmd_per_lun = inl(vd->port+Devspec+4*3);
647 cfg->event_info_size = inl(vd->port+Devspec+4*4);
648 cfg->sense_size = inl(vd->port+Devspec+4*5);
649 cfg->cdb_size = inl(vd->port+Devspec+4*6);
650 cfg->max_channel = ins(vd->port+Devspec+4*7);
651 cfg->max_target = ins(vd->port+Devspec+4*7+2);
652 cfg->max_lun = inl(vd->port+Devspec+4*8);
654 if(cfg->max_target == 0){
658 if((cfg->cdb_size > CDBSIZE) || (cfg->sense_size > SENSESIZE)){
659 print("sdvirtio: cdb %ud or sense size %ud too big\n",
660 cfg->cdb_size, cfg->sense_size);
666 if((s = malloc(sizeof(*s))) == nil)
670 s->ifc = &sdvirtioifc;
671 s->nunit = cfg->max_target;
682 SDifc sdvirtioifc = {
687 vioenable, /* enable */
688 viodisable, /* disable */
690 vioverify, /* verify */
691 vioonline, /* online */