#include "qemu/osdep.h"
#include "qapi/error.h"
#include "qemu/iov.h"
+#include "qemu/main-loop.h"
+#include "qemu/module.h"
+#include "migration/qemu-file-types.h"
#include "monitor/monitor.h"
#include "qemu/error-report.h"
#include "qemu/queue.h"
return NULL;
}
+static VirtIOSerialPort *find_first_connected_console(VirtIOSerial *vser)
+{
+ VirtIOSerialPort *port;
+
+ QTAILQ_FOREACH(port, &vser->ports, next) {
+ VirtIOSerialPortClass const *vsc = VIRTIO_SERIAL_PORT_GET_CLASS(port);
+ if (vsc->is_console && port->host_connected) {
+ return port;
+ }
+ }
+ return NULL;
+}
+
static bool use_multiport(VirtIOSerial *vser)
{
VirtIODevice *vdev = VIRTIO_DEVICE(vser);
virtio_notify(vdev, vq);
}
+static void discard_throttle_data(VirtIOSerialPort *port)
+{
+ if (port->elem) {
+ virtqueue_detach_element(port->ovq, port->elem, 0);
+ g_free(port->elem);
+ port->elem = NULL;
+ }
+}
+
static void do_flush_queued_data(VirtIOSerialPort *port, VirtQueue *vq,
VirtIODevice *vdev)
{
port->elem->out_sg[i].iov_base
+ port->iov_offset,
buf_size);
+ if (!port->elem) { /* bail if we got disconnected */
+ return;
+ }
if (port->throttled) {
port->iov_idx = i;
if (ret > 0) {
* consume, reset the throttling flag and discard the data.
*/
port->throttled = false;
+ discard_throttle_data(port);
discard_vq_data(port->ovq, VIRTIO_DEVICE(port->vser));
send_control_event(port->vser, port->id, VIRTIO_CONSOLE_PORT_OPEN, 0);
vser = VIRTIO_SERIAL(vdev);
+ features |= vser->host_features;
if (vser->bus.max_nr_ports > 1) {
virtio_add_feature(&features, VIRTIO_CONSOLE_F_MULTIPORT);
}
vser->serial.max_virtserial_ports);
}
+/* Guest sent new config info */
+static void set_config(VirtIODevice *vdev, const uint8_t *config_data)
+{
+ VirtIOSerial *vser = VIRTIO_SERIAL(vdev);
+ struct virtio_console_config *config =
+ (struct virtio_console_config *)config_data;
+ VirtIOSerialPort *port = find_first_connected_console(vser);
+ VirtIOSerialPortClass *vsc;
+ uint8_t emerg_wr_lo;
+
+ if (!virtio_has_feature(vser->host_features,
+ VIRTIO_CONSOLE_F_EMERG_WRITE) || !config->emerg_wr) {
+ return;
+ }
+
+ emerg_wr_lo = le32_to_cpu(config->emerg_wr);
+ /* Make sure we don't misdetect an emergency write when the guest
+ * does a short config write after an emergency write. */
+ config->emerg_wr = 0;
+ if (!port) {
+ return;
+ }
+ vsc = VIRTIO_SERIAL_PORT_GET_CLASS(port);
+ (void)vsc->have_data(port, &emerg_wr_lo, 1);
+}
+
static void guest_reset(VirtIOSerial *vser)
{
VirtIOSerialPort *port;
QTAILQ_FOREACH(port, &vser->ports, next) {
vsc = VIRTIO_SERIAL_PORT_GET_CLASS(port);
+
+ discard_throttle_data(port);
+
if (port->guest_connected) {
port->guest_connected = false;
if (vsc->set_guest_connected) {
if (!(status & VIRTIO_CONFIG_S_DRIVER_OK)) {
guest_reset(vser);
}
+
+ QTAILQ_FOREACH(port, &vser->ports, next) {
+ VirtIOSerialPortClass *vsc = VIRTIO_SERIAL_PORT_GET_CLASS(port);
+ if (vsc->enable_backend) {
+ vsc->enable_backend(port, vdev->vm_running);
+ }
+ }
}
static void vser_reset(VirtIODevice *vdev)
guest_reset(vser);
}
-static void virtio_serial_save(QEMUFile *f, void *opaque)
-{
- /* The virtio device */
- virtio_save(VIRTIO_DEVICE(opaque), f);
-}
-
static void virtio_serial_save_device(VirtIODevice *vdev, QEMUFile *f)
{
VirtIOSerial *s = VIRTIO_SERIAL(vdev);
/* The config space (ignored on the far end in current versions) */
get_config(vdev, (uint8_t *)&config);
- qemu_put_be16s(f, &config.cols);
- qemu_put_be16s(f, &config.rows);
- qemu_put_be32s(f, &config.max_nr_ports);
+ qemu_put_be16(f, config.cols);
+ qemu_put_be16(f, config.rows);
+ qemu_put_be32(f, config.max_nr_ports);
/* The ports map */
max_nr_ports = s->serial.max_virtserial_ports;
- for (i = 0; i < (max_nr_ports + 31) / 32; i++) {
+ for (i = 0; i < DIV_ROUND_UP(max_nr_ports, 32); i++) {
qemu_put_be32s(f, &s->ports_map[i]);
}
qemu_put_byte(f, port->guest_connected);
qemu_put_byte(f, port->host_connected);
- elem_popped = 0;
+ elem_popped = 0;
if (port->elem) {
elem_popped = 1;
}
}
}
g_free(s->post_load->connected);
+ timer_del(s->post_load->timer);
timer_free(s->post_load->timer);
g_free(s->post_load);
s->post_load = NULL;
static int fetch_active_ports_list(QEMUFile *f,
VirtIOSerial *s, uint32_t nr_active_ports)
{
+ VirtIODevice *vdev = VIRTIO_DEVICE(s);
uint32_t i;
s->post_load = g_malloc0(sizeof(*s->post_load));
qemu_get_be64s(f, &port->iov_offset);
port->elem =
- qemu_get_virtqueue_element(f, sizeof(VirtQueueElement));
+ qemu_get_virtqueue_element(vdev, f, sizeof(VirtQueueElement));
/*
* Port was throttled on source machine. Let's
return 0;
}
-static int virtio_serial_load(QEMUFile *f, void *opaque, int version_id)
-{
- if (version_id != 3) {
- return -EINVAL;
- }
-
- /* The virtio device */
- return virtio_load(VIRTIO_DEVICE(opaque), f, version_id);
-}
-
static int virtio_serial_load_device(VirtIODevice *vdev, QEMUFile *f,
int version_id)
{
qemu_get_be32s(f, &tmp);
max_nr_ports = s->serial.max_virtserial_ports;
- for (i = 0; i < (max_nr_ports + 31) / 32; i++) {
+ for (i = 0; i < DIV_ROUND_UP(max_nr_ports, 32); i++) {
qemu_get_be32s(f, &ports_map);
if (ports_map != s->ports_map[i]) {
unsigned int i, max_nr_ports;
max_nr_ports = vser->serial.max_virtserial_ports;
- for (i = 0; i < (max_nr_ports + 31) / 32; i++) {
+ for (i = 0; i < DIV_ROUND_UP(max_nr_ports, 32); i++) {
uint32_t map, zeroes;
map = vser->ports_map[i];
assert(port);
/* Flush out any unconsumed buffers first */
+ discard_throttle_data(port);
discard_vq_data(port->ovq, VIRTIO_DEVICE(port->vser));
send_control_event(vser, port->id, VIRTIO_CONSOLE_PORT_REMOVE, 1);
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
VirtIOSerial *vser = VIRTIO_SERIAL(dev);
uint32_t i, max_supported_ports;
+ size_t config_size = sizeof(struct virtio_console_config);
if (!vser->serial.max_virtserial_ports) {
error_setg(errp, "Maximum number of serial ports not specified");
return;
}
- /* We don't support emergency write, skip it for now. */
- /* TODO: cleaner fix, depending on host features. */
+ if (!virtio_has_feature(vser->host_features,
+ VIRTIO_CONSOLE_F_EMERG_WRITE)) {
+ config_size = offsetof(struct virtio_console_config, emerg_wr);
+ }
virtio_init(vdev, "virtio-serial", VIRTIO_ID_CONSOLE,
- offsetof(struct virtio_console_config, emerg_wr));
+ config_size);
/* Spawn a new virtio-serial bus on which the ports will ride as devices */
qbus_create_inplace(&vser->bus, sizeof(vser->bus), TYPE_VIRTIO_SERIAL_BUS,
dev, vdev->bus_name);
- qbus_set_hotplug_handler(BUS(&vser->bus), DEVICE(vser), errp);
+ qbus_set_hotplug_handler(BUS(&vser->bus), OBJECT(vser), errp);
vser->bus.vser = vser;
QTAILQ_INIT(&vser->ports);
vser->ovqs[i] = virtio_add_queue(vdev, 128, handle_output);
}
- vser->ports_map = g_malloc0(((vser->serial.max_virtserial_ports + 31) / 32)
+ vser->ports_map = g_malloc0((DIV_ROUND_UP(vser->serial.max_virtserial_ports, 32))
* sizeof(vser->ports_map[0]));
/*
* Reserve location 0 for a console port for backward compat
vser->post_load = NULL;
- /*
- * Register for the savevm section with the virtio-console name
- * to preserve backward compat
- */
- register_savevm(dev, "virtio-console", -1, 3, virtio_serial_save,
- virtio_serial_load, vser);
-
QLIST_INSERT_HEAD(&vserdevices.devices, vser, next);
}
QLIST_REMOVE(vser, next);
- unregister_savevm(dev, "virtio-console", vser);
-
g_free(vser->ivqs);
g_free(vser->ovqs);
g_free(vser->ports_map);
timer_free(vser->post_load->timer);
g_free(vser->post_load);
}
+
+ qbus_set_hotplug_handler(BUS(&vser->bus), NULL, errp);
+
virtio_cleanup(vdev);
}
+/* Note: 'console' is used for backwards compatibility */
+static const VMStateDescription vmstate_virtio_console = {
+ .name = "virtio-console",
+ .minimum_version_id = 3,
+ .version_id = 3,
+ .fields = (VMStateField[]) {
+ VMSTATE_VIRTIO_DEVICE,
+ VMSTATE_END_OF_LIST()
+ },
+};
+
static Property virtio_serial_properties[] = {
DEFINE_PROP_UINT32("max_ports", VirtIOSerial, serial.max_virtserial_ports,
31),
+ DEFINE_PROP_BIT64("emergency-write", VirtIOSerial, host_features,
+ VIRTIO_CONSOLE_F_EMERG_WRITE, true),
DEFINE_PROP_END_OF_LIST(),
};
QLIST_INIT(&vserdevices.devices);
dc->props = virtio_serial_properties;
+ dc->vmsd = &vmstate_virtio_console;
set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
vdc->realize = virtio_serial_device_realize;
vdc->unrealize = virtio_serial_device_unrealize;
vdc->get_features = get_features;
vdc->get_config = get_config;
+ vdc->set_config = set_config;
vdc->set_status = set_status;
vdc->reset = vser_reset;
vdc->save = virtio_serial_save_device;