1 /*- 2 * Copyright (c) 2014, Bryan Venteicher <bryanv@FreeBSD.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice unmodified, this list of conditions, and the following 10 * disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27 /* Driver for VirtIO console devices. */ 28 29 #include <sys/cdefs.h> 30 __FBSDID("$FreeBSD$"); 31 32 #include <sys/param.h> 33 #include <sys/systm.h> 34 #include <sys/kernel.h> 35 #include <sys/malloc.h> 36 #include <sys/module.h> 37 #include <sys/kdb.h> 38 #include <sys/lock.h> 39 #include <sys/mutex.h> 40 #include <sys/sglist.h> 41 #include <sys/sysctl.h> 42 #include <sys/taskqueue.h> 43 #include <sys/queue.h> 44 45 #include <sys/conf.h> 46 #include <sys/cons.h> 47 #include <sys/tty.h> 48 49 #include <machine/bus.h> 50 #include <machine/resource.h> 51 #include <sys/bus.h> 52 53 #include <dev/virtio/virtio.h> 54 #include <dev/virtio/virtqueue.h> 55 #include <dev/virtio/console/virtio_console.h> 56 57 #include "virtio_if.h" 58 59 #define VTCON_MAX_PORTS 32 60 #define VTCON_TTY_PREFIX "V" 61 #define VTCON_BULK_BUFSZ 128 62 63 /* 64 * The buffer cannot cross more than one page boundary due to the 65 * size of the sglist segment array used. 66 */ 67 CTASSERT(VTCON_BULK_BUFSZ <= PAGE_SIZE); 68 69 struct vtcon_softc; 70 struct vtcon_softc_port; 71 72 struct vtcon_port { 73 struct mtx vtcport_mtx; 74 struct vtcon_softc *vtcport_sc; 75 struct vtcon_softc_port *vtcport_scport; 76 struct tty *vtcport_tty; 77 struct virtqueue *vtcport_invq; 78 struct virtqueue *vtcport_outvq; 79 int vtcport_id; 80 int vtcport_flags; 81 #define VTCON_PORT_FLAG_GONE 0x01 82 #define VTCON_PORT_FLAG_CONSOLE 0x02 83 84 #if defined(KDB) 85 int vtcport_alt_break_state; 86 #endif 87 }; 88 89 #define VTCON_PORT_LOCK(_port) mtx_lock(&(_port)->vtcport_mtx) 90 #define VTCON_PORT_UNLOCK(_port) mtx_unlock(&(_port)->vtcport_mtx) 91 92 struct vtcon_softc_port { 93 struct vtcon_softc *vcsp_sc; 94 struct vtcon_port *vcsp_port; 95 struct virtqueue *vcsp_invq; 96 struct virtqueue *vcsp_outvq; 97 }; 98 99 struct vtcon_softc { 100 device_t vtcon_dev; 101 struct mtx vtcon_mtx; 102 uint64_t vtcon_features; 103 uint32_t vtcon_max_ports; 104 uint32_t vtcon_flags; 105 #define VTCON_FLAG_DETACHED 0x01 106 #define VTCON_FLAG_SIZE 0x02 107 #define VTCON_FLAG_MULTIPORT 0x04 108 109 /* 110 * Ports can be added and removed during runtime, but we have 111 * to allocate all the virtqueues during attach. This array is 112 * indexed by the port ID. 113 */ 114 struct vtcon_softc_port *vtcon_ports; 115 116 struct task vtcon_ctrl_task; 117 struct virtqueue *vtcon_ctrl_rxvq; 118 struct virtqueue *vtcon_ctrl_txvq; 119 struct mtx vtcon_ctrl_tx_mtx; 120 }; 121 122 #define VTCON_LOCK(_sc) mtx_lock(&(_sc)->vtcon_mtx) 123 #define VTCON_UNLOCK(_sc) mtx_unlock(&(_sc)->vtcon_mtx) 124 #define VTCON_LOCK_ASSERT(_sc) \ 125 mtx_assert(&(_sc)->vtcon_mtx, MA_OWNED) 126 #define VTCON_LOCK_ASSERT_NOTOWNED(_sc) \ 127 mtx_assert(&(_sc)->vtcon_mtx, MA_NOTOWNED) 128 129 #define VTCON_CTRL_TX_LOCK(_sc) mtx_lock(&(_sc)->vtcon_ctrl_tx_mtx) 130 #define VTCON_CTRL_TX_UNLOCK(_sc) mtx_unlock(&(_sc)->vtcon_ctrl_tx_mtx) 131 132 #define VTCON_ASSERT_VALID_PORTID(_sc, _id) \ 133 KASSERT((_id) >= 0 && (_id) < (_sc)->vtcon_max_ports, \ 134 ("%s: port ID %d out of range", __func__, _id)) 135 136 #define VTCON_FEATURES VIRTIO_CONSOLE_F_MULTIPORT 137 138 static struct virtio_feature_desc vtcon_feature_desc[] = { 139 { VIRTIO_CONSOLE_F_SIZE, "ConsoleSize" }, 140 { VIRTIO_CONSOLE_F_MULTIPORT, "MultiplePorts" }, 141 { VIRTIO_CONSOLE_F_EMERG_WRITE, "EmergencyWrite" }, 142 143 { 0, NULL } 144 }; 145 146 static int vtcon_modevent(module_t, int, void *); 147 static void vtcon_drain_all(void); 148 149 static int vtcon_probe(device_t); 150 static int vtcon_attach(device_t); 151 static int vtcon_detach(device_t); 152 static int vtcon_config_change(device_t); 153 154 static void vtcon_setup_features(struct vtcon_softc *); 155 static void vtcon_negotiate_features(struct vtcon_softc *); 156 static int vtcon_alloc_scports(struct vtcon_softc *); 157 static int vtcon_alloc_virtqueues(struct vtcon_softc *); 158 static void vtcon_read_config(struct vtcon_softc *, 159 struct virtio_console_config *); 160 161 static void vtcon_determine_max_ports(struct vtcon_softc *, 162 struct virtio_console_config *); 163 static void vtcon_destroy_ports(struct vtcon_softc *); 164 static void vtcon_stop(struct vtcon_softc *); 165 166 static int vtcon_ctrl_event_enqueue(struct vtcon_softc *, 167 struct virtio_console_control *); 168 static int vtcon_ctrl_event_create(struct vtcon_softc *); 169 static void vtcon_ctrl_event_requeue(struct vtcon_softc *, 170 struct virtio_console_control *); 171 static int vtcon_ctrl_event_populate(struct vtcon_softc *); 172 static void vtcon_ctrl_event_drain(struct vtcon_softc *); 173 static int vtcon_ctrl_init(struct vtcon_softc *); 174 static void vtcon_ctrl_deinit(struct vtcon_softc *); 175 static void vtcon_ctrl_port_add_event(struct vtcon_softc *, int); 176 static void vtcon_ctrl_port_remove_event(struct vtcon_softc *, int); 177 static void vtcon_ctrl_port_console_event(struct vtcon_softc *, int); 178 static void vtcon_ctrl_port_open_event(struct vtcon_softc *, int); 179 static void vtcon_ctrl_port_name_event(struct vtcon_softc *, int, 180 const char *, size_t); 181 static void vtcon_ctrl_process_event(struct vtcon_softc *, 182 struct virtio_console_control *, void *, size_t); 183 static void vtcon_ctrl_task_cb(void *, int); 184 static void vtcon_ctrl_event_intr(void *); 185 static void vtcon_ctrl_poll(struct vtcon_softc *, 186 struct virtio_console_control *control); 187 static void vtcon_ctrl_send_control(struct vtcon_softc *, uint32_t, 188 uint16_t, uint16_t); 189 190 static int vtcon_port_enqueue_buf(struct vtcon_port *, void *, size_t); 191 static int vtcon_port_create_buf(struct vtcon_port *); 192 static void vtcon_port_requeue_buf(struct vtcon_port *, void *); 193 static int vtcon_port_populate(struct vtcon_port *); 194 static void vtcon_port_destroy(struct vtcon_port *); 195 static int vtcon_port_create(struct vtcon_softc *, int); 196 static void vtcon_port_drain_bufs(struct virtqueue *); 197 static void vtcon_port_drain(struct vtcon_port *); 198 static void vtcon_port_teardown(struct vtcon_port *); 199 static void vtcon_port_change_size(struct vtcon_port *, uint16_t, 200 uint16_t); 201 static void vtcon_port_update_console_size(struct vtcon_softc *); 202 static void vtcon_port_enable_intr(struct vtcon_port *); 203 static void vtcon_port_disable_intr(struct vtcon_port *); 204 static void vtcon_port_in(struct vtcon_port *); 205 static void vtcon_port_intr(void *); 206 static void vtcon_port_out(struct vtcon_port *, void *, int); 207 static void vtcon_port_submit_event(struct vtcon_port *, uint16_t, 208 uint16_t); 209 210 static int vtcon_tty_open(struct tty *); 211 static void vtcon_tty_close(struct tty *); 212 static void vtcon_tty_outwakeup(struct tty *); 213 static void vtcon_tty_free(void *); 214 215 static void vtcon_get_console_size(struct vtcon_softc *, uint16_t *, 216 uint16_t *); 217 218 static void vtcon_enable_interrupts(struct vtcon_softc *); 219 static void vtcon_disable_interrupts(struct vtcon_softc *); 220 221 static int vtcon_pending_free; 222 223 static struct ttydevsw vtcon_tty_class = { 224 .tsw_flags = 0, 225 .tsw_open = vtcon_tty_open, 226 .tsw_close = vtcon_tty_close, 227 .tsw_outwakeup = vtcon_tty_outwakeup, 228 .tsw_free = vtcon_tty_free, 229 }; 230 231 static device_method_t vtcon_methods[] = { 232 /* Device methods. */ 233 DEVMETHOD(device_probe, vtcon_probe), 234 DEVMETHOD(device_attach, vtcon_attach), 235 DEVMETHOD(device_detach, vtcon_detach), 236 237 /* VirtIO methods. */ 238 DEVMETHOD(virtio_config_change, vtcon_config_change), 239 240 DEVMETHOD_END 241 }; 242 243 static driver_t vtcon_driver = { 244 "vtcon", 245 vtcon_methods, 246 sizeof(struct vtcon_softc) 247 }; 248 static devclass_t vtcon_devclass; 249 250 DRIVER_MODULE(virtio_console, virtio_pci, vtcon_driver, vtcon_devclass, 251 vtcon_modevent, 0); 252 MODULE_VERSION(virtio_console, 1); 253 MODULE_DEPEND(virtio_console, virtio, 1, 1, 1); 254 255 static int 256 vtcon_modevent(module_t mod, int type, void *unused) 257 { 258 int error; 259 260 switch (type) { 261 case MOD_LOAD: 262 error = 0; 263 break; 264 case MOD_QUIESCE: 265 error = 0; 266 break; 267 case MOD_UNLOAD: 268 vtcon_drain_all(); 269 error = 0; 270 break; 271 case MOD_SHUTDOWN: 272 error = 0; 273 break; 274 default: 275 error = EOPNOTSUPP; 276 break; 277 } 278 279 return (error); 280 } 281 282 static void 283 vtcon_drain_all(void) 284 { 285 int first; 286 287 for (first = 1; vtcon_pending_free != 0; first = 0) { 288 if (first != 0) { 289 printf("virtio_console: Waiting for all detached TTY " 290 "devices to have open fds closed.\n"); 291 } 292 pause("vtcondra", hz); 293 } 294 } 295 296 static int 297 vtcon_probe(device_t dev) 298 { 299 300 if (virtio_get_device_type(dev) != VIRTIO_ID_CONSOLE) 301 return (ENXIO); 302 303 device_set_desc(dev, "VirtIO Console Adapter"); 304 305 return (BUS_PROBE_DEFAULT); 306 } 307 308 static int 309 vtcon_attach(device_t dev) 310 { 311 struct vtcon_softc *sc; 312 struct virtio_console_config concfg; 313 int error; 314 315 sc = device_get_softc(dev); 316 sc->vtcon_dev = dev; 317 318 mtx_init(&sc->vtcon_mtx, "vtconmtx", NULL, MTX_DEF); 319 mtx_init(&sc->vtcon_ctrl_tx_mtx, "vtconctrlmtx", NULL, MTX_DEF); 320 321 virtio_set_feature_desc(dev, vtcon_feature_desc); 322 vtcon_setup_features(sc); 323 324 vtcon_read_config(sc, &concfg); 325 vtcon_determine_max_ports(sc, &concfg); 326 327 error = vtcon_alloc_scports(sc); 328 if (error) { 329 device_printf(dev, "cannot allocate softc port structures\n"); 330 goto fail; 331 } 332 333 error = vtcon_alloc_virtqueues(sc); 334 if (error) { 335 device_printf(dev, "cannot allocate virtqueues\n"); 336 goto fail; 337 } 338 339 if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) { 340 TASK_INIT(&sc->vtcon_ctrl_task, 0, vtcon_ctrl_task_cb, sc); 341 error = vtcon_ctrl_init(sc); 342 if (error) 343 goto fail; 344 } else { 345 error = vtcon_port_create(sc, 0); 346 if (error) 347 goto fail; 348 if (sc->vtcon_flags & VTCON_FLAG_SIZE) 349 vtcon_port_update_console_size(sc); 350 } 351 352 error = virtio_setup_intr(dev, INTR_TYPE_TTY); 353 if (error) { 354 device_printf(dev, "cannot setup virtqueue interrupts\n"); 355 goto fail; 356 } 357 358 vtcon_enable_interrupts(sc); 359 360 vtcon_ctrl_send_control(sc, VIRTIO_CONSOLE_BAD_ID, 361 VIRTIO_CONSOLE_DEVICE_READY, 1); 362 363 fail: 364 if (error) 365 vtcon_detach(dev); 366 367 return (error); 368 } 369 370 static int 371 vtcon_detach(device_t dev) 372 { 373 struct vtcon_softc *sc; 374 375 sc = device_get_softc(dev); 376 377 VTCON_LOCK(sc); 378 sc->vtcon_flags |= VTCON_FLAG_DETACHED; 379 if (device_is_attached(dev)) 380 vtcon_stop(sc); 381 VTCON_UNLOCK(sc); 382 383 if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) { 384 taskqueue_drain(taskqueue_thread, &sc->vtcon_ctrl_task); 385 vtcon_ctrl_deinit(sc); 386 } 387 388 vtcon_destroy_ports(sc); 389 mtx_destroy(&sc->vtcon_mtx); 390 mtx_destroy(&sc->vtcon_ctrl_tx_mtx); 391 392 return (0); 393 } 394 395 static int 396 vtcon_config_change(device_t dev) 397 { 398 struct vtcon_softc *sc; 399 400 sc = device_get_softc(dev); 401 402 /* 403 * When the multiport feature is negotiated, all configuration 404 * changes are done through control virtqueue events. 405 */ 406 if ((sc->vtcon_flags & VTCON_FLAG_MULTIPORT) == 0) { 407 if (sc->vtcon_flags & VTCON_FLAG_SIZE) 408 vtcon_port_update_console_size(sc); 409 } 410 411 return (0); 412 } 413 414 static void 415 vtcon_negotiate_features(struct vtcon_softc *sc) 416 { 417 device_t dev; 418 uint64_t features; 419 420 dev = sc->vtcon_dev; 421 features = VTCON_FEATURES; 422 423 sc->vtcon_features = virtio_negotiate_features(dev, features); 424 } 425 426 static void 427 vtcon_setup_features(struct vtcon_softc *sc) 428 { 429 device_t dev; 430 431 dev = sc->vtcon_dev; 432 433 vtcon_negotiate_features(sc); 434 435 if (virtio_with_feature(dev, VIRTIO_CONSOLE_F_SIZE)) 436 sc->vtcon_flags |= VTCON_FLAG_SIZE; 437 if (virtio_with_feature(dev, VIRTIO_CONSOLE_F_MULTIPORT)) 438 sc->vtcon_flags |= VTCON_FLAG_MULTIPORT; 439 } 440 441 #define VTCON_GET_CONFIG(_dev, _feature, _field, _cfg) \ 442 if (virtio_with_feature(_dev, _feature)) { \ 443 virtio_read_device_config(_dev, \ 444 offsetof(struct virtio_console_config, _field), \ 445 &(_cfg)->_field, sizeof((_cfg)->_field)); \ 446 } 447 448 static void 449 vtcon_read_config(struct vtcon_softc *sc, struct virtio_console_config *concfg) 450 { 451 device_t dev; 452 453 dev = sc->vtcon_dev; 454 455 bzero(concfg, sizeof(struct virtio_console_config)); 456 457 VTCON_GET_CONFIG(dev, VIRTIO_CONSOLE_F_SIZE, cols, concfg); 458 VTCON_GET_CONFIG(dev, VIRTIO_CONSOLE_F_SIZE, rows, concfg); 459 VTCON_GET_CONFIG(dev, VIRTIO_CONSOLE_F_MULTIPORT, max_nr_ports, concfg); 460 } 461 462 #undef VTCON_GET_CONFIG 463 464 static int 465 vtcon_alloc_scports(struct vtcon_softc *sc) 466 { 467 struct vtcon_softc_port *scport; 468 int max, i; 469 470 max = sc->vtcon_max_ports; 471 472 sc->vtcon_ports = malloc(sizeof(struct vtcon_softc_port) * max, 473 M_DEVBUF, M_NOWAIT | M_ZERO); 474 if (sc->vtcon_ports == NULL) 475 return (ENOMEM); 476 477 for (i = 0; i < max; i++) { 478 scport = &sc->vtcon_ports[i]; 479 scport->vcsp_sc = sc; 480 } 481 482 return (0); 483 } 484 485 static int 486 vtcon_alloc_virtqueues(struct vtcon_softc *sc) 487 { 488 device_t dev; 489 struct vq_alloc_info *info; 490 struct vtcon_softc_port *scport; 491 int i, idx, portidx, nvqs, error; 492 493 dev = sc->vtcon_dev; 494 495 nvqs = sc->vtcon_max_ports * 2; 496 if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) 497 nvqs += 2; 498 499 info = malloc(sizeof(struct vq_alloc_info) * nvqs, M_TEMP, M_NOWAIT); 500 if (info == NULL) 501 return (ENOMEM); 502 503 for (i = 0, idx = 0, portidx = 0; i < nvqs / 2; i++, idx += 2) { 504 505 if (i == 1) { 506 /* The control virtqueues are after the first port. */ 507 VQ_ALLOC_INFO_INIT(&info[idx], 0, 508 vtcon_ctrl_event_intr, sc, &sc->vtcon_ctrl_rxvq, 509 "%s-control rx", device_get_nameunit(dev)); 510 VQ_ALLOC_INFO_INIT(&info[idx+1], 0, 511 NULL, sc, &sc->vtcon_ctrl_txvq, 512 "%s-control tx", device_get_nameunit(dev)); 513 continue; 514 } 515 516 scport = &sc->vtcon_ports[portidx]; 517 518 VQ_ALLOC_INFO_INIT(&info[idx], 0, vtcon_port_intr, 519 scport, &scport->vcsp_invq, "%s-port%d in", 520 device_get_nameunit(dev), i); 521 VQ_ALLOC_INFO_INIT(&info[idx+1], 0, NULL, 522 NULL, &scport->vcsp_outvq, "%s-port%d out", 523 device_get_nameunit(dev), i); 524 525 portidx++; 526 } 527 528 error = virtio_alloc_virtqueues(dev, 0, nvqs, info); 529 free(info, M_TEMP); 530 531 return (error); 532 } 533 534 static void 535 vtcon_determine_max_ports(struct vtcon_softc *sc, 536 struct virtio_console_config *concfg) 537 { 538 539 if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) { 540 sc->vtcon_max_ports = 541 min(concfg->max_nr_ports, VTCON_MAX_PORTS); 542 if (sc->vtcon_max_ports == 0) 543 sc->vtcon_max_ports = 1; 544 } else 545 sc->vtcon_max_ports = 1; 546 } 547 548 static void 549 vtcon_destroy_ports(struct vtcon_softc *sc) 550 { 551 struct vtcon_softc_port *scport; 552 struct vtcon_port *port; 553 struct virtqueue *vq; 554 int i; 555 556 if (sc->vtcon_ports == NULL) 557 return; 558 559 VTCON_LOCK(sc); 560 for (i = 0; i < sc->vtcon_max_ports; i++) { 561 scport = &sc->vtcon_ports[i]; 562 563 port = scport->vcsp_port; 564 if (port != NULL) { 565 scport->vcsp_port = NULL; 566 VTCON_PORT_LOCK(port); 567 VTCON_UNLOCK(sc); 568 vtcon_port_teardown(port); 569 VTCON_LOCK(sc); 570 } 571 572 vq = scport->vcsp_invq; 573 if (vq != NULL) 574 vtcon_port_drain_bufs(vq); 575 } 576 VTCON_UNLOCK(sc); 577 578 free(sc->vtcon_ports, M_DEVBUF); 579 sc->vtcon_ports = NULL; 580 } 581 582 static void 583 vtcon_stop(struct vtcon_softc *sc) 584 { 585 586 vtcon_disable_interrupts(sc); 587 virtio_stop(sc->vtcon_dev); 588 } 589 590 static int 591 vtcon_ctrl_event_enqueue(struct vtcon_softc *sc, 592 struct virtio_console_control *control) 593 { 594 struct sglist_seg segs[2]; 595 struct sglist sg; 596 struct virtqueue *vq; 597 int error; 598 599 vq = sc->vtcon_ctrl_rxvq; 600 601 sglist_init(&sg, 2, segs); 602 error = sglist_append(&sg, control, 603 sizeof(struct virtio_console_control)); 604 KASSERT(error == 0, ("%s: error %d adding control to sglist", 605 __func__, error)); 606 607 return (virtqueue_enqueue(vq, control, &sg, 0, sg.sg_nseg)); 608 } 609 610 static int 611 vtcon_ctrl_event_create(struct vtcon_softc *sc) 612 { 613 struct virtio_console_control *control; 614 int error; 615 616 control = malloc( 617 sizeof(struct virtio_console_control) + VTCON_BULK_BUFSZ, 618 M_DEVBUF, M_ZERO | M_NOWAIT); 619 620 if (control == NULL) 621 return (ENOMEM); 622 623 error = vtcon_ctrl_event_enqueue(sc, control); 624 if (error) 625 free(control, M_DEVBUF); 626 627 return (error); 628 } 629 630 static void 631 vtcon_ctrl_event_requeue(struct vtcon_softc *sc, 632 struct virtio_console_control *control) 633 { 634 int error; 635 636 bzero(control, sizeof(struct virtio_console_control)); 637 638 error = vtcon_ctrl_event_enqueue(sc, control); 639 KASSERT(error == 0, 640 ("%s: cannot requeue control buffer %d", __func__, error)); 641 } 642 643 static int 644 vtcon_ctrl_event_populate(struct vtcon_softc *sc) 645 { 646 struct virtqueue *vq; 647 int nbufs, error; 648 649 vq = sc->vtcon_ctrl_rxvq; 650 error = ENOSPC; 651 652 for (nbufs = 0; !virtqueue_full(vq); nbufs++) { 653 error = vtcon_ctrl_event_create(sc); 654 if (error) 655 break; 656 } 657 658 if (nbufs > 0) { 659 virtqueue_notify(vq); 660 error = 0; 661 } 662 663 return (error); 664 } 665 666 static void 667 vtcon_ctrl_event_drain(struct vtcon_softc *sc) 668 { 669 struct virtio_console_control *control; 670 struct virtqueue *vq; 671 int last; 672 673 vq = sc->vtcon_ctrl_rxvq; 674 last = 0; 675 676 if (vq == NULL) 677 return; 678 679 VTCON_LOCK(sc); 680 while ((control = virtqueue_drain(vq, &last)) != NULL) 681 free(control, M_DEVBUF); 682 VTCON_UNLOCK(sc); 683 } 684 685 static int 686 vtcon_ctrl_init(struct vtcon_softc *sc) 687 { 688 int error; 689 690 error = vtcon_ctrl_event_populate(sc); 691 692 return (error); 693 } 694 695 static void 696 vtcon_ctrl_deinit(struct vtcon_softc *sc) 697 { 698 699 vtcon_ctrl_event_drain(sc); 700 } 701 702 static void 703 vtcon_ctrl_port_add_event(struct vtcon_softc *sc, int id) 704 { 705 device_t dev; 706 int error; 707 708 dev = sc->vtcon_dev; 709 710 /* This single thread only way for ports to be created. */ 711 if (sc->vtcon_ports[id].vcsp_port != NULL) { 712 device_printf(dev, "%s: adding port %d, but already exists\n", 713 __func__, id); 714 return; 715 } 716 717 error = vtcon_port_create(sc, id); 718 if (error) { 719 device_printf(dev, "%s: cannot create port %d: %d\n", 720 __func__, id, error); 721 vtcon_ctrl_send_control(sc, id, VIRTIO_CONSOLE_PORT_READY, 0); 722 return; 723 } 724 } 725 726 static void 727 vtcon_ctrl_port_remove_event(struct vtcon_softc *sc, int id) 728 { 729 device_t dev; 730 struct vtcon_softc_port *scport; 731 struct vtcon_port *port; 732 733 dev = sc->vtcon_dev; 734 scport = &sc->vtcon_ports[id]; 735 736 VTCON_LOCK(sc); 737 port = scport->vcsp_port; 738 if (port == NULL) { 739 VTCON_UNLOCK(sc); 740 device_printf(dev, "%s: remove port %d, but does not exist\n", 741 __func__, id); 742 return; 743 } 744 745 scport->vcsp_port = NULL; 746 VTCON_PORT_LOCK(port); 747 VTCON_UNLOCK(sc); 748 vtcon_port_teardown(port); 749 } 750 751 static void 752 vtcon_ctrl_port_console_event(struct vtcon_softc *sc, int id) 753 { 754 device_t dev; 755 struct vtcon_softc_port *scport; 756 struct vtcon_port *port; 757 758 dev = sc->vtcon_dev; 759 scport = &sc->vtcon_ports[id]; 760 761 VTCON_LOCK(sc); 762 port = scport->vcsp_port; 763 if (port == NULL) { 764 VTCON_UNLOCK(sc); 765 device_printf(dev, "%s: console port %d, but does not exist\n", 766 __func__, id); 767 return; 768 } 769 770 VTCON_PORT_LOCK(port); 771 VTCON_UNLOCK(sc); 772 port->vtcport_flags |= VTCON_PORT_FLAG_CONSOLE; 773 vtcon_port_submit_event(port, VIRTIO_CONSOLE_PORT_OPEN, 1); 774 VTCON_PORT_UNLOCK(port); 775 } 776 777 static void 778 vtcon_ctrl_port_open_event(struct vtcon_softc *sc, int id) 779 { 780 device_t dev; 781 struct vtcon_softc_port *scport; 782 struct vtcon_port *port; 783 784 dev = sc->vtcon_dev; 785 scport = &sc->vtcon_ports[id]; 786 787 VTCON_LOCK(sc); 788 port = scport->vcsp_port; 789 if (port == NULL) { 790 VTCON_UNLOCK(sc); 791 device_printf(dev, "%s: open port %d, but does not exist\n", 792 __func__, id); 793 return; 794 } 795 796 VTCON_PORT_LOCK(port); 797 VTCON_UNLOCK(sc); 798 vtcon_port_enable_intr(port); 799 VTCON_PORT_UNLOCK(port); 800 } 801 802 static void 803 vtcon_ctrl_port_name_event(struct vtcon_softc *sc, int id, const char *name, 804 size_t len) 805 { 806 device_t dev; 807 struct vtcon_softc_port *scport; 808 struct vtcon_port *port; 809 810 dev = sc->vtcon_dev; 811 scport = &sc->vtcon_ports[id]; 812 813 port = scport->vcsp_port; 814 if (port == NULL) { 815 device_printf(dev, "%s: name port %d, but does not exist\n", 816 __func__, id); 817 return; 818 } 819 820 tty_makealias(port->vtcport_tty, "vtcon/%*s", (int)len, name); 821 } 822 823 static void 824 vtcon_ctrl_process_event(struct vtcon_softc *sc, 825 struct virtio_console_control *control, void *payload, size_t plen) 826 { 827 device_t dev; 828 int id; 829 830 dev = sc->vtcon_dev; 831 id = control->id; 832 833 if (id < 0 || id >= sc->vtcon_max_ports) { 834 device_printf(dev, "%s: invalid port ID %d\n", __func__, id); 835 return; 836 } 837 838 switch (control->event) { 839 case VIRTIO_CONSOLE_PORT_ADD: 840 vtcon_ctrl_port_add_event(sc, id); 841 break; 842 843 case VIRTIO_CONSOLE_PORT_REMOVE: 844 vtcon_ctrl_port_remove_event(sc, id); 845 break; 846 847 case VIRTIO_CONSOLE_CONSOLE_PORT: 848 vtcon_ctrl_port_console_event(sc, id); 849 break; 850 851 case VIRTIO_CONSOLE_RESIZE: 852 break; 853 854 case VIRTIO_CONSOLE_PORT_OPEN: 855 vtcon_ctrl_port_open_event(sc, id); 856 break; 857 858 case VIRTIO_CONSOLE_PORT_NAME: 859 if (payload != NULL && plen > 0) 860 vtcon_ctrl_port_name_event(sc, id, 861 (const char *)payload, plen); 862 break; 863 } 864 } 865 866 static void 867 vtcon_ctrl_task_cb(void *xsc, int pending) 868 { 869 struct vtcon_softc *sc; 870 struct virtqueue *vq; 871 struct virtio_console_control *control; 872 int detached; 873 uint32_t len; 874 size_t plen; 875 void *payload; 876 877 sc = xsc; 878 vq = sc->vtcon_ctrl_rxvq; 879 880 VTCON_LOCK(sc); 881 882 while ((detached = (sc->vtcon_flags & VTCON_FLAG_DETACHED)) == 0) { 883 control = virtqueue_dequeue(vq, &len); 884 payload = NULL; 885 plen = 0; 886 887 if (control == NULL) 888 break; 889 890 if (len > sizeof(control)) { 891 payload = (void *)(control + 1); 892 plen = len - sizeof(control); 893 } 894 895 VTCON_UNLOCK(sc); 896 vtcon_ctrl_process_event(sc, control, payload, plen); 897 VTCON_LOCK(sc); 898 vtcon_ctrl_event_requeue(sc, control); 899 } 900 901 if (!detached) { 902 virtqueue_notify(vq); 903 if (virtqueue_enable_intr(vq) != 0) 904 taskqueue_enqueue(taskqueue_thread, 905 &sc->vtcon_ctrl_task); 906 } 907 908 VTCON_UNLOCK(sc); 909 } 910 911 static void 912 vtcon_ctrl_event_intr(void *xsc) 913 { 914 struct vtcon_softc *sc; 915 916 sc = xsc; 917 918 /* 919 * Only some events require us to potentially block, but it 920 * easier to just defer all event handling to the taskqueue. 921 */ 922 taskqueue_enqueue(taskqueue_thread, &sc->vtcon_ctrl_task); 923 } 924 925 static void 926 vtcon_ctrl_poll(struct vtcon_softc *sc, 927 struct virtio_console_control *control) 928 { 929 struct sglist_seg segs[2]; 930 struct sglist sg; 931 struct virtqueue *vq; 932 int error; 933 934 vq = sc->vtcon_ctrl_txvq; 935 936 sglist_init(&sg, 2, segs); 937 error = sglist_append(&sg, control, 938 sizeof(struct virtio_console_control)); 939 KASSERT(error == 0, ("%s: error %d adding control to sglist", 940 __func__, error)); 941 942 /* 943 * We cannot use the softc lock to serialize access to this 944 * virtqueue since this is called from the tty layer with the 945 * port lock held. Acquiring the softc would violate our lock 946 * ordering. 947 */ 948 VTCON_CTRL_TX_LOCK(sc); 949 KASSERT(virtqueue_empty(vq), 950 ("%s: virtqueue is not emtpy", __func__)); 951 error = virtqueue_enqueue(vq, control, &sg, sg.sg_nseg, 0); 952 if (error == 0) { 953 virtqueue_notify(vq); 954 virtqueue_poll(vq, NULL); 955 } 956 VTCON_CTRL_TX_UNLOCK(sc); 957 } 958 959 static void 960 vtcon_ctrl_send_control(struct vtcon_softc *sc, uint32_t portid, 961 uint16_t event, uint16_t value) 962 { 963 struct virtio_console_control control; 964 965 if ((sc->vtcon_flags & VTCON_FLAG_MULTIPORT) == 0) 966 return; 967 968 control.id = portid; 969 control.event = event; 970 control.value = value; 971 972 vtcon_ctrl_poll(sc, &control); 973 } 974 975 static int 976 vtcon_port_enqueue_buf(struct vtcon_port *port, void *buf, size_t len) 977 { 978 struct sglist_seg segs[2]; 979 struct sglist sg; 980 struct virtqueue *vq; 981 int error; 982 983 vq = port->vtcport_invq; 984 985 sglist_init(&sg, 2, segs); 986 error = sglist_append(&sg, buf, len); 987 KASSERT(error == 0, 988 ("%s: error %d adding buffer to sglist", __func__, error)); 989 990 error = virtqueue_enqueue(vq, buf, &sg, 0, sg.sg_nseg); 991 992 return (error); 993 } 994 995 static int 996 vtcon_port_create_buf(struct vtcon_port *port) 997 { 998 void *buf; 999 int error; 1000 1001 buf = malloc(VTCON_BULK_BUFSZ, M_DEVBUF, M_ZERO | M_NOWAIT); 1002 if (buf == NULL) 1003 return (ENOMEM); 1004 1005 error = vtcon_port_enqueue_buf(port, buf, VTCON_BULK_BUFSZ); 1006 if (error) 1007 free(buf, M_DEVBUF); 1008 1009 return (error); 1010 } 1011 1012 static void 1013 vtcon_port_requeue_buf(struct vtcon_port *port, void *buf) 1014 { 1015 int error; 1016 1017 error = vtcon_port_enqueue_buf(port, buf, VTCON_BULK_BUFSZ); 1018 KASSERT(error == 0, 1019 ("%s: cannot requeue input buffer %d", __func__, error)); 1020 } 1021 1022 static int 1023 vtcon_port_populate(struct vtcon_port *port) 1024 { 1025 struct virtqueue *vq; 1026 int nbufs, error; 1027 1028 vq = port->vtcport_invq; 1029 error = ENOSPC; 1030 1031 for (nbufs = 0; !virtqueue_full(vq); nbufs++) { 1032 error = vtcon_port_create_buf(port); 1033 if (error) 1034 break; 1035 } 1036 1037 if (nbufs > 0) { 1038 virtqueue_notify(vq); 1039 error = 0; 1040 } 1041 1042 return (error); 1043 } 1044 1045 static void 1046 vtcon_port_destroy(struct vtcon_port *port) 1047 { 1048 1049 port->vtcport_sc = NULL; 1050 port->vtcport_scport = NULL; 1051 port->vtcport_invq = NULL; 1052 port->vtcport_outvq = NULL; 1053 port->vtcport_id = -1; 1054 mtx_destroy(&port->vtcport_mtx); 1055 free(port, M_DEVBUF); 1056 } 1057 1058 static int 1059 vtcon_port_init_vqs(struct vtcon_port *port) 1060 { 1061 struct vtcon_softc_port *scport; 1062 int error; 1063 1064 scport = port->vtcport_scport; 1065 1066 port->vtcport_invq = scport->vcsp_invq; 1067 port->vtcport_outvq = scport->vcsp_outvq; 1068 1069 /* 1070 * Free any data left over from when this virtqueue was in use by a 1071 * prior port. We have not yet notified the host that the port is 1072 * ready, so assume nothing in the virtqueue can be for us. 1073 */ 1074 vtcon_port_drain(port); 1075 1076 KASSERT(virtqueue_empty(port->vtcport_invq), 1077 ("%s: in virtqueue is not empty", __func__)); 1078 KASSERT(virtqueue_empty(port->vtcport_outvq), 1079 ("%s: out virtqueue is not empty", __func__)); 1080 1081 error = vtcon_port_populate(port); 1082 if (error) 1083 return (error); 1084 1085 return (0); 1086 } 1087 1088 static int 1089 vtcon_port_create(struct vtcon_softc *sc, int id) 1090 { 1091 device_t dev; 1092 struct vtcon_softc_port *scport; 1093 struct vtcon_port *port; 1094 int error; 1095 1096 dev = sc->vtcon_dev; 1097 scport = &sc->vtcon_ports[id]; 1098 1099 VTCON_ASSERT_VALID_PORTID(sc, id); 1100 MPASS(scport->vcsp_port == NULL); 1101 1102 port = malloc(sizeof(struct vtcon_port), M_DEVBUF, M_NOWAIT | M_ZERO); 1103 if (port == NULL) 1104 return (ENOMEM); 1105 1106 port->vtcport_sc = sc; 1107 port->vtcport_scport = scport; 1108 port->vtcport_id = id; 1109 mtx_init(&port->vtcport_mtx, "vtcpmtx", NULL, MTX_DEF); 1110 port->vtcport_tty = tty_alloc_mutex(&vtcon_tty_class, port, 1111 &port->vtcport_mtx); 1112 1113 error = vtcon_port_init_vqs(port); 1114 if (error) { 1115 VTCON_PORT_LOCK(port); 1116 vtcon_port_teardown(port); 1117 return (error); 1118 } 1119 1120 VTCON_LOCK(sc); 1121 VTCON_PORT_LOCK(port); 1122 scport->vcsp_port = port; 1123 vtcon_port_enable_intr(port); 1124 vtcon_port_submit_event(port, VIRTIO_CONSOLE_PORT_READY, 1); 1125 VTCON_PORT_UNLOCK(port); 1126 VTCON_UNLOCK(sc); 1127 1128 tty_makedev(port->vtcport_tty, NULL, "%s%r.%r", VTCON_TTY_PREFIX, 1129 device_get_unit(dev), id); 1130 1131 return (0); 1132 } 1133 1134 static void 1135 vtcon_port_drain_bufs(struct virtqueue *vq) 1136 { 1137 void *buf; 1138 int last; 1139 1140 last = 0; 1141 1142 while ((buf = virtqueue_drain(vq, &last)) != NULL) 1143 free(buf, M_DEVBUF); 1144 } 1145 1146 static void 1147 vtcon_port_drain(struct vtcon_port *port) 1148 { 1149 1150 vtcon_port_drain_bufs(port->vtcport_invq); 1151 } 1152 1153 static void 1154 vtcon_port_teardown(struct vtcon_port *port) 1155 { 1156 struct tty *tp; 1157 1158 tp = port->vtcport_tty; 1159 1160 port->vtcport_flags |= VTCON_PORT_FLAG_GONE; 1161 1162 if (tp != NULL) { 1163 atomic_add_int(&vtcon_pending_free, 1); 1164 tty_rel_gone(tp); 1165 } else 1166 vtcon_port_destroy(port); 1167 } 1168 1169 static void 1170 vtcon_port_change_size(struct vtcon_port *port, uint16_t cols, uint16_t rows) 1171 { 1172 struct tty *tp; 1173 struct winsize sz; 1174 1175 tp = port->vtcport_tty; 1176 1177 if (tp == NULL) 1178 return; 1179 1180 bzero(&sz, sizeof(struct winsize)); 1181 sz.ws_col = cols; 1182 sz.ws_row = rows; 1183 1184 tty_set_winsize(tp, &sz); 1185 } 1186 1187 static void 1188 vtcon_port_update_console_size(struct vtcon_softc *sc) 1189 { 1190 struct vtcon_port *port; 1191 struct vtcon_softc_port *scport; 1192 uint16_t cols, rows; 1193 1194 vtcon_get_console_size(sc, &cols, &rows); 1195 1196 /* 1197 * For now, assume the first (only) port is the console. Note 1198 * QEMU does not implement this feature yet. 1199 */ 1200 scport = &sc->vtcon_ports[0]; 1201 1202 VTCON_LOCK(sc); 1203 port = scport->vcsp_port; 1204 1205 if (port != NULL) { 1206 VTCON_PORT_LOCK(port); 1207 VTCON_UNLOCK(sc); 1208 vtcon_port_change_size(port, cols, rows); 1209 VTCON_PORT_UNLOCK(port); 1210 } else 1211 VTCON_UNLOCK(sc); 1212 } 1213 1214 static void 1215 vtcon_port_enable_intr(struct vtcon_port *port) 1216 { 1217 1218 /* 1219 * NOTE: The out virtqueue is always polled, so its interrupt 1220 * kept disabled. 1221 */ 1222 virtqueue_enable_intr(port->vtcport_invq); 1223 } 1224 1225 static void 1226 vtcon_port_disable_intr(struct vtcon_port *port) 1227 { 1228 1229 if (port->vtcport_invq != NULL) 1230 virtqueue_disable_intr(port->vtcport_invq); 1231 if (port->vtcport_outvq != NULL) 1232 virtqueue_disable_intr(port->vtcport_outvq); 1233 } 1234 1235 static void 1236 vtcon_port_in(struct vtcon_port *port) 1237 { 1238 struct virtqueue *vq; 1239 struct tty *tp; 1240 char *buf; 1241 uint32_t len; 1242 int i, deq; 1243 1244 tp = port->vtcport_tty; 1245 vq = port->vtcport_invq; 1246 1247 again: 1248 deq = 0; 1249 1250 while ((buf = virtqueue_dequeue(vq, &len)) != NULL) { 1251 for (i = 0; i < len; i++) { 1252 #if defined(KDB) 1253 if (port->vtcport_flags & VTCON_PORT_FLAG_CONSOLE) 1254 kdb_alt_break(buf[i], 1255 &port->vtcport_alt_break_state); 1256 #endif 1257 ttydisc_rint(tp, buf[i], 0); 1258 } 1259 vtcon_port_requeue_buf(port, buf); 1260 deq++; 1261 } 1262 ttydisc_rint_done(tp); 1263 1264 if (deq > 0) 1265 virtqueue_notify(vq); 1266 1267 if (virtqueue_enable_intr(vq) != 0) 1268 goto again; 1269 } 1270 1271 static void 1272 vtcon_port_intr(void *scportx) 1273 { 1274 struct vtcon_softc_port *scport; 1275 struct vtcon_softc *sc; 1276 struct vtcon_port *port; 1277 1278 scport = scportx; 1279 sc = scport->vcsp_sc; 1280 1281 VTCON_LOCK(sc); 1282 port = scport->vcsp_port; 1283 if (port == NULL) { 1284 VTCON_UNLOCK(sc); 1285 return; 1286 } 1287 VTCON_PORT_LOCK(port); 1288 VTCON_UNLOCK(sc); 1289 if ((port->vtcport_flags & VTCON_PORT_FLAG_GONE) == 0) 1290 vtcon_port_in(port); 1291 VTCON_PORT_UNLOCK(port); 1292 } 1293 1294 static void 1295 vtcon_port_out(struct vtcon_port *port, void *buf, int bufsize) 1296 { 1297 struct sglist_seg segs[2]; 1298 struct sglist sg; 1299 struct virtqueue *vq; 1300 int error; 1301 1302 vq = port->vtcport_outvq; 1303 KASSERT(virtqueue_empty(vq), 1304 ("%s: port %p out virtqueue not emtpy", __func__, port)); 1305 1306 sglist_init(&sg, 2, segs); 1307 error = sglist_append(&sg, buf, bufsize); 1308 KASSERT(error == 0, ("%s: error %d adding buffer to sglist", 1309 __func__, error)); 1310 1311 error = virtqueue_enqueue(vq, buf, &sg, sg.sg_nseg, 0); 1312 if (error == 0) { 1313 virtqueue_notify(vq); 1314 virtqueue_poll(vq, NULL); 1315 } 1316 } 1317 1318 static void 1319 vtcon_port_submit_event(struct vtcon_port *port, uint16_t event, 1320 uint16_t value) 1321 { 1322 struct vtcon_softc *sc; 1323 1324 sc = port->vtcport_sc; 1325 1326 vtcon_ctrl_send_control(sc, port->vtcport_id, event, value); 1327 } 1328 1329 static int 1330 vtcon_tty_open(struct tty *tp) 1331 { 1332 struct vtcon_port *port; 1333 1334 port = tty_softc(tp); 1335 1336 if (port->vtcport_flags & VTCON_PORT_FLAG_GONE) 1337 return (ENXIO); 1338 1339 vtcon_port_submit_event(port, VIRTIO_CONSOLE_PORT_OPEN, 1); 1340 1341 return (0); 1342 } 1343 1344 static void 1345 vtcon_tty_close(struct tty *tp) 1346 { 1347 struct vtcon_port *port; 1348 1349 port = tty_softc(tp); 1350 1351 if (port->vtcport_flags & VTCON_PORT_FLAG_GONE) 1352 return; 1353 1354 vtcon_port_submit_event(port, VIRTIO_CONSOLE_PORT_OPEN, 0); 1355 } 1356 1357 static void 1358 vtcon_tty_outwakeup(struct tty *tp) 1359 { 1360 struct vtcon_port *port; 1361 char buf[VTCON_BULK_BUFSZ]; 1362 int len; 1363 1364 port = tty_softc(tp); 1365 1366 if (port->vtcport_flags & VTCON_PORT_FLAG_GONE) 1367 return; 1368 1369 while ((len = ttydisc_getc(tp, buf, sizeof(buf))) != 0) 1370 vtcon_port_out(port, buf, len); 1371 } 1372 1373 static void 1374 vtcon_tty_free(void *xport) 1375 { 1376 struct vtcon_port *port; 1377 1378 port = xport; 1379 1380 vtcon_port_destroy(port); 1381 atomic_subtract_int(&vtcon_pending_free, 1); 1382 } 1383 1384 static void 1385 vtcon_get_console_size(struct vtcon_softc *sc, uint16_t *cols, uint16_t *rows) 1386 { 1387 struct virtio_console_config concfg; 1388 1389 KASSERT(sc->vtcon_flags & VTCON_FLAG_SIZE, 1390 ("%s: size feature not negotiated", __func__)); 1391 1392 vtcon_read_config(sc, &concfg); 1393 1394 *cols = concfg.cols; 1395 *rows = concfg.rows; 1396 } 1397 1398 static void 1399 vtcon_enable_interrupts(struct vtcon_softc *sc) 1400 { 1401 struct vtcon_softc_port *scport; 1402 struct vtcon_port *port; 1403 int i; 1404 1405 VTCON_LOCK(sc); 1406 1407 if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) 1408 virtqueue_enable_intr(sc->vtcon_ctrl_rxvq); 1409 1410 for (i = 0; i < sc->vtcon_max_ports; i++) { 1411 scport = &sc->vtcon_ports[i]; 1412 1413 port = scport->vcsp_port; 1414 if (port == NULL) 1415 continue; 1416 1417 VTCON_PORT_LOCK(port); 1418 vtcon_port_enable_intr(port); 1419 VTCON_PORT_UNLOCK(port); 1420 } 1421 1422 VTCON_UNLOCK(sc); 1423 } 1424 1425 static void 1426 vtcon_disable_interrupts(struct vtcon_softc *sc) 1427 { 1428 struct vtcon_softc_port *scport; 1429 struct vtcon_port *port; 1430 int i; 1431 1432 VTCON_LOCK_ASSERT(sc); 1433 1434 if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) 1435 virtqueue_disable_intr(sc->vtcon_ctrl_rxvq); 1436 1437 for (i = 0; i < sc->vtcon_max_ports; i++) { 1438 scport = &sc->vtcon_ports[i]; 1439 1440 port = scport->vcsp_port; 1441 if (port == NULL) 1442 continue; 1443 1444 VTCON_PORT_LOCK(port); 1445 vtcon_port_disable_intr(port); 1446 VTCON_PORT_UNLOCK(port); 1447 } 1448 } 1449