2 * Copyright (c) 2015 iXsystems Inc.
3 * Copyright (c) 2017-2018 Jakub Klama <jceel@FreeBSD.org>
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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.
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
30 * VirtIO filesystem passthrough using 9p protocol.
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
36 #include <sys/param.h>
37 #include <sys/linker_set.h>
39 #include <sys/capsicum.h>
51 #include <backend/fs.h>
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))
64 static int pci_vt9p_debug;
65 #define DPRINTF(params) if (pci_vt9p_debug) printf params
66 #define WPRINTF(params) printf params
71 struct pci_vt9p_softc {
72 struct virtio_softc vsc_vs;
73 struct vqueue_info vsc_vq;
74 pthread_mutex_t vsc_mtx;
76 uint64_t vsc_features;
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;
84 struct pci_vt9p_request {
85 struct pci_vt9p_softc * vsr_sc;
86 struct iovec * vsr_iov;
93 struct pci_vt9p_config {
96 } __attribute__((packed));
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,
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);
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 */
121 pci_vt9p_reset(void *vsc)
123 struct pci_vt9p_softc *sc;
127 DPRINTF(("vt9p: device reset requested !\n"));
128 vi_reset_dev(&sc->vsc_vs);
132 pci_vt9p_neg_features(void *vsc, uint64_t negotiated_features)
134 struct pci_vt9p_softc *sc = vsc;
136 sc->vsc_features = negotiated_features;
140 pci_vt9p_cfgread(void *vsc, int offset, int size, uint32_t *retval)
142 struct pci_vt9p_softc *sc = vsc;
145 ptr = (uint8_t *)sc->vsc_config + offset;
146 memcpy(retval, ptr, size);
151 pci_vt9p_get_buffer(struct l9p_request *req, struct iovec *iov, size_t *niov,
154 struct pci_vt9p_request *preq = req->lr_aux;
155 size_t n = preq->vsr_niov - preq->vsr_respidx;
157 memcpy(iov, preq->vsr_iov + preq->vsr_respidx,
158 n * sizeof(struct iovec));
164 pci_vt9p_send(struct l9p_request *req, const struct iovec *iov,
165 const size_t niov, const size_t iolen, void *arg)
167 struct pci_vt9p_request *preq = req->lr_aux;
168 struct pci_vt9p_softc *sc = preq->vsr_sc;
170 preq->vsr_iolen = iolen;
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);
181 pci_vt9p_drop(struct l9p_request *req, const struct iovec *iov, size_t niov,
184 struct pci_vt9p_request *preq = req->lr_aux;
185 struct pci_vt9p_softc *sc = preq->vsr_sc;
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);
195 pci_vt9p_notify(void *vsc, struct vqueue_info *vq)
197 struct iovec iov[VT9P_MAX_IOV];
198 struct pci_vt9p_softc *sc;
199 struct pci_vt9p_request *preq;
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));
210 preq->vsr_idx = req.idx;
213 preq->vsr_respidx = req.readable;
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,
221 l9p_connection_recv(sc->vsc_conn, iov, preq->vsr_respidx, preq);
226 pci_vt9p_legacy_config(nvlist_t *nvl, const char *opts)
228 char *sharename = NULL, *tofree, *token, *tokens;
233 tokens = tofree = strdup(opts);
234 while ((token = strsep(&tokens, ",")) != NULL) {
235 if (strchr(token, '=') != NULL) {
236 if (sharename != NULL) {
238 "virtio-9p: more than one share name given");
242 sharename = strsep(&token, "=");
243 set_config_value_node(nvl, "sharename", sharename);
244 set_config_value_node(nvl, "path", token);
246 set_config_bool_node(nvl, token, true);
253 pci_vt9p_init(struct vmctx *ctx, struct pci_devinst *pi, nvlist_t *nvl)
255 struct pci_vt9p_softc *sc;
257 const char *sharename;
260 cap_rights_t rootcap;
262 ro = get_config_bool_node_default(nvl, "ro", false);
264 value = get_config_value_node(nvl, "path");
266 EPRINTLN("virtio-9p: path required");
269 rootfd = open(value, O_DIRECTORY);
271 EPRINTLN("virtio-9p: failed to open '%s': %s", value,
276 sharename = get_config_value_node(nvl, "sharename");
277 if (sharename == NULL) {
278 EPRINTLN("virtio-9p: share name required");
281 if (strlen(sharename) > VT9P_MAXTAGSZ) {
282 EPRINTLN("virtio-9p: share name too long");
286 sc = calloc(1, sizeof(struct pci_vt9p_softc));
287 sc->vsc_config = calloc(1, sizeof(struct pci_vt9p_config) +
290 pthread_mutex_init(&sc->vsc_mtx, NULL);
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);
302 if (cap_rights_limit(rootfd, &rootcap) != 0)
305 sc->vsc_config->tag_len = (uint16_t)strlen(sharename);
306 memcpy(sc->vsc_config->tag, sharename, sc->vsc_config->tag_len);
308 if (l9p_backend_fs_init(&sc->vsc_fs_backend, rootfd, ro) != 0) {
313 if (l9p_server_init(&sc->vsc_server, sc->vsc_fs_backend) != 0) {
318 if (l9p_connection_init(sc->vsc_server, &sc->vsc_conn) != 0) {
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;
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;
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);
339 if (vi_intr_init(&sc->vsc_vs, 1, fbsdrun_virtio_msix()))
341 vi_set_io_bar(&sc->vsc_vs, 0);
346 static const 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
353 PCI_EMUL_SET(pci_de_v9p);