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