Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > media > main-src > by-pkgid > aadbe78a25743146bb784eee19f007c5 > files > 34

kvm-83-164.el5_5.9.src.rpm

From af595fab3adeff06543efb28527c7843745f3882 Mon Sep 17 00:00:00 2001
From: Eduardo Habkost <ehabkost@redhat.com>
Date: Thu, 29 Jan 2009 15:03:21 -0200
Subject: [PATCH 33/54] Add vmchannel support again

Revert "kvm: qemu: remove hypercall device"

This reverts commit 1b1d6d0629d022dbe95fae5ab450be585f73e41d.

Conflicts:

	qemu/Makefile.target
	qemu/vl.c
---
 qemu/Makefile.target |    3 +
 qemu/hw/hypercall.c  |  362 ++++++++++++++++++++++++++++++++++++++++++++++++++
 qemu/hw/hypercall.h  |   45 ++++++
 qemu/hw/pc.c         |    4 +
 qemu/sysemu.h        |    6 +
 qemu/vl.c            |   60 +++++++++
 6 files changed, 480 insertions(+), 0 deletions(-)
 create mode 100644 qemu/hw/hypercall.c
 create mode 100644 qemu/hw/hypercall.h

diff --git a/qemu/Makefile.target b/qemu/Makefile.target
index 806fdb5..eaec68e 100644
--- a/qemu/Makefile.target
+++ b/qemu/Makefile.target
@@ -699,6 +699,9 @@ OBJS += pcnet.o
 OBJS += rtl8139.o
 OBJS += e1000.o
 
+# PCI Hypercall
+OBJS+= hypercall.o
+
 OBJS += device-hotplug.o
 
 ifeq ($(USE_KVM_DEVICE_ASSIGNMENT), 1)
diff --git a/qemu/hw/hypercall.c b/qemu/hw/hypercall.c
new file mode 100644
index 0000000..73f6bb1
--- /dev/null
+++ b/qemu/hw/hypercall.c
@@ -0,0 +1,362 @@
+/*
+ * QEMU-KVM Hypercall emulation
+ * 
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ * Copyright (c) 2006 Qumranet
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw/hw.h"
+#include "sysemu.h"
+#include "qemu-char.h"
+#include "hw/isa.h"
+#include "hw/irq.h"
+#include "hw/pci.h"
+#include "hypercall.h"
+#include <stddef.h>
+
+#define HYPERCALL_IOPORT_SIZE 0x100
+
+static int use_hypercall_dev = 0;
+
+typedef struct VmChannelCharDriverState {
+    CharDriverState *vmchannel_hd;
+    uint32_t deviceid;
+} VmChannelCharDriverState;
+
+static VmChannelCharDriverState vmchannel_hds[MAX_VMCHANNEL_DEVICES];
+
+typedef struct HypercallState {
+    uint32_t hcr;
+    uint32_t hsr;
+    uint32_t txsize;
+    uint32_t txbuff;
+    uint32_t rxsize;
+    uint8_t  RxBuff[HP_MEM_SIZE];
+    uint8_t  txbufferaccu[HP_MEM_SIZE];
+    int      txbufferaccu_offset;
+    int      irq;
+    PCIDevice *pci_dev;
+    uint32_t index;
+} HypercallState;
+
+static HypercallState *pHypercallStates[MAX_VMCHANNEL_DEVICES] = {NULL};
+
+//#define HYPERCALL_DEBUG 1
+
+static void hp_reset(HypercallState *s)
+{
+    s->hcr = HCR_DI;
+    s->hsr = 0;
+    s->txsize = 0;
+    s->txbuff = 0;
+    s->rxsize= 0;
+    s->txbufferaccu_offset = 0;
+}
+
+static void hypercall_update_irq(HypercallState *s);
+
+
+static void hp_ioport_write(void *opaque, uint32_t addr, uint32_t val)
+{
+    HypercallState *s = opaque;
+
+#ifdef HYPERCALL_DEBUG
+    printf("%s: addr=0x%x, val=0x%x\n", __FUNCTION__, addr, val);
+#endif
+    addr &= 0xff;
+
+    switch(addr)
+    {
+        case HCR_REGISTER:
+        {
+            s->hcr = val;
+	    if (s->hcr & HCR_DI)
+                hypercall_update_irq(s);
+            if (val & HCR_GRS){
+                hp_reset(s);
+            }
+            break;
+        }
+
+        case HP_TXSIZE:
+        {
+            // handle the case when the we are being called when txsize is not 0
+            if (s->txsize != 0) {
+                printf("txsize is being set, but txsize is not 0!!!\n");
+            }
+            if (val > HP_MEM_SIZE) {
+                printf("txsize is larger than allowed by hw!!!\n");
+            }
+            s->txsize = val;
+            s->txbufferaccu_offset = 0;
+            break;
+        }
+
+        case HP_TXBUFF:
+        {
+            if (s->txsize == 0) {
+                printf("error with txbuff!!!\n");
+                break;
+            }
+
+            s->txbufferaccu[s->txbufferaccu_offset] = val;
+            s->txbufferaccu_offset++;
+            if (s->txbufferaccu_offset >= s->txsize) {
+                qemu_chr_write(vmchannel_hds[s->index].vmchannel_hd, s->txbufferaccu, s->txsize);
+                s->txbufferaccu_offset = 0;
+                s->txsize = 0;
+            }
+            break;
+        }
+        default:
+        {
+            printf("hp_ioport_write to unhandled address!!!\n");
+        }
+    }
+}
+
+static uint32_t hp_ioport_read(void *opaque, uint32_t addr)
+{
+    HypercallState *s = opaque;
+    int ret;
+
+    addr &= 0xff;
+#ifdef HYPERCALL_DEBUG
+    // Since HSR_REGISTER is being repeatedly read in the guest ISR we don't print it
+    if (addr != HSR_REGISTER)
+        printf("%s: addr=0x%x", __FUNCTION__, addr);
+#endif
+
+    if (addr >= offsetof(HypercallState, RxBuff) )
+    {
+        int RxBuffOffset = addr - (offsetof(HypercallState, RxBuff));
+        ret = s->RxBuff[RxBuffOffset];
+#ifdef HYPERCALL_DEBUG
+    printf(" val=%x\n", ret);
+#endif
+        return ret;
+    }
+
+    switch (addr)
+    {
+    case HSR_REGISTER:
+        ret = s->hsr;
+        if (ret & HSR_VDR) {
+            s->hsr &= ~HSR_VDR;
+        }
+        break;
+    case HP_RXSIZE:
+        ret = s->rxsize;
+        break;
+
+    default:
+        ret = 0x00;
+        break;
+    }
+#ifdef HYPERCALL_DEBUG
+    printf(" val=%x\n", ret);
+#endif
+    return ret;
+}
+
+/***********************************************************/
+/* PCI Hypercall definitions */
+
+typedef struct PCIHypercallState {
+    PCIDevice dev;
+    HypercallState hp;
+} PCIHypercallState;
+
+static void hp_map(PCIDevice *pci_dev, int region_num, 
+                       uint32_t addr, uint32_t size, int type)
+{
+    PCIHypercallState *d = (PCIHypercallState *)pci_dev;
+    HypercallState *s = &d->hp;
+
+    register_ioport_write(addr, HYPERCALL_IOPORT_SIZE, 1, hp_ioport_write, s);
+    register_ioport_read(addr, HYPERCALL_IOPORT_SIZE, 1, hp_ioport_read, s);
+
+}
+
+
+static void hypercall_update_irq(HypercallState *s)
+{
+    /* PCI irq */
+    qemu_set_irq(s->pci_dev->irq[0], !(s->hcr & HCR_DI));
+}
+
+static void hc_save(QEMUFile* f,void* opaque)
+{
+    HypercallState* s=(HypercallState*)opaque;
+
+    pci_device_save(s->pci_dev, f);
+
+    qemu_put_be32s(f, &s->hcr);
+    qemu_put_be32s(f, &s->hsr);
+    qemu_put_be32s(f, &s->txsize);
+    qemu_put_be32s(f, &s->txbuff);
+    qemu_put_be32s(f, &s->rxsize);
+    qemu_put_buffer(f, s->RxBuff, HP_MEM_SIZE);
+    qemu_put_buffer(f, s->txbufferaccu, HP_MEM_SIZE);
+    qemu_put_be32s(f, &s->txbufferaccu_offset);
+    qemu_put_be32s(f, &s->irq);
+    qemu_put_be32s(f, &s->index);
+
+}
+
+static int hc_load(QEMUFile* f,void* opaque,int version_id)
+{
+    HypercallState* s=(HypercallState*)opaque;
+    int ret;
+
+    if (version_id != 1)
+        return -EINVAL;
+
+    ret = pci_device_load(s->pci_dev, f);
+    if (ret < 0)
+        return ret;
+
+    qemu_get_be32s(f, &s->hcr);
+    qemu_get_be32s(f, &s->hsr);
+    qemu_get_be32s(f, &s->txsize);
+    qemu_get_be32s(f, &s->txbuff);
+    qemu_get_be32s(f, &s->rxsize);
+    qemu_get_buffer(f, s->RxBuff, HP_MEM_SIZE);
+    qemu_get_buffer(f, s->txbufferaccu, HP_MEM_SIZE);
+    qemu_get_be32s(f, &s->txbufferaccu_offset);
+    qemu_get_be32s(f, &s->irq);
+    qemu_get_be32s(f, &s->index);
+
+    return 0;
+}
+
+static void pci_hypercall_single_init(PCIBus *bus, uint32_t deviceid, uint32_t index)
+{
+    PCIHypercallState *d;
+    HypercallState *s;
+    uint8_t *pci_conf;
+    char name[sizeof("HypercallX")];
+
+    sprintf(name, "Hypercall%d", index);
+
+#ifdef HYPERCALL_DEBUG
+    printf("%s, devicename:%s\n", __FUNCTION__, name);
+#endif
+
+    // If the vmchannel wasn't initialized, we don't want the Hypercall device in the guest
+    if (use_hypercall_dev == 0) {
+        return;
+    }
+
+    d = (PCIHypercallState *)pci_register_device(bus,
+                                                 name, sizeof(PCIHypercallState),
+                                                 -1,
+                                                 NULL, NULL);
+
+    pci_conf = d->dev.config;
+    pci_conf[0x00] = 0x02; // Qumranet vendor ID 0x5002
+    pci_conf[0x01] = 0x50;
+    pci_conf[0x02] = deviceid & 0x00ff;
+    pci_conf[0x03] = (deviceid & 0xff00) >> 8;
+
+    pci_conf[0x09] = 0x00; // ProgIf
+    pci_conf[0x0a] = 0x00; // SubClass
+    pci_conf[0x0b] = 0x05; // BaseClass
+
+    pci_conf[0x0e] = 0x00; // header_type
+    pci_conf[0x3d] = 1; // interrupt pin 0
+
+    pci_register_io_region(&d->dev, 0, HYPERCALL_IOPORT_SIZE,
+                           PCI_ADDRESS_SPACE_IO, hp_map);
+    s = &d->hp;
+    pHypercallStates[index] = s;
+    s->index = index;
+    s->irq = 16; /* PCI interrupt */
+    s->pci_dev = (PCIDevice *)d;
+
+    hp_reset(s);
+    register_savevm(name, index, 1, hc_save, hc_load, s);
+}
+
+void pci_hypercall_init(PCIBus *bus)
+{
+    int i;
+
+    // loop devices & call pci_hypercall_single_init with device id's
+    for(i = 0; i < MAX_VMCHANNEL_DEVICES; i++){
+        if (vmchannel_hds[i].vmchannel_hd) {
+            pci_hypercall_single_init(bus, vmchannel_hds[i].deviceid, i);
+        }
+    }
+}
+
+static int vmchannel_can_read(void *opaque)
+{
+    return 128;
+}
+
+static void vmchannel_event(void *opaque, int event)
+{
+
+#ifdef HYPERCALL_DEBUG
+    // if index is to be used outside the printf, take it out of the #ifdef block!
+    long index = (long)opaque;
+    printf("%s index:%ld, got event %i\n", __FUNCTION__, index, event);
+#endif
+    
+    return;
+}
+
+// input from vmchannel outside caller
+static void vmchannel_read(void *opaque, const uint8_t *buf, int size)
+{
+    int i;
+    long index = (long)opaque;
+
+#ifdef HYPERCALL_DEBUG    
+    printf("vmchannel_read buf size:%d\n", size);
+#endif
+
+    // if the hypercall device is in interrupts disabled state, don't accept the data
+    if (pHypercallStates[index]->hcr & HCR_DI) {
+        return;
+    }
+
+    for(i = 0; i < size; i++) {
+        pHypercallStates[index]->RxBuff[i] = buf[i];
+    }
+    pHypercallStates[index]->rxsize = size;
+    pHypercallStates[index]->hsr = HSR_VDR;
+    hypercall_update_irq(pHypercallStates[index]);
+}
+
+void vmchannel_init(CharDriverState *hd, uint32_t deviceid, uint32_t index)
+{
+#ifdef HYPERCALL_DEBUG
+    printf("vmchannel_init, index=%d, deviceid=0x%x\n", index, deviceid);
+#endif
+
+    vmchannel_hds[index].deviceid = deviceid;
+    vmchannel_hds[index].vmchannel_hd = hd;
+   
+    use_hypercall_dev = 1;
+    qemu_chr_add_handlers(vmchannel_hds[index].vmchannel_hd, vmchannel_can_read, vmchannel_read,
+                          vmchannel_event, (void *)(long)index);
+}
diff --git a/qemu/hw/hypercall.h b/qemu/hw/hypercall.h
new file mode 100644
index 0000000..97434a7
--- /dev/null
+++ b/qemu/hw/hypercall.h
@@ -0,0 +1,45 @@
+/*
+ * QEMU-KVM Hypercall emulation
+ * 
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ * Copyright (c) 2006 Qumranet
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#define HCR_REGISTER    0x00  // Hypercall Command Register WR
+#define HSR_REGISTER    0x04  // Hypercall Status Register RD
+#define HP_TXSIZE       0x08
+#define HP_TXBUFF       0x0c
+#define HP_RXSIZE       0x10
+#define HP_RXBUFF       0x14
+
+// HCR_REGISTER commands
+#define HCR_DI		1 // disable interrupts
+#define HCR_EI		2 // enable interrupts
+#define HCR_GRS		4 // Global reset
+#define HCR_RESET	(HCR_GRS|HCR_DI)
+
+
+// Bits in HSR_REGISTER
+#define HSR_VDR		0x01  // vmchannel Data is ready to be read
+
+#define HP_MEM_SIZE    0xE0
+
+
diff --git a/qemu/hw/pc.c b/qemu/hw/pc.c
index 52e17fd..951c4f4 100644
--- a/qemu/hw/pc.c
+++ b/qemu/hw/pc.c
@@ -1084,6 +1084,10 @@ static void pc_init1(ram_addr_t ram_size, int vga_ram_size,
     }
 
     qemu_system_hot_add_init(cpu_model);
+#define USE_HYPERCALL
+#ifdef USE_HYPERCALL
+    pci_hypercall_init(pci_bus);
+#endif
 
     if (drive_get_max_bus(IF_IDE) >= MAX_IDE_BUS) {
         fprintf(stderr, "qemu: too many IDE bus\n");
diff --git a/qemu/sysemu.h b/qemu/sysemu.h
index 027b8e7..f4e62fc 100644
--- a/qemu/sysemu.h
+++ b/qemu/sysemu.h
@@ -181,6 +181,12 @@ void drive_hot_add(int pcibus, const char *devfn_string, const char *opts);
 void device_hot_remove(int pcibus, int slot);
 void device_hot_remove_success(int pcibus, int slot);
 
+/* vmchannel devices */
+
+#define MAX_VMCHANNEL_DEVICES 4
+void pci_hypercall_init(PCIBus *bus);
+void vmchannel_init(CharDriverState *hd, uint32_t deviceid, uint32_t index);
+
 /* serial ports */
 
 #define MAX_SERIAL_PORTS 4
diff --git a/qemu/vl.c b/qemu/vl.c
index 25c97d1..8bdfd7e 100644
--- a/qemu/vl.c
+++ b/qemu/vl.c
@@ -218,6 +218,8 @@ static int full_screen = 0;
 static int no_frame = 0;
 #endif
 int no_quit = 0;
+int balloon_used = 0;
+CharDriverState *vmchannel_hds[MAX_VMCHANNEL_DEVICES];
 CharDriverState *serial_hds[MAX_SERIAL_PORTS];
 CharDriverState *parallel_hds[MAX_PARALLEL_PORTS];
 #ifdef TARGET_I386
@@ -4121,6 +4123,8 @@ static void help(int exitcode)
            "\n"
            "Debug/Expert options:\n"
            "-monitor dev    redirect the monitor to char device 'dev'\n"
+           "-vmchannel di:DI,dev  redirect the hypercall device with device id DI, to char device 'dev'\n"
+           "-balloon dev    redirect the balloon hypercall device to char device 'dev'\n"
            "-serial dev     redirect the serial port to char device 'dev'\n"
            "-parallel dev   redirect the parallel port to char device 'dev'\n"
            "-pidfile file   Write PID to 'file'\n"
@@ -4256,6 +4260,8 @@ enum {
     QEMU_OPTION_vga,
     QEMU_OPTION_echr,
     QEMU_OPTION_monitor,
+    QEMU_OPTION_balloon,
+    QEMU_OPTION_vmchannel,
     QEMU_OPTION_serial,
     QEMU_OPTION_parallel,
     QEMU_OPTION_loadvm,
@@ -4394,6 +4400,7 @@ static const QEMUOption qemu_options[] = {
 #endif
     { "localtime", 0, QEMU_OPTION_localtime },
     { "vga", HAS_ARG, QEMU_OPTION_vga },
+    { "vmchannel", 1, QEMU_OPTION_vmchannel },
     { "echr", HAS_ARG, QEMU_OPTION_echr },
     { "monitor", HAS_ARG, QEMU_OPTION_monitor },
     { "serial", HAS_ARG, QEMU_OPTION_serial },
@@ -4839,6 +4846,8 @@ int main(int argc, char **argv, char **envp)
     const char *monitor_device;
     const char *serial_devices[MAX_SERIAL_PORTS];
     int serial_device_index;
+    char vmchannel_devices[MAX_VMCHANNEL_DEVICES][128];
+    int vmchannel_device_index;
     const char *parallel_devices[MAX_PARALLEL_PORTS];
     int parallel_device_index;
     const char *loadvm = NULL;
@@ -4905,6 +4914,10 @@ int main(int argc, char **argv, char **envp)
     translation = BIOS_ATA_TRANSLATION_AUTO;
     monitor_device = "vc";
 
+    for(i = 0; i < MAX_VMCHANNEL_DEVICES; i++)
+        vmchannel_devices[i][0] = '\0';
+    vmchannel_device_index = 0;
+
     serial_devices[0] = "vc:80Cx24C";
     for(i = 1; i < MAX_SERIAL_PORTS; i++)
         serial_devices[i] = NULL;
@@ -5294,6 +5307,28 @@ int main(int argc, char **argv, char **envp)
             case QEMU_OPTION_monitor:
                 monitor_device = optarg;
                 break;
+            case QEMU_OPTION_balloon:
+                if (vmchannel_device_index >= MAX_VMCHANNEL_DEVICES) {
+                    fprintf(stderr, "qemu: too many balloon/vmchannel devices\n");
+                    exit(1);
+                }
+                if (balloon_used) {
+                    fprintf(stderr, "qemu: only one balloon device can be used\n");
+                    exit(1);
+                }
+                sprintf(vmchannel_devices[vmchannel_device_index],"di:cdcd,%s", optarg);
+                vmchannel_device_index++;
+                balloon_used = 1;
+                break;
+            case QEMU_OPTION_vmchannel:
+                if (vmchannel_device_index >= MAX_VMCHANNEL_DEVICES) {
+                    fprintf(stderr, "qemu: too many balloon/vmchannel devices\n");
+                    exit(1);
+                }
+                pstrcpy(vmchannel_devices[vmchannel_device_index], 
+                        sizeof(vmchannel_devices[0]), optarg);
+                vmchannel_device_index++;
+                break;
             case QEMU_OPTION_serial:
                 if (serial_device_index >= MAX_SERIAL_PORTS) {
                     fprintf(stderr, "qemu: too many serial ports\n");
@@ -5928,6 +5963,31 @@ int main(int argc, char **argv, char **envp)
         monitor_init(monitor_hd, !nographic);
     }
 
+    for(i = 0; i < MAX_VMCHANNEL_DEVICES; i++) {
+        const char *devname = vmchannel_devices[i];
+        if (devname[0] != '\0' && strcmp(devname, "none")) {
+            int devid;
+            char *termn;
+
+            if (strstart(devname, "di:", &devname)) {
+                devid = strtol(devname, &termn, 16);
+                devname = termn + 1;
+            }
+            else {
+                fprintf(stderr, "qemu: could not find vmchannel device id '%s'\n", 
+                        devname);
+                exit(1);
+            }
+            vmchannel_hds[i] = qemu_chr_open(devname);
+            if (!vmchannel_hds[i]) {
+                fprintf(stderr, "qemu: could not open vmchannel device '%s'\n", 
+                        devname);
+                exit(1);
+            }
+            vmchannel_init(vmchannel_hds[i], devid, i);
+        }
+    }
+
     for(i = 0; i < MAX_SERIAL_PORTS; i++) {
         const char *devname = serial_devices[i];
         if (devname && strcmp(devname, "none")) {
-- 
1.6.1