16e790746SPaolo Bonzini /* 26e790746SPaolo Bonzini * A bus for connecting virtio serial and console ports 36e790746SPaolo Bonzini * 46e790746SPaolo Bonzini * Copyright (C) 2009, 2010 Red Hat, Inc. 56e790746SPaolo Bonzini * 66e790746SPaolo Bonzini * Author(s): 76e790746SPaolo Bonzini * Amit Shah <amit.shah@redhat.com> 86e790746SPaolo Bonzini * 96e790746SPaolo Bonzini * Some earlier parts are: 106e790746SPaolo Bonzini * Copyright IBM, Corp. 2008 116e790746SPaolo Bonzini * authored by 126e790746SPaolo Bonzini * Christian Ehrhardt <ehrhardt@linux.vnet.ibm.com> 136e790746SPaolo Bonzini * 146e790746SPaolo Bonzini * This work is licensed under the terms of the GNU GPL, version 2. See 156e790746SPaolo Bonzini * the COPYING file in the top-level directory. 166e790746SPaolo Bonzini * 176e790746SPaolo Bonzini * Contributions after 2012-01-13 are licensed under the terms of the 186e790746SPaolo Bonzini * GNU GPL, version 2 or (at your option) any later version. 196e790746SPaolo Bonzini */ 206e790746SPaolo Bonzini 219b8bfe21SPeter Maydell #include "qemu/osdep.h" 22da34e65cSMarkus Armbruster #include "qapi/error.h" 236e790746SPaolo Bonzini #include "qemu/iov.h" 246e790746SPaolo Bonzini #include "monitor/monitor.h" 25d49b6836SMarkus Armbruster #include "qemu/error-report.h" 266e790746SPaolo Bonzini #include "qemu/queue.h" 276e790746SPaolo Bonzini #include "hw/sysbus.h" 286e790746SPaolo Bonzini #include "trace.h" 296e790746SPaolo Bonzini #include "hw/virtio/virtio-serial.h" 30e0ab7facSRusty Russell #include "hw/virtio/virtio-access.h" 316e790746SPaolo Bonzini 3243d73554SStefan Weil static struct VirtIOSerialDevices { 33a1857ad1SAmit Shah QLIST_HEAD(, VirtIOSerial) devices; 34a1857ad1SAmit Shah } vserdevices; 35a1857ad1SAmit Shah 366e790746SPaolo Bonzini static VirtIOSerialPort *find_port_by_id(VirtIOSerial *vser, uint32_t id) 376e790746SPaolo Bonzini { 386e790746SPaolo Bonzini VirtIOSerialPort *port; 396e790746SPaolo Bonzini 406e790746SPaolo Bonzini if (id == VIRTIO_CONSOLE_BAD_ID) { 416e790746SPaolo Bonzini return NULL; 426e790746SPaolo Bonzini } 436e790746SPaolo Bonzini 446e790746SPaolo Bonzini QTAILQ_FOREACH(port, &vser->ports, next) { 456e790746SPaolo Bonzini if (port->id == id) 466e790746SPaolo Bonzini return port; 476e790746SPaolo Bonzini } 486e790746SPaolo Bonzini return NULL; 496e790746SPaolo Bonzini } 506e790746SPaolo Bonzini 516e790746SPaolo Bonzini static VirtIOSerialPort *find_port_by_vq(VirtIOSerial *vser, VirtQueue *vq) 526e790746SPaolo Bonzini { 536e790746SPaolo Bonzini VirtIOSerialPort *port; 546e790746SPaolo Bonzini 556e790746SPaolo Bonzini QTAILQ_FOREACH(port, &vser->ports, next) { 566e790746SPaolo Bonzini if (port->ivq == vq || port->ovq == vq) 576e790746SPaolo Bonzini return port; 586e790746SPaolo Bonzini } 596e790746SPaolo Bonzini return NULL; 606e790746SPaolo Bonzini } 616e790746SPaolo Bonzini 62d0a0bfe6SAmit Shah static VirtIOSerialPort *find_port_by_name(char *name) 63d0a0bfe6SAmit Shah { 64d0a0bfe6SAmit Shah VirtIOSerial *vser; 65d0a0bfe6SAmit Shah 66d0a0bfe6SAmit Shah QLIST_FOREACH(vser, &vserdevices.devices, next) { 67d0a0bfe6SAmit Shah VirtIOSerialPort *port; 68d0a0bfe6SAmit Shah 69d0a0bfe6SAmit Shah QTAILQ_FOREACH(port, &vser->ports, next) { 70b18a755cSAmit Shah if (port->name && !strcmp(port->name, name)) { 71d0a0bfe6SAmit Shah return port; 72d0a0bfe6SAmit Shah } 73d0a0bfe6SAmit Shah } 74d0a0bfe6SAmit Shah } 75d0a0bfe6SAmit Shah return NULL; 76d0a0bfe6SAmit Shah } 77d0a0bfe6SAmit Shah 7809da01c3SSascha Silbe static VirtIOSerialPort *find_first_connected_console(VirtIOSerial *vser) 7909da01c3SSascha Silbe { 8009da01c3SSascha Silbe VirtIOSerialPort *port; 8109da01c3SSascha Silbe 8209da01c3SSascha Silbe QTAILQ_FOREACH(port, &vser->ports, next) { 8309da01c3SSascha Silbe VirtIOSerialPortClass const *vsc = VIRTIO_SERIAL_PORT_GET_CLASS(port); 8409da01c3SSascha Silbe if (vsc->is_console && port->host_connected) { 8509da01c3SSascha Silbe return port; 8609da01c3SSascha Silbe } 8709da01c3SSascha Silbe } 8809da01c3SSascha Silbe return NULL; 8909da01c3SSascha Silbe } 9009da01c3SSascha Silbe 916e790746SPaolo Bonzini static bool use_multiport(VirtIOSerial *vser) 926e790746SPaolo Bonzini { 9376017fd2SKONRAD Frederic VirtIODevice *vdev = VIRTIO_DEVICE(vser); 9495129d6fSCornelia Huck return virtio_vdev_has_feature(vdev, VIRTIO_CONSOLE_F_MULTIPORT); 956e790746SPaolo Bonzini } 966e790746SPaolo Bonzini 976e790746SPaolo Bonzini static size_t write_to_port(VirtIOSerialPort *port, 986e790746SPaolo Bonzini const uint8_t *buf, size_t size) 996e790746SPaolo Bonzini { 10051b19ebeSPaolo Bonzini VirtQueueElement *elem; 1016e790746SPaolo Bonzini VirtQueue *vq; 1026e790746SPaolo Bonzini size_t offset; 1036e790746SPaolo Bonzini 1046e790746SPaolo Bonzini vq = port->ivq; 1056e790746SPaolo Bonzini if (!virtio_queue_ready(vq)) { 1066e790746SPaolo Bonzini return 0; 1076e790746SPaolo Bonzini } 1086e790746SPaolo Bonzini 1096e790746SPaolo Bonzini offset = 0; 1106e790746SPaolo Bonzini while (offset < size) { 1116e790746SPaolo Bonzini size_t len; 1126e790746SPaolo Bonzini 11351b19ebeSPaolo Bonzini elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); 11451b19ebeSPaolo Bonzini if (!elem) { 1156e790746SPaolo Bonzini break; 1166e790746SPaolo Bonzini } 1176e790746SPaolo Bonzini 11851b19ebeSPaolo Bonzini len = iov_from_buf(elem->in_sg, elem->in_num, 0, 1196e790746SPaolo Bonzini buf + offset, size - offset); 1206e790746SPaolo Bonzini offset += len; 1216e790746SPaolo Bonzini 12251b19ebeSPaolo Bonzini virtqueue_push(vq, elem, len); 12351b19ebeSPaolo Bonzini g_free(elem); 1246e790746SPaolo Bonzini } 1256e790746SPaolo Bonzini 12676017fd2SKONRAD Frederic virtio_notify(VIRTIO_DEVICE(port->vser), vq); 1276e790746SPaolo Bonzini return offset; 1286e790746SPaolo Bonzini } 1296e790746SPaolo Bonzini 1306e790746SPaolo Bonzini static void discard_vq_data(VirtQueue *vq, VirtIODevice *vdev) 1316e790746SPaolo Bonzini { 13251b19ebeSPaolo Bonzini VirtQueueElement *elem; 1336e790746SPaolo Bonzini 1346e790746SPaolo Bonzini if (!virtio_queue_ready(vq)) { 1356e790746SPaolo Bonzini return; 1366e790746SPaolo Bonzini } 13751b19ebeSPaolo Bonzini for (;;) { 13851b19ebeSPaolo Bonzini elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); 13951b19ebeSPaolo Bonzini if (!elem) { 14051b19ebeSPaolo Bonzini break; 14151b19ebeSPaolo Bonzini } 14251b19ebeSPaolo Bonzini virtqueue_push(vq, elem, 0); 14351b19ebeSPaolo Bonzini g_free(elem); 1446e790746SPaolo Bonzini } 1456e790746SPaolo Bonzini virtio_notify(vdev, vq); 1466e790746SPaolo Bonzini } 1476e790746SPaolo Bonzini 148d4c19cdeSStefan Hajnoczi static void discard_throttle_data(VirtIOSerialPort *port) 149d4c19cdeSStefan Hajnoczi { 150d4c19cdeSStefan Hajnoczi if (port->elem) { 151d4c19cdeSStefan Hajnoczi virtqueue_detach_element(port->ovq, port->elem, 0); 152d4c19cdeSStefan Hajnoczi g_free(port->elem); 153d4c19cdeSStefan Hajnoczi port->elem = NULL; 154d4c19cdeSStefan Hajnoczi } 155d4c19cdeSStefan Hajnoczi } 156d4c19cdeSStefan Hajnoczi 1576e790746SPaolo Bonzini static void do_flush_queued_data(VirtIOSerialPort *port, VirtQueue *vq, 1586e790746SPaolo Bonzini VirtIODevice *vdev) 1596e790746SPaolo Bonzini { 1606e790746SPaolo Bonzini VirtIOSerialPortClass *vsc; 1616e790746SPaolo Bonzini 1626e790746SPaolo Bonzini assert(port); 1636e790746SPaolo Bonzini assert(virtio_queue_ready(vq)); 1646e790746SPaolo Bonzini 1656e790746SPaolo Bonzini vsc = VIRTIO_SERIAL_PORT_GET_CLASS(port); 1666e790746SPaolo Bonzini 1676e790746SPaolo Bonzini while (!port->throttled) { 1686e790746SPaolo Bonzini unsigned int i; 1696e790746SPaolo Bonzini 1706e790746SPaolo Bonzini /* Pop an elem only if we haven't left off a previous one mid-way */ 17151b19ebeSPaolo Bonzini if (!port->elem) { 17251b19ebeSPaolo Bonzini port->elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); 17351b19ebeSPaolo Bonzini if (!port->elem) { 1746e790746SPaolo Bonzini break; 1756e790746SPaolo Bonzini } 1766e790746SPaolo Bonzini port->iov_idx = 0; 1776e790746SPaolo Bonzini port->iov_offset = 0; 1786e790746SPaolo Bonzini } 1796e790746SPaolo Bonzini 18051b19ebeSPaolo Bonzini for (i = port->iov_idx; i < port->elem->out_num; i++) { 1816e790746SPaolo Bonzini size_t buf_size; 1826e790746SPaolo Bonzini ssize_t ret; 1836e790746SPaolo Bonzini 18451b19ebeSPaolo Bonzini buf_size = port->elem->out_sg[i].iov_len - port->iov_offset; 1856e790746SPaolo Bonzini ret = vsc->have_data(port, 18651b19ebeSPaolo Bonzini port->elem->out_sg[i].iov_base 1876e790746SPaolo Bonzini + port->iov_offset, 1886e790746SPaolo Bonzini buf_size); 18946764fe0SStefan Hajnoczi if (!port->elem) { /* bail if we got disconnected */ 19046764fe0SStefan Hajnoczi return; 19146764fe0SStefan Hajnoczi } 1926e790746SPaolo Bonzini if (port->throttled) { 1936e790746SPaolo Bonzini port->iov_idx = i; 1946e790746SPaolo Bonzini if (ret > 0) { 1956e790746SPaolo Bonzini port->iov_offset += ret; 1966e790746SPaolo Bonzini } 1976e790746SPaolo Bonzini break; 1986e790746SPaolo Bonzini } 1996e790746SPaolo Bonzini port->iov_offset = 0; 2006e790746SPaolo Bonzini } 2016e790746SPaolo Bonzini if (port->throttled) { 2026e790746SPaolo Bonzini break; 2036e790746SPaolo Bonzini } 20451b19ebeSPaolo Bonzini virtqueue_push(vq, port->elem, 0); 20551b19ebeSPaolo Bonzini g_free(port->elem); 20651b19ebeSPaolo Bonzini port->elem = NULL; 2076e790746SPaolo Bonzini } 2086e790746SPaolo Bonzini virtio_notify(vdev, vq); 2096e790746SPaolo Bonzini } 2106e790746SPaolo Bonzini 2116e790746SPaolo Bonzini static void flush_queued_data(VirtIOSerialPort *port) 2126e790746SPaolo Bonzini { 2136e790746SPaolo Bonzini assert(port); 2146e790746SPaolo Bonzini 2156e790746SPaolo Bonzini if (!virtio_queue_ready(port->ovq)) { 2166e790746SPaolo Bonzini return; 2176e790746SPaolo Bonzini } 21876017fd2SKONRAD Frederic do_flush_queued_data(port, port->ovq, VIRTIO_DEVICE(port->vser)); 2196e790746SPaolo Bonzini } 2206e790746SPaolo Bonzini 2216e790746SPaolo Bonzini static size_t send_control_msg(VirtIOSerial *vser, void *buf, size_t len) 2226e790746SPaolo Bonzini { 22351b19ebeSPaolo Bonzini VirtQueueElement *elem; 2246e790746SPaolo Bonzini VirtQueue *vq; 2256e790746SPaolo Bonzini 2266e790746SPaolo Bonzini vq = vser->c_ivq; 2276e790746SPaolo Bonzini if (!virtio_queue_ready(vq)) { 2286e790746SPaolo Bonzini return 0; 2296e790746SPaolo Bonzini } 23051b19ebeSPaolo Bonzini 23151b19ebeSPaolo Bonzini elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); 23251b19ebeSPaolo Bonzini if (!elem) { 2336e790746SPaolo Bonzini return 0; 2346e790746SPaolo Bonzini } 2356e790746SPaolo Bonzini 23678820803SMichael S. Tsirkin /* TODO: detect a buffer that's too short, set NEEDS_RESET */ 23751b19ebeSPaolo Bonzini iov_from_buf(elem->in_sg, elem->in_num, 0, buf, len); 2386e790746SPaolo Bonzini 23951b19ebeSPaolo Bonzini virtqueue_push(vq, elem, len); 24076017fd2SKONRAD Frederic virtio_notify(VIRTIO_DEVICE(vser), vq); 24151b19ebeSPaolo Bonzini g_free(elem); 24251b19ebeSPaolo Bonzini 2436e790746SPaolo Bonzini return len; 2446e790746SPaolo Bonzini } 2456e790746SPaolo Bonzini 2466e790746SPaolo Bonzini static size_t send_control_event(VirtIOSerial *vser, uint32_t port_id, 2476e790746SPaolo Bonzini uint16_t event, uint16_t value) 2486e790746SPaolo Bonzini { 249e0ab7facSRusty Russell VirtIODevice *vdev = VIRTIO_DEVICE(vser); 2506e790746SPaolo Bonzini struct virtio_console_control cpkt; 2516e790746SPaolo Bonzini 252e0ab7facSRusty Russell virtio_stl_p(vdev, &cpkt.id, port_id); 253e0ab7facSRusty Russell virtio_stw_p(vdev, &cpkt.event, event); 254e0ab7facSRusty Russell virtio_stw_p(vdev, &cpkt.value, value); 2556e790746SPaolo Bonzini 2566e790746SPaolo Bonzini trace_virtio_serial_send_control_event(port_id, event, value); 2576e790746SPaolo Bonzini return send_control_msg(vser, &cpkt, sizeof(cpkt)); 2586e790746SPaolo Bonzini } 2596e790746SPaolo Bonzini 2606e790746SPaolo Bonzini /* Functions for use inside qemu to open and read from/write to ports */ 2616e790746SPaolo Bonzini int virtio_serial_open(VirtIOSerialPort *port) 2626e790746SPaolo Bonzini { 2636e790746SPaolo Bonzini /* Don't allow opening an already-open port */ 2646e790746SPaolo Bonzini if (port->host_connected) { 2656e790746SPaolo Bonzini return 0; 2666e790746SPaolo Bonzini } 2676e790746SPaolo Bonzini /* Send port open notification to the guest */ 2686e790746SPaolo Bonzini port->host_connected = true; 2696e790746SPaolo Bonzini send_control_event(port->vser, port->id, VIRTIO_CONSOLE_PORT_OPEN, 1); 2706e790746SPaolo Bonzini 2716e790746SPaolo Bonzini return 0; 2726e790746SPaolo Bonzini } 2736e790746SPaolo Bonzini 2746e790746SPaolo Bonzini int virtio_serial_close(VirtIOSerialPort *port) 2756e790746SPaolo Bonzini { 2766e790746SPaolo Bonzini port->host_connected = false; 2776e790746SPaolo Bonzini /* 2786e790746SPaolo Bonzini * If there's any data the guest sent which the app didn't 2796e790746SPaolo Bonzini * consume, reset the throttling flag and discard the data. 2806e790746SPaolo Bonzini */ 2816e790746SPaolo Bonzini port->throttled = false; 282d4c19cdeSStefan Hajnoczi discard_throttle_data(port); 28376017fd2SKONRAD Frederic discard_vq_data(port->ovq, VIRTIO_DEVICE(port->vser)); 2846e790746SPaolo Bonzini 2856e790746SPaolo Bonzini send_control_event(port->vser, port->id, VIRTIO_CONSOLE_PORT_OPEN, 0); 2866e790746SPaolo Bonzini 2876e790746SPaolo Bonzini return 0; 2886e790746SPaolo Bonzini } 2896e790746SPaolo Bonzini 2906e790746SPaolo Bonzini /* Individual ports/apps call this function to write to the guest. */ 2916e790746SPaolo Bonzini ssize_t virtio_serial_write(VirtIOSerialPort *port, const uint8_t *buf, 2926e790746SPaolo Bonzini size_t size) 2936e790746SPaolo Bonzini { 2946e790746SPaolo Bonzini if (!port || !port->host_connected || !port->guest_connected) { 2956e790746SPaolo Bonzini return 0; 2966e790746SPaolo Bonzini } 2976e790746SPaolo Bonzini return write_to_port(port, buf, size); 2986e790746SPaolo Bonzini } 2996e790746SPaolo Bonzini 3006e790746SPaolo Bonzini /* 3016e790746SPaolo Bonzini * Readiness of the guest to accept data on a port. 3026e790746SPaolo Bonzini * Returns max. data the guest can receive 3036e790746SPaolo Bonzini */ 3046e790746SPaolo Bonzini size_t virtio_serial_guest_ready(VirtIOSerialPort *port) 3056e790746SPaolo Bonzini { 30676017fd2SKONRAD Frederic VirtIODevice *vdev = VIRTIO_DEVICE(port->vser); 3076e790746SPaolo Bonzini VirtQueue *vq = port->ivq; 3086e790746SPaolo Bonzini unsigned int bytes; 3096e790746SPaolo Bonzini 3106e790746SPaolo Bonzini if (!virtio_queue_ready(vq) || 31176017fd2SKONRAD Frederic !(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK) || 3126e790746SPaolo Bonzini virtio_queue_empty(vq)) { 3136e790746SPaolo Bonzini return 0; 3146e790746SPaolo Bonzini } 3156e790746SPaolo Bonzini if (use_multiport(port->vser) && !port->guest_connected) { 3166e790746SPaolo Bonzini return 0; 3176e790746SPaolo Bonzini } 3186e790746SPaolo Bonzini virtqueue_get_avail_bytes(vq, &bytes, NULL, 4096, 0); 3196e790746SPaolo Bonzini return bytes; 3206e790746SPaolo Bonzini } 3216e790746SPaolo Bonzini 3226e790746SPaolo Bonzini static void flush_queued_data_bh(void *opaque) 3236e790746SPaolo Bonzini { 3246e790746SPaolo Bonzini VirtIOSerialPort *port = opaque; 3256e790746SPaolo Bonzini 3266e790746SPaolo Bonzini flush_queued_data(port); 3276e790746SPaolo Bonzini } 3286e790746SPaolo Bonzini 3296e790746SPaolo Bonzini void virtio_serial_throttle_port(VirtIOSerialPort *port, bool throttle) 3306e790746SPaolo Bonzini { 3316e790746SPaolo Bonzini if (!port) { 3326e790746SPaolo Bonzini return; 3336e790746SPaolo Bonzini } 3346e790746SPaolo Bonzini 3356e790746SPaolo Bonzini trace_virtio_serial_throttle_port(port->id, throttle); 3366e790746SPaolo Bonzini port->throttled = throttle; 3376e790746SPaolo Bonzini if (throttle) { 3386e790746SPaolo Bonzini return; 3396e790746SPaolo Bonzini } 3406e790746SPaolo Bonzini qemu_bh_schedule(port->bh); 3416e790746SPaolo Bonzini } 3426e790746SPaolo Bonzini 3436e790746SPaolo Bonzini /* Guest wants to notify us of some event */ 3446e790746SPaolo Bonzini static void handle_control_message(VirtIOSerial *vser, void *buf, size_t len) 3456e790746SPaolo Bonzini { 346e0ab7facSRusty Russell VirtIODevice *vdev = VIRTIO_DEVICE(vser); 3476e790746SPaolo Bonzini struct VirtIOSerialPort *port; 3486e790746SPaolo Bonzini VirtIOSerialPortClass *vsc; 3496e790746SPaolo Bonzini struct virtio_console_control cpkt, *gcpkt; 3506e790746SPaolo Bonzini uint8_t *buffer; 3516e790746SPaolo Bonzini size_t buffer_len; 3526e790746SPaolo Bonzini 3536e790746SPaolo Bonzini gcpkt = buf; 3546e790746SPaolo Bonzini 3556e790746SPaolo Bonzini if (len < sizeof(cpkt)) { 3566e790746SPaolo Bonzini /* The guest sent an invalid control packet */ 3576e790746SPaolo Bonzini return; 3586e790746SPaolo Bonzini } 3596e790746SPaolo Bonzini 360e0ab7facSRusty Russell cpkt.event = virtio_lduw_p(vdev, &gcpkt->event); 361e0ab7facSRusty Russell cpkt.value = virtio_lduw_p(vdev, &gcpkt->value); 3626e790746SPaolo Bonzini 3636e790746SPaolo Bonzini trace_virtio_serial_handle_control_message(cpkt.event, cpkt.value); 3646e790746SPaolo Bonzini 3656e790746SPaolo Bonzini if (cpkt.event == VIRTIO_CONSOLE_DEVICE_READY) { 3666e790746SPaolo Bonzini if (!cpkt.value) { 3676e790746SPaolo Bonzini error_report("virtio-serial-bus: Guest failure in adding device %s", 3686e790746SPaolo Bonzini vser->bus.qbus.name); 3696e790746SPaolo Bonzini return; 3706e790746SPaolo Bonzini } 3716e790746SPaolo Bonzini /* 3726e790746SPaolo Bonzini * The device is up, we can now tell the device about all the 3736e790746SPaolo Bonzini * ports we have here. 3746e790746SPaolo Bonzini */ 3756e790746SPaolo Bonzini QTAILQ_FOREACH(port, &vser->ports, next) { 3766e790746SPaolo Bonzini send_control_event(vser, port->id, VIRTIO_CONSOLE_PORT_ADD, 1); 3776e790746SPaolo Bonzini } 3786e790746SPaolo Bonzini return; 3796e790746SPaolo Bonzini } 3806e790746SPaolo Bonzini 381e0ab7facSRusty Russell port = find_port_by_id(vser, virtio_ldl_p(vdev, &gcpkt->id)); 3826e790746SPaolo Bonzini if (!port) { 3836e790746SPaolo Bonzini error_report("virtio-serial-bus: Unexpected port id %u for device %s", 384e0ab7facSRusty Russell virtio_ldl_p(vdev, &gcpkt->id), vser->bus.qbus.name); 3856e790746SPaolo Bonzini return; 3866e790746SPaolo Bonzini } 3876e790746SPaolo Bonzini 3886e790746SPaolo Bonzini trace_virtio_serial_handle_control_message_port(port->id); 3896e790746SPaolo Bonzini 3906e790746SPaolo Bonzini vsc = VIRTIO_SERIAL_PORT_GET_CLASS(port); 3916e790746SPaolo Bonzini 3926e790746SPaolo Bonzini switch(cpkt.event) { 3936e790746SPaolo Bonzini case VIRTIO_CONSOLE_PORT_READY: 3946e790746SPaolo Bonzini if (!cpkt.value) { 3956e790746SPaolo Bonzini error_report("virtio-serial-bus: Guest failure in adding port %u for device %s", 3966e790746SPaolo Bonzini port->id, vser->bus.qbus.name); 3976e790746SPaolo Bonzini break; 3986e790746SPaolo Bonzini } 3996e790746SPaolo Bonzini /* 4006e790746SPaolo Bonzini * Now that we know the guest asked for the port name, we're 4016e790746SPaolo Bonzini * sure the guest has initialised whatever state is necessary 4026e790746SPaolo Bonzini * for this port. Now's a good time to let the guest know if 4036e790746SPaolo Bonzini * this port is a console port so that the guest can hook it 4046e790746SPaolo Bonzini * up to hvc. 4056e790746SPaolo Bonzini */ 4066e790746SPaolo Bonzini if (vsc->is_console) { 4076e790746SPaolo Bonzini send_control_event(vser, port->id, VIRTIO_CONSOLE_CONSOLE_PORT, 1); 4086e790746SPaolo Bonzini } 4096e790746SPaolo Bonzini 4106e790746SPaolo Bonzini if (port->name) { 411e0ab7facSRusty Russell virtio_stl_p(vdev, &cpkt.id, port->id); 412e0ab7facSRusty Russell virtio_stw_p(vdev, &cpkt.event, VIRTIO_CONSOLE_PORT_NAME); 413e0ab7facSRusty Russell virtio_stw_p(vdev, &cpkt.value, 1); 4146e790746SPaolo Bonzini 4156e790746SPaolo Bonzini buffer_len = sizeof(cpkt) + strlen(port->name) + 1; 4166e790746SPaolo Bonzini buffer = g_malloc(buffer_len); 4176e790746SPaolo Bonzini 4186e790746SPaolo Bonzini memcpy(buffer, &cpkt, sizeof(cpkt)); 4196e790746SPaolo Bonzini memcpy(buffer + sizeof(cpkt), port->name, strlen(port->name)); 4206e790746SPaolo Bonzini buffer[buffer_len - 1] = 0; 4216e790746SPaolo Bonzini 4226e790746SPaolo Bonzini send_control_msg(vser, buffer, buffer_len); 4236e790746SPaolo Bonzini g_free(buffer); 4246e790746SPaolo Bonzini } 4256e790746SPaolo Bonzini 4266e790746SPaolo Bonzini if (port->host_connected) { 4276e790746SPaolo Bonzini send_control_event(vser, port->id, VIRTIO_CONSOLE_PORT_OPEN, 1); 4286e790746SPaolo Bonzini } 4296e790746SPaolo Bonzini 4306e790746SPaolo Bonzini /* 4316e790746SPaolo Bonzini * When the guest has asked us for this information it means 4326e790746SPaolo Bonzini * the guest is all setup and has its virtqueues 4336e790746SPaolo Bonzini * initialised. If some app is interested in knowing about 4346e790746SPaolo Bonzini * this event, let it know. 4356e790746SPaolo Bonzini */ 4366e790746SPaolo Bonzini if (vsc->guest_ready) { 4376e790746SPaolo Bonzini vsc->guest_ready(port); 4386e790746SPaolo Bonzini } 4396e790746SPaolo Bonzini break; 4406e790746SPaolo Bonzini 4416e790746SPaolo Bonzini case VIRTIO_CONSOLE_PORT_OPEN: 4426e790746SPaolo Bonzini port->guest_connected = cpkt.value; 4436e790746SPaolo Bonzini if (vsc->set_guest_connected) { 4446e790746SPaolo Bonzini /* Send the guest opened notification if an app is interested */ 4456e790746SPaolo Bonzini vsc->set_guest_connected(port, cpkt.value); 4466e790746SPaolo Bonzini } 4476e790746SPaolo Bonzini break; 4486e790746SPaolo Bonzini } 4496e790746SPaolo Bonzini } 4506e790746SPaolo Bonzini 4516e790746SPaolo Bonzini static void control_in(VirtIODevice *vdev, VirtQueue *vq) 4526e790746SPaolo Bonzini { 4536e790746SPaolo Bonzini } 4546e790746SPaolo Bonzini 4556e790746SPaolo Bonzini static void control_out(VirtIODevice *vdev, VirtQueue *vq) 4566e790746SPaolo Bonzini { 45751b19ebeSPaolo Bonzini VirtQueueElement *elem; 4586e790746SPaolo Bonzini VirtIOSerial *vser; 4596e790746SPaolo Bonzini uint8_t *buf; 4606e790746SPaolo Bonzini size_t len; 4616e790746SPaolo Bonzini 46276017fd2SKONRAD Frederic vser = VIRTIO_SERIAL(vdev); 4636e790746SPaolo Bonzini 4646e790746SPaolo Bonzini len = 0; 4656e790746SPaolo Bonzini buf = NULL; 46651b19ebeSPaolo Bonzini for (;;) { 4676e790746SPaolo Bonzini size_t cur_len; 4686e790746SPaolo Bonzini 46951b19ebeSPaolo Bonzini elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); 47051b19ebeSPaolo Bonzini if (!elem) { 47151b19ebeSPaolo Bonzini break; 47251b19ebeSPaolo Bonzini } 47351b19ebeSPaolo Bonzini 47451b19ebeSPaolo Bonzini cur_len = iov_size(elem->out_sg, elem->out_num); 4756e790746SPaolo Bonzini /* 4766e790746SPaolo Bonzini * Allocate a new buf only if we didn't have one previously or 4776e790746SPaolo Bonzini * if the size of the buf differs 4786e790746SPaolo Bonzini */ 4796e790746SPaolo Bonzini if (cur_len > len) { 4806e790746SPaolo Bonzini g_free(buf); 4816e790746SPaolo Bonzini 4826e790746SPaolo Bonzini buf = g_malloc(cur_len); 4836e790746SPaolo Bonzini len = cur_len; 4846e790746SPaolo Bonzini } 48551b19ebeSPaolo Bonzini iov_to_buf(elem->out_sg, elem->out_num, 0, buf, cur_len); 4866e790746SPaolo Bonzini 4876e790746SPaolo Bonzini handle_control_message(vser, buf, cur_len); 48851b19ebeSPaolo Bonzini virtqueue_push(vq, elem, 0); 48951b19ebeSPaolo Bonzini g_free(elem); 4906e790746SPaolo Bonzini } 4916e790746SPaolo Bonzini g_free(buf); 4926e790746SPaolo Bonzini virtio_notify(vdev, vq); 4936e790746SPaolo Bonzini } 4946e790746SPaolo Bonzini 4956e790746SPaolo Bonzini /* Guest wrote something to some port. */ 4966e790746SPaolo Bonzini static void handle_output(VirtIODevice *vdev, VirtQueue *vq) 4976e790746SPaolo Bonzini { 4986e790746SPaolo Bonzini VirtIOSerial *vser; 4996e790746SPaolo Bonzini VirtIOSerialPort *port; 5006e790746SPaolo Bonzini 50176017fd2SKONRAD Frederic vser = VIRTIO_SERIAL(vdev); 5026e790746SPaolo Bonzini port = find_port_by_vq(vser, vq); 5036e790746SPaolo Bonzini 5046e790746SPaolo Bonzini if (!port || !port->host_connected) { 5056e790746SPaolo Bonzini discard_vq_data(vq, vdev); 5066e790746SPaolo Bonzini return; 5076e790746SPaolo Bonzini } 5086e790746SPaolo Bonzini 5096e790746SPaolo Bonzini if (!port->throttled) { 5106e790746SPaolo Bonzini do_flush_queued_data(port, vq, vdev); 5116e790746SPaolo Bonzini return; 5126e790746SPaolo Bonzini } 5136e790746SPaolo Bonzini } 5146e790746SPaolo Bonzini 5156e790746SPaolo Bonzini static void handle_input(VirtIODevice *vdev, VirtQueue *vq) 5166e790746SPaolo Bonzini { 5174add73aaSAmit Shah /* 5184add73aaSAmit Shah * Users of virtio-serial would like to know when guest becomes 5194add73aaSAmit Shah * writable again -- i.e. if a vq had stuff queued up and the 5204add73aaSAmit Shah * guest wasn't reading at all, the host would not be able to 5214add73aaSAmit Shah * write to the vq anymore. Once the guest reads off something, 5224add73aaSAmit Shah * we can start queueing things up again. However, this call is 5234add73aaSAmit Shah * made for each buffer addition by the guest -- even though free 5244add73aaSAmit Shah * buffers existed prior to the current buffer addition. This is 5254add73aaSAmit Shah * done so as not to maintain previous state, which will need 5264add73aaSAmit Shah * additional live-migration-related changes. 5274add73aaSAmit Shah */ 5284add73aaSAmit Shah VirtIOSerial *vser; 5294add73aaSAmit Shah VirtIOSerialPort *port; 5304add73aaSAmit Shah VirtIOSerialPortClass *vsc; 5314add73aaSAmit Shah 5324add73aaSAmit Shah vser = VIRTIO_SERIAL(vdev); 5334add73aaSAmit Shah port = find_port_by_vq(vser, vq); 5344add73aaSAmit Shah 5354add73aaSAmit Shah if (!port) { 5364add73aaSAmit Shah return; 5374add73aaSAmit Shah } 5384add73aaSAmit Shah vsc = VIRTIO_SERIAL_PORT_GET_CLASS(port); 5394add73aaSAmit Shah 5404add73aaSAmit Shah /* 5414add73aaSAmit Shah * If guest_connected is false, this call is being made by the 5424add73aaSAmit Shah * early-boot queueing up of descriptors, which is just noise for 5434add73aaSAmit Shah * the host apps -- don't disturb them in that case. 5444add73aaSAmit Shah */ 5454add73aaSAmit Shah if (port->guest_connected && port->host_connected && vsc->guest_writable) { 5464add73aaSAmit Shah vsc->guest_writable(port); 5474add73aaSAmit Shah } 5486e790746SPaolo Bonzini } 5496e790746SPaolo Bonzini 5509d5b731dSJason Wang static uint64_t get_features(VirtIODevice *vdev, uint64_t features, 5519d5b731dSJason Wang Error **errp) 5526e790746SPaolo Bonzini { 5536e790746SPaolo Bonzini VirtIOSerial *vser; 5546e790746SPaolo Bonzini 55576017fd2SKONRAD Frederic vser = VIRTIO_SERIAL(vdev); 5566e790746SPaolo Bonzini 557a06b1daeSSascha Silbe features |= vser->host_features; 5586e790746SPaolo Bonzini if (vser->bus.max_nr_ports > 1) { 5590cd09c3aSCornelia Huck virtio_add_feature(&features, VIRTIO_CONSOLE_F_MULTIPORT); 5606e790746SPaolo Bonzini } 5616e790746SPaolo Bonzini return features; 5626e790746SPaolo Bonzini } 5636e790746SPaolo Bonzini 5646e790746SPaolo Bonzini /* Guest requested config info */ 5656e790746SPaolo Bonzini static void get_config(VirtIODevice *vdev, uint8_t *config_data) 5666e790746SPaolo Bonzini { 56708f432aaSDavid Gibson VirtIOSerial *vser = VIRTIO_SERIAL(vdev); 56808f432aaSDavid Gibson struct virtio_console_config *config = 56908f432aaSDavid Gibson (struct virtio_console_config *)config_data; 5706e790746SPaolo Bonzini 57108f432aaSDavid Gibson config->cols = 0; 57208f432aaSDavid Gibson config->rows = 0; 57308f432aaSDavid Gibson config->max_nr_ports = virtio_tswap32(vdev, 57408f432aaSDavid Gibson vser->serial.max_virtserial_ports); 5756e790746SPaolo Bonzini } 5766e790746SPaolo Bonzini 57709da01c3SSascha Silbe /* Guest sent new config info */ 57809da01c3SSascha Silbe static void set_config(VirtIODevice *vdev, const uint8_t *config_data) 57909da01c3SSascha Silbe { 58009da01c3SSascha Silbe VirtIOSerial *vser = VIRTIO_SERIAL(vdev); 58109da01c3SSascha Silbe struct virtio_console_config *config = 58209da01c3SSascha Silbe (struct virtio_console_config *)config_data; 58309da01c3SSascha Silbe uint8_t emerg_wr_lo = le32_to_cpu(config->emerg_wr); 58409da01c3SSascha Silbe VirtIOSerialPort *port = find_first_connected_console(vser); 58509da01c3SSascha Silbe VirtIOSerialPortClass *vsc; 58609da01c3SSascha Silbe 58709da01c3SSascha Silbe if (!config->emerg_wr) { 58809da01c3SSascha Silbe return; 58909da01c3SSascha Silbe } 59009da01c3SSascha Silbe /* Make sure we don't misdetect an emergency write when the guest 59109da01c3SSascha Silbe * does a short config write after an emergency write. */ 59209da01c3SSascha Silbe config->emerg_wr = 0; 59309da01c3SSascha Silbe if (!port) { 59409da01c3SSascha Silbe return; 59509da01c3SSascha Silbe } 59609da01c3SSascha Silbe vsc = VIRTIO_SERIAL_PORT_GET_CLASS(port); 59709da01c3SSascha Silbe (void)vsc->have_data(port, &emerg_wr_lo, 1); 59809da01c3SSascha Silbe } 59909da01c3SSascha Silbe 6006e790746SPaolo Bonzini static void guest_reset(VirtIOSerial *vser) 6016e790746SPaolo Bonzini { 6026e790746SPaolo Bonzini VirtIOSerialPort *port; 6036e790746SPaolo Bonzini VirtIOSerialPortClass *vsc; 6046e790746SPaolo Bonzini 6056e790746SPaolo Bonzini QTAILQ_FOREACH(port, &vser->ports, next) { 6066e790746SPaolo Bonzini vsc = VIRTIO_SERIAL_PORT_GET_CLASS(port); 607d4c19cdeSStefan Hajnoczi 608d4c19cdeSStefan Hajnoczi discard_throttle_data(port); 609d4c19cdeSStefan Hajnoczi 6106e790746SPaolo Bonzini if (port->guest_connected) { 6116e790746SPaolo Bonzini port->guest_connected = false; 6126e790746SPaolo Bonzini if (vsc->set_guest_connected) { 6136e790746SPaolo Bonzini vsc->set_guest_connected(port, false); 6146e790746SPaolo Bonzini } 6156e790746SPaolo Bonzini } 6166e790746SPaolo Bonzini } 6176e790746SPaolo Bonzini } 6186e790746SPaolo Bonzini 6196e790746SPaolo Bonzini static void set_status(VirtIODevice *vdev, uint8_t status) 6206e790746SPaolo Bonzini { 6216e790746SPaolo Bonzini VirtIOSerial *vser; 6226e790746SPaolo Bonzini VirtIOSerialPort *port; 6236e790746SPaolo Bonzini 62476017fd2SKONRAD Frederic vser = VIRTIO_SERIAL(vdev); 6256e790746SPaolo Bonzini port = find_port_by_id(vser, 0); 6266e790746SPaolo Bonzini 6276e790746SPaolo Bonzini if (port && !use_multiport(port->vser) 6286e790746SPaolo Bonzini && (status & VIRTIO_CONFIG_S_DRIVER_OK)) { 6296e790746SPaolo Bonzini /* 6306e790746SPaolo Bonzini * Non-multiport guests won't be able to tell us guest 6316e790746SPaolo Bonzini * open/close status. Such guests can only have a port at id 6326e790746SPaolo Bonzini * 0, so set guest_connected for such ports as soon as guest 6336e790746SPaolo Bonzini * is up. 6346e790746SPaolo Bonzini */ 6356e790746SPaolo Bonzini port->guest_connected = true; 6366e790746SPaolo Bonzini } 6376e790746SPaolo Bonzini if (!(status & VIRTIO_CONFIG_S_DRIVER_OK)) { 6386e790746SPaolo Bonzini guest_reset(vser); 6396e790746SPaolo Bonzini } 6406e790746SPaolo Bonzini } 6416e790746SPaolo Bonzini 6426e790746SPaolo Bonzini static void vser_reset(VirtIODevice *vdev) 6436e790746SPaolo Bonzini { 6446e790746SPaolo Bonzini VirtIOSerial *vser; 6456e790746SPaolo Bonzini 64676017fd2SKONRAD Frederic vser = VIRTIO_SERIAL(vdev); 6476e790746SPaolo Bonzini guest_reset(vser); 6486e790746SPaolo Bonzini } 6496e790746SPaolo Bonzini 65013c6855aSGreg Kurz static void virtio_serial_save_device(VirtIODevice *vdev, QEMUFile *f) 65113c6855aSGreg Kurz { 65213c6855aSGreg Kurz VirtIOSerial *s = VIRTIO_SERIAL(vdev); 6536e790746SPaolo Bonzini VirtIOSerialPort *port; 6546e790746SPaolo Bonzini uint32_t nr_active_ports; 6556e790746SPaolo Bonzini unsigned int i, max_nr_ports; 65608f432aaSDavid Gibson struct virtio_console_config config; 6576e790746SPaolo Bonzini 65808f432aaSDavid Gibson /* The config space (ignored on the far end in current versions) */ 65908f432aaSDavid Gibson get_config(vdev, (uint8_t *)&config); 66008f432aaSDavid Gibson qemu_put_be16s(f, &config.cols); 66108f432aaSDavid Gibson qemu_put_be16s(f, &config.rows); 66208f432aaSDavid Gibson qemu_put_be32s(f, &config.max_nr_ports); 6636e790746SPaolo Bonzini 6646e790746SPaolo Bonzini /* The ports map */ 665f2f6e00bSDavid Gibson max_nr_ports = s->serial.max_virtserial_ports; 666*7b9a27cdSMarc-André Lureau for (i = 0; i < DIV_ROUND_UP(max_nr_ports, 32); i++) { 6676e790746SPaolo Bonzini qemu_put_be32s(f, &s->ports_map[i]); 6686e790746SPaolo Bonzini } 6696e790746SPaolo Bonzini 6706e790746SPaolo Bonzini /* Ports */ 6716e790746SPaolo Bonzini 6726e790746SPaolo Bonzini nr_active_ports = 0; 6736e790746SPaolo Bonzini QTAILQ_FOREACH(port, &s->ports, next) { 6746e790746SPaolo Bonzini nr_active_ports++; 6756e790746SPaolo Bonzini } 6766e790746SPaolo Bonzini 6776e790746SPaolo Bonzini qemu_put_be32s(f, &nr_active_ports); 6786e790746SPaolo Bonzini 6796e790746SPaolo Bonzini /* 6806e790746SPaolo Bonzini * Items in struct VirtIOSerialPort. 6816e790746SPaolo Bonzini */ 6826e790746SPaolo Bonzini QTAILQ_FOREACH(port, &s->ports, next) { 6836e790746SPaolo Bonzini uint32_t elem_popped; 6846e790746SPaolo Bonzini 6856e790746SPaolo Bonzini qemu_put_be32s(f, &port->id); 6866e790746SPaolo Bonzini qemu_put_byte(f, port->guest_connected); 6876e790746SPaolo Bonzini qemu_put_byte(f, port->host_connected); 6886e790746SPaolo Bonzini 6896e790746SPaolo Bonzini elem_popped = 0; 69051b19ebeSPaolo Bonzini if (port->elem) { 6916e790746SPaolo Bonzini elem_popped = 1; 6926e790746SPaolo Bonzini } 6936e790746SPaolo Bonzini qemu_put_be32s(f, &elem_popped); 6946e790746SPaolo Bonzini if (elem_popped) { 6956e790746SPaolo Bonzini qemu_put_be32s(f, &port->iov_idx); 6966e790746SPaolo Bonzini qemu_put_be64s(f, &port->iov_offset); 697ab281c17SPaolo Bonzini qemu_put_virtqueue_element(f, port->elem); 6986e790746SPaolo Bonzini } 6996e790746SPaolo Bonzini } 7006e790746SPaolo Bonzini } 7016e790746SPaolo Bonzini 7026e790746SPaolo Bonzini static void virtio_serial_post_load_timer_cb(void *opaque) 7036e790746SPaolo Bonzini { 7046e790746SPaolo Bonzini uint32_t i; 70576017fd2SKONRAD Frederic VirtIOSerial *s = VIRTIO_SERIAL(opaque); 7066e790746SPaolo Bonzini VirtIOSerialPort *port; 7076e790746SPaolo Bonzini uint8_t host_connected; 7086e790746SPaolo Bonzini VirtIOSerialPortClass *vsc; 7096e790746SPaolo Bonzini 7106e790746SPaolo Bonzini if (!s->post_load) { 7116e790746SPaolo Bonzini return; 7126e790746SPaolo Bonzini } 7136e790746SPaolo Bonzini for (i = 0 ; i < s->post_load->nr_active_ports; ++i) { 7146e790746SPaolo Bonzini port = s->post_load->connected[i].port; 7156e790746SPaolo Bonzini host_connected = s->post_load->connected[i].host_connected; 7166e790746SPaolo Bonzini if (host_connected != port->host_connected) { 7176e790746SPaolo Bonzini /* 7186e790746SPaolo Bonzini * We have to let the guest know of the host connection 7196e790746SPaolo Bonzini * status change 7206e790746SPaolo Bonzini */ 7216e790746SPaolo Bonzini send_control_event(s, port->id, VIRTIO_CONSOLE_PORT_OPEN, 7226e790746SPaolo Bonzini port->host_connected); 7236e790746SPaolo Bonzini } 7246e790746SPaolo Bonzini vsc = VIRTIO_SERIAL_PORT_GET_CLASS(port); 7256e790746SPaolo Bonzini if (vsc->set_guest_connected) { 7266e790746SPaolo Bonzini vsc->set_guest_connected(port, port->guest_connected); 7276e790746SPaolo Bonzini } 7286e790746SPaolo Bonzini } 7296e790746SPaolo Bonzini g_free(s->post_load->connected); 730bdf4c4ecSzhanghailiang timer_del(s->post_load->timer); 731bc72ad67SAlex Bligh timer_free(s->post_load->timer); 7326e790746SPaolo Bonzini g_free(s->post_load); 7336e790746SPaolo Bonzini s->post_load = NULL; 7346e790746SPaolo Bonzini } 7356e790746SPaolo Bonzini 73671945ae1SDr. David Alan Gilbert static int fetch_active_ports_list(QEMUFile *f, 7376e790746SPaolo Bonzini VirtIOSerial *s, uint32_t nr_active_ports) 7386e790746SPaolo Bonzini { 7398607f5c3SJason Wang VirtIODevice *vdev = VIRTIO_DEVICE(s); 7406e790746SPaolo Bonzini uint32_t i; 7416e790746SPaolo Bonzini 7426e790746SPaolo Bonzini s->post_load = g_malloc0(sizeof(*s->post_load)); 7436e790746SPaolo Bonzini s->post_load->nr_active_ports = nr_active_ports; 7446e790746SPaolo Bonzini s->post_load->connected = 7456e790746SPaolo Bonzini g_malloc0(sizeof(*s->post_load->connected) * nr_active_ports); 7466e790746SPaolo Bonzini 747bc72ad67SAlex Bligh s->post_load->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, 7486e790746SPaolo Bonzini virtio_serial_post_load_timer_cb, 7496e790746SPaolo Bonzini s); 7506e790746SPaolo Bonzini 7516e790746SPaolo Bonzini /* Items in struct VirtIOSerialPort */ 7526e790746SPaolo Bonzini for (i = 0; i < nr_active_ports; i++) { 7536e790746SPaolo Bonzini VirtIOSerialPort *port; 75471945ae1SDr. David Alan Gilbert uint32_t elem_popped; 7556e790746SPaolo Bonzini uint32_t id; 7566e790746SPaolo Bonzini 7576e790746SPaolo Bonzini id = qemu_get_be32(f); 7586e790746SPaolo Bonzini port = find_port_by_id(s, id); 7596e790746SPaolo Bonzini if (!port) { 7606e790746SPaolo Bonzini return -EINVAL; 7616e790746SPaolo Bonzini } 7626e790746SPaolo Bonzini 7636e790746SPaolo Bonzini port->guest_connected = qemu_get_byte(f); 7646e790746SPaolo Bonzini s->post_load->connected[i].port = port; 7656e790746SPaolo Bonzini s->post_load->connected[i].host_connected = qemu_get_byte(f); 7666e790746SPaolo Bonzini 7676e790746SPaolo Bonzini qemu_get_be32s(f, &elem_popped); 7686e790746SPaolo Bonzini if (elem_popped) { 7696e790746SPaolo Bonzini qemu_get_be32s(f, &port->iov_idx); 7706e790746SPaolo Bonzini qemu_get_be64s(f, &port->iov_offset); 7716e790746SPaolo Bonzini 772ab281c17SPaolo Bonzini port->elem = 7738607f5c3SJason Wang qemu_get_virtqueue_element(vdev, f, sizeof(VirtQueueElement)); 7746e790746SPaolo Bonzini 7756e790746SPaolo Bonzini /* 7766e790746SPaolo Bonzini * Port was throttled on source machine. Let's 7776e790746SPaolo Bonzini * unthrottle it here so data starts flowing again. 7786e790746SPaolo Bonzini */ 7796e790746SPaolo Bonzini virtio_serial_throttle_port(port, false); 7806e790746SPaolo Bonzini } 7816e790746SPaolo Bonzini } 782bc72ad67SAlex Bligh timer_mod(s->post_load->timer, 1); 7836e790746SPaolo Bonzini return 0; 7846e790746SPaolo Bonzini } 7856e790746SPaolo Bonzini 78613c6855aSGreg Kurz static int virtio_serial_load_device(VirtIODevice *vdev, QEMUFile *f, 78713c6855aSGreg Kurz int version_id) 78813c6855aSGreg Kurz { 78913c6855aSGreg Kurz VirtIOSerial *s = VIRTIO_SERIAL(vdev); 79013c6855aSGreg Kurz uint32_t max_nr_ports, nr_active_ports, ports_map; 79113c6855aSGreg Kurz unsigned int i; 79213c6855aSGreg Kurz int ret; 79313c6855aSGreg Kurz uint32_t tmp; 79413c6855aSGreg Kurz 795e38e943aSAlexander Graf /* Unused */ 796e38e943aSAlexander Graf qemu_get_be16s(f, (uint16_t *) &tmp); 797e38e943aSAlexander Graf qemu_get_be16s(f, (uint16_t *) &tmp); 798e38e943aSAlexander Graf qemu_get_be32s(f, &tmp); 7996e790746SPaolo Bonzini 800f2f6e00bSDavid Gibson max_nr_ports = s->serial.max_virtserial_ports; 801*7b9a27cdSMarc-André Lureau for (i = 0; i < DIV_ROUND_UP(max_nr_ports, 32); i++) { 8026e790746SPaolo Bonzini qemu_get_be32s(f, &ports_map); 8036e790746SPaolo Bonzini 8046e790746SPaolo Bonzini if (ports_map != s->ports_map[i]) { 8056e790746SPaolo Bonzini /* 8066e790746SPaolo Bonzini * Ports active on source and destination don't 8076e790746SPaolo Bonzini * match. Fail migration. 8086e790746SPaolo Bonzini */ 8096e790746SPaolo Bonzini return -EINVAL; 8106e790746SPaolo Bonzini } 8116e790746SPaolo Bonzini } 8126e790746SPaolo Bonzini 8136e790746SPaolo Bonzini qemu_get_be32s(f, &nr_active_ports); 8146e790746SPaolo Bonzini 8156e790746SPaolo Bonzini if (nr_active_ports) { 81671945ae1SDr. David Alan Gilbert ret = fetch_active_ports_list(f, s, nr_active_ports); 8176e790746SPaolo Bonzini if (ret) { 8186e790746SPaolo Bonzini return ret; 8196e790746SPaolo Bonzini } 8206e790746SPaolo Bonzini } 8216e790746SPaolo Bonzini return 0; 8226e790746SPaolo Bonzini } 8236e790746SPaolo Bonzini 8246e790746SPaolo Bonzini static void virtser_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent); 8256e790746SPaolo Bonzini 8266e790746SPaolo Bonzini static Property virtser_props[] = { 8276e790746SPaolo Bonzini DEFINE_PROP_UINT32("nr", VirtIOSerialPort, id, VIRTIO_CONSOLE_BAD_ID), 8286e790746SPaolo Bonzini DEFINE_PROP_STRING("name", VirtIOSerialPort, name), 8296e790746SPaolo Bonzini DEFINE_PROP_END_OF_LIST() 8306e790746SPaolo Bonzini }; 8316e790746SPaolo Bonzini 8326e790746SPaolo Bonzini #define TYPE_VIRTIO_SERIAL_BUS "virtio-serial-bus" 8336e790746SPaolo Bonzini #define VIRTIO_SERIAL_BUS(obj) \ 8346e790746SPaolo Bonzini OBJECT_CHECK(VirtIOSerialBus, (obj), TYPE_VIRTIO_SERIAL_BUS) 8356e790746SPaolo Bonzini 8366e790746SPaolo Bonzini static void virtser_bus_class_init(ObjectClass *klass, void *data) 8376e790746SPaolo Bonzini { 8386e790746SPaolo Bonzini BusClass *k = BUS_CLASS(klass); 8396e790746SPaolo Bonzini k->print_dev = virtser_bus_dev_print; 8406e790746SPaolo Bonzini } 8416e790746SPaolo Bonzini 8426e790746SPaolo Bonzini static const TypeInfo virtser_bus_info = { 8436e790746SPaolo Bonzini .name = TYPE_VIRTIO_SERIAL_BUS, 8446e790746SPaolo Bonzini .parent = TYPE_BUS, 8456e790746SPaolo Bonzini .instance_size = sizeof(VirtIOSerialBus), 8466e790746SPaolo Bonzini .class_init = virtser_bus_class_init, 8476e790746SPaolo Bonzini }; 8486e790746SPaolo Bonzini 8496e790746SPaolo Bonzini static void virtser_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent) 8506e790746SPaolo Bonzini { 851d9eb0be2SCao jin VirtIOSerialPort *port = VIRTIO_SERIAL_PORT(qdev); 8526e790746SPaolo Bonzini 8536e790746SPaolo Bonzini monitor_printf(mon, "%*sport %d, guest %s, host %s, throttle %s\n", 8546e790746SPaolo Bonzini indent, "", port->id, 8556e790746SPaolo Bonzini port->guest_connected ? "on" : "off", 8566e790746SPaolo Bonzini port->host_connected ? "on" : "off", 8576e790746SPaolo Bonzini port->throttled ? "on" : "off"); 8586e790746SPaolo Bonzini } 8596e790746SPaolo Bonzini 8606e790746SPaolo Bonzini /* This function is only used if a port id is not provided by the user */ 8616e790746SPaolo Bonzini static uint32_t find_free_port_id(VirtIOSerial *vser) 8626e790746SPaolo Bonzini { 8636e790746SPaolo Bonzini unsigned int i, max_nr_ports; 8646e790746SPaolo Bonzini 865f2f6e00bSDavid Gibson max_nr_ports = vser->serial.max_virtserial_ports; 866*7b9a27cdSMarc-André Lureau for (i = 0; i < DIV_ROUND_UP(max_nr_ports, 32); i++) { 867bd2a8884SStefan Hajnoczi uint32_t map, zeroes; 8686e790746SPaolo Bonzini 8696e790746SPaolo Bonzini map = vser->ports_map[i]; 870bd2a8884SStefan Hajnoczi zeroes = ctz32(~map); 871bd2a8884SStefan Hajnoczi if (zeroes != 32) { 872bd2a8884SStefan Hajnoczi return zeroes + i * 32; 8736e790746SPaolo Bonzini } 8746e790746SPaolo Bonzini } 8756e790746SPaolo Bonzini return VIRTIO_CONSOLE_BAD_ID; 8766e790746SPaolo Bonzini } 8776e790746SPaolo Bonzini 8786e790746SPaolo Bonzini static void mark_port_added(VirtIOSerial *vser, uint32_t port_id) 8796e790746SPaolo Bonzini { 8806e790746SPaolo Bonzini unsigned int i; 8816e790746SPaolo Bonzini 8826e790746SPaolo Bonzini i = port_id / 32; 8836e790746SPaolo Bonzini vser->ports_map[i] |= 1U << (port_id % 32); 8846e790746SPaolo Bonzini } 8856e790746SPaolo Bonzini 8866e790746SPaolo Bonzini static void add_port(VirtIOSerial *vser, uint32_t port_id) 8876e790746SPaolo Bonzini { 8886e790746SPaolo Bonzini mark_port_added(vser, port_id); 8896e790746SPaolo Bonzini send_control_event(vser, port_id, VIRTIO_CONSOLE_PORT_ADD, 1); 8906e790746SPaolo Bonzini } 8916e790746SPaolo Bonzini 8926e790746SPaolo Bonzini static void remove_port(VirtIOSerial *vser, uint32_t port_id) 8936e790746SPaolo Bonzini { 8946e790746SPaolo Bonzini VirtIOSerialPort *port; 89557d84cf3SAmit Shah 89657d84cf3SAmit Shah /* 89757d84cf3SAmit Shah * Don't mark port 0 removed -- we explicitly reserve it for 89857d84cf3SAmit Shah * backward compat with older guests, ensure a virtconsole device 89957d84cf3SAmit Shah * unplug retains the reservation. 90057d84cf3SAmit Shah */ 90157d84cf3SAmit Shah if (port_id) { 9026e790746SPaolo Bonzini unsigned int i; 9036e790746SPaolo Bonzini 9046e790746SPaolo Bonzini i = port_id / 32; 9056e790746SPaolo Bonzini vser->ports_map[i] &= ~(1U << (port_id % 32)); 90657d84cf3SAmit Shah } 9076e790746SPaolo Bonzini 9086e790746SPaolo Bonzini port = find_port_by_id(vser, port_id); 9096e790746SPaolo Bonzini /* 9106e790746SPaolo Bonzini * This function is only called from qdev's unplug callback; if we 9116e790746SPaolo Bonzini * get a NULL port here, we're in trouble. 9126e790746SPaolo Bonzini */ 9136e790746SPaolo Bonzini assert(port); 9146e790746SPaolo Bonzini 9156e790746SPaolo Bonzini /* Flush out any unconsumed buffers first */ 916d4c19cdeSStefan Hajnoczi discard_throttle_data(port); 91776017fd2SKONRAD Frederic discard_vq_data(port->ovq, VIRTIO_DEVICE(port->vser)); 9186e790746SPaolo Bonzini 9196e790746SPaolo Bonzini send_control_event(vser, port->id, VIRTIO_CONSOLE_PORT_REMOVE, 1); 9206e790746SPaolo Bonzini } 9216e790746SPaolo Bonzini 9222ef66625SAndreas Färber static void virtser_port_device_realize(DeviceState *dev, Error **errp) 9236e790746SPaolo Bonzini { 9242ef66625SAndreas Färber VirtIOSerialPort *port = VIRTIO_SERIAL_PORT(dev); 9256e790746SPaolo Bonzini VirtIOSerialPortClass *vsc = VIRTIO_SERIAL_PORT_GET_CLASS(port); 9262ef66625SAndreas Färber VirtIOSerialBus *bus = VIRTIO_SERIAL_BUS(qdev_get_parent_bus(dev)); 9272ef66625SAndreas Färber int max_nr_ports; 9286e790746SPaolo Bonzini bool plugging_port0; 9292ef66625SAndreas Färber Error *err = NULL; 9306e790746SPaolo Bonzini 9316e790746SPaolo Bonzini port->vser = bus->vser; 9326e790746SPaolo Bonzini port->bh = qemu_bh_new(flush_queued_data_bh, port); 9336e790746SPaolo Bonzini 9346e790746SPaolo Bonzini assert(vsc->have_data); 9356e790746SPaolo Bonzini 9366e790746SPaolo Bonzini /* 9376e790746SPaolo Bonzini * Is the first console port we're seeing? If so, put it up at 9386e790746SPaolo Bonzini * location 0. This is done for backward compatibility (old 9396e790746SPaolo Bonzini * kernel, new qemu). 9406e790746SPaolo Bonzini */ 9416e790746SPaolo Bonzini plugging_port0 = vsc->is_console && !find_port_by_id(port->vser, 0); 9426e790746SPaolo Bonzini 9436e790746SPaolo Bonzini if (find_port_by_id(port->vser, port->id)) { 9442ef66625SAndreas Färber error_setg(errp, "virtio-serial-bus: A port already exists at id %u", 9456e790746SPaolo Bonzini port->id); 9462ef66625SAndreas Färber return; 9476e790746SPaolo Bonzini } 9486e790746SPaolo Bonzini 9497eb73114SMarc-André Lureau if (port->name != NULL && find_port_by_name(port->name)) { 950d0a0bfe6SAmit Shah error_setg(errp, "virtio-serial-bus: A port already exists by name %s", 951d0a0bfe6SAmit Shah port->name); 952d0a0bfe6SAmit Shah return; 953d0a0bfe6SAmit Shah } 954d0a0bfe6SAmit Shah 9556e790746SPaolo Bonzini if (port->id == VIRTIO_CONSOLE_BAD_ID) { 9566e790746SPaolo Bonzini if (plugging_port0) { 9576e790746SPaolo Bonzini port->id = 0; 9586e790746SPaolo Bonzini } else { 9596e790746SPaolo Bonzini port->id = find_free_port_id(port->vser); 9606e790746SPaolo Bonzini if (port->id == VIRTIO_CONSOLE_BAD_ID) { 9612ef66625SAndreas Färber error_setg(errp, "virtio-serial-bus: Maximum port limit for " 9622ef66625SAndreas Färber "this device reached"); 9632ef66625SAndreas Färber return; 9646e790746SPaolo Bonzini } 9656e790746SPaolo Bonzini } 9666e790746SPaolo Bonzini } 9676e790746SPaolo Bonzini 968f2f6e00bSDavid Gibson max_nr_ports = port->vser->serial.max_virtserial_ports; 9696e790746SPaolo Bonzini if (port->id >= max_nr_ports) { 9702ef66625SAndreas Färber error_setg(errp, "virtio-serial-bus: Out-of-range port id specified, " 9712ef66625SAndreas Färber "max. allowed: %u", max_nr_ports - 1); 9722ef66625SAndreas Färber return; 9736e790746SPaolo Bonzini } 9746e790746SPaolo Bonzini 9752ef66625SAndreas Färber vsc->realize(dev, &err); 9762ef66625SAndreas Färber if (err != NULL) { 9772ef66625SAndreas Färber error_propagate(errp, err); 9782ef66625SAndreas Färber return; 9796e790746SPaolo Bonzini } 9806e790746SPaolo Bonzini 98151b19ebeSPaolo Bonzini port->elem = NULL; 9820ddef15bSIgor Mammedov } 9830ddef15bSIgor Mammedov 9840ddef15bSIgor Mammedov static void virtser_port_device_plug(HotplugHandler *hotplug_dev, 9850ddef15bSIgor Mammedov DeviceState *dev, Error **errp) 9860ddef15bSIgor Mammedov { 9870ddef15bSIgor Mammedov VirtIOSerialPort *port = VIRTIO_SERIAL_PORT(dev); 9886e790746SPaolo Bonzini 9896e790746SPaolo Bonzini QTAILQ_INSERT_TAIL(&port->vser->ports, port, next); 9906e790746SPaolo Bonzini port->ivq = port->vser->ivqs[port->id]; 9916e790746SPaolo Bonzini port->ovq = port->vser->ovqs[port->id]; 9926e790746SPaolo Bonzini 9936e790746SPaolo Bonzini add_port(port->vser, port->id); 9946e790746SPaolo Bonzini 9956e790746SPaolo Bonzini /* Send an update to the guest about this new port added */ 9960ddef15bSIgor Mammedov virtio_notify_config(VIRTIO_DEVICE(hotplug_dev)); 9976e790746SPaolo Bonzini } 9986e790746SPaolo Bonzini 9992ef66625SAndreas Färber static void virtser_port_device_unrealize(DeviceState *dev, Error **errp) 10006e790746SPaolo Bonzini { 10012ef66625SAndreas Färber VirtIOSerialPort *port = VIRTIO_SERIAL_PORT(dev); 10022ef66625SAndreas Färber VirtIOSerialPortClass *vsc = VIRTIO_SERIAL_PORT_GET_CLASS(dev); 10036e790746SPaolo Bonzini VirtIOSerial *vser = port->vser; 10046e790746SPaolo Bonzini 10056e790746SPaolo Bonzini qemu_bh_delete(port->bh); 10066e790746SPaolo Bonzini remove_port(port->vser, port->id); 10076e790746SPaolo Bonzini 10086e790746SPaolo Bonzini QTAILQ_REMOVE(&vser->ports, port, next); 10096e790746SPaolo Bonzini 10102ef66625SAndreas Färber if (vsc->unrealize) { 10112ef66625SAndreas Färber vsc->unrealize(dev, errp); 10126e790746SPaolo Bonzini } 10136e790746SPaolo Bonzini } 10146e790746SPaolo Bonzini 101586346244SAndreas Färber static void virtio_serial_device_realize(DeviceState *dev, Error **errp) 10166e790746SPaolo Bonzini { 101786346244SAndreas Färber VirtIODevice *vdev = VIRTIO_DEVICE(dev); 1018b1a20c3fSAndreas Färber VirtIOSerial *vser = VIRTIO_SERIAL(dev); 10196e790746SPaolo Bonzini uint32_t i, max_supported_ports; 1020a06b1daeSSascha Silbe size_t config_size = sizeof(struct virtio_console_config); 10216e790746SPaolo Bonzini 102234b95b2cSKONRAD Frederic if (!vser->serial.max_virtserial_ports) { 102386346244SAndreas Färber error_setg(errp, "Maximum number of serial ports not specified"); 102486346244SAndreas Färber return; 102534b95b2cSKONRAD Frederic } 10266e790746SPaolo Bonzini 10276e790746SPaolo Bonzini /* Each port takes 2 queues, and one pair is for the control queue */ 102887b3bd1cSJason Wang max_supported_ports = VIRTIO_QUEUE_MAX / 2 - 1; 10296e790746SPaolo Bonzini 103034b95b2cSKONRAD Frederic if (vser->serial.max_virtserial_ports > max_supported_ports) { 103186346244SAndreas Färber error_setg(errp, "maximum ports supported: %u", max_supported_ports); 103286346244SAndreas Färber return; 10336e790746SPaolo Bonzini } 10346e790746SPaolo Bonzini 1035a06b1daeSSascha Silbe if (!virtio_has_feature(vser->host_features, 1036a06b1daeSSascha Silbe VIRTIO_CONSOLE_F_EMERG_WRITE)) { 1037a06b1daeSSascha Silbe config_size = offsetof(struct virtio_console_config, emerg_wr); 1038a06b1daeSSascha Silbe } 10392cd2b016SKONRAD Frederic virtio_init(vdev, "virtio-serial", VIRTIO_ID_CONSOLE, 1040a06b1daeSSascha Silbe config_size); 10416e790746SPaolo Bonzini 10426e790746SPaolo Bonzini /* Spawn a new virtio-serial bus on which the ports will ride as devices */ 1043fb17dfe0SAndreas Färber qbus_create_inplace(&vser->bus, sizeof(vser->bus), TYPE_VIRTIO_SERIAL_BUS, 1044b1a20c3fSAndreas Färber dev, vdev->bus_name); 10450ddef15bSIgor Mammedov qbus_set_hotplug_handler(BUS(&vser->bus), DEVICE(vser), errp); 10466e790746SPaolo Bonzini vser->bus.vser = vser; 10476e790746SPaolo Bonzini QTAILQ_INIT(&vser->ports); 10486e790746SPaolo Bonzini 104934b95b2cSKONRAD Frederic vser->bus.max_nr_ports = vser->serial.max_virtserial_ports; 105034b95b2cSKONRAD Frederic vser->ivqs = g_malloc(vser->serial.max_virtserial_ports 105134b95b2cSKONRAD Frederic * sizeof(VirtQueue *)); 105234b95b2cSKONRAD Frederic vser->ovqs = g_malloc(vser->serial.max_virtserial_ports 105334b95b2cSKONRAD Frederic * sizeof(VirtQueue *)); 10546e790746SPaolo Bonzini 10556e790746SPaolo Bonzini /* Add a queue for host to guest transfers for port 0 (backward compat) */ 10566e790746SPaolo Bonzini vser->ivqs[0] = virtio_add_queue(vdev, 128, handle_input); 10576e790746SPaolo Bonzini /* Add a queue for guest to host transfers for port 0 (backward compat) */ 10586e790746SPaolo Bonzini vser->ovqs[0] = virtio_add_queue(vdev, 128, handle_output); 10596e790746SPaolo Bonzini 10606e790746SPaolo Bonzini /* TODO: host to guest notifications can get dropped 10616e790746SPaolo Bonzini * if the queue fills up. Implement queueing in host, 10626e790746SPaolo Bonzini * this might also make it possible to reduce the control 10636e790746SPaolo Bonzini * queue size: as guest preposts buffers there, 10646e790746SPaolo Bonzini * this will save 4Kbyte of guest memory per entry. */ 10656e790746SPaolo Bonzini 10666e790746SPaolo Bonzini /* control queue: host to guest */ 10676e790746SPaolo Bonzini vser->c_ivq = virtio_add_queue(vdev, 32, control_in); 10686e790746SPaolo Bonzini /* control queue: guest to host */ 10696e790746SPaolo Bonzini vser->c_ovq = virtio_add_queue(vdev, 32, control_out); 10706e790746SPaolo Bonzini 10716e790746SPaolo Bonzini for (i = 1; i < vser->bus.max_nr_ports; i++) { 10726e790746SPaolo Bonzini /* Add a per-port queue for host to guest transfers */ 10736e790746SPaolo Bonzini vser->ivqs[i] = virtio_add_queue(vdev, 128, handle_input); 10746e790746SPaolo Bonzini /* Add a per-per queue for guest to host transfers */ 10756e790746SPaolo Bonzini vser->ovqs[i] = virtio_add_queue(vdev, 128, handle_output); 10766e790746SPaolo Bonzini } 10776e790746SPaolo Bonzini 1078*7b9a27cdSMarc-André Lureau vser->ports_map = g_malloc0((DIV_ROUND_UP(vser->serial.max_virtserial_ports, 32)) 10796e790746SPaolo Bonzini * sizeof(vser->ports_map[0])); 10806e790746SPaolo Bonzini /* 10816e790746SPaolo Bonzini * Reserve location 0 for a console port for backward compat 10826e790746SPaolo Bonzini * (old kernel, new qemu) 10836e790746SPaolo Bonzini */ 10846e790746SPaolo Bonzini mark_port_added(vser, 0); 10856e790746SPaolo Bonzini 10866e790746SPaolo Bonzini vser->post_load = NULL; 10876e790746SPaolo Bonzini 1088a1857ad1SAmit Shah QLIST_INSERT_HEAD(&vserdevices.devices, vser, next); 10896e790746SPaolo Bonzini } 10906e790746SPaolo Bonzini 10916e790746SPaolo Bonzini static void virtio_serial_port_class_init(ObjectClass *klass, void *data) 10926e790746SPaolo Bonzini { 10936e790746SPaolo Bonzini DeviceClass *k = DEVICE_CLASS(klass); 10942ef66625SAndreas Färber 1095125ee0edSMarcel Apfelbaum set_bit(DEVICE_CATEGORY_INPUT, k->categories); 10966e790746SPaolo Bonzini k->bus_type = TYPE_VIRTIO_SERIAL_BUS; 10972ef66625SAndreas Färber k->realize = virtser_port_device_realize; 10982ef66625SAndreas Färber k->unrealize = virtser_port_device_unrealize; 10996e790746SPaolo Bonzini k->props = virtser_props; 11006e790746SPaolo Bonzini } 11016e790746SPaolo Bonzini 11026e790746SPaolo Bonzini static const TypeInfo virtio_serial_port_type_info = { 11036e790746SPaolo Bonzini .name = TYPE_VIRTIO_SERIAL_PORT, 11046e790746SPaolo Bonzini .parent = TYPE_DEVICE, 11056e790746SPaolo Bonzini .instance_size = sizeof(VirtIOSerialPort), 11066e790746SPaolo Bonzini .abstract = true, 11076e790746SPaolo Bonzini .class_size = sizeof(VirtIOSerialPortClass), 11086e790746SPaolo Bonzini .class_init = virtio_serial_port_class_init, 11096e790746SPaolo Bonzini }; 11106e790746SPaolo Bonzini 1111306ec6c3SAndreas Färber static void virtio_serial_device_unrealize(DeviceState *dev, Error **errp) 11122cd2b016SKONRAD Frederic { 1113306ec6c3SAndreas Färber VirtIODevice *vdev = VIRTIO_DEVICE(dev); 1114306ec6c3SAndreas Färber VirtIOSerial *vser = VIRTIO_SERIAL(dev); 11152cd2b016SKONRAD Frederic 1116a1857ad1SAmit Shah QLIST_REMOVE(vser, next); 1117a1857ad1SAmit Shah 11182cd2b016SKONRAD Frederic g_free(vser->ivqs); 11192cd2b016SKONRAD Frederic g_free(vser->ovqs); 11202cd2b016SKONRAD Frederic g_free(vser->ports_map); 11212cd2b016SKONRAD Frederic if (vser->post_load) { 11222cd2b016SKONRAD Frederic g_free(vser->post_load->connected); 1123bc72ad67SAlex Bligh timer_del(vser->post_load->timer); 1124bc72ad67SAlex Bligh timer_free(vser->post_load->timer); 11252cd2b016SKONRAD Frederic g_free(vser->post_load); 11262cd2b016SKONRAD Frederic } 1127f811f970SLadi Prosek 1128f811f970SLadi Prosek qbus_set_hotplug_handler(BUS(&vser->bus), NULL, errp); 1129f811f970SLadi Prosek 11306a1a8cc7SKONRAD Frederic virtio_cleanup(vdev); 11312cd2b016SKONRAD Frederic } 11322cd2b016SKONRAD Frederic 113342e6c039SDr. David Alan Gilbert /* Note: 'console' is used for backwards compatibility */ 113497eed24fSHalil Pasic static const VMStateDescription vmstate_virtio_console = { 113597eed24fSHalil Pasic .name = "virtio-console", 113697eed24fSHalil Pasic .minimum_version_id = 3, 113797eed24fSHalil Pasic .version_id = 3, 113897eed24fSHalil Pasic .fields = (VMStateField[]) { 113997eed24fSHalil Pasic VMSTATE_VIRTIO_DEVICE, 114097eed24fSHalil Pasic VMSTATE_END_OF_LIST() 114197eed24fSHalil Pasic }, 114297eed24fSHalil Pasic }; 114342e6c039SDr. David Alan Gilbert 11442cd2b016SKONRAD Frederic static Property virtio_serial_properties[] = { 1145448777c4SShannon Zhao DEFINE_PROP_UINT32("max_ports", VirtIOSerial, serial.max_virtserial_ports, 1146448777c4SShannon Zhao 31), 1147a06b1daeSSascha Silbe DEFINE_PROP_BIT64("emergency-write", VirtIOSerial, host_features, 1148a06b1daeSSascha Silbe VIRTIO_CONSOLE_F_EMERG_WRITE, true), 11492cd2b016SKONRAD Frederic DEFINE_PROP_END_OF_LIST(), 11502cd2b016SKONRAD Frederic }; 11512cd2b016SKONRAD Frederic 11522cd2b016SKONRAD Frederic static void virtio_serial_class_init(ObjectClass *klass, void *data) 11532cd2b016SKONRAD Frederic { 11542cd2b016SKONRAD Frederic DeviceClass *dc = DEVICE_CLASS(klass); 11552cd2b016SKONRAD Frederic VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); 11560ddef15bSIgor Mammedov HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); 115786346244SAndreas Färber 1158a1857ad1SAmit Shah QLIST_INIT(&vserdevices.devices); 1159a1857ad1SAmit Shah 11602cd2b016SKONRAD Frederic dc->props = virtio_serial_properties; 116142e6c039SDr. David Alan Gilbert dc->vmsd = &vmstate_virtio_console; 1162125ee0edSMarcel Apfelbaum set_bit(DEVICE_CATEGORY_INPUT, dc->categories); 116386346244SAndreas Färber vdc->realize = virtio_serial_device_realize; 1164306ec6c3SAndreas Färber vdc->unrealize = virtio_serial_device_unrealize; 11652cd2b016SKONRAD Frederic vdc->get_features = get_features; 11662cd2b016SKONRAD Frederic vdc->get_config = get_config; 116709da01c3SSascha Silbe vdc->set_config = set_config; 11682cd2b016SKONRAD Frederic vdc->set_status = set_status; 11692cd2b016SKONRAD Frederic vdc->reset = vser_reset; 117013c6855aSGreg Kurz vdc->save = virtio_serial_save_device; 117113c6855aSGreg Kurz vdc->load = virtio_serial_load_device; 11720ddef15bSIgor Mammedov hc->plug = virtser_port_device_plug; 11730ddef15bSIgor Mammedov hc->unplug = qdev_simple_device_unplug_cb; 11742cd2b016SKONRAD Frederic } 11752cd2b016SKONRAD Frederic 11762cd2b016SKONRAD Frederic static const TypeInfo virtio_device_info = { 11772cd2b016SKONRAD Frederic .name = TYPE_VIRTIO_SERIAL, 11782cd2b016SKONRAD Frederic .parent = TYPE_VIRTIO_DEVICE, 11792cd2b016SKONRAD Frederic .instance_size = sizeof(VirtIOSerial), 11802cd2b016SKONRAD Frederic .class_init = virtio_serial_class_init, 11810ddef15bSIgor Mammedov .interfaces = (InterfaceInfo[]) { 11820ddef15bSIgor Mammedov { TYPE_HOTPLUG_HANDLER }, 11830ddef15bSIgor Mammedov { } 11840ddef15bSIgor Mammedov } 11852cd2b016SKONRAD Frederic }; 11862cd2b016SKONRAD Frederic 11876e790746SPaolo Bonzini static void virtio_serial_register_types(void) 11886e790746SPaolo Bonzini { 11896e790746SPaolo Bonzini type_register_static(&virtser_bus_info); 11906e790746SPaolo Bonzini type_register_static(&virtio_serial_port_type_info); 11912cd2b016SKONRAD Frederic type_register_static(&virtio_device_info); 11926e790746SPaolo Bonzini } 11936e790746SPaolo Bonzini 11946e790746SPaolo Bonzini type_init(virtio_serial_register_types) 1195