xref: /openbmc/qemu/hw/char/virtio-serial-bus.c (revision 7b9a27cd)
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