xref: /openbmc/qemu/hw/char/virtio-serial-bus.c (revision b4295bff)
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"
24db725815SMarkus Armbruster #include "qemu/main-loop.h"
250b8fa32fSMarkus Armbruster #include "qemu/module.h"
26ca77ee28SMarkus Armbruster #include "migration/qemu-file-types.h"
276e790746SPaolo Bonzini #include "monitor/monitor.h"
28d49b6836SMarkus Armbruster #include "qemu/error-report.h"
296e790746SPaolo Bonzini #include "qemu/queue.h"
30a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h"
316e790746SPaolo Bonzini #include "trace.h"
326e790746SPaolo Bonzini #include "hw/virtio/virtio-serial.h"
33e0ab7facSRusty Russell #include "hw/virtio/virtio-access.h"
346e790746SPaolo Bonzini 
3543d73554SStefan Weil static struct VirtIOSerialDevices {
36a1857ad1SAmit Shah     QLIST_HEAD(, VirtIOSerial) devices;
37a1857ad1SAmit Shah } vserdevices;
38a1857ad1SAmit Shah 
find_port_by_id(VirtIOSerial * vser,uint32_t id)396e790746SPaolo Bonzini static VirtIOSerialPort *find_port_by_id(VirtIOSerial *vser, uint32_t id)
406e790746SPaolo Bonzini {
416e790746SPaolo Bonzini     VirtIOSerialPort *port;
426e790746SPaolo Bonzini 
436e790746SPaolo Bonzini     if (id == VIRTIO_CONSOLE_BAD_ID) {
446e790746SPaolo Bonzini         return NULL;
456e790746SPaolo Bonzini     }
466e790746SPaolo Bonzini 
476e790746SPaolo Bonzini     QTAILQ_FOREACH(port, &vser->ports, next) {
486e790746SPaolo Bonzini         if (port->id == id)
496e790746SPaolo Bonzini             return port;
506e790746SPaolo Bonzini     }
516e790746SPaolo Bonzini     return NULL;
526e790746SPaolo Bonzini }
536e790746SPaolo Bonzini 
find_port_by_vq(VirtIOSerial * vser,VirtQueue * vq)546e790746SPaolo Bonzini static VirtIOSerialPort *find_port_by_vq(VirtIOSerial *vser, VirtQueue *vq)
556e790746SPaolo Bonzini {
566e790746SPaolo Bonzini     VirtIOSerialPort *port;
576e790746SPaolo Bonzini 
586e790746SPaolo Bonzini     QTAILQ_FOREACH(port, &vser->ports, next) {
596e790746SPaolo Bonzini         if (port->ivq == vq || port->ovq == vq)
606e790746SPaolo Bonzini             return port;
616e790746SPaolo Bonzini     }
626e790746SPaolo Bonzini     return NULL;
636e790746SPaolo Bonzini }
646e790746SPaolo Bonzini 
find_port_by_name(char * name)65d0a0bfe6SAmit Shah static VirtIOSerialPort *find_port_by_name(char *name)
66d0a0bfe6SAmit Shah {
67d0a0bfe6SAmit Shah     VirtIOSerial *vser;
68d0a0bfe6SAmit Shah 
69d0a0bfe6SAmit Shah     QLIST_FOREACH(vser, &vserdevices.devices, next) {
70d0a0bfe6SAmit Shah         VirtIOSerialPort *port;
71d0a0bfe6SAmit Shah 
72d0a0bfe6SAmit Shah         QTAILQ_FOREACH(port, &vser->ports, next) {
73b18a755cSAmit Shah             if (port->name && !strcmp(port->name, name)) {
74d0a0bfe6SAmit Shah                 return port;
75d0a0bfe6SAmit Shah             }
76d0a0bfe6SAmit Shah         }
77d0a0bfe6SAmit Shah     }
78d0a0bfe6SAmit Shah     return NULL;
79d0a0bfe6SAmit Shah }
80d0a0bfe6SAmit Shah 
find_first_connected_console(VirtIOSerial * vser)8109da01c3SSascha Silbe static VirtIOSerialPort *find_first_connected_console(VirtIOSerial *vser)
8209da01c3SSascha Silbe {
8309da01c3SSascha Silbe     VirtIOSerialPort *port;
8409da01c3SSascha Silbe 
8509da01c3SSascha Silbe     QTAILQ_FOREACH(port, &vser->ports, next) {
8609da01c3SSascha Silbe         VirtIOSerialPortClass const *vsc = VIRTIO_SERIAL_PORT_GET_CLASS(port);
8709da01c3SSascha Silbe         if (vsc->is_console && port->host_connected) {
8809da01c3SSascha Silbe             return port;
8909da01c3SSascha Silbe         }
9009da01c3SSascha Silbe     }
9109da01c3SSascha Silbe     return NULL;
9209da01c3SSascha Silbe }
9309da01c3SSascha Silbe 
use_multiport(VirtIOSerial * vser)946e790746SPaolo Bonzini static bool use_multiport(VirtIOSerial *vser)
956e790746SPaolo Bonzini {
9676017fd2SKONRAD Frederic     VirtIODevice *vdev = VIRTIO_DEVICE(vser);
9795129d6fSCornelia Huck     return virtio_vdev_has_feature(vdev, VIRTIO_CONSOLE_F_MULTIPORT);
986e790746SPaolo Bonzini }
996e790746SPaolo Bonzini 
write_to_port(VirtIOSerialPort * port,const uint8_t * buf,size_t size)1006e790746SPaolo Bonzini static size_t write_to_port(VirtIOSerialPort *port,
1016e790746SPaolo Bonzini                             const uint8_t *buf, size_t size)
1026e790746SPaolo Bonzini {
10351b19ebeSPaolo Bonzini     VirtQueueElement *elem;
1046e790746SPaolo Bonzini     VirtQueue *vq;
1056e790746SPaolo Bonzini     size_t offset;
1066e790746SPaolo Bonzini 
1076e790746SPaolo Bonzini     vq = port->ivq;
1086e790746SPaolo Bonzini     if (!virtio_queue_ready(vq)) {
1096e790746SPaolo Bonzini         return 0;
1106e790746SPaolo Bonzini     }
1116e790746SPaolo Bonzini 
1126e790746SPaolo Bonzini     offset = 0;
1136e790746SPaolo Bonzini     while (offset < size) {
1146e790746SPaolo Bonzini         size_t len;
1156e790746SPaolo Bonzini 
11651b19ebeSPaolo Bonzini         elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
11751b19ebeSPaolo Bonzini         if (!elem) {
1186e790746SPaolo Bonzini             break;
1196e790746SPaolo Bonzini         }
1206e790746SPaolo Bonzini 
12151b19ebeSPaolo Bonzini         len = iov_from_buf(elem->in_sg, elem->in_num, 0,
1226e790746SPaolo Bonzini                            buf + offset, size - offset);
1236e790746SPaolo Bonzini         offset += len;
1246e790746SPaolo Bonzini 
12551b19ebeSPaolo Bonzini         virtqueue_push(vq, elem, len);
12651b19ebeSPaolo Bonzini         g_free(elem);
1276e790746SPaolo Bonzini     }
1286e790746SPaolo Bonzini 
12976017fd2SKONRAD Frederic     virtio_notify(VIRTIO_DEVICE(port->vser), vq);
1306e790746SPaolo Bonzini     return offset;
1316e790746SPaolo Bonzini }
1326e790746SPaolo Bonzini 
discard_vq_data(VirtQueue * vq,VirtIODevice * vdev)1336e790746SPaolo Bonzini static void discard_vq_data(VirtQueue *vq, VirtIODevice *vdev)
1346e790746SPaolo Bonzini {
13551b19ebeSPaolo Bonzini     VirtQueueElement *elem;
1366e790746SPaolo Bonzini 
1376e790746SPaolo Bonzini     if (!virtio_queue_ready(vq)) {
1386e790746SPaolo Bonzini         return;
1396e790746SPaolo Bonzini     }
14051b19ebeSPaolo Bonzini     for (;;) {
14151b19ebeSPaolo Bonzini         elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
14251b19ebeSPaolo Bonzini         if (!elem) {
14351b19ebeSPaolo Bonzini             break;
14451b19ebeSPaolo Bonzini         }
14551b19ebeSPaolo Bonzini         virtqueue_push(vq, elem, 0);
14651b19ebeSPaolo Bonzini         g_free(elem);
1476e790746SPaolo Bonzini     }
1486e790746SPaolo Bonzini     virtio_notify(vdev, vq);
1496e790746SPaolo Bonzini }
1506e790746SPaolo Bonzini 
discard_throttle_data(VirtIOSerialPort * port)151d4c19cdeSStefan Hajnoczi static void discard_throttle_data(VirtIOSerialPort *port)
152d4c19cdeSStefan Hajnoczi {
153d4c19cdeSStefan Hajnoczi     if (port->elem) {
154d4c19cdeSStefan Hajnoczi         virtqueue_detach_element(port->ovq, port->elem, 0);
155d4c19cdeSStefan Hajnoczi         g_free(port->elem);
156d4c19cdeSStefan Hajnoczi         port->elem = NULL;
157d4c19cdeSStefan Hajnoczi     }
158d4c19cdeSStefan Hajnoczi }
159d4c19cdeSStefan Hajnoczi 
do_flush_queued_data(VirtIOSerialPort * port,VirtQueue * vq,VirtIODevice * vdev)1606e790746SPaolo Bonzini static void do_flush_queued_data(VirtIOSerialPort *port, VirtQueue *vq,
1616e790746SPaolo Bonzini                                  VirtIODevice *vdev)
1626e790746SPaolo Bonzini {
1636e790746SPaolo Bonzini     VirtIOSerialPortClass *vsc;
1646e790746SPaolo Bonzini 
1656e790746SPaolo Bonzini     assert(port);
1666e790746SPaolo Bonzini     assert(virtio_queue_ready(vq));
1676e790746SPaolo Bonzini 
1686e790746SPaolo Bonzini     vsc = VIRTIO_SERIAL_PORT_GET_CLASS(port);
1696e790746SPaolo Bonzini 
1706e790746SPaolo Bonzini     while (!port->throttled) {
1716e790746SPaolo Bonzini         unsigned int i;
1726e790746SPaolo Bonzini 
1736e790746SPaolo Bonzini         /* Pop an elem only if we haven't left off a previous one mid-way */
17451b19ebeSPaolo Bonzini         if (!port->elem) {
17551b19ebeSPaolo Bonzini             port->elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
17651b19ebeSPaolo Bonzini             if (!port->elem) {
1776e790746SPaolo Bonzini                 break;
1786e790746SPaolo Bonzini             }
1796e790746SPaolo Bonzini             port->iov_idx = 0;
1806e790746SPaolo Bonzini             port->iov_offset = 0;
1816e790746SPaolo Bonzini         }
1826e790746SPaolo Bonzini 
18351b19ebeSPaolo Bonzini         for (i = port->iov_idx; i < port->elem->out_num; i++) {
1846e790746SPaolo Bonzini             size_t buf_size;
1856e790746SPaolo Bonzini             ssize_t ret;
1866e790746SPaolo Bonzini 
18751b19ebeSPaolo Bonzini             buf_size = port->elem->out_sg[i].iov_len - port->iov_offset;
1886e790746SPaolo Bonzini             ret = vsc->have_data(port,
18951b19ebeSPaolo Bonzini                                   port->elem->out_sg[i].iov_base
1906e790746SPaolo Bonzini                                   + port->iov_offset,
1916e790746SPaolo Bonzini                                   buf_size);
19246764fe0SStefan Hajnoczi             if (!port->elem) { /* bail if we got disconnected */
19346764fe0SStefan Hajnoczi                 return;
19446764fe0SStefan Hajnoczi             }
1956e790746SPaolo Bonzini             if (port->throttled) {
1966e790746SPaolo Bonzini                 port->iov_idx = i;
1976e790746SPaolo Bonzini                 if (ret > 0) {
1986e790746SPaolo Bonzini                     port->iov_offset += ret;
1996e790746SPaolo Bonzini                 }
2006e790746SPaolo Bonzini                 break;
2016e790746SPaolo Bonzini             }
2026e790746SPaolo Bonzini             port->iov_offset = 0;
2036e790746SPaolo Bonzini         }
2046e790746SPaolo Bonzini         if (port->throttled) {
2056e790746SPaolo Bonzini             break;
2066e790746SPaolo Bonzini         }
20751b19ebeSPaolo Bonzini         virtqueue_push(vq, port->elem, 0);
20851b19ebeSPaolo Bonzini         g_free(port->elem);
20951b19ebeSPaolo Bonzini         port->elem = NULL;
2106e790746SPaolo Bonzini     }
2116e790746SPaolo Bonzini     virtio_notify(vdev, vq);
2126e790746SPaolo Bonzini }
2136e790746SPaolo Bonzini 
flush_queued_data(VirtIOSerialPort * port)2146e790746SPaolo Bonzini static void flush_queued_data(VirtIOSerialPort *port)
2156e790746SPaolo Bonzini {
2166e790746SPaolo Bonzini     assert(port);
2176e790746SPaolo Bonzini 
2186e790746SPaolo Bonzini     if (!virtio_queue_ready(port->ovq)) {
2196e790746SPaolo Bonzini         return;
2206e790746SPaolo Bonzini     }
22176017fd2SKONRAD Frederic     do_flush_queued_data(port, port->ovq, VIRTIO_DEVICE(port->vser));
2226e790746SPaolo Bonzini }
2236e790746SPaolo Bonzini 
send_control_msg(VirtIOSerial * vser,void * buf,size_t len)2246e790746SPaolo Bonzini static size_t send_control_msg(VirtIOSerial *vser, void *buf, size_t len)
2256e790746SPaolo Bonzini {
22651b19ebeSPaolo Bonzini     VirtQueueElement *elem;
2276e790746SPaolo Bonzini     VirtQueue *vq;
2286e790746SPaolo Bonzini 
2296e790746SPaolo Bonzini     vq = vser->c_ivq;
2306e790746SPaolo Bonzini     if (!virtio_queue_ready(vq)) {
2316e790746SPaolo Bonzini         return 0;
2326e790746SPaolo Bonzini     }
23351b19ebeSPaolo Bonzini 
23451b19ebeSPaolo Bonzini     elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
23551b19ebeSPaolo Bonzini     if (!elem) {
2366e790746SPaolo Bonzini         return 0;
2376e790746SPaolo Bonzini     }
2386e790746SPaolo Bonzini 
23978820803SMichael S. Tsirkin     /* TODO: detect a buffer that's too short, set NEEDS_RESET */
24051b19ebeSPaolo Bonzini     iov_from_buf(elem->in_sg, elem->in_num, 0, buf, len);
2416e790746SPaolo Bonzini 
24251b19ebeSPaolo Bonzini     virtqueue_push(vq, elem, len);
24376017fd2SKONRAD Frederic     virtio_notify(VIRTIO_DEVICE(vser), vq);
24451b19ebeSPaolo Bonzini     g_free(elem);
24551b19ebeSPaolo Bonzini 
2466e790746SPaolo Bonzini     return len;
2476e790746SPaolo Bonzini }
2486e790746SPaolo Bonzini 
send_control_event(VirtIOSerial * vser,uint32_t port_id,uint16_t event,uint16_t value)2496e790746SPaolo Bonzini static size_t send_control_event(VirtIOSerial *vser, uint32_t port_id,
2506e790746SPaolo Bonzini                                  uint16_t event, uint16_t value)
2516e790746SPaolo Bonzini {
252e0ab7facSRusty Russell     VirtIODevice *vdev = VIRTIO_DEVICE(vser);
2536e790746SPaolo Bonzini     struct virtio_console_control cpkt;
2546e790746SPaolo Bonzini 
255e0ab7facSRusty Russell     virtio_stl_p(vdev, &cpkt.id, port_id);
256e0ab7facSRusty Russell     virtio_stw_p(vdev, &cpkt.event, event);
257e0ab7facSRusty Russell     virtio_stw_p(vdev, &cpkt.value, value);
2586e790746SPaolo Bonzini 
2596e790746SPaolo Bonzini     trace_virtio_serial_send_control_event(port_id, event, value);
2606e790746SPaolo Bonzini     return send_control_msg(vser, &cpkt, sizeof(cpkt));
2616e790746SPaolo Bonzini }
2626e790746SPaolo Bonzini 
2636e790746SPaolo Bonzini /* Functions for use inside qemu to open and read from/write to ports */
virtio_serial_open(VirtIOSerialPort * port)2646e790746SPaolo Bonzini int virtio_serial_open(VirtIOSerialPort *port)
2656e790746SPaolo Bonzini {
2666e790746SPaolo Bonzini     /* Don't allow opening an already-open port */
2676e790746SPaolo Bonzini     if (port->host_connected) {
2686e790746SPaolo Bonzini         return 0;
2696e790746SPaolo Bonzini     }
2706e790746SPaolo Bonzini     /* Send port open notification to the guest */
2716e790746SPaolo Bonzini     port->host_connected = true;
2726e790746SPaolo Bonzini     send_control_event(port->vser, port->id, VIRTIO_CONSOLE_PORT_OPEN, 1);
2736e790746SPaolo Bonzini 
2746e790746SPaolo Bonzini     return 0;
2756e790746SPaolo Bonzini }
2766e790746SPaolo Bonzini 
virtio_serial_close(VirtIOSerialPort * port)2776e790746SPaolo Bonzini int virtio_serial_close(VirtIOSerialPort *port)
2786e790746SPaolo Bonzini {
2796e790746SPaolo Bonzini     port->host_connected = false;
2806e790746SPaolo Bonzini     /*
2816e790746SPaolo Bonzini      * If there's any data the guest sent which the app didn't
2826e790746SPaolo Bonzini      * consume, reset the throttling flag and discard the data.
2836e790746SPaolo Bonzini      */
2846e790746SPaolo Bonzini     port->throttled = false;
285d4c19cdeSStefan Hajnoczi     discard_throttle_data(port);
28676017fd2SKONRAD Frederic     discard_vq_data(port->ovq, VIRTIO_DEVICE(port->vser));
2876e790746SPaolo Bonzini 
2886e790746SPaolo Bonzini     send_control_event(port->vser, port->id, VIRTIO_CONSOLE_PORT_OPEN, 0);
2896e790746SPaolo Bonzini 
2906e790746SPaolo Bonzini     return 0;
2916e790746SPaolo Bonzini }
2926e790746SPaolo Bonzini 
2936e790746SPaolo Bonzini /* Individual ports/apps call this function to write to the guest. */
virtio_serial_write(VirtIOSerialPort * port,const uint8_t * buf,size_t size)2946e790746SPaolo Bonzini ssize_t virtio_serial_write(VirtIOSerialPort *port, const uint8_t *buf,
2956e790746SPaolo Bonzini                             size_t size)
2966e790746SPaolo Bonzini {
2976e790746SPaolo Bonzini     if (!port || !port->host_connected || !port->guest_connected) {
2986e790746SPaolo Bonzini         return 0;
2996e790746SPaolo Bonzini     }
3006e790746SPaolo Bonzini     return write_to_port(port, buf, size);
3016e790746SPaolo Bonzini }
3026e790746SPaolo Bonzini 
3036e790746SPaolo Bonzini /*
3046e790746SPaolo Bonzini  * Readiness of the guest to accept data on a port.
3056e790746SPaolo Bonzini  * Returns max. data the guest can receive
3066e790746SPaolo Bonzini  */
virtio_serial_guest_ready(VirtIOSerialPort * port)3076e790746SPaolo Bonzini size_t virtio_serial_guest_ready(VirtIOSerialPort *port)
3086e790746SPaolo Bonzini {
30976017fd2SKONRAD Frederic     VirtIODevice *vdev = VIRTIO_DEVICE(port->vser);
3106e790746SPaolo Bonzini     VirtQueue *vq = port->ivq;
3116e790746SPaolo Bonzini     unsigned int bytes;
3126e790746SPaolo Bonzini 
3136e790746SPaolo Bonzini     if (!virtio_queue_ready(vq) ||
31476017fd2SKONRAD Frederic         !(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK) ||
3156e790746SPaolo Bonzini         virtio_queue_empty(vq)) {
3166e790746SPaolo Bonzini         return 0;
3176e790746SPaolo Bonzini     }
3186e790746SPaolo Bonzini     if (use_multiport(port->vser) && !port->guest_connected) {
3196e790746SPaolo Bonzini         return 0;
3206e790746SPaolo Bonzini     }
3216e790746SPaolo Bonzini     virtqueue_get_avail_bytes(vq, &bytes, NULL, 4096, 0);
3226e790746SPaolo Bonzini     return bytes;
3236e790746SPaolo Bonzini }
3246e790746SPaolo Bonzini 
flush_queued_data_bh(void * opaque)3256e790746SPaolo Bonzini static void flush_queued_data_bh(void *opaque)
3266e790746SPaolo Bonzini {
3276e790746SPaolo Bonzini     VirtIOSerialPort *port = opaque;
3286e790746SPaolo Bonzini 
3296e790746SPaolo Bonzini     flush_queued_data(port);
3306e790746SPaolo Bonzini }
3316e790746SPaolo Bonzini 
virtio_serial_throttle_port(VirtIOSerialPort * port,bool throttle)3326e790746SPaolo Bonzini void virtio_serial_throttle_port(VirtIOSerialPort *port, bool throttle)
3336e790746SPaolo Bonzini {
3346e790746SPaolo Bonzini     if (!port) {
3356e790746SPaolo Bonzini         return;
3366e790746SPaolo Bonzini     }
3376e790746SPaolo Bonzini 
3386e790746SPaolo Bonzini     trace_virtio_serial_throttle_port(port->id, throttle);
3396e790746SPaolo Bonzini     port->throttled = throttle;
3406e790746SPaolo Bonzini     if (throttle) {
3416e790746SPaolo Bonzini         return;
3426e790746SPaolo Bonzini     }
3436e790746SPaolo Bonzini     qemu_bh_schedule(port->bh);
3446e790746SPaolo Bonzini }
3456e790746SPaolo Bonzini 
3466e790746SPaolo Bonzini /* Guest wants to notify us of some event */
handle_control_message(VirtIOSerial * vser,void * buf,size_t len)3476e790746SPaolo Bonzini static void handle_control_message(VirtIOSerial *vser, void *buf, size_t len)
3486e790746SPaolo Bonzini {
349e0ab7facSRusty Russell     VirtIODevice *vdev = VIRTIO_DEVICE(vser);
3506e790746SPaolo Bonzini     struct VirtIOSerialPort *port;
3516e790746SPaolo Bonzini     VirtIOSerialPortClass *vsc;
3526e790746SPaolo Bonzini     struct virtio_console_control cpkt, *gcpkt;
3536e790746SPaolo Bonzini     uint8_t *buffer;
3546e790746SPaolo Bonzini     size_t buffer_len;
3556e790746SPaolo Bonzini 
3566e790746SPaolo Bonzini     gcpkt = buf;
3576e790746SPaolo Bonzini 
3586e790746SPaolo Bonzini     if (len < sizeof(cpkt)) {
3596e790746SPaolo Bonzini         /* The guest sent an invalid control packet */
3606e790746SPaolo Bonzini         return;
3616e790746SPaolo Bonzini     }
3626e790746SPaolo Bonzini 
363e0ab7facSRusty Russell     cpkt.event = virtio_lduw_p(vdev, &gcpkt->event);
364e0ab7facSRusty Russell     cpkt.value = virtio_lduw_p(vdev, &gcpkt->value);
3656e790746SPaolo Bonzini 
3666e790746SPaolo Bonzini     trace_virtio_serial_handle_control_message(cpkt.event, cpkt.value);
3676e790746SPaolo Bonzini 
3686e790746SPaolo Bonzini     if (cpkt.event == VIRTIO_CONSOLE_DEVICE_READY) {
3696e790746SPaolo Bonzini         if (!cpkt.value) {
3706e790746SPaolo Bonzini             error_report("virtio-serial-bus: Guest failure in adding device %s",
3716e790746SPaolo Bonzini                          vser->bus.qbus.name);
3726e790746SPaolo Bonzini             return;
3736e790746SPaolo Bonzini         }
3746e790746SPaolo Bonzini         /*
3756e790746SPaolo Bonzini          * The device is up, we can now tell the device about all the
3766e790746SPaolo Bonzini          * ports we have here.
3776e790746SPaolo Bonzini          */
3786e790746SPaolo Bonzini         QTAILQ_FOREACH(port, &vser->ports, next) {
3796e790746SPaolo Bonzini             send_control_event(vser, port->id, VIRTIO_CONSOLE_PORT_ADD, 1);
3806e790746SPaolo Bonzini         }
3816e790746SPaolo Bonzini         return;
3826e790746SPaolo Bonzini     }
3836e790746SPaolo Bonzini 
384e0ab7facSRusty Russell     port = find_port_by_id(vser, virtio_ldl_p(vdev, &gcpkt->id));
3856e790746SPaolo Bonzini     if (!port) {
3866e790746SPaolo Bonzini         error_report("virtio-serial-bus: Unexpected port id %u for device %s",
387e0ab7facSRusty Russell                      virtio_ldl_p(vdev, &gcpkt->id), vser->bus.qbus.name);
3886e790746SPaolo Bonzini         return;
3896e790746SPaolo Bonzini     }
3906e790746SPaolo Bonzini 
3916e790746SPaolo Bonzini     trace_virtio_serial_handle_control_message_port(port->id);
3926e790746SPaolo Bonzini 
3936e790746SPaolo Bonzini     vsc = VIRTIO_SERIAL_PORT_GET_CLASS(port);
3946e790746SPaolo Bonzini 
3956e790746SPaolo Bonzini     switch(cpkt.event) {
3966e790746SPaolo Bonzini     case VIRTIO_CONSOLE_PORT_READY:
3976e790746SPaolo Bonzini         if (!cpkt.value) {
3986e790746SPaolo Bonzini             error_report("virtio-serial-bus: Guest failure in adding port %u for device %s",
3996e790746SPaolo Bonzini                          port->id, vser->bus.qbus.name);
4006e790746SPaolo Bonzini             break;
4016e790746SPaolo Bonzini         }
4026e790746SPaolo Bonzini         /*
4036e790746SPaolo Bonzini          * Now that we know the guest asked for the port name, we're
4046e790746SPaolo Bonzini          * sure the guest has initialised whatever state is necessary
4056e790746SPaolo Bonzini          * for this port. Now's a good time to let the guest know if
4066e790746SPaolo Bonzini          * this port is a console port so that the guest can hook it
4076e790746SPaolo Bonzini          * up to hvc.
4086e790746SPaolo Bonzini          */
4096e790746SPaolo Bonzini         if (vsc->is_console) {
4106e790746SPaolo Bonzini             send_control_event(vser, port->id, VIRTIO_CONSOLE_CONSOLE_PORT, 1);
4116e790746SPaolo Bonzini         }
4126e790746SPaolo Bonzini 
4136e790746SPaolo Bonzini         if (port->name) {
414e0ab7facSRusty Russell             virtio_stl_p(vdev, &cpkt.id, port->id);
415e0ab7facSRusty Russell             virtio_stw_p(vdev, &cpkt.event, VIRTIO_CONSOLE_PORT_NAME);
416e0ab7facSRusty Russell             virtio_stw_p(vdev, &cpkt.value, 1);
4176e790746SPaolo Bonzini 
4186e790746SPaolo Bonzini             buffer_len = sizeof(cpkt) + strlen(port->name) + 1;
4196e790746SPaolo Bonzini             buffer = g_malloc(buffer_len);
4206e790746SPaolo Bonzini 
4216e790746SPaolo Bonzini             memcpy(buffer, &cpkt, sizeof(cpkt));
4226e790746SPaolo Bonzini             memcpy(buffer + sizeof(cpkt), port->name, strlen(port->name));
4236e790746SPaolo Bonzini             buffer[buffer_len - 1] = 0;
4246e790746SPaolo Bonzini 
4256e790746SPaolo Bonzini             send_control_msg(vser, buffer, buffer_len);
4266e790746SPaolo Bonzini             g_free(buffer);
4276e790746SPaolo Bonzini         }
4286e790746SPaolo Bonzini 
4296e790746SPaolo Bonzini         if (port->host_connected) {
4306e790746SPaolo Bonzini             send_control_event(vser, port->id, VIRTIO_CONSOLE_PORT_OPEN, 1);
4316e790746SPaolo Bonzini         }
4326e790746SPaolo Bonzini 
4336e790746SPaolo Bonzini         /*
4346e790746SPaolo Bonzini          * When the guest has asked us for this information it means
4356e790746SPaolo Bonzini          * the guest is all setup and has its virtqueues
4366e790746SPaolo Bonzini          * initialised. If some app is interested in knowing about
4376e790746SPaolo Bonzini          * this event, let it know.
4386e790746SPaolo Bonzini          */
4396e790746SPaolo Bonzini         if (vsc->guest_ready) {
4406e790746SPaolo Bonzini             vsc->guest_ready(port);
4416e790746SPaolo Bonzini         }
4426e790746SPaolo Bonzini         break;
4436e790746SPaolo Bonzini 
4446e790746SPaolo Bonzini     case VIRTIO_CONSOLE_PORT_OPEN:
4456e790746SPaolo Bonzini         port->guest_connected = cpkt.value;
4466e790746SPaolo Bonzini         if (vsc->set_guest_connected) {
4476e790746SPaolo Bonzini             /* Send the guest opened notification if an app is interested */
4486e790746SPaolo Bonzini             vsc->set_guest_connected(port, cpkt.value);
4496e790746SPaolo Bonzini         }
4506e790746SPaolo Bonzini         break;
4516e790746SPaolo Bonzini     }
4526e790746SPaolo Bonzini }
4536e790746SPaolo Bonzini 
control_in(VirtIODevice * vdev,VirtQueue * vq)4546e790746SPaolo Bonzini static void control_in(VirtIODevice *vdev, VirtQueue *vq)
4556e790746SPaolo Bonzini {
4566e790746SPaolo Bonzini }
4576e790746SPaolo Bonzini 
control_out(VirtIODevice * vdev,VirtQueue * vq)4586e790746SPaolo Bonzini static void control_out(VirtIODevice *vdev, VirtQueue *vq)
4596e790746SPaolo Bonzini {
46051b19ebeSPaolo Bonzini     VirtQueueElement *elem;
4616e790746SPaolo Bonzini     VirtIOSerial *vser;
4626e790746SPaolo Bonzini     uint8_t *buf;
4636e790746SPaolo Bonzini     size_t len;
4646e790746SPaolo Bonzini 
46576017fd2SKONRAD Frederic     vser = VIRTIO_SERIAL(vdev);
4666e790746SPaolo Bonzini 
4676e790746SPaolo Bonzini     len = 0;
4686e790746SPaolo Bonzini     buf = NULL;
46951b19ebeSPaolo Bonzini     for (;;) {
4706e790746SPaolo Bonzini         size_t cur_len;
4716e790746SPaolo Bonzini 
47251b19ebeSPaolo Bonzini         elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
47351b19ebeSPaolo Bonzini         if (!elem) {
47451b19ebeSPaolo Bonzini             break;
47551b19ebeSPaolo Bonzini         }
47651b19ebeSPaolo Bonzini 
47751b19ebeSPaolo Bonzini         cur_len = iov_size(elem->out_sg, elem->out_num);
4786e790746SPaolo Bonzini         /*
4796e790746SPaolo Bonzini          * Allocate a new buf only if we didn't have one previously or
4806e790746SPaolo Bonzini          * if the size of the buf differs
4816e790746SPaolo Bonzini          */
4826e790746SPaolo Bonzini         if (cur_len > len) {
4836e790746SPaolo Bonzini             g_free(buf);
4846e790746SPaolo Bonzini 
4856e790746SPaolo Bonzini             buf = g_malloc(cur_len);
4866e790746SPaolo Bonzini             len = cur_len;
4876e790746SPaolo Bonzini         }
48851b19ebeSPaolo Bonzini         iov_to_buf(elem->out_sg, elem->out_num, 0, buf, cur_len);
4896e790746SPaolo Bonzini 
4906e790746SPaolo Bonzini         handle_control_message(vser, buf, cur_len);
49151b19ebeSPaolo Bonzini         virtqueue_push(vq, elem, 0);
49251b19ebeSPaolo Bonzini         g_free(elem);
4936e790746SPaolo Bonzini     }
4946e790746SPaolo Bonzini     g_free(buf);
4956e790746SPaolo Bonzini     virtio_notify(vdev, vq);
4966e790746SPaolo Bonzini }
4976e790746SPaolo Bonzini 
4986e790746SPaolo Bonzini /* Guest wrote something to some port. */
handle_output(VirtIODevice * vdev,VirtQueue * vq)4996e790746SPaolo Bonzini static void handle_output(VirtIODevice *vdev, VirtQueue *vq)
5006e790746SPaolo Bonzini {
5016e790746SPaolo Bonzini     VirtIOSerial *vser;
5026e790746SPaolo Bonzini     VirtIOSerialPort *port;
5036e790746SPaolo Bonzini 
50476017fd2SKONRAD Frederic     vser = VIRTIO_SERIAL(vdev);
5056e790746SPaolo Bonzini     port = find_port_by_vq(vser, vq);
5066e790746SPaolo Bonzini 
5076e790746SPaolo Bonzini     if (!port || !port->host_connected) {
5086e790746SPaolo Bonzini         discard_vq_data(vq, vdev);
5096e790746SPaolo Bonzini         return;
5106e790746SPaolo Bonzini     }
5116e790746SPaolo Bonzini 
5126e790746SPaolo Bonzini     if (!port->throttled) {
5136e790746SPaolo Bonzini         do_flush_queued_data(port, vq, vdev);
5146e790746SPaolo Bonzini         return;
5156e790746SPaolo Bonzini     }
5166e790746SPaolo Bonzini }
5176e790746SPaolo Bonzini 
handle_input(VirtIODevice * vdev,VirtQueue * vq)5186e790746SPaolo Bonzini static void handle_input(VirtIODevice *vdev, VirtQueue *vq)
5196e790746SPaolo Bonzini {
5204add73aaSAmit Shah     /*
5214add73aaSAmit Shah      * Users of virtio-serial would like to know when guest becomes
5224add73aaSAmit Shah      * writable again -- i.e. if a vq had stuff queued up and the
5234add73aaSAmit Shah      * guest wasn't reading at all, the host would not be able to
5244add73aaSAmit Shah      * write to the vq anymore.  Once the guest reads off something,
5254add73aaSAmit Shah      * we can start queueing things up again.  However, this call is
5264add73aaSAmit Shah      * made for each buffer addition by the guest -- even though free
5274add73aaSAmit Shah      * buffers existed prior to the current buffer addition.  This is
5284add73aaSAmit Shah      * done so as not to maintain previous state, which will need
5294add73aaSAmit Shah      * additional live-migration-related changes.
5304add73aaSAmit Shah      */
5314add73aaSAmit Shah     VirtIOSerial *vser;
5324add73aaSAmit Shah     VirtIOSerialPort *port;
5334add73aaSAmit Shah     VirtIOSerialPortClass *vsc;
5344add73aaSAmit Shah 
5354add73aaSAmit Shah     vser = VIRTIO_SERIAL(vdev);
5364add73aaSAmit Shah     port = find_port_by_vq(vser, vq);
5374add73aaSAmit Shah 
5384add73aaSAmit Shah     if (!port) {
5394add73aaSAmit Shah         return;
5404add73aaSAmit Shah     }
5414add73aaSAmit Shah     vsc = VIRTIO_SERIAL_PORT_GET_CLASS(port);
5424add73aaSAmit Shah 
5434add73aaSAmit Shah     /*
5444add73aaSAmit Shah      * If guest_connected is false, this call is being made by the
5454add73aaSAmit Shah      * early-boot queueing up of descriptors, which is just noise for
5464add73aaSAmit Shah      * the host apps -- don't disturb them in that case.
5474add73aaSAmit Shah      */
5484add73aaSAmit Shah     if (port->guest_connected && port->host_connected && vsc->guest_writable) {
5494add73aaSAmit Shah         vsc->guest_writable(port);
5504add73aaSAmit Shah     }
5516e790746SPaolo Bonzini }
5526e790746SPaolo Bonzini 
get_features(VirtIODevice * vdev,uint64_t features,Error ** errp)5539d5b731dSJason Wang static uint64_t get_features(VirtIODevice *vdev, uint64_t features,
5549d5b731dSJason Wang                              Error **errp)
5556e790746SPaolo Bonzini {
5566e790746SPaolo Bonzini     VirtIOSerial *vser;
5576e790746SPaolo Bonzini 
55876017fd2SKONRAD Frederic     vser = VIRTIO_SERIAL(vdev);
5596e790746SPaolo Bonzini 
560a06b1daeSSascha Silbe     features |= vser->host_features;
5616e790746SPaolo Bonzini     if (vser->bus.max_nr_ports > 1) {
5620cd09c3aSCornelia Huck         virtio_add_feature(&features, VIRTIO_CONSOLE_F_MULTIPORT);
5636e790746SPaolo Bonzini     }
5646e790746SPaolo Bonzini     return features;
5656e790746SPaolo Bonzini }
5666e790746SPaolo Bonzini 
5676e790746SPaolo Bonzini /* Guest requested config info */
get_config(VirtIODevice * vdev,uint8_t * config_data)5686e790746SPaolo Bonzini static void get_config(VirtIODevice *vdev, uint8_t *config_data)
5696e790746SPaolo Bonzini {
57008f432aaSDavid Gibson     VirtIOSerial *vser = VIRTIO_SERIAL(vdev);
57108f432aaSDavid Gibson     struct virtio_console_config *config =
57208f432aaSDavid Gibson         (struct virtio_console_config *)config_data;
5736e790746SPaolo Bonzini 
57408f432aaSDavid Gibson     config->cols = 0;
57508f432aaSDavid Gibson     config->rows = 0;
57608f432aaSDavid Gibson     config->max_nr_ports = virtio_tswap32(vdev,
57708f432aaSDavid Gibson                                           vser->serial.max_virtserial_ports);
5786e790746SPaolo Bonzini }
5796e790746SPaolo Bonzini 
58009da01c3SSascha Silbe /* Guest sent new config info */
set_config(VirtIODevice * vdev,const uint8_t * config_data)58109da01c3SSascha Silbe static void set_config(VirtIODevice *vdev, const uint8_t *config_data)
58209da01c3SSascha Silbe {
58309da01c3SSascha Silbe     VirtIOSerial *vser = VIRTIO_SERIAL(vdev);
58409da01c3SSascha Silbe     struct virtio_console_config *config =
58509da01c3SSascha Silbe         (struct virtio_console_config *)config_data;
58609da01c3SSascha Silbe     VirtIOSerialPort *port = find_first_connected_console(vser);
58709da01c3SSascha Silbe     VirtIOSerialPortClass *vsc;
588d434e5acSlinzhecheng     uint8_t emerg_wr_lo;
58909da01c3SSascha Silbe 
590d434e5acSlinzhecheng     if (!virtio_has_feature(vser->host_features,
591d434e5acSlinzhecheng         VIRTIO_CONSOLE_F_EMERG_WRITE) || !config->emerg_wr) {
59209da01c3SSascha Silbe         return;
59309da01c3SSascha Silbe     }
594d434e5acSlinzhecheng 
595d434e5acSlinzhecheng     emerg_wr_lo = le32_to_cpu(config->emerg_wr);
59609da01c3SSascha Silbe     /* Make sure we don't misdetect an emergency write when the guest
59709da01c3SSascha Silbe      * does a short config write after an emergency write. */
59809da01c3SSascha Silbe     config->emerg_wr = 0;
59909da01c3SSascha Silbe     if (!port) {
60009da01c3SSascha Silbe         return;
60109da01c3SSascha Silbe     }
60209da01c3SSascha Silbe     vsc = VIRTIO_SERIAL_PORT_GET_CLASS(port);
60309da01c3SSascha Silbe     (void)vsc->have_data(port, &emerg_wr_lo, 1);
60409da01c3SSascha Silbe }
60509da01c3SSascha Silbe 
guest_reset(VirtIOSerial * vser)6066e790746SPaolo Bonzini static void guest_reset(VirtIOSerial *vser)
6076e790746SPaolo Bonzini {
6086e790746SPaolo Bonzini     VirtIOSerialPort *port;
6096e790746SPaolo Bonzini     VirtIOSerialPortClass *vsc;
6106e790746SPaolo Bonzini 
6116e790746SPaolo Bonzini     QTAILQ_FOREACH(port, &vser->ports, next) {
6126e790746SPaolo Bonzini         vsc = VIRTIO_SERIAL_PORT_GET_CLASS(port);
613d4c19cdeSStefan Hajnoczi 
614d4c19cdeSStefan Hajnoczi         discard_throttle_data(port);
615d4c19cdeSStefan Hajnoczi 
6166e790746SPaolo Bonzini         if (port->guest_connected) {
6176e790746SPaolo Bonzini             port->guest_connected = false;
6186e790746SPaolo Bonzini             if (vsc->set_guest_connected) {
6196e790746SPaolo Bonzini                 vsc->set_guest_connected(port, false);
6206e790746SPaolo Bonzini             }
6216e790746SPaolo Bonzini         }
6226e790746SPaolo Bonzini     }
6236e790746SPaolo Bonzini }
6246e790746SPaolo Bonzini 
set_status(VirtIODevice * vdev,uint8_t status)6256e790746SPaolo Bonzini static void set_status(VirtIODevice *vdev, uint8_t status)
6266e790746SPaolo Bonzini {
6276e790746SPaolo Bonzini     VirtIOSerial *vser;
6286e790746SPaolo Bonzini     VirtIOSerialPort *port;
6296e790746SPaolo Bonzini 
63076017fd2SKONRAD Frederic     vser = VIRTIO_SERIAL(vdev);
6316e790746SPaolo Bonzini     port = find_port_by_id(vser, 0);
6326e790746SPaolo Bonzini 
6336e790746SPaolo Bonzini     if (port && !use_multiport(port->vser)
6346e790746SPaolo Bonzini         && (status & VIRTIO_CONFIG_S_DRIVER_OK)) {
6356e790746SPaolo Bonzini         /*
6366e790746SPaolo Bonzini          * Non-multiport guests won't be able to tell us guest
6376e790746SPaolo Bonzini          * open/close status.  Such guests can only have a port at id
6386e790746SPaolo Bonzini          * 0, so set guest_connected for such ports as soon as guest
6396e790746SPaolo Bonzini          * is up.
6406e790746SPaolo Bonzini          */
6416e790746SPaolo Bonzini         port->guest_connected = true;
6426e790746SPaolo Bonzini     }
6436e790746SPaolo Bonzini     if (!(status & VIRTIO_CONFIG_S_DRIVER_OK)) {
6446e790746SPaolo Bonzini         guest_reset(vser);
6456e790746SPaolo Bonzini     }
64655289fb0SPavel Butsykin 
64755289fb0SPavel Butsykin     QTAILQ_FOREACH(port, &vser->ports, next) {
64855289fb0SPavel Butsykin         VirtIOSerialPortClass *vsc = VIRTIO_SERIAL_PORT_GET_CLASS(port);
64955289fb0SPavel Butsykin         if (vsc->enable_backend) {
65055289fb0SPavel Butsykin             vsc->enable_backend(port, vdev->vm_running);
65155289fb0SPavel Butsykin         }
65255289fb0SPavel Butsykin     }
6536e790746SPaolo Bonzini }
6546e790746SPaolo Bonzini 
vser_reset(VirtIODevice * vdev)6556e790746SPaolo Bonzini static void vser_reset(VirtIODevice *vdev)
6566e790746SPaolo Bonzini {
6576e790746SPaolo Bonzini     VirtIOSerial *vser;
6586e790746SPaolo Bonzini 
65976017fd2SKONRAD Frederic     vser = VIRTIO_SERIAL(vdev);
6606e790746SPaolo Bonzini     guest_reset(vser);
6616e790746SPaolo Bonzini }
6626e790746SPaolo Bonzini 
virtio_serial_save_device(VirtIODevice * vdev,QEMUFile * f)66313c6855aSGreg Kurz static void virtio_serial_save_device(VirtIODevice *vdev, QEMUFile *f)
66413c6855aSGreg Kurz {
66513c6855aSGreg Kurz     VirtIOSerial *s = VIRTIO_SERIAL(vdev);
6666e790746SPaolo Bonzini     VirtIOSerialPort *port;
6676e790746SPaolo Bonzini     uint32_t nr_active_ports;
6686e790746SPaolo Bonzini     unsigned int i, max_nr_ports;
66908f432aaSDavid Gibson     struct virtio_console_config config;
6706e790746SPaolo Bonzini 
67108f432aaSDavid Gibson     /* The config space (ignored on the far end in current versions) */
67208f432aaSDavid Gibson     get_config(vdev, (uint8_t *)&config);
673d41ca5afSPaolo Bonzini     qemu_put_be16(f, config.cols);
674d41ca5afSPaolo Bonzini     qemu_put_be16(f, config.rows);
675d41ca5afSPaolo Bonzini     qemu_put_be32(f, config.max_nr_ports);
6766e790746SPaolo Bonzini 
6776e790746SPaolo Bonzini     /* The ports map */
678f2f6e00bSDavid Gibson     max_nr_ports = s->serial.max_virtserial_ports;
6797b9a27cdSMarc-André Lureau     for (i = 0; i < DIV_ROUND_UP(max_nr_ports, 32); i++) {
6806e790746SPaolo Bonzini         qemu_put_be32s(f, &s->ports_map[i]);
6816e790746SPaolo Bonzini     }
6826e790746SPaolo Bonzini 
6836e790746SPaolo Bonzini     /* Ports */
6846e790746SPaolo Bonzini 
6856e790746SPaolo Bonzini     nr_active_ports = 0;
6866e790746SPaolo Bonzini     QTAILQ_FOREACH(port, &s->ports, next) {
6876e790746SPaolo Bonzini         nr_active_ports++;
6886e790746SPaolo Bonzini     }
6896e790746SPaolo Bonzini 
6906e790746SPaolo Bonzini     qemu_put_be32s(f, &nr_active_ports);
6916e790746SPaolo Bonzini 
6926e790746SPaolo Bonzini     /*
6936e790746SPaolo Bonzini      * Items in struct VirtIOSerialPort.
6946e790746SPaolo Bonzini      */
6956e790746SPaolo Bonzini     QTAILQ_FOREACH(port, &s->ports, next) {
6966e790746SPaolo Bonzini         uint32_t elem_popped;
6976e790746SPaolo Bonzini 
6986e790746SPaolo Bonzini         qemu_put_be32s(f, &port->id);
6996e790746SPaolo Bonzini         qemu_put_byte(f, port->guest_connected);
7006e790746SPaolo Bonzini         qemu_put_byte(f, port->host_connected);
7016e790746SPaolo Bonzini 
7026e790746SPaolo Bonzini         elem_popped = 0;
70351b19ebeSPaolo Bonzini         if (port->elem) {
7046e790746SPaolo Bonzini             elem_popped = 1;
7056e790746SPaolo Bonzini         }
7066e790746SPaolo Bonzini         qemu_put_be32s(f, &elem_popped);
7076e790746SPaolo Bonzini         if (elem_popped) {
7086e790746SPaolo Bonzini             qemu_put_be32s(f, &port->iov_idx);
7096e790746SPaolo Bonzini             qemu_put_be64s(f, &port->iov_offset);
71086044b24SJason Wang             qemu_put_virtqueue_element(vdev, f, port->elem);
7116e790746SPaolo Bonzini         }
7126e790746SPaolo Bonzini     }
7136e790746SPaolo Bonzini }
7146e790746SPaolo Bonzini 
virtio_serial_post_load_timer_cb(void * opaque)7156e790746SPaolo Bonzini static void virtio_serial_post_load_timer_cb(void *opaque)
7166e790746SPaolo Bonzini {
7176e790746SPaolo Bonzini     uint32_t i;
71876017fd2SKONRAD Frederic     VirtIOSerial *s = VIRTIO_SERIAL(opaque);
7196e790746SPaolo Bonzini     VirtIOSerialPort *port;
7206e790746SPaolo Bonzini     uint8_t host_connected;
7216e790746SPaolo Bonzini     VirtIOSerialPortClass *vsc;
7226e790746SPaolo Bonzini 
7236e790746SPaolo Bonzini     if (!s->post_load) {
7246e790746SPaolo Bonzini         return;
7256e790746SPaolo Bonzini     }
7266e790746SPaolo Bonzini     for (i = 0 ; i < s->post_load->nr_active_ports; ++i) {
7276e790746SPaolo Bonzini         port = s->post_load->connected[i].port;
7286e790746SPaolo Bonzini         host_connected = s->post_load->connected[i].host_connected;
7296e790746SPaolo Bonzini         if (host_connected != port->host_connected) {
7306e790746SPaolo Bonzini             /*
7316e790746SPaolo Bonzini              * We have to let the guest know of the host connection
7326e790746SPaolo Bonzini              * status change
7336e790746SPaolo Bonzini              */
7346e790746SPaolo Bonzini             send_control_event(s, port->id, VIRTIO_CONSOLE_PORT_OPEN,
7356e790746SPaolo Bonzini                                port->host_connected);
7366e790746SPaolo Bonzini         }
7376e790746SPaolo Bonzini         vsc = VIRTIO_SERIAL_PORT_GET_CLASS(port);
7386e790746SPaolo Bonzini         if (vsc->set_guest_connected) {
7396e790746SPaolo Bonzini             vsc->set_guest_connected(port, port->guest_connected);
7406e790746SPaolo Bonzini         }
7416e790746SPaolo Bonzini     }
7426e790746SPaolo Bonzini     g_free(s->post_load->connected);
743bc72ad67SAlex Bligh     timer_free(s->post_load->timer);
7446e790746SPaolo Bonzini     g_free(s->post_load);
7456e790746SPaolo Bonzini     s->post_load = NULL;
7466e790746SPaolo Bonzini }
7476e790746SPaolo Bonzini 
fetch_active_ports_list(QEMUFile * f,VirtIOSerial * s,uint32_t nr_active_ports)74871945ae1SDr. David Alan Gilbert static int fetch_active_ports_list(QEMUFile *f,
7496e790746SPaolo Bonzini                                    VirtIOSerial *s, uint32_t nr_active_ports)
7506e790746SPaolo Bonzini {
7518607f5c3SJason Wang     VirtIODevice *vdev = VIRTIO_DEVICE(s);
7526e790746SPaolo Bonzini     uint32_t i;
7536e790746SPaolo Bonzini 
7546e790746SPaolo Bonzini     s->post_load = g_malloc0(sizeof(*s->post_load));
7556e790746SPaolo Bonzini     s->post_load->nr_active_ports = nr_active_ports;
7566e790746SPaolo Bonzini     s->post_load->connected =
7576e790746SPaolo Bonzini         g_malloc0(sizeof(*s->post_load->connected) * nr_active_ports);
7586e790746SPaolo Bonzini 
759bc72ad67SAlex Bligh     s->post_load->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
7606e790746SPaolo Bonzini                                             virtio_serial_post_load_timer_cb,
7616e790746SPaolo Bonzini                                             s);
7626e790746SPaolo Bonzini 
7636e790746SPaolo Bonzini     /* Items in struct VirtIOSerialPort */
7646e790746SPaolo Bonzini     for (i = 0; i < nr_active_ports; i++) {
7656e790746SPaolo Bonzini         VirtIOSerialPort *port;
76671945ae1SDr. David Alan Gilbert         uint32_t elem_popped;
7676e790746SPaolo Bonzini         uint32_t id;
7686e790746SPaolo Bonzini 
7696e790746SPaolo Bonzini         id = qemu_get_be32(f);
7706e790746SPaolo Bonzini         port = find_port_by_id(s, id);
7716e790746SPaolo Bonzini         if (!port) {
7726e790746SPaolo Bonzini             return -EINVAL;
7736e790746SPaolo Bonzini         }
7746e790746SPaolo Bonzini 
7756e790746SPaolo Bonzini         port->guest_connected = qemu_get_byte(f);
7766e790746SPaolo Bonzini         s->post_load->connected[i].port = port;
7776e790746SPaolo Bonzini         s->post_load->connected[i].host_connected = qemu_get_byte(f);
7786e790746SPaolo Bonzini 
7796e790746SPaolo Bonzini         qemu_get_be32s(f, &elem_popped);
7806e790746SPaolo Bonzini         if (elem_popped) {
7816e790746SPaolo Bonzini             qemu_get_be32s(f, &port->iov_idx);
7826e790746SPaolo Bonzini             qemu_get_be64s(f, &port->iov_offset);
7836e790746SPaolo Bonzini 
784ab281c17SPaolo Bonzini             port->elem =
7858607f5c3SJason Wang                 qemu_get_virtqueue_element(vdev, f, sizeof(VirtQueueElement));
7866e790746SPaolo Bonzini 
7876e790746SPaolo Bonzini             /*
7886e790746SPaolo Bonzini              *  Port was throttled on source machine.  Let's
7896e790746SPaolo Bonzini              *  unthrottle it here so data starts flowing again.
7906e790746SPaolo Bonzini              */
7916e790746SPaolo Bonzini             virtio_serial_throttle_port(port, false);
7926e790746SPaolo Bonzini         }
7936e790746SPaolo Bonzini     }
794bc72ad67SAlex Bligh     timer_mod(s->post_load->timer, 1);
7956e790746SPaolo Bonzini     return 0;
7966e790746SPaolo Bonzini }
7976e790746SPaolo Bonzini 
virtio_serial_load_device(VirtIODevice * vdev,QEMUFile * f,int version_id)79813c6855aSGreg Kurz static int virtio_serial_load_device(VirtIODevice *vdev, QEMUFile *f,
79913c6855aSGreg Kurz                                      int version_id)
80013c6855aSGreg Kurz {
80113c6855aSGreg Kurz     VirtIOSerial *s = VIRTIO_SERIAL(vdev);
80213c6855aSGreg Kurz     uint32_t max_nr_ports, nr_active_ports, ports_map;
80313c6855aSGreg Kurz     unsigned int i;
80413c6855aSGreg Kurz     int ret;
80513c6855aSGreg Kurz     uint32_t tmp;
80613c6855aSGreg Kurz 
807e38e943aSAlexander Graf     /* Unused */
808e38e943aSAlexander Graf     qemu_get_be16s(f, (uint16_t *) &tmp);
809e38e943aSAlexander Graf     qemu_get_be16s(f, (uint16_t *) &tmp);
810e38e943aSAlexander Graf     qemu_get_be32s(f, &tmp);
8116e790746SPaolo Bonzini 
812f2f6e00bSDavid Gibson     max_nr_ports = s->serial.max_virtserial_ports;
8137b9a27cdSMarc-André Lureau     for (i = 0; i < DIV_ROUND_UP(max_nr_ports, 32); i++) {
8146e790746SPaolo Bonzini         qemu_get_be32s(f, &ports_map);
8156e790746SPaolo Bonzini 
8166e790746SPaolo Bonzini         if (ports_map != s->ports_map[i]) {
8176e790746SPaolo Bonzini             /*
8186e790746SPaolo Bonzini              * Ports active on source and destination don't
8196e790746SPaolo Bonzini              * match. Fail migration.
8206e790746SPaolo Bonzini              */
8216e790746SPaolo Bonzini             return -EINVAL;
8226e790746SPaolo Bonzini         }
8236e790746SPaolo Bonzini     }
8246e790746SPaolo Bonzini 
8256e790746SPaolo Bonzini     qemu_get_be32s(f, &nr_active_ports);
8266e790746SPaolo Bonzini 
8276e790746SPaolo Bonzini     if (nr_active_ports) {
82871945ae1SDr. David Alan Gilbert         ret = fetch_active_ports_list(f, s, nr_active_ports);
8296e790746SPaolo Bonzini         if (ret) {
8306e790746SPaolo Bonzini             return ret;
8316e790746SPaolo Bonzini         }
8326e790746SPaolo Bonzini     }
8336e790746SPaolo Bonzini     return 0;
8346e790746SPaolo Bonzini }
8356e790746SPaolo Bonzini 
8366e790746SPaolo Bonzini static void virtser_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent);
8376e790746SPaolo Bonzini 
8386e790746SPaolo Bonzini static Property virtser_props[] = {
8396e790746SPaolo Bonzini     DEFINE_PROP_UINT32("nr", VirtIOSerialPort, id, VIRTIO_CONSOLE_BAD_ID),
8406e790746SPaolo Bonzini     DEFINE_PROP_STRING("name", VirtIOSerialPort, name),
8416e790746SPaolo Bonzini     DEFINE_PROP_END_OF_LIST()
8426e790746SPaolo Bonzini };
8436e790746SPaolo Bonzini 
virtser_bus_class_init(ObjectClass * klass,void * data)8446e790746SPaolo Bonzini static void virtser_bus_class_init(ObjectClass *klass, void *data)
8456e790746SPaolo Bonzini {
8466e790746SPaolo Bonzini     BusClass *k = BUS_CLASS(klass);
8476e790746SPaolo Bonzini     k->print_dev = virtser_bus_dev_print;
8486e790746SPaolo Bonzini }
8496e790746SPaolo Bonzini 
8506e790746SPaolo Bonzini static const TypeInfo virtser_bus_info = {
8516e790746SPaolo Bonzini     .name = TYPE_VIRTIO_SERIAL_BUS,
8526e790746SPaolo Bonzini     .parent = TYPE_BUS,
8536e790746SPaolo Bonzini     .instance_size = sizeof(VirtIOSerialBus),
8546e790746SPaolo Bonzini     .class_init = virtser_bus_class_init,
8556e790746SPaolo Bonzini };
8566e790746SPaolo Bonzini 
virtser_bus_dev_print(Monitor * mon,DeviceState * qdev,int indent)8576e790746SPaolo Bonzini static void virtser_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent)
8586e790746SPaolo Bonzini {
859d9eb0be2SCao jin     VirtIOSerialPort *port = VIRTIO_SERIAL_PORT(qdev);
8606e790746SPaolo Bonzini 
8616e790746SPaolo Bonzini     monitor_printf(mon, "%*sport %d, guest %s, host %s, throttle %s\n",
8626e790746SPaolo Bonzini                    indent, "", port->id,
8636e790746SPaolo Bonzini                    port->guest_connected ? "on" : "off",
8646e790746SPaolo Bonzini                    port->host_connected ? "on" : "off",
8656e790746SPaolo Bonzini                    port->throttled ? "on" : "off");
8666e790746SPaolo Bonzini }
8676e790746SPaolo Bonzini 
8686e790746SPaolo Bonzini /* This function is only used if a port id is not provided by the user */
find_free_port_id(VirtIOSerial * vser)8696e790746SPaolo Bonzini static uint32_t find_free_port_id(VirtIOSerial *vser)
8706e790746SPaolo Bonzini {
8716e790746SPaolo Bonzini     unsigned int i, max_nr_ports;
8726e790746SPaolo Bonzini 
873f2f6e00bSDavid Gibson     max_nr_ports = vser->serial.max_virtserial_ports;
8747b9a27cdSMarc-André Lureau     for (i = 0; i < DIV_ROUND_UP(max_nr_ports, 32); i++) {
875bd2a8884SStefan Hajnoczi         uint32_t map, zeroes;
8766e790746SPaolo Bonzini 
8776e790746SPaolo Bonzini         map = vser->ports_map[i];
878bd2a8884SStefan Hajnoczi         zeroes = ctz32(~map);
879bd2a8884SStefan Hajnoczi         if (zeroes != 32) {
880bd2a8884SStefan Hajnoczi             return zeroes + i * 32;
8816e790746SPaolo Bonzini         }
8826e790746SPaolo Bonzini     }
8836e790746SPaolo Bonzini     return VIRTIO_CONSOLE_BAD_ID;
8846e790746SPaolo Bonzini }
8856e790746SPaolo Bonzini 
mark_port_added(VirtIOSerial * vser,uint32_t port_id)8866e790746SPaolo Bonzini static void mark_port_added(VirtIOSerial *vser, uint32_t port_id)
8876e790746SPaolo Bonzini {
8886e790746SPaolo Bonzini     unsigned int i;
8896e790746SPaolo Bonzini 
8906e790746SPaolo Bonzini     i = port_id / 32;
8916e790746SPaolo Bonzini     vser->ports_map[i] |= 1U << (port_id % 32);
8926e790746SPaolo Bonzini }
8936e790746SPaolo Bonzini 
add_port(VirtIOSerial * vser,uint32_t port_id)8946e790746SPaolo Bonzini static void add_port(VirtIOSerial *vser, uint32_t port_id)
8956e790746SPaolo Bonzini {
8966e790746SPaolo Bonzini     mark_port_added(vser, port_id);
8976e790746SPaolo Bonzini     send_control_event(vser, port_id, VIRTIO_CONSOLE_PORT_ADD, 1);
8986e790746SPaolo Bonzini }
8996e790746SPaolo Bonzini 
remove_port(VirtIOSerial * vser,uint32_t port_id)9006e790746SPaolo Bonzini static void remove_port(VirtIOSerial *vser, uint32_t port_id)
9016e790746SPaolo Bonzini {
9026e790746SPaolo Bonzini     VirtIOSerialPort *port;
90357d84cf3SAmit Shah 
90457d84cf3SAmit Shah     /*
90557d84cf3SAmit Shah      * Don't mark port 0 removed -- we explicitly reserve it for
90657d84cf3SAmit Shah      * backward compat with older guests, ensure a virtconsole device
90757d84cf3SAmit Shah      * unplug retains the reservation.
90857d84cf3SAmit Shah      */
90957d84cf3SAmit Shah     if (port_id) {
9106e790746SPaolo Bonzini         unsigned int i;
9116e790746SPaolo Bonzini 
9126e790746SPaolo Bonzini         i = port_id / 32;
9136e790746SPaolo Bonzini         vser->ports_map[i] &= ~(1U << (port_id % 32));
91457d84cf3SAmit Shah     }
9156e790746SPaolo Bonzini 
9166e790746SPaolo Bonzini     port = find_port_by_id(vser, port_id);
9176e790746SPaolo Bonzini     /*
9186e790746SPaolo Bonzini      * This function is only called from qdev's unplug callback; if we
9196e790746SPaolo Bonzini      * get a NULL port here, we're in trouble.
9206e790746SPaolo Bonzini      */
9216e790746SPaolo Bonzini     assert(port);
9226e790746SPaolo Bonzini 
9236e790746SPaolo Bonzini     /* Flush out any unconsumed buffers first */
924d4c19cdeSStefan Hajnoczi     discard_throttle_data(port);
92576017fd2SKONRAD Frederic     discard_vq_data(port->ovq, VIRTIO_DEVICE(port->vser));
9266e790746SPaolo Bonzini 
9276e790746SPaolo Bonzini     send_control_event(vser, port->id, VIRTIO_CONSOLE_PORT_REMOVE, 1);
9286e790746SPaolo Bonzini }
9296e790746SPaolo Bonzini 
virtser_port_device_realize(DeviceState * dev,Error ** errp)9302ef66625SAndreas Färber static void virtser_port_device_realize(DeviceState *dev, Error **errp)
9316e790746SPaolo Bonzini {
9322ef66625SAndreas Färber     VirtIOSerialPort *port = VIRTIO_SERIAL_PORT(dev);
9336e790746SPaolo Bonzini     VirtIOSerialPortClass *vsc = VIRTIO_SERIAL_PORT_GET_CLASS(port);
9342ef66625SAndreas Färber     VirtIOSerialBus *bus = VIRTIO_SERIAL_BUS(qdev_get_parent_bus(dev));
9352ef66625SAndreas Färber     int max_nr_ports;
9366e790746SPaolo Bonzini     bool plugging_port0;
9372ef66625SAndreas Färber     Error *err = NULL;
9386e790746SPaolo Bonzini 
9396e790746SPaolo Bonzini     port->vser = bus->vser;
9406e790746SPaolo Bonzini 
9416e790746SPaolo Bonzini     assert(vsc->have_data);
9426e790746SPaolo Bonzini 
9436e790746SPaolo Bonzini     /*
9446e790746SPaolo Bonzini      * Is the first console port we're seeing? If so, put it up at
9456e790746SPaolo Bonzini      * location 0. This is done for backward compatibility (old
9466e790746SPaolo Bonzini      * kernel, new qemu).
9476e790746SPaolo Bonzini      */
9486e790746SPaolo Bonzini     plugging_port0 = vsc->is_console && !find_port_by_id(port->vser, 0);
9496e790746SPaolo Bonzini 
9506e790746SPaolo Bonzini     if (find_port_by_id(port->vser, port->id)) {
9512ef66625SAndreas Färber         error_setg(errp, "virtio-serial-bus: A port already exists at id %u",
9526e790746SPaolo Bonzini                    port->id);
9532ef66625SAndreas Färber         return;
9546e790746SPaolo Bonzini     }
9556e790746SPaolo Bonzini 
9567eb73114SMarc-André Lureau     if (port->name != NULL && find_port_by_name(port->name)) {
957d0a0bfe6SAmit Shah         error_setg(errp, "virtio-serial-bus: A port already exists by name %s",
958d0a0bfe6SAmit Shah                    port->name);
959d0a0bfe6SAmit Shah         return;
960d0a0bfe6SAmit Shah     }
961d0a0bfe6SAmit Shah 
9626e790746SPaolo Bonzini     if (port->id == VIRTIO_CONSOLE_BAD_ID) {
9636e790746SPaolo Bonzini         if (plugging_port0) {
9646e790746SPaolo Bonzini             port->id = 0;
9656e790746SPaolo Bonzini         } else {
9666e790746SPaolo Bonzini             port->id = find_free_port_id(port->vser);
9676e790746SPaolo Bonzini             if (port->id == VIRTIO_CONSOLE_BAD_ID) {
9682ef66625SAndreas Färber                 error_setg(errp, "virtio-serial-bus: Maximum port limit for "
9692ef66625SAndreas Färber                                  "this device reached");
9702ef66625SAndreas Färber                 return;
9716e790746SPaolo Bonzini             }
9726e790746SPaolo Bonzini         }
9736e790746SPaolo Bonzini     }
9746e790746SPaolo Bonzini 
975f2f6e00bSDavid Gibson     max_nr_ports = port->vser->serial.max_virtserial_ports;
9766e790746SPaolo Bonzini     if (port->id >= max_nr_ports) {
9772ef66625SAndreas Färber         error_setg(errp, "virtio-serial-bus: Out-of-range port id specified, "
9782ef66625SAndreas Färber                          "max. allowed: %u", max_nr_ports - 1);
9792ef66625SAndreas Färber         return;
9806e790746SPaolo Bonzini     }
9816e790746SPaolo Bonzini 
9822ef66625SAndreas Färber     vsc->realize(dev, &err);
9832ef66625SAndreas Färber     if (err != NULL) {
9842ef66625SAndreas Färber         error_propagate(errp, err);
9852ef66625SAndreas Färber         return;
9866e790746SPaolo Bonzini     }
9876e790746SPaolo Bonzini 
988*b4295bffSPhilippe Mathieu-Daudé     port->bh = virtio_bh_new_guarded(dev, flush_queued_data_bh, port);
98951b19ebeSPaolo Bonzini     port->elem = NULL;
9900ddef15bSIgor Mammedov }
9910ddef15bSIgor Mammedov 
virtser_port_device_plug(HotplugHandler * hotplug_dev,DeviceState * dev,Error ** errp)9920ddef15bSIgor Mammedov static void virtser_port_device_plug(HotplugHandler *hotplug_dev,
9930ddef15bSIgor Mammedov                                      DeviceState *dev, Error **errp)
9940ddef15bSIgor Mammedov {
9950ddef15bSIgor Mammedov     VirtIOSerialPort *port = VIRTIO_SERIAL_PORT(dev);
9966e790746SPaolo Bonzini 
9976e790746SPaolo Bonzini     QTAILQ_INSERT_TAIL(&port->vser->ports, port, next);
9986e790746SPaolo Bonzini     port->ivq = port->vser->ivqs[port->id];
9996e790746SPaolo Bonzini     port->ovq = port->vser->ovqs[port->id];
10006e790746SPaolo Bonzini 
10016e790746SPaolo Bonzini     add_port(port->vser, port->id);
10026e790746SPaolo Bonzini 
10036e790746SPaolo Bonzini     /* Send an update to the guest about this new port added */
10040ddef15bSIgor Mammedov     virtio_notify_config(VIRTIO_DEVICE(hotplug_dev));
10056e790746SPaolo Bonzini }
10066e790746SPaolo Bonzini 
virtser_port_device_unrealize(DeviceState * dev)1007b69c3c21SMarkus Armbruster static void virtser_port_device_unrealize(DeviceState *dev)
10086e790746SPaolo Bonzini {
10092ef66625SAndreas Färber     VirtIOSerialPort *port = VIRTIO_SERIAL_PORT(dev);
10102ef66625SAndreas Färber     VirtIOSerialPortClass *vsc = VIRTIO_SERIAL_PORT_GET_CLASS(dev);
10116e790746SPaolo Bonzini     VirtIOSerial *vser = port->vser;
10126e790746SPaolo Bonzini 
10136e790746SPaolo Bonzini     qemu_bh_delete(port->bh);
10146e790746SPaolo Bonzini     remove_port(port->vser, port->id);
10156e790746SPaolo Bonzini 
10166e790746SPaolo Bonzini     QTAILQ_REMOVE(&vser->ports, port, next);
10176e790746SPaolo Bonzini 
10182ef66625SAndreas Färber     if (vsc->unrealize) {
1019b69c3c21SMarkus Armbruster         vsc->unrealize(dev);
10206e790746SPaolo Bonzini     }
10216e790746SPaolo Bonzini }
10226e790746SPaolo Bonzini 
virtio_serial_device_realize(DeviceState * dev,Error ** errp)102386346244SAndreas Färber static void virtio_serial_device_realize(DeviceState *dev, Error **errp)
10246e790746SPaolo Bonzini {
102586346244SAndreas Färber     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
1026b1a20c3fSAndreas Färber     VirtIOSerial *vser = VIRTIO_SERIAL(dev);
10276e790746SPaolo Bonzini     uint32_t i, max_supported_ports;
1028a06b1daeSSascha Silbe     size_t config_size = sizeof(struct virtio_console_config);
10296e790746SPaolo Bonzini 
103034b95b2cSKONRAD Frederic     if (!vser->serial.max_virtserial_ports) {
103186346244SAndreas Färber         error_setg(errp, "Maximum number of serial ports not specified");
103286346244SAndreas Färber         return;
103334b95b2cSKONRAD Frederic     }
10346e790746SPaolo Bonzini 
10356e790746SPaolo Bonzini     /* Each port takes 2 queues, and one pair is for the control queue */
103687b3bd1cSJason Wang     max_supported_ports = VIRTIO_QUEUE_MAX / 2 - 1;
10376e790746SPaolo Bonzini 
103834b95b2cSKONRAD Frederic     if (vser->serial.max_virtserial_ports > max_supported_ports) {
103986346244SAndreas Färber         error_setg(errp, "maximum ports supported: %u", max_supported_ports);
104086346244SAndreas Färber         return;
10416e790746SPaolo Bonzini     }
10426e790746SPaolo Bonzini 
1043a06b1daeSSascha Silbe     if (!virtio_has_feature(vser->host_features,
1044a06b1daeSSascha Silbe                             VIRTIO_CONSOLE_F_EMERG_WRITE)) {
1045a06b1daeSSascha Silbe         config_size = offsetof(struct virtio_console_config, emerg_wr);
1046a06b1daeSSascha Silbe     }
10473857cd5cSJonah Palmer     virtio_init(vdev, VIRTIO_ID_CONSOLE, config_size);
10486e790746SPaolo Bonzini 
10496e790746SPaolo Bonzini     /* Spawn a new virtio-serial bus on which the ports will ride as devices */
1050d637e1dcSPeter Maydell     qbus_init(&vser->bus, sizeof(vser->bus), TYPE_VIRTIO_SERIAL_BUS,
1051b1a20c3fSAndreas Färber               dev, vdev->bus_name);
10529bc6bfdfSMarkus Armbruster     qbus_set_hotplug_handler(BUS(&vser->bus), OBJECT(vser));
10536e790746SPaolo Bonzini     vser->bus.vser = vser;
10546e790746SPaolo Bonzini     QTAILQ_INIT(&vser->ports);
10556e790746SPaolo Bonzini 
105634b95b2cSKONRAD Frederic     vser->bus.max_nr_ports = vser->serial.max_virtserial_ports;
1057b21e2380SMarkus Armbruster     vser->ivqs = g_new(VirtQueue *, vser->serial.max_virtserial_ports);
1058b21e2380SMarkus Armbruster     vser->ovqs = g_new(VirtQueue *, vser->serial.max_virtserial_ports);
10596e790746SPaolo Bonzini 
10606e790746SPaolo Bonzini     /* Add a queue for host to guest transfers for port 0 (backward compat) */
10616e790746SPaolo Bonzini     vser->ivqs[0] = virtio_add_queue(vdev, 128, handle_input);
10626e790746SPaolo Bonzini     /* Add a queue for guest to host transfers for port 0 (backward compat) */
10636e790746SPaolo Bonzini     vser->ovqs[0] = virtio_add_queue(vdev, 128, handle_output);
10646e790746SPaolo Bonzini 
10656e790746SPaolo Bonzini     /* TODO: host to guest notifications can get dropped
10666e790746SPaolo Bonzini      * if the queue fills up. Implement queueing in host,
10676e790746SPaolo Bonzini      * this might also make it possible to reduce the control
10686e790746SPaolo Bonzini      * queue size: as guest preposts buffers there,
10696e790746SPaolo Bonzini      * this will save 4Kbyte of guest memory per entry. */
10706e790746SPaolo Bonzini 
10716e790746SPaolo Bonzini     /* control queue: host to guest */
10726e790746SPaolo Bonzini     vser->c_ivq = virtio_add_queue(vdev, 32, control_in);
10736e790746SPaolo Bonzini     /* control queue: guest to host */
10746e790746SPaolo Bonzini     vser->c_ovq = virtio_add_queue(vdev, 32, control_out);
10756e790746SPaolo Bonzini 
10766e790746SPaolo Bonzini     for (i = 1; i < vser->bus.max_nr_ports; i++) {
10776e790746SPaolo Bonzini         /* Add a per-port queue for host to guest transfers */
10786e790746SPaolo Bonzini         vser->ivqs[i] = virtio_add_queue(vdev, 128, handle_input);
10796e790746SPaolo Bonzini         /* Add a per-per queue for guest to host transfers */
10806e790746SPaolo Bonzini         vser->ovqs[i] = virtio_add_queue(vdev, 128, handle_output);
10816e790746SPaolo Bonzini     }
10826e790746SPaolo Bonzini 
10837b9a27cdSMarc-André Lureau     vser->ports_map = g_malloc0((DIV_ROUND_UP(vser->serial.max_virtserial_ports, 32))
10846e790746SPaolo Bonzini         * sizeof(vser->ports_map[0]));
10856e790746SPaolo Bonzini     /*
10866e790746SPaolo Bonzini      * Reserve location 0 for a console port for backward compat
10876e790746SPaolo Bonzini      * (old kernel, new qemu)
10886e790746SPaolo Bonzini      */
10896e790746SPaolo Bonzini     mark_port_added(vser, 0);
10906e790746SPaolo Bonzini 
10916e790746SPaolo Bonzini     vser->post_load = NULL;
10926e790746SPaolo Bonzini 
1093a1857ad1SAmit Shah     QLIST_INSERT_HEAD(&vserdevices.devices, vser, next);
10946e790746SPaolo Bonzini }
10956e790746SPaolo Bonzini 
virtio_serial_port_class_init(ObjectClass * klass,void * data)10966e790746SPaolo Bonzini static void virtio_serial_port_class_init(ObjectClass *klass, void *data)
10976e790746SPaolo Bonzini {
10986e790746SPaolo Bonzini     DeviceClass *k = DEVICE_CLASS(klass);
10992ef66625SAndreas Färber 
1100125ee0edSMarcel Apfelbaum     set_bit(DEVICE_CATEGORY_INPUT, k->categories);
11016e790746SPaolo Bonzini     k->bus_type = TYPE_VIRTIO_SERIAL_BUS;
11022ef66625SAndreas Färber     k->realize = virtser_port_device_realize;
11032ef66625SAndreas Färber     k->unrealize = virtser_port_device_unrealize;
11044f67d30bSMarc-André Lureau     device_class_set_props(k, virtser_props);
11056e790746SPaolo Bonzini }
11066e790746SPaolo Bonzini 
11076e790746SPaolo Bonzini static const TypeInfo virtio_serial_port_type_info = {
11086e790746SPaolo Bonzini     .name = TYPE_VIRTIO_SERIAL_PORT,
11096e790746SPaolo Bonzini     .parent = TYPE_DEVICE,
11106e790746SPaolo Bonzini     .instance_size = sizeof(VirtIOSerialPort),
11116e790746SPaolo Bonzini     .abstract = true,
11126e790746SPaolo Bonzini     .class_size = sizeof(VirtIOSerialPortClass),
11136e790746SPaolo Bonzini     .class_init = virtio_serial_port_class_init,
11146e790746SPaolo Bonzini };
11156e790746SPaolo Bonzini 
virtio_serial_device_unrealize(DeviceState * dev)1116b69c3c21SMarkus Armbruster static void virtio_serial_device_unrealize(DeviceState *dev)
11172cd2b016SKONRAD Frederic {
1118306ec6c3SAndreas Färber     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
1119306ec6c3SAndreas Färber     VirtIOSerial *vser = VIRTIO_SERIAL(dev);
1120e615c157SPan Nengyuan     int i;
11212cd2b016SKONRAD Frederic 
1122a1857ad1SAmit Shah     QLIST_REMOVE(vser, next);
1123a1857ad1SAmit Shah 
1124e615c157SPan Nengyuan     virtio_delete_queue(vser->c_ivq);
1125e615c157SPan Nengyuan     virtio_delete_queue(vser->c_ovq);
1126e615c157SPan Nengyuan     for (i = 0; i < vser->bus.max_nr_ports; i++) {
1127e615c157SPan Nengyuan         virtio_delete_queue(vser->ivqs[i]);
1128e615c157SPan Nengyuan         virtio_delete_queue(vser->ovqs[i]);
1129e615c157SPan Nengyuan     }
1130e615c157SPan Nengyuan 
11312cd2b016SKONRAD Frederic     g_free(vser->ivqs);
11322cd2b016SKONRAD Frederic     g_free(vser->ovqs);
11332cd2b016SKONRAD Frederic     g_free(vser->ports_map);
11342cd2b016SKONRAD Frederic     if (vser->post_load) {
11352cd2b016SKONRAD Frederic         g_free(vser->post_load->connected);
1136bc72ad67SAlex Bligh         timer_free(vser->post_load->timer);
11372cd2b016SKONRAD Frederic         g_free(vser->post_load);
11382cd2b016SKONRAD Frederic     }
1139f811f970SLadi Prosek 
11409bc6bfdfSMarkus Armbruster     qbus_set_hotplug_handler(BUS(&vser->bus), NULL);
1141f811f970SLadi Prosek 
11426a1a8cc7SKONRAD Frederic     virtio_cleanup(vdev);
11432cd2b016SKONRAD Frederic }
11442cd2b016SKONRAD Frederic 
114542e6c039SDr. David Alan Gilbert /* Note: 'console' is used for backwards compatibility */
114697eed24fSHalil Pasic static const VMStateDescription vmstate_virtio_console = {
114797eed24fSHalil Pasic     .name = "virtio-console",
114897eed24fSHalil Pasic     .minimum_version_id = 3,
114997eed24fSHalil Pasic     .version_id = 3,
11502f6cab05SRichard Henderson     .fields = (const VMStateField[]) {
115197eed24fSHalil Pasic         VMSTATE_VIRTIO_DEVICE,
115297eed24fSHalil Pasic         VMSTATE_END_OF_LIST()
115397eed24fSHalil Pasic     },
115497eed24fSHalil Pasic };
115542e6c039SDr. David Alan Gilbert 
11562cd2b016SKONRAD Frederic static Property virtio_serial_properties[] = {
1157448777c4SShannon Zhao     DEFINE_PROP_UINT32("max_ports", VirtIOSerial, serial.max_virtserial_ports,
1158448777c4SShannon Zhao                                                   31),
1159a06b1daeSSascha Silbe     DEFINE_PROP_BIT64("emergency-write", VirtIOSerial, host_features,
1160a06b1daeSSascha Silbe                       VIRTIO_CONSOLE_F_EMERG_WRITE, true),
11612cd2b016SKONRAD Frederic     DEFINE_PROP_END_OF_LIST(),
11622cd2b016SKONRAD Frederic };
11632cd2b016SKONRAD Frederic 
virtio_serial_class_init(ObjectClass * klass,void * data)11642cd2b016SKONRAD Frederic static void virtio_serial_class_init(ObjectClass *klass, void *data)
11652cd2b016SKONRAD Frederic {
11662cd2b016SKONRAD Frederic     DeviceClass *dc = DEVICE_CLASS(klass);
11672cd2b016SKONRAD Frederic     VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
11680ddef15bSIgor Mammedov     HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass);
116986346244SAndreas Färber 
1170a1857ad1SAmit Shah     QLIST_INIT(&vserdevices.devices);
1171a1857ad1SAmit Shah 
11724f67d30bSMarc-André Lureau     device_class_set_props(dc, virtio_serial_properties);
117342e6c039SDr. David Alan Gilbert     dc->vmsd = &vmstate_virtio_console;
1174125ee0edSMarcel Apfelbaum     set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
117586346244SAndreas Färber     vdc->realize = virtio_serial_device_realize;
1176306ec6c3SAndreas Färber     vdc->unrealize = virtio_serial_device_unrealize;
11772cd2b016SKONRAD Frederic     vdc->get_features = get_features;
11782cd2b016SKONRAD Frederic     vdc->get_config = get_config;
117909da01c3SSascha Silbe     vdc->set_config = set_config;
11802cd2b016SKONRAD Frederic     vdc->set_status = set_status;
11812cd2b016SKONRAD Frederic     vdc->reset = vser_reset;
118213c6855aSGreg Kurz     vdc->save = virtio_serial_save_device;
118313c6855aSGreg Kurz     vdc->load = virtio_serial_load_device;
11840ddef15bSIgor Mammedov     hc->plug = virtser_port_device_plug;
11850ddef15bSIgor Mammedov     hc->unplug = qdev_simple_device_unplug_cb;
11862cd2b016SKONRAD Frederic }
11872cd2b016SKONRAD Frederic 
11882cd2b016SKONRAD Frederic static const TypeInfo virtio_device_info = {
11892cd2b016SKONRAD Frederic     .name = TYPE_VIRTIO_SERIAL,
11902cd2b016SKONRAD Frederic     .parent = TYPE_VIRTIO_DEVICE,
11912cd2b016SKONRAD Frederic     .instance_size = sizeof(VirtIOSerial),
11922cd2b016SKONRAD Frederic     .class_init = virtio_serial_class_init,
11930ddef15bSIgor Mammedov     .interfaces = (InterfaceInfo[]) {
11940ddef15bSIgor Mammedov         { TYPE_HOTPLUG_HANDLER },
11950ddef15bSIgor Mammedov         { }
11960ddef15bSIgor Mammedov     }
11972cd2b016SKONRAD Frederic };
11982cd2b016SKONRAD Frederic 
virtio_serial_register_types(void)11996e790746SPaolo Bonzini static void virtio_serial_register_types(void)
12006e790746SPaolo Bonzini {
12016e790746SPaolo Bonzini     type_register_static(&virtser_bus_info);
12026e790746SPaolo Bonzini     type_register_static(&virtio_serial_port_type_info);
12032cd2b016SKONRAD Frederic     type_register_static(&virtio_device_info);
12046e790746SPaolo Bonzini }
12056e790746SPaolo Bonzini 
12066e790746SPaolo Bonzini type_init(virtio_serial_register_types)
1207