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