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