1 /* $OpenBSD: viocon.c,v 1.7 2019/05/26 15:20:04 sf Exp $ */ 2 3 /* 4 * Copyright (c) 2013-2015 Stefan Fritsch <sf@sfritsch.de> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/param.h> 20 #include <sys/systm.h> 21 #include <sys/kernel.h> 22 #include <sys/timeout.h> 23 #include <machine/bus.h> 24 #include <sys/device.h> 25 #include <sys/conf.h> 26 #include <sys/tty.h> 27 #include <dev/pci/pcivar.h> 28 #include <dev/pv/virtioreg.h> 29 #include <dev/pv/virtiovar.h> 30 31 32 /* features */ 33 #define VIRTIO_CONSOLE_F_SIZE (1ULL<<0) 34 #define VIRTIO_CONSOLE_F_MULTIPORT (1ULL<<1) 35 #define VIRTIO_CONSOLE_F_EMERG_WRITE (1ULL<<2) 36 37 /* config space */ 38 #define VIRTIO_CONSOLE_COLS 0 /* 16 bits */ 39 #define VIRTIO_CONSOLE_ROWS 2 /* 16 bits */ 40 #define VIRTIO_CONSOLE_MAX_NR_PORTS 4 /* 32 bits */ 41 #define VIRTIO_CONSOLE_EMERG_WR 8 /* 32 bits */ 42 43 #define VIOCON_DEBUG 0 44 45 #if VIOCON_DEBUG 46 #define DPRINTF(x...) printf(x) 47 #else 48 #define DPRINTF(x...) 49 #endif 50 51 struct virtio_feature_name viocon_feature_names[] = { 52 #if VIRTIO_DEBUG 53 { VIRTIO_CONSOLE_F_SIZE, "Size" }, 54 { VIRTIO_CONSOLE_F_MULTIPORT, "MultiPort" }, 55 { VIRTIO_CONSOLE_F_EMERG_WRITE, "EmergWrite" }, 56 #endif 57 { 0, NULL }, 58 }; 59 60 struct virtio_console_control { 61 uint32_t id; /* Port number */ 62 63 #define VIRTIO_CONSOLE_DEVICE_READY 0 64 #define VIRTIO_CONSOLE_PORT_ADD 1 65 #define VIRTIO_CONSOLE_PORT_REMOVE 2 66 #define VIRTIO_CONSOLE_PORT_READY 3 67 #define VIRTIO_CONSOLE_CONSOLE_PORT 4 68 #define VIRTIO_CONSOLE_RESIZE 5 69 #define VIRTIO_CONSOLE_PORT_OPEN 6 70 #define VIRTIO_CONSOLE_PORT_NAME 7 71 uint16_t event; 72 73 uint16_t value; 74 }; 75 76 struct virtio_console_control_resize { 77 /* yes, the order is different than in config space */ 78 uint16_t rows; 79 uint16_t cols; 80 }; 81 82 #define BUFSIZE 128 83 CTASSERT(BUFSIZE < TTHIWATMINSPACE); 84 85 #define VIOCONUNIT(x) (minor(x) >> 4) 86 #define VIOCONPORT(x) (minor(x) & 0x0f) 87 88 struct viocon_port { 89 struct viocon_softc *vp_sc; 90 struct virtqueue *vp_rx; 91 struct virtqueue *vp_tx; 92 void *vp_si; 93 struct tty *vp_tty; 94 const char *vp_name; 95 bus_dma_segment_t vp_dmaseg; 96 bus_dmamap_t vp_dmamap; 97 #ifdef NOTYET 98 unsigned int vp_host_open:1; /* XXX needs F_MULTIPORT */ 99 unsigned int vp_guest_open:1; /* XXX needs F_MULTIPORT */ 100 unsigned int vp_is_console:1; /* XXX needs F_MULTIPORT */ 101 #endif 102 unsigned int vp_iflow:1; /* rx flow control */ 103 uint16_t vp_rows; 104 uint16_t vp_cols; 105 u_char *vp_rx_buf; 106 u_char *vp_tx_buf; 107 }; 108 109 struct viocon_softc { 110 struct device sc_dev; 111 struct virtio_softc *sc_virtio; 112 113 struct virtqueue *sc_c_vq_rx; 114 struct virtqueue *sc_c_vq_tx; 115 116 unsigned int sc_max_ports; 117 struct viocon_port **sc_ports; 118 119 bus_dmamap_t sc_dmamap; 120 }; 121 122 int viocon_match(struct device *, void *, void *); 123 void viocon_attach(struct device *, struct device *, void *); 124 int viocon_tx_intr(struct virtqueue *); 125 int viocon_tx_drain(struct viocon_port *, struct virtqueue *vq); 126 int viocon_rx_intr(struct virtqueue *); 127 void viocon_rx_soft(void *); 128 void viocon_rx_fill(struct viocon_port *); 129 int viocon_port_create(struct viocon_softc *, int); 130 void vioconstart(struct tty *); 131 int vioconhwiflow(struct tty *, int); 132 int vioconparam(struct tty *, struct termios *); 133 int vioconopen(dev_t, int, int, struct proc *); 134 int vioconclose(dev_t, int, int, struct proc *); 135 int vioconread(dev_t, struct uio *, int); 136 int vioconwrite(dev_t, struct uio *, int); 137 int vioconstop(struct tty *, int); 138 int vioconioctl(dev_t, u_long, caddr_t, int, struct proc *); 139 struct tty *viocontty(dev_t dev); 140 141 struct cfattach viocon_ca = { 142 sizeof(struct viocon_softc), 143 viocon_match, 144 viocon_attach, 145 NULL 146 }; 147 148 struct cfdriver viocon_cd = { 149 NULL, "viocon", DV_TTY 150 }; 151 152 static inline struct viocon_softc * 153 dev2sc(dev_t dev) 154 { 155 return viocon_cd.cd_devs[VIOCONUNIT(dev)]; 156 } 157 158 static inline struct viocon_port * 159 dev2port(dev_t dev) 160 { 161 return dev2sc(dev)->sc_ports[VIOCONPORT(dev)]; 162 } 163 164 int viocon_match(struct device *parent, void *match, void *aux) 165 { 166 struct virtio_softc *va = aux; 167 if (va->sc_childdevid == PCI_PRODUCT_VIRTIO_CONSOLE) 168 return 1; 169 return 0; 170 } 171 172 void 173 viocon_attach(struct device *parent, struct device *self, void *aux) 174 { 175 struct viocon_softc *sc = (struct viocon_softc *)self; 176 struct virtio_softc *vsc = (struct virtio_softc *)parent; 177 int maxports = 1; 178 179 if (vsc->sc_child) 180 panic("already attached to something else"); 181 vsc->sc_child = self; 182 vsc->sc_ipl = IPL_TTY; 183 vsc->sc_config_change = 0; 184 sc->sc_virtio = vsc; 185 sc->sc_max_ports = maxports; 186 187 vsc->sc_vqs = malloc(2 * (maxports + 1) * sizeof(struct virtqueue), M_DEVBUF, 188 M_WAITOK|M_CANFAIL|M_ZERO); 189 sc->sc_ports = malloc(maxports * sizeof(sc->sc_ports[0]), M_DEVBUF, 190 M_WAITOK|M_CANFAIL|M_ZERO); 191 if (vsc->sc_vqs == NULL || sc->sc_ports == NULL) { 192 printf("\n%s: Cannot allocate memory\n", __func__); 193 goto err; 194 } 195 196 vsc->sc_driver_features = VIRTIO_CONSOLE_F_SIZE; 197 virtio_negotiate_features(vsc, viocon_feature_names); 198 199 printf("\n"); 200 DPRINTF("%s: softc: %p\n", __func__, sc); 201 if (viocon_port_create(sc, 0) != 0) { 202 printf("\n%s: viocon_port_create failed\n", __func__); 203 goto err; 204 } 205 viocon_rx_fill(sc->sc_ports[0]); 206 207 return; 208 err: 209 vsc->sc_child = VIRTIO_CHILD_ERROR; 210 free(vsc->sc_vqs, M_DEVBUF, 2 * (maxports + 1) * sizeof(struct virtqueue)); 211 free(sc->sc_ports, M_DEVBUF, maxports * sizeof(sc->sc_ports[0])); 212 } 213 214 int 215 viocon_port_create(struct viocon_softc *sc, int portidx) 216 { 217 struct virtio_softc *vsc = sc->sc_virtio; 218 int rxidx, txidx, allocsize, nsegs; 219 char name[6]; 220 struct viocon_port *vp; 221 caddr_t kva; 222 struct tty *tp; 223 224 vp = malloc(sizeof(*vp), M_DEVBUF, M_WAITOK|M_CANFAIL|M_ZERO); 225 if (vp == NULL) 226 return ENOMEM; 227 sc->sc_ports[portidx] = vp; 228 vp->vp_sc = sc; 229 DPRINTF("%s: vp: %p\n", __func__, vp); 230 231 if (portidx == 0) 232 rxidx = 0; 233 else 234 rxidx = 2 * (portidx + 1); 235 txidx = rxidx + 1; 236 237 snprintf(name, sizeof(name), "p%drx", portidx); 238 if (virtio_alloc_vq(vsc, &vsc->sc_vqs[rxidx], rxidx, BUFSIZE, 1, 239 name) != 0) { 240 printf("\nCan't alloc %s virtqueue\n", name); 241 goto err; 242 } 243 vp->vp_rx = &vsc->sc_vqs[rxidx]; 244 vp->vp_rx->vq_done = viocon_rx_intr; 245 vp->vp_si = softintr_establish(IPL_TTY, viocon_rx_soft, vp); 246 DPRINTF("%s: rx: %p\n", __func__, vp->vp_rx); 247 248 snprintf(name, sizeof(name), "p%dtx", portidx); 249 if (virtio_alloc_vq(vsc, &vsc->sc_vqs[txidx], txidx, BUFSIZE, 1, 250 name) != 0) { 251 printf("\nCan't alloc %s virtqueue\n", name); 252 goto err; 253 } 254 vp->vp_tx = &vsc->sc_vqs[txidx]; 255 vp->vp_tx->vq_done = viocon_tx_intr; 256 DPRINTF("%s: tx: %p\n", __func__, vp->vp_tx); 257 258 vsc->sc_nvqs += 2; 259 260 allocsize = (vp->vp_rx->vq_num + vp->vp_tx->vq_num) * BUFSIZE; 261 262 if (bus_dmamap_create(vsc->sc_dmat, allocsize, 1, allocsize, 0, 263 BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &vp->vp_dmamap) != 0) 264 goto err; 265 if (bus_dmamem_alloc(vsc->sc_dmat, allocsize, 8, 0, &vp->vp_dmaseg, 266 1, &nsegs, BUS_DMA_NOWAIT | BUS_DMA_ZERO) != 0) 267 goto err; 268 if (bus_dmamem_map(vsc->sc_dmat, &vp->vp_dmaseg, nsegs, 269 allocsize, &kva, BUS_DMA_NOWAIT) != 0) 270 goto err; 271 if (bus_dmamap_load(vsc->sc_dmat, vp->vp_dmamap, kva, 272 allocsize, NULL, BUS_DMA_NOWAIT) != 0) 273 goto err; 274 vp->vp_rx_buf = (unsigned char *)kva; 275 /* 276 * XXX use only a small circular tx buffer instead of many BUFSIZE buffers? 277 */ 278 vp->vp_tx_buf = vp->vp_rx_buf + vp->vp_rx->vq_num * BUFSIZE; 279 280 if (virtio_has_feature(vsc, VIRTIO_CONSOLE_F_SIZE)) { 281 vp->vp_cols = virtio_read_device_config_2(vsc, 282 VIRTIO_CONSOLE_COLS); 283 vp->vp_rows = virtio_read_device_config_2(vsc, 284 VIRTIO_CONSOLE_ROWS); 285 } 286 287 tp = ttymalloc(1000000); 288 tp->t_oproc = vioconstart; 289 tp->t_param = vioconparam; 290 tp->t_hwiflow = vioconhwiflow; 291 tp->t_dev = (sc->sc_dev.dv_unit << 4) | portidx; 292 vp->vp_tty = tp; 293 DPRINTF("%s: tty: %p\n", __func__, tp); 294 295 virtio_start_vq_intr(vsc, vp->vp_rx); 296 virtio_start_vq_intr(vsc, vp->vp_tx); 297 298 return 0; 299 err: 300 panic("%s failed", __func__); 301 return -1; 302 } 303 304 int 305 viocon_tx_drain(struct viocon_port *vp, struct virtqueue *vq) 306 { 307 struct virtio_softc *vsc = vq->vq_owner; 308 int ndone = 0, len, slot; 309 310 splassert(IPL_TTY); 311 while (virtio_dequeue(vsc, vq, &slot, &len) == 0) { 312 bus_dmamap_sync(vsc->sc_dmat, vp->vp_dmamap, 313 vp->vp_tx_buf - vp->vp_rx_buf + slot * BUFSIZE, BUFSIZE, 314 BUS_DMASYNC_POSTREAD); 315 virtio_dequeue_commit(vq, slot); 316 ndone++; 317 } 318 return ndone; 319 } 320 321 int 322 viocon_tx_intr(struct virtqueue *vq) 323 { 324 struct virtio_softc *vsc = vq->vq_owner; 325 struct viocon_softc *sc = (struct viocon_softc *)vsc->sc_child; 326 int ndone = 0; 327 int portidx = (vq->vq_index - 1) / 2; 328 struct viocon_port *vp = sc->sc_ports[portidx]; 329 struct tty *tp = vp->vp_tty; 330 331 splassert(IPL_TTY); 332 ndone = viocon_tx_drain(vp, vq); 333 if (ndone && ISSET(tp->t_state, TS_BUSY)) { 334 CLR(tp->t_state, TS_BUSY); 335 linesw[tp->t_line].l_start(tp); 336 } 337 338 return 1; 339 } 340 341 void 342 viocon_rx_fill(struct viocon_port *vp) 343 { 344 struct virtqueue *vq = vp->vp_rx; 345 struct virtio_softc *vsc = vp->vp_sc->sc_virtio; 346 int r, slot, ndone = 0; 347 348 while ((r = virtio_enqueue_prep(vq, &slot)) == 0) { 349 if (virtio_enqueue_reserve(vq, slot, 1) != 0) 350 break; 351 bus_dmamap_sync(vsc->sc_dmat, vp->vp_dmamap, slot * BUFSIZE, 352 BUFSIZE, BUS_DMASYNC_PREREAD); 353 virtio_enqueue_p(vq, slot, vp->vp_dmamap, slot * BUFSIZE, 354 BUFSIZE, 0); 355 virtio_enqueue_commit(vsc, vq, slot, 0); 356 ndone++; 357 } 358 KASSERT(r == 0 || r == EAGAIN); 359 if (ndone > 0) 360 virtio_notify(vsc, vq); 361 } 362 363 int 364 viocon_rx_intr(struct virtqueue *vq) 365 { 366 struct virtio_softc *vsc = vq->vq_owner; 367 struct viocon_softc *sc = (struct viocon_softc *)vsc->sc_child; 368 int portidx = (vq->vq_index - 1) / 2; 369 struct viocon_port *vp = sc->sc_ports[portidx]; 370 371 softintr_schedule(vp->vp_si); 372 return 1; 373 } 374 375 void 376 viocon_rx_soft(void *arg) 377 { 378 struct viocon_port *vp = arg; 379 struct virtqueue *vq = vp->vp_rx; 380 struct virtio_softc *vsc = vq->vq_owner; 381 struct tty *tp = vp->vp_tty; 382 int slot, len, i; 383 u_char *p; 384 385 while (!vp->vp_iflow && virtio_dequeue(vsc, vq, &slot, &len) == 0) { 386 bus_dmamap_sync(vsc->sc_dmat, vp->vp_dmamap, 387 slot * BUFSIZE, BUFSIZE, BUS_DMASYNC_POSTREAD); 388 p = vp->vp_rx_buf + slot * BUFSIZE; 389 for (i = 0; i < len; i++) 390 (*linesw[tp->t_line].l_rint)(*p++, tp); 391 virtio_dequeue_commit(vq, slot); 392 } 393 394 viocon_rx_fill(vp); 395 396 return; 397 } 398 399 void 400 vioconstart(struct tty *tp) 401 { 402 struct viocon_softc *sc = dev2sc(tp->t_dev); 403 struct virtio_softc *vsc; 404 struct viocon_port *vp = dev2port(tp->t_dev); 405 struct virtqueue *vq; 406 u_char *buf; 407 int s, cnt, slot, ret, ndone; 408 409 vsc = sc->sc_virtio; 410 vq = vp->vp_tx; 411 412 s = spltty(); 413 414 ndone = viocon_tx_drain(vp, vq); 415 if (ISSET(tp->t_state, TS_BUSY)) { 416 if (ndone > 0) 417 CLR(tp->t_state, TS_BUSY); 418 else 419 goto out; 420 } 421 if (ISSET(tp->t_state, TS_TIMEOUT | TS_TTSTOP)) 422 goto out; 423 424 if (tp->t_outq.c_cc == 0) 425 goto out; 426 ndone = 0; 427 428 while (tp->t_outq.c_cc > 0) { 429 ret = virtio_enqueue_prep(vq, &slot); 430 if (ret == EAGAIN) 431 break; 432 KASSERT(ret == 0); 433 ret = virtio_enqueue_reserve(vq, slot, 1); 434 KASSERT(ret == 0); 435 buf = vp->vp_tx_buf + slot * BUFSIZE; 436 cnt = q_to_b(&tp->t_outq, buf, BUFSIZE); 437 bus_dmamap_sync(vsc->sc_dmat, vp->vp_dmamap, 438 vp->vp_tx_buf - vp->vp_rx_buf + slot * BUFSIZE, cnt, 439 BUS_DMASYNC_PREWRITE); 440 virtio_enqueue_p(vq, slot, vp->vp_dmamap, 441 vp->vp_tx_buf - vp->vp_rx_buf + slot * BUFSIZE, cnt, 1); 442 virtio_enqueue_commit(vsc, vq, slot, 0); 443 ndone++; 444 } 445 if (ret == EAGAIN) 446 SET(tp->t_state, TS_BUSY); 447 if (ndone > 0) 448 virtio_notify(vsc, vq); 449 ttwakeupwr(tp); 450 out: 451 splx(s); 452 } 453 454 int 455 vioconhwiflow(struct tty *tp, int stop) 456 { 457 struct viocon_port *vp = dev2port(tp->t_dev); 458 int s; 459 460 s = spltty(); 461 vp->vp_iflow = stop; 462 if (stop) { 463 virtio_stop_vq_intr(vp->vp_sc->sc_virtio, vp->vp_rx); 464 } else { 465 virtio_start_vq_intr(vp->vp_sc->sc_virtio, vp->vp_rx); 466 softintr_schedule(vp->vp_si); 467 } 468 splx(s); 469 return 1; 470 } 471 472 int 473 vioconparam(struct tty *tp, struct termios *t) 474 { 475 tp->t_ispeed = t->c_ispeed; 476 tp->t_ospeed = t->c_ospeed; 477 tp->t_cflag = t->c_cflag; 478 479 vioconstart(tp); 480 return 0; 481 } 482 483 int 484 vioconopen(dev_t dev, int flag, int mode, struct proc *p) 485 { 486 int unit = VIOCONUNIT(dev); 487 int port = VIOCONPORT(dev); 488 struct viocon_softc *sc; 489 struct viocon_port *vp; 490 struct tty *tp; 491 int s, error; 492 493 if (unit >= viocon_cd.cd_ndevs) 494 return (ENXIO); 495 sc = viocon_cd.cd_devs[unit]; 496 if (sc == NULL) 497 return (ENXIO); 498 if (ISSET(sc->sc_dev.dv_flags, DVF_ACTIVE) == 0) 499 return (ENXIO); 500 501 s = spltty(); 502 if (port >= sc->sc_max_ports) { 503 splx(s); 504 return (ENXIO); 505 } 506 vp = sc->sc_ports[port]; 507 tp = vp->vp_tty; 508 #ifdef NOTYET 509 vp->vp_guest_open = 1; 510 #endif 511 splx(s); 512 513 if (!ISSET(tp->t_state, TS_ISOPEN)) { 514 SET(tp->t_state, TS_WOPEN); 515 ttychars(tp); 516 tp->t_ispeed = 1000000; 517 tp->t_ospeed = 1000000; 518 tp->t_cflag = TTYDEF_CFLAG|CLOCAL|CRTSCTS; 519 tp->t_iflag = TTYDEF_IFLAG; 520 tp->t_oflag = TTYDEF_OFLAG; 521 tp->t_lflag = TTYDEF_LFLAG; 522 if (vp->vp_cols != 0) { 523 tp->t_winsize.ws_col = vp->vp_cols; 524 tp->t_winsize.ws_row = vp->vp_rows; 525 } 526 527 s = spltty(); 528 vioconparam(tp, &tp->t_termios); 529 ttsetwater(tp); 530 splx(s); 531 } 532 else if (ISSET(tp->t_state, TS_XCLUDE) && suser(p) != 0) { 533 return (EBUSY); 534 } 535 536 error = linesw[tp->t_line].l_open(dev, tp, p); 537 return error; 538 } 539 540 int 541 vioconclose(dev_t dev, int flag, int mode, struct proc *p) 542 { 543 struct viocon_port *vp = dev2port(dev); 544 struct tty *tp = vp->vp_tty; 545 int s; 546 547 if (!ISSET(tp->t_state, TS_ISOPEN)) 548 return 0; 549 550 linesw[tp->t_line].l_close(tp, flag, p); 551 s = spltty(); 552 #ifdef NOTYET 553 vp->vp_guest_open = 0; 554 #endif 555 CLR(tp->t_state, TS_BUSY | TS_FLUSH); 556 ttyclose(tp); 557 splx(s); 558 559 return 0; 560 } 561 562 int 563 vioconread(dev_t dev, struct uio *uio, int flag) 564 { 565 struct viocon_port *vp = dev2port(dev); 566 struct tty *tp = vp->vp_tty; 567 568 return linesw[tp->t_line].l_read(tp, uio, flag); 569 } 570 571 int 572 vioconwrite(dev_t dev, struct uio *uio, int flag) 573 { 574 struct viocon_port *vp = dev2port(dev); 575 struct tty *tp = vp->vp_tty; 576 577 return linesw[tp->t_line].l_write(tp, uio, flag); 578 } 579 580 struct tty * 581 viocontty(dev_t dev) 582 { 583 struct viocon_port *vp = dev2port(dev); 584 585 return vp->vp_tty; 586 } 587 588 int 589 vioconstop(struct tty *tp, int flag) 590 { 591 int s; 592 593 s = spltty(); 594 if (ISSET(tp->t_state, TS_BUSY)) 595 if (!ISSET(tp->t_state, TS_TTSTOP)) 596 SET(tp->t_state, TS_FLUSH); 597 splx(s); 598 return 0; 599 } 600 601 int 602 vioconioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) 603 { 604 struct viocon_port *vp = dev2port(dev); 605 struct tty *tp; 606 int error1, error2; 607 608 tp = vp->vp_tty; 609 610 error1 = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); 611 if (error1 >= 0) 612 return error1; 613 error2 = ttioctl(tp, cmd, data, flag, p); 614 if (error2 >= 0) 615 return error2; 616 return ENOTTY; 617 } 618