2012-04-22 03:19:34 +00:00
|
|
|
#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"
|
|
|
|
|
2012-04-22 05:20:30 +00:00
|
|
|
typedef struct Vring Vring;
|
2012-04-22 03:19:34 +00:00
|
|
|
typedef struct Vdesc Vdesc;
|
|
|
|
typedef struct Vused Vused;
|
|
|
|
typedef struct Vqueue Vqueue;
|
|
|
|
typedef struct Vdev Vdev;
|
|
|
|
|
2014-06-07 00:13:57 +00:00
|
|
|
typedef struct ScsiCfg ScsiCfg;
|
|
|
|
|
|
|
|
/* device types */
|
|
|
|
enum {
|
|
|
|
TypBlk = 2,
|
|
|
|
TypSCSI = 8,
|
|
|
|
};
|
|
|
|
|
2012-04-24 05:48:59 +00:00
|
|
|
/* status flags */
|
2012-04-22 03:19:34 +00:00
|
|
|
enum {
|
2012-04-24 05:48:59 +00:00
|
|
|
Acknowledge = 1,
|
|
|
|
Driver = 2,
|
|
|
|
DriverOk = 4,
|
|
|
|
Failed = 0x80,
|
2012-04-22 03:19:34 +00:00
|
|
|
};
|
|
|
|
|
2012-04-24 05:48:59 +00:00
|
|
|
/* virtio ports */
|
2012-04-22 03:19:34 +00:00
|
|
|
enum {
|
|
|
|
Devfeat = 0,
|
|
|
|
Drvfeat = 4,
|
|
|
|
Qaddr = 8,
|
|
|
|
Qsize = 12,
|
|
|
|
Qselect = 14,
|
|
|
|
Qnotify = 16,
|
|
|
|
Status = 18,
|
|
|
|
Isr = 19,
|
|
|
|
|
|
|
|
Devspec = 20,
|
|
|
|
};
|
|
|
|
|
2012-04-24 05:48:59 +00:00
|
|
|
/* descriptor flags */
|
2012-04-22 03:19:34 +00:00
|
|
|
enum {
|
|
|
|
Next = 1,
|
|
|
|
Write = 2,
|
|
|
|
Indirect = 4,
|
2014-02-03 09:22:26 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/* struct sizes */
|
|
|
|
enum {
|
|
|
|
VringSize = 4,
|
2012-04-22 03:19:34 +00:00
|
|
|
};
|
|
|
|
|
2012-04-22 05:20:30 +00:00
|
|
|
struct Vring
|
2012-04-22 03:19:34 +00:00
|
|
|
{
|
|
|
|
u16int flags;
|
|
|
|
u16int idx;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct Vdesc
|
|
|
|
{
|
|
|
|
u64int addr;
|
|
|
|
u32int len;
|
|
|
|
u16int flags;
|
|
|
|
u16int next;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct Vused
|
|
|
|
{
|
|
|
|
u32int id;
|
|
|
|
u32int len;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct Vqueue
|
|
|
|
{
|
2012-04-23 09:42:05 +00:00
|
|
|
Lock;
|
2014-06-07 00:13:57 +00:00
|
|
|
|
|
|
|
Vdev *dev;
|
|
|
|
int idx;
|
|
|
|
|
2012-04-22 05:20:30 +00:00
|
|
|
int size;
|
2012-04-22 03:19:34 +00:00
|
|
|
|
2012-04-22 05:20:30 +00:00
|
|
|
int free;
|
|
|
|
int nfree;
|
2012-04-22 03:19:34 +00:00
|
|
|
|
2012-04-22 05:20:30 +00:00
|
|
|
Vdesc *desc;
|
2012-04-22 03:19:34 +00:00
|
|
|
|
2012-04-22 05:20:30 +00:00
|
|
|
Vring *avail;
|
|
|
|
u16int *availent;
|
|
|
|
u16int *availevent;
|
2012-04-22 03:19:34 +00:00
|
|
|
|
2012-04-22 05:20:30 +00:00
|
|
|
Vring *used;
|
|
|
|
Vused *usedent;
|
|
|
|
u16int *usedevent;
|
|
|
|
u16int lastused;
|
2012-04-22 03:19:34 +00:00
|
|
|
|
2012-04-23 09:42:05 +00:00
|
|
|
void *rock[];
|
2012-04-22 03:19:34 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct Vdev
|
|
|
|
{
|
|
|
|
int typ;
|
|
|
|
|
|
|
|
Pcidev *pci;
|
|
|
|
|
|
|
|
ulong port;
|
2012-04-23 09:42:05 +00:00
|
|
|
ulong feat;
|
2012-04-22 03:19:34 +00:00
|
|
|
|
|
|
|
int nqueue;
|
|
|
|
Vqueue *queue[16];
|
|
|
|
|
2014-06-07 00:13:57 +00:00
|
|
|
void *cfg; /* device specific config (for scsi) */
|
|
|
|
|
2012-04-22 03:19:34 +00:00
|
|
|
Vdev *next;
|
|
|
|
};
|
|
|
|
|
2014-06-07 00:13:57 +00:00
|
|
|
enum {
|
|
|
|
CDBSIZE = 32,
|
|
|
|
SENSESIZE = 96,
|
|
|
|
};
|
|
|
|
|
|
|
|
struct ScsiCfg
|
|
|
|
{
|
|
|
|
u32int num_queues;
|
|
|
|
u32int seg_max;
|
|
|
|
u32int max_sectors;
|
|
|
|
u32int cmd_per_lun;
|
|
|
|
u32int event_info_size;
|
|
|
|
u32int sense_size;
|
|
|
|
u32int cdb_size;
|
|
|
|
u16int max_channel;
|
|
|
|
u16int max_target;
|
|
|
|
u32int max_lun;
|
|
|
|
};
|
|
|
|
|
2012-04-22 03:19:34 +00:00
|
|
|
static Vqueue*
|
|
|
|
mkvqueue(int size)
|
|
|
|
{
|
|
|
|
Vqueue *q;
|
|
|
|
uchar *p;
|
|
|
|
int i;
|
|
|
|
|
2012-04-23 09:42:05 +00:00
|
|
|
q = malloc(sizeof(*q) + sizeof(void*)*size);
|
2012-04-22 03:19:34 +00:00
|
|
|
p = mallocalign(
|
|
|
|
PGROUND(sizeof(Vdesc)*size +
|
2014-02-03 09:22:26 +00:00
|
|
|
VringSize +
|
2012-04-22 03:19:34 +00:00
|
|
|
sizeof(u16int)*size +
|
|
|
|
sizeof(u16int)) +
|
2014-02-03 09:22:26 +00:00
|
|
|
PGROUND(VringSize +
|
2012-04-22 03:19:34 +00:00
|
|
|
sizeof(Vused)*size +
|
|
|
|
sizeof(u16int)),
|
|
|
|
BY2PG, 0, 0);
|
|
|
|
if(p == nil || q == nil){
|
2012-04-24 06:46:23 +00:00
|
|
|
print("virtio: no memory for Vqueue\n");
|
2012-04-22 03:19:34 +00:00
|
|
|
free(p);
|
|
|
|
free(q);
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
q->desc = (void*)p;
|
|
|
|
p += sizeof(Vdesc)*size;
|
|
|
|
q->avail = (void*)p;
|
2014-02-03 09:22:26 +00:00
|
|
|
p += VringSize;
|
2012-04-22 03:19:34 +00:00
|
|
|
q->availent = (void*)p;
|
|
|
|
p += sizeof(u16int)*size;
|
|
|
|
q->availevent = (void*)p;
|
|
|
|
p += sizeof(u16int);
|
|
|
|
|
2014-02-03 09:22:26 +00:00
|
|
|
p = (uchar*)PGROUND((uintptr)p);
|
2012-04-22 03:19:34 +00:00
|
|
|
q->used = (void*)p;
|
2014-02-03 09:22:26 +00:00
|
|
|
p += VringSize;
|
2012-04-22 03:19:34 +00:00
|
|
|
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)
|
|
|
|
{
|
2012-04-24 05:48:59 +00:00
|
|
|
Vdev *vd, *h, *t;
|
2014-06-07 00:13:57 +00:00
|
|
|
Vqueue *q;
|
2012-04-22 03:19:34 +00:00
|
|
|
Pcidev *p;
|
|
|
|
int n, i;
|
|
|
|
|
2012-04-24 05:48:59 +00:00
|
|
|
h = t = nil;
|
2014-06-07 00:13:57 +00:00
|
|
|
for(p = nil; p = pcimatch(p, 0x1AF4, 0);){
|
|
|
|
if((p->did < 0x1000) || (p->did > 0x103F))
|
2012-04-22 03:19:34 +00:00
|
|
|
continue;
|
|
|
|
if(p->rid != 0)
|
|
|
|
continue;
|
|
|
|
if(pcicfgr16(p, 0x2E) != typ)
|
|
|
|
continue;
|
|
|
|
if((vd = malloc(sizeof(*vd))) == nil){
|
2012-04-24 06:46:23 +00:00
|
|
|
print("virtio: no memory for Vdev\n");
|
2012-04-22 03:19:34 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
vd->port = p->mem[0].bar & ~0x1;
|
2012-04-24 05:48:59 +00:00
|
|
|
if(ioalloc(vd->port, p->mem[0].size, 0, "virtio") < 0){
|
2012-04-24 06:46:23 +00:00
|
|
|
print("virtio: port %lux in use\n", vd->port);
|
2012-04-24 05:48:59 +00:00
|
|
|
free(vd);
|
|
|
|
continue;
|
|
|
|
}
|
2012-04-22 03:19:34 +00:00
|
|
|
vd->typ = typ;
|
|
|
|
vd->pci = p;
|
2012-04-23 09:42:05 +00:00
|
|
|
|
|
|
|
/* reset */
|
|
|
|
outb(vd->port+Status, 0);
|
|
|
|
|
|
|
|
vd->feat = inl(vd->port+Devfeat);
|
|
|
|
outb(vd->port+Status, Acknowledge|Driver);
|
2012-04-22 03:19:34 +00:00
|
|
|
for(i=0; i<nelem(vd->queue); i++){
|
|
|
|
outs(vd->port+Qselect, i);
|
2012-04-24 06:46:23 +00:00
|
|
|
n = ins(vd->port+Qsize);
|
|
|
|
if(n == 0 || (n & (n-1)) != 0)
|
2012-04-22 03:19:34 +00:00
|
|
|
break;
|
2014-06-07 00:13:57 +00:00
|
|
|
if((q = mkvqueue(n)) == nil)
|
2012-04-22 03:19:34 +00:00
|
|
|
break;
|
2014-06-07 00:13:57 +00:00
|
|
|
q->dev = vd;
|
|
|
|
q->idx = i;
|
|
|
|
vd->queue[i] = q;
|
2012-04-22 03:19:34 +00:00
|
|
|
coherence();
|
2012-04-24 05:48:59 +00:00
|
|
|
outl(vd->port+Qaddr, PADDR(vd->queue[i]->desc)/BY2PG);
|
2012-04-22 03:19:34 +00:00
|
|
|
}
|
|
|
|
vd->nqueue = i;
|
|
|
|
|
2012-04-24 05:48:59 +00:00
|
|
|
if(h == nil)
|
|
|
|
h = vd;
|
2012-04-22 03:19:34 +00:00
|
|
|
else
|
2012-04-24 05:48:59 +00:00
|
|
|
t->next = vd;
|
|
|
|
t = vd;
|
2012-04-22 03:19:34 +00:00
|
|
|
}
|
2012-04-24 05:48:59 +00:00
|
|
|
|
|
|
|
return h;
|
2012-04-22 03:19:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
struct Rock {
|
|
|
|
int done;
|
2012-04-23 09:42:05 +00:00
|
|
|
Rendez *sleep;
|
2012-04-22 03:19:34 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static void
|
2014-06-07 00:13:57 +00:00
|
|
|
vqinterrupt(Vqueue *q)
|
2012-04-22 03:19:34 +00:00
|
|
|
{
|
2012-04-23 09:42:05 +00:00
|
|
|
int id, free, m;
|
|
|
|
struct Rock *r;
|
2012-08-24 10:52:34 +00:00
|
|
|
Rendez *z;
|
2012-04-22 03:19:34 +00:00
|
|
|
|
2014-06-07 00:13:57 +00:00
|
|
|
m = q->size-1;
|
2012-04-23 09:42:05 +00:00
|
|
|
|
2014-06-07 00:13:57 +00:00
|
|
|
ilock(q);
|
|
|
|
while((q->lastused ^ q->used->idx) & m){
|
|
|
|
id = q->usedent[q->lastused++ & m].id;
|
|
|
|
if(r = q->rock[id]){
|
|
|
|
q->rock[id] = nil;
|
|
|
|
z = r->sleep;
|
|
|
|
r->done = 1; /* hands off */
|
|
|
|
if(z != nil)
|
|
|
|
wakeup(z);
|
2012-04-23 09:42:05 +00:00
|
|
|
}
|
2014-06-07 00:13:57 +00:00
|
|
|
do {
|
|
|
|
free = id;
|
|
|
|
id = q->desc[free].next;
|
|
|
|
q->desc[free].next = q->free;
|
|
|
|
q->free = free;
|
|
|
|
q->nfree++;
|
|
|
|
} while(q->desc[free].flags & Next);
|
2012-04-23 09:42:05 +00:00
|
|
|
}
|
2014-06-07 00:13:57 +00:00
|
|
|
iunlock(q);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
viointerrupt(Ureg *, void *arg)
|
|
|
|
{
|
|
|
|
Vdev *vd = arg;
|
|
|
|
|
|
|
|
if(inb(vd->port+Isr) & 1)
|
|
|
|
vqinterrupt(vd->queue[vd->typ == TypSCSI ? 2 : 0]);
|
2012-04-22 03:19:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
viodone(void *arg)
|
|
|
|
{
|
2012-04-23 09:42:05 +00:00
|
|
|
return ((struct Rock*)arg)->done;
|
2012-04-22 03:19:34 +00:00
|
|
|
}
|
|
|
|
|
2014-12-22 11:17:48 +00:00
|
|
|
static void
|
|
|
|
vqio(Vqueue *q, int head)
|
|
|
|
{
|
|
|
|
struct Rock rock;
|
|
|
|
|
|
|
|
rock.done = 0;
|
|
|
|
rock.sleep = &up->sleep;
|
|
|
|
q->rock[head] = &rock;
|
|
|
|
q->availent[q->avail->idx & (q->size-1)] = head;
|
|
|
|
coherence();
|
|
|
|
q->avail->idx++;
|
|
|
|
iunlock(q);
|
|
|
|
if((q->used->flags & 1) == 0)
|
|
|
|
outs(q->dev->port+Qnotify, q->idx);
|
|
|
|
while(!rock.done){
|
|
|
|
while(waserror())
|
|
|
|
;
|
|
|
|
tsleep(rock.sleep, viodone, &rock, 1000);
|
|
|
|
poperror();
|
|
|
|
|
|
|
|
if(!rock.done)
|
|
|
|
vqinterrupt(q);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-04-22 05:20:30 +00:00
|
|
|
static int
|
2014-06-07 00:13:57 +00:00
|
|
|
vioblkreq(Vdev *vd, int typ, void *a, long count, long secsize, uvlong lba)
|
2012-04-22 03:19:34 +00:00
|
|
|
{
|
2012-04-23 09:42:05 +00:00
|
|
|
int free, head;
|
2012-04-22 03:19:34 +00:00
|
|
|
Vqueue *q;
|
|
|
|
Vdesc *d;
|
|
|
|
|
2012-04-22 05:20:30 +00:00
|
|
|
u8int status;
|
2014-06-07 00:13:57 +00:00
|
|
|
struct Vioblkreqhdr {
|
2012-04-22 05:20:30 +00:00
|
|
|
u32int typ;
|
|
|
|
u32int prio;
|
|
|
|
u64int lba;
|
|
|
|
} req;
|
2012-04-22 03:19:34 +00:00
|
|
|
|
2012-04-22 05:20:30 +00:00
|
|
|
status = 0;
|
|
|
|
req.typ = typ;
|
|
|
|
req.prio = 0;
|
|
|
|
req.lba = lba;
|
|
|
|
|
|
|
|
q = vd->queue[0];
|
2012-04-23 09:42:05 +00:00
|
|
|
ilock(q);
|
|
|
|
while(q->nfree < 3){
|
|
|
|
iunlock(q);
|
|
|
|
|
2012-04-22 05:20:30 +00:00
|
|
|
if(!waserror())
|
|
|
|
tsleep(&up->sleep, return0, 0, 500);
|
|
|
|
poperror();
|
2012-04-23 09:42:05 +00:00
|
|
|
|
|
|
|
ilock(q);
|
2012-04-22 03:19:34 +00:00
|
|
|
}
|
|
|
|
|
2012-04-22 05:20:30 +00:00
|
|
|
head = free = q->free;
|
2012-04-22 03:19:34 +00:00
|
|
|
|
|
|
|
d = &q->desc[free]; free = d->next;
|
2012-04-22 05:20:30 +00:00
|
|
|
d->addr = PADDR(&req);
|
|
|
|
d->len = sizeof(req);
|
2012-04-22 03:19:34 +00:00
|
|
|
d->flags = Next;
|
|
|
|
|
2012-04-23 09:42:05 +00:00
|
|
|
d = &q->desc[free]; free = d->next;
|
|
|
|
d->addr = PADDR(a);
|
|
|
|
d->len = secsize*count;
|
|
|
|
d->flags = typ ? Next : (Write|Next);
|
2012-04-22 03:19:34 +00:00
|
|
|
|
|
|
|
d = &q->desc[free]; free = d->next;
|
|
|
|
d->addr = PADDR(&status);
|
|
|
|
d->len = sizeof(status);
|
|
|
|
d->flags = Write;
|
|
|
|
|
|
|
|
q->free = free;
|
2012-04-23 09:42:05 +00:00
|
|
|
q->nfree -= 3;
|
2012-04-22 03:19:34 +00:00
|
|
|
|
2014-12-22 11:17:48 +00:00
|
|
|
/* queue io, unlock and wait for completion */
|
|
|
|
vqio(q, head);
|
2012-04-22 03:19:34 +00:00
|
|
|
|
2012-04-22 05:20:30 +00:00
|
|
|
return status;
|
|
|
|
}
|
2012-04-22 03:19:34 +00:00
|
|
|
|
2014-06-07 00:13:57 +00:00
|
|
|
static int
|
|
|
|
vioscsireq(SDreq *r)
|
|
|
|
{
|
|
|
|
u8int resp[4+4+2+2+SENSESIZE];
|
|
|
|
u8int req[8+8+3+CDBSIZE];
|
|
|
|
int free, head;
|
|
|
|
u32int len;
|
|
|
|
Vqueue *q;
|
|
|
|
Vdesc *d;
|
|
|
|
Vdev *vd;
|
|
|
|
SDunit *u;
|
|
|
|
ScsiCfg *cfg;
|
|
|
|
|
|
|
|
u = r->unit;
|
|
|
|
vd = u->dev->ctlr;
|
|
|
|
cfg = vd->cfg;
|
|
|
|
|
|
|
|
memset(resp, 0, sizeof(resp));
|
|
|
|
memset(req, 0, sizeof(req));
|
|
|
|
req[0] = 1;
|
|
|
|
req[1] = u->subno;
|
|
|
|
req[2] = r->lun>>8;
|
|
|
|
req[3] = r->lun&0xFF;
|
|
|
|
*(u64int*)(&req[8]) = (uintptr)r;
|
|
|
|
|
|
|
|
memmove(&req[8+8+3], r->cmd, r->clen);
|
|
|
|
|
|
|
|
q = vd->queue[2];
|
|
|
|
ilock(q);
|
|
|
|
while(q->nfree < 3){
|
|
|
|
iunlock(q);
|
|
|
|
|
|
|
|
if(!waserror())
|
|
|
|
tsleep(&up->sleep, return0, 0, 500);
|
|
|
|
poperror();
|
|
|
|
|
|
|
|
ilock(q);
|
|
|
|
}
|
|
|
|
|
|
|
|
head = free = q->free;
|
|
|
|
|
|
|
|
d = &q->desc[free]; free = d->next;
|
|
|
|
d->addr = PADDR(req);
|
|
|
|
d->len = 8+8+3+cfg->cdb_size;
|
|
|
|
d->flags = Next;
|
|
|
|
|
|
|
|
if(r->write && r->dlen > 0){
|
|
|
|
d = &q->desc[free]; free = d->next;
|
|
|
|
d->addr = PADDR(r->data);
|
|
|
|
d->len = r->dlen;
|
|
|
|
d->flags = Next;
|
|
|
|
}
|
|
|
|
|
|
|
|
d = &q->desc[free]; free = d->next;
|
|
|
|
d->addr = PADDR(resp);
|
|
|
|
d->len = 4+4+2+2+cfg->sense_size;
|
|
|
|
d->flags = Write;
|
|
|
|
|
|
|
|
if(!r->write && r->dlen > 0){
|
|
|
|
d->flags |= Next;
|
|
|
|
|
|
|
|
d = &q->desc[free]; free = d->next;
|
|
|
|
d->addr = PADDR(r->data);
|
|
|
|
d->len = r->dlen;
|
|
|
|
d->flags = Write;
|
|
|
|
}
|
|
|
|
|
|
|
|
q->free = free;
|
|
|
|
q->nfree -= 2 + (r->dlen > 0);
|
|
|
|
|
2014-12-22 11:17:48 +00:00
|
|
|
/* queue io, unlock and wait for completion */
|
|
|
|
vqio(q, head);
|
2014-06-07 00:13:57 +00:00
|
|
|
|
|
|
|
/* response+status */
|
|
|
|
r->status = resp[10];
|
|
|
|
if(resp[11] != 0)
|
|
|
|
r->status = SDcheck;
|
|
|
|
|
|
|
|
/* sense_len */
|
|
|
|
len = *((u32int*)&resp[0]);
|
|
|
|
if(len > 0){
|
|
|
|
if(len > sizeof(r->sense))
|
|
|
|
len = sizeof(r->sense);
|
|
|
|
memmove(r->sense, &resp[4+4+2+2], len);
|
|
|
|
r->flags |= SDvalidsense;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* data residue */
|
|
|
|
len = *((u32int*)&resp[4]);
|
|
|
|
if(len > r->dlen)
|
|
|
|
r->rlen = 0;
|
|
|
|
else
|
|
|
|
r->rlen = r->dlen - len;
|
|
|
|
|
|
|
|
return r->status;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2012-04-22 05:20:30 +00:00
|
|
|
static long
|
2014-06-07 00:13:57 +00:00
|
|
|
viobio(SDunit *u, int lun, int write, void *a, long count, uvlong lba)
|
2012-04-22 05:20:30 +00:00
|
|
|
{
|
|
|
|
long ss, cc, max, ret;
|
|
|
|
Vdev *vd;
|
|
|
|
|
|
|
|
vd = u->dev->ctlr;
|
2014-06-07 00:13:57 +00:00
|
|
|
if(vd->typ == TypSCSI)
|
|
|
|
return scsibio(u, lun, write, a, count, lba);
|
2012-04-22 05:20:30 +00:00
|
|
|
|
2014-06-07 00:13:57 +00:00
|
|
|
max = 32;
|
|
|
|
ss = u->secsize;
|
2012-04-22 05:20:30 +00:00
|
|
|
ret = 0;
|
|
|
|
while(count > 0){
|
|
|
|
if((cc = count) > max)
|
|
|
|
cc = max;
|
2014-06-07 00:13:57 +00:00
|
|
|
if(vioblkreq(vd, write != 0, (uchar*)a + ret, cc, ss, lba) != 0)
|
2012-04-22 05:20:30 +00:00
|
|
|
error(Eio);
|
|
|
|
ret += cc*ss;
|
|
|
|
count -= cc;
|
|
|
|
lba += cc;
|
|
|
|
}
|
|
|
|
return ret;
|
2012-04-22 03:19:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
viorio(SDreq *r)
|
|
|
|
{
|
|
|
|
int i, count, rw;
|
|
|
|
uvlong lba;
|
|
|
|
SDunit *u;
|
2014-06-07 00:13:57 +00:00
|
|
|
Vdev *vd;
|
2012-04-22 03:19:34 +00:00
|
|
|
|
|
|
|
u = r->unit;
|
2014-06-07 00:13:57 +00:00
|
|
|
vd = u->dev->ctlr;
|
|
|
|
if(vd->typ == TypSCSI)
|
|
|
|
return vioscsireq(r);
|
2012-04-22 03:19:34 +00:00
|
|
|
if(r->cmd[0] == 0x35 || r->cmd[0] == 0x91){
|
2014-06-07 00:13:57 +00:00
|
|
|
if(vioblkreq(vd, 4, nil, 0, 0, 0) != 0)
|
2012-04-22 05:20:30 +00:00
|
|
|
return sdsetsense(r, SDcheck, 3, 0xc, 2);
|
|
|
|
return sdsetsense(r, SDok, 0, 0, 0);
|
2012-04-22 03:19:34 +00:00
|
|
|
}
|
2012-04-22 05:20:30 +00:00
|
|
|
if((i = sdfakescsi(r)) != SDnostatus)
|
|
|
|
return r->status = i;
|
2012-04-22 03:19:34 +00:00
|
|
|
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;
|
2014-06-07 00:13:57 +00:00
|
|
|
if(vd->typ == TypSCSI)
|
|
|
|
return scsionline(u);
|
|
|
|
|
2012-04-22 03:19:34 +00:00
|
|
|
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
|
2014-06-07 00:13:57 +00:00
|
|
|
vioverify(SDunit *u)
|
2012-04-22 03:19:34 +00:00
|
|
|
{
|
2014-06-07 00:13:57 +00:00
|
|
|
Vdev *vd;
|
|
|
|
|
|
|
|
vd = u->dev->ctlr;
|
|
|
|
if(vd->typ == TypSCSI)
|
|
|
|
return scsiverify(u);
|
|
|
|
|
2012-04-22 03:19:34 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
SDifc sdvirtioifc;
|
|
|
|
|
2014-06-07 00:13:57 +00:00
|
|
|
static void
|
|
|
|
vdevenable(Vdev *vd)
|
|
|
|
{
|
|
|
|
intrenable(vd->pci->intl, viointerrupt, vd, vd->pci->tbdf, "virtio");
|
|
|
|
outb(vd->port+Status, inb(vd->port+Status) | DriverOk);
|
|
|
|
}
|
|
|
|
|
2012-04-22 03:19:34 +00:00
|
|
|
static SDev*
|
|
|
|
viopnp(void)
|
|
|
|
{
|
|
|
|
SDev *s, *h, *t;
|
|
|
|
Vdev *vd;
|
|
|
|
int id;
|
2014-06-07 00:13:57 +00:00
|
|
|
|
2012-04-22 03:19:34 +00:00
|
|
|
h = t = nil;
|
2014-06-07 00:13:57 +00:00
|
|
|
|
|
|
|
id = 'F';
|
|
|
|
for(vd = viopnpdevs(TypBlk); vd; vd = vd->next){
|
2012-04-22 03:19:34 +00:00
|
|
|
if(vd->nqueue != 1)
|
|
|
|
continue;
|
|
|
|
|
2014-06-07 00:13:57 +00:00
|
|
|
vdevenable(vd);
|
2012-04-22 03:19:34 +00:00
|
|
|
|
2012-04-24 05:48:59 +00:00
|
|
|
if((s = malloc(sizeof(*s))) == nil)
|
2012-04-22 03:19:34 +00:00
|
|
|
break;
|
|
|
|
s->ctlr = vd;
|
|
|
|
s->idno = id++;
|
|
|
|
s->ifc = &sdvirtioifc;
|
|
|
|
s->nunit = 1;
|
|
|
|
if(h)
|
|
|
|
t->next = s;
|
|
|
|
else
|
|
|
|
h = s;
|
|
|
|
t = s;
|
|
|
|
}
|
|
|
|
|
2014-06-07 00:13:57 +00:00
|
|
|
id = '0';
|
|
|
|
for(vd = viopnpdevs(TypSCSI); vd; vd = vd->next){
|
|
|
|
ScsiCfg *cfg;
|
|
|
|
|
|
|
|
if(vd->nqueue < 3)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if((cfg = malloc(sizeof(*cfg))) == nil)
|
|
|
|
break;
|
|
|
|
cfg->num_queues = inl(vd->port+Devspec+4*0);
|
|
|
|
cfg->seg_max = inl(vd->port+Devspec+4*1);
|
|
|
|
cfg->max_sectors = inl(vd->port+Devspec+4*2);
|
|
|
|
cfg->cmd_per_lun = inl(vd->port+Devspec+4*3);
|
|
|
|
cfg->event_info_size = inl(vd->port+Devspec+4*4);
|
|
|
|
cfg->sense_size = inl(vd->port+Devspec+4*5);
|
|
|
|
cfg->cdb_size = inl(vd->port+Devspec+4*6);
|
|
|
|
cfg->max_channel = ins(vd->port+Devspec+4*7);
|
|
|
|
cfg->max_target = ins(vd->port+Devspec+4*7+2);
|
|
|
|
cfg->max_lun = inl(vd->port+Devspec+4*8);
|
|
|
|
|
|
|
|
if(cfg->max_target == 0){
|
|
|
|
free(cfg);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if((cfg->cdb_size > CDBSIZE) || (cfg->sense_size > SENSESIZE)){
|
|
|
|
print("sdvirtio: cdb %ud or sense size %ud too big\n",
|
|
|
|
cfg->cdb_size, cfg->sense_size);
|
|
|
|
free(cfg);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
vd->cfg = cfg;
|
|
|
|
|
|
|
|
vdevenable(vd);
|
|
|
|
|
|
|
|
if((s = malloc(sizeof(*s))) == nil)
|
|
|
|
break;
|
|
|
|
s->ctlr = vd;
|
|
|
|
s->idno = id++;
|
|
|
|
s->ifc = &sdvirtioifc;
|
|
|
|
s->nunit = cfg->max_target;
|
|
|
|
if(h)
|
|
|
|
t->next = s;
|
|
|
|
else
|
|
|
|
h = s;
|
|
|
|
t = s;
|
|
|
|
}
|
|
|
|
|
2012-04-22 03:19:34 +00:00
|
|
|
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 */
|
|
|
|
};
|