qemu/hw/char/virtio-console.c
<<
>>
Prefs
   1/*
   2 * Virtio Console and Generic Serial Port Devices
   3 *
   4 * Copyright Red Hat, Inc. 2009, 2010
   5 *
   6 * Authors:
   7 *  Amit Shah <amit.shah@redhat.com>
   8 *
   9 * This work is licensed under the terms of the GNU GPL, version 2.  See
  10 * the COPYING file in the top-level directory.
  11 */
  12
  13#include "qemu/osdep.h"
  14#include "chardev/char-fe.h"
  15#include "qemu/error-report.h"
  16#include "qemu/module.h"
  17#include "trace.h"
  18#include "hw/qdev-properties.h"
  19#include "hw/qdev-properties-system.h"
  20#include "hw/virtio/virtio-serial.h"
  21#include "qapi/error.h"
  22#include "qapi/qapi-events-char.h"
  23#include "qom/object.h"
  24
  25#define TYPE_VIRTIO_CONSOLE_SERIAL_PORT "virtserialport"
  26typedef struct VirtConsole VirtConsole;
  27DECLARE_INSTANCE_CHECKER(VirtConsole, VIRTIO_CONSOLE,
  28                         TYPE_VIRTIO_CONSOLE_SERIAL_PORT)
  29
  30struct VirtConsole {
  31    VirtIOSerialPort parent_obj;
  32
  33    CharBackend chr;
  34    guint watch;
  35};
  36
  37/*
  38 * Callback function that's called from chardevs when backend becomes
  39 * writable.
  40 */
  41static gboolean chr_write_unblocked(GIOChannel *chan, GIOCondition cond,
  42                                    void *opaque)
  43{
  44    VirtConsole *vcon = opaque;
  45
  46    vcon->watch = 0;
  47    virtio_serial_throttle_port(VIRTIO_SERIAL_PORT(vcon), false);
  48    return FALSE;
  49}
  50
  51/* Callback function that's called when the guest sends us data */
  52static ssize_t flush_buf(VirtIOSerialPort *port,
  53                         const uint8_t *buf, ssize_t len)
  54{
  55    VirtConsole *vcon = VIRTIO_CONSOLE(port);
  56    ssize_t ret;
  57
  58    if (!qemu_chr_fe_backend_connected(&vcon->chr)) {
  59        /* If there's no backend, we can just say we consumed all data. */
  60        return len;
  61    }
  62
  63    ret = qemu_chr_fe_write(&vcon->chr, buf, len);
  64    trace_virtio_console_flush_buf(port->id, len, ret);
  65
  66    if (ret < len) {
  67        VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_GET_CLASS(port);
  68
  69        /*
  70         * Ideally we'd get a better error code than just -1, but
  71         * that's what the chardev interface gives us right now.  If
  72         * we had a finer-grained message, like -EPIPE, we could close
  73         * this connection.
  74         */
  75        if (ret < 0)
  76            ret = 0;
  77
  78        /* XXX we should be queuing data to send later for the
  79         * console devices too rather than silently dropping
  80         * console data on EAGAIN. The Linux virtio-console
  81         * hvc driver though does sends with spinlocks held,
  82         * so if we enable throttling that'll stall the entire
  83         * guest kernel, not merely the process writing to the
  84         * console.
  85         *
  86         * While we could queue data for later write without
  87         * enabling throttling, this would result in the guest
  88         * being able to trigger arbitrary memory usage in QEMU
  89         * buffering data for later writes.
  90         *
  91         * So fixing this problem likely requires fixing the
  92         * Linux virtio-console hvc driver to not hold spinlocks
  93         * while writing, and instead merely block the process
  94         * that's writing. QEMU would then need some way to detect
  95         * if the guest had the fixed driver too, before we can
  96         * use throttling on host side.
  97         */
  98        if (!k->is_console) {
  99            virtio_serial_throttle_port(port, true);
 100            if (!vcon->watch) {
 101                vcon->watch = qemu_chr_fe_add_watch(&vcon->chr,
 102                                                    G_IO_OUT|G_IO_HUP,
 103                                                    chr_write_unblocked, vcon);
 104            }
 105        }
 106    }
 107    return ret;
 108}
 109
 110/* Callback function that's called when the guest opens/closes the port */
 111static void set_guest_connected(VirtIOSerialPort *port, int guest_connected)
 112{
 113    VirtConsole *vcon = VIRTIO_CONSOLE(port);
 114    DeviceState *dev = DEVICE(port);
 115    VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_GET_CLASS(port);
 116
 117    if (!k->is_console) {
 118        qemu_chr_fe_set_open(&vcon->chr, guest_connected);
 119    }
 120
 121    if (dev->id) {
 122        qapi_event_send_vserport_change(dev->id, guest_connected);
 123    }
 124}
 125
 126static void guest_writable(VirtIOSerialPort *port)
 127{
 128    VirtConsole *vcon = VIRTIO_CONSOLE(port);
 129
 130    qemu_chr_fe_accept_input(&vcon->chr);
 131}
 132
 133/* Readiness of the guest to accept data on a port */
 134static int chr_can_read(void *opaque)
 135{
 136    VirtConsole *vcon = opaque;
 137
 138    return virtio_serial_guest_ready(VIRTIO_SERIAL_PORT(vcon));
 139}
 140
 141/* Send data from a char device over to the guest */
 142static void chr_read(void *opaque, const uint8_t *buf, int size)
 143{
 144    VirtConsole *vcon = opaque;
 145    VirtIOSerialPort *port = VIRTIO_SERIAL_PORT(vcon);
 146
 147    trace_virtio_console_chr_read(port->id, size);
 148    virtio_serial_write(port, buf, size);
 149}
 150
 151static void chr_event(void *opaque, QEMUChrEvent event)
 152{
 153    VirtConsole *vcon = opaque;
 154    VirtIOSerialPort *port = VIRTIO_SERIAL_PORT(vcon);
 155
 156    trace_virtio_console_chr_event(port->id, event);
 157    switch (event) {
 158    case CHR_EVENT_OPENED:
 159        virtio_serial_open(port);
 160        break;
 161    case CHR_EVENT_CLOSED:
 162        if (vcon->watch) {
 163            g_source_remove(vcon->watch);
 164            vcon->watch = 0;
 165        }
 166        virtio_serial_close(port);
 167        break;
 168    case CHR_EVENT_BREAK:
 169    case CHR_EVENT_MUX_IN:
 170    case CHR_EVENT_MUX_OUT:
 171        /* Ignore */
 172        break;
 173    }
 174}
 175
 176static int chr_be_change(void *opaque)
 177{
 178    VirtConsole *vcon = opaque;
 179    VirtIOSerialPort *port = VIRTIO_SERIAL_PORT(vcon);
 180    VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_GET_CLASS(port);
 181
 182    if (k->is_console) {
 183        qemu_chr_fe_set_handlers(&vcon->chr, chr_can_read, chr_read,
 184                                 NULL, chr_be_change, vcon, NULL, true);
 185    } else {
 186        qemu_chr_fe_set_handlers(&vcon->chr, chr_can_read, chr_read,
 187                                 chr_event, chr_be_change, vcon, NULL, false);
 188    }
 189
 190    if (vcon->watch) {
 191        g_source_remove(vcon->watch);
 192        vcon->watch = qemu_chr_fe_add_watch(&vcon->chr,
 193                                            G_IO_OUT | G_IO_HUP,
 194                                            chr_write_unblocked, vcon);
 195    }
 196
 197    return 0;
 198}
 199
 200static void virtconsole_enable_backend(VirtIOSerialPort *port, bool enable)
 201{
 202    VirtConsole *vcon = VIRTIO_CONSOLE(port);
 203
 204    if (!qemu_chr_fe_backend_connected(&vcon->chr)) {
 205        return;
 206    }
 207
 208    if (enable) {
 209        VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_GET_CLASS(port);
 210
 211        qemu_chr_fe_set_handlers(&vcon->chr, chr_can_read, chr_read,
 212                                 k->is_console ? NULL : chr_event,
 213                                 chr_be_change, vcon, NULL, false);
 214    } else {
 215        qemu_chr_fe_set_handlers(&vcon->chr, NULL, NULL, NULL,
 216                                 NULL, NULL, NULL, false);
 217    }
 218}
 219
 220static void virtconsole_realize(DeviceState *dev, Error **errp)
 221{
 222    VirtIOSerialPort *port = VIRTIO_SERIAL_PORT(dev);
 223    VirtConsole *vcon = VIRTIO_CONSOLE(dev);
 224    VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_GET_CLASS(dev);
 225
 226    if (port->id == 0 && !k->is_console) {
 227        error_setg(errp, "Port number 0 on virtio-serial devices reserved "
 228                   "for virtconsole devices for backward compatibility.");
 229        return;
 230    }
 231
 232    if (qemu_chr_fe_backend_connected(&vcon->chr)) {
 233        /*
 234         * For consoles we don't block guest data transfer just
 235         * because nothing is connected - we'll just let it go
 236         * whetherever the chardev wants - /dev/null probably.
 237         *
 238         * For serial ports we need 100% reliable data transfer
 239         * so we use the opened/closed signals from chardev to
 240         * trigger open/close of the device
 241         */
 242        if (k->is_console) {
 243            qemu_chr_fe_set_handlers(&vcon->chr, chr_can_read, chr_read,
 244                                     NULL, chr_be_change,
 245                                     vcon, NULL, true);
 246            virtio_serial_open(port);
 247        } else {
 248            qemu_chr_fe_set_handlers(&vcon->chr, chr_can_read, chr_read,
 249                                     chr_event, chr_be_change,
 250                                     vcon, NULL, false);
 251        }
 252    }
 253}
 254
 255static void virtconsole_unrealize(DeviceState *dev)
 256{
 257    VirtConsole *vcon = VIRTIO_CONSOLE(dev);
 258
 259    if (vcon->watch) {
 260        g_source_remove(vcon->watch);
 261    }
 262}
 263
 264static void virtconsole_class_init(ObjectClass *klass, void *data)
 265{
 266    VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_CLASS(klass);
 267
 268    k->is_console = true;
 269}
 270
 271static const TypeInfo virtconsole_info = {
 272    .name          = "virtconsole",
 273    .parent        = TYPE_VIRTIO_CONSOLE_SERIAL_PORT,
 274    .class_init    = virtconsole_class_init,
 275};
 276
 277static Property virtserialport_properties[] = {
 278    DEFINE_PROP_CHR("chardev", VirtConsole, chr),
 279    DEFINE_PROP_END_OF_LIST(),
 280};
 281
 282static void virtserialport_class_init(ObjectClass *klass, void *data)
 283{
 284    DeviceClass *dc = DEVICE_CLASS(klass);
 285    VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_CLASS(klass);
 286
 287    k->realize = virtconsole_realize;
 288    k->unrealize = virtconsole_unrealize;
 289    k->have_data = flush_buf;
 290    k->set_guest_connected = set_guest_connected;
 291    k->enable_backend = virtconsole_enable_backend;
 292    k->guest_writable = guest_writable;
 293    device_class_set_props(dc, virtserialport_properties);
 294}
 295
 296static const TypeInfo virtserialport_info = {
 297    .name          = TYPE_VIRTIO_CONSOLE_SERIAL_PORT,
 298    .parent        = TYPE_VIRTIO_SERIAL_PORT,
 299    .instance_size = sizeof(VirtConsole),
 300    .class_init    = virtserialport_class_init,
 301};
 302
 303static void virtconsole_register_types(void)
 304{
 305    type_register_static(&virtserialport_info);
 306    type_register_static(&virtconsole_info);
 307}
 308
 309type_init(virtconsole_register_types)
 310