sdvirtio: primitive virtio block driver

front
cinap_lenrek 2012-04-22 05:19:34 +02:00
parent dd318f09cd
commit ee983985d9
2 changed files with 436 additions and 0 deletions

View File

@ -88,6 +88,7 @@ misc
sdmylex pci sdscsi sdmylex pci sdscsi
sdiahci pci sdscsi led sdiahci pci sdscsi led
sdodin pci sdscsi led sdodin pci sdscsi led
sdvirtio pci sdscsi
sdloop sdloop
uarti8250 uarti8250

435
sys/src/9/pc/sdvirtio.c Normal file
View File

@ -0,0 +1,435 @@
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
#include "ureg.h"
#include "../port/error.h"
#include "../port/sd.h"
typedef struct Vioreqhdr Vioreqhdr;
typedef struct Vringhdr Vringhdr;
typedef struct Vdesc Vdesc;
typedef struct Vused Vused;
typedef struct Vqueue Vqueue;
typedef struct Vdev Vdev;
enum {
Acknowledge = 1,
Driver = 2,
DriverOk = 4,
Failed = 128,
};
enum {
Devfeat = 0,
Drvfeat = 4,
Qaddr = 8,
Qsize = 12,
Qselect = 14,
Qnotify = 16,
Status = 18,
Isr = 19,
Devspec = 20,
};
enum {
Next = 1,
Write = 2,
Indirect = 4,
};
struct Vioreqhdr
{
u32int typ;
u32int prio;
u64int lba;
};
struct Vringhdr
{
u16int flags;
u16int idx;
};
struct Vdesc
{
u64int addr;
u32int len;
u16int flags;
u16int next;
};
struct Vused
{
u32int id;
u32int len;
};
struct Vqueue
{
int size;
int free;
int nfree;
Vdesc *desc;
Vringhdr *avail;
u16int *availent;
u16int *availevent;
Vringhdr *used;
Vused *usedent;
u16int *usedevent;
u16int lastused;
Rendez;
QLock;
Lock;
};
struct Vdev
{
int typ;
Pcidev *pci;
ulong port;
ulong features;
int nqueue;
Vqueue *queue[16];
Vdev *next;
};
static Vqueue*
mkvqueue(int size)
{
Vqueue *q;
uchar *p;
int i;
q = malloc(sizeof(*q));
p = mallocalign(
PGROUND(sizeof(Vdesc)*size +
sizeof(Vringhdr) +
sizeof(u16int)*size +
sizeof(u16int)) +
PGROUND(sizeof(Vringhdr) +
sizeof(Vused)*size +
sizeof(u16int)),
BY2PG, 0, 0);
if(p == nil || q == nil){
print("mkvqueue: no memory for Vqueue\n");
free(p);
free(q);
return nil;
}
q->desc = (void*)p;
p += sizeof(Vdesc)*size;
q->avail = (void*)p;
p += sizeof(Vringhdr);
q->availent = (void*)p;
p += sizeof(u16int)*size;
q->availevent = (void*)p;
p += sizeof(u16int);
p = (uchar*)PGROUND((ulong)p);
q->used = (void*)p;
p += sizeof(Vringhdr);
q->usedent = (void*)p;
p += sizeof(Vused)*size;
q->usedevent = (void*)p;
q->free = -1;
q->nfree = q->size = size;
for(i=0; i<size; i++){
q->desc[i].next = q->free;
q->free = i;
}
return q;
}
static Vdev*
viopnpdevs(int typ)
{
Vdev *vd, *head, *tail;
Pcidev *p;
u32int a;
int n, i;
head = tail = nil;
for(p = nil; p = pcimatch(p, 0, 0);){
if(p->vid != 0x1AF4)
continue;
if((p->did < 0x1000) || (p->did >= 0x1040))
continue;
if(p->rid != 0)
continue;
if(pcicfgr16(p, 0x2E) != typ)
continue;
if((vd = malloc(sizeof(*vd))) == nil){
print("viopnpdevs: cannot allocate memory for Vdev\n");
break;
}
vd->port = p->mem[0].bar & ~0x1;
vd->typ = typ;
vd->pci = p;
vd->features = inl(vd->port+Devfeat);
outb(vd->port+Status, inb(vd->port+Status)|Acknowledge|Driver);
for(i=0; i<nelem(vd->queue); i++){
outs(vd->port+Qselect, i);
if((n = ins(vd->port+Qsize)) == 0)
break;
if((vd->queue[i] = mkvqueue(n)) == nil)
break;
coherence();
a = PADDR(vd->queue[i]->desc)/BY2PG;
outl(vd->port+Qaddr, a);
}
vd->nqueue = i;
if(head == nil)
head = vd;
else
tail->next = vd;
tail = vd;
}
return head;
}
struct Rock {
Vqueue *q;
int id;
int done;
};
static void
viointerrupt(Ureg *, void *arg)
{
Vdev *vd;
int i;
vd = arg;
if(inb(vd->port+Isr) & 1)
for(i=0; i<vd->nqueue; i++)
wakeup(vd->queue[i]);
}
static int
viodone(void *arg)
{
struct Rock *r;
Vqueue *q;
u16int i;
r = arg;
q = r->q;
for(i = q->lastused; i != q->used->idx; i++)
if(q->usedent[i % q->size].id == r->id){
if(i == q->lastused)
q->lastused++;
r->done = 1;
break;
}
return r->done;
}
static void
viowait(Vqueue *q, int id)
{
struct Rock r;
r.q = q;
r.id = id;
r.done = 0;
do {
qlock(q);
while(waserror())
;
sleep(q, viodone, &r);
poperror();
qunlock(q);
} while(!r.done);
}
static long
viobio(SDunit *u, int, int write, void *a, long count, uvlong lba)
{
int i, free, head;
u8int status;
Vioreqhdr h;
Vqueue *q;
Vdev *vd;
Vdesc *d;
uchar *p;
vd = u->dev->ctlr;
q = vd->queue[0];
lock(q);
if(q->nfree < (2+count)){
unlock(q);
error("out of virtio descriptors");
}
head = free = q->free;
status = 0;
h.typ = write != 0;
h.lba = lba;
h.prio = 0;
d = &q->desc[free]; free = d->next;
d->addr = PADDR(&h);
d->len = sizeof(h);
d->flags = Next;
p = a;
for(i = 0; i<count; i++){
d = &q->desc[free]; free = d->next;
d->addr = PADDR(p);
d->len = u->secsize;
d->flags = write ? Next : (Write|Next);
p += d->len;
}
d = &q->desc[free]; free = d->next;
d->addr = PADDR(&status);
d->len = sizeof(status);
d->flags = Write;
d->next = -1;
q->free = free;
q->nfree -= 2+count;
coherence();
q->availent[q->avail->idx++ % q->size] = head;
unlock(q);
coherence();
outs(vd->port+Qnotify, 0);
viowait(q, head);
lock(q);
d->next = q->free;
q->free = head;
q->nfree += 2+count;
unlock(q);
if(status != 0)
error(Eio);
return count*u->secsize;
}
static int
viorio(SDreq *r)
{
int i, count, rw;
uvlong lba;
SDunit *u;
u = r->unit;
if(r->cmd[0] == 0x35 || r->cmd[0] == 0x91){
/* flush */
// return sdsetsense(r, SDok, 0, 0, 0);
return sdsetsense(r, SDcheck, 3, 0xc, 2);
}
if((i = sdfakescsi(r)) != SDnostatus){
r->status = i;
return i;
}
if((i = sdfakescsirw(r, &lba, &count, &rw)) != SDnostatus)
return i;
r->rlen = viobio(u, r->lun, rw == SDwrite, r->data, count, lba);
return r->status = SDok;
}
static int
vioonline(SDunit *u)
{
uvlong cap;
Vdev *vd;
vd = u->dev->ctlr;
cap = inl(vd->port+Devspec+4);
cap <<= 32;
cap |= inl(vd->port+Devspec);
if(u->sectors != cap){
u->sectors = cap;
u->secsize = 512;
return 2;
}
return 1;
}
static int
vioverify(SDunit *)
{
return 1;
}
SDifc sdvirtioifc;
static SDev*
viopnp(void)
{
SDev *s, *h, *t;
Vdev *vd;
int id;
id = 'F';
h = t = nil;
for(vd = viopnpdevs(2); vd; vd = vd->next){
if(vd->nqueue != 1)
continue;
intrenable(vd->pci->intl, viointerrupt, vd, vd->pci->tbdf, "sdvirtio");
outb(vd->port+Status, inb(vd->port+Status) | DriverOk);
s = malloc(sizeof(*s));
if(s == nil)
break;
s->ctlr = vd;
s->idno = id++;
s->ifc = &sdvirtioifc;
s->nunit = 1;
if(h)
t->next = s;
else
h = s;
t = s;
}
return h;
}
SDifc sdvirtioifc = {
"virtio", /* name */
viopnp, /* pnp */
nil, /* legacy */
nil, /* enable */
nil, /* disable */
vioverify, /* verify */
vioonline, /* online */
viorio, /* rio */
nil, /* rctl */
nil, /* wctl */
viobio, /* bio */
nil, /* probe */
nil, /* clear */
nil, /* rtopctl */
nil, /* wtopctl */
};