]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/bhyve/pci_virtio_9p.c
ident(1): Normalizing date format
[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 "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         "vt9p",                 /* our name */
107         1,                      /* we support 1 virtqueue */
108         VT9P_CONFIGSPACESZ,     /* config reg size */
109         pci_vt9p_reset,         /* reset */
110         pci_vt9p_notify,        /* device-wide qnotify */
111         pci_vt9p_cfgread,       /* read virtio config */
112         NULL,                   /* write virtio config */
113         pci_vt9p_neg_features,  /* apply negotiated features */
114         (1 << 0),               /* our capabilities */
115 };
116
117
118 static void
119 pci_vt9p_reset(void *vsc)
120 {
121         struct pci_vt9p_softc *sc;
122
123         sc = vsc;
124
125         DPRINTF(("vt9p: device reset requested !\n"));
126         vi_reset_dev(&sc->vsc_vs);
127 }
128
129 static void
130 pci_vt9p_neg_features(void *vsc, uint64_t negotiated_features)
131 {
132         struct pci_vt9p_softc *sc = vsc;
133
134         sc->vsc_features = negotiated_features;
135 }
136
137 static int
138 pci_vt9p_cfgread(void *vsc, int offset, int size, uint32_t *retval)
139 {
140         struct pci_vt9p_softc *sc = vsc;
141         void *ptr;
142
143         ptr = (uint8_t *)sc->vsc_config + offset;
144         memcpy(retval, ptr, size);
145         return (0);
146 }
147
148 static int
149 pci_vt9p_get_buffer(struct l9p_request *req, struct iovec *iov, size_t *niov,
150     void *arg)
151 {
152         struct pci_vt9p_request *preq = req->lr_aux;
153         size_t n = preq->vsr_niov - preq->vsr_respidx;
154         
155         memcpy(iov, preq->vsr_iov + preq->vsr_respidx,
156             n * sizeof(struct iovec));
157         *niov = n;
158         return (0);
159 }
160
161 static int
162 pci_vt9p_send(struct l9p_request *req, const struct iovec *iov,
163     const size_t niov, const size_t iolen, void *arg)
164 {
165         struct pci_vt9p_request *preq = req->lr_aux;
166         struct pci_vt9p_softc *sc = preq->vsr_sc;
167
168         preq->vsr_iolen = iolen;
169
170         pthread_mutex_lock(&sc->vsc_mtx);
171         vq_relchain(&sc->vsc_vq, preq->vsr_idx, preq->vsr_iolen);
172         vq_endchains(&sc->vsc_vq, 1);
173         pthread_mutex_unlock(&sc->vsc_mtx);
174         free(preq);
175         return (0);
176 }
177
178 static void
179 pci_vt9p_drop(struct l9p_request *req, const struct iovec *iov, size_t niov,
180     void *arg)
181 {
182         struct pci_vt9p_request *preq = req->lr_aux;
183         struct pci_vt9p_softc *sc = preq->vsr_sc;
184
185         pthread_mutex_lock(&sc->vsc_mtx);
186         vq_relchain(&sc->vsc_vq, preq->vsr_idx, 0);
187         vq_endchains(&sc->vsc_vq, 1);
188         pthread_mutex_unlock(&sc->vsc_mtx);
189         free(preq);
190 }
191
192 static void
193 pci_vt9p_notify(void *vsc, struct vqueue_info *vq)
194 {
195         struct iovec iov[VT9P_MAX_IOV];
196         struct pci_vt9p_softc *sc;
197         struct pci_vt9p_request *preq;
198         uint16_t idx, n, i;
199         uint16_t flags[VT9P_MAX_IOV];
200
201         sc = vsc;
202
203         while (vq_has_descs(vq)) {
204                 n = vq_getchain(vq, &idx, iov, VT9P_MAX_IOV, flags);
205                 preq = calloc(1, sizeof(struct pci_vt9p_request));
206                 preq->vsr_sc = sc;
207                 preq->vsr_idx = idx;
208                 preq->vsr_iov = iov;
209                 preq->vsr_niov = n;
210                 preq->vsr_respidx = 0;
211
212                 /* Count readable descriptors */
213                 for (i = 0; i < n; i++) {
214                         if (flags[i] & VRING_DESC_F_WRITE)
215                                 break;
216
217                         preq->vsr_respidx++;
218                 }
219
220                 for (int i = 0; i < n; i++) {
221                         DPRINTF(("vt9p: vt9p_notify(): desc%d base=%p, "
222                             "len=%zu, flags=0x%04x\r\n", i, iov[i].iov_base,
223                             iov[i].iov_len, flags[i]));
224                 }
225
226                 l9p_connection_recv(sc->vsc_conn, iov, preq->vsr_respidx, preq);
227         }
228 }
229
230
231 static int
232 pci_vt9p_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
233 {
234         struct pci_vt9p_softc *sc;
235         char *opt;
236         char *sharename = NULL;
237         char *rootpath = NULL;
238         int rootfd;
239         bool ro = false;
240         cap_rights_t rootcap;
241
242         if (opts == NULL) {
243                 printf("virtio-9p: share name and path required\n");
244                 return (1);
245         }
246
247         while ((opt = strsep(&opts, ",")) != NULL) {
248                 if (strchr(opt, '=') != NULL) {
249                         if (sharename != NULL) {
250                                 printf("virtio-9p: more than one share name given\n");
251                                 return (1);
252                         }
253
254                         sharename = strsep(&opt, "=");
255                         rootpath = opt;
256                         continue;
257                 }
258
259                 if (strcmp(opt, "ro") == 0) {
260                         DPRINTF(("read-only mount requested\r\n"));
261                         ro = true;
262                         continue;
263                 }
264
265                 printf("virtio-9p: invalid option '%s'\n", opt);
266                 return (1);
267         }
268
269         if (strlen(sharename) > VT9P_MAXTAGSZ) {
270                 printf("virtio-9p: share name too long\n");
271                 return (1);
272         }
273
274         rootfd = open(rootpath, O_DIRECTORY);
275         if (rootfd < 0)
276                 return (-1);
277
278         sc = calloc(1, sizeof(struct pci_vt9p_softc));
279         sc->vsc_config = calloc(1, sizeof(struct pci_vt9p_config) +
280             VT9P_MAXTAGSZ);
281
282         pthread_mutex_init(&sc->vsc_mtx, NULL);
283
284         cap_rights_init(&rootcap,
285             CAP_LOOKUP, CAP_ACL_CHECK, CAP_ACL_DELETE, CAP_ACL_GET,
286             CAP_ACL_SET, CAP_READ, CAP_WRITE, CAP_SEEK, CAP_FSTAT,
287             CAP_CREATE, CAP_FCHMODAT, CAP_FCHOWNAT, CAP_FTRUNCATE,
288             CAP_LINKAT_SOURCE, CAP_LINKAT_TARGET, CAP_MKDIRAT, CAP_MKNODAT,
289             CAP_PREAD, CAP_PWRITE, CAP_RENAMEAT_SOURCE, CAP_RENAMEAT_TARGET,
290             CAP_SEEK, CAP_SYMLINKAT, CAP_UNLINKAT, CAP_EXTATTR_DELETE,
291             CAP_EXTATTR_GET, CAP_EXTATTR_LIST, CAP_EXTATTR_SET,
292             CAP_FUTIMES, CAP_FSTATFS, CAP_FSYNC, CAP_FPATHCONF);
293
294         if (cap_rights_limit(rootfd, &rootcap) != 0)
295                 return (1);
296
297         sc->vsc_config->tag_len = (uint16_t)strlen(sharename);
298         memcpy(sc->vsc_config->tag, sharename, sc->vsc_config->tag_len);
299         
300         if (l9p_backend_fs_init(&sc->vsc_fs_backend, rootfd, ro) != 0) {
301                 errno = ENXIO;
302                 return (1);
303         }
304
305         if (l9p_server_init(&sc->vsc_server, sc->vsc_fs_backend) != 0) {
306                 errno = ENXIO;
307                 return (1);
308         }
309
310         if (l9p_connection_init(sc->vsc_server, &sc->vsc_conn) != 0) {
311                 errno = EIO;
312                 return (1);
313         }
314
315         sc->vsc_conn->lc_msize = L9P_MAX_IOV * PAGE_SIZE;
316         sc->vsc_conn->lc_lt.lt_get_response_buffer = pci_vt9p_get_buffer;
317         sc->vsc_conn->lc_lt.lt_send_response = pci_vt9p_send;
318         sc->vsc_conn->lc_lt.lt_drop_response = pci_vt9p_drop;
319
320         vi_softc_linkup(&sc->vsc_vs, &vt9p_vi_consts, sc, pi, &sc->vsc_vq);
321         sc->vsc_vs.vs_mtx = &sc->vsc_mtx;
322         sc->vsc_vq.vq_qsize = VT9P_RINGSZ;
323
324         /* initialize config space */
325         pci_set_cfgdata16(pi, PCIR_DEVICE, VIRTIO_DEV_9P);
326         pci_set_cfgdata16(pi, PCIR_VENDOR, VIRTIO_VENDOR);
327         pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_STORAGE);
328         pci_set_cfgdata16(pi, PCIR_SUBDEV_0, VIRTIO_TYPE_9P);
329         pci_set_cfgdata16(pi, PCIR_SUBVEND_0, VIRTIO_VENDOR);
330
331         if (vi_intr_init(&sc->vsc_vs, 1, fbsdrun_virtio_msix()))
332                 return (1);
333         vi_set_io_bar(&sc->vsc_vs, 0);
334
335         return (0);
336 }
337
338 struct pci_devemu pci_de_v9p = {
339         .pe_emu =       "virtio-9p",
340         .pe_init =      pci_vt9p_init,
341         .pe_barwrite =  vi_pci_write,
342         .pe_barread =   vi_pci_read
343 };
344 PCI_EMUL_SET(pci_de_v9p);