virtio-console.c (9685B)
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(void *do_not_use, 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)