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, QEMUChrEvent 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 case CHR_EVENT_BREAK: 166 case CHR_EVENT_MUX_IN: 167 case CHR_EVENT_MUX_OUT: 168 /* Ignore */ 169 break; 170 } 171 } 172 173 static int chr_be_change(void *opaque) 174 { 175 VirtConsole *vcon = opaque; 176 VirtIOSerialPort *port = VIRTIO_SERIAL_PORT(vcon); 177 VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_GET_CLASS(port); 178 179 if (k->is_console) { 180 qemu_chr_fe_set_handlers(&vcon->chr, chr_can_read, chr_read, 181 NULL, chr_be_change, vcon, NULL, true); 182 } else { 183 qemu_chr_fe_set_handlers(&vcon->chr, chr_can_read, chr_read, 184 chr_event, chr_be_change, vcon, NULL, false); 185 } 186 187 if (vcon->watch) { 188 g_source_remove(vcon->watch); 189 vcon->watch = qemu_chr_fe_add_watch(&vcon->chr, 190 G_IO_OUT | G_IO_HUP, 191 chr_write_unblocked, vcon); 192 } 193 194 return 0; 195 } 196 197 static void virtconsole_enable_backend(VirtIOSerialPort *port, bool enable) 198 { 199 VirtConsole *vcon = VIRTIO_CONSOLE(port); 200 201 if (!qemu_chr_fe_backend_connected(&vcon->chr)) { 202 return; 203 } 204 205 if (enable) { 206 VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_GET_CLASS(port); 207 208 qemu_chr_fe_set_handlers(&vcon->chr, chr_can_read, chr_read, 209 k->is_console ? NULL : chr_event, 210 chr_be_change, vcon, NULL, false); 211 } else { 212 qemu_chr_fe_set_handlers(&vcon->chr, NULL, NULL, NULL, 213 NULL, NULL, NULL, false); 214 } 215 } 216 217 static void virtconsole_realize(DeviceState *dev, Error **errp) 218 { 219 VirtIOSerialPort *port = VIRTIO_SERIAL_PORT(dev); 220 VirtConsole *vcon = VIRTIO_CONSOLE(dev); 221 VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_GET_CLASS(dev); 222 223 if (port->id == 0 && !k->is_console) { 224 error_setg(errp, "Port number 0 on virtio-serial devices reserved " 225 "for virtconsole devices for backward compatibility."); 226 return; 227 } 228 229 if (qemu_chr_fe_backend_connected(&vcon->chr)) { 230 /* 231 * For consoles we don't block guest data transfer just 232 * because nothing is connected - we'll just let it go 233 * whetherever the chardev wants - /dev/null probably. 234 * 235 * For serial ports we need 100% reliable data transfer 236 * so we use the opened/closed signals from chardev to 237 * trigger open/close of the device 238 */ 239 if (k->is_console) { 240 qemu_chr_fe_set_handlers(&vcon->chr, chr_can_read, chr_read, 241 NULL, chr_be_change, 242 vcon, NULL, true); 243 virtio_serial_open(port); 244 } else { 245 qemu_chr_fe_set_handlers(&vcon->chr, chr_can_read, chr_read, 246 chr_event, chr_be_change, 247 vcon, NULL, false); 248 } 249 } 250 } 251 252 static void virtconsole_unrealize(DeviceState *dev) 253 { 254 VirtConsole *vcon = VIRTIO_CONSOLE(dev); 255 256 if (vcon->watch) { 257 g_source_remove(vcon->watch); 258 } 259 } 260 261 static void virtconsole_class_init(ObjectClass *klass, void *data) 262 { 263 VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_CLASS(klass); 264 265 k->is_console = true; 266 } 267 268 static const TypeInfo virtconsole_info = { 269 .name = "virtconsole", 270 .parent = TYPE_VIRTIO_CONSOLE_SERIAL_PORT, 271 .class_init = virtconsole_class_init, 272 }; 273 274 static Property virtserialport_properties[] = { 275 DEFINE_PROP_CHR("chardev", VirtConsole, chr), 276 DEFINE_PROP_END_OF_LIST(), 277 }; 278 279 static void virtserialport_class_init(ObjectClass *klass, void *data) 280 { 281 DeviceClass *dc = DEVICE_CLASS(klass); 282 VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_CLASS(klass); 283 284 k->realize = virtconsole_realize; 285 k->unrealize = virtconsole_unrealize; 286 k->have_data = flush_buf; 287 k->set_guest_connected = set_guest_connected; 288 k->enable_backend = virtconsole_enable_backend; 289 k->guest_writable = guest_writable; 290 device_class_set_props(dc, virtserialport_properties); 291 } 292 293 static const TypeInfo virtserialport_info = { 294 .name = TYPE_VIRTIO_CONSOLE_SERIAL_PORT, 295 .parent = TYPE_VIRTIO_SERIAL_PORT, 296 .instance_size = sizeof(VirtConsole), 297 .class_init = virtserialport_class_init, 298 }; 299 300 static void virtconsole_register_types(void) 301 { 302 type_register_static(&virtserialport_info); 303 type_register_static(&virtconsole_info); 304 } 305 306 type_init(virtconsole_register_types) 307