sdvirtio: primitive virtio block driver
parent
dd318f09cd
commit
ee983985d9
|
@ -88,6 +88,7 @@ misc
|
|||
sdmylex pci sdscsi
|
||||
sdiahci pci sdscsi led
|
||||
sdodin pci sdscsi led
|
||||
sdvirtio pci sdscsi
|
||||
sdloop
|
||||
|
||||
uarti8250
|
||||
|
|
|
@ -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 */
|
||||
};
|
Loading…
Reference in New Issue