xref: /kvmtool/virtio/9p.c (revision 6c8ca053403bb560c54c58de7908d60b4e8616d6)
11c7850f9SSasha Levin #include "kvm/virtio-9p.h"
21c7850f9SSasha Levin #include "kvm/virtio-pci-dev.h"
31c7850f9SSasha Levin #include "kvm/virtio.h"
41c7850f9SSasha Levin #include "kvm/ioport.h"
51c7850f9SSasha Levin #include "kvm/mutex.h"
61c7850f9SSasha Levin #include "kvm/util.h"
71c7850f9SSasha Levin #include "kvm/kvm.h"
81c7850f9SSasha Levin #include "kvm/pci.h"
91c7850f9SSasha Levin #include "kvm/threadpool.h"
101c7850f9SSasha Levin #include "kvm/irq.h"
1160eb42d5SSasha Levin #include "kvm/ioeventfd.h"
121c7850f9SSasha Levin 
131c7850f9SSasha Levin #include <fcntl.h>
141c7850f9SSasha Levin #include <sys/types.h>
151c7850f9SSasha Levin #include <sys/stat.h>
161c7850f9SSasha Levin #include <pthread.h>
171c7850f9SSasha Levin #include <dirent.h>
181c7850f9SSasha Levin 
192daa28d4SAneesh Kumar K.V #include <linux/virtio_ring.h>
202daa28d4SAneesh Kumar K.V #include <linux/virtio_9p.h>
212daa28d4SAneesh Kumar K.V #include <net/9p/9p.h>
222daa28d4SAneesh Kumar K.V 
231c7850f9SSasha Levin #define NUM_VIRT_QUEUES		1
24612038c5SAneesh Kumar K.V #define VIRTQUEUE_NUM		128
25b4422bf3SAneesh Kumar K.V #define	VIRTIO_P9_DEFAULT_TAG	"kvm_9p"
261c7850f9SSasha Levin #define VIRTIO_P9_HDR_LEN	(sizeof(u32)+sizeof(u8)+sizeof(u16))
271c7850f9SSasha Levin #define VIRTIO_P9_MAX_FID	128
281c7850f9SSasha Levin #define VIRTIO_P9_VERSION	"9P2000"
29b4422bf3SAneesh Kumar K.V #define MAX_TAG_LEN		32
30b4422bf3SAneesh Kumar K.V 
311c7850f9SSasha Levin 
321c7850f9SSasha Levin struct p9_msg {
331c7850f9SSasha Levin 	u32			size;
341c7850f9SSasha Levin 	u8			cmd;
351c7850f9SSasha Levin 	u16			tag;
361c7850f9SSasha Levin 	u8			msg[0];
371c7850f9SSasha Levin } __attribute__((packed));
381c7850f9SSasha Levin 
391c7850f9SSasha Levin struct p9_fid {
401c7850f9SSasha Levin 	u32			fid;
411c7850f9SSasha Levin 	u8			is_dir;
421c7850f9SSasha Levin 	char			abs_path[PATH_MAX];
431c7850f9SSasha Levin 	char			*path;
441c7850f9SSasha Levin 	DIR			*dir;
451c7850f9SSasha Levin 	int			fd;
461c7850f9SSasha Levin };
471c7850f9SSasha Levin 
48b4422bf3SAneesh Kumar K.V struct p9_dev_job {
49b4422bf3SAneesh Kumar K.V 	struct virt_queue		*vq;
50b4422bf3SAneesh Kumar K.V 	struct p9_dev			*p9dev;
51b4422bf3SAneesh Kumar K.V 	void				*job_id;
521c7850f9SSasha Levin };
531c7850f9SSasha Levin 
541c7850f9SSasha Levin struct p9_dev {
551c7850f9SSasha Levin 	u8			status;
561c7850f9SSasha Levin 	u8			isr;
571c7850f9SSasha Levin 	u16			config_vector;
581c7850f9SSasha Levin 	u32			features;
591c7850f9SSasha Levin 	struct virtio_9p_config	*config;
60f884f920SSasha Levin 	u16			base_addr;
611c7850f9SSasha Levin 
621c7850f9SSasha Levin 	/* virtio queue */
631c7850f9SSasha Levin 	u16			queue_selector;
641c7850f9SSasha Levin 	struct virt_queue	vqs[NUM_VIRT_QUEUES];
65b4422bf3SAneesh Kumar K.V 	struct p9_dev_job	jobs[NUM_VIRT_QUEUES];
661c7850f9SSasha Levin 	struct p9_fid		fids[VIRTIO_P9_MAX_FID];
671c7850f9SSasha Levin 	char			root_dir[PATH_MAX];
68b4422bf3SAneesh Kumar K.V 	struct pci_device_header pci_hdr;
691c7850f9SSasha Levin };
701c7850f9SSasha Levin 
71af045e53SAneesh Kumar K.V struct p9_pdu {
72af045e53SAneesh Kumar K.V 	u32 queue_head;
73af045e53SAneesh Kumar K.V 	int offset;
74af045e53SAneesh Kumar K.V 	u16 out_iov_cnt;
75af045e53SAneesh Kumar K.V 	u16 in_iov_cnt;
76af045e53SAneesh Kumar K.V 	struct iovec in_iov[VIRTQUEUE_NUM];
77af045e53SAneesh Kumar K.V 	struct iovec out_iov[VIRTQUEUE_NUM];
78af045e53SAneesh Kumar K.V };
79af045e53SAneesh Kumar K.V 
801c7850f9SSasha Levin /* Warning: Immediately use value returned from this function */
81b4422bf3SAneesh Kumar K.V static const char *rel_to_abs(struct p9_dev *p9dev,
82b4422bf3SAneesh Kumar K.V 			      const char *path, char *abs_path)
831c7850f9SSasha Levin {
84b4422bf3SAneesh Kumar K.V 	sprintf(abs_path, "%s/%s", p9dev->root_dir, path);
851c7850f9SSasha Levin 
861c7850f9SSasha Levin 	return abs_path;
871c7850f9SSasha Levin }
881c7850f9SSasha Levin 
89b4422bf3SAneesh Kumar K.V static bool virtio_p9_dev_in(struct p9_dev *p9dev, void *data,
90b4422bf3SAneesh Kumar K.V 			     unsigned long offset,
91b4422bf3SAneesh Kumar K.V 			     int size, u32 count)
921c7850f9SSasha Levin {
93b4422bf3SAneesh Kumar K.V 	u8 *config_space = (u8 *) p9dev->config;
941c7850f9SSasha Levin 
951c7850f9SSasha Levin 	if (size != 1 || count != 1)
961c7850f9SSasha Levin 		return false;
971c7850f9SSasha Levin 
981c7850f9SSasha Levin 	ioport__write8(data, config_space[offset - VIRTIO_MSI_CONFIG_VECTOR]);
991c7850f9SSasha Levin 
1001c7850f9SSasha Levin 	return true;
1011c7850f9SSasha Levin }
1021c7850f9SSasha Levin 
103b4422bf3SAneesh Kumar K.V static bool virtio_p9_pci_io_in(struct ioport *ioport, struct kvm *kvm,
104b4422bf3SAneesh Kumar K.V 				u16 port, void *data, int size, u32 count)
1051c7850f9SSasha Levin {
1061c7850f9SSasha Levin 	bool ret = true;
107b4422bf3SAneesh Kumar K.V 	unsigned long offset;
108b4422bf3SAneesh Kumar K.V 	struct p9_dev *p9dev = ioport->priv;
1091c7850f9SSasha Levin 
110b4422bf3SAneesh Kumar K.V 
111b4422bf3SAneesh Kumar K.V 	offset = port - p9dev->base_addr;
1121c7850f9SSasha Levin 
1131c7850f9SSasha Levin 	switch (offset) {
1141c7850f9SSasha Levin 	case VIRTIO_PCI_HOST_FEATURES:
115b4422bf3SAneesh Kumar K.V 		ioport__write32(data, p9dev->features);
1161c7850f9SSasha Levin 		ret = true;
1171c7850f9SSasha Levin 		break;
1181c7850f9SSasha Levin 	case VIRTIO_PCI_GUEST_FEATURES:
1191c7850f9SSasha Levin 	case VIRTIO_PCI_QUEUE_SEL:
1201c7850f9SSasha Levin 	case VIRTIO_PCI_QUEUE_NOTIFY:
1211c7850f9SSasha Levin 		ret = false;
1221c7850f9SSasha Levin 		break;
1231c7850f9SSasha Levin 	case VIRTIO_PCI_QUEUE_PFN:
124b4422bf3SAneesh Kumar K.V 		ioport__write32(data, p9dev->vqs[p9dev->queue_selector].pfn);
1251c7850f9SSasha Levin 		break;
1261c7850f9SSasha Levin 	case VIRTIO_PCI_QUEUE_NUM:
127612038c5SAneesh Kumar K.V 		ioport__write16(data, VIRTQUEUE_NUM);
1281c7850f9SSasha Levin 		break;
1291c7850f9SSasha Levin 	case VIRTIO_PCI_STATUS:
130b4422bf3SAneesh Kumar K.V 		ioport__write8(data, p9dev->status);
1311c7850f9SSasha Levin 		break;
1321c7850f9SSasha Levin 	case VIRTIO_PCI_ISR:
133b4422bf3SAneesh Kumar K.V 		ioport__write8(data, p9dev->isr);
134b4422bf3SAneesh Kumar K.V 		kvm__irq_line(kvm, p9dev->pci_hdr.irq_line, VIRTIO_IRQ_LOW);
135b4422bf3SAneesh Kumar K.V 		p9dev->isr = VIRTIO_IRQ_LOW;
1361c7850f9SSasha Levin 		break;
1371c7850f9SSasha Levin 	default:
138b4422bf3SAneesh Kumar K.V 		ret = virtio_p9_dev_in(p9dev, data, offset, size, count);
1391c7850f9SSasha Levin 		break;
1401c7850f9SSasha Levin 	};
1411c7850f9SSasha Levin 
1421c7850f9SSasha Levin 	return ret;
1431c7850f9SSasha Levin }
1441c7850f9SSasha Levin 
1451c7850f9SSasha Levin static int omode2uflags(u8 mode)
1461c7850f9SSasha Levin {
1471c7850f9SSasha Levin 	int ret = 0;
1481c7850f9SSasha Levin 
1491c7850f9SSasha Levin 	/* Basic open modes are same as uflags */
1501c7850f9SSasha Levin 	ret = mode & 3;
1511c7850f9SSasha Levin 
1521c7850f9SSasha Levin 	/* Everything else is different */
1531c7850f9SSasha Levin 	if (mode & P9_OTRUNC)
1541c7850f9SSasha Levin 		ret |= O_TRUNC;
1551c7850f9SSasha Levin 
1561c7850f9SSasha Levin 	if (mode & P9_OAPPEND)
1571c7850f9SSasha Levin 		ret |= O_APPEND;
1581c7850f9SSasha Levin 
1591c7850f9SSasha Levin 	if (mode & P9_OEXCL)
1601c7850f9SSasha Levin 		ret |= O_EXCL;
1611c7850f9SSasha Levin 
1621c7850f9SSasha Levin 	return ret;
1631c7850f9SSasha Levin }
1641c7850f9SSasha Levin 
1651c7850f9SSasha Levin static void st2qid(struct stat *st, struct p9_qid *qid)
1661c7850f9SSasha Levin {
1671c7850f9SSasha Levin 	*qid = (struct p9_qid) {
1681c7850f9SSasha Levin 		.path		= st->st_ino,
1691c7850f9SSasha Levin 		.version	= st->st_mtime,
1701c7850f9SSasha Levin 	};
1711c7850f9SSasha Levin 
1721c7850f9SSasha Levin 	if (S_ISDIR(st->st_mode))
1731c7850f9SSasha Levin 		qid->type	|= P9_QTDIR;
1741c7850f9SSasha Levin }
1751c7850f9SSasha Levin 
176b4422bf3SAneesh Kumar K.V static void close_fid(struct p9_dev *p9dev, u32 fid)
1771c7850f9SSasha Levin {
178b4422bf3SAneesh Kumar K.V 	if (p9dev->fids[fid].fd > 0) {
179b4422bf3SAneesh Kumar K.V 		close(p9dev->fids[fid].fd);
180b4422bf3SAneesh Kumar K.V 		p9dev->fids[fid].fd = -1;
1811c7850f9SSasha Levin 	}
182b4422bf3SAneesh Kumar K.V 	if (p9dev->fids[fid].dir) {
183b4422bf3SAneesh Kumar K.V 		closedir(p9dev->fids[fid].dir);
184b4422bf3SAneesh Kumar K.V 		p9dev->fids[fid].dir = NULL;
1851c7850f9SSasha Levin 	}
1861c7850f9SSasha Levin }
1871c7850f9SSasha Levin 
1881c7850f9SSasha Levin static void set_p9msg_hdr(struct p9_msg *msg, u32 size, u8 cmd, u16 tag)
1891c7850f9SSasha Levin {
1901c7850f9SSasha Levin 	*msg = (struct p9_msg) {
1911c7850f9SSasha Levin 		.size	= size,
1921c7850f9SSasha Levin 		.tag	= tag,
1931c7850f9SSasha Levin 		.cmd	= cmd,
1941c7850f9SSasha Levin 	};
1951c7850f9SSasha Levin }
1961c7850f9SSasha Levin 
1976b163a87SAneesh Kumar K.V static u16 virtio_p9_update_iov_cnt(struct iovec iov[], u32 count, int iov_cnt)
1986b163a87SAneesh Kumar K.V {
1996b163a87SAneesh Kumar K.V 	int i;
2006b163a87SAneesh Kumar K.V 	u32 total = 0;
2016b163a87SAneesh Kumar K.V 	for (i = 0; (i < iov_cnt) && (total < count); i++) {
2026b163a87SAneesh Kumar K.V 		if (total + iov[i].iov_len > count) {
2036b163a87SAneesh Kumar K.V 			/* we don't need this iov fully */
2046b163a87SAneesh Kumar K.V 			iov[i].iov_len -= ((total + iov[i].iov_len) - count);
2056b163a87SAneesh Kumar K.V 			i++;
2066b163a87SAneesh Kumar K.V 			break;
2076b163a87SAneesh Kumar K.V 		}
2086b163a87SAneesh Kumar K.V 		total += iov[i].iov_len;
2096b163a87SAneesh Kumar K.V 	}
2106b163a87SAneesh Kumar K.V 	return i;
2116b163a87SAneesh Kumar K.V }
2126b163a87SAneesh Kumar K.V 
213eee1ba8eSAneesh Kumar K.V static void virtio_p9_error_reply(struct p9_dev *p9dev,
214eee1ba8eSAneesh Kumar K.V 				  struct p9_pdu *pdu, int err, u32 *outlen)
215eee1ba8eSAneesh Kumar K.V {
216eee1ba8eSAneesh Kumar K.V 	char *err_str;
217eee1ba8eSAneesh Kumar K.V 	struct p9_msg *inmsg  = pdu->in_iov[0].iov_base;
218eee1ba8eSAneesh Kumar K.V 	struct p9_msg *outmsg = pdu->out_iov[0].iov_base;
219eee1ba8eSAneesh Kumar K.V 	struct p9_rerror *rerror  = (struct p9_rerror *)inmsg->msg;
220eee1ba8eSAneesh Kumar K.V 
221eee1ba8eSAneesh Kumar K.V 	err_str = strerror(err);
222eee1ba8eSAneesh Kumar K.V 	rerror->error.len = strlen(err_str);
223eee1ba8eSAneesh Kumar K.V 	memcpy(&rerror->error.str, err_str, rerror->error.len);
224eee1ba8eSAneesh Kumar K.V 
225eee1ba8eSAneesh Kumar K.V 	*outlen = VIRTIO_P9_HDR_LEN + rerror->error.len + sizeof(u16);
226eee1ba8eSAneesh Kumar K.V 	set_p9msg_hdr(inmsg, *outlen, P9_RERROR, outmsg->tag);
227eee1ba8eSAneesh Kumar K.V }
228eee1ba8eSAneesh Kumar K.V 
229ead43b01SAneesh Kumar K.V static void virtio_p9_version(struct p9_dev *p9dev,
230af045e53SAneesh Kumar K.V 			      struct p9_pdu *pdu, u32 *outlen)
2311c7850f9SSasha Levin {
232af045e53SAneesh Kumar K.V 	struct p9_msg *inmsg  = pdu->in_iov[0].iov_base;
233af045e53SAneesh Kumar K.V 	struct p9_msg *outmsg = pdu->out_iov[0].iov_base;
234af045e53SAneesh Kumar K.V 	struct p9_rversion *rversion = (struct p9_rversion *)inmsg->msg;
2351c7850f9SSasha Levin 
2361c7850f9SSasha Levin 	rversion->msize		= 4096;
2371c7850f9SSasha Levin 	rversion->version.len	= strlen(VIRTIO_P9_VERSION);
2381c7850f9SSasha Levin 	memcpy(&rversion->version.str, VIRTIO_P9_VERSION, rversion->version.len);
2391c7850f9SSasha Levin 
240b4422bf3SAneesh Kumar K.V 	*outlen = VIRTIO_P9_HDR_LEN +
241b4422bf3SAneesh Kumar K.V 		rversion->version.len + sizeof(u16) + sizeof(u32);
242af045e53SAneesh Kumar K.V 	set_p9msg_hdr(inmsg, *outlen, P9_RVERSION, outmsg->tag);
2431c7850f9SSasha Levin 
244ead43b01SAneesh Kumar K.V 	return;
2451c7850f9SSasha Levin }
2461c7850f9SSasha Levin 
247ead43b01SAneesh Kumar K.V static void virtio_p9_clunk(struct p9_dev *p9dev,
248af045e53SAneesh Kumar K.V 			    struct p9_pdu *pdu, u32 *outlen)
2491c7850f9SSasha Levin {
250af045e53SAneesh Kumar K.V 	struct p9_msg *inmsg  = pdu->in_iov[0].iov_base;
251af045e53SAneesh Kumar K.V 	struct p9_msg *outmsg = pdu->out_iov[0].iov_base;
252af045e53SAneesh Kumar K.V 	struct p9_tclunk *tclunk = (struct p9_tclunk *)outmsg->msg;
2531c7850f9SSasha Levin 
254b4422bf3SAneesh Kumar K.V 	close_fid(p9dev, tclunk->fid);
2551c7850f9SSasha Levin 
2561c7850f9SSasha Levin 	*outlen = VIRTIO_P9_HDR_LEN;
257af045e53SAneesh Kumar K.V 	set_p9msg_hdr(inmsg, *outlen, P9_RCLUNK, outmsg->tag);
2581c7850f9SSasha Levin 
259ead43b01SAneesh Kumar K.V 	return;
2601c7850f9SSasha Levin }
2611c7850f9SSasha Levin 
262ead43b01SAneesh Kumar K.V static void virtio_p9_open(struct p9_dev *p9dev,
263af045e53SAneesh Kumar K.V 			   struct p9_pdu *pdu, u32 *outlen)
2641c7850f9SSasha Levin {
2651c7850f9SSasha Levin 	struct stat st;
266af045e53SAneesh Kumar K.V 	struct p9_msg *inmsg  = pdu->in_iov[0].iov_base;
267af045e53SAneesh Kumar K.V 	struct p9_msg *outmsg = pdu->out_iov[0].iov_base;
268af045e53SAneesh Kumar K.V 	struct p9_topen *topen	= (struct p9_topen *)outmsg->msg;
269af045e53SAneesh Kumar K.V 	struct p9_ropen *ropen	= (struct p9_ropen *)inmsg->msg;
270af045e53SAneesh Kumar K.V 	struct p9_fid *new_fid	= &p9dev->fids[topen->fid];
2711c7850f9SSasha Levin 
27230204a77SAneesh Kumar K.V 	if (lstat(new_fid->abs_path, &st) < 0)
273eee1ba8eSAneesh Kumar K.V 		goto err_out;
2741c7850f9SSasha Levin 
2751c7850f9SSasha Levin 	st2qid(&st, &ropen->qid);
2761c7850f9SSasha Levin 	ropen->iounit = 0;
2771c7850f9SSasha Levin 
278eee1ba8eSAneesh Kumar K.V 	if (new_fid->is_dir) {
2791c7850f9SSasha Levin 		new_fid->dir = opendir(new_fid->abs_path);
280eee1ba8eSAneesh Kumar K.V 		if (!new_fid->dir)
281eee1ba8eSAneesh Kumar K.V 			goto err_out;
282eee1ba8eSAneesh Kumar K.V 	} else {
283eee1ba8eSAneesh Kumar K.V 		new_fid->fd  = open(new_fid->abs_path,
284eee1ba8eSAneesh Kumar K.V 				   omode2uflags(topen->mode) | O_NOFOLLOW);
285eee1ba8eSAneesh Kumar K.V 		if (new_fid->fd < 0)
286eee1ba8eSAneesh Kumar K.V 			goto err_out;
287eee1ba8eSAneesh Kumar K.V 	}
2881c7850f9SSasha Levin 	*outlen = VIRTIO_P9_HDR_LEN + sizeof(*ropen);
289af045e53SAneesh Kumar K.V 	set_p9msg_hdr(inmsg, *outlen, P9_ROPEN, outmsg->tag);
290ead43b01SAneesh Kumar K.V 	return;
291eee1ba8eSAneesh Kumar K.V err_out:
292eee1ba8eSAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
293ead43b01SAneesh Kumar K.V 	return;
2941c7850f9SSasha Levin }
2951c7850f9SSasha Levin 
296ead43b01SAneesh Kumar K.V static void virtio_p9_create(struct p9_dev *p9dev,
297af045e53SAneesh Kumar K.V 			     struct p9_pdu *pdu, u32 *outlen)
2981c7850f9SSasha Levin {
2991c7850f9SSasha Levin 	u8 mode;
3001c7850f9SSasha Levin 	u32 perm;
301af045e53SAneesh Kumar K.V 	struct stat st;
302af045e53SAneesh Kumar K.V 	struct p9_msg *inmsg  = pdu->in_iov[0].iov_base;
303af045e53SAneesh Kumar K.V 	struct p9_msg *outmsg = pdu->out_iov[0].iov_base;
304af045e53SAneesh Kumar K.V 	struct p9_tcreate *tcreate	= (struct p9_tcreate *)outmsg->msg;
305af045e53SAneesh Kumar K.V 	struct p9_rcreate *rcreate	= (struct p9_rcreate *)inmsg->msg;
306af045e53SAneesh Kumar K.V 	struct p9_fid *fid		= &p9dev->fids[tcreate->fid];
307af045e53SAneesh Kumar K.V 
3081c7850f9SSasha Levin 
3091c7850f9SSasha Levin 	rcreate->iounit = 0;
3101c7850f9SSasha Levin 
3111c7850f9SSasha Levin 	/* Get last byte of the variable length struct */
312af045e53SAneesh Kumar K.V 	mode = *((u8 *)outmsg + outmsg->size - 1);
313af045e53SAneesh Kumar K.V 	perm = *(u32 *)((u8 *)outmsg + outmsg->size - 5);
3141c7850f9SSasha Levin 
3151c7850f9SSasha Levin 	sprintf(fid->path, "%s/%.*s", fid->path, tcreate->name.len, (char *)&tcreate->name.str);
3161c7850f9SSasha Levin 
317b4422bf3SAneesh Kumar K.V 	close_fid(p9dev, tcreate->fid);
3181c7850f9SSasha Levin 
3191c7850f9SSasha Levin 	if (perm & P9_DMDIR) {
3201c7850f9SSasha Levin 		mkdir(fid->abs_path, perm & 0xFFFF);
3211c7850f9SSasha Levin 		fid->dir = opendir(fid->abs_path);
3221c7850f9SSasha Levin 		fid->is_dir = 1;
3231c7850f9SSasha Levin 	} else {
3241c7850f9SSasha Levin 		fid->fd = open(fid->abs_path, omode2uflags(mode) | O_CREAT, 0777);
3251c7850f9SSasha Levin 	}
3261c7850f9SSasha Levin 
32730204a77SAneesh Kumar K.V 	if (lstat(fid->abs_path, &st) < 0)
328*6c8ca053SAneesh Kumar K.V 		goto err_out;
3291c7850f9SSasha Levin 
3301c7850f9SSasha Levin 	st2qid(&st, &rcreate->qid);
3311c7850f9SSasha Levin 
3321c7850f9SSasha Levin 	*outlen = VIRTIO_P9_HDR_LEN + sizeof(*rcreate);
333af045e53SAneesh Kumar K.V 	set_p9msg_hdr(inmsg, *outlen, P9_RCREATE, outmsg->tag);
334*6c8ca053SAneesh Kumar K.V 	return;
335*6c8ca053SAneesh Kumar K.V err_out:
336*6c8ca053SAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
337ead43b01SAneesh Kumar K.V 	return;
3381c7850f9SSasha Levin }
3391c7850f9SSasha Levin 
340ead43b01SAneesh Kumar K.V static void virtio_p9_walk(struct p9_dev *p9dev,
341af045e53SAneesh Kumar K.V 			   struct p9_pdu *pdu, u32 *outlen)
3421c7850f9SSasha Levin {
343af045e53SAneesh Kumar K.V 	u8 i;
344af045e53SAneesh Kumar K.V 	struct p9_msg *inmsg  = pdu->in_iov[0].iov_base;
345af045e53SAneesh Kumar K.V 	struct p9_msg *outmsg = pdu->out_iov[0].iov_base;
346af045e53SAneesh Kumar K.V 	struct p9_twalk *twalk	= (struct p9_twalk *)outmsg->msg;
347af045e53SAneesh Kumar K.V 	struct p9_rwalk *rwalk	= (struct p9_rwalk *)inmsg->msg;
3481c7850f9SSasha Levin 	struct p9_str *str	= twalk->wnames;
349b4422bf3SAneesh Kumar K.V 	struct p9_fid *new_fid	= &p9dev->fids[twalk->newfid];
350af045e53SAneesh Kumar K.V 
3511c7850f9SSasha Levin 
3521c7850f9SSasha Levin 	rwalk->nwqid = 0;
3531c7850f9SSasha Levin 	if (twalk->nwname) {
354b4422bf3SAneesh Kumar K.V 		struct p9_fid *fid = &p9dev->fids[twalk->fid];
3551c7850f9SSasha Levin 
3561c7850f9SSasha Levin 		for (i = 0; i < twalk->nwname; i++) {
3571c7850f9SSasha Levin 			char tmp[PATH_MAX] = {0};
3581c7850f9SSasha Levin 			char full_path[PATH_MAX];
3591c7850f9SSasha Levin 			struct stat st;
3601c7850f9SSasha Levin 
3611c7850f9SSasha Levin 			/* Format the new path we're 'walk'ing into */
362af045e53SAneesh Kumar K.V 			sprintf(tmp, "%s/%.*s", fid->path,
363af045e53SAneesh Kumar K.V 				str->len, (char *)&str->str);
3641c7850f9SSasha Levin 
36530204a77SAneesh Kumar K.V 			if (lstat(rel_to_abs(p9dev, tmp, full_path), &st) < 0)
366*6c8ca053SAneesh Kumar K.V 				goto err_out;
3671c7850f9SSasha Levin 
3681c7850f9SSasha Levin 			st2qid(&st, &rwalk->wqids[i]);
3691c7850f9SSasha Levin 			new_fid->is_dir = S_ISDIR(st.st_mode);
3701c7850f9SSasha Levin 			strcpy(new_fid->path, tmp);
3711c7850f9SSasha Levin 			new_fid->fid = twalk->newfid;
3721c7850f9SSasha Levin 			rwalk->nwqid++;
3731c7850f9SSasha Levin 		}
3741c7850f9SSasha Levin 	} else {
375b4422bf3SAneesh Kumar K.V 		new_fid->is_dir = p9dev->fids[twalk->fid].is_dir;
376b4422bf3SAneesh Kumar K.V 		strcpy(new_fid->path, p9dev->fids[twalk->fid].path);
3771c7850f9SSasha Levin 		new_fid->fid	= twalk->newfid;
3781c7850f9SSasha Levin 	}
3791c7850f9SSasha Levin 
380af045e53SAneesh Kumar K.V 	*outlen = VIRTIO_P9_HDR_LEN + sizeof(u16) +
381af045e53SAneesh Kumar K.V 		sizeof(struct p9_qid)*rwalk->nwqid;
382af045e53SAneesh Kumar K.V 	set_p9msg_hdr(inmsg, *outlen, P9_RWALK, outmsg->tag);
383*6c8ca053SAneesh Kumar K.V 	return;
384*6c8ca053SAneesh Kumar K.V err_out:
385*6c8ca053SAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
386ead43b01SAneesh Kumar K.V 	return;
3871c7850f9SSasha Levin }
3881c7850f9SSasha Levin 
389ead43b01SAneesh Kumar K.V static void virtio_p9_attach(struct p9_dev *p9dev,
390af045e53SAneesh Kumar K.V 			     struct p9_pdu *pdu, u32 *outlen)
3911c7850f9SSasha Levin {
392af045e53SAneesh Kumar K.V 	u32 i;
3931c7850f9SSasha Levin 	struct stat st;
3941c7850f9SSasha Levin 	struct p9_fid *fid;
395af045e53SAneesh Kumar K.V 	struct p9_msg *inmsg  = pdu->in_iov[0].iov_base;
396af045e53SAneesh Kumar K.V 	struct p9_msg *outmsg = pdu->out_iov[0].iov_base;
397af045e53SAneesh Kumar K.V 	struct p9_rattach *rattach = (struct p9_rattach *)inmsg->msg;
398af045e53SAneesh Kumar K.V 	struct p9_tattach *tattach = (struct p9_tattach *)outmsg->msg;
3991c7850f9SSasha Levin 
4001c7850f9SSasha Levin 	/* Reset everything */
4011c7850f9SSasha Levin 	for (i = 0; i < VIRTIO_P9_MAX_FID; i++)
402b4422bf3SAneesh Kumar K.V 		p9dev->fids[i].fid = P9_NOFID;
4031c7850f9SSasha Levin 
40430204a77SAneesh Kumar K.V 	if (lstat(p9dev->root_dir, &st) < 0)
405*6c8ca053SAneesh Kumar K.V 		goto err_out;
4061c7850f9SSasha Levin 
4071c7850f9SSasha Levin 	st2qid(&st, &rattach->qid);
4081c7850f9SSasha Levin 
409b4422bf3SAneesh Kumar K.V 	fid = &p9dev->fids[tattach->fid];
4101c7850f9SSasha Levin 	fid->fid = tattach->fid;
4111c7850f9SSasha Levin 	fid->is_dir = 1;
4121c7850f9SSasha Levin 	strcpy(fid->path, "/");
4131c7850f9SSasha Levin 
4141c7850f9SSasha Levin 	*outlen = VIRTIO_P9_HDR_LEN + sizeof(*rattach);
415af045e53SAneesh Kumar K.V 	set_p9msg_hdr(inmsg, *outlen, P9_RATTACH, outmsg->tag);
416*6c8ca053SAneesh Kumar K.V 	return;
417*6c8ca053SAneesh Kumar K.V err_out:
418*6c8ca053SAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
419ead43b01SAneesh Kumar K.V 	return;
4201c7850f9SSasha Levin }
4211c7850f9SSasha Levin 
422b4422bf3SAneesh Kumar K.V static u32 virtio_p9_fill_stat(struct p9_dev *p9dev, const char *name,
423b4422bf3SAneesh Kumar K.V 			       struct stat *st, struct p9_rstat *rstat)
4241c7850f9SSasha Levin {
4251c7850f9SSasha Levin 	struct p9_str *str;
4261c7850f9SSasha Levin 
4271c7850f9SSasha Levin 	rstat->stat.type = 0;
4281c7850f9SSasha Levin 	rstat->stat.dev = 0;
4291c7850f9SSasha Levin 	st2qid(st, &rstat->stat.qid);
4301c7850f9SSasha Levin 	rstat->stat.mode = st->st_mode;
4311c7850f9SSasha Levin 	rstat->stat.length = st->st_size;
4321c7850f9SSasha Levin 	if (S_ISDIR(st->st_mode)) {
4331c7850f9SSasha Levin 		rstat->stat.length = 0;
4341c7850f9SSasha Levin 		rstat->stat.mode |= P9_DMDIR;
4351c7850f9SSasha Levin 	}
4361c7850f9SSasha Levin 
4371c7850f9SSasha Levin 	rstat->stat.atime = st->st_atime;
4381c7850f9SSasha Levin 	rstat->stat.mtime = st->st_mtime;
4391c7850f9SSasha Levin 
4401c7850f9SSasha Levin 	str = (struct p9_str *)&rstat->stat.name;
4411c7850f9SSasha Levin 	str->len = strlen(name);
4421c7850f9SSasha Levin 	memcpy(&str->str, name, str->len);
4431c7850f9SSasha Levin 	str = (void *)str + str->len + sizeof(u16);
4441c7850f9SSasha Levin 
4451c7850f9SSasha Levin 	/* TODO: Pass usernames to the client */
4461c7850f9SSasha Levin 	str->len = 0;
4471c7850f9SSasha Levin 	str = (void *)str + sizeof(u16);
4481c7850f9SSasha Levin 	str->len = 0;
4491c7850f9SSasha Levin 	str = (void *)str + sizeof(u16);
4501c7850f9SSasha Levin 	str->len = 0;
4511c7850f9SSasha Levin 	str = (void *)str + sizeof(u16);
4521c7850f9SSasha Levin 
453b4422bf3SAneesh Kumar K.V 	/*
454b4422bf3SAneesh Kumar K.V 	 * We subtract a u16 here because rstat->size
455b4422bf3SAneesh Kumar K.V 	 * doesn't include rstat->size itself
456b4422bf3SAneesh Kumar K.V 	 */
4571c7850f9SSasha Levin 	rstat->stat.size = (void *)str - (void *)&rstat->stat - sizeof(u16);
4581c7850f9SSasha Levin 
4591c7850f9SSasha Levin 	return rstat->stat.size + sizeof(u16);
4601c7850f9SSasha Levin }
4611c7850f9SSasha Levin 
462ead43b01SAneesh Kumar K.V static void virtio_p9_read(struct p9_dev *p9dev,
463af045e53SAneesh Kumar K.V 			   struct p9_pdu *pdu, u32 *outlen)
4641c7850f9SSasha Levin {
465af045e53SAneesh Kumar K.V 	struct p9_msg *inmsg  = pdu->in_iov[0].iov_base;
466af045e53SAneesh Kumar K.V 	struct p9_msg *outmsg = pdu->out_iov[0].iov_base;
467af045e53SAneesh Kumar K.V 	struct p9_tread *tread	= (struct p9_tread *)outmsg->msg;
468af045e53SAneesh Kumar K.V 	struct p9_rread *rread	= (struct p9_rread *)inmsg->msg;
469af045e53SAneesh Kumar K.V 	struct p9_rstat *rstat	= (struct p9_rstat *)pdu->in_iov[1].iov_base;
470b4422bf3SAneesh Kumar K.V 	struct p9_fid *fid	= &p9dev->fids[tread->fid];
4711c7850f9SSasha Levin 	struct stat st;
4721c7850f9SSasha Levin 
4731c7850f9SSasha Levin 	rread->count = 0;
4741c7850f9SSasha Levin 
4751c7850f9SSasha Levin 	if (fid->is_dir) {
4761c7850f9SSasha Levin 		/* If reading a dir, fill the buffer with p9_stat entries */
4771c7850f9SSasha Levin 		struct dirent *cur = readdir(fid->dir);
4781c7850f9SSasha Levin 		char full_path[PATH_MAX];
4791c7850f9SSasha Levin 
4801c7850f9SSasha Levin 		while (cur) {
4811c7850f9SSasha Levin 			u32 read;
4821c7850f9SSasha Levin 
48330204a77SAneesh Kumar K.V 			lstat(rel_to_abs(p9dev, cur->d_name, full_path), &st);
484b4422bf3SAneesh Kumar K.V 			read = virtio_p9_fill_stat(p9dev, cur->d_name,
485b4422bf3SAneesh Kumar K.V 						   &st, rstat);
4861c7850f9SSasha Levin 			rread->count += read;
4871c7850f9SSasha Levin 			rstat = (void *)rstat + read;
4881c7850f9SSasha Levin 			cur = readdir(fid->dir);
4891c7850f9SSasha Levin 		}
4901c7850f9SSasha Levin 	} else {
491af045e53SAneesh Kumar K.V 		pdu->in_iov[0].iov_base += VIRTIO_P9_HDR_LEN + sizeof(u32);
492af045e53SAneesh Kumar K.V 		pdu->in_iov[0].iov_len -= VIRTIO_P9_HDR_LEN + sizeof(u32);
4936b163a87SAneesh Kumar K.V 		pdu->in_iov_cnt = virtio_p9_update_iov_cnt(pdu->in_iov,
4946b163a87SAneesh Kumar K.V 							    tread->count,
4956b163a87SAneesh Kumar K.V 							    pdu->in_iov_cnt);
496af045e53SAneesh Kumar K.V 		rread->count = preadv(fid->fd, pdu->in_iov,
497af045e53SAneesh Kumar K.V 				      pdu->in_iov_cnt, tread->offset);
4981c7850f9SSasha Levin 		if (rread->count > tread->count)
4991c7850f9SSasha Levin 			rread->count = tread->count;
5001c7850f9SSasha Levin 	}
5011c7850f9SSasha Levin 
5021c7850f9SSasha Levin 	*outlen = VIRTIO_P9_HDR_LEN + sizeof(u32) + rread->count;
503af045e53SAneesh Kumar K.V 	set_p9msg_hdr(inmsg, *outlen, P9_RREAD, outmsg->tag);
5041c7850f9SSasha Levin 
505ead43b01SAneesh Kumar K.V 	return;
5061c7850f9SSasha Levin }
5071c7850f9SSasha Levin 
508ead43b01SAneesh Kumar K.V static void virtio_p9_stat(struct p9_dev *p9dev,
509af045e53SAneesh Kumar K.V 			   struct p9_pdu *pdu, u32 *outlen)
5101c7850f9SSasha Levin {
5111c7850f9SSasha Levin 	u32 ret;
512af045e53SAneesh Kumar K.V 	struct stat st;
513af045e53SAneesh Kumar K.V 	struct p9_msg *inmsg  = pdu->in_iov[0].iov_base;
514af045e53SAneesh Kumar K.V 	struct p9_msg *outmsg = pdu->out_iov[0].iov_base;
515af045e53SAneesh Kumar K.V 	struct p9_tstat *tstat = (struct p9_tstat *)outmsg->msg;
516af045e53SAneesh Kumar K.V 	struct p9_rstat *rstat = (struct p9_rstat *)(inmsg->msg + sizeof(u16));
517af045e53SAneesh Kumar K.V 	struct p9_fid *fid = &p9dev->fids[tstat->fid];
5181c7850f9SSasha Levin 
51930204a77SAneesh Kumar K.V 	if (lstat(fid->abs_path, &st) < 0)
520*6c8ca053SAneesh Kumar K.V 		goto err_out;
5211c7850f9SSasha Levin 
522b4422bf3SAneesh Kumar K.V 	ret = virtio_p9_fill_stat(p9dev, fid->path, &st, rstat);
5231c7850f9SSasha Levin 
524c81fc2c7SAneesh Kumar K.V 	*outlen = VIRTIO_P9_HDR_LEN + ret + sizeof(u16);
525af045e53SAneesh Kumar K.V 	set_p9msg_hdr(inmsg, *outlen, P9_RSTAT, outmsg->tag);
526ead43b01SAneesh Kumar K.V 	return;
527*6c8ca053SAneesh Kumar K.V err_out:
528*6c8ca053SAneesh Kumar K.V 	virtio_p9_error_reply(p9dev, pdu, errno, outlen);
529*6c8ca053SAneesh Kumar K.V 	return;
5301c7850f9SSasha Levin }
5311c7850f9SSasha Levin 
532ead43b01SAneesh Kumar K.V static void virtio_p9_wstat(struct p9_dev *p9dev,
533af045e53SAneesh Kumar K.V 			    struct p9_pdu *pdu, u32 *outlen)
5341c7850f9SSasha Levin {
535aec426f0SCyrill Gorcunov 	int res = 0;
536af045e53SAneesh Kumar K.V 	struct p9_str *str;
537af045e53SAneesh Kumar K.V 	struct p9_msg *inmsg  = pdu->in_iov[0].iov_base;
538af045e53SAneesh Kumar K.V 	struct p9_msg *outmsg = pdu->out_iov[0].iov_base;
539af045e53SAneesh Kumar K.V 	struct p9_twstat *twstat = (struct p9_twstat *)outmsg->msg;
540af045e53SAneesh Kumar K.V 	struct p9_fid *fid = &p9dev->fids[twstat->fid];
541af045e53SAneesh Kumar K.V 
5421c7850f9SSasha Levin 
5431c7850f9SSasha Levin 	if (twstat->stat.length != -1UL)
5441c7850f9SSasha Levin 		res = ftruncate(fid->fd, twstat->stat.length);
5451c7850f9SSasha Levin 
5461c7850f9SSasha Levin 	if (twstat->stat.mode != -1U)
5471c7850f9SSasha Levin 		chmod(fid->abs_path, twstat->stat.mode & 0xFFFF);
5481c7850f9SSasha Levin 
5491c7850f9SSasha Levin 	str = (void *)&twstat->stat.name + sizeof(u16);
5501c7850f9SSasha Levin 	if (str->len > 0) {
5511c7850f9SSasha Levin 		char new_name[PATH_MAX] = {0};
5521c7850f9SSasha Levin 		char full_path[PATH_MAX];
5531c7850f9SSasha Levin 		char *last_dir = strrchr(fid->path, '/');
5541c7850f9SSasha Levin 
5551c7850f9SSasha Levin 		/* We need to get the full file name out of twstat->name */
5561c7850f9SSasha Levin 		if (last_dir)
5571c7850f9SSasha Levin 			strncpy(new_name, fid->path, last_dir - fid->path + 1);
5581c7850f9SSasha Levin 
5591c7850f9SSasha Levin 		memcpy(new_name + strlen(new_name), &str->str, str->len);
5601c7850f9SSasha Levin 
5611c7850f9SSasha Levin 		/* fid is reused for the new file */
562b4422bf3SAneesh Kumar K.V 		rename(fid->abs_path, rel_to_abs(p9dev, new_name, full_path));
5631c7850f9SSasha Levin 		sprintf(fid->path, "%s", new_name);
5641c7850f9SSasha Levin 	}
5651c7850f9SSasha Levin 
5661c7850f9SSasha Levin 	*outlen = VIRTIO_P9_HDR_LEN;
567af045e53SAneesh Kumar K.V 	set_p9msg_hdr(inmsg, *outlen, P9_RWSTAT, outmsg->tag);
568aec426f0SCyrill Gorcunov 
569ead43b01SAneesh Kumar K.V 	return;
5701c7850f9SSasha Levin }
5711c7850f9SSasha Levin 
572ead43b01SAneesh Kumar K.V static void virtio_p9_remove(struct p9_dev *p9dev,
573af045e53SAneesh Kumar K.V 			     struct p9_pdu *pdu, u32 *outlen)
5741c7850f9SSasha Levin {
575af045e53SAneesh Kumar K.V 	struct p9_msg *inmsg  = pdu->in_iov[0].iov_base;
576af045e53SAneesh Kumar K.V 	struct p9_msg *outmsg = pdu->out_iov[0].iov_base;
577af045e53SAneesh Kumar K.V 	struct p9_tremove *tremove = (struct p9_tremove *)outmsg->msg;
578b4422bf3SAneesh Kumar K.V 	struct p9_fid *fid = &p9dev->fids[tremove->fid];
5791c7850f9SSasha Levin 
580b4422bf3SAneesh Kumar K.V 	close_fid(p9dev, tremove->fid);
5811c7850f9SSasha Levin 	if (fid->is_dir)
5821c7850f9SSasha Levin 		rmdir(fid->abs_path);
5831c7850f9SSasha Levin 	else
5841c7850f9SSasha Levin 		unlink(fid->abs_path);
5851c7850f9SSasha Levin 
5861c7850f9SSasha Levin 	*outlen = VIRTIO_P9_HDR_LEN;
587af045e53SAneesh Kumar K.V 	set_p9msg_hdr(inmsg, *outlen, P9_RREMOVE, outmsg->tag);
588ead43b01SAneesh Kumar K.V 	return;
5891c7850f9SSasha Levin }
5901c7850f9SSasha Levin 
591ead43b01SAneesh Kumar K.V static void virtio_p9_write(struct p9_dev *p9dev,
592af045e53SAneesh Kumar K.V 			    struct p9_pdu *pdu, u32 *outlen)
5931c7850f9SSasha Levin {
594af045e53SAneesh Kumar K.V 	struct p9_msg *inmsg  = pdu->in_iov[0].iov_base;
595af045e53SAneesh Kumar K.V 	struct p9_msg *outmsg = pdu->out_iov[0].iov_base;
596af045e53SAneesh Kumar K.V 	struct p9_twrite *twrite = (struct p9_twrite *)outmsg->msg;
597af045e53SAneesh Kumar K.V 	struct p9_rwrite *rwrite = (struct p9_rwrite *)inmsg->msg;
598b4422bf3SAneesh Kumar K.V 	struct p9_fid *fid = &p9dev->fids[twrite->fid];
5991c7850f9SSasha Levin 
600af045e53SAneesh Kumar K.V 
601af045e53SAneesh Kumar K.V 	pdu->out_iov[0].iov_base += (sizeof(*outmsg) + sizeof(*twrite));
602af045e53SAneesh Kumar K.V 	pdu->out_iov[0].iov_len -= (sizeof(*outmsg) + sizeof(*twrite));
6036b163a87SAneesh Kumar K.V 	pdu->out_iov_cnt = virtio_p9_update_iov_cnt(pdu->out_iov, twrite->count,
6046b163a87SAneesh Kumar K.V 						    pdu->out_iov_cnt);
605af045e53SAneesh Kumar K.V 	rwrite->count = pwritev(fid->fd, pdu->out_iov,
606af045e53SAneesh Kumar K.V 				pdu->out_iov_cnt, twrite->offset);
6071c7850f9SSasha Levin 	*outlen = VIRTIO_P9_HDR_LEN + sizeof(u32);
608af045e53SAneesh Kumar K.V 	set_p9msg_hdr(inmsg, *outlen, P9_RWRITE, outmsg->tag);
6091c7850f9SSasha Levin 
610ead43b01SAneesh Kumar K.V 	return;
6111c7850f9SSasha Levin }
6121c7850f9SSasha Levin 
613ead43b01SAneesh Kumar K.V typedef void p9_handler(struct p9_dev *p9dev,
614af045e53SAneesh Kumar K.V 			struct p9_pdu *pdu, u32 *outlen);
615b4422bf3SAneesh Kumar K.V 
616b4422bf3SAneesh Kumar K.V static p9_handler *virtio_9p_handler [] = {
617b4422bf3SAneesh Kumar K.V 	[P9_TVERSION] = virtio_p9_version,
618b4422bf3SAneesh Kumar K.V 	[P9_TATTACH]  = virtio_p9_attach,
619b4422bf3SAneesh Kumar K.V 	[P9_TSTAT]    = virtio_p9_stat,
620b4422bf3SAneesh Kumar K.V 	[P9_TCLUNK]   =	virtio_p9_clunk,
621b4422bf3SAneesh Kumar K.V 	[P9_TWALK]    =	virtio_p9_walk,
622b4422bf3SAneesh Kumar K.V 	[P9_TOPEN]    =	virtio_p9_open,
623b4422bf3SAneesh Kumar K.V 	[P9_TREAD]    = virtio_p9_read,
624b4422bf3SAneesh Kumar K.V 	[P9_TCREATE]  =	virtio_p9_create,
625b4422bf3SAneesh Kumar K.V 	[P9_TWSTAT]   =	virtio_p9_wstat,
626b4422bf3SAneesh Kumar K.V 	[P9_TREMOVE]  =	virtio_p9_remove,
627b4422bf3SAneesh Kumar K.V 	[P9_TWRITE]   =	virtio_p9_write,
628b4422bf3SAneesh Kumar K.V };
629b4422bf3SAneesh Kumar K.V 
630af045e53SAneesh Kumar K.V static struct p9_pdu *virtio_p9_pdu_init(struct kvm *kvm, struct virt_queue *vq)
631af045e53SAneesh Kumar K.V {
632af045e53SAneesh Kumar K.V 	struct p9_pdu *pdu = calloc(1, sizeof(*pdu));
633af045e53SAneesh Kumar K.V 	if (!pdu)
634af045e53SAneesh Kumar K.V 		return NULL;
635af045e53SAneesh Kumar K.V 
636af045e53SAneesh Kumar K.V 	pdu->queue_head  = virt_queue__get_inout_iov(kvm, vq, pdu->in_iov,
637af045e53SAneesh Kumar K.V 						     pdu->out_iov,
638af045e53SAneesh Kumar K.V 						     &pdu->in_iov_cnt,
639af045e53SAneesh Kumar K.V 						     &pdu->out_iov_cnt);
640af045e53SAneesh Kumar K.V 	return pdu;
641af045e53SAneesh Kumar K.V }
642af045e53SAneesh Kumar K.V 
643af045e53SAneesh Kumar K.V static u8 virtio_p9_get_cmd(struct p9_pdu *pdu)
644af045e53SAneesh Kumar K.V {
645af045e53SAneesh Kumar K.V 	struct p9_msg *msg;
646af045e53SAneesh Kumar K.V 	/*
647af045e53SAneesh Kumar K.V 	 * we can peek directly into pdu for a u8
648af045e53SAneesh Kumar K.V 	 * value. The host endianess won't be an issue
649af045e53SAneesh Kumar K.V 	 */
650af045e53SAneesh Kumar K.V 	msg = pdu->out_iov[0].iov_base;
651af045e53SAneesh Kumar K.V 	return msg->cmd;
652af045e53SAneesh Kumar K.V }
653af045e53SAneesh Kumar K.V 
654b4422bf3SAneesh Kumar K.V static bool virtio_p9_do_io_request(struct kvm *kvm, struct p9_dev_job *job)
6551c7850f9SSasha Levin {
656af045e53SAneesh Kumar K.V 	u8 cmd;
657b4422bf3SAneesh Kumar K.V 	u32 len = 0;
658b4422bf3SAneesh Kumar K.V 	p9_handler *handler;
659b4422bf3SAneesh Kumar K.V 	struct p9_dev *p9dev;
660af045e53SAneesh Kumar K.V 	struct virt_queue *vq;
661af045e53SAneesh Kumar K.V 	struct p9_pdu *p9pdu;
6621c7850f9SSasha Levin 
663b4422bf3SAneesh Kumar K.V 	vq = job->vq;
664b4422bf3SAneesh Kumar K.V 	p9dev = job->p9dev;
6651c7850f9SSasha Levin 
666af045e53SAneesh Kumar K.V 	p9pdu = virtio_p9_pdu_init(kvm, vq);
667af045e53SAneesh Kumar K.V 	cmd = virtio_p9_get_cmd(p9pdu);
668af045e53SAneesh Kumar K.V 
669af045e53SAneesh Kumar K.V 	if (cmd >= ARRAY_SIZE(virtio_9p_handler) ||
670af045e53SAneesh Kumar K.V 	    !virtio_9p_handler[cmd]) {
671af045e53SAneesh Kumar K.V 		printf("Unsupported P9 message type: %u\n", cmd);
6721c7850f9SSasha Levin 
673b4422bf3SAneesh Kumar K.V 	} else {
674af045e53SAneesh Kumar K.V 		handler = virtio_9p_handler[cmd];
675af045e53SAneesh Kumar K.V 		handler(p9dev, p9pdu, &len);
676b4422bf3SAneesh Kumar K.V 	}
677af045e53SAneesh Kumar K.V 	virt_queue__set_used_elem(vq, p9pdu->queue_head, len);
678af045e53SAneesh Kumar K.V 	free(p9pdu);
6791c7850f9SSasha Levin 	return true;
6801c7850f9SSasha Levin }
6811c7850f9SSasha Levin 
6821c7850f9SSasha Levin static void virtio_p9_do_io(struct kvm *kvm, void *param)
6831c7850f9SSasha Levin {
684b4422bf3SAneesh Kumar K.V 	struct p9_dev_job *job = (struct p9_dev_job *)param;
685b4422bf3SAneesh Kumar K.V 	struct p9_dev *p9dev   = job->p9dev;
686b4422bf3SAneesh Kumar K.V 	struct virt_queue *vq  = job->vq;
6871c7850f9SSasha Levin 
6881c7850f9SSasha Levin 	while (virt_queue__available(vq)) {
689b4422bf3SAneesh Kumar K.V 		virtio_p9_do_io_request(kvm, job);
690b4422bf3SAneesh Kumar K.V 		virt_queue__trigger_irq(vq, p9dev->pci_hdr.irq_line,
691b4422bf3SAneesh Kumar K.V 					&p9dev->isr, kvm);
6921c7850f9SSasha Levin 	}
6931c7850f9SSasha Levin }
6941c7850f9SSasha Levin 
69560eb42d5SSasha Levin static void ioevent_callback(struct kvm *kvm, void *param)
69660eb42d5SSasha Levin {
69760eb42d5SSasha Levin 	struct p9_dev_job *job = param;
69860eb42d5SSasha Levin 
69960eb42d5SSasha Levin 	thread_pool__do_job(job->job_id);
70060eb42d5SSasha Levin }
70160eb42d5SSasha Levin 
702b4422bf3SAneesh Kumar K.V static bool virtio_p9_pci_io_out(struct ioport *ioport, struct kvm *kvm,
703b4422bf3SAneesh Kumar K.V 				 u16 port, void *data, int size, u32 count)
7041c7850f9SSasha Levin {
7051c7850f9SSasha Levin 	unsigned long offset;
7061c7850f9SSasha Levin 	bool ret = true;
707b4422bf3SAneesh Kumar K.V 	struct p9_dev  *p9dev;
70860eb42d5SSasha Levin 	struct ioevent ioevent;
7091c7850f9SSasha Levin 
710b4422bf3SAneesh Kumar K.V 	p9dev = ioport->priv;
711b4422bf3SAneesh Kumar K.V 	offset = port - p9dev->base_addr;
7121c7850f9SSasha Levin 
7131c7850f9SSasha Levin 	switch (offset) {
7141c7850f9SSasha Levin 	case VIRTIO_MSI_QUEUE_VECTOR:
7151c7850f9SSasha Levin 	case VIRTIO_PCI_GUEST_FEATURES:
7161c7850f9SSasha Levin 		break;
7171c7850f9SSasha Levin 	case VIRTIO_PCI_QUEUE_PFN: {
7181c7850f9SSasha Levin 		void *p;
719b4422bf3SAneesh Kumar K.V 		struct p9_dev_job *job;
720b4422bf3SAneesh Kumar K.V 		struct virt_queue *queue;
7211c7850f9SSasha Levin 
722b4422bf3SAneesh Kumar K.V 		job			= &p9dev->jobs[p9dev->queue_selector];
723b4422bf3SAneesh Kumar K.V 		queue			= &p9dev->vqs[p9dev->queue_selector];
7241c7850f9SSasha Levin 		queue->pfn		= ioport__read32(data);
7251c7850f9SSasha Levin 		p			= guest_pfn_to_host(kvm, queue->pfn);
7261c7850f9SSasha Levin 
727612038c5SAneesh Kumar K.V 		vring_init(&queue->vring, VIRTQUEUE_NUM, p,
728b4422bf3SAneesh Kumar K.V 			   VIRTIO_PCI_VRING_ALIGN);
7291c7850f9SSasha Levin 
730b4422bf3SAneesh Kumar K.V 		*job			= (struct p9_dev_job) {
731b4422bf3SAneesh Kumar K.V 			.vq			= queue,
732b4422bf3SAneesh Kumar K.V 			.p9dev			= p9dev,
733b4422bf3SAneesh Kumar K.V 		};
734b4422bf3SAneesh Kumar K.V 		job->job_id = thread_pool__add_job(kvm, virtio_p9_do_io, job);
73560eb42d5SSasha Levin 
73660eb42d5SSasha Levin 		ioevent = (struct ioevent) {
73760eb42d5SSasha Levin 			.io_addr		= p9dev->base_addr + VIRTIO_PCI_QUEUE_NOTIFY,
73860eb42d5SSasha Levin 			.io_len			= sizeof(u16),
73960eb42d5SSasha Levin 			.fn			= ioevent_callback,
74060eb42d5SSasha Levin 			.datamatch		= p9dev->queue_selector,
74160eb42d5SSasha Levin 			.fn_ptr			= &p9dev->jobs[p9dev->queue_selector],
74260eb42d5SSasha Levin 			.fn_kvm			= kvm,
74360eb42d5SSasha Levin 			.fd			= eventfd(0, 0),
74460eb42d5SSasha Levin 		};
74560eb42d5SSasha Levin 
74660eb42d5SSasha Levin 		ioeventfd__add_event(&ioevent);
74760eb42d5SSasha Levin 
7481c7850f9SSasha Levin 		break;
7491c7850f9SSasha Levin 	}
7501c7850f9SSasha Levin 	case VIRTIO_PCI_QUEUE_SEL:
751b4422bf3SAneesh Kumar K.V 		p9dev->queue_selector	= ioport__read16(data);
7521c7850f9SSasha Levin 		break;
7531c7850f9SSasha Levin 	case VIRTIO_PCI_QUEUE_NOTIFY: {
7541c7850f9SSasha Levin 		u16 queue_index;
75560eb42d5SSasha Levin 
7561c7850f9SSasha Levin 		queue_index		= ioport__read16(data);
757b4422bf3SAneesh Kumar K.V 		thread_pool__do_job(p9dev->jobs[queue_index].job_id);
7581c7850f9SSasha Levin 		break;
7591c7850f9SSasha Levin 	}
7601c7850f9SSasha Levin 	case VIRTIO_PCI_STATUS:
761b4422bf3SAneesh Kumar K.V 		p9dev->status		= ioport__read8(data);
7621c7850f9SSasha Levin 		break;
7631c7850f9SSasha Levin 	case VIRTIO_MSI_CONFIG_VECTOR:
764b4422bf3SAneesh Kumar K.V 		p9dev->config_vector	= VIRTIO_MSI_NO_VECTOR;
7651c7850f9SSasha Levin 		break;
7661c7850f9SSasha Levin 	default:
7671c7850f9SSasha Levin 		ret			= false;
7681c7850f9SSasha Levin 		break;
7691c7850f9SSasha Levin 	};
7701c7850f9SSasha Levin 
7711c7850f9SSasha Levin 	return ret;
7721c7850f9SSasha Levin }
7731c7850f9SSasha Levin 
7741c7850f9SSasha Levin static struct ioport_operations virtio_p9_io_ops = {
7751c7850f9SSasha Levin 	.io_in				= virtio_p9_pci_io_in,
7761c7850f9SSasha Levin 	.io_out				= virtio_p9_pci_io_out,
7771c7850f9SSasha Levin };
7781c7850f9SSasha Levin 
779b4422bf3SAneesh Kumar K.V void virtio_9p__init(struct kvm *kvm, const char *root, const char *tag_name)
7801c7850f9SSasha Levin {
7811c7850f9SSasha Levin 	u8 pin, line, dev;
7821c7850f9SSasha Levin 	u32 i, root_len;
783f884f920SSasha Levin 	u16 p9_base_addr;
784b4422bf3SAneesh Kumar K.V 	struct p9_dev *p9dev;
7851c7850f9SSasha Levin 
786b4422bf3SAneesh Kumar K.V 	p9dev = calloc(1, sizeof(*p9dev));
787b4422bf3SAneesh Kumar K.V 	if (!p9dev)
7881c7850f9SSasha Levin 		return;
789b4422bf3SAneesh Kumar K.V 	if (!tag_name)
790b4422bf3SAneesh Kumar K.V 		tag_name = VIRTIO_P9_DEFAULT_TAG;
791b4422bf3SAneesh Kumar K.V 	p9dev->config = calloc(1, sizeof(*p9dev->config) + strlen(tag_name) + 1);
792b4422bf3SAneesh Kumar K.V 	if (p9dev->config == NULL)
793b4422bf3SAneesh Kumar K.V 		goto free_p9dev;
7941c7850f9SSasha Levin 
795b4422bf3SAneesh Kumar K.V 	strcpy(p9dev->root_dir, root);
7961c7850f9SSasha Levin 	root_len = strlen(root);
7971c7850f9SSasha Levin 	/*
7981c7850f9SSasha Levin 	 * We prefix the full path in all fids, This allows us to get the
7991c7850f9SSasha Levin 	 * absolute path of an fid without playing with strings.
8001c7850f9SSasha Levin 	 */
8011c7850f9SSasha Levin 	for (i = 0; i < VIRTIO_P9_MAX_FID; i++) {
802b4422bf3SAneesh Kumar K.V 		strcpy(p9dev->fids[i].abs_path, root);
803b4422bf3SAneesh Kumar K.V 		p9dev->fids[i].path = p9dev->fids[i].abs_path + root_len;
8041c7850f9SSasha Levin 	}
805b4422bf3SAneesh Kumar K.V 	p9dev->config->tag_len = strlen(tag_name);
806b4422bf3SAneesh Kumar K.V 	if (p9dev->config->tag_len > MAX_TAG_LEN)
807b4422bf3SAneesh Kumar K.V 		goto free_p9dev_config;
8081c7850f9SSasha Levin 
809b4422bf3SAneesh Kumar K.V 	memcpy(p9dev->config->tag, tag_name, strlen(tag_name));
810b4422bf3SAneesh Kumar K.V 	p9dev->features |= 1 << VIRTIO_9P_MOUNT_TAG;
8111c7850f9SSasha Levin 
8121c7850f9SSasha Levin 	if (irq__register_device(VIRTIO_ID_9P, &dev, &pin, &line) < 0)
813b4422bf3SAneesh Kumar K.V 		goto free_p9dev_config;
8141c7850f9SSasha Levin 
815b4422bf3SAneesh Kumar K.V 	p9_base_addr			= ioport__register(IOPORT_EMPTY,
816b4422bf3SAneesh Kumar K.V 							   &virtio_p9_io_ops,
817b4422bf3SAneesh Kumar K.V 							   IOPORT_SIZE, p9dev);
818b4422bf3SAneesh Kumar K.V 	p9dev->base_addr		    = p9_base_addr;
819b4422bf3SAneesh Kumar K.V 	p9dev->pci_hdr = (struct pci_device_header) {
820b4422bf3SAneesh Kumar K.V 		.vendor_id		= PCI_VENDOR_ID_REDHAT_QUMRANET,
821b4422bf3SAneesh Kumar K.V 		.device_id		= PCI_DEVICE_ID_VIRTIO_P9,
822b4422bf3SAneesh Kumar K.V 		.header_type		= PCI_HEADER_TYPE_NORMAL,
823b4422bf3SAneesh Kumar K.V 		.revision_id		= 0,
824b4422bf3SAneesh Kumar K.V 		.class			= 0x010000,
825b4422bf3SAneesh Kumar K.V 		.subsys_vendor_id	= PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET,
826b4422bf3SAneesh Kumar K.V 		.subsys_id		= VIRTIO_ID_9P,
827b4422bf3SAneesh Kumar K.V 		.irq_pin		= pin,
828b4422bf3SAneesh Kumar K.V 		.irq_line		= line,
829b4422bf3SAneesh Kumar K.V 		.bar[0]			= p9_base_addr | PCI_BASE_ADDRESS_SPACE_IO,
830b4422bf3SAneesh Kumar K.V 	};
831b4422bf3SAneesh Kumar K.V 	pci__register(&p9dev->pci_hdr, dev);
832b4422bf3SAneesh Kumar K.V 
833b4422bf3SAneesh Kumar K.V 	return;
834b4422bf3SAneesh Kumar K.V free_p9dev_config:
835b4422bf3SAneesh Kumar K.V 	free(p9dev->config);
836b4422bf3SAneesh Kumar K.V free_p9dev:
837b4422bf3SAneesh Kumar K.V 	free(p9dev);
8381c7850f9SSasha Levin }
839