xref: /kvmtool/virtio/9p.c (revision 1c7850f959039518ff1e261f45fdf255e1a52635)
1*1c7850f9SSasha Levin #include "kvm/virtio-9p.h"
2*1c7850f9SSasha Levin #include "kvm/virtio-pci-dev.h"
3*1c7850f9SSasha Levin #include "kvm/virtio.h"
4*1c7850f9SSasha Levin #include "kvm/ioport.h"
5*1c7850f9SSasha Levin #include "kvm/mutex.h"
6*1c7850f9SSasha Levin #include "kvm/util.h"
7*1c7850f9SSasha Levin #include "kvm/kvm.h"
8*1c7850f9SSasha Levin #include "kvm/pci.h"
9*1c7850f9SSasha Levin #include "kvm/threadpool.h"
10*1c7850f9SSasha Levin #include "kvm/irq.h"
11*1c7850f9SSasha Levin 
12*1c7850f9SSasha Levin #include <linux/virtio_ring.h>
13*1c7850f9SSasha Levin #include <linux/virtio_9p.h>
14*1c7850f9SSasha Levin #include <net/9p/9p.h>
15*1c7850f9SSasha Levin 
16*1c7850f9SSasha Levin #include <fcntl.h>
17*1c7850f9SSasha Levin #include <sys/types.h>
18*1c7850f9SSasha Levin #include <sys/stat.h>
19*1c7850f9SSasha Levin #include <pthread.h>
20*1c7850f9SSasha Levin #include <dirent.h>
21*1c7850f9SSasha Levin 
22*1c7850f9SSasha Levin #define NUM_VIRT_QUEUES		1
23*1c7850f9SSasha Levin #define VIRTIO_P9_QUEUE_SIZE	128
24*1c7850f9SSasha Levin #define	VIRTIO_P9_TAG		"kvm_9p"
25*1c7850f9SSasha Levin #define VIRTIO_P9_HDR_LEN	(sizeof(u32)+sizeof(u8)+sizeof(u16))
26*1c7850f9SSasha Levin #define VIRTIO_P9_MAX_FID	128
27*1c7850f9SSasha Levin #define VIRTIO_P9_VERSION	"9P2000"
28*1c7850f9SSasha Levin 
29*1c7850f9SSasha Levin struct p9_msg {
30*1c7850f9SSasha Levin 	u32			size;
31*1c7850f9SSasha Levin 	u8			cmd;
32*1c7850f9SSasha Levin 	u16			tag;
33*1c7850f9SSasha Levin 	u8			msg[0];
34*1c7850f9SSasha Levin } __attribute__((packed));
35*1c7850f9SSasha Levin 
36*1c7850f9SSasha Levin struct p9_fid {
37*1c7850f9SSasha Levin 	u32			fid;
38*1c7850f9SSasha Levin 	u8			is_dir;
39*1c7850f9SSasha Levin 	char			abs_path[PATH_MAX];
40*1c7850f9SSasha Levin 	char			*path;
41*1c7850f9SSasha Levin 	DIR			*dir;
42*1c7850f9SSasha Levin 	int			fd;
43*1c7850f9SSasha Levin };
44*1c7850f9SSasha Levin 
45*1c7850f9SSasha Levin static struct pci_device_header virtio_p9_pci_device = {
46*1c7850f9SSasha Levin 	.vendor_id		= PCI_VENDOR_ID_REDHAT_QUMRANET,
47*1c7850f9SSasha Levin 	.device_id		= PCI_DEVICE_ID_VIRTIO_P9,
48*1c7850f9SSasha Levin 	.header_type		= PCI_HEADER_TYPE_NORMAL,
49*1c7850f9SSasha Levin 	.revision_id		= 0,
50*1c7850f9SSasha Levin 	.class			= 0x010000,
51*1c7850f9SSasha Levin 	.subsys_vendor_id	= PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET,
52*1c7850f9SSasha Levin 	.subsys_id		= VIRTIO_ID_9P,
53*1c7850f9SSasha Levin 	.bar[0]			= IOPORT_VIRTIO_P9 | PCI_BASE_ADDRESS_SPACE_IO,
54*1c7850f9SSasha Levin };
55*1c7850f9SSasha Levin 
56*1c7850f9SSasha Levin struct p9_dev {
57*1c7850f9SSasha Levin 	u8			status;
58*1c7850f9SSasha Levin 	u8			isr;
59*1c7850f9SSasha Levin 	u16			config_vector;
60*1c7850f9SSasha Levin 	u32			features;
61*1c7850f9SSasha Levin 	struct virtio_9p_config	*config;
62*1c7850f9SSasha Levin 
63*1c7850f9SSasha Levin 	/* virtio queue */
64*1c7850f9SSasha Levin 	u16			queue_selector;
65*1c7850f9SSasha Levin 	struct virt_queue	vqs[NUM_VIRT_QUEUES];
66*1c7850f9SSasha Levin 	void			*jobs[NUM_VIRT_QUEUES];
67*1c7850f9SSasha Levin 
68*1c7850f9SSasha Levin 	struct p9_fid		fids[VIRTIO_P9_MAX_FID];
69*1c7850f9SSasha Levin 	char			root_dir[PATH_MAX];
70*1c7850f9SSasha Levin };
71*1c7850f9SSasha Levin 
72*1c7850f9SSasha Levin static struct p9_dev p9dev;
73*1c7850f9SSasha Levin 
74*1c7850f9SSasha Levin /* Warning: Immediately use value returned from this function */
75*1c7850f9SSasha Levin static const char *rel_to_abs(const char *path, char *abs_path)
76*1c7850f9SSasha Levin {
77*1c7850f9SSasha Levin 	sprintf(abs_path, "%s/%s", p9dev.root_dir, path);
78*1c7850f9SSasha Levin 
79*1c7850f9SSasha Levin 	return abs_path;
80*1c7850f9SSasha Levin }
81*1c7850f9SSasha Levin 
82*1c7850f9SSasha Levin static bool virtio_p9_dev_in(void *data, unsigned long offset, int size, u32 count)
83*1c7850f9SSasha Levin {
84*1c7850f9SSasha Levin 	u8 *config_space = (u8 *) p9dev.config;
85*1c7850f9SSasha Levin 
86*1c7850f9SSasha Levin 	if (size != 1 || count != 1)
87*1c7850f9SSasha Levin 		return false;
88*1c7850f9SSasha Levin 
89*1c7850f9SSasha Levin 	ioport__write8(data, config_space[offset - VIRTIO_MSI_CONFIG_VECTOR]);
90*1c7850f9SSasha Levin 
91*1c7850f9SSasha Levin 	return true;
92*1c7850f9SSasha Levin }
93*1c7850f9SSasha Levin 
94*1c7850f9SSasha Levin static bool virtio_p9_pci_io_in(struct kvm *kvm, u16 port, void *data, int size, u32 count)
95*1c7850f9SSasha Levin {
96*1c7850f9SSasha Levin 	unsigned long offset;
97*1c7850f9SSasha Levin 	bool ret = true;
98*1c7850f9SSasha Levin 
99*1c7850f9SSasha Levin 	offset = port - IOPORT_VIRTIO_P9;
100*1c7850f9SSasha Levin 
101*1c7850f9SSasha Levin 	switch (offset) {
102*1c7850f9SSasha Levin 	case VIRTIO_PCI_HOST_FEATURES:
103*1c7850f9SSasha Levin 		ioport__write32(data, p9dev.features);
104*1c7850f9SSasha Levin 		ret = true;
105*1c7850f9SSasha Levin 		break;
106*1c7850f9SSasha Levin 	case VIRTIO_PCI_GUEST_FEATURES:
107*1c7850f9SSasha Levin 	case VIRTIO_PCI_QUEUE_SEL:
108*1c7850f9SSasha Levin 	case VIRTIO_PCI_QUEUE_NOTIFY:
109*1c7850f9SSasha Levin 		ret = false;
110*1c7850f9SSasha Levin 		break;
111*1c7850f9SSasha Levin 	case VIRTIO_PCI_QUEUE_PFN:
112*1c7850f9SSasha Levin 		ioport__write32(data, p9dev.vqs[p9dev.queue_selector].pfn);
113*1c7850f9SSasha Levin 		break;
114*1c7850f9SSasha Levin 	case VIRTIO_PCI_QUEUE_NUM:
115*1c7850f9SSasha Levin 		ioport__write16(data, VIRTIO_P9_QUEUE_SIZE);
116*1c7850f9SSasha Levin 		break;
117*1c7850f9SSasha Levin 	case VIRTIO_PCI_STATUS:
118*1c7850f9SSasha Levin 		ioport__write8(data, p9dev.status);
119*1c7850f9SSasha Levin 		break;
120*1c7850f9SSasha Levin 	case VIRTIO_PCI_ISR:
121*1c7850f9SSasha Levin 		ioport__write8(data, p9dev.isr);
122*1c7850f9SSasha Levin 		kvm__irq_line(kvm, virtio_p9_pci_device.irq_line, VIRTIO_IRQ_LOW);
123*1c7850f9SSasha Levin 		p9dev.isr = VIRTIO_IRQ_LOW;
124*1c7850f9SSasha Levin 		break;
125*1c7850f9SSasha Levin 	default:
126*1c7850f9SSasha Levin 		ret = virtio_p9_dev_in(data, offset, size, count);
127*1c7850f9SSasha Levin 		break;
128*1c7850f9SSasha Levin 	};
129*1c7850f9SSasha Levin 
130*1c7850f9SSasha Levin 	return ret;
131*1c7850f9SSasha Levin }
132*1c7850f9SSasha Levin 
133*1c7850f9SSasha Levin static int omode2uflags(u8 mode)
134*1c7850f9SSasha Levin {
135*1c7850f9SSasha Levin 	int ret = 0;
136*1c7850f9SSasha Levin 
137*1c7850f9SSasha Levin 	/* Basic open modes are same as uflags */
138*1c7850f9SSasha Levin 	ret = mode & 3;
139*1c7850f9SSasha Levin 
140*1c7850f9SSasha Levin 	/* Everything else is different */
141*1c7850f9SSasha Levin 	if (mode & P9_OTRUNC)
142*1c7850f9SSasha Levin 		ret |= O_TRUNC;
143*1c7850f9SSasha Levin 
144*1c7850f9SSasha Levin 	if (mode & P9_OAPPEND)
145*1c7850f9SSasha Levin 		ret |= O_APPEND;
146*1c7850f9SSasha Levin 
147*1c7850f9SSasha Levin 	if (mode & P9_OEXCL)
148*1c7850f9SSasha Levin 		ret |= O_EXCL;
149*1c7850f9SSasha Levin 
150*1c7850f9SSasha Levin 	return ret;
151*1c7850f9SSasha Levin }
152*1c7850f9SSasha Levin 
153*1c7850f9SSasha Levin static void st2qid(struct stat *st, struct p9_qid *qid)
154*1c7850f9SSasha Levin {
155*1c7850f9SSasha Levin 	*qid = (struct p9_qid) {
156*1c7850f9SSasha Levin 		.path		= st->st_ino,
157*1c7850f9SSasha Levin 		.version	= st->st_mtime,
158*1c7850f9SSasha Levin 	};
159*1c7850f9SSasha Levin 
160*1c7850f9SSasha Levin 	if (S_ISDIR(st->st_mode))
161*1c7850f9SSasha Levin 		qid->type	|= P9_QTDIR;
162*1c7850f9SSasha Levin }
163*1c7850f9SSasha Levin 
164*1c7850f9SSasha Levin static void close_fid(u32 fid)
165*1c7850f9SSasha Levin {
166*1c7850f9SSasha Levin 	if (p9dev.fids[fid].fd > 0) {
167*1c7850f9SSasha Levin 		close(p9dev.fids[fid].fd);
168*1c7850f9SSasha Levin 		p9dev.fids[fid].fd = -1;
169*1c7850f9SSasha Levin 	}
170*1c7850f9SSasha Levin 	if (p9dev.fids[fid].dir) {
171*1c7850f9SSasha Levin 		closedir(p9dev.fids[fid].dir);
172*1c7850f9SSasha Levin 		p9dev.fids[fid].dir = NULL;
173*1c7850f9SSasha Levin 	}
174*1c7850f9SSasha Levin }
175*1c7850f9SSasha Levin 
176*1c7850f9SSasha Levin static void set_p9msg_hdr(struct p9_msg *msg, u32 size, u8 cmd, u16 tag)
177*1c7850f9SSasha Levin {
178*1c7850f9SSasha Levin 	*msg = (struct p9_msg) {
179*1c7850f9SSasha Levin 		.size	= size,
180*1c7850f9SSasha Levin 		.tag	= tag,
181*1c7850f9SSasha Levin 		.cmd	= cmd,
182*1c7850f9SSasha Levin 	};
183*1c7850f9SSasha Levin }
184*1c7850f9SSasha Levin 
185*1c7850f9SSasha Levin static bool virtio_p9_version(struct p9_msg *msg, u32 len, struct iovec *iov, u32 *outlen)
186*1c7850f9SSasha Levin {
187*1c7850f9SSasha Levin 	struct p9_msg *outmsg = iov[0].iov_base;
188*1c7850f9SSasha Levin 	struct p9_rversion *rversion = (struct p9_rversion *)outmsg->msg;
189*1c7850f9SSasha Levin 
190*1c7850f9SSasha Levin 	rversion->msize		= 4096;
191*1c7850f9SSasha Levin 	rversion->version.len	= strlen(VIRTIO_P9_VERSION);
192*1c7850f9SSasha Levin 	memcpy(&rversion->version.str, VIRTIO_P9_VERSION, rversion->version.len);
193*1c7850f9SSasha Levin 
194*1c7850f9SSasha Levin 	*outlen = VIRTIO_P9_HDR_LEN + rversion->version.len + sizeof(u16) + sizeof(u32);
195*1c7850f9SSasha Levin 	set_p9msg_hdr(outmsg, *outlen, P9_RVERSION, msg->tag);
196*1c7850f9SSasha Levin 
197*1c7850f9SSasha Levin 	return true;
198*1c7850f9SSasha Levin }
199*1c7850f9SSasha Levin 
200*1c7850f9SSasha Levin static bool virtio_p9_clunk(struct p9_msg *msg, u32 len, struct iovec *iov, u32 *outlen)
201*1c7850f9SSasha Levin {
202*1c7850f9SSasha Levin 	struct p9_msg *outmsg = iov[0].iov_base;
203*1c7850f9SSasha Levin 	struct p9_tclunk *tclunk = (struct p9_tclunk *)msg->msg;
204*1c7850f9SSasha Levin 
205*1c7850f9SSasha Levin 	close_fid(tclunk->fid);
206*1c7850f9SSasha Levin 
207*1c7850f9SSasha Levin 	*outlen = VIRTIO_P9_HDR_LEN;
208*1c7850f9SSasha Levin 	set_p9msg_hdr(outmsg, *outlen, P9_RCLUNK, msg->tag);
209*1c7850f9SSasha Levin 
210*1c7850f9SSasha Levin 	return true;
211*1c7850f9SSasha Levin }
212*1c7850f9SSasha Levin 
213*1c7850f9SSasha Levin static bool virtio_p9_open(struct p9_msg *msg, u32 len, struct iovec *iov, u32 *outlen)
214*1c7850f9SSasha Levin {
215*1c7850f9SSasha Levin 	struct p9_msg *outmsg	= iov[0].iov_base;
216*1c7850f9SSasha Levin 	struct p9_topen *topen	= (struct p9_topen *)msg->msg;
217*1c7850f9SSasha Levin 	struct p9_ropen *ropen	= (struct p9_ropen *)outmsg->msg;
218*1c7850f9SSasha Levin 	struct p9_fid *new_fid	= &p9dev.fids[topen->fid];
219*1c7850f9SSasha Levin 	struct stat st;
220*1c7850f9SSasha Levin 
221*1c7850f9SSasha Levin 	if (stat(new_fid->abs_path, &st) < 0)
222*1c7850f9SSasha Levin 		return false;
223*1c7850f9SSasha Levin 
224*1c7850f9SSasha Levin 	st2qid(&st, &ropen->qid);
225*1c7850f9SSasha Levin 	ropen->iounit = 0;
226*1c7850f9SSasha Levin 
227*1c7850f9SSasha Levin 	if (new_fid->is_dir)
228*1c7850f9SSasha Levin 		new_fid->dir	= opendir(new_fid->abs_path);
229*1c7850f9SSasha Levin 	else
230*1c7850f9SSasha Levin 		new_fid->fd	= open(new_fid->abs_path, omode2uflags(topen->mode));
231*1c7850f9SSasha Levin 
232*1c7850f9SSasha Levin 	*outlen = VIRTIO_P9_HDR_LEN + sizeof(*ropen);
233*1c7850f9SSasha Levin 	set_p9msg_hdr(outmsg, *outlen, P9_ROPEN, msg->tag);
234*1c7850f9SSasha Levin 
235*1c7850f9SSasha Levin 	return true;
236*1c7850f9SSasha Levin }
237*1c7850f9SSasha Levin 
238*1c7850f9SSasha Levin static bool virtio_p9_create(struct p9_msg *msg, u32 len, struct iovec *iov, u32 *outlen)
239*1c7850f9SSasha Levin {
240*1c7850f9SSasha Levin 	struct p9_msg *outmsg		= iov[0].iov_base;
241*1c7850f9SSasha Levin 	struct p9_tcreate *tcreate	= (struct p9_tcreate *)msg->msg;
242*1c7850f9SSasha Levin 	struct p9_rcreate *rcreate	= (struct p9_rcreate *)outmsg->msg;
243*1c7850f9SSasha Levin 	struct p9_fid *fid		= &p9dev.fids[tcreate->fid];
244*1c7850f9SSasha Levin 	struct stat st;
245*1c7850f9SSasha Levin 	u8 mode;
246*1c7850f9SSasha Levin 	u32 perm;
247*1c7850f9SSasha Levin 
248*1c7850f9SSasha Levin 	rcreate->iounit = 0;
249*1c7850f9SSasha Levin 
250*1c7850f9SSasha Levin 	/* Get last byte of the variable length struct */
251*1c7850f9SSasha Levin 	mode = *((u8 *)msg + msg->size - 1);
252*1c7850f9SSasha Levin 	perm = *(u32 *)((u8 *)msg + msg->size - 5);
253*1c7850f9SSasha Levin 
254*1c7850f9SSasha Levin 	sprintf(fid->path, "%s/%.*s", fid->path, tcreate->name.len, (char *)&tcreate->name.str);
255*1c7850f9SSasha Levin 
256*1c7850f9SSasha Levin 	close_fid(tcreate->fid);
257*1c7850f9SSasha Levin 
258*1c7850f9SSasha Levin 	if (perm & P9_DMDIR) {
259*1c7850f9SSasha Levin 		mkdir(fid->abs_path, perm & 0xFFFF);
260*1c7850f9SSasha Levin 		fid->dir = opendir(fid->abs_path);
261*1c7850f9SSasha Levin 		fid->is_dir = 1;
262*1c7850f9SSasha Levin 	} else {
263*1c7850f9SSasha Levin 		fid->fd = open(fid->abs_path, omode2uflags(mode) | O_CREAT, 0777);
264*1c7850f9SSasha Levin 	}
265*1c7850f9SSasha Levin 
266*1c7850f9SSasha Levin 	if (stat(fid->abs_path, &st) < 0)
267*1c7850f9SSasha Levin 		return false;
268*1c7850f9SSasha Levin 
269*1c7850f9SSasha Levin 	st2qid(&st, &rcreate->qid);
270*1c7850f9SSasha Levin 
271*1c7850f9SSasha Levin 	*outlen = VIRTIO_P9_HDR_LEN + sizeof(*rcreate);
272*1c7850f9SSasha Levin 	set_p9msg_hdr(outmsg, *outlen, P9_RCREATE, msg->tag);
273*1c7850f9SSasha Levin 
274*1c7850f9SSasha Levin 	return true;
275*1c7850f9SSasha Levin }
276*1c7850f9SSasha Levin 
277*1c7850f9SSasha Levin static bool virtio_p9_walk(struct p9_msg *msg, u32 len, struct iovec *iov, u32 *outlen)
278*1c7850f9SSasha Levin {
279*1c7850f9SSasha Levin 	struct p9_msg *outmsg	= iov[0].iov_base;
280*1c7850f9SSasha Levin 	struct p9_twalk *twalk	= (struct p9_twalk *)msg->msg;
281*1c7850f9SSasha Levin 	struct p9_rwalk *rwalk	= (struct p9_rwalk *)outmsg->msg;
282*1c7850f9SSasha Levin 	struct p9_str *str	= twalk->wnames;
283*1c7850f9SSasha Levin 	struct p9_fid *new_fid	= &p9dev.fids[twalk->newfid];
284*1c7850f9SSasha Levin 	u8 i;
285*1c7850f9SSasha Levin 
286*1c7850f9SSasha Levin 	rwalk->nwqid = 0;
287*1c7850f9SSasha Levin 	if (twalk->nwname) {
288*1c7850f9SSasha Levin 		struct p9_fid *fid = &p9dev.fids[twalk->fid];
289*1c7850f9SSasha Levin 
290*1c7850f9SSasha Levin 		for (i = 0; i < twalk->nwname; i++) {
291*1c7850f9SSasha Levin 			char tmp[PATH_MAX] = {0};
292*1c7850f9SSasha Levin 			char full_path[PATH_MAX];
293*1c7850f9SSasha Levin 			struct stat st;
294*1c7850f9SSasha Levin 
295*1c7850f9SSasha Levin 			/* Format the new path we're 'walk'ing into */
296*1c7850f9SSasha Levin 			sprintf(tmp, "%s/%.*s", fid->path, str->len, (char *)&str->str);
297*1c7850f9SSasha Levin 
298*1c7850f9SSasha Levin 			if (stat(rel_to_abs(tmp, full_path), &st) < 0)
299*1c7850f9SSasha Levin 				break;
300*1c7850f9SSasha Levin 
301*1c7850f9SSasha Levin 			st2qid(&st, &rwalk->wqids[i]);
302*1c7850f9SSasha Levin 			new_fid->is_dir = S_ISDIR(st.st_mode);
303*1c7850f9SSasha Levin 			strcpy(new_fid->path, tmp);
304*1c7850f9SSasha Levin 			new_fid->fid = twalk->newfid;
305*1c7850f9SSasha Levin 			rwalk->nwqid++;
306*1c7850f9SSasha Levin 		}
307*1c7850f9SSasha Levin 	} else {
308*1c7850f9SSasha Levin 		new_fid->is_dir = p9dev.fids[twalk->fid].is_dir;
309*1c7850f9SSasha Levin 		strcpy(new_fid->path, p9dev.fids[twalk->fid].path);
310*1c7850f9SSasha Levin 		new_fid->fid	= twalk->newfid;
311*1c7850f9SSasha Levin 	}
312*1c7850f9SSasha Levin 
313*1c7850f9SSasha Levin 	*outlen = VIRTIO_P9_HDR_LEN + sizeof(u16) + sizeof(struct p9_qid) * rwalk->nwqid;
314*1c7850f9SSasha Levin 	set_p9msg_hdr(outmsg, *outlen, P9_RWALK, msg->tag);
315*1c7850f9SSasha Levin 
316*1c7850f9SSasha Levin 	return true;
317*1c7850f9SSasha Levin }
318*1c7850f9SSasha Levin 
319*1c7850f9SSasha Levin static bool virtio_p9_attach(struct p9_msg *msg, u32 len, struct iovec *iov, u32 *outlen)
320*1c7850f9SSasha Levin {
321*1c7850f9SSasha Levin 	struct p9_msg *outmsg = iov[0].iov_base;
322*1c7850f9SSasha Levin 	struct p9_rattach *rattach = (struct p9_rattach *)outmsg->msg;
323*1c7850f9SSasha Levin 	struct p9_tattach *tattach = (struct p9_tattach *)msg->msg;
324*1c7850f9SSasha Levin 	struct stat st;
325*1c7850f9SSasha Levin 	struct p9_fid *fid;
326*1c7850f9SSasha Levin 	u32 i;
327*1c7850f9SSasha Levin 
328*1c7850f9SSasha Levin 	/* Reset everything */
329*1c7850f9SSasha Levin 	for (i = 0; i < VIRTIO_P9_MAX_FID; i++)
330*1c7850f9SSasha Levin 		p9dev.fids[i].fid = P9_NOFID;
331*1c7850f9SSasha Levin 
332*1c7850f9SSasha Levin 	if (stat(p9dev.root_dir, &st) < 0)
333*1c7850f9SSasha Levin 		return false;
334*1c7850f9SSasha Levin 
335*1c7850f9SSasha Levin 	st2qid(&st, &rattach->qid);
336*1c7850f9SSasha Levin 
337*1c7850f9SSasha Levin 	fid = &p9dev.fids[tattach->fid];
338*1c7850f9SSasha Levin 	fid->fid = tattach->fid;
339*1c7850f9SSasha Levin 	fid->is_dir = 1;
340*1c7850f9SSasha Levin 	strcpy(fid->path, "/");
341*1c7850f9SSasha Levin 
342*1c7850f9SSasha Levin 	*outlen = VIRTIO_P9_HDR_LEN + sizeof(*rattach);
343*1c7850f9SSasha Levin 	set_p9msg_hdr(outmsg, *outlen, P9_RATTACH, msg->tag);
344*1c7850f9SSasha Levin 
345*1c7850f9SSasha Levin 	return true;
346*1c7850f9SSasha Levin }
347*1c7850f9SSasha Levin 
348*1c7850f9SSasha Levin static u32 virtio_p9_fill_stat(const char *name, struct stat *st, struct p9_rstat *rstat)
349*1c7850f9SSasha Levin {
350*1c7850f9SSasha Levin 	struct p9_str *str;
351*1c7850f9SSasha Levin 
352*1c7850f9SSasha Levin 	rstat->stat.type = 0;
353*1c7850f9SSasha Levin 	rstat->stat.dev = 0;
354*1c7850f9SSasha Levin 	st2qid(st, &rstat->stat.qid);
355*1c7850f9SSasha Levin 	rstat->stat.mode = st->st_mode;
356*1c7850f9SSasha Levin 	rstat->stat.length = st->st_size;
357*1c7850f9SSasha Levin 	if (S_ISDIR(st->st_mode)) {
358*1c7850f9SSasha Levin 		rstat->stat.length = 0;
359*1c7850f9SSasha Levin 		rstat->stat.mode |= P9_DMDIR;
360*1c7850f9SSasha Levin 	}
361*1c7850f9SSasha Levin 
362*1c7850f9SSasha Levin 	rstat->stat.atime = st->st_atime;
363*1c7850f9SSasha Levin 	rstat->stat.mtime = st->st_mtime;
364*1c7850f9SSasha Levin 
365*1c7850f9SSasha Levin 	str = (struct p9_str *)&rstat->stat.name;
366*1c7850f9SSasha Levin 	str->len = strlen(name);
367*1c7850f9SSasha Levin 	memcpy(&str->str, name, str->len);
368*1c7850f9SSasha Levin 	str = (void *)str + str->len + sizeof(u16);
369*1c7850f9SSasha Levin 
370*1c7850f9SSasha Levin 	/* TODO: Pass usernames to the client */
371*1c7850f9SSasha Levin 	str->len = 0;
372*1c7850f9SSasha Levin 	str = (void *)str + sizeof(u16);
373*1c7850f9SSasha Levin 	str->len = 0;
374*1c7850f9SSasha Levin 	str = (void *)str + sizeof(u16);
375*1c7850f9SSasha Levin 	str->len = 0;
376*1c7850f9SSasha Levin 	str = (void *)str + sizeof(u16);
377*1c7850f9SSasha Levin 
378*1c7850f9SSasha Levin 	/* We subtract a u16 here because rstat->size doesn't include rstat->size itself */
379*1c7850f9SSasha Levin 	rstat->stat.size = (void *)str - (void *)&rstat->stat - sizeof(u16);
380*1c7850f9SSasha Levin 
381*1c7850f9SSasha Levin 	return rstat->stat.size + sizeof(u16);
382*1c7850f9SSasha Levin }
383*1c7850f9SSasha Levin 
384*1c7850f9SSasha Levin static bool virtio_p9_read(struct p9_msg *msg, u32 len, struct iovec *iov, int iovcnt, u32 *outlen)
385*1c7850f9SSasha Levin {
386*1c7850f9SSasha Levin 	struct p9_msg *outmsg	= iov[0].iov_base;
387*1c7850f9SSasha Levin 	struct p9_tread *tread	= (struct p9_tread *)msg->msg;
388*1c7850f9SSasha Levin 	struct p9_rread *rread	= (struct p9_rread *)outmsg->msg;
389*1c7850f9SSasha Levin 	struct p9_rstat *rstat	= (struct p9_rstat *)iov[1].iov_base;
390*1c7850f9SSasha Levin 	struct p9_fid *fid	= &p9dev.fids[tread->fid];
391*1c7850f9SSasha Levin 	struct stat st;
392*1c7850f9SSasha Levin 
393*1c7850f9SSasha Levin 	rread->count = 0;
394*1c7850f9SSasha Levin 
395*1c7850f9SSasha Levin 	if (fid->is_dir) {
396*1c7850f9SSasha Levin 		/* If reading a dir, fill the buffer with p9_stat entries */
397*1c7850f9SSasha Levin 		struct dirent *cur = readdir(fid->dir);
398*1c7850f9SSasha Levin 		char full_path[PATH_MAX];
399*1c7850f9SSasha Levin 
400*1c7850f9SSasha Levin 		while (cur) {
401*1c7850f9SSasha Levin 			u32 read;
402*1c7850f9SSasha Levin 
403*1c7850f9SSasha Levin 			stat(rel_to_abs(cur->d_name, full_path), &st);
404*1c7850f9SSasha Levin 			read = virtio_p9_fill_stat(cur->d_name, &st, rstat);
405*1c7850f9SSasha Levin 			rread->count += read;
406*1c7850f9SSasha Levin 			rstat = (void *)rstat + read;
407*1c7850f9SSasha Levin 			cur = readdir(fid->dir);
408*1c7850f9SSasha Levin 		}
409*1c7850f9SSasha Levin 	} else {
410*1c7850f9SSasha Levin 		iov[0].iov_base += VIRTIO_P9_HDR_LEN + sizeof(u32);
411*1c7850f9SSasha Levin 		iov[0].iov_len -= VIRTIO_P9_HDR_LEN + sizeof(u32);
412*1c7850f9SSasha Levin 		rread->count = preadv(fid->fd, iov, iovcnt, tread->offset);
413*1c7850f9SSasha Levin 		if (rread->count > tread->count)
414*1c7850f9SSasha Levin 			rread->count = tread->count;
415*1c7850f9SSasha Levin 	}
416*1c7850f9SSasha Levin 
417*1c7850f9SSasha Levin 	*outlen = VIRTIO_P9_HDR_LEN + sizeof(u32) + rread->count;
418*1c7850f9SSasha Levin 	set_p9msg_hdr(outmsg, *outlen, P9_RREAD, msg->tag);
419*1c7850f9SSasha Levin 
420*1c7850f9SSasha Levin 	return true;
421*1c7850f9SSasha Levin }
422*1c7850f9SSasha Levin 
423*1c7850f9SSasha Levin static bool virtio_p9_stat(struct p9_msg *msg, u32 len, struct iovec *iov, u32 *outlen)
424*1c7850f9SSasha Levin {
425*1c7850f9SSasha Levin 	struct p9_msg *outmsg = iov[0].iov_base;
426*1c7850f9SSasha Levin 	struct p9_tstat *tstat = (struct p9_tstat *)msg->msg;
427*1c7850f9SSasha Levin 	struct p9_rstat *rstat = (struct p9_rstat *)(outmsg->msg + sizeof(u16));
428*1c7850f9SSasha Levin 	struct stat st;
429*1c7850f9SSasha Levin 	struct p9_fid *fid = &p9dev.fids[tstat->fid];
430*1c7850f9SSasha Levin 	u32 ret;
431*1c7850f9SSasha Levin 
432*1c7850f9SSasha Levin 	if (stat(fid->abs_path, &st) < 0)
433*1c7850f9SSasha Levin 		return false;
434*1c7850f9SSasha Levin 
435*1c7850f9SSasha Levin 	ret = virtio_p9_fill_stat(fid->path, &st, rstat);
436*1c7850f9SSasha Levin 
437*1c7850f9SSasha Levin 	*outlen = VIRTIO_P9_HDR_LEN + ret + sizeof(u16) * 2;
438*1c7850f9SSasha Levin 	set_p9msg_hdr(outmsg, *outlen, P9_RSTAT, msg->tag);
439*1c7850f9SSasha Levin 	return true;
440*1c7850f9SSasha Levin }
441*1c7850f9SSasha Levin 
442*1c7850f9SSasha Levin static bool virtio_p9_wstat(struct p9_msg *msg, u32 len, struct iovec *iov, u32 *outlen)
443*1c7850f9SSasha Levin {
444*1c7850f9SSasha Levin 	struct p9_msg *outmsg = iov[0].iov_base;
445*1c7850f9SSasha Levin 	struct p9_twstat *twstat = (struct p9_twstat *)msg->msg;
446*1c7850f9SSasha Levin 	struct p9_str *str;
447*1c7850f9SSasha Levin 	struct p9_fid *fid = &p9dev.fids[twstat->fid];
448*1c7850f9SSasha Levin 	int res;
449*1c7850f9SSasha Levin 
450*1c7850f9SSasha Levin 	if (twstat->stat.length != -1UL)
451*1c7850f9SSasha Levin 		res = ftruncate(fid->fd, twstat->stat.length);
452*1c7850f9SSasha Levin 
453*1c7850f9SSasha Levin 	if (twstat->stat.mode != -1U)
454*1c7850f9SSasha Levin 		chmod(fid->abs_path, twstat->stat.mode & 0xFFFF);
455*1c7850f9SSasha Levin 
456*1c7850f9SSasha Levin 	str = (void *)&twstat->stat.name + sizeof(u16);
457*1c7850f9SSasha Levin 	if (str->len > 0) {
458*1c7850f9SSasha Levin 		char new_name[PATH_MAX] = {0};
459*1c7850f9SSasha Levin 		char full_path[PATH_MAX];
460*1c7850f9SSasha Levin 		char *last_dir = strrchr(fid->path, '/');
461*1c7850f9SSasha Levin 
462*1c7850f9SSasha Levin 		/* We need to get the full file name out of twstat->name */
463*1c7850f9SSasha Levin 		if (last_dir)
464*1c7850f9SSasha Levin 			strncpy(new_name, fid->path, last_dir - fid->path + 1);
465*1c7850f9SSasha Levin 
466*1c7850f9SSasha Levin 		memcpy(new_name + strlen(new_name), &str->str, str->len);
467*1c7850f9SSasha Levin 
468*1c7850f9SSasha Levin 		/* fid is reused for the new file */
469*1c7850f9SSasha Levin 		rename(fid->abs_path, rel_to_abs(new_name, full_path));
470*1c7850f9SSasha Levin 		sprintf(fid->path, "%s", new_name);
471*1c7850f9SSasha Levin 	}
472*1c7850f9SSasha Levin 
473*1c7850f9SSasha Levin 	*outlen = VIRTIO_P9_HDR_LEN;
474*1c7850f9SSasha Levin 	set_p9msg_hdr(outmsg, *outlen, P9_RWSTAT, msg->tag);
475*1c7850f9SSasha Levin 	return true;
476*1c7850f9SSasha Levin }
477*1c7850f9SSasha Levin 
478*1c7850f9SSasha Levin static bool virtio_p9_remove(struct p9_msg *msg, u32 len, struct iovec *iov, u32 *outlen)
479*1c7850f9SSasha Levin {
480*1c7850f9SSasha Levin 	struct p9_msg *outmsg = iov[0].iov_base;
481*1c7850f9SSasha Levin 	struct p9_tremove *tremove = (struct p9_tremove *)msg->msg;
482*1c7850f9SSasha Levin 	struct p9_fid *fid = &p9dev.fids[tremove->fid];
483*1c7850f9SSasha Levin 
484*1c7850f9SSasha Levin 	close_fid(tremove->fid);
485*1c7850f9SSasha Levin 	if (fid->is_dir)
486*1c7850f9SSasha Levin 		rmdir(fid->abs_path);
487*1c7850f9SSasha Levin 	else
488*1c7850f9SSasha Levin 		unlink(fid->abs_path);
489*1c7850f9SSasha Levin 
490*1c7850f9SSasha Levin 	*outlen = VIRTIO_P9_HDR_LEN;
491*1c7850f9SSasha Levin 	set_p9msg_hdr(outmsg, *outlen, P9_RREMOVE, msg->tag);
492*1c7850f9SSasha Levin 	return true;
493*1c7850f9SSasha Levin }
494*1c7850f9SSasha Levin 
495*1c7850f9SSasha Levin static bool virtio_p9_write(struct p9_msg *msg, u32 len, struct iovec *iov, int iovcnt, u32 *outlen)
496*1c7850f9SSasha Levin {
497*1c7850f9SSasha Levin 	struct p9_msg *outmsg;
498*1c7850f9SSasha Levin 	struct p9_rwrite *rwrite;
499*1c7850f9SSasha Levin 	struct p9_twrite *twrite = (struct p9_twrite *)msg->msg;
500*1c7850f9SSasha Levin 	struct p9_fid *fid = &p9dev.fids[twrite->fid];
501*1c7850f9SSasha Levin 
502*1c7850f9SSasha Levin 	if (iovcnt == 1) {
503*1c7850f9SSasha Levin 		outmsg = iov[0].iov_base;
504*1c7850f9SSasha Levin 		rwrite = (struct p9_rwrite *)outmsg->msg;
505*1c7850f9SSasha Levin 		rwrite->count = pwrite(fid->fd, &twrite->data, twrite->count, twrite->offset);
506*1c7850f9SSasha Levin 	} else {
507*1c7850f9SSasha Levin 		outmsg = iov[2].iov_base;
508*1c7850f9SSasha Levin 		rwrite = (struct p9_rwrite *)outmsg->msg;
509*1c7850f9SSasha Levin 		rwrite->count = pwrite(fid->fd, iov[1].iov_base, twrite->count, twrite->offset);
510*1c7850f9SSasha Levin 	}
511*1c7850f9SSasha Levin 
512*1c7850f9SSasha Levin 	*outlen = VIRTIO_P9_HDR_LEN + sizeof(u32);
513*1c7850f9SSasha Levin 	set_p9msg_hdr(outmsg, *outlen, P9_RWRITE, msg->tag);
514*1c7850f9SSasha Levin 
515*1c7850f9SSasha Levin 	return true;
516*1c7850f9SSasha Levin }
517*1c7850f9SSasha Levin 
518*1c7850f9SSasha Levin static bool virtio_p9_do_io_request(struct kvm *kvm, struct virt_queue *queue)
519*1c7850f9SSasha Levin {
520*1c7850f9SSasha Levin 	struct iovec iov[VIRTIO_P9_QUEUE_SIZE];
521*1c7850f9SSasha Levin 	u16 out, in, head;
522*1c7850f9SSasha Levin 	struct p9_msg *msg;
523*1c7850f9SSasha Levin 	u32 len = 0;
524*1c7850f9SSasha Levin 
525*1c7850f9SSasha Levin 	head		= virt_queue__get_iov(queue, iov, &out, &in, kvm);
526*1c7850f9SSasha Levin 	msg		= iov[0].iov_base;
527*1c7850f9SSasha Levin 
528*1c7850f9SSasha Levin 	switch (msg->cmd) {
529*1c7850f9SSasha Levin 	case P9_TVERSION:
530*1c7850f9SSasha Levin 		virtio_p9_version(msg, iov[0].iov_len, iov+1, &len);
531*1c7850f9SSasha Levin 		break;
532*1c7850f9SSasha Levin 	case P9_TATTACH:
533*1c7850f9SSasha Levin 		virtio_p9_attach(msg, iov[0].iov_len, iov+1, &len);
534*1c7850f9SSasha Levin 		break;
535*1c7850f9SSasha Levin 	case P9_TSTAT:
536*1c7850f9SSasha Levin 		virtio_p9_stat(msg, iov[0].iov_len, iov+1, &len);
537*1c7850f9SSasha Levin 		break;
538*1c7850f9SSasha Levin 	case P9_TCLUNK:
539*1c7850f9SSasha Levin 		virtio_p9_clunk(msg, iov[0].iov_len, iov+1, &len);
540*1c7850f9SSasha Levin 		break;
541*1c7850f9SSasha Levin 	case P9_TWALK:
542*1c7850f9SSasha Levin 		virtio_p9_walk(msg, iov[0].iov_len, iov+1, &len);
543*1c7850f9SSasha Levin 		break;
544*1c7850f9SSasha Levin 	case P9_TOPEN:
545*1c7850f9SSasha Levin 		virtio_p9_open(msg, iov[0].iov_len, iov+1, &len);
546*1c7850f9SSasha Levin 		break;
547*1c7850f9SSasha Levin 	case P9_TREAD:
548*1c7850f9SSasha Levin 		virtio_p9_read(msg, iov[0].iov_len, iov+1, in, &len);
549*1c7850f9SSasha Levin 		break;
550*1c7850f9SSasha Levin 	case P9_TCREATE:
551*1c7850f9SSasha Levin 		virtio_p9_create(msg, iov[0].iov_len, iov+1, &len);
552*1c7850f9SSasha Levin 		break;
553*1c7850f9SSasha Levin 	case P9_TWSTAT:
554*1c7850f9SSasha Levin 		virtio_p9_wstat(msg, iov[0].iov_len, iov+1, &len);
555*1c7850f9SSasha Levin 		break;
556*1c7850f9SSasha Levin 	case P9_TREMOVE:
557*1c7850f9SSasha Levin 		virtio_p9_remove(msg, iov[0].iov_len, iov+1, &len);
558*1c7850f9SSasha Levin 		break;
559*1c7850f9SSasha Levin 	case P9_TWRITE:
560*1c7850f9SSasha Levin 		virtio_p9_write(msg, iov[0].iov_len, iov+1, out, &len);
561*1c7850f9SSasha Levin 		break;
562*1c7850f9SSasha Levin 	default:
563*1c7850f9SSasha Levin 		printf("Unsupported P9 message type: %u\n", msg->cmd);
564*1c7850f9SSasha Levin 		break;
565*1c7850f9SSasha Levin 	}
566*1c7850f9SSasha Levin 	virt_queue__set_used_elem(queue, head, len);
567*1c7850f9SSasha Levin 
568*1c7850f9SSasha Levin 	return true;
569*1c7850f9SSasha Levin }
570*1c7850f9SSasha Levin 
571*1c7850f9SSasha Levin static void virtio_p9_do_io(struct kvm *kvm, void *param)
572*1c7850f9SSasha Levin {
573*1c7850f9SSasha Levin 	struct virt_queue *vq = param;
574*1c7850f9SSasha Levin 
575*1c7850f9SSasha Levin 	while (virt_queue__available(vq)) {
576*1c7850f9SSasha Levin 		virtio_p9_do_io_request(kvm, vq);
577*1c7850f9SSasha Levin 		virt_queue__trigger_irq(vq, virtio_p9_pci_device.irq_line, &p9dev.isr, kvm);
578*1c7850f9SSasha Levin 	}
579*1c7850f9SSasha Levin }
580*1c7850f9SSasha Levin 
581*1c7850f9SSasha Levin static bool virtio_p9_pci_io_out(struct kvm *kvm, u16 port, void *data, int size, u32 count)
582*1c7850f9SSasha Levin {
583*1c7850f9SSasha Levin 	unsigned long offset;
584*1c7850f9SSasha Levin 	bool ret = true;
585*1c7850f9SSasha Levin 
586*1c7850f9SSasha Levin 	offset		= port - IOPORT_VIRTIO_P9;
587*1c7850f9SSasha Levin 
588*1c7850f9SSasha Levin 	switch (offset) {
589*1c7850f9SSasha Levin 	case VIRTIO_MSI_QUEUE_VECTOR:
590*1c7850f9SSasha Levin 	case VIRTIO_PCI_GUEST_FEATURES:
591*1c7850f9SSasha Levin 		break;
592*1c7850f9SSasha Levin 	case VIRTIO_PCI_QUEUE_PFN: {
593*1c7850f9SSasha Levin 		struct virt_queue *queue;
594*1c7850f9SSasha Levin 		void *p;
595*1c7850f9SSasha Levin 
596*1c7850f9SSasha Levin 		queue			= &p9dev.vqs[p9dev.queue_selector];
597*1c7850f9SSasha Levin 		queue->pfn		= ioport__read32(data);
598*1c7850f9SSasha Levin 		p			= guest_pfn_to_host(kvm, queue->pfn);
599*1c7850f9SSasha Levin 
600*1c7850f9SSasha Levin 		vring_init(&queue->vring, VIRTIO_P9_QUEUE_SIZE, p, VIRTIO_PCI_VRING_ALIGN);
601*1c7850f9SSasha Levin 
602*1c7850f9SSasha Levin 		p9dev.jobs[p9dev.queue_selector] = thread_pool__add_job(kvm, virtio_p9_do_io, queue);
603*1c7850f9SSasha Levin 
604*1c7850f9SSasha Levin 		break;
605*1c7850f9SSasha Levin 	}
606*1c7850f9SSasha Levin 	case VIRTIO_PCI_QUEUE_SEL:
607*1c7850f9SSasha Levin 		p9dev.queue_selector	= ioport__read16(data);
608*1c7850f9SSasha Levin 		break;
609*1c7850f9SSasha Levin 	case VIRTIO_PCI_QUEUE_NOTIFY: {
610*1c7850f9SSasha Levin 		u16 queue_index;
611*1c7850f9SSasha Levin 		queue_index		= ioport__read16(data);
612*1c7850f9SSasha Levin 		thread_pool__do_job(p9dev.jobs[queue_index]);
613*1c7850f9SSasha Levin 		break;
614*1c7850f9SSasha Levin 	}
615*1c7850f9SSasha Levin 	case VIRTIO_PCI_STATUS:
616*1c7850f9SSasha Levin 		p9dev.status		= ioport__read8(data);
617*1c7850f9SSasha Levin 		break;
618*1c7850f9SSasha Levin 	case VIRTIO_MSI_CONFIG_VECTOR:
619*1c7850f9SSasha Levin 		p9dev.config_vector	= VIRTIO_MSI_NO_VECTOR;
620*1c7850f9SSasha Levin 		break;
621*1c7850f9SSasha Levin 	default:
622*1c7850f9SSasha Levin 		ret			= false;
623*1c7850f9SSasha Levin 		break;
624*1c7850f9SSasha Levin 	};
625*1c7850f9SSasha Levin 
626*1c7850f9SSasha Levin 	return ret;
627*1c7850f9SSasha Levin }
628*1c7850f9SSasha Levin 
629*1c7850f9SSasha Levin static struct ioport_operations virtio_p9_io_ops = {
630*1c7850f9SSasha Levin 	.io_in				= virtio_p9_pci_io_in,
631*1c7850f9SSasha Levin 	.io_out				= virtio_p9_pci_io_out,
632*1c7850f9SSasha Levin };
633*1c7850f9SSasha Levin 
634*1c7850f9SSasha Levin void virtio_9p__init(struct kvm *kvm, const char *root)
635*1c7850f9SSasha Levin {
636*1c7850f9SSasha Levin 	u8 pin, line, dev;
637*1c7850f9SSasha Levin 	u32 i, root_len;
638*1c7850f9SSasha Levin 
639*1c7850f9SSasha Levin 	p9dev.config = calloc(1, sizeof(*p9dev.config) + sizeof(VIRTIO_P9_TAG));
640*1c7850f9SSasha Levin 	if (p9dev.config == NULL)
641*1c7850f9SSasha Levin 		return;
642*1c7850f9SSasha Levin 
643*1c7850f9SSasha Levin 	strcpy(p9dev.root_dir, root);
644*1c7850f9SSasha Levin 	root_len = strlen(root);
645*1c7850f9SSasha Levin 
646*1c7850f9SSasha Levin 	/*
647*1c7850f9SSasha Levin 	 * We prefix the full path in all fids, This allows us to get the
648*1c7850f9SSasha Levin 	 * absolute path of an fid without playing with strings.
649*1c7850f9SSasha Levin 	 */
650*1c7850f9SSasha Levin 	for (i = 0; i < VIRTIO_P9_MAX_FID; i++) {
651*1c7850f9SSasha Levin 		strcpy(p9dev.fids[i].abs_path, root);
652*1c7850f9SSasha Levin 		p9dev.fids[i].path = p9dev.fids[i].abs_path + root_len;
653*1c7850f9SSasha Levin 	}
654*1c7850f9SSasha Levin 
655*1c7850f9SSasha Levin 	p9dev.config->tag_len = strlen(VIRTIO_P9_TAG);
656*1c7850f9SSasha Levin 	memcpy(p9dev.config->tag, VIRTIO_P9_TAG, strlen(VIRTIO_P9_TAG));
657*1c7850f9SSasha Levin 	p9dev.features |= 1 << VIRTIO_9P_MOUNT_TAG;
658*1c7850f9SSasha Levin 
659*1c7850f9SSasha Levin 	if (irq__register_device(VIRTIO_ID_9P, &dev, &pin, &line) < 0)
660*1c7850f9SSasha Levin 		return;
661*1c7850f9SSasha Levin 
662*1c7850f9SSasha Levin 	virtio_p9_pci_device.irq_pin	= pin;
663*1c7850f9SSasha Levin 	virtio_p9_pci_device.irq_line	= line;
664*1c7850f9SSasha Levin 	pci__register(&virtio_p9_pci_device, dev);
665*1c7850f9SSasha Levin 
666*1c7850f9SSasha Levin 	ioport__register(IOPORT_VIRTIO_P9, &virtio_p9_io_ops, IOPORT_VIRTIO_P9_SIZE);
667*1c7850f9SSasha Levin }
668