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