xref: /freebsd/usr.sbin/bhyve/pci_virtio_9p.c (revision 22d7dd834bc5cd189810e414701e3ad1e98102e4)
1 /*-
2  * Copyright (c) 2015 iXsystems Inc.
3  * Copyright (c) 2017-2018 Jakub Klama <jceel@FreeBSD.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer
11  *    in this position and unchanged.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 /*
30  * VirtIO filesystem passthrough using 9p protocol.
31  */
32 
33 #include <sys/cdefs.h>
34 #include <sys/param.h>
35 #include <sys/linker_set.h>
36 #include <sys/uio.h>
37 #include <sys/capsicum.h>
38 
39 #include <errno.h>
40 #include <fcntl.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <unistd.h>
45 #include <assert.h>
46 #include <pthread.h>
47 
48 #include <lib9p.h>
49 #include <backend/fs.h>
50 
51 #include "bhyverun.h"
52 #include "config.h"
53 #include "debug.h"
54 #include "pci_emul.h"
55 #include "virtio.h"
56 
57 #define	VT9P_MAX_IOV	128
58 #define VT9P_RINGSZ	256
59 #define	VT9P_MAXTAGSZ	256
60 #define	VT9P_CONFIGSPACESZ	(VT9P_MAXTAGSZ + sizeof(uint16_t))
61 
62 static int pci_vt9p_debug;
63 #define DPRINTF(params) if (pci_vt9p_debug) printf params
64 #define WPRINTF(params) printf params
65 
66 /*
67  * Per-device softc
68  */
69 struct pci_vt9p_softc {
70 	struct virtio_softc      vsc_vs;
71 	struct vqueue_info       vsc_vq;
72 	pthread_mutex_t          vsc_mtx;
73 	uint64_t                 vsc_cfg;
74 	uint64_t                 vsc_features;
75 	char *                   vsc_rootpath;
76 	struct pci_vt9p_config * vsc_config;
77 	struct l9p_backend *     vsc_fs_backend;
78 	struct l9p_server *      vsc_server;
79         struct l9p_connection *  vsc_conn;
80 };
81 
82 struct pci_vt9p_request {
83 	struct pci_vt9p_softc *	vsr_sc;
84 	struct iovec *		vsr_iov;
85 	size_t			vsr_niov;
86 	size_t			vsr_respidx;
87 	size_t			vsr_iolen;
88 	uint16_t		vsr_idx;
89 };
90 
91 struct pci_vt9p_config {
92 	uint16_t tag_len;
93 	char tag[0];
94 } __attribute__((packed));
95 
96 static int pci_vt9p_send(struct l9p_request *, const struct iovec *,
97     const size_t, const size_t, void *);
98 static void pci_vt9p_drop(struct l9p_request *, const struct iovec *, size_t,
99     void *);
100 static void pci_vt9p_reset(void *);
101 static void pci_vt9p_notify(void *, struct vqueue_info *);
102 static int pci_vt9p_cfgread(void *, int, int, uint32_t *);
103 static void pci_vt9p_neg_features(void *, uint64_t);
104 
105 static struct virtio_consts vt9p_vi_consts = {
106 	.vc_name =	"vt9p",
107 	.vc_nvq =	1,
108 	.vc_cfgsize =	VT9P_CONFIGSPACESZ,
109 	.vc_reset =	pci_vt9p_reset,
110 	.vc_qnotify =	pci_vt9p_notify,
111 	.vc_cfgread =	pci_vt9p_cfgread,
112 	.vc_apply_features = pci_vt9p_neg_features,
113 	.vc_hv_caps =	(1 << 0),
114 };
115 
116 static void
117 pci_vt9p_reset(void *vsc)
118 {
119 	struct pci_vt9p_softc *sc;
120 
121 	sc = vsc;
122 
123 	DPRINTF(("vt9p: device reset requested !\n"));
124 	vi_reset_dev(&sc->vsc_vs);
125 }
126 
127 static void
128 pci_vt9p_neg_features(void *vsc, uint64_t negotiated_features)
129 {
130 	struct pci_vt9p_softc *sc = vsc;
131 
132 	sc->vsc_features = negotiated_features;
133 }
134 
135 static int
136 pci_vt9p_cfgread(void *vsc, int offset, int size, uint32_t *retval)
137 {
138 	struct pci_vt9p_softc *sc = vsc;
139 	void *ptr;
140 
141 	ptr = (uint8_t *)sc->vsc_config + offset;
142 	memcpy(retval, ptr, size);
143 	return (0);
144 }
145 
146 static int
147 pci_vt9p_get_buffer(struct l9p_request *req, struct iovec *iov, size_t *niov,
148     void *arg __unused)
149 {
150 	struct pci_vt9p_request *preq = req->lr_aux;
151 	size_t n = preq->vsr_niov - preq->vsr_respidx;
152 
153 	memcpy(iov, preq->vsr_iov + preq->vsr_respidx,
154 	    n * sizeof(struct iovec));
155 	*niov = n;
156 	return (0);
157 }
158 
159 static int
160 pci_vt9p_send(struct l9p_request *req, const struct iovec *iov __unused,
161     const size_t niov __unused, const size_t iolen, void *arg __unused)
162 {
163 	struct pci_vt9p_request *preq = req->lr_aux;
164 	struct pci_vt9p_softc *sc = preq->vsr_sc;
165 
166 	preq->vsr_iolen = iolen;
167 
168 	pthread_mutex_lock(&sc->vsc_mtx);
169 	vq_relchain(&sc->vsc_vq, preq->vsr_idx, preq->vsr_iolen);
170 	vq_endchains(&sc->vsc_vq, 1);
171 	pthread_mutex_unlock(&sc->vsc_mtx);
172 	free(preq);
173 	return (0);
174 }
175 
176 static void
177 pci_vt9p_drop(struct l9p_request *req, const struct iovec *iov __unused,
178     size_t niov __unused, void *arg __unused)
179 {
180 	struct pci_vt9p_request *preq = req->lr_aux;
181 	struct pci_vt9p_softc *sc = preq->vsr_sc;
182 
183 	pthread_mutex_lock(&sc->vsc_mtx);
184 	vq_relchain(&sc->vsc_vq, preq->vsr_idx, 0);
185 	vq_endchains(&sc->vsc_vq, 1);
186 	pthread_mutex_unlock(&sc->vsc_mtx);
187 	free(preq);
188 }
189 
190 static void
191 pci_vt9p_notify(void *vsc, struct vqueue_info *vq)
192 {
193 	struct iovec iov[VT9P_MAX_IOV];
194 	struct pci_vt9p_softc *sc;
195 	struct pci_vt9p_request *preq;
196 	struct vi_req req;
197 	int n;
198 
199 	sc = vsc;
200 
201 	while (vq_has_descs(vq)) {
202 		n = vq_getchain(vq, iov, VT9P_MAX_IOV, &req);
203 		assert(n >= 1 && n <= VT9P_MAX_IOV);
204 		preq = calloc(1, sizeof(struct pci_vt9p_request));
205 		preq->vsr_sc = sc;
206 		preq->vsr_idx = req.idx;
207 		preq->vsr_iov = iov;
208 		preq->vsr_niov = n;
209 		preq->vsr_respidx = req.readable;
210 
211 		for (int i = 0; i < n; i++) {
212 			DPRINTF(("vt9p: vt9p_notify(): desc%d base=%p, "
213 			    "len=%zu\r\n", i, iov[i].iov_base,
214 			    iov[i].iov_len));
215 		}
216 
217 		l9p_connection_recv(sc->vsc_conn, iov, preq->vsr_respidx, preq);
218 	}
219 }
220 
221 static int
222 pci_vt9p_legacy_config(nvlist_t *nvl, const char *opts)
223 {
224 	char *sharename = NULL, *tofree, *token, *tokens;
225 
226 	if (opts == NULL)
227 		return (0);
228 
229 	tokens = tofree = strdup(opts);
230 	while ((token = strsep(&tokens, ",")) != NULL) {
231 		if (strchr(token, '=') != NULL) {
232 			if (sharename != NULL) {
233 				EPRINTLN(
234 			    "virtio-9p: more than one share name given");
235 				return (-1);
236 			}
237 
238 			sharename = strsep(&token, "=");
239 			set_config_value_node(nvl, "sharename", sharename);
240 			set_config_value_node(nvl, "path", token);
241 		} else
242 			set_config_bool_node(nvl, token, true);
243 	}
244 	free(tofree);
245 	return (0);
246 }
247 
248 static int
249 pci_vt9p_init(struct pci_devinst *pi, nvlist_t *nvl)
250 {
251 	struct pci_vt9p_softc *sc;
252 	const char *value;
253 	const char *sharename;
254 	int rootfd;
255 	bool ro;
256 	cap_rights_t rootcap;
257 
258 	ro = get_config_bool_node_default(nvl, "ro", false);
259 
260 	value = get_config_value_node(nvl, "path");
261 	if (value == NULL) {
262 		EPRINTLN("virtio-9p: path required");
263 		return (1);
264 	}
265 	rootfd = open(value, O_DIRECTORY);
266 	if (rootfd < 0) {
267 		EPRINTLN("virtio-9p: failed to open '%s': %s", value,
268 		    strerror(errno));
269 		return (-1);
270 	}
271 
272 	sharename = get_config_value_node(nvl, "sharename");
273 	if (sharename == NULL) {
274 		EPRINTLN("virtio-9p: share name required");
275 		return (1);
276 	}
277 	if (strlen(sharename) > VT9P_MAXTAGSZ) {
278 		EPRINTLN("virtio-9p: share name too long");
279 		return (1);
280 	}
281 
282 	sc = calloc(1, sizeof(struct pci_vt9p_softc));
283 	sc->vsc_config = calloc(1, sizeof(struct pci_vt9p_config) +
284 	    VT9P_MAXTAGSZ);
285 
286 	pthread_mutex_init(&sc->vsc_mtx, NULL);
287 
288 	cap_rights_init(&rootcap,
289 	    CAP_LOOKUP, CAP_ACL_CHECK, CAP_ACL_DELETE, CAP_ACL_GET,
290 	    CAP_ACL_SET, CAP_READ, CAP_WRITE, CAP_SEEK, CAP_FSTAT,
291 	    CAP_CREATE, CAP_FCHMODAT, CAP_FCHOWNAT, CAP_FTRUNCATE,
292 	    CAP_LINKAT_SOURCE, CAP_LINKAT_TARGET, CAP_MKDIRAT, CAP_MKNODAT,
293 	    CAP_PREAD, CAP_PWRITE, CAP_RENAMEAT_SOURCE, CAP_RENAMEAT_TARGET,
294 	    CAP_SEEK, CAP_SYMLINKAT, CAP_UNLINKAT, CAP_EXTATTR_DELETE,
295 	    CAP_EXTATTR_GET, CAP_EXTATTR_LIST, CAP_EXTATTR_SET,
296 	    CAP_FUTIMES, CAP_FSTATFS, CAP_FSYNC, CAP_FPATHCONF);
297 
298 	if (cap_rights_limit(rootfd, &rootcap) != 0)
299 		return (1);
300 
301 	sc->vsc_config->tag_len = (uint16_t)strlen(sharename);
302 	memcpy(sc->vsc_config->tag, sharename, sc->vsc_config->tag_len);
303 
304 	if (l9p_backend_fs_init(&sc->vsc_fs_backend, rootfd, ro) != 0) {
305 		errno = ENXIO;
306 		return (1);
307 	}
308 
309 	if (l9p_server_init(&sc->vsc_server, sc->vsc_fs_backend) != 0) {
310 		errno = ENXIO;
311 		return (1);
312 	}
313 
314 	if (l9p_connection_init(sc->vsc_server, &sc->vsc_conn) != 0) {
315 		errno = EIO;
316 		return (1);
317 	}
318 
319 	sc->vsc_conn->lc_msize = L9P_MAX_IOV * PAGE_SIZE;
320 	sc->vsc_conn->lc_lt.lt_get_response_buffer = pci_vt9p_get_buffer;
321 	sc->vsc_conn->lc_lt.lt_send_response = pci_vt9p_send;
322 	sc->vsc_conn->lc_lt.lt_drop_response = pci_vt9p_drop;
323 
324 	vi_softc_linkup(&sc->vsc_vs, &vt9p_vi_consts, sc, pi, &sc->vsc_vq);
325 	sc->vsc_vs.vs_mtx = &sc->vsc_mtx;
326 	sc->vsc_vq.vq_qsize = VT9P_RINGSZ;
327 
328 	/* initialize config space */
329 	pci_set_cfgdata16(pi, PCIR_DEVICE, VIRTIO_DEV_9P);
330 	pci_set_cfgdata16(pi, PCIR_VENDOR, VIRTIO_VENDOR);
331 	pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_STORAGE);
332 	pci_set_cfgdata16(pi, PCIR_SUBDEV_0, VIRTIO_ID_9P);
333 	pci_set_cfgdata16(pi, PCIR_SUBVEND_0, VIRTIO_VENDOR);
334 
335 	if (vi_intr_init(&sc->vsc_vs, 1, fbsdrun_virtio_msix()))
336 		return (1);
337 	vi_set_io_bar(&sc->vsc_vs, 0);
338 
339 	return (0);
340 }
341 
342 static const struct pci_devemu pci_de_v9p = {
343 	.pe_emu =	"virtio-9p",
344 	.pe_legacy_config = pci_vt9p_legacy_config,
345 	.pe_init =	pci_vt9p_init,
346 	.pe_barwrite =	vi_pci_write,
347 	.pe_barread =	vi_pci_read
348 };
349 PCI_EMUL_SET(pci_de_v9p);
350