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
35 #include <sys/param.h>
36 #include <sys/linker_set.h>
37 #include <sys/uio.h>
38 #ifndef WITHOUT_CAPSICUM
39 #include <sys/capsicum.h>
40 #endif
41
42 #include <errno.h>
43 #include <fcntl.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <unistd.h>
48 #include <assert.h>
49 #include <pthread.h>
50
51 #include <lib9p.h>
52 #include <backend/fs.h>
53
54 #include "bhyverun.h"
55 #include "config.h"
56 #include "debug.h"
57 #include "pci_emul.h"
58 #include "virtio.h"
59
60 #ifndef __FreeBSD__
61 #include "privileges.h"
62 #endif
63
64 #define VT9P_MAX_IOV 128
65 #define VT9P_RINGSZ 256
66 #define VT9P_MAXTAGSZ 256
67 #define VT9P_CONFIGSPACESZ (VT9P_MAXTAGSZ + sizeof(uint16_t))
68
69 static int pci_vt9p_debug;
70 #define DPRINTF(params) if (pci_vt9p_debug) printf params
71 #define WPRINTF(params) printf params
72
73 /*
74 * Per-device softc
75 */
76 struct pci_vt9p_softc {
77 struct virtio_softc vsc_vs;
78 struct vqueue_info vsc_vq;
79 pthread_mutex_t vsc_mtx;
80 uint64_t vsc_cfg;
81 uint64_t vsc_features;
82 char * vsc_rootpath;
83 struct pci_vt9p_config * vsc_config;
84 struct l9p_backend * vsc_fs_backend;
85 struct l9p_server * vsc_server;
86 struct l9p_connection * vsc_conn;
87 };
88
89 struct pci_vt9p_request {
90 struct pci_vt9p_softc * vsr_sc;
91 struct iovec * vsr_iov;
92 size_t vsr_niov;
93 size_t vsr_respidx;
94 size_t vsr_iolen;
95 uint16_t vsr_idx;
96 };
97
98 struct pci_vt9p_config {
99 uint16_t tag_len;
100 #ifdef __FreeBSD__
101 char tag[0];
102 #else
103 char tag[VT9P_MAXTAGSZ];
104 #endif
105 } __attribute__((packed));
106
107 static int pci_vt9p_send(struct l9p_request *, const struct iovec *,
108 const size_t, const size_t, void *);
109 static void pci_vt9p_drop(struct l9p_request *, const struct iovec *, size_t,
110 void *);
111 static void pci_vt9p_reset(void *);
112 static void pci_vt9p_notify(void *, struct vqueue_info *);
113 static int pci_vt9p_cfgread(void *, int, int, uint32_t *);
114 static void pci_vt9p_neg_features(void *, uint64_t);
115
116 static struct virtio_consts vt9p_vi_consts = {
117 .vc_name = "vt9p",
118 .vc_nvq = 1,
119 .vc_cfgsize = VT9P_CONFIGSPACESZ,
120 .vc_reset = pci_vt9p_reset,
121 .vc_qnotify = pci_vt9p_notify,
122 .vc_cfgread = pci_vt9p_cfgread,
123 .vc_apply_features = pci_vt9p_neg_features,
124 .vc_hv_caps = (1 << 0),
125 };
126
127 static void
pci_vt9p_reset(void * vsc)128 pci_vt9p_reset(void *vsc)
129 {
130 struct pci_vt9p_softc *sc;
131
132 sc = vsc;
133
134 DPRINTF(("vt9p: device reset requested !\n"));
135 vi_reset_dev(&sc->vsc_vs);
136 }
137
138 static void
pci_vt9p_neg_features(void * vsc,uint64_t negotiated_features)139 pci_vt9p_neg_features(void *vsc, uint64_t negotiated_features)
140 {
141 struct pci_vt9p_softc *sc = vsc;
142
143 sc->vsc_features = negotiated_features;
144 }
145
146 static int
pci_vt9p_cfgread(void * vsc,int offset,int size,uint32_t * retval)147 pci_vt9p_cfgread(void *vsc, int offset, int size, uint32_t *retval)
148 {
149 struct pci_vt9p_softc *sc = vsc;
150 void *ptr;
151
152 ptr = (uint8_t *)sc->vsc_config + offset;
153 memcpy(retval, ptr, size);
154 return (0);
155 }
156
157 static int
pci_vt9p_get_buffer(struct l9p_request * req,struct iovec * iov,size_t * niov,void * arg __unused)158 pci_vt9p_get_buffer(struct l9p_request *req, struct iovec *iov, size_t *niov,
159 void *arg __unused)
160 {
161 struct pci_vt9p_request *preq = req->lr_aux;
162 size_t n = preq->vsr_niov - preq->vsr_respidx;
163
164 memcpy(iov, preq->vsr_iov + preq->vsr_respidx,
165 n * sizeof(struct iovec));
166 *niov = n;
167 return (0);
168 }
169
170 static int
pci_vt9p_send(struct l9p_request * req,const struct iovec * iov __unused,const size_t niov __unused,const size_t iolen,void * arg __unused)171 pci_vt9p_send(struct l9p_request *req, const struct iovec *iov __unused,
172 const size_t niov __unused, const size_t iolen, void *arg __unused)
173 {
174 struct pci_vt9p_request *preq = req->lr_aux;
175 struct pci_vt9p_softc *sc = preq->vsr_sc;
176
177 preq->vsr_iolen = iolen;
178
179 pthread_mutex_lock(&sc->vsc_mtx);
180 vq_relchain(&sc->vsc_vq, preq->vsr_idx, preq->vsr_iolen);
181 vq_endchains(&sc->vsc_vq, 1);
182 pthread_mutex_unlock(&sc->vsc_mtx);
183 free(preq);
184 return (0);
185 }
186
187 static void
pci_vt9p_drop(struct l9p_request * req,const struct iovec * iov __unused,size_t niov __unused,void * arg __unused)188 pci_vt9p_drop(struct l9p_request *req, const struct iovec *iov __unused,
189 size_t niov __unused, void *arg __unused)
190 {
191 struct pci_vt9p_request *preq = req->lr_aux;
192 struct pci_vt9p_softc *sc = preq->vsr_sc;
193
194 pthread_mutex_lock(&sc->vsc_mtx);
195 vq_relchain(&sc->vsc_vq, preq->vsr_idx, 0);
196 vq_endchains(&sc->vsc_vq, 1);
197 pthread_mutex_unlock(&sc->vsc_mtx);
198 free(preq);
199 }
200
201 static void
pci_vt9p_notify(void * vsc,struct vqueue_info * vq)202 pci_vt9p_notify(void *vsc, struct vqueue_info *vq)
203 {
204 struct iovec iov[VT9P_MAX_IOV];
205 struct pci_vt9p_softc *sc;
206 struct pci_vt9p_request *preq;
207 struct vi_req req;
208 int n;
209
210 sc = vsc;
211
212 while (vq_has_descs(vq)) {
213 n = vq_getchain(vq, iov, VT9P_MAX_IOV, &req);
214 assert(n >= 1 && n <= VT9P_MAX_IOV);
215 preq = calloc(1, sizeof(struct pci_vt9p_request));
216 #ifndef __FreeBSD__
217 if (preq == NULL) {
218 EPRINTLN("virtio-9p: allocation failure: %s",
219 strerror(errno));
220 break;
221 }
222 #endif
223 preq->vsr_sc = sc;
224 preq->vsr_idx = req.idx;
225 preq->vsr_iov = iov;
226 preq->vsr_niov = n;
227 preq->vsr_respidx = req.readable;
228
229 for (int i = 0; i < n; i++) {
230 DPRINTF(("vt9p: vt9p_notify(): desc%d base=%p, "
231 "len=%zu\r\n", i, iov[i].iov_base,
232 iov[i].iov_len));
233 }
234
235 l9p_connection_recv(sc->vsc_conn, iov, preq->vsr_respidx, preq);
236 }
237 }
238
239 static int
pci_vt9p_legacy_config(nvlist_t * nvl,const char * opts)240 pci_vt9p_legacy_config(nvlist_t *nvl, const char *opts)
241 {
242 char *sharename = NULL, *tofree, *token, *tokens;
243
244 if (opts == NULL)
245 return (0);
246
247 tokens = tofree = strdup(opts);
248 while ((token = strsep(&tokens, ",")) != NULL) {
249 if (strchr(token, '=') != NULL) {
250 if (sharename != NULL) {
251 EPRINTLN(
252 "virtio-9p: more than one share name given");
253 return (-1);
254 }
255
256 sharename = strsep(&token, "=");
257 set_config_value_node(nvl, "sharename", sharename);
258 set_config_value_node(nvl, "path", token);
259 } else
260 set_config_bool_node(nvl, token, true);
261 }
262 free(tofree);
263
264 return (0);
265 }
266
267 static int
pci_vt9p_init(struct pci_devinst * pi,nvlist_t * nvl)268 pci_vt9p_init(struct pci_devinst *pi, nvlist_t *nvl)
269 {
270 struct pci_vt9p_softc *sc;
271 const char *value;
272 const char *sharename;
273 int rootfd;
274 bool ro;
275 #ifndef WITHOUT_CAPSICUM
276 cap_rights_t rootcap;
277 #endif
278
279 ro = get_config_bool_node_default(nvl, "ro", false);
280
281 #ifndef __FreeBSD__
282 illumos_priv_add_min(PRIV_FILE_DAC_READ, "vt9p");
283 illumos_priv_add_min(PRIV_FILE_DAC_SEARCH, "vt9p");
284
285 if (!ro) {
286 illumos_priv_add_min(PRIV_FILE_CHOWN, "vt9p");
287 illumos_priv_add_min(PRIV_FILE_CHOWN_SELF, "vt9p");
288 illumos_priv_add_min(PRIV_FILE_WRITE, "vt9p");
289 illumos_priv_add_min(PRIV_FILE_DAC_WRITE, "vt9p");
290 illumos_priv_add_min(PRIV_FILE_OWNER, "vt9p");
291 illumos_priv_add_min(PRIV_FILE_LINK_ANY, "vt9p");
292 }
293 #endif
294
295 value = get_config_value_node(nvl, "path");
296 if (value == NULL) {
297 EPRINTLN("virtio-9p: path required");
298 return (1);
299 }
300 rootfd = open(value, O_DIRECTORY);
301 if (rootfd < 0) {
302 EPRINTLN("virtio-9p: failed to open '%s': %s", value,
303 strerror(errno));
304 return (-1);
305 }
306
307 sharename = get_config_value_node(nvl, "sharename");
308 if (sharename == NULL) {
309 EPRINTLN("virtio-9p: share name required");
310 return (1);
311 }
312 if (strlen(sharename) > VT9P_MAXTAGSZ) {
313 EPRINTLN("virtio-9p: share name too long");
314 return (1);
315 }
316
317 sc = calloc(1, sizeof(struct pci_vt9p_softc));
318 #ifdef __FreeBSD__
319 sc->vsc_config = calloc(1, sizeof(struct pci_vt9p_config) +
320 VT9P_MAXTAGSZ);
321 #else
322 if (sc == NULL) {
323 EPRINTLN("virtio-9p: soft state allocation failure: %s",
324 strerror(errno));
325 return (1);
326 }
327 sc->vsc_config = calloc(1, sizeof(struct pci_vt9p_config));
328 if (sc == NULL) {
329 EPRINTLN("virtio-9p: vsc_config allocation failure: %s",
330 strerror(errno));
331 return (1);
332 }
333 #endif
334
335 pthread_mutex_init(&sc->vsc_mtx, NULL);
336
337 #ifndef WITHOUT_CAPSICUM
338 cap_rights_init(&rootcap,
339 CAP_LOOKUP, CAP_ACL_CHECK, CAP_ACL_DELETE, CAP_ACL_GET,
340 CAP_ACL_SET, CAP_READ, CAP_WRITE, CAP_SEEK, CAP_FSTAT,
341 CAP_CREATE, CAP_FCHMODAT, CAP_FCHOWNAT, CAP_FTRUNCATE,
342 CAP_LINKAT_SOURCE, CAP_LINKAT_TARGET, CAP_MKDIRAT, CAP_MKNODAT,
343 CAP_PREAD, CAP_PWRITE, CAP_RENAMEAT_SOURCE, CAP_RENAMEAT_TARGET,
344 CAP_SEEK, CAP_SYMLINKAT, CAP_UNLINKAT, CAP_EXTATTR_DELETE,
345 CAP_EXTATTR_GET, CAP_EXTATTR_LIST, CAP_EXTATTR_SET,
346 CAP_FUTIMES, CAP_FSTATFS, CAP_FSYNC, CAP_FPATHCONF);
347
348 if (cap_rights_limit(rootfd, &rootcap) != 0)
349 return (1);
350 #endif
351
352 sc->vsc_config->tag_len = (uint16_t)strlen(sharename);
353 memcpy(sc->vsc_config->tag, sharename, sc->vsc_config->tag_len);
354
355 if (l9p_backend_fs_init(&sc->vsc_fs_backend, rootfd, ro) != 0) {
356 errno = ENXIO;
357 return (1);
358 }
359
360 if (l9p_server_init(&sc->vsc_server, sc->vsc_fs_backend) != 0) {
361 errno = ENXIO;
362 return (1);
363 }
364
365 if (l9p_connection_init(sc->vsc_server, &sc->vsc_conn) != 0) {
366 errno = EIO;
367 return (1);
368 }
369
370 sc->vsc_conn->lc_msize = L9P_MAX_IOV * PAGE_SIZE;
371 sc->vsc_conn->lc_lt.lt_get_response_buffer = pci_vt9p_get_buffer;
372 sc->vsc_conn->lc_lt.lt_send_response = pci_vt9p_send;
373 sc->vsc_conn->lc_lt.lt_drop_response = pci_vt9p_drop;
374
375 vi_softc_linkup(&sc->vsc_vs, &vt9p_vi_consts, sc, pi, &sc->vsc_vq);
376 sc->vsc_vs.vs_mtx = &sc->vsc_mtx;
377 sc->vsc_vq.vq_qsize = VT9P_RINGSZ;
378
379 /* initialize config space */
380 pci_set_cfgdata16(pi, PCIR_DEVICE, VIRTIO_DEV_9P);
381 pci_set_cfgdata16(pi, PCIR_VENDOR, VIRTIO_VENDOR);
382 pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_STORAGE);
383 pci_set_cfgdata16(pi, PCIR_SUBDEV_0, VIRTIO_ID_9P);
384 pci_set_cfgdata16(pi, PCIR_SUBVEND_0, VIRTIO_VENDOR);
385
386 if (vi_intr_init(&sc->vsc_vs, 1, fbsdrun_virtio_msix()))
387 return (1);
388 vi_set_io_bar(&sc->vsc_vs, 0);
389
390 return (0);
391 }
392
393 static const struct pci_devemu pci_de_v9p = {
394 .pe_emu = "virtio-9p",
395 .pe_legacy_config = pci_vt9p_legacy_config,
396 .pe_init = pci_vt9p_init,
397 .pe_barwrite = vi_pci_write,
398 .pe_barread = vi_pci_read
399 };
400 PCI_EMUL_SET(pci_de_v9p);
401