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