Browse Source

Tversion, Tattach can be exchanged with host.

Signed-off-by: golubovsky <golubovsky@gmail.com>
golubovsky 7 years ago
parent
commit
6f6ea292f8
1 changed files with 232 additions and 2 deletions
  1. 232 2
      sys/src/9/port/dev9p.c

+ 232 - 2
sys/src/9/port/dev9p.c

@@ -27,14 +27,244 @@
 
 #include	"virtio_lib.h"
 
+char* strdup(char *s);
+
+// We use this version string to communicate with QEMU virtio-9P.
+
+#define    VERSION9PU       "9P2000.u"
+
+// Same as in devmnt.c
+
+#define MAXRPC (IOHDRSZ+128*1024)
+
+// Buffer size
+
+#define BUFSZ (8192 + IOHDRSZ)
+
+// Shared with devmnt
+
+extern char Enoversion[];
+extern int alloctag(void);
+extern void freetag(int t);
+
+extern Dev v9pdevtab;
+
+// Array of defined 9p mounts and their number
+
+static uint32_t nv9p;
+
+static Vqctl **v9ps;
+
+static struct v9pmnt
+{
+	char *tag;
+	char *version;
+	uint32_t msize;
+} *mounts;
+
+// Find a mount tag index, return -1 if none found.
+
+static int 
+findtag(char *tag)
+{
+	for(int i = 0; i < nv9p; i++)
+	{
+		if(mounts[i].tag && (!strcmp(mounts[i].tag, tag)))
+			return i;
+	}
+	return -1;
+}
+
+static void
+v9pinit(void)
+{
+	uint32_t nvdev;
+
+	print("virtio-9p initializing\n");
+	nvdev = getvdevnum();
+	if(nvdev <= 0)
+		return;
+	v9ps = mallocz(nvdev * sizeof(Vqctl *), 1);
+	if(v9ps == nil) {
+		print("no memory to allocate v9p\n");
+		return;
+	}
+	nv9p = 0;
+	nv9p = getvdevsbypciid(PCI_DEVICE_ID_VIRTIO_9P, v9ps, nvdev);
+	if(nv9p <= 0)
+		return;
+	mounts = mallocz(sizeof(struct v9pmnt) * nv9p, 1);
+	if(mounts == nil) {
+		print("no memory to allocate v9p\n");
+		return;
+	}
+	print("virtio 9p mounts found: %d\n", nv9p);
+	for(int i = 0; i < nv9p; i++) {
+		struct virtio_9p_config vcfg;
+		int rc = readvdevcfg(v9ps[i], &vcfg, sizeof(vcfg), 0);
+		if(rc < 0)
+			continue;
+		print("config area size %d tag_len %d\n", rc, vcfg.tag_len);
+		mounts[i].tag = mallocz(vcfg.tag_len + 1, 1);
+		readvdevcfg(v9ps[i], mounts[i].tag, vcfg.tag_len, rc);
+		print("tag %s\n", mounts[i].tag);
+		finalinitvdev(v9ps[i]);
+	}
+}
+
+// General virtio request. It takes 2 buffers, one for input and other for output.
+// Both buffers should be mappable to physical addresses (that is, malloced from the
+// system heap, not in any application's address space). The request will be made in the
+// indirect mode because this is the only way that work properly with QEMU 9p.
+
+static int32_t
+do_request(int tidx, void *inbuf, int32_t inlen, void *outbuf, int32_t outlen)
+{
+	uint16_t descr[1];
+	Virtq *vq = v9ps[tidx]->vqs[0];
+	int rc = getdescr(vq, 1, descr);
+	if(rc < 1) {
+		error("Insufficient number of descriptors in virtqueue");
+		return -1;
+	}
+	struct vring_desc req[2] = {
+		{
+			.addr = PADDR(outbuf),
+			.len = outlen,
+			.flags = VRING_DESC_F_NEXT,
+			.next = 1
+		},
+		{
+			.addr = PADDR(inbuf),
+			.len = inlen,
+			.flags = VRING_DESC_F_WRITE,
+			.next = 0
+		}
+	};
+	q2descr(vq, descr[0])->addr = PADDR(&req);
+	q2descr(vq, descr[0])->len = sizeof(req);
+	q2descr(vq, descr[0])->flags = VRING_DESC_F_INDIRECT;
+	q2descr(vq, descr[0])->next = 0;
+	queuedescr(vq, 1, descr);
+	reldescr(vq, 1, descr);
+	return 0;
+}
+
+// Send a version message over the given virtqueue.
+
+static int
+v9pversion(int tidx)
+{
+	uint8_t *msg, *rsp;
+	usize k;
+	Fcall f;
+	f.type = Tversion;
+	f.tag = NOTAG;
+	f.msize = MAXRPC;
+	f.version = VERSION9PU;
+	msg = mallocz(BUFSZ, 1);
+	rsp = mallocz(BUFSZ, 1);
+	if(msg == nil || rsp == nil)
+		exhausted("version memory");
+	k = convS2M(&f, msg, BUFSZ);
+	if(k == 0) {
+		free(msg);
+		free(rsp);
+		error("bad version conversion on send");
+	}
+	int rc = do_request(tidx, rsp, BUFSZ, msg, BUFSZ);
+	int retval = rc;
+	free(msg);
+	if(rc >= 0) {
+		k = convM2S(rsp, BUFSZ, &f);
+		if(f.type == Rerror) {
+			free(rsp);
+			error(f.ename);
+			return -1;
+		} else if(f.type != Rversion) {
+			free(rsp);
+			error("unexpected reply type in fversion");
+			return -1;
+		}
+	}
+	if(f.msize > MAXRPC) {
+		free(rsp);
+		error("server tries to increase msize in fversion");
+		return -1;
+	}
+	if(f.msize<256 || f.msize>1024*1024) {
+		free(rsp);
+		error("nonsense value of msize in fversion");
+		return -1;
+	}
+	mounts[tidx].msize = f.msize;
+	mounts[tidx].version = strdup(f.version);
+	free(rsp);
+	return retval;
+}
+
+static Chan*
+v9pattach(char *spec)
+{
+print("v9pattach %s\n", spec);
+	int tidx = findtag(spec);
+	if(tidx < 0) {
+		error(Enonexist);
+		return nil;
+	}
+	if(!mounts[tidx].version) {
+		int rc = v9pversion(tidx);
+		if(rc < 0) {
+			error(Enoversion);
+			return nil;
+		}
+	}
+	Chan *ch = devattach(v9pdevtab.dc, spec);
+	Fcall r;
+	r.type = Tattach;
+	r.tag = alloctag();
+	r.fid = ch->fid;
+	r.afid = NOFID;
+	r.uname = "";
+	r.aname = "";
+	uint8_t *msg, *rsp;
+	uint32_t ms = mounts[tidx].msize;
+	msg = mallocz(ms, 1);
+	rsp = mallocz(ms, 1);
+	if(msg == nil || rsp == nil)
+		exhausted("attach memory");
+	usize k = convS2M(&r, msg, ms);
+	if(k == 0) {
+		free(msg);
+		free(rsp);
+		freetag(r.tag);
+		error("bad attach conversion on send");
+	}
+	int rc = do_request(tidx, rsp, ms, msg, ms);
+	freetag(r.tag);
+	free(msg);
+	if(rc >= 0) {
+		k = convM2S(rsp, ms, &r);
+		if(r.type == Rerror) {
+			free(rsp);
+			error(r.ename);
+			return nil;
+		}
+		ch->qid = r.qid;
+	}
+	free(rsp);
+	return ch;
+}
+
+
 Dev v9pdevtab = {
 	.dc = '9',
 	.name = "9p",
 
 	.reset = devreset,
-//	.init = v9pinit,
+	.init = v9pinit,
 	.shutdown = devshutdown,
-//	.attach = v9pattach,
+	.attach = v9pattach,
 //	.walk = v9pwalk,
 //	.stat = v9pstat,
 //	.open = v9popen,