mvadkert / rpms / qemu

Forked from rpms/qemu 6 years ago
Clone
a81953e
From 5bdc01e675a51a123a813d62a8ae837db9360b7f Mon Sep 17 00:00:00 2001
a81953e
From: Gerd Hoffmann <kraxel@redhat.com>
a81953e
Date: Tue, 20 Apr 2010 13:33:54 +0200
a81953e
Subject: [PATCH 38/39] spice: add virtio-serial based spice vmchannel backend.
a81953e
a81953e
Adds the spicevmc device.  This is a communication channel between the
a81953e
spice client and the guest.  It is used to send display information and
a81953e
mouse events from the spice clients to the guest.
a81953e
---
a81953e
 Makefile.target |    1 +
a81953e
 hw/spice-vmc.c  |  262 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
a81953e
 2 files changed, 263 insertions(+), 0 deletions(-)
a81953e
 create mode 100644 hw/spice-vmc.c
a81953e
a81953e
diff --git a/Makefile.target b/Makefile.target
a81953e
index 4da33b5..90544c5 100644
a81953e
--- a/Makefile.target
a81953e
+++ b/Makefile.target
a81953e
@@ -217,6 +217,7 @@ obj-i386-y += pc_piix.o
a81953e
 obj-i386-y += testdev.o
a81953e
 obj-i386-y += acpi.o acpi_piix4.o
a81953e
 obj-i386-$(CONFIG_SPICE) += qxl.o qxl-logger.o qxl-render.o
a81953e
+obj-i386-$(CONFIG_SPICE) += spice-vmc.o
a81953e
a81953e
 obj-i386-y += pcspk.o i8254.o
a81953e
 obj-i386-$(CONFIG_KVM_PIT) += i8254-kvm.o
a81953e
diff --git a/hw/spice-vmc.c b/hw/spice-vmc.c
a81953e
new file mode 100644
a81953e
index 0000000..b77fc60
a81953e
--- /dev/null
a81953e
+++ b/hw/spice-vmc.c
a81953e
@@ -0,0 +1,262 @@
a81953e
+/*
a81953e
+
a81953e
+ Spice Virtual Machine Channel (VMC).
a81953e
+
a81953e
+ A virtio-serial port used for spice to guest communication, over
a81953e
+ which spice client and a daemon in the guest operating system
a81953e
+ communicate.
a81953e
+
a81953e
+ Replaces the old vdi_port PCI device.
a81953e
+
a81953e
+*/
a81953e
+
a81953e
+#include <stdio.h>
a81953e
+#include <stdbool.h>
a81953e
+#include <spice.h>
a81953e
+#include <spice-experimental.h>
a81953e
+
a81953e
+#include "virtio-serial.h"
a81953e
+#include "qemu-spice.h"
a81953e
+
a81953e
+#define VMC_GUEST_DEVICE_NAME "com.redhat.spice.0"
a81953e
+#define VMC_DEVICE_NAME       "spicevmc"
a81953e
+
a81953e
+/* windows guest driver bug workaround */
a81953e
+#define VMC_MAX_HOST_WRITE    2048
a81953e
+
a81953e
+#define dprintf(_svc, _level, _fmt, ...)                                \
a81953e
+    do {                                                                \
a81953e
+        static unsigned __dprintf_counter = 0;                          \
a81953e
+        if (_svc->debug >= _level) {                                    \
a81953e
+            fprintf(stderr, "svc: %3d: " _fmt, ++__dprintf_counter, ## __VA_ARGS__);\
a81953e
+        }                                                               \
a81953e
+    } while (0)
a81953e
+
a81953e
+typedef struct SpiceVirtualChannel {
a81953e
+    VirtIOSerialPort         port;
a81953e
+    VMChangeStateEntry       *vmstate;
a81953e
+    SpiceCharDeviceInstance  sin;
a81953e
+    char                     *subtype;
a81953e
+    bool                     active;
a81953e
+    uint8_t                  *buffer;
a81953e
+    uint8_t                  *datapos;
a81953e
+    ssize_t                  bufsize, datalen;
a81953e
+    uint32_t                 debug;
a81953e
+} SpiceVirtualChannel;
a81953e
+
a81953e
+static int vmc_write(SpiceCharDeviceInstance *sin, const uint8_t *buf, int len)
a81953e
+{
a81953e
+    SpiceVirtualChannel *svc = container_of(sin, SpiceVirtualChannel, sin);
a81953e
+    ssize_t out = 0;
a81953e
+    ssize_t last_out;
a81953e
+    uint8_t* p = (uint8_t*)buf;
a81953e
+
a81953e
+    while (len > 0) {
a81953e
+        last_out = virtio_serial_write(&svc->port, p,
a81953e
+                            MIN(len, VMC_MAX_HOST_WRITE));
a81953e
+        if (last_out > 0) {
a81953e
+            out += last_out;
a81953e
+            len -= last_out;
a81953e
+            p += last_out;
a81953e
+        } else {
a81953e
+            break;
a81953e
+        }
a81953e
+    }
a81953e
+
a81953e
+    dprintf(svc, 3, "%s: %lu/%zd\n", __func__, out, len + out);
a81953e
+    return out;
a81953e
+}
a81953e
+
a81953e
+static int vmc_read(SpiceCharDeviceInstance *sin, uint8_t *buf, int len)
a81953e
+{
a81953e
+    SpiceVirtualChannel *svc = container_of(sin, SpiceVirtualChannel, sin);
a81953e
+    int bytes = MIN(len, svc->datalen);
a81953e
+
a81953e
+    dprintf(svc, 2, "%s: %p %d/%d/%zd\n", __func__, svc->datapos, len, bytes, svc->datalen);
a81953e
+    if (bytes > 0) {
a81953e
+        memcpy(buf, svc->datapos, bytes);
a81953e
+        svc->datapos += bytes;
a81953e
+        svc->datalen -= bytes;
a81953e
+        assert(svc->datalen >= 0);
a81953e
+        if (svc->datalen == 0) {
a81953e
+            svc->datapos = 0;
a81953e
+            virtio_serial_throttle_port(&svc->port, false);
a81953e
+            // ^^^ !!! may call vmc_have_data, so don't touch svc after it!
a81953e
+        }
a81953e
+    }
a81953e
+    return bytes;
a81953e
+}
a81953e
+
a81953e
+static SpiceCharDeviceInterface vmc_interface = {
a81953e
+    .base.type          = SPICE_INTERFACE_CHAR_DEVICE,
a81953e
+    .base.description   = "spice virtual channel char device",
a81953e
+    .base.major_version = SPICE_INTERFACE_CHAR_DEVICE_MAJOR,
a81953e
+    .base.minor_version = SPICE_INTERFACE_CHAR_DEVICE_MINOR,
a81953e
+    .write              = vmc_write,
a81953e
+    .read               = vmc_read,
a81953e
+};
a81953e
+
a81953e
+static void vmc_register_interface(SpiceVirtualChannel *svc)
a81953e
+{
a81953e
+    if (svc->active) {
a81953e
+        return;
a81953e
+    }
a81953e
+    dprintf(svc, 1, "%s\n", __func__);
a81953e
+    svc->sin.base.sif = &vmc_interface.base;
a81953e
+    spice_server_add_interface(spice_server, &svc->sin.base);
a81953e
+    svc->active = true;
a81953e
+}
a81953e
+
a81953e
+static void vmc_unregister_interface(SpiceVirtualChannel *svc)
a81953e
+{
a81953e
+    if (!svc->active) {
a81953e
+        return;
a81953e
+    }
a81953e
+    dprintf(svc, 1, "%s\n", __func__);
a81953e
+    spice_server_remove_interface(&svc->sin.base);
a81953e
+    svc->active = false;
a81953e
+}
a81953e
+
a81953e
+
a81953e
+static void vmc_change_state_handler(void *opaque, int running, int reason)
a81953e
+{
a81953e
+    SpiceVirtualChannel *svc = opaque;
a81953e
+
a81953e
+    if (running && svc->active) {
a81953e
+        spice_server_char_device_wakeup(&svc->sin);
a81953e
+    }
a81953e
+}
a81953e
+
a81953e
+/*
a81953e
+ * virtio-serial callbacks
a81953e
+ */
a81953e
+
a81953e
+static void vmc_guest_open(VirtIOSerialPort *port)
a81953e
+{
a81953e
+    SpiceVirtualChannel *svc = DO_UPCAST(SpiceVirtualChannel, port, port);
a81953e
+
a81953e
+    dprintf(svc, 1, "%s\n", __func__);
a81953e
+    vmc_register_interface(svc);
a81953e
+}
a81953e
+
a81953e
+static void vmc_guest_close(VirtIOSerialPort *port)
a81953e
+{
a81953e
+    SpiceVirtualChannel *svc = DO_UPCAST(SpiceVirtualChannel, port, port);
a81953e
+
a81953e
+    dprintf(svc, 1, "%s\n", __func__);
a81953e
+    vmc_unregister_interface(svc);
a81953e
+}
a81953e
+
a81953e
+static void vmc_guest_ready(VirtIOSerialPort *port)
a81953e
+{
a81953e
+    SpiceVirtualChannel *svc = DO_UPCAST(SpiceVirtualChannel, port, port);
a81953e
+
a81953e
+    dprintf(svc, 1, "%s\n", __func__);
a81953e
+    if (svc->active) {
a81953e
+        spice_server_char_device_wakeup(&svc->sin);
a81953e
+    }
a81953e
+}
a81953e
+
a81953e
+static void vmc_have_data(VirtIOSerialPort *port, const uint8_t *buf, size_t len)
a81953e
+{
a81953e
+    SpiceVirtualChannel *svc = DO_UPCAST(SpiceVirtualChannel, port, port);
a81953e
+
a81953e
+    dprintf(svc, 2, "%s: %zd\n", __func__, len);
a81953e
+    assert(svc->datalen == 0);
a81953e
+    if (svc->bufsize < len) {
a81953e
+        svc->bufsize = len;
a81953e
+        svc->buffer = qemu_realloc(svc->buffer, svc->bufsize);
a81953e
+    }
a81953e
+    memcpy(svc->buffer, buf, len);
a81953e
+    svc->datapos = svc->buffer;
a81953e
+    svc->datalen = len;
a81953e
+    virtio_serial_throttle_port(&svc->port, true);
a81953e
+    spice_server_char_device_wakeup(&svc->sin);
a81953e
+}
a81953e
+
a81953e
+static void vmc_print_optional_subtypes(void)
a81953e
+{
a81953e
+    const char** psubtype = spice_server_char_device_recognized_subtypes();
a81953e
+    int i;
a81953e
+
a81953e
+    fprintf(stderr, "supported subtypes: ");
a81953e
+    for(i=0; *psubtype != NULL; ++psubtype, ++i) {
a81953e
+        if (i == 0) {
a81953e
+            fprintf(stderr, *psubtype);
a81953e
+        } else {
a81953e
+            fprintf(stderr, ", %s", *psubtype);
a81953e
+        }
a81953e
+    }
a81953e
+    fprintf(stderr, "\n");
a81953e
+}
a81953e
+
a81953e
+static int vmc_initfn(VirtIOSerialDevice *dev)
a81953e
+{
a81953e
+    VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, dev, &dev->qdev);
a81953e
+    SpiceVirtualChannel *svc = DO_UPCAST(SpiceVirtualChannel, port, port);
a81953e
+    const char** psubtype = spice_server_char_device_recognized_subtypes();
a81953e
+    const char *subtype = NULL;
a81953e
+
a81953e
+    if (!using_spice) {
a81953e
+        return -1;
a81953e
+    }
a81953e
+
a81953e
+    dprintf(svc, 1, "%s\n", __func__);
a81953e
+
a81953e
+    if (svc->subtype == NULL) {
a81953e
+        svc->subtype = strdup("vdagent");
a81953e
+    }
a81953e
+
a81953e
+    for(;*psubtype != NULL; ++psubtype) {
a81953e
+        if (strcmp(svc->subtype, *psubtype) == 0) {
a81953e
+            subtype = *psubtype;
a81953e
+            break;
a81953e
+        }
a81953e
+    }
a81953e
+    if (subtype == NULL) {
a81953e
+        fprintf(stderr, "spice-vmc: unsupported subtype\n");
a81953e
+        vmc_print_optional_subtypes();
a81953e
+        return -1;
a81953e
+    }
a81953e
+    port->name = qemu_strdup(VMC_GUEST_DEVICE_NAME);
a81953e
+    svc->vmstate = qemu_add_vm_change_state_handler
a81953e
+        (vmc_change_state_handler, svc);
a81953e
+    svc->sin.subtype = svc->subtype;
a81953e
+    virtio_serial_open(port);
a81953e
+    return 0;
a81953e
+}
a81953e
+
a81953e
+static int vmc_exitfn(VirtIOSerialDevice *dev)
a81953e
+{
a81953e
+    VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, dev, &dev->qdev);
a81953e
+    SpiceVirtualChannel *svc = DO_UPCAST(SpiceVirtualChannel, port, port);
a81953e
+
a81953e
+    dprintf(svc, 1, "%s\n", __func__);
a81953e
+    vmc_unregister_interface(svc);
a81953e
+    qemu_del_vm_change_state_handler(svc->vmstate);
a81953e
+    virtio_serial_close(port);
a81953e
+    return 0;
a81953e
+}
a81953e
+
a81953e
+static VirtIOSerialPortInfo vmc_info = {
a81953e
+    .qdev.name     = VMC_DEVICE_NAME,
a81953e
+    .qdev.size     = sizeof(SpiceVirtualChannel),
a81953e
+    .init          = vmc_initfn,
a81953e
+    .exit          = vmc_exitfn,
a81953e
+    .guest_open    = vmc_guest_open,
a81953e
+    .guest_close   = vmc_guest_close,
a81953e
+    .guest_ready   = vmc_guest_ready,
a81953e
+    .have_data     = vmc_have_data,
a81953e
+    .qdev.props = (Property[]) {
a81953e
+        DEFINE_PROP_UINT32("nr", SpiceVirtualChannel, port.id, VIRTIO_CONSOLE_BAD_ID),
a81953e
+        DEFINE_PROP_UINT32("debug", SpiceVirtualChannel, debug, 1),
a81953e
+        DEFINE_PROP_STRING("subtype", SpiceVirtualChannel, subtype),
a81953e
+        DEFINE_PROP_END_OF_LIST(),
a81953e
+    }
a81953e
+};
a81953e
+
a81953e
+static void vmc_register(void)
a81953e
+{
a81953e
+    virtio_serial_port_qdev_register(&vmc_info);
a81953e
+}
a81953e
+device_init(vmc_register)
a81953e
-- 
a81953e
1.7.2.3
a81953e