1 /* $OpenBSD: viocon.c,v 1.10 2023/07/28 16:54:48 dv 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 const 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 = NULL; 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 virtio_set_status(vsc, VIRTIO_CONFIG_DEVICE_STATUS_DRIVER_OK); 207 208 return; 209 err: 210 vsc->sc_child = VIRTIO_CHILD_ERROR; 211 free(vsc->sc_vqs, M_DEVBUF, 2 * (maxports + 1) * sizeof(struct virtqueue)); 212 free(sc->sc_ports, M_DEVBUF, maxports * sizeof(sc->sc_ports[0])); 213 } 214 215 int 216 viocon_port_create(struct viocon_softc *sc, int portidx) 217 { 218 struct virtio_softc *vsc = sc->sc_virtio; 219 int rxidx, txidx, allocsize, nsegs; 220 char name[6]; 221 struct viocon_port *vp; 222 caddr_t kva; 223 struct tty *tp; 224 225 vp = malloc(sizeof(*vp), M_DEVBUF, M_WAITOK|M_CANFAIL|M_ZERO); 226 if (vp == NULL) 227 return ENOMEM; 228 sc->sc_ports[portidx] = vp; 229 vp->vp_sc = sc; 230 DPRINTF("%s: vp: %p\n", __func__, vp); 231 232 if (portidx == 0) 233 rxidx = 0; 234 else 235 rxidx = 2 * (portidx + 1); 236 txidx = rxidx + 1; 237 238 snprintf(name, sizeof(name), "p%drx", portidx); 239 if (virtio_alloc_vq(vsc, &vsc->sc_vqs[rxidx], rxidx, BUFSIZE, 1, 240 name) != 0) { 241 printf("\nCan't alloc %s virtqueue\n", name); 242 goto err; 243 } 244 vp->vp_rx = &vsc->sc_vqs[rxidx]; 245 vp->vp_rx->vq_done = viocon_rx_intr; 246 vp->vp_si = softintr_establish(IPL_TTY, viocon_rx_soft, vp); 247 DPRINTF("%s: rx: %p\n", __func__, vp->vp_rx); 248 249 snprintf(name, sizeof(name), "p%dtx", portidx); 250 if (virtio_alloc_vq(vsc, &vsc->sc_vqs[txidx], txidx, BUFSIZE, 1, 251 name) != 0) { 252 printf("\nCan't alloc %s virtqueue\n", name); 253 goto err; 254 } 255 vp->vp_tx = &vsc->sc_vqs[txidx]; 256 vp->vp_tx->vq_done = viocon_tx_intr; 257 DPRINTF("%s: tx: %p\n", __func__, vp->vp_tx); 258 259 vsc->sc_nvqs += 2; 260 261 allocsize = (vp->vp_rx->vq_num + vp->vp_tx->vq_num) * BUFSIZE; 262 263 if (bus_dmamap_create(vsc->sc_dmat, allocsize, 1, allocsize, 0, 264 BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &vp->vp_dmamap) != 0) 265 goto err; 266 if (bus_dmamem_alloc(vsc->sc_dmat, allocsize, 8, 0, &vp->vp_dmaseg, 267 1, &nsegs, BUS_DMA_NOWAIT | BUS_DMA_ZERO) != 0) 268 goto err; 269 if (bus_dmamem_map(vsc->sc_dmat, &vp->vp_dmaseg, nsegs, 270 allocsize, &kva, BUS_DMA_NOWAIT) != 0) 271 goto err; 272 if (bus_dmamap_load(vsc->sc_dmat, vp->vp_dmamap, kva, 273 allocsize, NULL, BUS_DMA_NOWAIT) != 0) 274 goto err; 275 vp->vp_rx_buf = (unsigned char *)kva; 276 /* 277 * XXX use only a small circular tx buffer instead of many BUFSIZE buffers? 278 */ 279 vp->vp_tx_buf = vp->vp_rx_buf + vp->vp_rx->vq_num * BUFSIZE; 280 281 if (virtio_has_feature(vsc, VIRTIO_CONSOLE_F_SIZE)) { 282 vp->vp_cols = virtio_read_device_config_2(vsc, 283 VIRTIO_CONSOLE_COLS); 284 vp->vp_rows = virtio_read_device_config_2(vsc, 285 VIRTIO_CONSOLE_ROWS); 286 } 287 288 tp = ttymalloc(1000000); 289 tp->t_oproc = vioconstart; 290 tp->t_param = vioconparam; 291 tp->t_hwiflow = vioconhwiflow; 292 tp->t_dev = (sc->sc_dev.dv_unit << 4) | portidx; 293 vp->vp_tty = tp; 294 DPRINTF("%s: tty: %p\n", __func__, tp); 295 296 virtio_start_vq_intr(vsc, vp->vp_rx); 297 virtio_start_vq_intr(vsc, vp->vp_tx); 298 299 return 0; 300 err: 301 panic("%s failed", __func__); 302 return -1; 303 } 304 305 int 306 viocon_tx_drain(struct viocon_port *vp, struct virtqueue *vq) 307 { 308 struct virtio_softc *vsc = vq->vq_owner; 309 int ndone = 0, len, slot; 310 311 splassert(IPL_TTY); 312 while (virtio_dequeue(vsc, vq, &slot, &len) == 0) { 313 bus_dmamap_sync(vsc->sc_dmat, vp->vp_dmamap, 314 vp->vp_tx_buf - vp->vp_rx_buf + slot * BUFSIZE, BUFSIZE, 315 BUS_DMASYNC_POSTREAD); 316 virtio_dequeue_commit(vq, slot); 317 ndone++; 318 } 319 return ndone; 320 } 321 322 int 323 viocon_tx_intr(struct virtqueue *vq) 324 { 325 struct virtio_softc *vsc = vq->vq_owner; 326 struct viocon_softc *sc = (struct viocon_softc *)vsc->sc_child; 327 int ndone = 0; 328 int portidx = (vq->vq_index - 1) / 2; 329 struct viocon_port *vp = sc->sc_ports[portidx]; 330 struct tty *tp = vp->vp_tty; 331 332 splassert(IPL_TTY); 333 ndone = viocon_tx_drain(vp, vq); 334 if (ndone && ISSET(tp->t_state, TS_BUSY)) { 335 CLR(tp->t_state, TS_BUSY); 336 linesw[tp->t_line].l_start(tp); 337 } 338 339 return 1; 340 } 341 342 void 343 viocon_rx_fill(struct viocon_port *vp) 344 { 345 struct virtqueue *vq = vp->vp_rx; 346 struct virtio_softc *vsc = vp->vp_sc->sc_virtio; 347 int r, slot, ndone = 0; 348 349 while ((r = virtio_enqueue_prep(vq, &slot)) == 0) { 350 if (virtio_enqueue_reserve(vq, slot, 1) != 0) 351 break; 352 bus_dmamap_sync(vsc->sc_dmat, vp->vp_dmamap, slot * BUFSIZE, 353 BUFSIZE, BUS_DMASYNC_PREREAD); 354 virtio_enqueue_p(vq, slot, vp->vp_dmamap, slot * BUFSIZE, 355 BUFSIZE, 0); 356 virtio_enqueue_commit(vsc, vq, slot, 0); 357 ndone++; 358 } 359 KASSERT(r == 0 || r == EAGAIN); 360 if (ndone > 0) 361 virtio_notify(vsc, vq); 362 } 363 364 int 365 viocon_rx_intr(struct virtqueue *vq) 366 { 367 struct virtio_softc *vsc = vq->vq_owner; 368 struct viocon_softc *sc = (struct viocon_softc *)vsc->sc_child; 369 int portidx = (vq->vq_index - 1) / 2; 370 struct viocon_port *vp = sc->sc_ports[portidx]; 371 372 softintr_schedule(vp->vp_si); 373 return 1; 374 } 375 376 void 377 viocon_rx_soft(void *arg) 378 { 379 struct viocon_port *vp = arg; 380 struct virtqueue *vq = vp->vp_rx; 381 struct virtio_softc *vsc = vq->vq_owner; 382 struct tty *tp = vp->vp_tty; 383 int slot, len, i; 384 u_char *p; 385 386 while (!vp->vp_iflow && virtio_dequeue(vsc, vq, &slot, &len) == 0) { 387 bus_dmamap_sync(vsc->sc_dmat, vp->vp_dmamap, 388 slot * BUFSIZE, BUFSIZE, BUS_DMASYNC_POSTREAD); 389 p = vp->vp_rx_buf + slot * BUFSIZE; 390 for (i = 0; i < len; i++) 391 (*linesw[tp->t_line].l_rint)(*p++, tp); 392 virtio_dequeue_commit(vq, slot); 393 } 394 395 viocon_rx_fill(vp); 396 397 return; 398 } 399 400 void 401 vioconstart(struct tty *tp) 402 { 403 struct viocon_softc *sc = dev2sc(tp->t_dev); 404 struct virtio_softc *vsc; 405 struct viocon_port *vp = dev2port(tp->t_dev); 406 struct virtqueue *vq; 407 u_char *buf; 408 int s, cnt, slot, ret, ndone; 409 410 vsc = sc->sc_virtio; 411 vq = vp->vp_tx; 412 413 s = spltty(); 414 415 ndone = viocon_tx_drain(vp, vq); 416 if (ISSET(tp->t_state, TS_BUSY)) { 417 if (ndone > 0) 418 CLR(tp->t_state, TS_BUSY); 419 else 420 goto out; 421 } 422 if (ISSET(tp->t_state, TS_TIMEOUT | TS_TTSTOP)) 423 goto out; 424 425 if (tp->t_outq.c_cc == 0) 426 goto out; 427 ndone = 0; 428 429 while (tp->t_outq.c_cc > 0) { 430 ret = virtio_enqueue_prep(vq, &slot); 431 if (ret == EAGAIN) 432 break; 433 KASSERT(ret == 0); 434 ret = virtio_enqueue_reserve(vq, slot, 1); 435 KASSERT(ret == 0); 436 buf = vp->vp_tx_buf + slot * BUFSIZE; 437 cnt = q_to_b(&tp->t_outq, buf, BUFSIZE); 438 bus_dmamap_sync(vsc->sc_dmat, vp->vp_dmamap, 439 vp->vp_tx_buf - vp->vp_rx_buf + slot * BUFSIZE, cnt, 440 BUS_DMASYNC_PREWRITE); 441 virtio_enqueue_p(vq, slot, vp->vp_dmamap, 442 vp->vp_tx_buf - vp->vp_rx_buf + slot * BUFSIZE, cnt, 1); 443 virtio_enqueue_commit(vsc, vq, slot, 0); 444 ndone++; 445 } 446 if (ret == EAGAIN) 447 SET(tp->t_state, TS_BUSY); 448 if (ndone > 0) 449 virtio_notify(vsc, vq); 450 ttwakeupwr(tp); 451 out: 452 splx(s); 453 } 454 455 int 456 vioconhwiflow(struct tty *tp, int stop) 457 { 458 struct viocon_port *vp = dev2port(tp->t_dev); 459 int s; 460 461 s = spltty(); 462 vp->vp_iflow = stop; 463 if (stop) { 464 virtio_stop_vq_intr(vp->vp_sc->sc_virtio, vp->vp_rx); 465 } else { 466 virtio_start_vq_intr(vp->vp_sc->sc_virtio, vp->vp_rx); 467 softintr_schedule(vp->vp_si); 468 } 469 splx(s); 470 return 1; 471 } 472 473 int 474 vioconparam(struct tty *tp, struct termios *t) 475 { 476 tp->t_ispeed = t->c_ispeed; 477 tp->t_ospeed = t->c_ospeed; 478 tp->t_cflag = t->c_cflag; 479 480 vioconstart(tp); 481 return 0; 482 } 483 484 int 485 vioconopen(dev_t dev, int flag, int mode, struct proc *p) 486 { 487 int unit = VIOCONUNIT(dev); 488 int port = VIOCONPORT(dev); 489 struct viocon_softc *sc; 490 struct viocon_port *vp; 491 struct tty *tp; 492 int s, error; 493 494 if (unit >= viocon_cd.cd_ndevs) 495 return (ENXIO); 496 sc = viocon_cd.cd_devs[unit]; 497 if (sc == NULL) 498 return (ENXIO); 499 if (ISSET(sc->sc_dev.dv_flags, DVF_ACTIVE) == 0) 500 return (ENXIO); 501 502 s = spltty(); 503 if (port >= sc->sc_max_ports) { 504 splx(s); 505 return (ENXIO); 506 } 507 vp = sc->sc_ports[port]; 508 tp = vp->vp_tty; 509 #ifdef NOTYET 510 vp->vp_guest_open = 1; 511 #endif 512 splx(s); 513 514 if (!ISSET(tp->t_state, TS_ISOPEN)) { 515 SET(tp->t_state, TS_WOPEN); 516 ttychars(tp); 517 tp->t_ispeed = 1000000; 518 tp->t_ospeed = 1000000; 519 tp->t_cflag = TTYDEF_CFLAG|CLOCAL|CRTSCTS; 520 tp->t_iflag = TTYDEF_IFLAG; 521 tp->t_oflag = TTYDEF_OFLAG; 522 tp->t_lflag = TTYDEF_LFLAG; 523 if (vp->vp_cols != 0) { 524 tp->t_winsize.ws_col = vp->vp_cols; 525 tp->t_winsize.ws_row = vp->vp_rows; 526 } 527 528 s = spltty(); 529 vioconparam(tp, &tp->t_termios); 530 ttsetwater(tp); 531 splx(s); 532 } 533 else if (ISSET(tp->t_state, TS_XCLUDE) && suser(p) != 0) { 534 return (EBUSY); 535 } 536 537 error = linesw[tp->t_line].l_open(dev, tp, p); 538 return error; 539 } 540 541 int 542 vioconclose(dev_t dev, int flag, int mode, struct proc *p) 543 { 544 struct viocon_port *vp = dev2port(dev); 545 struct tty *tp = vp->vp_tty; 546 int s; 547 548 if (!ISSET(tp->t_state, TS_ISOPEN)) 549 return 0; 550 551 linesw[tp->t_line].l_close(tp, flag, p); 552 s = spltty(); 553 #ifdef NOTYET 554 vp->vp_guest_open = 0; 555 #endif 556 CLR(tp->t_state, TS_BUSY | TS_FLUSH); 557 ttyclose(tp); 558 splx(s); 559 560 return 0; 561 } 562 563 int 564 vioconread(dev_t dev, struct uio *uio, int flag) 565 { 566 struct viocon_port *vp = dev2port(dev); 567 struct tty *tp = vp->vp_tty; 568 569 return linesw[tp->t_line].l_read(tp, uio, flag); 570 } 571 572 int 573 vioconwrite(dev_t dev, struct uio *uio, int flag) 574 { 575 struct viocon_port *vp = dev2port(dev); 576 struct tty *tp = vp->vp_tty; 577 578 return linesw[tp->t_line].l_write(tp, uio, flag); 579 } 580 581 struct tty * 582 viocontty(dev_t dev) 583 { 584 struct viocon_port *vp = dev2port(dev); 585 586 return vp->vp_tty; 587 } 588 589 int 590 vioconstop(struct tty *tp, int flag) 591 { 592 int s; 593 594 s = spltty(); 595 if (ISSET(tp->t_state, TS_BUSY)) 596 if (!ISSET(tp->t_state, TS_TTSTOP)) 597 SET(tp->t_state, TS_FLUSH); 598 splx(s); 599 return 0; 600 } 601 602 int 603 vioconioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) 604 { 605 struct viocon_port *vp = dev2port(dev); 606 struct tty *tp; 607 int error1, error2; 608 609 tp = vp->vp_tty; 610 611 error1 = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); 612 if (error1 >= 0) 613 return error1; 614 error2 = ttioctl(tp, cmd, data, flag, p); 615 if (error2 >= 0) 616 return error2; 617 return ENOTTY; 618 } 619