xref: /kvmtool/virtio/9p.c (revision 97b408af3d637965c6a60a043e5f028d9fa2ec16)
11c7850f9SSasha Levin #include "kvm/virtio-pci-dev.h"
21c7850f9SSasha Levin #include "kvm/ioport.h"
31c7850f9SSasha Levin #include "kvm/util.h"
41c7850f9SSasha Levin #include "kvm/threadpool.h"
560eb42d5SSasha Levin #include "kvm/ioeventfd.h"
6bfc15268SAneesh Kumar K.V #include "kvm/irq.h"
7bfc15268SAneesh Kumar K.V #include "kvm/virtio-9p.h"
81c7850f9SSasha Levin 
9bfc15268SAneesh Kumar K.V #include <stdio.h>
10bfc15268SAneesh Kumar K.V #include <stdlib.h>
111c7850f9SSasha Levin #include <fcntl.h>
121c7850f9SSasha Levin #include <sys/stat.h>
13bfc15268SAneesh Kumar K.V #include <unistd.h>
14bfc15268SAneesh Kumar K.V #include <string.h>
15bfc15268SAneesh Kumar K.V #include <errno.h>
161c7850f9SSasha Levin 
172daa28d4SAneesh Kumar K.V #include <linux/virtio_ring.h>
182daa28d4SAneesh Kumar K.V #include <linux/virtio_9p.h>
192daa28d4SAneesh Kumar K.V #include <net/9p/9p.h>
202daa28d4SAneesh Kumar K.V 
21af045e53SAneesh Kumar K.V 
221c7850f9SSasha Levin /* Warning: Immediately use value returned from this function */
23b4422bf3SAneesh Kumar K.V static const char *rel_to_abs(struct p9_dev *p9dev,
24b4422bf3SAneesh Kumar K.V 			      const char *path, char *abs_path)
251c7850f9SSasha Levin {
26b4422bf3SAneesh Kumar K.V 	sprintf(abs_path, "%s/%s", p9dev->root_dir, path);
271c7850f9SSasha Levin 
281c7850f9SSasha Levin 	return abs_path;
291c7850f9SSasha Levin }
301c7850f9SSasha Levin 
31b4422bf3SAneesh Kumar K.V static bool virtio_p9_dev_in(struct p9_dev *p9dev, void *data,
32b4422bf3SAneesh Kumar K.V 			     unsigned long offset,
33b4422bf3SAneesh Kumar K.V 			     int size, u32 count)
341c7850f9SSasha Levin {
35b4422bf3SAneesh Kumar K.V 	u8 *config_space = (u8 *) p9dev->config;
361c7850f9SSasha Levin 
371c7850f9SSasha Levin 	if (size != 1 || count != 1)
381c7850f9SSasha Levin 		return false;
391c7850f9SSasha Levin 
401c7850f9SSasha Levin 	ioport__write8(data, config_space[offset - VIRTIO_MSI_CONFIG_VECTOR]);
411c7850f9SSasha Levin 
421c7850f9SSasha Levin 	return true;
431c7850f9SSasha Levin }
441c7850f9SSasha Levin 
45b4422bf3SAneesh Kumar K.V static bool virtio_p9_pci_io_in(struct ioport *ioport, struct kvm *kvm,
46b4422bf3SAneesh Kumar K.V 				u16 port, void *data, int size, u32 count)
471c7850f9SSasha Levin {
481c7850f9SSasha Levin 	bool ret = true;
49b4422bf3SAneesh Kumar K.V 	unsigned long offset;
50b4422bf3SAneesh Kumar K.V 	struct p9_dev *p9dev = ioport->priv;
511c7850f9SSasha Levin 
52b4422bf3SAneesh Kumar K.V 
53b4422bf3SAneesh Kumar K.V 	offset = port - p9dev->base_addr;
541c7850f9SSasha Levin 
551c7850f9SSasha Levin 	switch (offset) {
561c7850f9SSasha Levin 	case VIRTIO_PCI_HOST_FEATURES:
57b4422bf3SAneesh Kumar K.V 		ioport__write32(data, p9dev->features);
581c7850f9SSasha Levin 		ret = true;
591c7850f9SSasha Levin 		break;
601c7850f9SSasha Levin 	case VIRTIO_PCI_GUEST_FEATURES:
611c7850f9SSasha Levin 	case VIRTIO_PCI_QUEUE_SEL:
621c7850f9SSasha Levin 	case VIRTIO_PCI_QUEUE_NOTIFY:
631c7850f9SSasha Levin 		ret = false;
641c7850f9SSasha Levin 		break;
651c7850f9SSasha Levin 	case VIRTIO_PCI_QUEUE_PFN:
66b4422bf3SAneesh Kumar K.V 		ioport__write32(data, p9dev->vqs[p9dev->queue_selector].pfn);
671c7850f9SSasha Levin 		break;
681c7850f9SSasha Levin 	case VIRTIO_PCI_QUEUE_NUM:
69612038c5SAneesh Kumar K.V 		ioport__write16(data, VIRTQUEUE_NUM);
701c7850f9SSasha Levin 		break;
711c7850f9SSasha Levin 	case VIRTIO_PCI_STATUS:
72b4422bf3SAneesh Kumar K.V 		ioport__write8(data, p9dev->status);
731c7850f9SSasha Levin 		break;
741c7850f9SSasha Levin 	case VIRTIO_PCI_ISR:
75b4422bf3SAneesh Kumar K.V 		ioport__write8(data, p9dev->isr);
76b4422bf3SAneesh Kumar K.V 		kvm__irq_line(kvm, p9dev->pci_hdr.irq_line, VIRTIO_IRQ_LOW);
77b4422bf3SAneesh Kumar K.V 		p9dev->isr = VIRTIO_IRQ_LOW;
781c7850f9SSasha Levin 		break;
791c7850f9SSasha Levin 	default:
80b4422bf3SAneesh Kumar K.V 		ret = virtio_p9_dev_in(p9dev, data, offset, size, count);
811c7850f9SSasha Levin 		break;
821c7850f9SSasha Levin 	};
831c7850f9SSasha Levin 
841c7850f9SSasha Levin 	return ret;
851c7850f9SSasha Levin }
861c7850f9SSasha Levin 
871c7850f9SSasha Levin static int omode2uflags(u8 mode)
881c7850f9SSasha Levin {
891c7850f9SSasha Levin 	int ret = 0;
901c7850f9SSasha Levin 
911c7850f9SSasha Levin 	/* Basic open modes are same as uflags */
921c7850f9SSasha Levin 	ret = mode & 3;
931c7850f9SSasha Levin 
941c7850f9SSasha Levin 	/* Everything else is different */
951c7850f9SSasha Levin 	if (mode & P9_OTRUNC)
961c7850f9SSasha Levin 		ret |= O_TRUNC;
971c7850f9SSasha Levin 
981c7850f9SSasha Levin 	if (mode & P9_OAPPEND)
991c7850f9SSasha Levin 		ret |= O_APPEND;
1001c7850f9SSasha Levin 
1011c7850f9SSasha Levin 	if (mode & P9_OEXCL)
1021c7850f9SSasha Levin 		ret |= O_EXCL;
1031c7850f9SSasha Levin 
1041c7850f9SSasha Levin 	return ret;
1051c7850f9SSasha Levin }
1061c7850f9SSasha Levin 
1071c7850f9SSasha Levin static void st2qid(struct stat *st, struct p9_qid *qid)
1081c7850f9SSasha Levin {
1091c7850f9SSasha Levin 	*qid = (struct p9_qid) {
1101c7850f9SSasha Levin 		.path		= st->st_ino,
1111c7850f9SSasha Levin 		.version	= st->st_mtime,
1121c7850f9SSasha Levin 	};
1131c7850f9SSasha Levin 
1141c7850f9SSasha Levin 	if (S_ISDIR(st->st_mode))
1151c7850f9SSasha Levin 		qid->type	|= P9_QTDIR;
1161c7850f9SSasha Levin }
1171c7850f9SSasha Levin 
118b4422bf3SAneesh Kumar K.V static void close_fid(struct p9_dev *p9dev, u32 fid)
1191c7850f9SSasha Levin {
120b4422bf3SAneesh Kumar K.V 	if (p9dev->fids[fid].fd > 0) {
121b4422bf3SAneesh Kumar K.V 		close(p9dev->fids[fid].fd);
122b4422bf3SAneesh Kumar K.V 		p9dev->fids[fid].fd = -1;
1231c7850f9SSasha Levin 	}
124b4422bf3SAneesh Kumar K.V 	if (p9dev->fids[fid].dir) {
125b4422bf3SAneesh Kumar K.V 		closedir(p9dev->fids[fid].dir);
126b4422bf3SAneesh Kumar K.V 		p9dev->fids[fid].dir = NULL;
1271c7850f9SSasha Levin 	}
1281c7850f9SSasha Levin }
1291c7850f9SSasha Levin 
130bfc15268SAneesh Kumar K.V static void virtio_p9_set_reply_header(struct p9_pdu *pdu, u32 size)
1311c7850f9SSasha Levin {
132bfc15268SAneesh Kumar K.V 	u8 cmd;
133bfc15268SAneesh Kumar K.V 	u16 tag;
134bfc15268SAneesh Kumar K.V 
135bfc15268SAneesh Kumar K.V 	pdu->read_offset = sizeof(u32);
136bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "bw", &cmd, &tag);
137bfc15268SAneesh Kumar K.V 	pdu->write_offset = 0;
138bfc15268SAneesh Kumar K.V 	/* cmd + 1 is the reply message */
139bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "dbw", size, cmd + 1, tag);
1401c7850f9SSasha Levin }
1411c7850f9SSasha Levin 
1426b163a87SAneesh Kumar K.V static u16 virtio_p9_update_iov_cnt(struct iovec iov[], u32 count, int iov_cnt)
1436b163a87SAneesh Kumar K.V {
1446b163a87SAneesh Kumar K.V 	int i;
1456b163a87SAneesh Kumar K.V 	u32 total = 0;
1466b163a87SAneesh Kumar K.V 	for (i = 0; (i < iov_cnt) && (total < count); i++) {
1476b163a87SAneesh Kumar K.V 		if (total + iov[i].iov_len > count) {
1486b163a87SAneesh Kumar K.V 			/* we don't need this iov fully */
1496b163a87SAneesh Kumar K.V 			iov[i].iov_len -= ((total + iov[i].iov_len) - count);
1506b163a87SAneesh Kumar K.V 			i++;
1516b163a87SAneesh Kumar K.V 			break;
1526b163a87SAneesh Kumar K.V 		}
1536b163a87SAneesh Kumar K.V 		total += iov[i].iov_len;
1546b163a87SAneesh Kumar K.V 	}
1556b163a87SAneesh Kumar K.V 	return i;
1566b163a87SAneesh Kumar K.V }
1576b163a87SAneesh Kumar K.V 
158eee1ba8eSAneesh Kumar K.V static void virtio_p9_error_reply(struct p9_dev *p9dev,
159eee1ba8eSAneesh Kumar K.V 				  struct p9_pdu *pdu, int err, u32 *outlen)
160eee1ba8eSAneesh Kumar K.V {
161bfc15268SAneesh Kumar K.V 	u16 tag;
162eee1ba8eSAneesh Kumar K.V 	char *err_str;
163eee1ba8eSAneesh Kumar K.V 
164eee1ba8eSAneesh Kumar K.V 	err_str = strerror(err);
165bfc15268SAneesh Kumar K.V 	pdu->write_offset = VIRTIO_P9_HDR_LEN;
166bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "s", err_str);
167bfc15268SAneesh Kumar K.V 	*outlen = pdu->write_offset;
168eee1ba8eSAneesh Kumar K.V 
169bfc15268SAneesh Kumar K.V 	pdu->read_offset = sizeof(u32) + sizeof(u8);
170bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "w", &tag);
171bfc15268SAneesh Kumar K.V 
172bfc15268SAneesh Kumar K.V 	pdu->write_offset = 0;
173bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "dbw", *outlen, P9_RERROR, tag);
174eee1ba8eSAneesh Kumar K.V }
175eee1ba8eSAneesh Kumar K.V 
176ead43b01SAneesh Kumar K.V static void virtio_p9_version(struct p9_dev *p9dev,
177af045e53SAneesh Kumar K.V 			      struct p9_pdu *pdu, u32 *outlen)
1781c7850f9SSasha Levin {
179bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "ds", 4096, VIRTIO_P9_VERSION);
1801c7850f9SSasha Levin 
181bfc15268SAneesh Kumar K.V 	*outlen = pdu->write_offset;
182bfc15268SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
183ead43b01SAneesh Kumar K.V 	return;
1841c7850f9SSasha Levin }
1851c7850f9SSasha Levin 
186ead43b01SAneesh Kumar K.V static void virtio_p9_clunk(struct p9_dev *p9dev,
187af045e53SAneesh Kumar K.V 			    struct p9_pdu *pdu, u32 *outlen)
1881c7850f9SSasha Levin {
189bfc15268SAneesh Kumar K.V 	u32 fid;
1901c7850f9SSasha Levin 
191bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "d", &fid);
192bfc15268SAneesh Kumar K.V 	close_fid(p9dev, fid);
1931c7850f9SSasha Levin 
194bfc15268SAneesh Kumar K.V 	*outlen = pdu->write_offset;
195bfc15268SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
196ead43b01SAneesh Kumar K.V 	return;
1971c7850f9SSasha Levin }
1981c7850f9SSasha Levin 
199ead43b01SAneesh Kumar K.V static void virtio_p9_open(struct p9_dev *p9dev,
200af045e53SAneesh Kumar K.V 			   struct p9_pdu *pdu, u32 *outlen)
2011c7850f9SSasha Levin {
202bfc15268SAneesh Kumar K.V 	u8 mode;
203bfc15268SAneesh Kumar K.V 	u32 fid;
2041c7850f9SSasha Levin 	struct stat st;
205bfc15268SAneesh Kumar K.V 	struct p9_qid qid;
206bfc15268SAneesh Kumar K.V 	struct p9_fid *new_fid;
207bfc15268SAneesh Kumar K.V 
208bfc15268SAneesh Kumar K.V 
209bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "db", &fid, &mode);
210bfc15268SAneesh Kumar K.V 	new_fid = &p9dev->fids[fid];
2111c7850f9SSasha Levin 
21230204a77SAneesh Kumar K.V 	if (lstat(new_fid->abs_path, &st) < 0)
213eee1ba8eSAneesh Kumar K.V 		goto err_out;
2141c7850f9SSasha Levin 
215bfc15268SAneesh Kumar K.V 	st2qid(&st, &qid);
2161c7850f9SSasha Levin 
217eee1ba8eSAneesh Kumar K.V 	if (new_fid->is_dir) {
2181c7850f9SSasha Levin 		new_fid->dir = opendir(new_fid->abs_path);
219eee1ba8eSAneesh Kumar K.V 		if (!new_fid->dir)
220eee1ba8eSAneesh Kumar K.V 			goto err_out;
221eee1ba8eSAneesh Kumar K.V 	} else {
222eee1ba8eSAneesh Kumar K.V 		new_fid->fd  = open(new_fid->abs_path,
223bfc15268SAneesh Kumar K.V 				    omode2uflags(mode) | O_NOFOLLOW);
224eee1ba8eSAneesh Kumar K.V 		if (new_fid->fd < 0)
225eee1ba8eSAneesh Kumar K.V 			goto err_out;
226eee1ba8eSAneesh Kumar K.V 	}
227bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "Qd", &qid, 0);
228bfc15268SAneesh Kumar K.V 
229bfc15268SAneesh Kumar K.V 	*outlen = pdu->write_offset;
230bfc15268SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
231ead43b01SAneesh Kumar K.V 	return;
232eee1ba8eSAneesh Kumar K.V err_out:
233eee1ba8eSAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
234ead43b01SAneesh Kumar K.V 	return;
2351c7850f9SSasha Levin }
2361c7850f9SSasha Levin 
237ead43b01SAneesh Kumar K.V static void virtio_p9_create(struct p9_dev *p9dev,
238af045e53SAneesh Kumar K.V 			     struct p9_pdu *pdu, u32 *outlen)
2391c7850f9SSasha Levin {
2401c7850f9SSasha Levin 	u8 mode;
2411c7850f9SSasha Levin 	u32 perm;
242bfc15268SAneesh Kumar K.V 	char *name;
243bfc15268SAneesh Kumar K.V 	u32 fid_val;
244af045e53SAneesh Kumar K.V 	struct stat st;
245bfc15268SAneesh Kumar K.V 	struct p9_qid qid;
246bfc15268SAneesh Kumar K.V 	struct p9_fid *fid;
247af045e53SAneesh Kumar K.V 
248bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "dsdb", &fid_val, &name, &perm, &mode);
249bfc15268SAneesh Kumar K.V 	fid = &p9dev->fids[fid_val];
2501c7850f9SSasha Levin 
251bfc15268SAneesh Kumar K.V 	sprintf(fid->path, "%s/%.*s", fid->path, (int)strlen(name), name);
252bfc15268SAneesh Kumar K.V 	close_fid(p9dev, fid_val);
2531c7850f9SSasha Levin 
2541c7850f9SSasha Levin 	if (perm & P9_DMDIR) {
2551c7850f9SSasha Levin 		mkdir(fid->abs_path, perm & 0xFFFF);
2561c7850f9SSasha Levin 		fid->dir = opendir(fid->abs_path);
2571c7850f9SSasha Levin 		fid->is_dir = 1;
2581c7850f9SSasha Levin 	} else {
259bfc15268SAneesh Kumar K.V 		fid->fd = open(fid->abs_path,
260bfc15268SAneesh Kumar K.V 			       omode2uflags(mode) | O_CREAT, 0777);
2611c7850f9SSasha Levin 	}
26230204a77SAneesh Kumar K.V 	if (lstat(fid->abs_path, &st) < 0)
2636c8ca053SAneesh Kumar K.V 		goto err_out;
2641c7850f9SSasha Levin 
265bfc15268SAneesh Kumar K.V 	st2qid(&st, &qid);
266bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "Qd", &qid, 0);
267bfc15268SAneesh Kumar K.V 	*outlen = pdu->write_offset;
268bfc15268SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
2696c8ca053SAneesh Kumar K.V 	return;
2706c8ca053SAneesh Kumar K.V err_out:
2716c8ca053SAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
272ead43b01SAneesh Kumar K.V 	return;
2731c7850f9SSasha Levin }
2741c7850f9SSasha Levin 
275ead43b01SAneesh Kumar K.V static void virtio_p9_walk(struct p9_dev *p9dev,
276af045e53SAneesh Kumar K.V 			   struct p9_pdu *pdu, u32 *outlen)
2771c7850f9SSasha Levin {
278af045e53SAneesh Kumar K.V 	u8 i;
279bfc15268SAneesh Kumar K.V 	u16 nwqid;
280bfc15268SAneesh Kumar K.V 	char *str;
281bfc15268SAneesh Kumar K.V 	u16 nwname;
282bfc15268SAneesh Kumar K.V 	u32 fid_val;
283bfc15268SAneesh Kumar K.V 	u32 newfid_val;
284bfc15268SAneesh Kumar K.V 	struct p9_qid wqid;
285bfc15268SAneesh Kumar K.V 	struct p9_fid *new_fid;
286af045e53SAneesh Kumar K.V 
2871c7850f9SSasha Levin 
288bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "ddw", &fid_val, &newfid_val, &nwname);
289bfc15268SAneesh Kumar K.V 	new_fid	= &p9dev->fids[newfid_val];
2901c7850f9SSasha Levin 
291bfc15268SAneesh Kumar K.V 	nwqid = 0;
292bfc15268SAneesh Kumar K.V 	if (nwname) {
293bfc15268SAneesh Kumar K.V 		struct p9_fid *fid = &p9dev->fids[fid_val];
294bfc15268SAneesh Kumar K.V 
295bfc15268SAneesh Kumar K.V 		/* skip the space for count */
296bfc15268SAneesh Kumar K.V 		pdu->write_offset += sizeof(u16);
297bfc15268SAneesh Kumar K.V 		for (i = 0; i < nwname; i++) {
298bfc15268SAneesh Kumar K.V 			struct stat st;
2991c7850f9SSasha Levin 			char tmp[PATH_MAX] = {0};
3001c7850f9SSasha Levin 			char full_path[PATH_MAX];
301bfc15268SAneesh Kumar K.V 
302bfc15268SAneesh Kumar K.V 			virtio_p9_pdu_readf(pdu, "s", &str);
3031c7850f9SSasha Levin 
3041c7850f9SSasha Levin 			/* Format the new path we're 'walk'ing into */
305bfc15268SAneesh Kumar K.V 			sprintf(tmp, "%s/%.*s",
306bfc15268SAneesh Kumar K.V 				fid->path, (int)strlen(str), str);
30730204a77SAneesh Kumar K.V 			if (lstat(rel_to_abs(p9dev, tmp, full_path), &st) < 0)
3086c8ca053SAneesh Kumar K.V 				goto err_out;
3091c7850f9SSasha Levin 
310bfc15268SAneesh Kumar K.V 			st2qid(&st, &wqid);
3111c7850f9SSasha Levin 			new_fid->is_dir = S_ISDIR(st.st_mode);
3121c7850f9SSasha Levin 			strcpy(new_fid->path, tmp);
313bfc15268SAneesh Kumar K.V 			new_fid->fid = newfid_val;
314bfc15268SAneesh Kumar K.V 			nwqid++;
315bfc15268SAneesh Kumar K.V 			virtio_p9_pdu_writef(pdu, "Q", &wqid);
3161c7850f9SSasha Levin 		}
3171c7850f9SSasha Levin 	} else {
318bfc15268SAneesh Kumar K.V 		/*
319bfc15268SAneesh Kumar K.V 		 * update write_offset so our outlen get correct value
320bfc15268SAneesh Kumar K.V 		 */
321bfc15268SAneesh Kumar K.V 		pdu->write_offset += sizeof(u16);
322bfc15268SAneesh Kumar K.V 		new_fid->is_dir = p9dev->fids[fid_val].is_dir;
323bfc15268SAneesh Kumar K.V 		strcpy(new_fid->path, p9dev->fids[fid_val].path);
324bfc15268SAneesh Kumar K.V 		new_fid->fid	= newfid_val;
3251c7850f9SSasha Levin 	}
326bfc15268SAneesh Kumar K.V 	*outlen = pdu->write_offset;
327bfc15268SAneesh Kumar K.V 	pdu->write_offset = VIRTIO_P9_HDR_LEN;
328bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "d", nwqid);
329bfc15268SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
3306c8ca053SAneesh Kumar K.V 	return;
3316c8ca053SAneesh Kumar K.V err_out:
3326c8ca053SAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
333ead43b01SAneesh Kumar K.V 	return;
3341c7850f9SSasha Levin }
3351c7850f9SSasha Levin 
336ead43b01SAneesh Kumar K.V static void virtio_p9_attach(struct p9_dev *p9dev,
337af045e53SAneesh Kumar K.V 			     struct p9_pdu *pdu, u32 *outlen)
3381c7850f9SSasha Levin {
339af045e53SAneesh Kumar K.V 	u32 i;
340bfc15268SAneesh Kumar K.V 	u32 fid_val;
341bfc15268SAneesh Kumar K.V 	u32 afid;
342bfc15268SAneesh Kumar K.V 	char *uname;
343bfc15268SAneesh Kumar K.V 	char *aname;
3441c7850f9SSasha Levin 	struct stat st;
345bfc15268SAneesh Kumar K.V 	struct p9_qid qid;
3461c7850f9SSasha Levin 	struct p9_fid *fid;
347bfc15268SAneesh Kumar K.V 
348bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "ddss", &fid_val, &afid, &uname, &aname);
3491c7850f9SSasha Levin 
3501c7850f9SSasha Levin 	/* Reset everything */
3511c7850f9SSasha Levin 	for (i = 0; i < VIRTIO_P9_MAX_FID; i++)
352b4422bf3SAneesh Kumar K.V 		p9dev->fids[i].fid = P9_NOFID;
3531c7850f9SSasha Levin 
35430204a77SAneesh Kumar K.V 	if (lstat(p9dev->root_dir, &st) < 0)
3556c8ca053SAneesh Kumar K.V 		goto err_out;
3561c7850f9SSasha Levin 
357bfc15268SAneesh Kumar K.V 	st2qid(&st, &qid);
3581c7850f9SSasha Levin 
359bfc15268SAneesh Kumar K.V 	fid = &p9dev->fids[fid_val];
360bfc15268SAneesh Kumar K.V 	fid->fid = fid_val;
3611c7850f9SSasha Levin 	fid->is_dir = 1;
3621c7850f9SSasha Levin 	strcpy(fid->path, "/");
3631c7850f9SSasha Levin 
364bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "Q", &qid);
365bfc15268SAneesh Kumar K.V 	*outlen = pdu->write_offset;
366bfc15268SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
3676c8ca053SAneesh Kumar K.V 	return;
3686c8ca053SAneesh Kumar K.V err_out:
3696c8ca053SAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
370ead43b01SAneesh Kumar K.V 	return;
3711c7850f9SSasha Levin }
3721c7850f9SSasha Levin 
373bfc15268SAneesh Kumar K.V static void virtio_p9_fill_stat(struct p9_dev *p9dev, const char *name,
374bfc15268SAneesh Kumar K.V 				struct stat *st, struct p9_wstat *wstat)
3751c7850f9SSasha Levin {
376bfc15268SAneesh Kumar K.V 	wstat->type = 0;
377bfc15268SAneesh Kumar K.V 	wstat->dev = 0;
378bfc15268SAneesh Kumar K.V 	st2qid(st, &wstat->qid);
379bfc15268SAneesh Kumar K.V 	wstat->mode = st->st_mode;
380bfc15268SAneesh Kumar K.V 	wstat->length = st->st_size;
3811c7850f9SSasha Levin 	if (S_ISDIR(st->st_mode)) {
382bfc15268SAneesh Kumar K.V 		wstat->length = 0;
383bfc15268SAneesh Kumar K.V 		wstat->mode |= P9_DMDIR;
3841c7850f9SSasha Levin 	}
3851c7850f9SSasha Levin 
386bfc15268SAneesh Kumar K.V 	wstat->atime = st->st_atime;
387bfc15268SAneesh Kumar K.V 	wstat->mtime = st->st_mtime;
3881c7850f9SSasha Levin 
389bfc15268SAneesh Kumar K.V 	wstat->name = strdup(name);
390bfc15268SAneesh Kumar K.V 	wstat->uid = NULL;
391bfc15268SAneesh Kumar K.V 	wstat->gid = NULL;
392bfc15268SAneesh Kumar K.V 	wstat->muid = NULL;
3931c7850f9SSasha Levin 
394bfc15268SAneesh Kumar K.V 	/* NOTE: size shouldn't include its own length */
395bfc15268SAneesh Kumar K.V 	/* size[2] type[2] dev[4] qid[13] */
396bfc15268SAneesh Kumar K.V 	/* mode[4] atime[4] mtime[4] length[8]*/
397bfc15268SAneesh Kumar K.V 	/* name[s] uid[s] gid[s] muid[s] */
398bfc15268SAneesh Kumar K.V 	wstat->size = 2+4+13+4+4+4+8+2+2+2+2;
399bfc15268SAneesh Kumar K.V 	if (wstat->name)
400bfc15268SAneesh Kumar K.V 		wstat->size += strlen(wstat->name);
4011c7850f9SSasha Levin }
4021c7850f9SSasha Levin 
403ead43b01SAneesh Kumar K.V static void virtio_p9_read(struct p9_dev *p9dev,
404af045e53SAneesh Kumar K.V 			   struct p9_pdu *pdu, u32 *outlen)
4051c7850f9SSasha Levin {
406bfc15268SAneesh Kumar K.V 	u64 offset;
407bfc15268SAneesh Kumar K.V 	u32 fid_val;
408bfc15268SAneesh Kumar K.V 	u32 count, rcount;
4091c7850f9SSasha Levin 	struct stat st;
410bfc15268SAneesh Kumar K.V 	struct p9_fid *fid;
411bfc15268SAneesh Kumar K.V 	struct p9_wstat wstat;
4121c7850f9SSasha Levin 
413bfc15268SAneesh Kumar K.V 	rcount = 0;
414bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "dqd", &fid_val, &offset, &count);
415bfc15268SAneesh Kumar K.V 	fid = &p9dev->fids[fid_val];
4161c7850f9SSasha Levin 	if (fid->is_dir) {
4171c7850f9SSasha Levin 		/* If reading a dir, fill the buffer with p9_stat entries */
4181c7850f9SSasha Levin 		char full_path[PATH_MAX];
419bfc15268SAneesh Kumar K.V 		struct dirent *cur = readdir(fid->dir);
4201c7850f9SSasha Levin 
421bfc15268SAneesh Kumar K.V 		/* Skip the space for writing count */
422bfc15268SAneesh Kumar K.V 		pdu->write_offset += sizeof(u32);
4231c7850f9SSasha Levin 		while (cur) {
4241c7850f9SSasha Levin 			u32 read;
4251c7850f9SSasha Levin 
42630204a77SAneesh Kumar K.V 			lstat(rel_to_abs(p9dev, cur->d_name, full_path), &st);
427bfc15268SAneesh Kumar K.V 			virtio_p9_fill_stat(p9dev, cur->d_name, &st, &wstat);
428bfc15268SAneesh Kumar K.V 
429bfc15268SAneesh Kumar K.V 			read = pdu->write_offset;
430bfc15268SAneesh Kumar K.V 			virtio_p9_pdu_writef(pdu, "S", &wstat);
431bfc15268SAneesh Kumar K.V 			rcount += pdu->write_offset - read;
432bfc15268SAneesh Kumar K.V 
4331c7850f9SSasha Levin 			cur = readdir(fid->dir);
4341c7850f9SSasha Levin 		}
4351c7850f9SSasha Levin 	} else {
436af045e53SAneesh Kumar K.V 		pdu->in_iov[0].iov_base += VIRTIO_P9_HDR_LEN + sizeof(u32);
437af045e53SAneesh Kumar K.V 		pdu->in_iov[0].iov_len -= VIRTIO_P9_HDR_LEN + sizeof(u32);
4386b163a87SAneesh Kumar K.V 		pdu->in_iov_cnt = virtio_p9_update_iov_cnt(pdu->in_iov,
439bfc15268SAneesh Kumar K.V 							   count,
4406b163a87SAneesh Kumar K.V 							   pdu->in_iov_cnt);
441bfc15268SAneesh Kumar K.V 		rcount = preadv(fid->fd, pdu->in_iov,
442bfc15268SAneesh Kumar K.V 				pdu->in_iov_cnt, offset);
443bfc15268SAneesh Kumar K.V 		if (rcount > count)
444bfc15268SAneesh Kumar K.V 			rcount = count;
445bfc15268SAneesh Kumar K.V 		/*
446bfc15268SAneesh Kumar K.V 		 * Update the iov_base back, so that rest of
447bfc15268SAneesh Kumar K.V 		 * pdu_writef works correctly.
448bfc15268SAneesh Kumar K.V 		 */
449bfc15268SAneesh Kumar K.V 		pdu->in_iov[0].iov_base -= VIRTIO_P9_HDR_LEN + sizeof(u32);
450bfc15268SAneesh Kumar K.V 		pdu->in_iov[0].iov_len += VIRTIO_P9_HDR_LEN + sizeof(u32);
4511c7850f9SSasha Levin 
452bfc15268SAneesh Kumar K.V 	}
453bfc15268SAneesh Kumar K.V 	pdu->write_offset = VIRTIO_P9_HDR_LEN;
454bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "d", rcount);
455bfc15268SAneesh Kumar K.V 	*outlen = pdu->write_offset + rcount;
456bfc15268SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
4571c7850f9SSasha Levin 
458ead43b01SAneesh Kumar K.V 	return;
4591c7850f9SSasha Levin }
4601c7850f9SSasha Levin 
461ead43b01SAneesh Kumar K.V static void virtio_p9_stat(struct p9_dev *p9dev,
462af045e53SAneesh Kumar K.V 			   struct p9_pdu *pdu, u32 *outlen)
4631c7850f9SSasha Levin {
464bfc15268SAneesh Kumar K.V 	u32 fid_val;
465af045e53SAneesh Kumar K.V 	struct stat st;
466bfc15268SAneesh Kumar K.V 	struct p9_fid *fid;
467bfc15268SAneesh Kumar K.V 	struct p9_wstat wstat;
4681c7850f9SSasha Levin 
469bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "d", &fid_val);
470bfc15268SAneesh Kumar K.V 	fid = &p9dev->fids[fid_val];
47130204a77SAneesh Kumar K.V 	if (lstat(fid->abs_path, &st) < 0)
4726c8ca053SAneesh Kumar K.V 		goto err_out;
4731c7850f9SSasha Levin 
474bfc15268SAneesh Kumar K.V 	virtio_p9_fill_stat(p9dev, fid->path, &st, &wstat);
4751c7850f9SSasha Levin 
476bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "wS", 0, &wstat);
477bfc15268SAneesh Kumar K.V 	*outlen = pdu->write_offset;
478bfc15268SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
479ead43b01SAneesh Kumar K.V 	return;
4806c8ca053SAneesh Kumar K.V err_out:
4816c8ca053SAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
4826c8ca053SAneesh Kumar K.V 	return;
4831c7850f9SSasha Levin }
4841c7850f9SSasha Levin 
485ead43b01SAneesh Kumar K.V static void virtio_p9_wstat(struct p9_dev *p9dev,
486af045e53SAneesh Kumar K.V 			    struct p9_pdu *pdu, u32 *outlen)
4871c7850f9SSasha Levin {
488aec426f0SCyrill Gorcunov 	int res = 0;
489bfc15268SAneesh Kumar K.V 	u32 fid_val;
490bfc15268SAneesh Kumar K.V 	u16 unused;
491bfc15268SAneesh Kumar K.V 	struct p9_fid *fid;
492bfc15268SAneesh Kumar K.V 	struct p9_wstat wstat;
493af045e53SAneesh Kumar K.V 
494bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "dwS", &fid_val, &unused, &wstat);
495bfc15268SAneesh Kumar K.V 	fid = &p9dev->fids[fid_val];
4961c7850f9SSasha Levin 
497bfc15268SAneesh Kumar K.V 	if (wstat.length != -1UL)
498bfc15268SAneesh Kumar K.V 		res = ftruncate(fid->fd, wstat.length);
4991c7850f9SSasha Levin 
500bfc15268SAneesh Kumar K.V 	if (wstat.mode != -1U)
501bfc15268SAneesh Kumar K.V 		chmod(fid->abs_path, wstat.mode & 0xFFFF);
5021c7850f9SSasha Levin 
503bfc15268SAneesh Kumar K.V 	if (strlen(wstat.name) > 0) {
5041c7850f9SSasha Levin 		char new_name[PATH_MAX] = {0};
5051c7850f9SSasha Levin 		char full_path[PATH_MAX];
5061c7850f9SSasha Levin 		char *last_dir = strrchr(fid->path, '/');
5071c7850f9SSasha Levin 
5081c7850f9SSasha Levin 		/* We need to get the full file name out of twstat->name */
5091c7850f9SSasha Levin 		if (last_dir)
5101c7850f9SSasha Levin 			strncpy(new_name, fid->path, last_dir - fid->path + 1);
5111c7850f9SSasha Levin 
512bfc15268SAneesh Kumar K.V 		memcpy(new_name + strlen(new_name),
513bfc15268SAneesh Kumar K.V 		       wstat.name, strlen(wstat.name));
5141c7850f9SSasha Levin 
5151c7850f9SSasha Levin 		/* fid is reused for the new file */
516b4422bf3SAneesh Kumar K.V 		rename(fid->abs_path, rel_to_abs(p9dev, new_name, full_path));
5171c7850f9SSasha Levin 		sprintf(fid->path, "%s", new_name);
5181c7850f9SSasha Levin 	}
5191c7850f9SSasha Levin 	*outlen = VIRTIO_P9_HDR_LEN;
520bfc15268SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
521ead43b01SAneesh Kumar K.V 	return;
5221c7850f9SSasha Levin }
5231c7850f9SSasha Levin 
524ead43b01SAneesh Kumar K.V static void virtio_p9_remove(struct p9_dev *p9dev,
525af045e53SAneesh Kumar K.V 			     struct p9_pdu *pdu, u32 *outlen)
5261c7850f9SSasha Levin {
527bfc15268SAneesh Kumar K.V 	u32 fid_val;
528bfc15268SAneesh Kumar K.V 	struct p9_fid *fid;
5291c7850f9SSasha Levin 
530bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "d", &fid_val);
531bfc15268SAneesh Kumar K.V 	fid = &p9dev->fids[fid_val];
532bfc15268SAneesh Kumar K.V 	close_fid(p9dev, fid_val);
5331c7850f9SSasha Levin 	if (fid->is_dir)
5341c7850f9SSasha Levin 		rmdir(fid->abs_path);
5351c7850f9SSasha Levin 	else
5361c7850f9SSasha Levin 		unlink(fid->abs_path);
5371c7850f9SSasha Levin 
5381c7850f9SSasha Levin 	*outlen = VIRTIO_P9_HDR_LEN;
539bfc15268SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
540ead43b01SAneesh Kumar K.V 	return;
5411c7850f9SSasha Levin }
5421c7850f9SSasha Levin 
543ead43b01SAneesh Kumar K.V static void virtio_p9_write(struct p9_dev *p9dev,
544af045e53SAneesh Kumar K.V 			    struct p9_pdu *pdu, u32 *outlen)
5451c7850f9SSasha Levin {
546bfc15268SAneesh Kumar K.V 	u64 offset;
547bfc15268SAneesh Kumar K.V 	u32 fid_val;
548bfc15268SAneesh Kumar K.V 	u32 count, rcount;
549bfc15268SAneesh Kumar K.V 	struct p9_fid *fid;
5501c7850f9SSasha Levin 
551bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "dqd", &fid_val, &offset, &count);
552bfc15268SAneesh Kumar K.V 	fid = &p9dev->fids[fid_val];
553af045e53SAneesh Kumar K.V 
554bfc15268SAneesh Kumar K.V 	/* Adjust the iovec to skip the header and meta data */
555bfc15268SAneesh Kumar K.V 	pdu->out_iov[0].iov_base += (sizeof(struct p9_msg) +
556bfc15268SAneesh Kumar K.V 				     sizeof(struct p9_twrite));
557bfc15268SAneesh Kumar K.V 	pdu->out_iov[0].iov_len -=  (sizeof(struct p9_msg) +
558bfc15268SAneesh Kumar K.V 				     sizeof(struct p9_twrite));
559bfc15268SAneesh Kumar K.V 	pdu->out_iov_cnt = virtio_p9_update_iov_cnt(pdu->out_iov, count,
5606b163a87SAneesh Kumar K.V 						    pdu->out_iov_cnt);
561bfc15268SAneesh Kumar K.V 	rcount = pwritev(fid->fd, pdu->out_iov, pdu->out_iov_cnt, offset);
562bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "d", rcount);
563bfc15268SAneesh Kumar K.V 	*outlen = pdu->write_offset;
564bfc15268SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
565ead43b01SAneesh Kumar K.V 	return;
5661c7850f9SSasha Levin }
5671c7850f9SSasha Levin 
568ead43b01SAneesh Kumar K.V typedef void p9_handler(struct p9_dev *p9dev,
569af045e53SAneesh Kumar K.V 			struct p9_pdu *pdu, u32 *outlen);
570b4422bf3SAneesh Kumar K.V 
571b4422bf3SAneesh Kumar K.V static p9_handler *virtio_9p_handler [] = {
572b4422bf3SAneesh Kumar K.V 	[P9_TVERSION] = virtio_p9_version,
573b4422bf3SAneesh Kumar K.V 	[P9_TATTACH]  = virtio_p9_attach,
574b4422bf3SAneesh Kumar K.V 	[P9_TSTAT]    = virtio_p9_stat,
575b4422bf3SAneesh Kumar K.V 	[P9_TCLUNK]   =	virtio_p9_clunk,
576b4422bf3SAneesh Kumar K.V 	[P9_TWALK]    =	virtio_p9_walk,
577b4422bf3SAneesh Kumar K.V 	[P9_TOPEN]    =	virtio_p9_open,
578b4422bf3SAneesh Kumar K.V 	[P9_TREAD]    = virtio_p9_read,
579b4422bf3SAneesh Kumar K.V 	[P9_TCREATE]  =	virtio_p9_create,
580b4422bf3SAneesh Kumar K.V 	[P9_TWSTAT]   =	virtio_p9_wstat,
581b4422bf3SAneesh Kumar K.V 	[P9_TREMOVE]  =	virtio_p9_remove,
582b4422bf3SAneesh Kumar K.V 	[P9_TWRITE]   =	virtio_p9_write,
583b4422bf3SAneesh Kumar K.V };
584b4422bf3SAneesh Kumar K.V 
585af045e53SAneesh Kumar K.V static struct p9_pdu *virtio_p9_pdu_init(struct kvm *kvm, struct virt_queue *vq)
586af045e53SAneesh Kumar K.V {
587af045e53SAneesh Kumar K.V 	struct p9_pdu *pdu = calloc(1, sizeof(*pdu));
588af045e53SAneesh Kumar K.V 	if (!pdu)
589af045e53SAneesh Kumar K.V 		return NULL;
590af045e53SAneesh Kumar K.V 
591bfc15268SAneesh Kumar K.V 	/* skip the pdu header p9_msg */
592bfc15268SAneesh Kumar K.V 	pdu->read_offset  = VIRTIO_P9_HDR_LEN;
593bfc15268SAneesh Kumar K.V 	pdu->write_offset = VIRTIO_P9_HDR_LEN;
594af045e53SAneesh Kumar K.V 	pdu->queue_head  = virt_queue__get_inout_iov(kvm, vq, pdu->in_iov,
595af045e53SAneesh Kumar K.V 						     pdu->out_iov,
596af045e53SAneesh Kumar K.V 						     &pdu->in_iov_cnt,
597af045e53SAneesh Kumar K.V 						     &pdu->out_iov_cnt);
598af045e53SAneesh Kumar K.V 	return pdu;
599af045e53SAneesh Kumar K.V }
600af045e53SAneesh Kumar K.V 
601af045e53SAneesh Kumar K.V static u8 virtio_p9_get_cmd(struct p9_pdu *pdu)
602af045e53SAneesh Kumar K.V {
603af045e53SAneesh Kumar K.V 	struct p9_msg *msg;
604af045e53SAneesh Kumar K.V 	/*
605af045e53SAneesh Kumar K.V 	 * we can peek directly into pdu for a u8
606af045e53SAneesh Kumar K.V 	 * value. The host endianess won't be an issue
607af045e53SAneesh Kumar K.V 	 */
608af045e53SAneesh Kumar K.V 	msg = pdu->out_iov[0].iov_base;
609af045e53SAneesh Kumar K.V 	return msg->cmd;
610af045e53SAneesh Kumar K.V }
611af045e53SAneesh Kumar K.V 
612*97b408afSAneesh Kumar K.V static void virtio_p9_eopnotsupp(struct p9_dev *p9dev,
613*97b408afSAneesh Kumar K.V 				 struct p9_pdu *pdu, u32 *outlen)
614*97b408afSAneesh Kumar K.V {
615*97b408afSAneesh Kumar K.V 	return virtio_p9_error_reply(p9dev, pdu, EOPNOTSUPP, outlen);
616*97b408afSAneesh Kumar K.V }
617*97b408afSAneesh Kumar K.V 
618b4422bf3SAneesh Kumar K.V static bool virtio_p9_do_io_request(struct kvm *kvm, struct p9_dev_job *job)
6191c7850f9SSasha Levin {
620af045e53SAneesh Kumar K.V 	u8 cmd;
621b4422bf3SAneesh Kumar K.V 	u32 len = 0;
622b4422bf3SAneesh Kumar K.V 	p9_handler *handler;
623b4422bf3SAneesh Kumar K.V 	struct p9_dev *p9dev;
624af045e53SAneesh Kumar K.V 	struct virt_queue *vq;
625af045e53SAneesh Kumar K.V 	struct p9_pdu *p9pdu;
6261c7850f9SSasha Levin 
627b4422bf3SAneesh Kumar K.V 	vq = job->vq;
628b4422bf3SAneesh Kumar K.V 	p9dev = job->p9dev;
6291c7850f9SSasha Levin 
630af045e53SAneesh Kumar K.V 	p9pdu = virtio_p9_pdu_init(kvm, vq);
631af045e53SAneesh Kumar K.V 	cmd = virtio_p9_get_cmd(p9pdu);
632af045e53SAneesh Kumar K.V 
633af045e53SAneesh Kumar K.V 	if (cmd >= ARRAY_SIZE(virtio_9p_handler) ||
634af045e53SAneesh Kumar K.V 	    !virtio_9p_handler[cmd]) {
635*97b408afSAneesh Kumar K.V 		handler = virtio_p9_eopnotsupp;
636b4422bf3SAneesh Kumar K.V 	} else {
637af045e53SAneesh Kumar K.V 		handler = virtio_9p_handler[cmd];
638af045e53SAneesh Kumar K.V 		handler(p9dev, p9pdu, &len);
639b4422bf3SAneesh Kumar K.V 	}
640af045e53SAneesh Kumar K.V 	virt_queue__set_used_elem(vq, p9pdu->queue_head, len);
641af045e53SAneesh Kumar K.V 	free(p9pdu);
6421c7850f9SSasha Levin 	return true;
6431c7850f9SSasha Levin }
6441c7850f9SSasha Levin 
6451c7850f9SSasha Levin static void virtio_p9_do_io(struct kvm *kvm, void *param)
6461c7850f9SSasha Levin {
647b4422bf3SAneesh Kumar K.V 	struct p9_dev_job *job = (struct p9_dev_job *)param;
648b4422bf3SAneesh Kumar K.V 	struct p9_dev *p9dev   = job->p9dev;
649b4422bf3SAneesh Kumar K.V 	struct virt_queue *vq  = job->vq;
6501c7850f9SSasha Levin 
6511c7850f9SSasha Levin 	while (virt_queue__available(vq)) {
652b4422bf3SAneesh Kumar K.V 		virtio_p9_do_io_request(kvm, job);
653b4422bf3SAneesh Kumar K.V 		virt_queue__trigger_irq(vq, p9dev->pci_hdr.irq_line,
654b4422bf3SAneesh Kumar K.V 					&p9dev->isr, kvm);
6551c7850f9SSasha Levin 	}
6561c7850f9SSasha Levin }
6571c7850f9SSasha Levin 
65860eb42d5SSasha Levin static void ioevent_callback(struct kvm *kvm, void *param)
65960eb42d5SSasha Levin {
66060eb42d5SSasha Levin 	struct p9_dev_job *job = param;
66160eb42d5SSasha Levin 
66260eb42d5SSasha Levin 	thread_pool__do_job(job->job_id);
66360eb42d5SSasha Levin }
66460eb42d5SSasha Levin 
665b4422bf3SAneesh Kumar K.V static bool virtio_p9_pci_io_out(struct ioport *ioport, struct kvm *kvm,
666b4422bf3SAneesh Kumar K.V 				 u16 port, void *data, int size, u32 count)
6671c7850f9SSasha Levin {
6681c7850f9SSasha Levin 	unsigned long offset;
6691c7850f9SSasha Levin 	bool ret = true;
670b4422bf3SAneesh Kumar K.V 	struct p9_dev  *p9dev;
67160eb42d5SSasha Levin 	struct ioevent ioevent;
6721c7850f9SSasha Levin 
673b4422bf3SAneesh Kumar K.V 	p9dev = ioport->priv;
674b4422bf3SAneesh Kumar K.V 	offset = port - p9dev->base_addr;
6751c7850f9SSasha Levin 
6761c7850f9SSasha Levin 	switch (offset) {
6771c7850f9SSasha Levin 	case VIRTIO_MSI_QUEUE_VECTOR:
6781c7850f9SSasha Levin 	case VIRTIO_PCI_GUEST_FEATURES:
6791c7850f9SSasha Levin 		break;
6801c7850f9SSasha Levin 	case VIRTIO_PCI_QUEUE_PFN: {
6811c7850f9SSasha Levin 		void *p;
682b4422bf3SAneesh Kumar K.V 		struct p9_dev_job *job;
683b4422bf3SAneesh Kumar K.V 		struct virt_queue *queue;
6841c7850f9SSasha Levin 
685b4422bf3SAneesh Kumar K.V 		job			= &p9dev->jobs[p9dev->queue_selector];
686b4422bf3SAneesh Kumar K.V 		queue			= &p9dev->vqs[p9dev->queue_selector];
6871c7850f9SSasha Levin 		queue->pfn		= ioport__read32(data);
6881c7850f9SSasha Levin 		p			= guest_pfn_to_host(kvm, queue->pfn);
6891c7850f9SSasha Levin 
690612038c5SAneesh Kumar K.V 		vring_init(&queue->vring, VIRTQUEUE_NUM, p,
691b4422bf3SAneesh Kumar K.V 			   VIRTIO_PCI_VRING_ALIGN);
6921c7850f9SSasha Levin 
693b4422bf3SAneesh Kumar K.V 		*job			= (struct p9_dev_job) {
694b4422bf3SAneesh Kumar K.V 			.vq			= queue,
695b4422bf3SAneesh Kumar K.V 			.p9dev			= p9dev,
696b4422bf3SAneesh Kumar K.V 		};
697b4422bf3SAneesh Kumar K.V 		job->job_id = thread_pool__add_job(kvm, virtio_p9_do_io, job);
69860eb42d5SSasha Levin 
69960eb42d5SSasha Levin 		ioevent = (struct ioevent) {
70060eb42d5SSasha Levin 			.io_addr		= p9dev->base_addr + VIRTIO_PCI_QUEUE_NOTIFY,
70160eb42d5SSasha Levin 			.io_len			= sizeof(u16),
70260eb42d5SSasha Levin 			.fn			= ioevent_callback,
70360eb42d5SSasha Levin 			.datamatch		= p9dev->queue_selector,
70460eb42d5SSasha Levin 			.fn_ptr			= &p9dev->jobs[p9dev->queue_selector],
70560eb42d5SSasha Levin 			.fn_kvm			= kvm,
70660eb42d5SSasha Levin 			.fd			= eventfd(0, 0),
70760eb42d5SSasha Levin 		};
70860eb42d5SSasha Levin 
70960eb42d5SSasha Levin 		ioeventfd__add_event(&ioevent);
71060eb42d5SSasha Levin 
7111c7850f9SSasha Levin 		break;
7121c7850f9SSasha Levin 	}
7131c7850f9SSasha Levin 	case VIRTIO_PCI_QUEUE_SEL:
714b4422bf3SAneesh Kumar K.V 		p9dev->queue_selector	= ioport__read16(data);
7151c7850f9SSasha Levin 		break;
7161c7850f9SSasha Levin 	case VIRTIO_PCI_QUEUE_NOTIFY: {
7171c7850f9SSasha Levin 		u16 queue_index;
71860eb42d5SSasha Levin 
7191c7850f9SSasha Levin 		queue_index		= ioport__read16(data);
720b4422bf3SAneesh Kumar K.V 		thread_pool__do_job(p9dev->jobs[queue_index].job_id);
7211c7850f9SSasha Levin 		break;
7221c7850f9SSasha Levin 	}
7231c7850f9SSasha Levin 	case VIRTIO_PCI_STATUS:
724b4422bf3SAneesh Kumar K.V 		p9dev->status		= ioport__read8(data);
7251c7850f9SSasha Levin 		break;
7261c7850f9SSasha Levin 	case VIRTIO_MSI_CONFIG_VECTOR:
727b4422bf3SAneesh Kumar K.V 		p9dev->config_vector	= VIRTIO_MSI_NO_VECTOR;
7281c7850f9SSasha Levin 		break;
7291c7850f9SSasha Levin 	default:
7301c7850f9SSasha Levin 		ret			= false;
7311c7850f9SSasha Levin 		break;
7321c7850f9SSasha Levin 	};
7331c7850f9SSasha Levin 
7341c7850f9SSasha Levin 	return ret;
7351c7850f9SSasha Levin }
7361c7850f9SSasha Levin 
7371c7850f9SSasha Levin static struct ioport_operations virtio_p9_io_ops = {
7381c7850f9SSasha Levin 	.io_in				= virtio_p9_pci_io_in,
7391c7850f9SSasha Levin 	.io_out				= virtio_p9_pci_io_out,
7401c7850f9SSasha Levin };
7411c7850f9SSasha Levin 
742b4422bf3SAneesh Kumar K.V void virtio_9p__init(struct kvm *kvm, const char *root, const char *tag_name)
7431c7850f9SSasha Levin {
7441c7850f9SSasha Levin 	u8 pin, line, dev;
7451c7850f9SSasha Levin 	u32 i, root_len;
746f884f920SSasha Levin 	u16 p9_base_addr;
747b4422bf3SAneesh Kumar K.V 	struct p9_dev *p9dev;
7481c7850f9SSasha Levin 
749b4422bf3SAneesh Kumar K.V 	p9dev = calloc(1, sizeof(*p9dev));
750b4422bf3SAneesh Kumar K.V 	if (!p9dev)
7511c7850f9SSasha Levin 		return;
752b4422bf3SAneesh Kumar K.V 	if (!tag_name)
753b4422bf3SAneesh Kumar K.V 		tag_name = VIRTIO_P9_DEFAULT_TAG;
754b4422bf3SAneesh Kumar K.V 	p9dev->config = calloc(1, sizeof(*p9dev->config) + strlen(tag_name) + 1);
755b4422bf3SAneesh Kumar K.V 	if (p9dev->config == NULL)
756b4422bf3SAneesh Kumar K.V 		goto free_p9dev;
7571c7850f9SSasha Levin 
758b4422bf3SAneesh Kumar K.V 	strcpy(p9dev->root_dir, root);
7591c7850f9SSasha Levin 	root_len = strlen(root);
7601c7850f9SSasha Levin 	/*
7611c7850f9SSasha Levin 	 * We prefix the full path in all fids, This allows us to get the
7621c7850f9SSasha Levin 	 * absolute path of an fid without playing with strings.
7631c7850f9SSasha Levin 	 */
7641c7850f9SSasha Levin 	for (i = 0; i < VIRTIO_P9_MAX_FID; i++) {
765b4422bf3SAneesh Kumar K.V 		strcpy(p9dev->fids[i].abs_path, root);
766b4422bf3SAneesh Kumar K.V 		p9dev->fids[i].path = p9dev->fids[i].abs_path + root_len;
7671c7850f9SSasha Levin 	}
768b4422bf3SAneesh Kumar K.V 	p9dev->config->tag_len = strlen(tag_name);
769b4422bf3SAneesh Kumar K.V 	if (p9dev->config->tag_len > MAX_TAG_LEN)
770b4422bf3SAneesh Kumar K.V 		goto free_p9dev_config;
7711c7850f9SSasha Levin 
772b4422bf3SAneesh Kumar K.V 	memcpy(p9dev->config->tag, tag_name, strlen(tag_name));
773b4422bf3SAneesh Kumar K.V 	p9dev->features |= 1 << VIRTIO_9P_MOUNT_TAG;
7741c7850f9SSasha Levin 
7751c7850f9SSasha Levin 	if (irq__register_device(VIRTIO_ID_9P, &dev, &pin, &line) < 0)
776b4422bf3SAneesh Kumar K.V 		goto free_p9dev_config;
7771c7850f9SSasha Levin 
778b4422bf3SAneesh Kumar K.V 	p9_base_addr			= ioport__register(IOPORT_EMPTY,
779b4422bf3SAneesh Kumar K.V 							   &virtio_p9_io_ops,
780b4422bf3SAneesh Kumar K.V 							   IOPORT_SIZE, p9dev);
781b4422bf3SAneesh Kumar K.V 	p9dev->base_addr		    = p9_base_addr;
782b4422bf3SAneesh Kumar K.V 	p9dev->pci_hdr = (struct pci_device_header) {
783b4422bf3SAneesh Kumar K.V 		.vendor_id		= PCI_VENDOR_ID_REDHAT_QUMRANET,
784b4422bf3SAneesh Kumar K.V 		.device_id		= PCI_DEVICE_ID_VIRTIO_P9,
785b4422bf3SAneesh Kumar K.V 		.header_type		= PCI_HEADER_TYPE_NORMAL,
786b4422bf3SAneesh Kumar K.V 		.revision_id		= 0,
787b4422bf3SAneesh Kumar K.V 		.class			= 0x010000,
788b4422bf3SAneesh Kumar K.V 		.subsys_vendor_id	= PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET,
789b4422bf3SAneesh Kumar K.V 		.subsys_id		= VIRTIO_ID_9P,
790b4422bf3SAneesh Kumar K.V 		.irq_pin		= pin,
791b4422bf3SAneesh Kumar K.V 		.irq_line		= line,
792b4422bf3SAneesh Kumar K.V 		.bar[0]			= p9_base_addr | PCI_BASE_ADDRESS_SPACE_IO,
793b4422bf3SAneesh Kumar K.V 	};
794b4422bf3SAneesh Kumar K.V 	pci__register(&p9dev->pci_hdr, dev);
795b4422bf3SAneesh Kumar K.V 
796b4422bf3SAneesh Kumar K.V 	return;
797b4422bf3SAneesh Kumar K.V free_p9dev_config:
798b4422bf3SAneesh Kumar K.V 	free(p9dev->config);
799b4422bf3SAneesh Kumar K.V free_p9dev:
800b4422bf3SAneesh Kumar K.V 	free(p9dev);
8011c7850f9SSasha Levin }
802