xref: /kvmtool/virtio/9p.c (revision 50c479e054002b37a94f061ade3bd19ebe5aad0e)
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 
211c7850f9SSasha Levin /* Warning: Immediately use value returned from this function */
22b4422bf3SAneesh Kumar K.V static const char *rel_to_abs(struct p9_dev *p9dev,
23b4422bf3SAneesh Kumar K.V 			      const char *path, char *abs_path)
241c7850f9SSasha Levin {
25b4422bf3SAneesh Kumar K.V 	sprintf(abs_path, "%s/%s", p9dev->root_dir, path);
261c7850f9SSasha Levin 
271c7850f9SSasha Levin 	return abs_path;
281c7850f9SSasha Levin }
291c7850f9SSasha Levin 
30b4422bf3SAneesh Kumar K.V static bool virtio_p9_dev_in(struct p9_dev *p9dev, void *data,
31b4422bf3SAneesh Kumar K.V 			     unsigned long offset,
32b4422bf3SAneesh Kumar K.V 			     int size, u32 count)
331c7850f9SSasha Levin {
34b4422bf3SAneesh Kumar K.V 	u8 *config_space = (u8 *) p9dev->config;
351c7850f9SSasha Levin 
361c7850f9SSasha Levin 	if (size != 1 || count != 1)
371c7850f9SSasha Levin 		return false;
381c7850f9SSasha Levin 
391c7850f9SSasha Levin 	ioport__write8(data, config_space[offset - VIRTIO_MSI_CONFIG_VECTOR]);
401c7850f9SSasha Levin 
411c7850f9SSasha Levin 	return true;
421c7850f9SSasha Levin }
431c7850f9SSasha Levin 
44b4422bf3SAneesh Kumar K.V static bool virtio_p9_pci_io_in(struct ioport *ioport, struct kvm *kvm,
45b4422bf3SAneesh Kumar K.V 				u16 port, void *data, int size, u32 count)
461c7850f9SSasha Levin {
471c7850f9SSasha Levin 	bool ret = true;
48b4422bf3SAneesh Kumar K.V 	unsigned long offset;
49b4422bf3SAneesh Kumar K.V 	struct p9_dev *p9dev = ioport->priv;
501c7850f9SSasha Levin 
51b4422bf3SAneesh Kumar K.V 
52b4422bf3SAneesh Kumar K.V 	offset = port - p9dev->base_addr;
531c7850f9SSasha Levin 
541c7850f9SSasha Levin 	switch (offset) {
551c7850f9SSasha Levin 	case VIRTIO_PCI_HOST_FEATURES:
56b4422bf3SAneesh Kumar K.V 		ioport__write32(data, p9dev->features);
571c7850f9SSasha Levin 		ret = true;
581c7850f9SSasha Levin 		break;
591c7850f9SSasha Levin 	case VIRTIO_PCI_GUEST_FEATURES:
601c7850f9SSasha Levin 	case VIRTIO_PCI_QUEUE_SEL:
611c7850f9SSasha Levin 	case VIRTIO_PCI_QUEUE_NOTIFY:
621c7850f9SSasha Levin 		ret = false;
631c7850f9SSasha Levin 		break;
641c7850f9SSasha Levin 	case VIRTIO_PCI_QUEUE_PFN:
65b4422bf3SAneesh Kumar K.V 		ioport__write32(data, p9dev->vqs[p9dev->queue_selector].pfn);
661c7850f9SSasha Levin 		break;
671c7850f9SSasha Levin 	case VIRTIO_PCI_QUEUE_NUM:
68612038c5SAneesh Kumar K.V 		ioport__write16(data, VIRTQUEUE_NUM);
691c7850f9SSasha Levin 		break;
701c7850f9SSasha Levin 	case VIRTIO_PCI_STATUS:
71b4422bf3SAneesh Kumar K.V 		ioport__write8(data, p9dev->status);
721c7850f9SSasha Levin 		break;
731c7850f9SSasha Levin 	case VIRTIO_PCI_ISR:
74b4422bf3SAneesh Kumar K.V 		ioport__write8(data, p9dev->isr);
75b4422bf3SAneesh Kumar K.V 		kvm__irq_line(kvm, p9dev->pci_hdr.irq_line, VIRTIO_IRQ_LOW);
76b4422bf3SAneesh Kumar K.V 		p9dev->isr = VIRTIO_IRQ_LOW;
771c7850f9SSasha Levin 		break;
781c7850f9SSasha Levin 	default:
79b4422bf3SAneesh Kumar K.V 		ret = virtio_p9_dev_in(p9dev, data, offset, size, count);
801c7850f9SSasha Levin 		break;
811c7850f9SSasha Levin 	};
821c7850f9SSasha Levin 
831c7850f9SSasha Levin 	return ret;
841c7850f9SSasha Levin }
851c7850f9SSasha Levin 
861c7850f9SSasha Levin static int omode2uflags(u8 mode)
871c7850f9SSasha Levin {
881c7850f9SSasha Levin 	int ret = 0;
891c7850f9SSasha Levin 
901c7850f9SSasha Levin 	/* Basic open modes are same as uflags */
911c7850f9SSasha Levin 	ret = mode & 3;
921c7850f9SSasha Levin 
931c7850f9SSasha Levin 	/* Everything else is different */
941c7850f9SSasha Levin 	if (mode & P9_OTRUNC)
951c7850f9SSasha Levin 		ret |= O_TRUNC;
961c7850f9SSasha Levin 
971c7850f9SSasha Levin 	if (mode & P9_OAPPEND)
981c7850f9SSasha Levin 		ret |= O_APPEND;
991c7850f9SSasha Levin 
1001c7850f9SSasha Levin 	if (mode & P9_OEXCL)
1011c7850f9SSasha Levin 		ret |= O_EXCL;
1021c7850f9SSasha Levin 
1031c7850f9SSasha Levin 	return ret;
1041c7850f9SSasha Levin }
1051c7850f9SSasha Levin 
1061c7850f9SSasha Levin static void st2qid(struct stat *st, struct p9_qid *qid)
1071c7850f9SSasha Levin {
1081c7850f9SSasha Levin 	*qid = (struct p9_qid) {
1091c7850f9SSasha Levin 		.path		= st->st_ino,
1101c7850f9SSasha Levin 		.version	= st->st_mtime,
1111c7850f9SSasha Levin 	};
1121c7850f9SSasha Levin 
1131c7850f9SSasha Levin 	if (S_ISDIR(st->st_mode))
1141c7850f9SSasha Levin 		qid->type	|= P9_QTDIR;
1151c7850f9SSasha Levin }
1161c7850f9SSasha Levin 
117b4422bf3SAneesh Kumar K.V static void close_fid(struct p9_dev *p9dev, u32 fid)
1181c7850f9SSasha Levin {
119b4422bf3SAneesh Kumar K.V 	if (p9dev->fids[fid].fd > 0) {
120b4422bf3SAneesh Kumar K.V 		close(p9dev->fids[fid].fd);
121b4422bf3SAneesh Kumar K.V 		p9dev->fids[fid].fd = -1;
1221c7850f9SSasha Levin 	}
123b4422bf3SAneesh Kumar K.V 	if (p9dev->fids[fid].dir) {
124b4422bf3SAneesh Kumar K.V 		closedir(p9dev->fids[fid].dir);
125b4422bf3SAneesh Kumar K.V 		p9dev->fids[fid].dir = NULL;
1261c7850f9SSasha Levin 	}
1271c7850f9SSasha Levin }
1281c7850f9SSasha Levin 
129bfc15268SAneesh Kumar K.V static void virtio_p9_set_reply_header(struct p9_pdu *pdu, u32 size)
1301c7850f9SSasha Levin {
131bfc15268SAneesh Kumar K.V 	u8 cmd;
132bfc15268SAneesh Kumar K.V 	u16 tag;
133bfc15268SAneesh Kumar K.V 
134bfc15268SAneesh Kumar K.V 	pdu->read_offset = sizeof(u32);
135bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "bw", &cmd, &tag);
136bfc15268SAneesh Kumar K.V 	pdu->write_offset = 0;
137bfc15268SAneesh Kumar K.V 	/* cmd + 1 is the reply message */
138bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "dbw", size, cmd + 1, tag);
1391c7850f9SSasha Levin }
1401c7850f9SSasha Levin 
1416b163a87SAneesh Kumar K.V static u16 virtio_p9_update_iov_cnt(struct iovec iov[], u32 count, int iov_cnt)
1426b163a87SAneesh Kumar K.V {
1436b163a87SAneesh Kumar K.V 	int i;
1446b163a87SAneesh Kumar K.V 	u32 total = 0;
1456b163a87SAneesh Kumar K.V 	for (i = 0; (i < iov_cnt) && (total < count); i++) {
1466b163a87SAneesh Kumar K.V 		if (total + iov[i].iov_len > count) {
1476b163a87SAneesh Kumar K.V 			/* we don't need this iov fully */
1486b163a87SAneesh Kumar K.V 			iov[i].iov_len -= ((total + iov[i].iov_len) - count);
1496b163a87SAneesh Kumar K.V 			i++;
1506b163a87SAneesh Kumar K.V 			break;
1516b163a87SAneesh Kumar K.V 		}
1526b163a87SAneesh Kumar K.V 		total += iov[i].iov_len;
1536b163a87SAneesh Kumar K.V 	}
1546b163a87SAneesh Kumar K.V 	return i;
1556b163a87SAneesh Kumar K.V }
1566b163a87SAneesh Kumar K.V 
157eee1ba8eSAneesh Kumar K.V static void virtio_p9_error_reply(struct p9_dev *p9dev,
158eee1ba8eSAneesh Kumar K.V 				  struct p9_pdu *pdu, int err, u32 *outlen)
159eee1ba8eSAneesh Kumar K.V {
160bfc15268SAneesh Kumar K.V 	u16 tag;
161eee1ba8eSAneesh Kumar K.V 	char *err_str;
162eee1ba8eSAneesh Kumar K.V 
163eee1ba8eSAneesh Kumar K.V 	err_str = strerror(err);
164bfc15268SAneesh Kumar K.V 	pdu->write_offset = VIRTIO_P9_HDR_LEN;
1655f900f6dSSasha Levin 	virtio_p9_pdu_writef(pdu, "sd", err_str, err);
166bfc15268SAneesh Kumar K.V 	*outlen = pdu->write_offset;
167eee1ba8eSAneesh Kumar K.V 
168bfc15268SAneesh Kumar K.V 	pdu->read_offset = sizeof(u32) + sizeof(u8);
169bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "w", &tag);
170bfc15268SAneesh Kumar K.V 
171bfc15268SAneesh Kumar K.V 	pdu->write_offset = 0;
172bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "dbw", *outlen, P9_RERROR, tag);
173eee1ba8eSAneesh Kumar K.V }
174eee1ba8eSAneesh Kumar K.V 
175ead43b01SAneesh Kumar K.V static void virtio_p9_version(struct p9_dev *p9dev,
176af045e53SAneesh Kumar K.V 			      struct p9_pdu *pdu, u32 *outlen)
1771c7850f9SSasha Levin {
178bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "ds", 4096, VIRTIO_P9_VERSION);
1791c7850f9SSasha Levin 
180bfc15268SAneesh Kumar K.V 	*outlen = pdu->write_offset;
181bfc15268SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
182ead43b01SAneesh Kumar K.V 	return;
1831c7850f9SSasha Levin }
1841c7850f9SSasha Levin 
185ead43b01SAneesh Kumar K.V static void virtio_p9_clunk(struct p9_dev *p9dev,
186af045e53SAneesh Kumar K.V 			    struct p9_pdu *pdu, u32 *outlen)
1871c7850f9SSasha Levin {
188bfc15268SAneesh Kumar K.V 	u32 fid;
1891c7850f9SSasha Levin 
190bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "d", &fid);
191bfc15268SAneesh Kumar K.V 	close_fid(p9dev, fid);
1921c7850f9SSasha Levin 
193bfc15268SAneesh Kumar K.V 	*outlen = pdu->write_offset;
194bfc15268SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
195ead43b01SAneesh Kumar K.V 	return;
1961c7850f9SSasha Levin }
1971c7850f9SSasha Levin 
198ead43b01SAneesh Kumar K.V static void virtio_p9_open(struct p9_dev *p9dev,
199af045e53SAneesh Kumar K.V 			   struct p9_pdu *pdu, u32 *outlen)
2001c7850f9SSasha Levin {
201bfc15268SAneesh Kumar K.V 	u8 mode;
202bfc15268SAneesh Kumar K.V 	u32 fid;
2031c7850f9SSasha Levin 	struct stat st;
204bfc15268SAneesh Kumar K.V 	struct p9_qid qid;
205bfc15268SAneesh Kumar K.V 	struct p9_fid *new_fid;
206bfc15268SAneesh Kumar K.V 
207bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "db", &fid, &mode);
208bfc15268SAneesh Kumar K.V 	new_fid = &p9dev->fids[fid];
2091c7850f9SSasha Levin 
21030204a77SAneesh Kumar K.V 	if (lstat(new_fid->abs_path, &st) < 0)
211eee1ba8eSAneesh Kumar K.V 		goto err_out;
2121c7850f9SSasha Levin 
213bfc15268SAneesh Kumar K.V 	st2qid(&st, &qid);
2141c7850f9SSasha Levin 
215eee1ba8eSAneesh Kumar K.V 	if (new_fid->is_dir) {
2161c7850f9SSasha Levin 		new_fid->dir = opendir(new_fid->abs_path);
217eee1ba8eSAneesh Kumar K.V 		if (!new_fid->dir)
218eee1ba8eSAneesh Kumar K.V 			goto err_out;
219eee1ba8eSAneesh Kumar K.V 	} else {
220eee1ba8eSAneesh Kumar K.V 		new_fid->fd  = open(new_fid->abs_path,
221bfc15268SAneesh Kumar K.V 				    omode2uflags(mode) | O_NOFOLLOW);
222eee1ba8eSAneesh Kumar K.V 		if (new_fid->fd < 0)
223eee1ba8eSAneesh Kumar K.V 			goto err_out;
224eee1ba8eSAneesh Kumar K.V 	}
225bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "Qd", &qid, 0);
226bfc15268SAneesh Kumar K.V 
227bfc15268SAneesh Kumar K.V 	*outlen = pdu->write_offset;
228bfc15268SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
229ead43b01SAneesh Kumar K.V 	return;
230eee1ba8eSAneesh Kumar K.V err_out:
231eee1ba8eSAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
232ead43b01SAneesh Kumar K.V 	return;
2331c7850f9SSasha Levin }
2341c7850f9SSasha Levin 
235ead43b01SAneesh Kumar K.V static void virtio_p9_create(struct p9_dev *p9dev,
236af045e53SAneesh Kumar K.V 			     struct p9_pdu *pdu, u32 *outlen)
2371c7850f9SSasha Levin {
2384bc9734aSAneesh Kumar K.V 	DIR *dir;
2394bc9734aSAneesh Kumar K.V 	int fd;
2404bc9734aSAneesh Kumar K.V 	int res;
2411c7850f9SSasha Levin 	u8 mode;
2421c7850f9SSasha Levin 	u32 perm;
243bfc15268SAneesh Kumar K.V 	char *name;
2445f900f6dSSasha Levin 	char *ext = NULL;
245bfc15268SAneesh Kumar K.V 	u32 fid_val;
246af045e53SAneesh Kumar K.V 	struct stat st;
247bfc15268SAneesh Kumar K.V 	struct p9_qid qid;
248bfc15268SAneesh Kumar K.V 	struct p9_fid *fid;
2494bc9734aSAneesh Kumar K.V 	char full_path[PATH_MAX];
250af045e53SAneesh Kumar K.V 
2515f900f6dSSasha Levin 	virtio_p9_pdu_readf(pdu, "dsdbs", &fid_val, &name, &perm, &mode, &ext);
252bfc15268SAneesh Kumar K.V 	fid = &p9dev->fids[fid_val];
2531c7850f9SSasha Levin 
2544bc9734aSAneesh Kumar K.V 	sprintf(full_path, "%s/%s", fid->abs_path, name);
2551c7850f9SSasha Levin 	if (perm & P9_DMDIR) {
2564bc9734aSAneesh Kumar K.V 		res = mkdir(full_path, perm & 0xFFFF);
2574bc9734aSAneesh Kumar K.V 		if (res < 0)
2584bc9734aSAneesh Kumar K.V 			goto err_out;
2594bc9734aSAneesh Kumar K.V 		dir = opendir(full_path);
2604bc9734aSAneesh Kumar K.V 		if (!dir)
2614bc9734aSAneesh Kumar K.V 			goto err_out;
2624bc9734aSAneesh Kumar K.V 		close_fid(p9dev, fid_val);
2634bc9734aSAneesh Kumar K.V 		fid->dir = dir;
2641c7850f9SSasha Levin 		fid->is_dir = 1;
2655f900f6dSSasha Levin 	} else if (perm & P9_DMSYMLINK) {
2665f900f6dSSasha Levin 		if (symlink(ext, full_path) < 0)
2675f900f6dSSasha Levin 			goto err_out;
2685f900f6dSSasha Levin 	} else if (perm & P9_DMLINK) {
2695f900f6dSSasha Levin 		int ext_fid = atoi(ext);
2705f900f6dSSasha Levin 
2715f900f6dSSasha Levin 		if (link(p9dev->fids[ext_fid].abs_path, full_path) < 0)
2725f900f6dSSasha Levin 			goto err_out;
2731c7850f9SSasha Levin 	} else {
2744bc9734aSAneesh Kumar K.V 		fd = open(full_path, omode2uflags(mode) | O_CREAT, 0777);
2754bc9734aSAneesh Kumar K.V 		if (fd < 0)
2764bc9734aSAneesh Kumar K.V 			goto err_out;
2774bc9734aSAneesh Kumar K.V 		close_fid(p9dev, fid_val);
2784bc9734aSAneesh Kumar K.V 		fid->fd = fd;
2791c7850f9SSasha Levin 	}
2804bc9734aSAneesh Kumar K.V 	if (lstat(full_path, &st) < 0)
2816c8ca053SAneesh Kumar K.V 		goto err_out;
2821c7850f9SSasha Levin 
2834bc9734aSAneesh Kumar K.V 	sprintf(fid->path, "%s/%s", fid->path, name);
284bfc15268SAneesh Kumar K.V 	st2qid(&st, &qid);
285bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "Qd", &qid, 0);
286bfc15268SAneesh Kumar K.V 	*outlen = pdu->write_offset;
287bfc15268SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
2885f900f6dSSasha Levin 
2895f900f6dSSasha Levin 	free(name);
2905f900f6dSSasha Levin 	free(ext);
2916c8ca053SAneesh Kumar K.V 	return;
2926c8ca053SAneesh Kumar K.V err_out:
2936c8ca053SAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
2945f900f6dSSasha Levin 	free(name);
2955f900f6dSSasha Levin 	free(ext);
296ead43b01SAneesh Kumar K.V 	return;
2971c7850f9SSasha Levin }
2981c7850f9SSasha Levin 
299ead43b01SAneesh Kumar K.V static void virtio_p9_walk(struct p9_dev *p9dev,
300af045e53SAneesh Kumar K.V 			   struct p9_pdu *pdu, u32 *outlen)
3011c7850f9SSasha Levin {
302af045e53SAneesh Kumar K.V 	u8 i;
303bfc15268SAneesh Kumar K.V 	u16 nwqid;
304bfc15268SAneesh Kumar K.V 	char *str;
305bfc15268SAneesh Kumar K.V 	u16 nwname;
306bfc15268SAneesh Kumar K.V 	u32 fid_val;
307bfc15268SAneesh Kumar K.V 	u32 newfid_val;
308bfc15268SAneesh Kumar K.V 	struct p9_qid wqid;
309bfc15268SAneesh Kumar K.V 	struct p9_fid *new_fid;
3105f900f6dSSasha Levin 	int ret;
3111c7850f9SSasha Levin 
312bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "ddw", &fid_val, &newfid_val, &nwname);
313bfc15268SAneesh Kumar K.V 	new_fid	= &p9dev->fids[newfid_val];
3141c7850f9SSasha Levin 
315bfc15268SAneesh Kumar K.V 	nwqid = 0;
316bfc15268SAneesh Kumar K.V 	if (nwname) {
317bfc15268SAneesh Kumar K.V 		struct p9_fid *fid = &p9dev->fids[fid_val];
318bfc15268SAneesh Kumar K.V 
319bfc15268SAneesh Kumar K.V 		/* skip the space for count */
320bfc15268SAneesh Kumar K.V 		pdu->write_offset += sizeof(u16);
321bfc15268SAneesh Kumar K.V 		for (i = 0; i < nwname; i++) {
322bfc15268SAneesh Kumar K.V 			struct stat st;
3231c7850f9SSasha Levin 			char tmp[PATH_MAX] = {0};
3241c7850f9SSasha Levin 			char full_path[PATH_MAX];
325bfc15268SAneesh Kumar K.V 
326bfc15268SAneesh Kumar K.V 			virtio_p9_pdu_readf(pdu, "s", &str);
3271c7850f9SSasha Levin 
3281c7850f9SSasha Levin 			/* Format the new path we're 'walk'ing into */
329bfc15268SAneesh Kumar K.V 			sprintf(tmp, "%s/%.*s",
330bfc15268SAneesh Kumar K.V 				fid->path, (int)strlen(str), str);
3315f900f6dSSasha Levin 
3325f900f6dSSasha Levin 			ret = lstat(rel_to_abs(p9dev, tmp, full_path), &st);
3335f900f6dSSasha Levin 			if (ret < 0)
3346c8ca053SAneesh Kumar K.V 				goto err_out;
3351c7850f9SSasha Levin 
336bfc15268SAneesh Kumar K.V 			st2qid(&st, &wqid);
3371c7850f9SSasha Levin 			new_fid->is_dir = S_ISDIR(st.st_mode);
3381c7850f9SSasha Levin 			strcpy(new_fid->path, tmp);
339bfc15268SAneesh Kumar K.V 			new_fid->fid = newfid_val;
340bfc15268SAneesh Kumar K.V 			nwqid++;
341bfc15268SAneesh Kumar K.V 			virtio_p9_pdu_writef(pdu, "Q", &wqid);
3421c7850f9SSasha Levin 		}
3431c7850f9SSasha Levin 	} else {
344bfc15268SAneesh Kumar K.V 		/*
345bfc15268SAneesh Kumar K.V 		 * update write_offset so our outlen get correct value
346bfc15268SAneesh Kumar K.V 		 */
347bfc15268SAneesh Kumar K.V 		pdu->write_offset += sizeof(u16);
348bfc15268SAneesh Kumar K.V 		new_fid->is_dir = p9dev->fids[fid_val].is_dir;
349bfc15268SAneesh Kumar K.V 		strcpy(new_fid->path, p9dev->fids[fid_val].path);
350bfc15268SAneesh Kumar K.V 		new_fid->fid	= newfid_val;
3511c7850f9SSasha Levin 	}
352bfc15268SAneesh Kumar K.V 	*outlen = pdu->write_offset;
353bfc15268SAneesh Kumar K.V 	pdu->write_offset = VIRTIO_P9_HDR_LEN;
354bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "d", nwqid);
355bfc15268SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
3566c8ca053SAneesh Kumar K.V 	return;
3576c8ca053SAneesh Kumar K.V err_out:
3586c8ca053SAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
359ead43b01SAneesh Kumar K.V 	return;
3601c7850f9SSasha Levin }
3611c7850f9SSasha Levin 
362ead43b01SAneesh Kumar K.V static void virtio_p9_attach(struct p9_dev *p9dev,
363af045e53SAneesh Kumar K.V 			     struct p9_pdu *pdu, u32 *outlen)
3641c7850f9SSasha Levin {
365af045e53SAneesh Kumar K.V 	u32 i;
366bfc15268SAneesh Kumar K.V 	u32 fid_val;
367bfc15268SAneesh Kumar K.V 	u32 afid;
368bfc15268SAneesh Kumar K.V 	char *uname;
369bfc15268SAneesh Kumar K.V 	char *aname;
3701c7850f9SSasha Levin 	struct stat st;
371bfc15268SAneesh Kumar K.V 	struct p9_qid qid;
3721c7850f9SSasha Levin 	struct p9_fid *fid;
373bfc15268SAneesh Kumar K.V 
374bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "ddss", &fid_val, &afid, &uname, &aname);
3751c7850f9SSasha Levin 
3761c7850f9SSasha Levin 	/* Reset everything */
3771c7850f9SSasha Levin 	for (i = 0; i < VIRTIO_P9_MAX_FID; i++)
378b4422bf3SAneesh Kumar K.V 		p9dev->fids[i].fid = P9_NOFID;
3791c7850f9SSasha Levin 
38030204a77SAneesh Kumar K.V 	if (lstat(p9dev->root_dir, &st) < 0)
3816c8ca053SAneesh Kumar K.V 		goto err_out;
3821c7850f9SSasha Levin 
383bfc15268SAneesh Kumar K.V 	st2qid(&st, &qid);
3841c7850f9SSasha Levin 
385bfc15268SAneesh Kumar K.V 	fid = &p9dev->fids[fid_val];
386bfc15268SAneesh Kumar K.V 	fid->fid = fid_val;
3871c7850f9SSasha Levin 	fid->is_dir = 1;
3881c7850f9SSasha Levin 	strcpy(fid->path, "/");
3891c7850f9SSasha Levin 
390bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "Q", &qid);
391bfc15268SAneesh Kumar K.V 	*outlen = pdu->write_offset;
392bfc15268SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
3935f900f6dSSasha Levin 	free(uname);
3945f900f6dSSasha Levin 	free(aname);
3956c8ca053SAneesh Kumar K.V 	return;
3966c8ca053SAneesh Kumar K.V err_out:
3975f900f6dSSasha Levin 	free(uname);
3985f900f6dSSasha Levin 	free(aname);
3996c8ca053SAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
400ead43b01SAneesh Kumar K.V 	return;
4011c7850f9SSasha Levin }
4021c7850f9SSasha Levin 
4035f900f6dSSasha Levin static void virtio_p9_free_stat(struct p9_wstat *wstat)
4045f900f6dSSasha Levin {
4055f900f6dSSasha Levin 	free(wstat->extension);
4065f900f6dSSasha Levin 	free(wstat->name);
4075f900f6dSSasha Levin }
4085f900f6dSSasha Levin 
409bfc15268SAneesh Kumar K.V static void virtio_p9_fill_stat(struct p9_dev *p9dev, const char *name,
410bfc15268SAneesh Kumar K.V 				struct stat *st, struct p9_wstat *wstat)
4111c7850f9SSasha Levin {
412bfc15268SAneesh Kumar K.V 	wstat->type = 0;
413bfc15268SAneesh Kumar K.V 	wstat->dev = 0;
414bfc15268SAneesh Kumar K.V 	st2qid(st, &wstat->qid);
415bfc15268SAneesh Kumar K.V 	wstat->mode = st->st_mode;
416bfc15268SAneesh Kumar K.V 	wstat->length = st->st_size;
4171c7850f9SSasha Levin 	if (S_ISDIR(st->st_mode)) {
418bfc15268SAneesh Kumar K.V 		wstat->length = 0;
419bfc15268SAneesh Kumar K.V 		wstat->mode |= P9_DMDIR;
4201c7850f9SSasha Levin 	}
4215f900f6dSSasha Levin 	if (S_ISLNK(st->st_mode)) {
4225f900f6dSSasha Levin 		char tmp[PATH_MAX] = {0}, full_path[PATH_MAX] = {0};
4235f900f6dSSasha Levin 
4245f900f6dSSasha Levin 		rel_to_abs(p9dev, name, full_path);
4255f900f6dSSasha Levin 
4265f900f6dSSasha Levin 		if (readlink(full_path, tmp, PATH_MAX) > 0)
4275f900f6dSSasha Levin 			wstat->extension = strdup(tmp);
4285f900f6dSSasha Levin 		wstat->mode |= P9_DMSYMLINK;
4295f900f6dSSasha Levin 	} else {
4305f900f6dSSasha Levin 		wstat->extension = NULL;
4315f900f6dSSasha Levin 	}
4321c7850f9SSasha Levin 
433bfc15268SAneesh Kumar K.V 	wstat->atime = st->st_atime;
434bfc15268SAneesh Kumar K.V 	wstat->mtime = st->st_mtime;
4351c7850f9SSasha Levin 
436bfc15268SAneesh Kumar K.V 	wstat->name = strdup(name);
437bfc15268SAneesh Kumar K.V 	wstat->uid = NULL;
438bfc15268SAneesh Kumar K.V 	wstat->gid = NULL;
439bfc15268SAneesh Kumar K.V 	wstat->muid = NULL;
4405f900f6dSSasha Levin 	wstat->n_uid = wstat->n_gid = wstat->n_muid = 0;
4411c7850f9SSasha Levin 
4425f900f6dSSasha Levin 	/*
4435f900f6dSSasha Levin 	 * NOTE: size shouldn't include its own length
4445f900f6dSSasha Levin 	 * size[2] type[2] dev[4] qid[13]
4455f900f6dSSasha Levin 	 * mode[4] atime[4] mtime[4] length[8]
4465f900f6dSSasha Levin 	 * name[s] uid[s] gid[s] muid[s]
4475f900f6dSSasha Levin 	 * ext[s] uid[4] gid[4] muid[4]
4485f900f6dSSasha Levin 	 */
4495f900f6dSSasha Levin 	wstat->size = 2+4+13+4+4+4+8+2+2+2+2+2+4+4+4;
450bfc15268SAneesh Kumar K.V 	if (wstat->name)
451bfc15268SAneesh Kumar K.V 		wstat->size += strlen(wstat->name);
4525f900f6dSSasha Levin 	if (wstat->extension)
4535f900f6dSSasha Levin 		wstat->size += strlen(wstat->extension);
4541c7850f9SSasha Levin }
4551c7850f9SSasha Levin 
456ead43b01SAneesh Kumar K.V static void virtio_p9_read(struct p9_dev *p9dev,
457af045e53SAneesh Kumar K.V 			   struct p9_pdu *pdu, u32 *outlen)
4581c7850f9SSasha Levin {
459bfc15268SAneesh Kumar K.V 	u64 offset;
460bfc15268SAneesh Kumar K.V 	u32 fid_val;
461bfc15268SAneesh Kumar K.V 	u32 count, rcount;
4621c7850f9SSasha Levin 	struct stat st;
463bfc15268SAneesh Kumar K.V 	struct p9_fid *fid;
464bfc15268SAneesh Kumar K.V 	struct p9_wstat wstat;
4651c7850f9SSasha Levin 
466bfc15268SAneesh Kumar K.V 	rcount = 0;
467bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "dqd", &fid_val, &offset, &count);
468bfc15268SAneesh Kumar K.V 	fid = &p9dev->fids[fid_val];
4691c7850f9SSasha Levin 	if (fid->is_dir) {
4701c7850f9SSasha Levin 		/* If reading a dir, fill the buffer with p9_stat entries */
4711c7850f9SSasha Levin 		char full_path[PATH_MAX];
472bfc15268SAneesh Kumar K.V 		struct dirent *cur = readdir(fid->dir);
4731c7850f9SSasha Levin 
474bfc15268SAneesh Kumar K.V 		/* Skip the space for writing count */
475bfc15268SAneesh Kumar K.V 		pdu->write_offset += sizeof(u32);
4761c7850f9SSasha Levin 		while (cur) {
4771c7850f9SSasha Levin 			u32 read;
4781c7850f9SSasha Levin 
47930204a77SAneesh Kumar K.V 			lstat(rel_to_abs(p9dev, cur->d_name, full_path), &st);
480bfc15268SAneesh Kumar K.V 			virtio_p9_fill_stat(p9dev, cur->d_name, &st, &wstat);
481bfc15268SAneesh Kumar K.V 
482bfc15268SAneesh Kumar K.V 			read = pdu->write_offset;
483bfc15268SAneesh Kumar K.V 			virtio_p9_pdu_writef(pdu, "S", &wstat);
484bfc15268SAneesh Kumar K.V 			rcount += pdu->write_offset - read;
4855f900f6dSSasha Levin 			virtio_p9_free_stat(&wstat);
486bfc15268SAneesh Kumar K.V 
4871c7850f9SSasha Levin 			cur = readdir(fid->dir);
4881c7850f9SSasha Levin 		}
4891c7850f9SSasha Levin 	} else {
490*50c479e0SAneesh Kumar K.V 		u16 iov_cnt;
491*50c479e0SAneesh Kumar K.V 		void *iov_base;
492*50c479e0SAneesh Kumar K.V 		size_t iov_len;
493*50c479e0SAneesh Kumar K.V 
494*50c479e0SAneesh Kumar K.V 		iov_base = pdu->in_iov[0].iov_base;
495*50c479e0SAneesh Kumar K.V 		iov_len  = pdu->in_iov[0].iov_len;
496*50c479e0SAneesh Kumar K.V 		iov_cnt  = pdu->in_iov_cnt;
497*50c479e0SAneesh Kumar K.V 
498af045e53SAneesh Kumar K.V 		pdu->in_iov[0].iov_base += VIRTIO_P9_HDR_LEN + sizeof(u32);
499af045e53SAneesh Kumar K.V 		pdu->in_iov[0].iov_len -= VIRTIO_P9_HDR_LEN + sizeof(u32);
5006b163a87SAneesh Kumar K.V 		pdu->in_iov_cnt = virtio_p9_update_iov_cnt(pdu->in_iov,
501bfc15268SAneesh Kumar K.V 							   count,
5026b163a87SAneesh Kumar K.V 							   pdu->in_iov_cnt);
503bfc15268SAneesh Kumar K.V 		rcount = preadv(fid->fd, pdu->in_iov,
504bfc15268SAneesh Kumar K.V 				pdu->in_iov_cnt, offset);
505bfc15268SAneesh Kumar K.V 		if (rcount > count)
506bfc15268SAneesh Kumar K.V 			rcount = count;
507bfc15268SAneesh Kumar K.V 		/*
508bfc15268SAneesh Kumar K.V 		 * Update the iov_base back, so that rest of
509bfc15268SAneesh Kumar K.V 		 * pdu_writef works correctly.
510bfc15268SAneesh Kumar K.V 		 */
511*50c479e0SAneesh Kumar K.V 		pdu->in_iov[0].iov_base = iov_base;
512*50c479e0SAneesh Kumar K.V 		pdu->in_iov[0].iov_len  = iov_len;
513*50c479e0SAneesh Kumar K.V 		pdu->in_iov_cnt         = iov_cnt;
514bfc15268SAneesh Kumar K.V 	}
515bfc15268SAneesh Kumar K.V 	pdu->write_offset = VIRTIO_P9_HDR_LEN;
516bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "d", rcount);
517bfc15268SAneesh Kumar K.V 	*outlen = pdu->write_offset + rcount;
518bfc15268SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
5191c7850f9SSasha Levin 
520ead43b01SAneesh Kumar K.V 	return;
5211c7850f9SSasha Levin }
5221c7850f9SSasha Levin 
523ead43b01SAneesh Kumar K.V static void virtio_p9_stat(struct p9_dev *p9dev,
524af045e53SAneesh Kumar K.V 			   struct p9_pdu *pdu, u32 *outlen)
5251c7850f9SSasha Levin {
526bfc15268SAneesh Kumar K.V 	u32 fid_val;
527af045e53SAneesh Kumar K.V 	struct stat st;
528bfc15268SAneesh Kumar K.V 	struct p9_fid *fid;
529bfc15268SAneesh Kumar K.V 	struct p9_wstat wstat;
5301c7850f9SSasha Levin 
531bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "d", &fid_val);
532bfc15268SAneesh Kumar K.V 	fid = &p9dev->fids[fid_val];
53330204a77SAneesh Kumar K.V 	if (lstat(fid->abs_path, &st) < 0)
5346c8ca053SAneesh Kumar K.V 		goto err_out;
5351c7850f9SSasha Levin 
536bfc15268SAneesh Kumar K.V 	virtio_p9_fill_stat(p9dev, fid->path, &st, &wstat);
5371c7850f9SSasha Levin 
538bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "wS", 0, &wstat);
539bfc15268SAneesh Kumar K.V 	*outlen = pdu->write_offset;
5405f900f6dSSasha Levin 	virtio_p9_free_stat(&wstat);
541bfc15268SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
542ead43b01SAneesh Kumar K.V 	return;
5436c8ca053SAneesh Kumar K.V err_out:
5446c8ca053SAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
5456c8ca053SAneesh Kumar K.V 	return;
5461c7850f9SSasha Levin }
5471c7850f9SSasha Levin 
548ead43b01SAneesh Kumar K.V static void virtio_p9_wstat(struct p9_dev *p9dev,
549af045e53SAneesh Kumar K.V 			    struct p9_pdu *pdu, u32 *outlen)
5501c7850f9SSasha Levin {
551aec426f0SCyrill Gorcunov 	int res = 0;
552bfc15268SAneesh Kumar K.V 	u32 fid_val;
553bfc15268SAneesh Kumar K.V 	u16 unused;
554bfc15268SAneesh Kumar K.V 	struct p9_fid *fid;
555bfc15268SAneesh Kumar K.V 	struct p9_wstat wstat;
556af045e53SAneesh Kumar K.V 
557bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "dwS", &fid_val, &unused, &wstat);
558bfc15268SAneesh Kumar K.V 	fid = &p9dev->fids[fid_val];
5591c7850f9SSasha Levin 
5604bc9734aSAneesh Kumar K.V 	if (wstat.length != -1UL) {
561c6fb59c4SAneesh Kumar K.V 		res = truncate(fid->abs_path, wstat.length);
5624bc9734aSAneesh Kumar K.V 		if (res < 0)
5634bc9734aSAneesh Kumar K.V 			goto err_out;
5644bc9734aSAneesh Kumar K.V 	}
5654bc9734aSAneesh Kumar K.V 	if (wstat.mode != -1U) {
5664bc9734aSAneesh Kumar K.V 		res = chmod(fid->abs_path, wstat.mode & 0xFFFF);
5674bc9734aSAneesh Kumar K.V 		if (res < 0)
5684bc9734aSAneesh Kumar K.V 			goto err_out;
5694bc9734aSAneesh Kumar K.V 	}
570bfc15268SAneesh Kumar K.V 	if (strlen(wstat.name) > 0) {
5711c7850f9SSasha Levin 		char new_name[PATH_MAX] = {0};
5721c7850f9SSasha Levin 		char full_path[PATH_MAX];
5731c7850f9SSasha Levin 		char *last_dir = strrchr(fid->path, '/');
5741c7850f9SSasha Levin 
5751c7850f9SSasha Levin 		/* We need to get the full file name out of twstat->name */
5761c7850f9SSasha Levin 		if (last_dir)
5771c7850f9SSasha Levin 			strncpy(new_name, fid->path, last_dir - fid->path + 1);
5781c7850f9SSasha Levin 
579bfc15268SAneesh Kumar K.V 		memcpy(new_name + strlen(new_name),
580bfc15268SAneesh Kumar K.V 		       wstat.name, strlen(wstat.name));
5811c7850f9SSasha Levin 
5821c7850f9SSasha Levin 		/* fid is reused for the new file */
5834bc9734aSAneesh Kumar K.V 		res = rename(fid->abs_path,
5844bc9734aSAneesh Kumar K.V 			     rel_to_abs(p9dev, new_name, full_path));
5854bc9734aSAneesh Kumar K.V 		if (res < 0)
5864bc9734aSAneesh Kumar K.V 			goto err_out;
5871c7850f9SSasha Levin 		sprintf(fid->path, "%s", new_name);
5881c7850f9SSasha Levin 	}
5891c7850f9SSasha Levin 	*outlen = VIRTIO_P9_HDR_LEN;
590bfc15268SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
591ead43b01SAneesh Kumar K.V 	return;
5924bc9734aSAneesh Kumar K.V err_out:
5934bc9734aSAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
5944bc9734aSAneesh Kumar K.V 	return;
5951c7850f9SSasha Levin }
5961c7850f9SSasha Levin 
597ead43b01SAneesh Kumar K.V static void virtio_p9_remove(struct p9_dev *p9dev,
598af045e53SAneesh Kumar K.V 			     struct p9_pdu *pdu, u32 *outlen)
5991c7850f9SSasha Levin {
6004bc9734aSAneesh Kumar K.V 	int res;
601bfc15268SAneesh Kumar K.V 	u32 fid_val;
602bfc15268SAneesh Kumar K.V 	struct p9_fid *fid;
6031c7850f9SSasha Levin 
604bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "d", &fid_val);
605bfc15268SAneesh Kumar K.V 	fid = &p9dev->fids[fid_val];
606bfc15268SAneesh Kumar K.V 	close_fid(p9dev, fid_val);
6071c7850f9SSasha Levin 	if (fid->is_dir)
6084bc9734aSAneesh Kumar K.V 		res = rmdir(fid->abs_path);
6091c7850f9SSasha Levin 	else
6104bc9734aSAneesh Kumar K.V 		res = unlink(fid->abs_path);
6114bc9734aSAneesh Kumar K.V 	if (res < 0)
6124bc9734aSAneesh Kumar K.V 		goto err_out;
6131c7850f9SSasha Levin 
6141c7850f9SSasha Levin 	*outlen = VIRTIO_P9_HDR_LEN;
615bfc15268SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
616ead43b01SAneesh Kumar K.V 	return;
6174bc9734aSAneesh Kumar K.V err_out:
6184bc9734aSAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
6194bc9734aSAneesh Kumar K.V 	return;
6201c7850f9SSasha Levin }
6211c7850f9SSasha Levin 
622ead43b01SAneesh Kumar K.V static void virtio_p9_write(struct p9_dev *p9dev,
623af045e53SAneesh Kumar K.V 			    struct p9_pdu *pdu, u32 *outlen)
6241c7850f9SSasha Levin {
6254bc9734aSAneesh Kumar K.V 
626bfc15268SAneesh Kumar K.V 	u64 offset;
627bfc15268SAneesh Kumar K.V 	u32 fid_val;
6284bc9734aSAneesh Kumar K.V 	u32 count;
6294bc9734aSAneesh Kumar K.V 	ssize_t res;
630*50c479e0SAneesh Kumar K.V 	u16 iov_cnt;
631*50c479e0SAneesh Kumar K.V 	void *iov_base;
632*50c479e0SAneesh Kumar K.V 	size_t iov_len;
633bfc15268SAneesh Kumar K.V 	struct p9_fid *fid;
634b064b05aSAneesh Kumar K.V 	/* u32 fid + u64 offset + u32 count */
635b064b05aSAneesh Kumar K.V 	int twrite_size = sizeof(u32) + sizeof(u64) + sizeof(u32);
6361c7850f9SSasha Levin 
637bfc15268SAneesh Kumar K.V 	virtio_p9_pdu_readf(pdu, "dqd", &fid_val, &offset, &count);
638bfc15268SAneesh Kumar K.V 	fid = &p9dev->fids[fid_val];
639af045e53SAneesh Kumar K.V 
640*50c479e0SAneesh Kumar K.V 	iov_base = pdu->out_iov[0].iov_base;
641*50c479e0SAneesh Kumar K.V 	iov_len  = pdu->out_iov[0].iov_len;
642*50c479e0SAneesh Kumar K.V 	iov_cnt  = pdu->out_iov_cnt;
643*50c479e0SAneesh Kumar K.V 
644bfc15268SAneesh Kumar K.V 	/* Adjust the iovec to skip the header and meta data */
645b064b05aSAneesh Kumar K.V 	pdu->out_iov[0].iov_base += (sizeof(struct p9_msg) + twrite_size);
646b064b05aSAneesh Kumar K.V 	pdu->out_iov[0].iov_len -=  (sizeof(struct p9_msg) + twrite_size);
647bfc15268SAneesh Kumar K.V 	pdu->out_iov_cnt = virtio_p9_update_iov_cnt(pdu->out_iov, count,
6486b163a87SAneesh Kumar K.V 						    pdu->out_iov_cnt);
6494bc9734aSAneesh Kumar K.V 	res = pwritev(fid->fd, pdu->out_iov, pdu->out_iov_cnt, offset);
650*50c479e0SAneesh Kumar K.V 	/*
651*50c479e0SAneesh Kumar K.V 	 * Update the iov_base back, so that rest of
652*50c479e0SAneesh Kumar K.V 	 * pdu_readf works correctly.
653*50c479e0SAneesh Kumar K.V 	 */
654*50c479e0SAneesh Kumar K.V 	pdu->out_iov[0].iov_base = iov_base;
655*50c479e0SAneesh Kumar K.V 	pdu->out_iov[0].iov_len  = iov_len;
656*50c479e0SAneesh Kumar K.V 	pdu->out_iov_cnt         = iov_cnt;
6574bc9734aSAneesh Kumar K.V 	if (res < 0)
6584bc9734aSAneesh Kumar K.V 		goto err_out;
6594bc9734aSAneesh Kumar K.V 	virtio_p9_pdu_writef(pdu, "d", res);
660bfc15268SAneesh Kumar K.V 	*outlen = pdu->write_offset;
661bfc15268SAneesh Kumar K.V 	virtio_p9_set_reply_header(pdu, *outlen);
662ead43b01SAneesh Kumar K.V 	return;
6634bc9734aSAneesh Kumar K.V err_out:
6644bc9734aSAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
6654bc9734aSAneesh Kumar K.V 	return;
6661c7850f9SSasha Levin }
6671c7850f9SSasha Levin 
668ead43b01SAneesh Kumar K.V typedef void p9_handler(struct p9_dev *p9dev,
669af045e53SAneesh Kumar K.V 			struct p9_pdu *pdu, u32 *outlen);
670b4422bf3SAneesh Kumar K.V 
671b4422bf3SAneesh Kumar K.V static p9_handler *virtio_9p_handler [] = {
672b4422bf3SAneesh Kumar K.V 	[P9_TVERSION] = virtio_p9_version,
673b4422bf3SAneesh Kumar K.V 	[P9_TATTACH]  = virtio_p9_attach,
674b4422bf3SAneesh Kumar K.V 	[P9_TSTAT]    = virtio_p9_stat,
675b4422bf3SAneesh Kumar K.V 	[P9_TCLUNK]   =	virtio_p9_clunk,
676b4422bf3SAneesh Kumar K.V 	[P9_TWALK]    =	virtio_p9_walk,
677b4422bf3SAneesh Kumar K.V 	[P9_TOPEN]    =	virtio_p9_open,
678b4422bf3SAneesh Kumar K.V 	[P9_TREAD]    = virtio_p9_read,
679b4422bf3SAneesh Kumar K.V 	[P9_TCREATE]  =	virtio_p9_create,
680b4422bf3SAneesh Kumar K.V 	[P9_TWSTAT]   =	virtio_p9_wstat,
681b4422bf3SAneesh Kumar K.V 	[P9_TREMOVE]  =	virtio_p9_remove,
682b4422bf3SAneesh Kumar K.V 	[P9_TWRITE]   =	virtio_p9_write,
683b4422bf3SAneesh Kumar K.V };
684b4422bf3SAneesh Kumar K.V 
685af045e53SAneesh Kumar K.V static struct p9_pdu *virtio_p9_pdu_init(struct kvm *kvm, struct virt_queue *vq)
686af045e53SAneesh Kumar K.V {
687af045e53SAneesh Kumar K.V 	struct p9_pdu *pdu = calloc(1, sizeof(*pdu));
688af045e53SAneesh Kumar K.V 	if (!pdu)
689af045e53SAneesh Kumar K.V 		return NULL;
690af045e53SAneesh Kumar K.V 
691bfc15268SAneesh Kumar K.V 	/* skip the pdu header p9_msg */
692bfc15268SAneesh Kumar K.V 	pdu->read_offset  = VIRTIO_P9_HDR_LEN;
693bfc15268SAneesh Kumar K.V 	pdu->write_offset = VIRTIO_P9_HDR_LEN;
694af045e53SAneesh Kumar K.V 	pdu->queue_head  = virt_queue__get_inout_iov(kvm, vq, pdu->in_iov,
695af045e53SAneesh Kumar K.V 						     pdu->out_iov,
696af045e53SAneesh Kumar K.V 						     &pdu->in_iov_cnt,
697af045e53SAneesh Kumar K.V 						     &pdu->out_iov_cnt);
698af045e53SAneesh Kumar K.V 	return pdu;
699af045e53SAneesh Kumar K.V }
700af045e53SAneesh Kumar K.V 
701af045e53SAneesh Kumar K.V static u8 virtio_p9_get_cmd(struct p9_pdu *pdu)
702af045e53SAneesh Kumar K.V {
703af045e53SAneesh Kumar K.V 	struct p9_msg *msg;
704af045e53SAneesh Kumar K.V 	/*
705af045e53SAneesh Kumar K.V 	 * we can peek directly into pdu for a u8
706af045e53SAneesh Kumar K.V 	 * value. The host endianess won't be an issue
707af045e53SAneesh Kumar K.V 	 */
708af045e53SAneesh Kumar K.V 	msg = pdu->out_iov[0].iov_base;
709af045e53SAneesh Kumar K.V 	return msg->cmd;
710af045e53SAneesh Kumar K.V }
711af045e53SAneesh Kumar K.V 
71297b408afSAneesh Kumar K.V static void virtio_p9_eopnotsupp(struct p9_dev *p9dev,
71397b408afSAneesh Kumar K.V 				 struct p9_pdu *pdu, u32 *outlen)
71497b408afSAneesh Kumar K.V {
71597b408afSAneesh Kumar K.V 	return virtio_p9_error_reply(p9dev, pdu, EOPNOTSUPP, outlen);
71697b408afSAneesh Kumar K.V }
71797b408afSAneesh Kumar K.V 
718b4422bf3SAneesh Kumar K.V static bool virtio_p9_do_io_request(struct kvm *kvm, struct p9_dev_job *job)
7191c7850f9SSasha Levin {
720af045e53SAneesh Kumar K.V 	u8 cmd;
721b4422bf3SAneesh Kumar K.V 	u32 len = 0;
722b4422bf3SAneesh Kumar K.V 	p9_handler *handler;
723b4422bf3SAneesh Kumar K.V 	struct p9_dev *p9dev;
724af045e53SAneesh Kumar K.V 	struct virt_queue *vq;
725af045e53SAneesh Kumar K.V 	struct p9_pdu *p9pdu;
7261c7850f9SSasha Levin 
727b4422bf3SAneesh Kumar K.V 	vq = job->vq;
728b4422bf3SAneesh Kumar K.V 	p9dev = job->p9dev;
7291c7850f9SSasha Levin 
730af045e53SAneesh Kumar K.V 	p9pdu = virtio_p9_pdu_init(kvm, vq);
731af045e53SAneesh Kumar K.V 	cmd = virtio_p9_get_cmd(p9pdu);
732af045e53SAneesh Kumar K.V 
733af045e53SAneesh Kumar K.V 	if (cmd >= ARRAY_SIZE(virtio_9p_handler) ||
734dd78d9eaSAneesh Kumar K.V 	    !virtio_9p_handler[cmd])
73597b408afSAneesh Kumar K.V 		handler = virtio_p9_eopnotsupp;
736dd78d9eaSAneesh Kumar K.V 	else
737af045e53SAneesh Kumar K.V 		handler = virtio_9p_handler[cmd];
738af045e53SAneesh Kumar K.V 	handler(p9dev, p9pdu, &len);
739af045e53SAneesh Kumar K.V 	virt_queue__set_used_elem(vq, p9pdu->queue_head, len);
740af045e53SAneesh Kumar K.V 	free(p9pdu);
7411c7850f9SSasha Levin 	return true;
7421c7850f9SSasha Levin }
7431c7850f9SSasha Levin 
7441c7850f9SSasha Levin static void virtio_p9_do_io(struct kvm *kvm, void *param)
7451c7850f9SSasha Levin {
746b4422bf3SAneesh Kumar K.V 	struct p9_dev_job *job = (struct p9_dev_job *)param;
747b4422bf3SAneesh Kumar K.V 	struct p9_dev *p9dev   = job->p9dev;
748b4422bf3SAneesh Kumar K.V 	struct virt_queue *vq  = job->vq;
7491c7850f9SSasha Levin 
7501c7850f9SSasha Levin 	while (virt_queue__available(vq)) {
751b4422bf3SAneesh Kumar K.V 		virtio_p9_do_io_request(kvm, job);
752b4422bf3SAneesh Kumar K.V 		virt_queue__trigger_irq(vq, p9dev->pci_hdr.irq_line,
753b4422bf3SAneesh Kumar K.V 					&p9dev->isr, kvm);
7541c7850f9SSasha Levin 	}
7551c7850f9SSasha Levin }
7561c7850f9SSasha Levin 
75760eb42d5SSasha Levin static void ioevent_callback(struct kvm *kvm, void *param)
75860eb42d5SSasha Levin {
75960eb42d5SSasha Levin 	struct p9_dev_job *job = param;
76060eb42d5SSasha Levin 
761df0c7f57SSasha Levin 	thread_pool__do_job(&job->job_id);
76260eb42d5SSasha Levin }
76360eb42d5SSasha Levin 
764b4422bf3SAneesh Kumar K.V static bool virtio_p9_pci_io_out(struct ioport *ioport, struct kvm *kvm,
765b4422bf3SAneesh Kumar K.V 				 u16 port, void *data, int size, u32 count)
7661c7850f9SSasha Levin {
7671c7850f9SSasha Levin 	unsigned long offset;
7681c7850f9SSasha Levin 	bool ret = true;
769b4422bf3SAneesh Kumar K.V 	struct p9_dev  *p9dev;
77060eb42d5SSasha Levin 	struct ioevent ioevent;
7711c7850f9SSasha Levin 
772b4422bf3SAneesh Kumar K.V 	p9dev = ioport->priv;
773b4422bf3SAneesh Kumar K.V 	offset = port - p9dev->base_addr;
7741c7850f9SSasha Levin 
7751c7850f9SSasha Levin 	switch (offset) {
7761c7850f9SSasha Levin 	case VIRTIO_MSI_QUEUE_VECTOR:
7771c7850f9SSasha Levin 	case VIRTIO_PCI_GUEST_FEATURES:
7781c7850f9SSasha Levin 		break;
7791c7850f9SSasha Levin 	case VIRTIO_PCI_QUEUE_PFN: {
7801c7850f9SSasha Levin 		void *p;
781b4422bf3SAneesh Kumar K.V 		struct p9_dev_job *job;
782b4422bf3SAneesh Kumar K.V 		struct virt_queue *queue;
7831c7850f9SSasha Levin 
784b4422bf3SAneesh Kumar K.V 		job			= &p9dev->jobs[p9dev->queue_selector];
785b4422bf3SAneesh Kumar K.V 		queue			= &p9dev->vqs[p9dev->queue_selector];
7861c7850f9SSasha Levin 		queue->pfn		= ioport__read32(data);
7871c7850f9SSasha Levin 		p			= guest_pfn_to_host(kvm, queue->pfn);
7881c7850f9SSasha Levin 
789612038c5SAneesh Kumar K.V 		vring_init(&queue->vring, VIRTQUEUE_NUM, p,
790b4422bf3SAneesh Kumar K.V 			   VIRTIO_PCI_VRING_ALIGN);
7911c7850f9SSasha Levin 
792b4422bf3SAneesh Kumar K.V 		*job			= (struct p9_dev_job) {
793b4422bf3SAneesh Kumar K.V 			.vq			= queue,
794b4422bf3SAneesh Kumar K.V 			.p9dev			= p9dev,
795b4422bf3SAneesh Kumar K.V 		};
796df0c7f57SSasha Levin 		thread_pool__init_job(&job->job_id, kvm, virtio_p9_do_io, job);
79760eb42d5SSasha Levin 
79860eb42d5SSasha Levin 		ioevent = (struct ioevent) {
79960eb42d5SSasha Levin 			.io_addr		= p9dev->base_addr + VIRTIO_PCI_QUEUE_NOTIFY,
80060eb42d5SSasha Levin 			.io_len			= sizeof(u16),
80160eb42d5SSasha Levin 			.fn			= ioevent_callback,
80260eb42d5SSasha Levin 			.datamatch		= p9dev->queue_selector,
80360eb42d5SSasha Levin 			.fn_ptr			= &p9dev->jobs[p9dev->queue_selector],
80460eb42d5SSasha Levin 			.fn_kvm			= kvm,
80560eb42d5SSasha Levin 			.fd			= eventfd(0, 0),
80660eb42d5SSasha Levin 		};
80760eb42d5SSasha Levin 
80860eb42d5SSasha Levin 		ioeventfd__add_event(&ioevent);
80960eb42d5SSasha Levin 
8101c7850f9SSasha Levin 		break;
8111c7850f9SSasha Levin 	}
8121c7850f9SSasha Levin 	case VIRTIO_PCI_QUEUE_SEL:
813b4422bf3SAneesh Kumar K.V 		p9dev->queue_selector	= ioport__read16(data);
8141c7850f9SSasha Levin 		break;
8151c7850f9SSasha Levin 	case VIRTIO_PCI_QUEUE_NOTIFY: {
8161c7850f9SSasha Levin 		u16 queue_index;
81760eb42d5SSasha Levin 
8181c7850f9SSasha Levin 		queue_index		= ioport__read16(data);
819df0c7f57SSasha Levin 		thread_pool__do_job(&p9dev->jobs[queue_index].job_id);
8201c7850f9SSasha Levin 		break;
8211c7850f9SSasha Levin 	}
8221c7850f9SSasha Levin 	case VIRTIO_PCI_STATUS:
823b4422bf3SAneesh Kumar K.V 		p9dev->status		= ioport__read8(data);
8241c7850f9SSasha Levin 		break;
8251c7850f9SSasha Levin 	case VIRTIO_MSI_CONFIG_VECTOR:
826b4422bf3SAneesh Kumar K.V 		p9dev->config_vector	= VIRTIO_MSI_NO_VECTOR;
8271c7850f9SSasha Levin 		break;
8281c7850f9SSasha Levin 	default:
8291c7850f9SSasha Levin 		ret			= false;
8301c7850f9SSasha Levin 		break;
8311c7850f9SSasha Levin 	};
8321c7850f9SSasha Levin 
8331c7850f9SSasha Levin 	return ret;
8341c7850f9SSasha Levin }
8351c7850f9SSasha Levin 
8361c7850f9SSasha Levin static struct ioport_operations virtio_p9_io_ops = {
8371c7850f9SSasha Levin 	.io_in				= virtio_p9_pci_io_in,
8381c7850f9SSasha Levin 	.io_out				= virtio_p9_pci_io_out,
8391c7850f9SSasha Levin };
8401c7850f9SSasha Levin 
84154f6802dSPekka Enberg int virtio_9p__init(struct kvm *kvm, const char *root, const char *tag_name)
8421c7850f9SSasha Levin {
843b4422bf3SAneesh Kumar K.V 	struct p9_dev *p9dev;
84454f6802dSPekka Enberg 	u8 pin, line, dev;
84554f6802dSPekka Enberg 	u16 p9_base_addr;
84654f6802dSPekka Enberg 	u32 i, root_len;
84754f6802dSPekka Enberg 	int err = 0;
8481c7850f9SSasha Levin 
849b4422bf3SAneesh Kumar K.V 	p9dev = calloc(1, sizeof(*p9dev));
850b4422bf3SAneesh Kumar K.V 	if (!p9dev)
85154f6802dSPekka Enberg 		return -ENOMEM;
85254f6802dSPekka Enberg 
853b4422bf3SAneesh Kumar K.V 	if (!tag_name)
854b4422bf3SAneesh Kumar K.V 		tag_name = VIRTIO_P9_DEFAULT_TAG;
85554f6802dSPekka Enberg 
856b4422bf3SAneesh Kumar K.V 	p9dev->config = calloc(1, sizeof(*p9dev->config) + strlen(tag_name) + 1);
85754f6802dSPekka Enberg 	if (p9dev->config == NULL) {
85854f6802dSPekka Enberg 		err = -ENOMEM;
859b4422bf3SAneesh Kumar K.V 		goto free_p9dev;
86054f6802dSPekka Enberg 	}
8611c7850f9SSasha Levin 
862b4422bf3SAneesh Kumar K.V 	strcpy(p9dev->root_dir, root);
8631c7850f9SSasha Levin 	root_len = strlen(root);
8641c7850f9SSasha Levin 	/*
8651c7850f9SSasha Levin 	 * We prefix the full path in all fids, This allows us to get the
8661c7850f9SSasha Levin 	 * absolute path of an fid without playing with strings.
8671c7850f9SSasha Levin 	 */
8681c7850f9SSasha Levin 	for (i = 0; i < VIRTIO_P9_MAX_FID; i++) {
869b4422bf3SAneesh Kumar K.V 		strcpy(p9dev->fids[i].abs_path, root);
870b4422bf3SAneesh Kumar K.V 		p9dev->fids[i].path = p9dev->fids[i].abs_path + root_len;
8711c7850f9SSasha Levin 	}
872b4422bf3SAneesh Kumar K.V 	p9dev->config->tag_len = strlen(tag_name);
87354f6802dSPekka Enberg 	if (p9dev->config->tag_len > MAX_TAG_LEN) {
87454f6802dSPekka Enberg 		err = -EINVAL;
875b4422bf3SAneesh Kumar K.V 		goto free_p9dev_config;
87654f6802dSPekka Enberg 	}
8771c7850f9SSasha Levin 
878b4422bf3SAneesh Kumar K.V 	memcpy(p9dev->config->tag, tag_name, strlen(tag_name));
879b4422bf3SAneesh Kumar K.V 	p9dev->features |= 1 << VIRTIO_9P_MOUNT_TAG;
8801c7850f9SSasha Levin 
88154f6802dSPekka Enberg 	err = irq__register_device(VIRTIO_ID_9P, &dev, &pin, &line);
88254f6802dSPekka Enberg 	if (err < 0)
883b4422bf3SAneesh Kumar K.V 		goto free_p9dev_config;
8841c7850f9SSasha Levin 
88554f6802dSPekka Enberg 	p9_base_addr = ioport__register(IOPORT_EMPTY, &virtio_p9_io_ops, IOPORT_SIZE, p9dev);
88654f6802dSPekka Enberg 
887b4422bf3SAneesh Kumar K.V 	p9dev->base_addr = p9_base_addr;
88854f6802dSPekka Enberg 
889b4422bf3SAneesh Kumar K.V 	p9dev->pci_hdr = (struct pci_device_header) {
890b4422bf3SAneesh Kumar K.V 		.vendor_id		= PCI_VENDOR_ID_REDHAT_QUMRANET,
891b4422bf3SAneesh Kumar K.V 		.device_id		= PCI_DEVICE_ID_VIRTIO_P9,
892b4422bf3SAneesh Kumar K.V 		.header_type		= PCI_HEADER_TYPE_NORMAL,
893b4422bf3SAneesh Kumar K.V 		.revision_id		= 0,
894b4422bf3SAneesh Kumar K.V 		.class			= 0x010000,
895b4422bf3SAneesh Kumar K.V 		.subsys_vendor_id	= PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET,
896b4422bf3SAneesh Kumar K.V 		.subsys_id		= VIRTIO_ID_9P,
897b4422bf3SAneesh Kumar K.V 		.irq_pin		= pin,
898b4422bf3SAneesh Kumar K.V 		.irq_line		= line,
899b4422bf3SAneesh Kumar K.V 		.bar[0]			= p9_base_addr | PCI_BASE_ADDRESS_SPACE_IO,
900b4422bf3SAneesh Kumar K.V 	};
901b4422bf3SAneesh Kumar K.V 	pci__register(&p9dev->pci_hdr, dev);
902b4422bf3SAneesh Kumar K.V 
90354f6802dSPekka Enberg 	return err;
90454f6802dSPekka Enberg 
905b4422bf3SAneesh Kumar K.V free_p9dev_config:
906b4422bf3SAneesh Kumar K.V 	free(p9dev->config);
907b4422bf3SAneesh Kumar K.V free_p9dev:
908b4422bf3SAneesh Kumar K.V 	free(p9dev);
90954f6802dSPekka Enberg 	return err;
9101c7850f9SSasha Levin }
911