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