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 #ifndef WITHOUT_CAPSICUM
40 #include <sys/capsicum.h>
53 #include <backend/fs.h>
62 #include "privileges.h"
65 #define VT9P_MAX_IOV 128
66 #define VT9P_RINGSZ 256
67 #define VT9P_MAXTAGSZ 256
68 #define VT9P_CONFIGSPACESZ (VT9P_MAXTAGSZ + sizeof(uint16_t))
70 static int pci_vt9p_debug
;
71 #define DPRINTF(params) if (pci_vt9p_debug) printf params
72 #define WPRINTF(params) printf params
77 struct pci_vt9p_softc
{
78 struct virtio_softc vsc_vs
;
79 struct vqueue_info vsc_vq
;
80 pthread_mutex_t vsc_mtx
;
82 uint64_t vsc_features
;
84 struct pci_vt9p_config
* vsc_config
;
85 struct l9p_backend
* vsc_fs_backend
;
86 struct l9p_server
* vsc_server
;
87 struct l9p_connection
* vsc_conn
;
90 struct pci_vt9p_request
{
91 struct pci_vt9p_softc
* vsr_sc
;
92 struct iovec
* vsr_iov
;
99 struct pci_vt9p_config
{
104 char tag
[VT9P_MAXTAGSZ
];
106 } __attribute__((packed
));
108 static int pci_vt9p_send(struct l9p_request
*, const struct iovec
*,
109 const size_t, const size_t, void *);
110 static void pci_vt9p_drop(struct l9p_request
*, const struct iovec
*, size_t,
112 static void pci_vt9p_reset(void *);
113 static void pci_vt9p_notify(void *, struct vqueue_info
*);
114 static int pci_vt9p_cfgread(void *, int, int, uint32_t *);
115 static void pci_vt9p_neg_features(void *, uint64_t);
117 static struct virtio_consts vt9p_vi_consts
= {
120 .vc_cfgsize
= VT9P_CONFIGSPACESZ
,
121 .vc_reset
= pci_vt9p_reset
,
122 .vc_qnotify
= pci_vt9p_notify
,
123 .vc_cfgread
= pci_vt9p_cfgread
,
124 .vc_apply_features
= pci_vt9p_neg_features
,
125 .vc_hv_caps
= (1 << 0),
129 pci_vt9p_reset(void *vsc
)
131 struct pci_vt9p_softc
*sc
;
135 DPRINTF(("vt9p: device reset requested !\n"));
136 vi_reset_dev(&sc
->vsc_vs
);
140 pci_vt9p_neg_features(void *vsc
, uint64_t negotiated_features
)
142 struct pci_vt9p_softc
*sc
= vsc
;
144 sc
->vsc_features
= negotiated_features
;
148 pci_vt9p_cfgread(void *vsc
, int offset
, int size
, uint32_t *retval
)
150 struct pci_vt9p_softc
*sc
= vsc
;
153 ptr
= (uint8_t *)sc
->vsc_config
+ offset
;
154 memcpy(retval
, ptr
, size
);
159 pci_vt9p_get_buffer(struct l9p_request
*req
, struct iovec
*iov
, size_t *niov
,
162 struct pci_vt9p_request
*preq
= req
->lr_aux
;
163 size_t n
= preq
->vsr_niov
- preq
->vsr_respidx
;
165 memcpy(iov
, preq
->vsr_iov
+ preq
->vsr_respidx
,
166 n
* sizeof(struct iovec
));
172 pci_vt9p_send(struct l9p_request
*req
, const struct iovec
*iov __unused
,
173 const size_t niov __unused
, const size_t iolen
, void *arg __unused
)
175 struct pci_vt9p_request
*preq
= req
->lr_aux
;
176 struct pci_vt9p_softc
*sc
= preq
->vsr_sc
;
178 preq
->vsr_iolen
= iolen
;
180 pthread_mutex_lock(&sc
->vsc_mtx
);
181 vq_relchain(&sc
->vsc_vq
, preq
->vsr_idx
, preq
->vsr_iolen
);
182 vq_endchains(&sc
->vsc_vq
, 1);
183 pthread_mutex_unlock(&sc
->vsc_mtx
);
189 pci_vt9p_drop(struct l9p_request
*req
, const struct iovec
*iov __unused
,
190 size_t niov __unused
, void *arg __unused
)
192 struct pci_vt9p_request
*preq
= req
->lr_aux
;
193 struct pci_vt9p_softc
*sc
= preq
->vsr_sc
;
195 pthread_mutex_lock(&sc
->vsc_mtx
);
196 vq_relchain(&sc
->vsc_vq
, preq
->vsr_idx
, 0);
197 vq_endchains(&sc
->vsc_vq
, 1);
198 pthread_mutex_unlock(&sc
->vsc_mtx
);
203 pci_vt9p_notify(void *vsc
, struct vqueue_info
*vq
)
205 struct iovec iov
[VT9P_MAX_IOV
];
206 struct pci_vt9p_softc
*sc
;
207 struct pci_vt9p_request
*preq
;
213 while (vq_has_descs(vq
)) {
214 n
= vq_getchain(vq
, iov
, VT9P_MAX_IOV
, &req
);
215 assert(n
>= 1 && n
<= VT9P_MAX_IOV
);
216 preq
= calloc(1, sizeof(struct pci_vt9p_request
));
219 EPRINTLN("virtio-9p: allocation failure: %s",
225 preq
->vsr_idx
= req
.idx
;
228 preq
->vsr_respidx
= req
.readable
;
230 for (int i
= 0; i
< n
; i
++) {
231 DPRINTF(("vt9p: vt9p_notify(): desc%d base=%p, "
232 "len=%zu\r\n", i
, iov
[i
].iov_base
,
236 l9p_connection_recv(sc
->vsc_conn
, iov
, preq
->vsr_respidx
, preq
);
241 pci_vt9p_legacy_config(nvlist_t
*nvl
, const char *opts
)
243 char *sharename
= NULL
, *tofree
, *token
, *tokens
;
248 tokens
= tofree
= strdup(opts
);
249 while ((token
= strsep(&tokens
, ",")) != NULL
) {
250 if (strchr(token
, '=') != NULL
) {
251 if (sharename
!= NULL
) {
253 "virtio-9p: more than one share name given");
257 sharename
= strsep(&token
, "=");
258 set_config_value_node(nvl
, "sharename", sharename
);
259 set_config_value_node(nvl
, "path", token
);
261 set_config_bool_node(nvl
, token
, true);
269 pci_vt9p_init(struct vmctx
*ctx __unused
, struct pci_devinst
*pi
, nvlist_t
*nvl
)
271 struct pci_vt9p_softc
*sc
;
273 const char *sharename
;
276 #ifndef WITHOUT_CAPSICUM
277 cap_rights_t rootcap
;
280 ro
= get_config_bool_node_default(nvl
, "ro", false);
283 illumos_priv_add_min(PRIV_FILE_DAC_READ
, "vt9p");
284 illumos_priv_add_min(PRIV_FILE_DAC_SEARCH
, "vt9p");
287 illumos_priv_add_min(PRIV_FILE_CHOWN
, "vt9p");
288 illumos_priv_add_min(PRIV_FILE_CHOWN_SELF
, "vt9p");
289 illumos_priv_add_min(PRIV_FILE_WRITE
, "vt9p");
290 illumos_priv_add_min(PRIV_FILE_DAC_WRITE
, "vt9p");
291 illumos_priv_add_min(PRIV_FILE_OWNER
, "vt9p");
292 illumos_priv_add_min(PRIV_FILE_LINK_ANY
, "vt9p");
296 value
= get_config_value_node(nvl
, "path");
298 EPRINTLN("virtio-9p: path required");
301 rootfd
= open(value
, O_DIRECTORY
);
303 EPRINTLN("virtio-9p: failed to open '%s': %s", value
,
308 sharename
= get_config_value_node(nvl
, "sharename");
309 if (sharename
== NULL
) {
310 EPRINTLN("virtio-9p: share name required");
313 if (strlen(sharename
) > VT9P_MAXTAGSZ
) {
314 EPRINTLN("virtio-9p: share name too long");
318 sc
= calloc(1, sizeof(struct pci_vt9p_softc
));
320 sc
->vsc_config
= calloc(1, sizeof(struct pci_vt9p_config
) +
324 EPRINTLN("virtio-9p: soft state allocation failure: %s",
328 sc
->vsc_config
= calloc(1, sizeof(struct pci_vt9p_config
));
330 EPRINTLN("virtio-9p: vsc_config allocation failure: %s",
336 pthread_mutex_init(&sc
->vsc_mtx
, NULL
);
338 #ifndef WITHOUT_CAPSICUM
339 cap_rights_init(&rootcap
,
340 CAP_LOOKUP
, CAP_ACL_CHECK
, CAP_ACL_DELETE
, CAP_ACL_GET
,
341 CAP_ACL_SET
, CAP_READ
, CAP_WRITE
, CAP_SEEK
, CAP_FSTAT
,
342 CAP_CREATE
, CAP_FCHMODAT
, CAP_FCHOWNAT
, CAP_FTRUNCATE
,
343 CAP_LINKAT_SOURCE
, CAP_LINKAT_TARGET
, CAP_MKDIRAT
, CAP_MKNODAT
,
344 CAP_PREAD
, CAP_PWRITE
, CAP_RENAMEAT_SOURCE
, CAP_RENAMEAT_TARGET
,
345 CAP_SEEK
, CAP_SYMLINKAT
, CAP_UNLINKAT
, CAP_EXTATTR_DELETE
,
346 CAP_EXTATTR_GET
, CAP_EXTATTR_LIST
, CAP_EXTATTR_SET
,
347 CAP_FUTIMES
, CAP_FSTATFS
, CAP_FSYNC
, CAP_FPATHCONF
);
349 if (cap_rights_limit(rootfd
, &rootcap
) != 0)
353 sc
->vsc_config
->tag_len
= (uint16_t)strlen(sharename
);
354 memcpy(sc
->vsc_config
->tag
, sharename
, sc
->vsc_config
->tag_len
);
356 if (l9p_backend_fs_init(&sc
->vsc_fs_backend
, rootfd
, ro
) != 0) {
361 if (l9p_server_init(&sc
->vsc_server
, sc
->vsc_fs_backend
) != 0) {
366 if (l9p_connection_init(sc
->vsc_server
, &sc
->vsc_conn
) != 0) {
371 sc
->vsc_conn
->lc_msize
= L9P_MAX_IOV
* PAGE_SIZE
;
372 sc
->vsc_conn
->lc_lt
.lt_get_response_buffer
= pci_vt9p_get_buffer
;
373 sc
->vsc_conn
->lc_lt
.lt_send_response
= pci_vt9p_send
;
374 sc
->vsc_conn
->lc_lt
.lt_drop_response
= pci_vt9p_drop
;
376 vi_softc_linkup(&sc
->vsc_vs
, &vt9p_vi_consts
, sc
, pi
, &sc
->vsc_vq
);
377 sc
->vsc_vs
.vs_mtx
= &sc
->vsc_mtx
;
378 sc
->vsc_vq
.vq_qsize
= VT9P_RINGSZ
;
380 /* initialize config space */
381 pci_set_cfgdata16(pi
, PCIR_DEVICE
, VIRTIO_DEV_9P
);
382 pci_set_cfgdata16(pi
, PCIR_VENDOR
, VIRTIO_VENDOR
);
383 pci_set_cfgdata8(pi
, PCIR_CLASS
, PCIC_STORAGE
);
384 pci_set_cfgdata16(pi
, PCIR_SUBDEV_0
, VIRTIO_ID_9P
);
385 pci_set_cfgdata16(pi
, PCIR_SUBVEND_0
, VIRTIO_VENDOR
);
387 if (vi_intr_init(&sc
->vsc_vs
, 1, fbsdrun_virtio_msix()))
389 vi_set_io_bar(&sc
->vsc_vs
, 0);
394 static const struct pci_devemu pci_de_v9p
= {
395 .pe_emu
= "virtio-9p",
396 .pe_legacy_config
= pci_vt9p_legacy_config
,
397 .pe_init
= pci_vt9p_init
,
398 .pe_barwrite
= vi_pci_write
,
399 .pe_barread
= vi_pci_read
401 PCI_EMUL_SET(pci_de_v9p
);