1273515Sbryanv/*- 2273515Sbryanv * Copyright (c) 2014, Bryan Venteicher <bryanv@FreeBSD.org> 3273515Sbryanv * All rights reserved. 4273515Sbryanv * 5273515Sbryanv * Redistribution and use in source and binary forms, with or without 6273515Sbryanv * modification, are permitted provided that the following conditions 7273515Sbryanv * are met: 8273515Sbryanv * 1. Redistributions of source code must retain the above copyright 9273515Sbryanv * notice unmodified, this list of conditions, and the following 10273515Sbryanv * disclaimer. 11273515Sbryanv * 2. Redistributions in binary form must reproduce the above copyright 12273515Sbryanv * notice, this list of conditions and the following disclaimer in the 13273515Sbryanv * documentation and/or other materials provided with the distribution. 14273515Sbryanv * 15273515Sbryanv * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16273515Sbryanv * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17273515Sbryanv * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18273515Sbryanv * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19273515Sbryanv * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20273515Sbryanv * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21273515Sbryanv * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22273515Sbryanv * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23273515Sbryanv * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24273515Sbryanv * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25273515Sbryanv */ 26273515Sbryanv 27273515Sbryanv/* Driver for VirtIO console devices. */ 28273515Sbryanv 29273515Sbryanv#include <sys/cdefs.h> 30273515Sbryanv__FBSDID("$FreeBSD$"); 31273515Sbryanv 32273515Sbryanv#include <sys/param.h> 33273515Sbryanv#include <sys/systm.h> 34273515Sbryanv#include <sys/kernel.h> 35273515Sbryanv#include <sys/malloc.h> 36273515Sbryanv#include <sys/module.h> 37275273Sbryanv#include <sys/kdb.h> 38273515Sbryanv#include <sys/lock.h> 39273515Sbryanv#include <sys/mutex.h> 40273515Sbryanv#include <sys/sglist.h> 41273515Sbryanv#include <sys/sysctl.h> 42273515Sbryanv#include <sys/taskqueue.h> 43273515Sbryanv#include <sys/queue.h> 44273515Sbryanv 45273515Sbryanv#include <sys/conf.h> 46273515Sbryanv#include <sys/cons.h> 47273515Sbryanv#include <sys/tty.h> 48273515Sbryanv 49273515Sbryanv#include <machine/bus.h> 50273515Sbryanv#include <machine/resource.h> 51273515Sbryanv#include <sys/bus.h> 52273515Sbryanv 53273515Sbryanv#include <dev/virtio/virtio.h> 54273515Sbryanv#include <dev/virtio/virtqueue.h> 55273515Sbryanv#include <dev/virtio/console/virtio_console.h> 56273515Sbryanv 57273515Sbryanv#include "virtio_if.h" 58273515Sbryanv 59275273Sbryanv#define VTCON_MAX_PORTS 32 60273515Sbryanv#define VTCON_TTY_PREFIX "V" 61273515Sbryanv#define VTCON_BULK_BUFSZ 128 62273515Sbryanv 63275273Sbryanv/* 64275273Sbryanv * The buffer cannot cross more than one page boundary due to the 65275273Sbryanv * size of the sglist segment array used. 66275273Sbryanv */ 67275273SbryanvCTASSERT(VTCON_BULK_BUFSZ <= PAGE_SIZE); 68275273Sbryanv 69273515Sbryanvstruct vtcon_softc; 70275273Sbryanvstruct vtcon_softc_port; 71273515Sbryanv 72273515Sbryanvstruct vtcon_port { 73275273Sbryanv struct mtx vtcport_mtx; 74275273Sbryanv struct vtcon_softc *vtcport_sc; 75275273Sbryanv struct vtcon_softc_port *vtcport_scport; 76275273Sbryanv struct tty *vtcport_tty; 77275273Sbryanv struct virtqueue *vtcport_invq; 78275273Sbryanv struct virtqueue *vtcport_outvq; 79275273Sbryanv int vtcport_id; 80275273Sbryanv int vtcport_flags; 81275273Sbryanv#define VTCON_PORT_FLAG_GONE 0x01 82275273Sbryanv#define VTCON_PORT_FLAG_CONSOLE 0x02 83275273Sbryanv 84275273Sbryanv#if defined(KDB) 85275273Sbryanv int vtcport_alt_break_state; 86275273Sbryanv#endif 87273515Sbryanv}; 88273515Sbryanv 89275273Sbryanv#define VTCON_PORT_LOCK(_port) mtx_lock(&(_port)->vtcport_mtx) 90275273Sbryanv#define VTCON_PORT_UNLOCK(_port) mtx_unlock(&(_port)->vtcport_mtx) 91273515Sbryanv 92275273Sbryanvstruct vtcon_softc_port { 93275273Sbryanv struct vtcon_softc *vcsp_sc; 94275273Sbryanv struct vtcon_port *vcsp_port; 95275273Sbryanv struct virtqueue *vcsp_invq; 96275273Sbryanv struct virtqueue *vcsp_outvq; 97275273Sbryanv}; 98275273Sbryanv 99273515Sbryanvstruct vtcon_softc { 100273515Sbryanv device_t vtcon_dev; 101273515Sbryanv struct mtx vtcon_mtx; 102273515Sbryanv uint64_t vtcon_features; 103275273Sbryanv uint32_t vtcon_max_ports; 104273515Sbryanv uint32_t vtcon_flags; 105275273Sbryanv#define VTCON_FLAG_DETACHED 0x01 106275273Sbryanv#define VTCON_FLAG_SIZE 0x02 107275273Sbryanv#define VTCON_FLAG_MULTIPORT 0x04 108273515Sbryanv 109273515Sbryanv /* 110273515Sbryanv * Ports can be added and removed during runtime, but we have 111273515Sbryanv * to allocate all the virtqueues during attach. This array is 112273515Sbryanv * indexed by the port ID. 113273515Sbryanv */ 114275273Sbryanv struct vtcon_softc_port *vtcon_ports; 115275273Sbryanv 116275273Sbryanv struct task vtcon_ctrl_task; 117275273Sbryanv struct virtqueue *vtcon_ctrl_rxvq; 118275273Sbryanv struct virtqueue *vtcon_ctrl_txvq; 119275273Sbryanv struct mtx vtcon_ctrl_tx_mtx; 120273515Sbryanv}; 121273515Sbryanv 122275273Sbryanv#define VTCON_LOCK(_sc) mtx_lock(&(_sc)->vtcon_mtx) 123275273Sbryanv#define VTCON_UNLOCK(_sc) mtx_unlock(&(_sc)->vtcon_mtx) 124275273Sbryanv#define VTCON_LOCK_ASSERT(_sc) \ 125275273Sbryanv mtx_assert(&(_sc)->vtcon_mtx, MA_OWNED) 126275273Sbryanv#define VTCON_LOCK_ASSERT_NOTOWNED(_sc) \ 127275273Sbryanv mtx_assert(&(_sc)->vtcon_mtx, MA_NOTOWNED) 128273515Sbryanv 129275273Sbryanv#define VTCON_CTRL_TX_LOCK(_sc) mtx_lock(&(_sc)->vtcon_ctrl_tx_mtx) 130275273Sbryanv#define VTCON_CTRL_TX_UNLOCK(_sc) mtx_unlock(&(_sc)->vtcon_ctrl_tx_mtx) 131275273Sbryanv 132273515Sbryanv#define VTCON_ASSERT_VALID_PORTID(_sc, _id) \ 133273515Sbryanv KASSERT((_id) >= 0 && (_id) < (_sc)->vtcon_max_ports, \ 134273515Sbryanv ("%s: port ID %d out of range", __func__, _id)) 135273515Sbryanv 136275273Sbryanv#define VTCON_FEATURES VIRTIO_CONSOLE_F_MULTIPORT 137273515Sbryanv 138273515Sbryanvstatic struct virtio_feature_desc vtcon_feature_desc[] = { 139273515Sbryanv { VIRTIO_CONSOLE_F_SIZE, "ConsoleSize" }, 140273515Sbryanv { VIRTIO_CONSOLE_F_MULTIPORT, "MultiplePorts" }, 141275273Sbryanv { VIRTIO_CONSOLE_F_EMERG_WRITE, "EmergencyWrite" }, 142273515Sbryanv 143273515Sbryanv { 0, NULL } 144273515Sbryanv}; 145273515Sbryanv 146273515Sbryanvstatic int vtcon_modevent(module_t, int, void *); 147275273Sbryanvstatic void vtcon_drain_all(void); 148273515Sbryanv 149273515Sbryanvstatic int vtcon_probe(device_t); 150273515Sbryanvstatic int vtcon_attach(device_t); 151273515Sbryanvstatic int vtcon_detach(device_t); 152273515Sbryanvstatic int vtcon_config_change(device_t); 153273515Sbryanv 154275273Sbryanvstatic void vtcon_setup_features(struct vtcon_softc *); 155273515Sbryanvstatic void vtcon_negotiate_features(struct vtcon_softc *); 156275273Sbryanvstatic int vtcon_alloc_scports(struct vtcon_softc *); 157273515Sbryanvstatic int vtcon_alloc_virtqueues(struct vtcon_softc *); 158273515Sbryanvstatic void vtcon_read_config(struct vtcon_softc *, 159273515Sbryanv struct virtio_console_config *); 160273515Sbryanv 161273515Sbryanvstatic void vtcon_determine_max_ports(struct vtcon_softc *, 162273515Sbryanv struct virtio_console_config *); 163275273Sbryanvstatic void vtcon_destroy_ports(struct vtcon_softc *); 164273515Sbryanvstatic void vtcon_stop(struct vtcon_softc *); 165273515Sbryanv 166275273Sbryanvstatic int vtcon_ctrl_event_enqueue(struct vtcon_softc *, 167273515Sbryanv struct virtio_console_control *); 168275273Sbryanvstatic int vtcon_ctrl_event_create(struct vtcon_softc *); 169275273Sbryanvstatic void vtcon_ctrl_event_requeue(struct vtcon_softc *, 170273515Sbryanv struct virtio_console_control *); 171275273Sbryanvstatic int vtcon_ctrl_event_populate(struct vtcon_softc *); 172275273Sbryanvstatic void vtcon_ctrl_event_drain(struct vtcon_softc *); 173273515Sbryanvstatic int vtcon_ctrl_init(struct vtcon_softc *); 174273515Sbryanvstatic void vtcon_ctrl_deinit(struct vtcon_softc *); 175273515Sbryanvstatic void vtcon_ctrl_port_add_event(struct vtcon_softc *, int); 176273515Sbryanvstatic void vtcon_ctrl_port_remove_event(struct vtcon_softc *, int); 177273515Sbryanvstatic void vtcon_ctrl_port_console_event(struct vtcon_softc *, int); 178273515Sbryanvstatic void vtcon_ctrl_port_open_event(struct vtcon_softc *, int); 179275273Sbryanvstatic void vtcon_ctrl_process_event(struct vtcon_softc *, 180273515Sbryanv struct virtio_console_control *); 181273515Sbryanvstatic void vtcon_ctrl_task_cb(void *, int); 182275273Sbryanvstatic void vtcon_ctrl_event_intr(void *); 183275273Sbryanvstatic void vtcon_ctrl_poll(struct vtcon_softc *, 184275273Sbryanv struct virtio_console_control *control); 185275273Sbryanvstatic void vtcon_ctrl_send_control(struct vtcon_softc *, uint32_t, 186275273Sbryanv uint16_t, uint16_t); 187273515Sbryanv 188275273Sbryanvstatic int vtcon_port_enqueue_buf(struct vtcon_port *, void *, size_t); 189275273Sbryanvstatic int vtcon_port_create_buf(struct vtcon_port *); 190275273Sbryanvstatic void vtcon_port_requeue_buf(struct vtcon_port *, void *); 191273515Sbryanvstatic int vtcon_port_populate(struct vtcon_port *); 192273515Sbryanvstatic void vtcon_port_destroy(struct vtcon_port *); 193275273Sbryanvstatic int vtcon_port_create(struct vtcon_softc *, int); 194275273Sbryanvstatic void vtcon_port_drain_bufs(struct virtqueue *); 195275273Sbryanvstatic void vtcon_port_drain(struct vtcon_port *); 196275273Sbryanvstatic void vtcon_port_teardown(struct vtcon_port *); 197273515Sbryanvstatic void vtcon_port_change_size(struct vtcon_port *, uint16_t, 198273515Sbryanv uint16_t); 199275273Sbryanvstatic void vtcon_port_update_console_size(struct vtcon_softc *); 200273515Sbryanvstatic void vtcon_port_enable_intr(struct vtcon_port *); 201273515Sbryanvstatic void vtcon_port_disable_intr(struct vtcon_port *); 202275273Sbryanvstatic void vtcon_port_in(struct vtcon_port *); 203275273Sbryanvstatic void vtcon_port_intr(void *); 204275273Sbryanvstatic void vtcon_port_out(struct vtcon_port *, void *, int); 205275273Sbryanvstatic void vtcon_port_submit_event(struct vtcon_port *, uint16_t, 206273515Sbryanv uint16_t); 207273515Sbryanv 208273515Sbryanvstatic int vtcon_tty_open(struct tty *); 209273515Sbryanvstatic void vtcon_tty_close(struct tty *); 210273515Sbryanvstatic void vtcon_tty_outwakeup(struct tty *); 211273515Sbryanvstatic void vtcon_tty_free(void *); 212273515Sbryanv 213273515Sbryanvstatic void vtcon_get_console_size(struct vtcon_softc *, uint16_t *, 214273515Sbryanv uint16_t *); 215273515Sbryanv 216273515Sbryanvstatic void vtcon_enable_interrupts(struct vtcon_softc *); 217273515Sbryanvstatic void vtcon_disable_interrupts(struct vtcon_softc *); 218273515Sbryanv 219273515Sbryanvstatic int vtcon_pending_free; 220273515Sbryanv 221273515Sbryanvstatic struct ttydevsw vtcon_tty_class = { 222273515Sbryanv .tsw_flags = 0, 223273515Sbryanv .tsw_open = vtcon_tty_open, 224273515Sbryanv .tsw_close = vtcon_tty_close, 225273515Sbryanv .tsw_outwakeup = vtcon_tty_outwakeup, 226273515Sbryanv .tsw_free = vtcon_tty_free, 227273515Sbryanv}; 228273515Sbryanv 229273515Sbryanvstatic device_method_t vtcon_methods[] = { 230273515Sbryanv /* Device methods. */ 231273515Sbryanv DEVMETHOD(device_probe, vtcon_probe), 232273515Sbryanv DEVMETHOD(device_attach, vtcon_attach), 233273515Sbryanv DEVMETHOD(device_detach, vtcon_detach), 234273515Sbryanv 235273515Sbryanv /* VirtIO methods. */ 236273515Sbryanv DEVMETHOD(virtio_config_change, vtcon_config_change), 237273515Sbryanv 238273515Sbryanv DEVMETHOD_END 239273515Sbryanv}; 240273515Sbryanv 241273515Sbryanvstatic driver_t vtcon_driver = { 242273515Sbryanv "vtcon", 243273515Sbryanv vtcon_methods, 244273515Sbryanv sizeof(struct vtcon_softc) 245273515Sbryanv}; 246273515Sbryanvstatic devclass_t vtcon_devclass; 247273515Sbryanv 248273515SbryanvDRIVER_MODULE(virtio_console, virtio_pci, vtcon_driver, vtcon_devclass, 249273515Sbryanv vtcon_modevent, 0); 250273515SbryanvMODULE_VERSION(virtio_console, 1); 251273515SbryanvMODULE_DEPEND(virtio_console, virtio, 1, 1, 1); 252273515Sbryanv 253273515Sbryanvstatic int 254273515Sbryanvvtcon_modevent(module_t mod, int type, void *unused) 255273515Sbryanv{ 256273515Sbryanv int error; 257273515Sbryanv 258273515Sbryanv switch (type) { 259273515Sbryanv case MOD_LOAD: 260273515Sbryanv error = 0; 261273515Sbryanv break; 262273515Sbryanv case MOD_QUIESCE: 263275273Sbryanv error = 0; 264275273Sbryanv break; 265273515Sbryanv case MOD_UNLOAD: 266275273Sbryanv vtcon_drain_all(); 267275273Sbryanv error = 0; 268273515Sbryanv break; 269273515Sbryanv case MOD_SHUTDOWN: 270273515Sbryanv error = 0; 271273515Sbryanv break; 272273515Sbryanv default: 273273515Sbryanv error = EOPNOTSUPP; 274273515Sbryanv break; 275273515Sbryanv } 276273515Sbryanv 277273515Sbryanv return (error); 278273515Sbryanv} 279273515Sbryanv 280275273Sbryanvstatic void 281275273Sbryanvvtcon_drain_all(void) 282275273Sbryanv{ 283275273Sbryanv int first; 284275273Sbryanv 285275273Sbryanv for (first = 1; vtcon_pending_free != 0; first = 0) { 286275273Sbryanv if (first != 0) { 287275273Sbryanv printf("virtio_console: Waiting for all detached TTY " 288275273Sbryanv "devices to have open fds closed.\n"); 289275273Sbryanv } 290275273Sbryanv pause("vtcondra", hz); 291275273Sbryanv } 292275273Sbryanv} 293275273Sbryanv 294273515Sbryanvstatic int 295273515Sbryanvvtcon_probe(device_t dev) 296273515Sbryanv{ 297273515Sbryanv 298273515Sbryanv if (virtio_get_device_type(dev) != VIRTIO_ID_CONSOLE) 299273515Sbryanv return (ENXIO); 300273515Sbryanv 301273515Sbryanv device_set_desc(dev, "VirtIO Console Adapter"); 302273515Sbryanv 303273515Sbryanv return (BUS_PROBE_DEFAULT); 304273515Sbryanv} 305273515Sbryanv 306273515Sbryanvstatic int 307273515Sbryanvvtcon_attach(device_t dev) 308273515Sbryanv{ 309273515Sbryanv struct vtcon_softc *sc; 310273515Sbryanv struct virtio_console_config concfg; 311273515Sbryanv int error; 312273515Sbryanv 313273515Sbryanv sc = device_get_softc(dev); 314273515Sbryanv sc->vtcon_dev = dev; 315273515Sbryanv 316275273Sbryanv mtx_init(&sc->vtcon_mtx, "vtconmtx", NULL, MTX_DEF); 317275273Sbryanv mtx_init(&sc->vtcon_ctrl_tx_mtx, "vtconctrlmtx", NULL, MTX_DEF); 318273515Sbryanv 319273515Sbryanv virtio_set_feature_desc(dev, vtcon_feature_desc); 320275273Sbryanv vtcon_setup_features(sc); 321273515Sbryanv 322273515Sbryanv vtcon_read_config(sc, &concfg); 323273515Sbryanv vtcon_determine_max_ports(sc, &concfg); 324273515Sbryanv 325275273Sbryanv error = vtcon_alloc_scports(sc); 326275273Sbryanv if (error) { 327275273Sbryanv device_printf(dev, "cannot allocate softc port structures\n"); 328275273Sbryanv goto fail; 329275273Sbryanv } 330275273Sbryanv 331273515Sbryanv error = vtcon_alloc_virtqueues(sc); 332273515Sbryanv if (error) { 333273515Sbryanv device_printf(dev, "cannot allocate virtqueues\n"); 334273515Sbryanv goto fail; 335273515Sbryanv } 336273515Sbryanv 337275273Sbryanv if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) { 338275273Sbryanv TASK_INIT(&sc->vtcon_ctrl_task, 0, vtcon_ctrl_task_cb, sc); 339273515Sbryanv error = vtcon_ctrl_init(sc); 340275273Sbryanv if (error) 341275273Sbryanv goto fail; 342275273Sbryanv } else { 343275273Sbryanv error = vtcon_port_create(sc, 0); 344275273Sbryanv if (error) 345275273Sbryanv goto fail; 346275273Sbryanv if (sc->vtcon_flags & VTCON_FLAG_SIZE) 347275273Sbryanv vtcon_port_update_console_size(sc); 348275273Sbryanv } 349273515Sbryanv 350273515Sbryanv error = virtio_setup_intr(dev, INTR_TYPE_TTY); 351273515Sbryanv if (error) { 352273515Sbryanv device_printf(dev, "cannot setup virtqueue interrupts\n"); 353273515Sbryanv goto fail; 354273515Sbryanv } 355273515Sbryanv 356273515Sbryanv vtcon_enable_interrupts(sc); 357273515Sbryanv 358275273Sbryanv vtcon_ctrl_send_control(sc, VIRTIO_CONSOLE_BAD_ID, 359273515Sbryanv VIRTIO_CONSOLE_DEVICE_READY, 1); 360273515Sbryanv 361273515Sbryanvfail: 362273515Sbryanv if (error) 363273515Sbryanv vtcon_detach(dev); 364273515Sbryanv 365273515Sbryanv return (error); 366273515Sbryanv} 367273515Sbryanv 368273515Sbryanvstatic int 369273515Sbryanvvtcon_detach(device_t dev) 370273515Sbryanv{ 371273515Sbryanv struct vtcon_softc *sc; 372273515Sbryanv 373273515Sbryanv sc = device_get_softc(dev); 374273515Sbryanv 375273515Sbryanv VTCON_LOCK(sc); 376273515Sbryanv sc->vtcon_flags |= VTCON_FLAG_DETACHED; 377273515Sbryanv if (device_is_attached(dev)) 378273515Sbryanv vtcon_stop(sc); 379273515Sbryanv VTCON_UNLOCK(sc); 380273515Sbryanv 381275273Sbryanv if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) { 382275273Sbryanv taskqueue_drain(taskqueue_thread, &sc->vtcon_ctrl_task); 383273515Sbryanv vtcon_ctrl_deinit(sc); 384275273Sbryanv } 385273515Sbryanv 386275273Sbryanv vtcon_destroy_ports(sc); 387275273Sbryanv mtx_destroy(&sc->vtcon_mtx); 388275273Sbryanv mtx_destroy(&sc->vtcon_ctrl_tx_mtx); 389273515Sbryanv 390273515Sbryanv return (0); 391273515Sbryanv} 392273515Sbryanv 393273515Sbryanvstatic int 394273515Sbryanvvtcon_config_change(device_t dev) 395273515Sbryanv{ 396273515Sbryanv struct vtcon_softc *sc; 397273515Sbryanv 398273515Sbryanv sc = device_get_softc(dev); 399273515Sbryanv 400273515Sbryanv /* 401275273Sbryanv * When the multiport feature is negotiated, all configuration 402275273Sbryanv * changes are done through control virtqueue events. 403273515Sbryanv */ 404275273Sbryanv if ((sc->vtcon_flags & VTCON_FLAG_MULTIPORT) == 0) { 405275273Sbryanv if (sc->vtcon_flags & VTCON_FLAG_SIZE) 406275273Sbryanv vtcon_port_update_console_size(sc); 407273515Sbryanv } 408273515Sbryanv 409273515Sbryanv return (0); 410273515Sbryanv} 411273515Sbryanv 412273515Sbryanvstatic void 413273515Sbryanvvtcon_negotiate_features(struct vtcon_softc *sc) 414273515Sbryanv{ 415273515Sbryanv device_t dev; 416273515Sbryanv uint64_t features; 417273515Sbryanv 418273515Sbryanv dev = sc->vtcon_dev; 419273515Sbryanv features = VTCON_FEATURES; 420273515Sbryanv 421273515Sbryanv sc->vtcon_features = virtio_negotiate_features(dev, features); 422273515Sbryanv} 423273515Sbryanv 424275273Sbryanvstatic void 425275273Sbryanvvtcon_setup_features(struct vtcon_softc *sc) 426275273Sbryanv{ 427275273Sbryanv device_t dev; 428275273Sbryanv 429275273Sbryanv dev = sc->vtcon_dev; 430275273Sbryanv 431275273Sbryanv vtcon_negotiate_features(sc); 432275273Sbryanv 433275273Sbryanv if (virtio_with_feature(dev, VIRTIO_CONSOLE_F_SIZE)) 434275273Sbryanv sc->vtcon_flags |= VTCON_FLAG_SIZE; 435275273Sbryanv if (virtio_with_feature(dev, VIRTIO_CONSOLE_F_MULTIPORT)) 436275273Sbryanv sc->vtcon_flags |= VTCON_FLAG_MULTIPORT; 437275273Sbryanv} 438275273Sbryanv 439273515Sbryanv#define VTCON_GET_CONFIG(_dev, _feature, _field, _cfg) \ 440273515Sbryanv if (virtio_with_feature(_dev, _feature)) { \ 441273515Sbryanv virtio_read_device_config(_dev, \ 442273515Sbryanv offsetof(struct virtio_console_config, _field), \ 443273515Sbryanv &(_cfg)->_field, sizeof((_cfg)->_field)); \ 444273515Sbryanv } 445273515Sbryanv 446273515Sbryanvstatic void 447273515Sbryanvvtcon_read_config(struct vtcon_softc *sc, struct virtio_console_config *concfg) 448273515Sbryanv{ 449273515Sbryanv device_t dev; 450273515Sbryanv 451273515Sbryanv dev = sc->vtcon_dev; 452273515Sbryanv 453273515Sbryanv bzero(concfg, sizeof(struct virtio_console_config)); 454273515Sbryanv 455273515Sbryanv VTCON_GET_CONFIG(dev, VIRTIO_CONSOLE_F_SIZE, cols, concfg); 456273515Sbryanv VTCON_GET_CONFIG(dev, VIRTIO_CONSOLE_F_SIZE, rows, concfg); 457273515Sbryanv VTCON_GET_CONFIG(dev, VIRTIO_CONSOLE_F_MULTIPORT, max_nr_ports, concfg); 458273515Sbryanv} 459273515Sbryanv 460273515Sbryanv#undef VTCON_GET_CONFIG 461273515Sbryanv 462273515Sbryanvstatic int 463275273Sbryanvvtcon_alloc_scports(struct vtcon_softc *sc) 464275273Sbryanv{ 465275273Sbryanv struct vtcon_softc_port *scport; 466275273Sbryanv int max, i; 467275273Sbryanv 468275273Sbryanv max = sc->vtcon_max_ports; 469275273Sbryanv 470275273Sbryanv sc->vtcon_ports = malloc(sizeof(struct vtcon_softc_port) * max, 471275273Sbryanv M_DEVBUF, M_NOWAIT | M_ZERO); 472275273Sbryanv if (sc->vtcon_ports == NULL) 473275273Sbryanv return (ENOMEM); 474275273Sbryanv 475275273Sbryanv for (i = 0; i < max; i++) { 476275273Sbryanv scport = &sc->vtcon_ports[i]; 477275273Sbryanv scport->vcsp_sc = sc; 478275273Sbryanv } 479275273Sbryanv 480275273Sbryanv return (0); 481275273Sbryanv} 482275273Sbryanv 483275273Sbryanvstatic int 484273515Sbryanvvtcon_alloc_virtqueues(struct vtcon_softc *sc) 485273515Sbryanv{ 486273515Sbryanv device_t dev; 487273515Sbryanv struct vq_alloc_info *info; 488275273Sbryanv struct vtcon_softc_port *scport; 489273515Sbryanv int i, idx, portidx, nvqs, error; 490273515Sbryanv 491273515Sbryanv dev = sc->vtcon_dev; 492273515Sbryanv 493273515Sbryanv nvqs = sc->vtcon_max_ports * 2; 494273515Sbryanv if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) 495273515Sbryanv nvqs += 2; 496273515Sbryanv 497273515Sbryanv info = malloc(sizeof(struct vq_alloc_info) * nvqs, M_TEMP, M_NOWAIT); 498273515Sbryanv if (info == NULL) 499273515Sbryanv return (ENOMEM); 500273515Sbryanv 501275273Sbryanv for (i = 0, idx = 0, portidx = 0; i < nvqs / 2; i++, idx += 2) { 502273515Sbryanv 503273515Sbryanv if (i == 1) { 504273515Sbryanv /* The control virtqueues are after the first port. */ 505273515Sbryanv VQ_ALLOC_INFO_INIT(&info[idx], 0, 506275273Sbryanv vtcon_ctrl_event_intr, sc, &sc->vtcon_ctrl_rxvq, 507273515Sbryanv "%s-control rx", device_get_nameunit(dev)); 508273515Sbryanv VQ_ALLOC_INFO_INIT(&info[idx+1], 0, 509273515Sbryanv NULL, sc, &sc->vtcon_ctrl_txvq, 510273515Sbryanv "%s-control tx", device_get_nameunit(dev)); 511273515Sbryanv continue; 512273515Sbryanv } 513273515Sbryanv 514275273Sbryanv scport = &sc->vtcon_ports[portidx]; 515273515Sbryanv 516275273Sbryanv VQ_ALLOC_INFO_INIT(&info[idx], 0, vtcon_port_intr, 517275273Sbryanv scport, &scport->vcsp_invq, "%s-port%d in", 518275273Sbryanv device_get_nameunit(dev), i); 519273515Sbryanv VQ_ALLOC_INFO_INIT(&info[idx+1], 0, NULL, 520275273Sbryanv NULL, &scport->vcsp_outvq, "%s-port%d out", 521275273Sbryanv device_get_nameunit(dev), i); 522273515Sbryanv 523273515Sbryanv portidx++; 524273515Sbryanv } 525273515Sbryanv 526273515Sbryanv error = virtio_alloc_virtqueues(dev, 0, nvqs, info); 527273515Sbryanv free(info, M_TEMP); 528273515Sbryanv 529273515Sbryanv return (error); 530273515Sbryanv} 531273515Sbryanv 532273515Sbryanvstatic void 533273515Sbryanvvtcon_determine_max_ports(struct vtcon_softc *sc, 534273515Sbryanv struct virtio_console_config *concfg) 535273515Sbryanv{ 536273515Sbryanv 537273515Sbryanv if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) { 538273515Sbryanv sc->vtcon_max_ports = 539273515Sbryanv min(concfg->max_nr_ports, VTCON_MAX_PORTS); 540273515Sbryanv if (sc->vtcon_max_ports == 0) 541273515Sbryanv sc->vtcon_max_ports = 1; 542273515Sbryanv } else 543273515Sbryanv sc->vtcon_max_ports = 1; 544273515Sbryanv} 545273515Sbryanv 546273515Sbryanvstatic void 547275273Sbryanvvtcon_destroy_ports(struct vtcon_softc *sc) 548273515Sbryanv{ 549275273Sbryanv struct vtcon_softc_port *scport; 550275273Sbryanv struct vtcon_port *port; 551275273Sbryanv struct virtqueue *vq; 552275273Sbryanv int i; 553273515Sbryanv 554275273Sbryanv if (sc->vtcon_ports == NULL) 555275273Sbryanv return; 556275273Sbryanv 557275273Sbryanv VTCON_LOCK(sc); 558275273Sbryanv for (i = 0; i < sc->vtcon_max_ports; i++) { 559275273Sbryanv scport = &sc->vtcon_ports[i]; 560275273Sbryanv 561275273Sbryanv port = scport->vcsp_port; 562275273Sbryanv if (port != NULL) { 563275273Sbryanv scport->vcsp_port = NULL; 564275273Sbryanv VTCON_PORT_LOCK(port); 565275273Sbryanv VTCON_UNLOCK(sc); 566275273Sbryanv vtcon_port_teardown(port); 567275273Sbryanv VTCON_LOCK(sc); 568275273Sbryanv } 569275273Sbryanv 570275273Sbryanv vq = scport->vcsp_invq; 571275273Sbryanv if (vq != NULL) 572275273Sbryanv vtcon_port_drain_bufs(vq); 573273515Sbryanv } 574275273Sbryanv VTCON_UNLOCK(sc); 575273515Sbryanv 576275273Sbryanv free(sc->vtcon_ports, M_DEVBUF); 577275273Sbryanv sc->vtcon_ports = NULL; 578273515Sbryanv} 579273515Sbryanv 580273515Sbryanvstatic void 581273515Sbryanvvtcon_stop(struct vtcon_softc *sc) 582273515Sbryanv{ 583273515Sbryanv 584273515Sbryanv vtcon_disable_interrupts(sc); 585273515Sbryanv virtio_stop(sc->vtcon_dev); 586273515Sbryanv} 587273515Sbryanv 588273515Sbryanvstatic int 589275273Sbryanvvtcon_ctrl_event_enqueue(struct vtcon_softc *sc, 590273515Sbryanv struct virtio_console_control *control) 591273515Sbryanv{ 592275273Sbryanv struct sglist_seg segs[2]; 593273515Sbryanv struct sglist sg; 594273515Sbryanv struct virtqueue *vq; 595275273Sbryanv int error; 596273515Sbryanv 597273515Sbryanv vq = sc->vtcon_ctrl_rxvq; 598273515Sbryanv 599275273Sbryanv sglist_init(&sg, 2, segs); 600275273Sbryanv error = sglist_append(&sg, control, 601275273Sbryanv sizeof(struct virtio_console_control)); 602275273Sbryanv KASSERT(error == 0, ("%s: error %d adding control to sglist", 603275273Sbryanv __func__, error)); 604273515Sbryanv 605275273Sbryanv return (virtqueue_enqueue(vq, control, &sg, 0, sg.sg_nseg)); 606273515Sbryanv} 607273515Sbryanv 608273515Sbryanvstatic int 609275273Sbryanvvtcon_ctrl_event_create(struct vtcon_softc *sc) 610273515Sbryanv{ 611273515Sbryanv struct virtio_console_control *control; 612273515Sbryanv int error; 613273515Sbryanv 614275273Sbryanv control = malloc(sizeof(struct virtio_console_control), M_DEVBUF, 615275273Sbryanv M_ZERO | M_NOWAIT); 616273515Sbryanv if (control == NULL) 617273515Sbryanv return (ENOMEM); 618273515Sbryanv 619275273Sbryanv error = vtcon_ctrl_event_enqueue(sc, control); 620273515Sbryanv if (error) 621273515Sbryanv free(control, M_DEVBUF); 622273515Sbryanv 623273515Sbryanv return (error); 624273515Sbryanv} 625273515Sbryanv 626273515Sbryanvstatic void 627275273Sbryanvvtcon_ctrl_event_requeue(struct vtcon_softc *sc, 628273515Sbryanv struct virtio_console_control *control) 629273515Sbryanv{ 630273515Sbryanv int error; 631273515Sbryanv 632275273Sbryanv bzero(control, sizeof(struct virtio_console_control)); 633273515Sbryanv 634275273Sbryanv error = vtcon_ctrl_event_enqueue(sc, control); 635273515Sbryanv KASSERT(error == 0, 636273515Sbryanv ("%s: cannot requeue control buffer %d", __func__, error)); 637273515Sbryanv} 638273515Sbryanv 639273515Sbryanvstatic int 640275273Sbryanvvtcon_ctrl_event_populate(struct vtcon_softc *sc) 641273515Sbryanv{ 642273515Sbryanv struct virtqueue *vq; 643273515Sbryanv int nbufs, error; 644273515Sbryanv 645273515Sbryanv vq = sc->vtcon_ctrl_rxvq; 646273515Sbryanv error = ENOSPC; 647273515Sbryanv 648273515Sbryanv for (nbufs = 0; !virtqueue_full(vq); nbufs++) { 649275273Sbryanv error = vtcon_ctrl_event_create(sc); 650273515Sbryanv if (error) 651273515Sbryanv break; 652273515Sbryanv } 653273515Sbryanv 654273515Sbryanv if (nbufs > 0) { 655273515Sbryanv virtqueue_notify(vq); 656275273Sbryanv error = 0; 657273515Sbryanv } 658273515Sbryanv 659273515Sbryanv return (error); 660273515Sbryanv} 661273515Sbryanv 662273515Sbryanvstatic void 663275273Sbryanvvtcon_ctrl_event_drain(struct vtcon_softc *sc) 664273515Sbryanv{ 665275273Sbryanv struct virtio_console_control *control; 666273515Sbryanv struct virtqueue *vq; 667275273Sbryanv int last; 668273515Sbryanv 669275273Sbryanv vq = sc->vtcon_ctrl_rxvq; 670275273Sbryanv last = 0; 671273515Sbryanv 672275273Sbryanv if (vq == NULL) 673273515Sbryanv return; 674273515Sbryanv 675275273Sbryanv VTCON_LOCK(sc); 676275273Sbryanv while ((control = virtqueue_drain(vq, &last)) != NULL) 677275273Sbryanv free(control, M_DEVBUF); 678275273Sbryanv VTCON_UNLOCK(sc); 679273515Sbryanv} 680273515Sbryanv 681273515Sbryanvstatic int 682273515Sbryanvvtcon_ctrl_init(struct vtcon_softc *sc) 683273515Sbryanv{ 684273515Sbryanv int error; 685273515Sbryanv 686275273Sbryanv error = vtcon_ctrl_event_populate(sc); 687273515Sbryanv 688273515Sbryanv return (error); 689273515Sbryanv} 690273515Sbryanv 691273515Sbryanvstatic void 692273515Sbryanvvtcon_ctrl_deinit(struct vtcon_softc *sc) 693273515Sbryanv{ 694273515Sbryanv 695275273Sbryanv vtcon_ctrl_event_drain(sc); 696273515Sbryanv} 697273515Sbryanv 698273515Sbryanvstatic void 699273515Sbryanvvtcon_ctrl_port_add_event(struct vtcon_softc *sc, int id) 700273515Sbryanv{ 701273515Sbryanv device_t dev; 702273515Sbryanv int error; 703273515Sbryanv 704273515Sbryanv dev = sc->vtcon_dev; 705273515Sbryanv 706275273Sbryanv /* This single thread only way for ports to be created. */ 707275273Sbryanv if (sc->vtcon_ports[id].vcsp_port != NULL) { 708273515Sbryanv device_printf(dev, "%s: adding port %d, but already exists\n", 709273515Sbryanv __func__, id); 710273515Sbryanv return; 711273515Sbryanv } 712273515Sbryanv 713275273Sbryanv error = vtcon_port_create(sc, id); 714273515Sbryanv if (error) { 715273515Sbryanv device_printf(dev, "%s: cannot create port %d: %d\n", 716273515Sbryanv __func__, id, error); 717275273Sbryanv vtcon_ctrl_send_control(sc, id, VIRTIO_CONSOLE_PORT_READY, 0); 718273515Sbryanv return; 719273515Sbryanv } 720273515Sbryanv} 721273515Sbryanv 722273515Sbryanvstatic void 723273515Sbryanvvtcon_ctrl_port_remove_event(struct vtcon_softc *sc, int id) 724273515Sbryanv{ 725273515Sbryanv device_t dev; 726275273Sbryanv struct vtcon_softc_port *scport; 727273515Sbryanv struct vtcon_port *port; 728273515Sbryanv 729273515Sbryanv dev = sc->vtcon_dev; 730275273Sbryanv scport = &sc->vtcon_ports[id]; 731273515Sbryanv 732275273Sbryanv VTCON_LOCK(sc); 733275273Sbryanv port = scport->vcsp_port; 734273515Sbryanv if (port == NULL) { 735275273Sbryanv VTCON_UNLOCK(sc); 736273515Sbryanv device_printf(dev, "%s: remove port %d, but does not exist\n", 737273515Sbryanv __func__, id); 738273515Sbryanv return; 739273515Sbryanv } 740273515Sbryanv 741275273Sbryanv scport->vcsp_port = NULL; 742275273Sbryanv VTCON_PORT_LOCK(port); 743275273Sbryanv VTCON_UNLOCK(sc); 744275273Sbryanv vtcon_port_teardown(port); 745273515Sbryanv} 746273515Sbryanv 747273515Sbryanvstatic void 748273515Sbryanvvtcon_ctrl_port_console_event(struct vtcon_softc *sc, int id) 749273515Sbryanv{ 750273515Sbryanv device_t dev; 751275273Sbryanv struct vtcon_softc_port *scport; 752275273Sbryanv struct vtcon_port *port; 753273515Sbryanv 754273515Sbryanv dev = sc->vtcon_dev; 755275273Sbryanv scport = &sc->vtcon_ports[id]; 756273515Sbryanv 757275273Sbryanv VTCON_LOCK(sc); 758275273Sbryanv port = scport->vcsp_port; 759275273Sbryanv if (port == NULL) { 760275273Sbryanv VTCON_UNLOCK(sc); 761275273Sbryanv device_printf(dev, "%s: console port %d, but does not exist\n", 762275273Sbryanv __func__, id); 763275273Sbryanv return; 764275273Sbryanv } 765275273Sbryanv 766275273Sbryanv VTCON_PORT_LOCK(port); 767275273Sbryanv VTCON_UNLOCK(sc); 768275273Sbryanv port->vtcport_flags |= VTCON_PORT_FLAG_CONSOLE; 769275273Sbryanv vtcon_port_submit_event(port, VIRTIO_CONSOLE_PORT_OPEN, 1); 770275273Sbryanv VTCON_PORT_UNLOCK(port); 771273515Sbryanv} 772273515Sbryanv 773273515Sbryanvstatic void 774273515Sbryanvvtcon_ctrl_port_open_event(struct vtcon_softc *sc, int id) 775273515Sbryanv{ 776273515Sbryanv device_t dev; 777275273Sbryanv struct vtcon_softc_port *scport; 778273515Sbryanv struct vtcon_port *port; 779273515Sbryanv 780273515Sbryanv dev = sc->vtcon_dev; 781275273Sbryanv scport = &sc->vtcon_ports[id]; 782273515Sbryanv 783275273Sbryanv VTCON_LOCK(sc); 784275273Sbryanv port = scport->vcsp_port; 785273515Sbryanv if (port == NULL) { 786275273Sbryanv VTCON_UNLOCK(sc); 787273515Sbryanv device_printf(dev, "%s: open port %d, but does not exist\n", 788273515Sbryanv __func__, id); 789273515Sbryanv return; 790273515Sbryanv } 791273515Sbryanv 792275273Sbryanv VTCON_PORT_LOCK(port); 793275273Sbryanv VTCON_UNLOCK(sc); 794273515Sbryanv vtcon_port_enable_intr(port); 795275273Sbryanv VTCON_PORT_UNLOCK(port); 796273515Sbryanv} 797273515Sbryanv 798273515Sbryanvstatic void 799275273Sbryanvvtcon_ctrl_process_event(struct vtcon_softc *sc, 800273515Sbryanv struct virtio_console_control *control) 801273515Sbryanv{ 802273515Sbryanv device_t dev; 803273515Sbryanv int id; 804273515Sbryanv 805273515Sbryanv dev = sc->vtcon_dev; 806273515Sbryanv id = control->id; 807273515Sbryanv 808273515Sbryanv if (id < 0 || id >= sc->vtcon_max_ports) { 809273515Sbryanv device_printf(dev, "%s: invalid port ID %d\n", __func__, id); 810273515Sbryanv return; 811273515Sbryanv } 812273515Sbryanv 813273515Sbryanv switch (control->event) { 814273515Sbryanv case VIRTIO_CONSOLE_PORT_ADD: 815273515Sbryanv vtcon_ctrl_port_add_event(sc, id); 816273515Sbryanv break; 817273515Sbryanv 818273515Sbryanv case VIRTIO_CONSOLE_PORT_REMOVE: 819273515Sbryanv vtcon_ctrl_port_remove_event(sc, id); 820273515Sbryanv break; 821273515Sbryanv 822273515Sbryanv case VIRTIO_CONSOLE_CONSOLE_PORT: 823273515Sbryanv vtcon_ctrl_port_console_event(sc, id); 824273515Sbryanv break; 825273515Sbryanv 826273515Sbryanv case VIRTIO_CONSOLE_RESIZE: 827273515Sbryanv break; 828273515Sbryanv 829273515Sbryanv case VIRTIO_CONSOLE_PORT_OPEN: 830273515Sbryanv vtcon_ctrl_port_open_event(sc, id); 831273515Sbryanv break; 832273515Sbryanv 833273515Sbryanv case VIRTIO_CONSOLE_PORT_NAME: 834273515Sbryanv break; 835273515Sbryanv } 836273515Sbryanv} 837273515Sbryanv 838273515Sbryanvstatic void 839273515Sbryanvvtcon_ctrl_task_cb(void *xsc, int pending) 840273515Sbryanv{ 841273515Sbryanv struct vtcon_softc *sc; 842273515Sbryanv struct virtqueue *vq; 843273515Sbryanv struct virtio_console_control *control; 844275273Sbryanv int detached; 845273515Sbryanv 846273515Sbryanv sc = xsc; 847273515Sbryanv vq = sc->vtcon_ctrl_rxvq; 848273515Sbryanv 849273515Sbryanv VTCON_LOCK(sc); 850275273Sbryanv 851275273Sbryanv while ((detached = (sc->vtcon_flags & VTCON_FLAG_DETACHED)) == 0) { 852273515Sbryanv control = virtqueue_dequeue(vq, NULL); 853273515Sbryanv if (control == NULL) 854273515Sbryanv break; 855273515Sbryanv 856273515Sbryanv VTCON_UNLOCK(sc); 857275273Sbryanv vtcon_ctrl_process_event(sc, control); 858273515Sbryanv VTCON_LOCK(sc); 859275273Sbryanv vtcon_ctrl_event_requeue(sc, control); 860273515Sbryanv } 861275273Sbryanv 862275273Sbryanv if (!detached) { 863275273Sbryanv virtqueue_notify(vq); 864275273Sbryanv if (virtqueue_enable_intr(vq) != 0) 865275273Sbryanv taskqueue_enqueue(taskqueue_thread, 866275273Sbryanv &sc->vtcon_ctrl_task); 867275273Sbryanv } 868275273Sbryanv 869273515Sbryanv VTCON_UNLOCK(sc); 870275273Sbryanv} 871273515Sbryanv 872275273Sbryanvstatic void 873275273Sbryanvvtcon_ctrl_event_intr(void *xsc) 874275273Sbryanv{ 875275273Sbryanv struct vtcon_softc *sc; 876275273Sbryanv 877275273Sbryanv sc = xsc; 878275273Sbryanv 879275273Sbryanv /* 880275273Sbryanv * Only some events require us to potentially block, but it 881275273Sbryanv * easier to just defer all event handling to the taskqueue. 882275273Sbryanv */ 883275273Sbryanv taskqueue_enqueue(taskqueue_thread, &sc->vtcon_ctrl_task); 884273515Sbryanv} 885273515Sbryanv 886275273Sbryanvstatic void 887275273Sbryanvvtcon_ctrl_poll(struct vtcon_softc *sc, 888275273Sbryanv struct virtio_console_control *control) 889275273Sbryanv{ 890275273Sbryanv struct sglist_seg segs[2]; 891275273Sbryanv struct sglist sg; 892275273Sbryanv struct virtqueue *vq; 893275273Sbryanv int error; 894275273Sbryanv 895275273Sbryanv vq = sc->vtcon_ctrl_txvq; 896275273Sbryanv 897275273Sbryanv sglist_init(&sg, 2, segs); 898275273Sbryanv error = sglist_append(&sg, control, 899275273Sbryanv sizeof(struct virtio_console_control)); 900275273Sbryanv KASSERT(error == 0, ("%s: error %d adding control to sglist", 901275273Sbryanv __func__, error)); 902275273Sbryanv 903275273Sbryanv /* 904275273Sbryanv * We cannot use the softc lock to serialize access to this 905275273Sbryanv * virtqueue since this is called from the tty layer with the 906275273Sbryanv * port lock held. Acquiring the softc would violate our lock 907275273Sbryanv * ordering. 908275273Sbryanv */ 909275273Sbryanv VTCON_CTRL_TX_LOCK(sc); 910275273Sbryanv KASSERT(virtqueue_empty(vq), 911275273Sbryanv ("%s: virtqueue is not emtpy", __func__)); 912275273Sbryanv error = virtqueue_enqueue(vq, control, &sg, sg.sg_nseg, 0); 913275273Sbryanv if (error == 0) { 914275273Sbryanv virtqueue_notify(vq); 915275273Sbryanv virtqueue_poll(vq, NULL); 916275273Sbryanv } 917275273Sbryanv VTCON_CTRL_TX_UNLOCK(sc); 918275273Sbryanv} 919275273Sbryanv 920275273Sbryanvstatic void 921275273Sbryanvvtcon_ctrl_send_control(struct vtcon_softc *sc, uint32_t portid, 922275273Sbryanv uint16_t event, uint16_t value) 923275273Sbryanv{ 924275273Sbryanv struct virtio_console_control control; 925275273Sbryanv 926275273Sbryanv if ((sc->vtcon_flags & VTCON_FLAG_MULTIPORT) == 0) 927275273Sbryanv return; 928275273Sbryanv 929275273Sbryanv control.id = portid; 930275273Sbryanv control.event = event; 931275273Sbryanv control.value = value; 932275273Sbryanv 933275273Sbryanv vtcon_ctrl_poll(sc, &control); 934275273Sbryanv} 935275273Sbryanv 936273515Sbryanvstatic int 937275273Sbryanvvtcon_port_enqueue_buf(struct vtcon_port *port, void *buf, size_t len) 938273515Sbryanv{ 939275273Sbryanv struct sglist_seg segs[2]; 940273515Sbryanv struct sglist sg; 941273515Sbryanv struct virtqueue *vq; 942273515Sbryanv int error; 943273515Sbryanv 944273515Sbryanv vq = port->vtcport_invq; 945273515Sbryanv 946275273Sbryanv sglist_init(&sg, 2, segs); 947273515Sbryanv error = sglist_append(&sg, buf, len); 948275273Sbryanv KASSERT(error == 0, 949273515Sbryanv ("%s: error %d adding buffer to sglist", __func__, error)); 950273515Sbryanv 951275273Sbryanv error = virtqueue_enqueue(vq, buf, &sg, 0, sg.sg_nseg); 952275273Sbryanv 953275273Sbryanv return (error); 954273515Sbryanv} 955273515Sbryanv 956273515Sbryanvstatic int 957275273Sbryanvvtcon_port_create_buf(struct vtcon_port *port) 958273515Sbryanv{ 959273515Sbryanv void *buf; 960273515Sbryanv int error; 961273515Sbryanv 962273515Sbryanv buf = malloc(VTCON_BULK_BUFSZ, M_DEVBUF, M_ZERO | M_NOWAIT); 963273515Sbryanv if (buf == NULL) 964273515Sbryanv return (ENOMEM); 965273515Sbryanv 966275273Sbryanv error = vtcon_port_enqueue_buf(port, buf, VTCON_BULK_BUFSZ); 967273515Sbryanv if (error) 968273515Sbryanv free(buf, M_DEVBUF); 969273515Sbryanv 970273515Sbryanv return (error); 971273515Sbryanv} 972273515Sbryanv 973273515Sbryanvstatic void 974275273Sbryanvvtcon_port_requeue_buf(struct vtcon_port *port, void *buf) 975273515Sbryanv{ 976275273Sbryanv int error; 977273515Sbryanv 978275273Sbryanv error = vtcon_port_enqueue_buf(port, buf, VTCON_BULK_BUFSZ); 979273515Sbryanv KASSERT(error == 0, 980273515Sbryanv ("%s: cannot requeue input buffer %d", __func__, error)); 981273515Sbryanv} 982273515Sbryanv 983273515Sbryanvstatic int 984273515Sbryanvvtcon_port_populate(struct vtcon_port *port) 985273515Sbryanv{ 986273515Sbryanv struct virtqueue *vq; 987273515Sbryanv int nbufs, error; 988273515Sbryanv 989273515Sbryanv vq = port->vtcport_invq; 990273515Sbryanv error = ENOSPC; 991273515Sbryanv 992273515Sbryanv for (nbufs = 0; !virtqueue_full(vq); nbufs++) { 993275273Sbryanv error = vtcon_port_create_buf(port); 994273515Sbryanv if (error) 995273515Sbryanv break; 996273515Sbryanv } 997273515Sbryanv 998273515Sbryanv if (nbufs > 0) { 999273515Sbryanv virtqueue_notify(vq); 1000275273Sbryanv error = 0; 1001273515Sbryanv } 1002273515Sbryanv 1003273515Sbryanv return (error); 1004273515Sbryanv} 1005273515Sbryanv 1006273515Sbryanvstatic void 1007273515Sbryanvvtcon_port_destroy(struct vtcon_port *port) 1008273515Sbryanv{ 1009273515Sbryanv 1010273515Sbryanv port->vtcport_sc = NULL; 1011275273Sbryanv port->vtcport_scport = NULL; 1012275273Sbryanv port->vtcport_invq = NULL; 1013275273Sbryanv port->vtcport_outvq = NULL; 1014273515Sbryanv port->vtcport_id = -1; 1015275273Sbryanv mtx_destroy(&port->vtcport_mtx); 1016273515Sbryanv free(port, M_DEVBUF); 1017273515Sbryanv} 1018273515Sbryanv 1019273515Sbryanvstatic int 1020275273Sbryanvvtcon_port_init_vqs(struct vtcon_port *port) 1021273515Sbryanv{ 1022275273Sbryanv struct vtcon_softc_port *scport; 1023275273Sbryanv int error; 1024275273Sbryanv 1025275273Sbryanv scport = port->vtcport_scport; 1026275273Sbryanv 1027275273Sbryanv port->vtcport_invq = scport->vcsp_invq; 1028275273Sbryanv port->vtcport_outvq = scport->vcsp_outvq; 1029275273Sbryanv 1030275273Sbryanv /* 1031275273Sbryanv * Free any data left over from when this virtqueue was in use by a 1032275273Sbryanv * prior port. We have not yet notified the host that the port is 1033275273Sbryanv * ready, so assume nothing in the virtqueue can be for us. 1034275273Sbryanv */ 1035275273Sbryanv vtcon_port_drain(port); 1036275273Sbryanv 1037275273Sbryanv KASSERT(virtqueue_empty(port->vtcport_invq), 1038275273Sbryanv ("%s: in virtqueue is not empty", __func__)); 1039275273Sbryanv KASSERT(virtqueue_empty(port->vtcport_outvq), 1040275273Sbryanv ("%s: out virtqueue is not empty", __func__)); 1041275273Sbryanv 1042275273Sbryanv error = vtcon_port_populate(port); 1043275273Sbryanv if (error) 1044275273Sbryanv return (error); 1045275273Sbryanv 1046275273Sbryanv return (0); 1047275273Sbryanv} 1048275273Sbryanv 1049275273Sbryanvstatic int 1050275273Sbryanvvtcon_port_create(struct vtcon_softc *sc, int id) 1051275273Sbryanv{ 1052273515Sbryanv device_t dev; 1053275273Sbryanv struct vtcon_softc_port *scport; 1054273515Sbryanv struct vtcon_port *port; 1055273515Sbryanv int error; 1056273515Sbryanv 1057273515Sbryanv dev = sc->vtcon_dev; 1058275273Sbryanv scport = &sc->vtcon_ports[id]; 1059273515Sbryanv 1060275273Sbryanv VTCON_ASSERT_VALID_PORTID(sc, id); 1061275273Sbryanv MPASS(scport->vcsp_port == NULL); 1062273515Sbryanv 1063273515Sbryanv port = malloc(sizeof(struct vtcon_port), M_DEVBUF, M_NOWAIT | M_ZERO); 1064273515Sbryanv if (port == NULL) 1065273515Sbryanv return (ENOMEM); 1066273515Sbryanv 1067273515Sbryanv port->vtcport_sc = sc; 1068275273Sbryanv port->vtcport_scport = scport; 1069273515Sbryanv port->vtcport_id = id; 1070275273Sbryanv mtx_init(&port->vtcport_mtx, "vtcpmtx", NULL, MTX_DEF); 1071273515Sbryanv port->vtcport_tty = tty_alloc_mutex(&vtcon_tty_class, port, 1072273515Sbryanv &port->vtcport_mtx); 1073273515Sbryanv 1074275273Sbryanv error = vtcon_port_init_vqs(port); 1075273515Sbryanv if (error) { 1076275273Sbryanv VTCON_PORT_LOCK(port); 1077275273Sbryanv vtcon_port_teardown(port); 1078273515Sbryanv return (error); 1079273515Sbryanv } 1080273515Sbryanv 1081273515Sbryanv VTCON_LOCK(sc); 1082275273Sbryanv VTCON_PORT_LOCK(port); 1083275273Sbryanv scport->vcsp_port = port; 1084275273Sbryanv vtcon_port_enable_intr(port); 1085275273Sbryanv vtcon_port_submit_event(port, VIRTIO_CONSOLE_PORT_READY, 1); 1086275273Sbryanv VTCON_PORT_UNLOCK(port); 1087273515Sbryanv VTCON_UNLOCK(sc); 1088273515Sbryanv 1089275273Sbryanv tty_makedev(port->vtcport_tty, NULL, "%s%r.%r", VTCON_TTY_PREFIX, 1090275273Sbryanv device_get_unit(dev), id); 1091273515Sbryanv 1092273515Sbryanv return (0); 1093273515Sbryanv} 1094273515Sbryanv 1095273515Sbryanvstatic void 1096275273Sbryanvvtcon_port_drain_bufs(struct virtqueue *vq) 1097273515Sbryanv{ 1098273515Sbryanv void *buf; 1099273515Sbryanv int last; 1100273515Sbryanv 1101273515Sbryanv last = 0; 1102273515Sbryanv 1103273515Sbryanv while ((buf = virtqueue_drain(vq, &last)) != NULL) 1104273515Sbryanv free(buf, M_DEVBUF); 1105273515Sbryanv} 1106273515Sbryanv 1107273515Sbryanvstatic void 1108275273Sbryanvvtcon_port_drain(struct vtcon_port *port) 1109273515Sbryanv{ 1110275273Sbryanv 1111275273Sbryanv vtcon_port_drain_bufs(port->vtcport_invq); 1112275273Sbryanv} 1113275273Sbryanv 1114275273Sbryanvstatic void 1115275273Sbryanvvtcon_port_teardown(struct vtcon_port *port) 1116275273Sbryanv{ 1117273515Sbryanv struct tty *tp; 1118273515Sbryanv 1119273515Sbryanv tp = port->vtcport_tty; 1120273515Sbryanv 1121275273Sbryanv port->vtcport_flags |= VTCON_PORT_FLAG_GONE; 1122273515Sbryanv 1123273515Sbryanv if (tp != NULL) { 1124273515Sbryanv atomic_add_int(&vtcon_pending_free, 1); 1125273515Sbryanv tty_rel_gone(tp); 1126273515Sbryanv } else 1127273515Sbryanv vtcon_port_destroy(port); 1128273515Sbryanv} 1129273515Sbryanv 1130273515Sbryanvstatic void 1131273515Sbryanvvtcon_port_change_size(struct vtcon_port *port, uint16_t cols, uint16_t rows) 1132273515Sbryanv{ 1133273515Sbryanv struct tty *tp; 1134273515Sbryanv struct winsize sz; 1135273515Sbryanv 1136273515Sbryanv tp = port->vtcport_tty; 1137273515Sbryanv 1138273515Sbryanv if (tp == NULL) 1139273515Sbryanv return; 1140273515Sbryanv 1141273515Sbryanv bzero(&sz, sizeof(struct winsize)); 1142273515Sbryanv sz.ws_col = cols; 1143273515Sbryanv sz.ws_row = rows; 1144273515Sbryanv 1145273515Sbryanv tty_set_winsize(tp, &sz); 1146273515Sbryanv} 1147273515Sbryanv 1148273515Sbryanvstatic void 1149275273Sbryanvvtcon_port_update_console_size(struct vtcon_softc *sc) 1150275273Sbryanv{ 1151275273Sbryanv struct vtcon_port *port; 1152275273Sbryanv struct vtcon_softc_port *scport; 1153275273Sbryanv uint16_t cols, rows; 1154275273Sbryanv 1155275273Sbryanv vtcon_get_console_size(sc, &cols, &rows); 1156275273Sbryanv 1157275273Sbryanv /* 1158275273Sbryanv * For now, assume the first (only) port is the console. Note 1159275273Sbryanv * QEMU does not implement this feature yet. 1160275273Sbryanv */ 1161275273Sbryanv scport = &sc->vtcon_ports[0]; 1162275273Sbryanv 1163275273Sbryanv VTCON_LOCK(sc); 1164275273Sbryanv port = scport->vcsp_port; 1165275273Sbryanv 1166275273Sbryanv if (port != NULL) { 1167275273Sbryanv VTCON_PORT_LOCK(port); 1168275273Sbryanv VTCON_UNLOCK(sc); 1169275273Sbryanv vtcon_port_change_size(port, cols, rows); 1170275273Sbryanv VTCON_PORT_UNLOCK(port); 1171275273Sbryanv } else 1172275273Sbryanv VTCON_UNLOCK(sc); 1173275273Sbryanv} 1174275273Sbryanv 1175275273Sbryanvstatic void 1176273515Sbryanvvtcon_port_enable_intr(struct vtcon_port *port) 1177273515Sbryanv{ 1178273515Sbryanv 1179273515Sbryanv /* 1180275273Sbryanv * NOTE: The out virtqueue is always polled, so its interupt 1181275273Sbryanv * kept disabled. 1182273515Sbryanv */ 1183273515Sbryanv virtqueue_enable_intr(port->vtcport_invq); 1184273515Sbryanv} 1185273515Sbryanv 1186273515Sbryanvstatic void 1187273515Sbryanvvtcon_port_disable_intr(struct vtcon_port *port) 1188273515Sbryanv{ 1189273515Sbryanv 1190273515Sbryanv if (port->vtcport_invq != NULL) 1191273515Sbryanv virtqueue_disable_intr(port->vtcport_invq); 1192273515Sbryanv if (port->vtcport_outvq != NULL) 1193273515Sbryanv virtqueue_disable_intr(port->vtcport_outvq); 1194273515Sbryanv} 1195273515Sbryanv 1196273515Sbryanvstatic void 1197275273Sbryanvvtcon_port_in(struct vtcon_port *port) 1198273515Sbryanv{ 1199275273Sbryanv struct virtqueue *vq; 1200273515Sbryanv struct tty *tp; 1201273515Sbryanv char *buf; 1202273515Sbryanv uint32_t len; 1203273515Sbryanv int i, deq; 1204273515Sbryanv 1205273515Sbryanv tp = port->vtcport_tty; 1206273515Sbryanv vq = port->vtcport_invq; 1207273515Sbryanv 1208273515Sbryanvagain: 1209273515Sbryanv deq = 0; 1210273515Sbryanv 1211273515Sbryanv while ((buf = virtqueue_dequeue(vq, &len)) != NULL) { 1212275273Sbryanv for (i = 0; i < len; i++) { 1213275273Sbryanv#if defined(KDB) 1214275273Sbryanv if (port->vtcport_flags & VTCON_PORT_FLAG_CONSOLE) 1215275273Sbryanv kdb_alt_break(buf[i], 1216275273Sbryanv &port->vtcport_alt_break_state); 1217275273Sbryanv#endif 1218275273Sbryanv ttydisc_rint(tp, buf[i], 0); 1219275273Sbryanv } 1220275273Sbryanv vtcon_port_requeue_buf(port, buf); 1221273515Sbryanv deq++; 1222273515Sbryanv } 1223273515Sbryanv ttydisc_rint_done(tp); 1224273515Sbryanv 1225273515Sbryanv if (deq > 0) 1226273515Sbryanv virtqueue_notify(vq); 1227273515Sbryanv 1228273515Sbryanv if (virtqueue_enable_intr(vq) != 0) 1229273515Sbryanv goto again; 1230273515Sbryanv} 1231273515Sbryanv 1232273515Sbryanvstatic void 1233275273Sbryanvvtcon_port_intr(void *scportx) 1234273515Sbryanv{ 1235275273Sbryanv struct vtcon_softc_port *scport; 1236275273Sbryanv struct vtcon_softc *sc; 1237273515Sbryanv struct vtcon_port *port; 1238273515Sbryanv 1239275273Sbryanv scport = scportx; 1240275273Sbryanv sc = scport->vcsp_sc; 1241273515Sbryanv 1242275273Sbryanv VTCON_LOCK(sc); 1243275273Sbryanv port = scport->vcsp_port; 1244275273Sbryanv if (port == NULL) { 1245275273Sbryanv VTCON_UNLOCK(sc); 1246275273Sbryanv return; 1247275273Sbryanv } 1248275273Sbryanv VTCON_PORT_LOCK(port); 1249275273Sbryanv VTCON_UNLOCK(sc); 1250275273Sbryanv if ((port->vtcport_flags & VTCON_PORT_FLAG_GONE) == 0) 1251275273Sbryanv vtcon_port_in(port); 1252275273Sbryanv VTCON_PORT_UNLOCK(port); 1253273515Sbryanv} 1254273515Sbryanv 1255273515Sbryanvstatic void 1256275273Sbryanvvtcon_port_out(struct vtcon_port *port, void *buf, int bufsize) 1257273515Sbryanv{ 1258275273Sbryanv struct sglist_seg segs[2]; 1259273515Sbryanv struct sglist sg; 1260273515Sbryanv struct virtqueue *vq; 1261273515Sbryanv int error; 1262273515Sbryanv 1263273515Sbryanv vq = port->vtcport_outvq; 1264275273Sbryanv KASSERT(virtqueue_empty(vq), 1265275273Sbryanv ("%s: port %p out virtqueue not emtpy", __func__, port)); 1266273515Sbryanv 1267275273Sbryanv sglist_init(&sg, 2, segs); 1268273515Sbryanv error = sglist_append(&sg, buf, bufsize); 1269275273Sbryanv KASSERT(error == 0, ("%s: error %d adding buffer to sglist", 1270275273Sbryanv __func__, error)); 1271273515Sbryanv 1272275273Sbryanv error = virtqueue_enqueue(vq, buf, &sg, sg.sg_nseg, 0); 1273275273Sbryanv if (error == 0) { 1274273515Sbryanv virtqueue_notify(vq); 1275273515Sbryanv virtqueue_poll(vq, NULL); 1276273515Sbryanv } 1277273515Sbryanv} 1278273515Sbryanv 1279273515Sbryanvstatic void 1280275273Sbryanvvtcon_port_submit_event(struct vtcon_port *port, uint16_t event, 1281273515Sbryanv uint16_t value) 1282273515Sbryanv{ 1283273515Sbryanv struct vtcon_softc *sc; 1284273515Sbryanv 1285273515Sbryanv sc = port->vtcport_sc; 1286273515Sbryanv 1287275273Sbryanv vtcon_ctrl_send_control(sc, port->vtcport_id, event, value); 1288273515Sbryanv} 1289273515Sbryanv 1290273515Sbryanvstatic int 1291273515Sbryanvvtcon_tty_open(struct tty *tp) 1292273515Sbryanv{ 1293273515Sbryanv struct vtcon_port *port; 1294273515Sbryanv 1295273515Sbryanv port = tty_softc(tp); 1296273515Sbryanv 1297275273Sbryanv if (port->vtcport_flags & VTCON_PORT_FLAG_GONE) 1298275273Sbryanv return (ENXIO); 1299273515Sbryanv 1300275273Sbryanv vtcon_port_submit_event(port, VIRTIO_CONSOLE_PORT_OPEN, 1); 1301275273Sbryanv 1302273515Sbryanv return (0); 1303273515Sbryanv} 1304273515Sbryanv 1305273515Sbryanvstatic void 1306273515Sbryanvvtcon_tty_close(struct tty *tp) 1307273515Sbryanv{ 1308273515Sbryanv struct vtcon_port *port; 1309273515Sbryanv 1310273515Sbryanv port = tty_softc(tp); 1311273515Sbryanv 1312275273Sbryanv if (port->vtcport_flags & VTCON_PORT_FLAG_GONE) 1313275273Sbryanv return; 1314275273Sbryanv 1315275273Sbryanv vtcon_port_submit_event(port, VIRTIO_CONSOLE_PORT_OPEN, 0); 1316273515Sbryanv} 1317273515Sbryanv 1318273515Sbryanvstatic void 1319273515Sbryanvvtcon_tty_outwakeup(struct tty *tp) 1320273515Sbryanv{ 1321273515Sbryanv struct vtcon_port *port; 1322273515Sbryanv char buf[VTCON_BULK_BUFSZ]; 1323273515Sbryanv int len; 1324273515Sbryanv 1325273515Sbryanv port = tty_softc(tp); 1326273515Sbryanv 1327275273Sbryanv if (port->vtcport_flags & VTCON_PORT_FLAG_GONE) 1328275273Sbryanv return; 1329275273Sbryanv 1330273515Sbryanv while ((len = ttydisc_getc(tp, buf, sizeof(buf))) != 0) 1331275273Sbryanv vtcon_port_out(port, buf, len); 1332273515Sbryanv} 1333273515Sbryanv 1334273515Sbryanvstatic void 1335273515Sbryanvvtcon_tty_free(void *xport) 1336273515Sbryanv{ 1337273515Sbryanv struct vtcon_port *port; 1338273515Sbryanv 1339273515Sbryanv port = xport; 1340273515Sbryanv 1341273515Sbryanv vtcon_port_destroy(port); 1342273515Sbryanv atomic_subtract_int(&vtcon_pending_free, 1); 1343273515Sbryanv} 1344273515Sbryanv 1345273515Sbryanvstatic void 1346273515Sbryanvvtcon_get_console_size(struct vtcon_softc *sc, uint16_t *cols, uint16_t *rows) 1347273515Sbryanv{ 1348273515Sbryanv struct virtio_console_config concfg; 1349273515Sbryanv 1350273515Sbryanv KASSERT(sc->vtcon_flags & VTCON_FLAG_SIZE, 1351273515Sbryanv ("%s: size feature not negotiated", __func__)); 1352273515Sbryanv 1353273515Sbryanv vtcon_read_config(sc, &concfg); 1354273515Sbryanv 1355273515Sbryanv *cols = concfg.cols; 1356273515Sbryanv *rows = concfg.rows; 1357273515Sbryanv} 1358273515Sbryanv 1359273515Sbryanvstatic void 1360273515Sbryanvvtcon_enable_interrupts(struct vtcon_softc *sc) 1361273515Sbryanv{ 1362275273Sbryanv struct vtcon_softc_port *scport; 1363273515Sbryanv struct vtcon_port *port; 1364275273Sbryanv int i; 1365273515Sbryanv 1366275273Sbryanv VTCON_LOCK(sc); 1367275273Sbryanv 1368273515Sbryanv if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) 1369273515Sbryanv virtqueue_enable_intr(sc->vtcon_ctrl_rxvq); 1370273515Sbryanv 1371275273Sbryanv for (i = 0; i < sc->vtcon_max_ports; i++) { 1372275273Sbryanv scport = &sc->vtcon_ports[i]; 1373275273Sbryanv 1374275273Sbryanv port = scport->vcsp_port; 1375275273Sbryanv if (port == NULL) 1376275273Sbryanv continue; 1377275273Sbryanv 1378275273Sbryanv VTCON_PORT_LOCK(port); 1379273515Sbryanv vtcon_port_enable_intr(port); 1380275273Sbryanv VTCON_PORT_UNLOCK(port); 1381275273Sbryanv } 1382275273Sbryanv 1383275273Sbryanv VTCON_UNLOCK(sc); 1384273515Sbryanv} 1385273515Sbryanv 1386273515Sbryanvstatic void 1387273515Sbryanvvtcon_disable_interrupts(struct vtcon_softc *sc) 1388273515Sbryanv{ 1389275273Sbryanv struct vtcon_softc_port *scport; 1390273515Sbryanv struct vtcon_port *port; 1391275273Sbryanv int i; 1392273515Sbryanv 1393275273Sbryanv VTCON_LOCK_ASSERT(sc); 1394275273Sbryanv 1395273515Sbryanv if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) 1396273515Sbryanv virtqueue_disable_intr(sc->vtcon_ctrl_rxvq); 1397273515Sbryanv 1398275273Sbryanv for (i = 0; i < sc->vtcon_max_ports; i++) { 1399275273Sbryanv scport = &sc->vtcon_ports[i]; 1400275273Sbryanv 1401275273Sbryanv port = scport->vcsp_port; 1402275273Sbryanv if (port == NULL) 1403275273Sbryanv continue; 1404275273Sbryanv 1405275273Sbryanv VTCON_PORT_LOCK(port); 1406273515Sbryanv vtcon_port_disable_intr(port); 1407275273Sbryanv VTCON_PORT_UNLOCK(port); 1408275273Sbryanv } 1409273515Sbryanv} 1410