1 /* $OpenBSD: vldcp.c,v 1.9 2014/07/12 18:44:43 tedu Exp $ */ 2 /* 3 * Copyright (c) 2009, 2012 Mark Kettenis 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/param.h> 19 #include <sys/conf.h> 20 #include <sys/device.h> 21 #include <sys/malloc.h> 22 #include <sys/poll.h> 23 #include <sys/proc.h> 24 #include <sys/systm.h> 25 26 #include <machine/autoconf.h> 27 #include <machine/hypervisor.h> 28 #include <machine/mdesc.h> 29 30 #include <uvm/uvm_extern.h> 31 32 #include <sparc64/dev/cbusvar.h> 33 #include <sparc64/dev/ldcvar.h> 34 35 #ifdef VLDCP_DEBUG 36 #define DPRINTF(x) printf x 37 #else 38 #define DPRINTF(x) 39 #endif 40 41 #include <sys/ioccom.h> 42 43 struct hv_io { 44 uint64_t hi_cookie; 45 void *hi_addr; 46 size_t hi_len; 47 }; 48 49 #define HVIOCREAD _IOW('h', 0, struct hv_io) 50 #define HVIOCWRITE _IOW('h', 1, struct hv_io) 51 52 #define VLDCP_TX_ENTRIES 128 53 #define VLDCP_RX_ENTRIES 128 54 55 struct vldcp_softc { 56 struct device sc_dv; 57 bus_space_tag_t sc_bustag; 58 bus_dma_tag_t sc_dmatag; 59 60 void *sc_tx_ih; 61 void *sc_rx_ih; 62 uint64_t sc_tx_sysino; 63 uint64_t sc_rx_sysino; 64 65 struct ldc_conn sc_lc; 66 67 struct selinfo sc_rsel; 68 struct selinfo sc_wsel; 69 }; 70 71 int vldcp_match(struct device *, void *, void *); 72 void vldcp_attach(struct device *, struct device *, void *); 73 74 struct cfattach vldcp_ca = { 75 sizeof(struct vldcp_softc), vldcp_match, vldcp_attach 76 }; 77 78 struct cfdriver vldcp_cd = { 79 NULL, "vldcp", DV_DULL 80 }; 81 82 int vldcp_tx_intr(void *); 83 int vldcp_rx_intr(void *); 84 85 /* 86 * We attach to certain well-known channels. These are assigned fixed 87 * device minor device numbers through their index in the table below. 88 * So "hvctl" gets minor 0, "spds" gets minor 1, etc. etc. 89 * 90 * We also attach to the domain services channels. These are named 91 * "ldom-<guestname>" and get assigned a device minor starting at 92 * VLDC_LDOM_OFFSET. 93 */ 94 #define VLDC_NUM_SERVICES 64 95 #define VLDC_LDOM_OFFSET 32 96 int vldc_num_ldoms; 97 98 struct vldc_svc { 99 const char *vs_name; 100 struct vldcp_softc *vs_sc; 101 }; 102 103 struct vldc_svc vldc_svc[VLDC_NUM_SERVICES] = { 104 { "hvctl" }, 105 { "spds" }, 106 { NULL } 107 }; 108 109 int 110 vldcp_match(struct device *parent, void *match, void *aux) 111 { 112 struct cbus_attach_args *ca = aux; 113 struct vldc_svc *svc; 114 115 for (svc = vldc_svc; svc->vs_name != NULL; svc++) 116 if (strcmp(ca->ca_name, svc->vs_name) == 0) 117 return (1); 118 119 if (strncmp(ca->ca_name, "ldom-", 5) == 0 && 120 strcmp(ca->ca_name, "ldom-primary") != 0) 121 return (1); 122 123 return (0); 124 } 125 126 void 127 vldcp_attach(struct device *parent, struct device *self, void *aux) 128 { 129 struct vldcp_softc *sc = (struct vldcp_softc *)self; 130 struct cbus_attach_args *ca = aux; 131 struct vldc_svc *svc; 132 struct ldc_conn *lc; 133 134 sc->sc_bustag = ca->ca_bustag; 135 sc->sc_dmatag = ca->ca_dmatag; 136 137 if (cbus_intr_map(ca->ca_node, ca->ca_tx_ino, &sc->sc_tx_sysino) || 138 cbus_intr_map(ca->ca_node, ca->ca_rx_ino, &sc->sc_rx_sysino)) { 139 printf(": can't map interrupt\n"); 140 return; 141 } 142 printf(": ivec 0x%llx, 0x%llx", sc->sc_tx_sysino, sc->sc_rx_sysino); 143 144 /* 145 * Un-configure queues before registering interrupt handlers, 146 * such that we dont get any stale LDC packets or events. 147 */ 148 hv_ldc_tx_qconf(ca->ca_id, 0, 0); 149 hv_ldc_rx_qconf(ca->ca_id, 0, 0); 150 151 sc->sc_tx_ih = bus_intr_establish(ca->ca_bustag, sc->sc_tx_sysino, 152 IPL_TTY, 0, vldcp_tx_intr, sc, sc->sc_dv.dv_xname); 153 sc->sc_rx_ih = bus_intr_establish(ca->ca_bustag, sc->sc_rx_sysino, 154 IPL_TTY, 0, vldcp_rx_intr, sc, sc->sc_dv.dv_xname); 155 if (sc->sc_tx_ih == NULL || sc->sc_rx_ih == NULL) { 156 printf(", can't establish interrupt\n"); 157 return; 158 } 159 160 lc = &sc->sc_lc; 161 lc->lc_id = ca->ca_id; 162 lc->lc_sc = sc; 163 164 lc->lc_txq = ldc_queue_alloc(sc->sc_dmatag, VLDCP_TX_ENTRIES); 165 if (lc->lc_txq == NULL) { 166 printf(", can't allocate tx queue\n"); 167 return; 168 } 169 170 lc->lc_rxq = ldc_queue_alloc(sc->sc_dmatag, VLDCP_RX_ENTRIES); 171 if (lc->lc_rxq == NULL) { 172 printf(", can't allocate rx queue\n"); 173 goto free_txqueue; 174 } 175 176 for (svc = vldc_svc; svc->vs_name != NULL; svc++) { 177 if (strcmp(ca->ca_name, svc->vs_name) == 0) { 178 svc->vs_sc = sc; 179 break; 180 } 181 } 182 183 if (strncmp(ca->ca_name, "ldom-", 5) == 0 && 184 strcmp(ca->ca_name, "ldom-primary") != 0) { 185 int minor = VLDC_LDOM_OFFSET + vldc_num_ldoms++; 186 if (minor < nitems(vldc_svc)) 187 vldc_svc[minor].vs_sc = sc; 188 } 189 190 printf(" channel \"%s\"\n", ca->ca_name); 191 return; 192 193 #if 0 194 free_rxqueue: 195 ldc_queue_free(sc->sc_dmatag, lc->lc_rxq); 196 #endif 197 free_txqueue: 198 ldc_queue_free(sc->sc_dmatag, lc->lc_txq); 199 } 200 201 int 202 vldcp_tx_intr(void *arg) 203 { 204 struct vldcp_softc *sc = arg; 205 struct ldc_conn *lc = &sc->sc_lc; 206 uint64_t tx_head, tx_tail, tx_state; 207 int err; 208 209 err = hv_ldc_tx_get_state(lc->lc_id, &tx_head, &tx_tail, &tx_state); 210 if (err != H_EOK) { 211 printf("%s: hv_ldc_tx_get_state %d\n", __func__, err); 212 return (0); 213 } 214 215 if (tx_state != lc->lc_tx_state) { 216 switch (tx_state) { 217 case LDC_CHANNEL_DOWN: 218 DPRINTF(("Tx link down\n")); 219 break; 220 case LDC_CHANNEL_UP: 221 DPRINTF(("Tx link up\n")); 222 break; 223 case LDC_CHANNEL_RESET: 224 DPRINTF(("Tx link reset\n")); 225 break; 226 } 227 lc->lc_tx_state = tx_state; 228 } 229 230 cbus_intr_setenabled(sc->sc_tx_sysino, INTR_DISABLED); 231 selwakeup(&sc->sc_wsel); 232 wakeup(lc->lc_txq); 233 return (1); 234 } 235 236 int 237 vldcp_rx_intr(void *arg) 238 { 239 struct vldcp_softc *sc = arg; 240 struct ldc_conn *lc = &sc->sc_lc; 241 uint64_t rx_head, rx_tail, rx_state; 242 int err; 243 244 err = hv_ldc_rx_get_state(lc->lc_id, &rx_head, &rx_tail, &rx_state); 245 if (err != H_EOK) { 246 printf("%s: hv_ldc_rx_get_state %d\n", __func__, err); 247 return (0); 248 } 249 250 if (rx_state != lc->lc_rx_state) { 251 switch (rx_state) { 252 case LDC_CHANNEL_DOWN: 253 DPRINTF(("Rx link down\n")); 254 break; 255 case LDC_CHANNEL_UP: 256 DPRINTF(("Rx link up\n")); 257 break; 258 case LDC_CHANNEL_RESET: 259 DPRINTF(("Rx link reset\n")); 260 break; 261 } 262 lc->lc_rx_state = rx_state; 263 cbus_intr_setenabled(sc->sc_rx_sysino, INTR_DISABLED); 264 selwakeup(&sc->sc_rsel); 265 wakeup(lc->lc_rxq); 266 return (1); 267 } 268 269 if (rx_head == rx_tail) 270 return (0); 271 272 cbus_intr_setenabled(sc->sc_rx_sysino, INTR_DISABLED); 273 selwakeup(&sc->sc_rsel); 274 wakeup(lc->lc_rxq); 275 return (1); 276 } 277 278 cdev_decl(vldcp); 279 struct vldcp_softc *vldcp_lookup(dev_t); 280 281 struct vldcp_softc * 282 vldcp_lookup(dev_t dev) 283 { 284 struct vldcp_softc *sc = NULL; 285 286 if (minor(dev) < nitems(vldc_svc)) 287 sc = vldc_svc[minor(dev)].vs_sc; 288 289 if (sc) 290 device_ref(&sc->sc_dv); 291 292 return (sc); 293 } 294 295 int 296 vldcpopen(dev_t dev, int flag, int mode, struct proc *p) 297 { 298 struct vldcp_softc *sc; 299 struct ldc_conn *lc; 300 uint64_t rx_head, rx_tail, rx_state; 301 int err; 302 303 sc = vldcp_lookup(dev); 304 if (sc == NULL) 305 return (ENXIO); 306 lc = &sc->sc_lc; 307 308 err = hv_ldc_tx_qconf(lc->lc_id, 309 lc->lc_txq->lq_map->dm_segs[0].ds_addr, lc->lc_txq->lq_nentries); 310 if (err != H_EOK) 311 printf("%s: hv_ldc_tx_qconf %d\n", __func__, err); 312 313 err = hv_ldc_rx_qconf(lc->lc_id, 314 lc->lc_rxq->lq_map->dm_segs[0].ds_addr, lc->lc_rxq->lq_nentries); 315 if (err != H_EOK) 316 printf("%s: hv_ldc_rx_qconf %d\n", __func__, err); 317 318 /* Clear a pending channel reset. */ 319 err = hv_ldc_rx_get_state(lc->lc_id, &rx_head, &rx_tail, &rx_state); 320 if (err != H_EOK) 321 printf("%s: hv_ldc_rx_get_state %d\n", __func__, err); 322 323 device_unref(&sc->sc_dv); 324 return (0); 325 } 326 327 int 328 vldcpclose(dev_t dev, int flag, int mode, struct proc *p) 329 { 330 struct vldcp_softc *sc; 331 332 sc = vldcp_lookup(dev); 333 if (sc == NULL) 334 return (ENXIO); 335 336 cbus_intr_setenabled(sc->sc_tx_sysino, INTR_DISABLED); 337 cbus_intr_setenabled(sc->sc_rx_sysino, INTR_DISABLED); 338 339 hv_ldc_tx_qconf(sc->sc_lc.lc_id, 0, 0); 340 hv_ldc_rx_qconf(sc->sc_lc.lc_id, 0, 0); 341 342 device_unref(&sc->sc_dv); 343 return (0); 344 } 345 346 int 347 vldcpread(dev_t dev, struct uio *uio, int ioflag) 348 { 349 struct vldcp_softc *sc; 350 struct ldc_conn *lc; 351 uint64_t rx_head, rx_tail, rx_state; 352 int err, ret; 353 int s; 354 355 sc = vldcp_lookup(dev); 356 if (sc == NULL) 357 return (ENXIO); 358 lc = &sc->sc_lc; 359 360 if (uio->uio_resid != 64) { 361 device_unref(&sc->sc_dv); 362 return (EINVAL); 363 } 364 365 s = spltty(); 366 retry: 367 err = hv_ldc_rx_get_state(lc->lc_id, &rx_head, &rx_tail, &rx_state); 368 if (err != H_EOK) { 369 splx(s); 370 printf("%s: hv_ldc_rx_get_state %d\n", __func__, err); 371 device_unref(&sc->sc_dv); 372 return (EIO); 373 } 374 375 if (rx_state != LDC_CHANNEL_UP) { 376 splx(s); 377 device_unref(&sc->sc_dv); 378 return (EIO); 379 } 380 381 DPRINTF(("rx head %llx, rx tail %llx\n", rx_head, rx_tail)); 382 383 if (rx_head == rx_tail) { 384 cbus_intr_setenabled(sc->sc_rx_sysino, INTR_ENABLED); 385 ret = tsleep(lc->lc_rxq, PWAIT | PCATCH, "hvrd", 0); 386 if (ret) { 387 splx(s); 388 device_unref(&sc->sc_dv); 389 return (ret); 390 } 391 goto retry; 392 } 393 splx(s); 394 395 ret = uiomove(lc->lc_rxq->lq_va + rx_head, 64, uio); 396 397 rx_head += 64; 398 rx_head &= ((lc->lc_rxq->lq_nentries * 64) - 1); 399 err = hv_ldc_rx_set_qhead(lc->lc_id, rx_head); 400 if (err != H_EOK) 401 printf("%s: hv_ldc_rx_set_qhead %d\n", __func__, err); 402 403 device_unref(&sc->sc_dv); 404 return (ret); 405 } 406 407 int 408 vldcpwrite(dev_t dev, struct uio *uio, int ioflag) 409 { 410 struct vldcp_softc *sc; 411 struct ldc_conn *lc; 412 uint64_t tx_head, tx_tail, tx_state; 413 uint64_t next_tx_tail; 414 int err, ret; 415 int s; 416 417 sc = vldcp_lookup(dev); 418 if (sc == NULL) 419 return (ENXIO); 420 lc = &sc->sc_lc; 421 422 if (uio->uio_resid != 64) { 423 device_unref(&sc->sc_dv); 424 return (EINVAL); 425 } 426 427 s = spltty(); 428 retry: 429 err = hv_ldc_tx_get_state(lc->lc_id, &tx_head, &tx_tail, &tx_state); 430 if (err != H_EOK) { 431 splx(s); 432 printf("%s: hv_ldc_tx_get_state %d\n", __func__, err); 433 device_unref(&sc->sc_dv); 434 return (EIO); 435 } 436 437 if (tx_state != LDC_CHANNEL_UP) { 438 splx(s); 439 device_unref(&sc->sc_dv); 440 return (EIO); 441 } 442 443 DPRINTF(("tx head %llx, tx tail %llx\n", tx_head, tx_tail)); 444 445 next_tx_tail = tx_tail + 64; 446 next_tx_tail &= ((lc->lc_txq->lq_nentries * 64) - 1); 447 448 if (tx_head == next_tx_tail) { 449 cbus_intr_setenabled(sc->sc_tx_sysino, INTR_ENABLED); 450 ret = tsleep(lc->lc_txq, PWAIT | PCATCH, "hvwr", 0); 451 if (ret) { 452 splx(s); 453 device_unref(&sc->sc_dv); 454 return (ret); 455 } 456 goto retry; 457 } 458 splx(s); 459 460 ret = uiomove(lc->lc_txq->lq_va + tx_tail, 64, uio); 461 462 err = hv_ldc_tx_set_qtail(lc->lc_id, next_tx_tail); 463 if (err != H_EOK) { 464 printf("%s: hv_ldc_tx_set_qtail: %d\n", __func__, err); 465 device_unref(&sc->sc_dv); 466 return (EIO); 467 } 468 469 device_unref(&sc->sc_dv); 470 return (ret); 471 } 472 473 int 474 vldcpioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) 475 { 476 struct vldcp_softc *sc; 477 struct ldc_conn *lc; 478 struct hv_io *hi = (struct hv_io *)data; 479 paddr_t pa, offset; 480 psize_t nbytes; 481 caddr_t buf; 482 size_t size; 483 int err; 484 485 sc = vldcp_lookup(dev); 486 if (sc == NULL) 487 return (ENXIO); 488 lc = &sc->sc_lc; 489 490 switch (cmd) { 491 case HVIOCREAD: 492 case HVIOCWRITE: 493 break; 494 default: 495 device_unref(&sc->sc_dv); 496 return (ENOTTY); 497 } 498 499 buf = malloc(PAGE_SIZE, M_DEVBUF, M_WAITOK); 500 501 switch(cmd) { 502 case HVIOCREAD: 503 size = hi->hi_len; 504 offset = 0; 505 while (size > 0) { 506 pmap_extract(pmap_kernel(), (vaddr_t)buf, &pa); 507 nbytes = min(PAGE_SIZE, size); 508 err = hv_ldc_copy(lc->lc_id, LDC_COPY_IN, 509 hi->hi_cookie + offset, pa, nbytes, &nbytes); 510 if (err != H_EOK) { 511 printf("hv_ldc_copy %d\n", err); 512 free(buf, M_DEVBUF, 0); 513 device_unref(&sc->sc_dv); 514 return (EINVAL); 515 } 516 err = copyout(buf, (caddr_t)hi->hi_addr + offset, nbytes); 517 if (err) { 518 free(buf, M_DEVBUF, 0); 519 device_unref(&sc->sc_dv); 520 return (err); 521 } 522 size -= nbytes; 523 offset += nbytes; 524 } 525 break; 526 case HVIOCWRITE: 527 size = hi->hi_len; 528 offset = 0; 529 while (size > 0) { 530 pmap_extract(pmap_kernel(), (vaddr_t)buf, &pa); 531 nbytes = min(PAGE_SIZE, size); 532 err = copyin((caddr_t)hi->hi_addr + offset, buf, nbytes); 533 if (err) { 534 free(buf, M_DEVBUF, 0); 535 device_unref(&sc->sc_dv); 536 return (err); 537 } 538 err = hv_ldc_copy(lc->lc_id, LDC_COPY_OUT, 539 hi->hi_cookie + offset, pa, nbytes, &nbytes); 540 if (err != H_EOK) { 541 printf("hv_ldc_copy %d\n", err); 542 free(buf, M_DEVBUF, 0); 543 device_unref(&sc->sc_dv); 544 return (EINVAL); 545 } 546 size -= nbytes; 547 offset += nbytes; 548 } 549 break; 550 551 } 552 553 free(buf, M_DEVBUF, 0); 554 555 device_unref(&sc->sc_dv); 556 return (0); 557 } 558 559 int 560 vldcppoll(dev_t dev, int events, struct proc *p) 561 { 562 struct vldcp_softc *sc; 563 struct ldc_conn *lc; 564 uint64_t head, tail, state; 565 int revents = 0; 566 int s, err; 567 568 sc = vldcp_lookup(dev); 569 if (sc == NULL) 570 return (ENXIO); 571 lc = &sc->sc_lc; 572 573 s = spltty(); 574 if (events & (POLLIN | POLLRDNORM)) { 575 err = hv_ldc_rx_get_state(lc->lc_id, &head, &tail, &state); 576 577 if (err == 0 && state == LDC_CHANNEL_UP && head != tail) 578 revents |= events & (POLLIN | POLLRDNORM); 579 } 580 if (events & (POLLOUT | POLLWRNORM)) { 581 err = hv_ldc_tx_get_state(lc->lc_id, &head, &tail, &state); 582 583 if (err == 0 && state == LDC_CHANNEL_UP && head != tail) 584 revents |= events & (POLLOUT | POLLWRNORM); 585 } 586 if (revents == 0) { 587 if (events & (POLLIN | POLLRDNORM)) { 588 cbus_intr_setenabled(sc->sc_rx_sysino, INTR_ENABLED); 589 selrecord(p, &sc->sc_rsel); 590 } 591 if (events & (POLLOUT | POLLWRNORM)) { 592 cbus_intr_setenabled(sc->sc_tx_sysino, INTR_ENABLED); 593 selrecord(p, &sc->sc_wsel); 594 } 595 } 596 splx(s); 597 return revents; 598 } 599