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