git: 9front

Download patch

ref: 9fcdbfca7f889f0641d35820d8408b4fcf768c94
parent: 121ff4e951df1d5d2076d85292d5f5a3ad74765e
author: cinap_lenrek <cinap_lenrek@gmx.de>
date: Sun Apr 22 01:19:34 EDT 2012

sdvirtio: primitive virtio block driver

--- a/sys/src/9/pc/pcf
+++ b/sys/src/9/pc/pcf
@@ -88,6 +88,7 @@
 	sdmylex		pci sdscsi
 	sdiahci		pci sdscsi led
 	sdodin		pci sdscsi led
+	sdvirtio	pci sdscsi
 	sdloop
 
 	uarti8250
--- /dev/null
+++ b/sys/src/9/pc/sdvirtio.c
@@ -1,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 */
+};
--