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