qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH 3/3] powerpc-virtio: virtio support introduced (bloc


From: David Gibson
Subject: [Qemu-devel] [PATCH 3/3] powerpc-virtio: virtio support introduced (block, network, serial, balloon, 9p-fs), both fullemu and power-kvm
Date: Tue, 17 May 2011 16:47:06 +1000

From: Alexey Kardashevskiy <address@hidden>

The recently added pseries machine does not currently support PCI
emulation.  For the (upcoming) kvm case, this is quite difficult to do
because the preferred HV mode for the host kernel does not allow MMIO
emulation (a hardware limitation).

Therefore, to support virtio devices, we implement a new virtio setup
protocol for PAPR guests.  This is based loosely on the s390 and lguest
methods, using the PAPR hcalls for the virtio primitive operations,
and the PAPR device tree to advertise the virtio device resources to the
guest.

This patch includes support for the virtio block, network, serial, and
balloon devices, and the 9p filesystem.

The guest linux kernel should be updated as well in order to
support a new virtio setup.

Supported devices are (below are QEMU command line switches):

- virtio-blk - block device, at the moment works as "IDE":
    usage: -drive file=test-virtio-blk.img,if=ide

- virtio-net - network device
    usage: -net nic,model=virtio-net

- virtio-balloon - memory hot-swap device (at the moment of commit, power-kvm
did not support balloon)
    usage: -device virtio-balloon-spapr

- virtio-serial - serial bus controller
    usage: -device virtio-serial-spapr \
           -chardev socket,id=CONSOLE,host=localhost,port=4444,server,telnet \
           -device virtconsole,chardev=CONSOLE
    The first switch tells QEMU to create a serial bus device and next
    2 switches create "chardev" and virtual console device connected to
    that "chardev".

- virtio-9p - plan9 filesystem with ability to work over virtio transport
    usage: -fsdev fstype=local,id=TAG,path=/home/aik/,security_model=none \
           -device virtio-9p-spapr,fsdev=TAG,mount_tag=TAG
    where TAG is a tag which should be used later when mounting is linux as:
           mount -t 9p -o trans=virtio TAG /mnt

    Configure for full emulation as:
       ./configure --target-list=ppc64-softmmu --enable-attr

    Configure for power-kvm as:
       ./configure --enable-kvm  --target-list=ppc64-softmmu --enable-fdt \
                    --cc="gcc -m64" \
                    --kerneldir=/root/kheaders --enable-io-thread --enable-attr

Note: --enable-attr is required for 9p support. On ppc64 systems, libattr
should be compiled and installed manually as it is not distributes in
64bit packages.

Signed-off-by: Alexey Kardashevskiy <address@hidden>
Signed-off-by: David Gibson <address@hidden>
---
 Makefile.target   |    1 +
 hw/spapr.c        |   47 ++++-
 hw/spapr.h        |   11 +-
 hw/spapr_virtio.c |  641 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/spapr_virtio.h |   60 +++++
 5 files changed, 756 insertions(+), 4 deletions(-)
 create mode 100644 hw/spapr_virtio.c
 create mode 100644 hw/spapr_virtio.h

diff --git a/Makefile.target b/Makefile.target
index 2e281a4..fb7d513 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -253,6 +253,7 @@ obj-ppc-y += ppc_newworld.o
 ifeq ($(CONFIG_FDT)$(TARGET_PPC64),yy)
 obj-ppc-y += spapr.o spapr_hcall.o spapr_rtas.o spapr_vio.o
 obj-ppc-y += xics.o spapr_vty.o spapr_llan.o spapr_vscsi.o
+obj-ppc-$(CONFIG_VIRTIO) += spapr_virtio.o
 endif
 # PowerPC 4xx boards
 obj-ppc-y += ppc4xx_devs.o ppc4xx_pci.o ppc405_uc.o ppc405_boards.o
diff --git a/hw/spapr.c b/hw/spapr.c
index 109b774..6815157 100644
--- a/hw/spapr.c
+++ b/hw/spapr.c
@@ -36,6 +36,7 @@
 
 #include "hw/spapr.h"
 #include "hw/spapr_vio.h"
+#include "hw/spapr_virtio.h"
 #include "hw/xics.h"
 
 #include <libfdt.h>
@@ -53,6 +54,7 @@
 
 #define MAX_CPUS                256
 #define XICS_IRQS              1024
+#define MAX_BLK_DEVS            10
 
 sPAPREnvironment *spapr;
 
@@ -214,6 +216,17 @@ static void *spapr_create_fdt_skel(const char *cpu_model,
 
     _FDT((fdt_end_node(fdt)));
 
+    /* virtio-bus */
+    _FDT((fdt_begin_node(fdt, "virtio-bus")));
+
+    _FDT((fdt_property_string(fdt, "compatible", "ibm,virtio-bus")));
+    _FDT((fdt_property_cell(fdt, "#address-cells", 0x2)));
+    _FDT((fdt_property_cell(fdt, "#size-cells", 0x1)));
+    _FDT((fdt_property_cell(fdt, "#interrupt-cells", 0x2)));
+    _FDT((fdt_property(fdt, "interrupt-controller", NULL, 0)));
+
+    _FDT((fdt_end_node(fdt)));
+
     _FDT((fdt_end_node(fdt))); /* close root node */
     _FDT((fdt_finish(fdt)));
 
@@ -239,6 +252,12 @@ static void spapr_finalize_fdt(sPAPREnvironment *spapr,
         exit(1);
     }
 
+    ret = spapr_populate_virtio_devices(spapr->virtio_bus, fdt);
+    if (ret < 0) {
+        fprintf(stderr, "couldn't setup virtio devices in fdt\n");
+        exit(1);
+    }
+
     /* RTAS */
     ret = spapr_rtas_device_tree_setup(fdt, rtas_addr, rtas_size);
     if (ret < 0) {
@@ -330,8 +349,10 @@ static void ppc_spapr_init(ram_addr_t ram_size,
     }
 
     /* allocate RAM */
-    ram_offset = qemu_ram_alloc(NULL, "ppc_spapr.ram", ram_size);
-    cpu_register_physical_memory(0, ram_size, ram_offset);
+    ram_offset = qemu_ram_alloc(NULL, "ppc_spapr.ram", ram_size +
+            SPAPR_VIRTIO_SHARED_MEM_SIZE);
+    cpu_register_physical_memory(0, ram_size +
+            SPAPR_VIRTIO_SHARED_MEM_SIZE, ram_offset);
 
     /* allocate hash page table.  For now we always make this 16mb,
      * later we should probably make it scale to the size of guest
@@ -378,9 +399,11 @@ static void ppc_spapr_init(ram_addr_t ram_size,
         if (strcmp(nd->model, "ibmveth") == 0) {
             spapr_vlan_create(spapr->vio_bus, 0x1000 + i, nd,
                               xics_find_qirq(spapr->icp, irq), irq);
+        } else if (strcmp(nd->model, "virtio-net") == 0) {
+            /* Create later */
         } else {
             fprintf(stderr, "pSeries (sPAPR) platform does not support "
-                    "NIC model '%s' (only ibmveth is supported)\n",
+                    "NIC model '%s' (ibmveth and virtio-net are supported)\n",
                     nd->model);
             exit(1);
         }
@@ -392,6 +415,24 @@ static void ppc_spapr_init(ram_addr_t ram_size,
         irq++;
     }
 
+    /* Initialize virtio bus */
+    spapr->virtio_bus = spapr_virtio_bus_init(ram_size,
+            spapr->icp, irq, XICS_IRQS - irq);
+
+    for (i = 0; i < nb_nics; i++) {
+        NICInfo *nd = &nd_table[i];
+        if (nd->model && (strcmp(nd->model, "virtio-net") == 0)) {
+            spapr_virtio_create_nic(spapr->virtio_bus, nd);
+        }
+    }
+
+    for (i = 0; i < MAX_BLK_DEVS; i++) {
+        DriveInfo *dinfo = drive_get(IF_IDE, 0, i);
+        if (dinfo) {
+            spapr_virtio_create_blk(spapr->virtio_bus, dinfo);
+        }
+    }
+
     if (kernel_filename) {
         uint64_t lowaddr = 0;
 
diff --git a/hw/spapr.h b/hw/spapr.h
index b52133a..382b0f1 100644
--- a/hw/spapr.h
+++ b/hw/spapr.h
@@ -2,10 +2,12 @@
 #define __HW_SPAPR_H__
 
 struct VIOsPAPRBus;
+struct VirtIOsPAPRBus;
 struct icp_state;
 
 typedef struct sPAPREnvironment {
     struct VIOsPAPRBus *vio_bus;
+    struct VirtIOsPAPRBus *virtio_bus;
     struct icp_state *icp;
 
     void *htab;
@@ -256,7 +258,14 @@ typedef struct sPAPREnvironment {
  */
 #define KVMPPC_HCALL_BASE       0xf000
 #define KVMPPC_H_RTAS           (KVMPPC_HCALL_BASE + 0x0)
-#define KVMPPC_HCALL_MAX        KVMPPC_H_RTAS
+#define KVMPPC_H_VIRTIO_QUEUE_NOTIFY         (KVMPPC_HCALL_BASE + 0x1)
+#define KVMPPC_H_VIRTIO_RESET                (KVMPPC_HCALL_BASE + 0x2)
+#define KVMPPC_H_VIRTIO_SET_STATUS           (KVMPPC_HCALL_BASE + 0x3)
+#define KVMPPC_H_VIRTIO_GET_STATUS           (KVMPPC_HCALL_BASE + 0x4)
+#define KVMPPC_H_VIRTIO_SET_FEATURES         (KVMPPC_HCALL_BASE + 0x5)
+#define KVMPPC_H_VIRTIO_GET_FEATURES         (KVMPPC_HCALL_BASE + 0x6)
+#define KVMPPC_H_VIRTIO_CONFIG_CHANGED       (KVMPPC_HCALL_BASE + 0x7)
+#define KVMPPC_HCALL_MAX       KVMPPC_H_VIRTIO_CONFIG_CHANGED
 
 extern sPAPREnvironment *spapr;
 
diff --git a/hw/spapr_virtio.c b/hw/spapr_virtio.c
new file mode 100644
index 0000000..1044878
--- /dev/null
+++ b/hw/spapr_virtio.c
@@ -0,0 +1,641 @@
+/*
+ * QEMU SPAPR virtio target
+ *
+ * Copyright (c) 2011 Alexey Kardashevskiy <address@hidden>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "hw.h"
+#include "block.h"
+#include "sysemu.h"
+#include "net.h"
+#include "boards.h"
+#include "monitor.h"
+#include "loader.h"
+#include "elf.h"
+#include "hw/virtio.h"
+#include "hw/virtio-serial.h"
+#include "hw/virtio-net.h"
+#include "hw/virtio-balloon.h"
+#include "hw/sysbus.h"
+#include "kvm.h"
+
+#include "hw/spapr.h"
+#include "hw/spapr_virtio.h"
+
+#include "blockdev.h"
+
+#include <libfdt.h>
+
+struct BusInfo spapr_virtio_bus_info = {
+    .name       = "spapr-virtio",
+    .size       = sizeof(VirtIOsPAPRBus),
+};
+
+typedef struct {
+    DeviceInfo qdev;
+    const char *dt_name;
+    int (*init)(struct VirtIOsPAPRDevice *dev);
+} VirtIOsPAPRDeviceInfo;
+
+typedef struct VirtIOsPAPRDevice {
+    DeviceState qdev;
+    VirtIODevice *vdev;
+
+    BlockConf block;
+
+    NICConf nic;
+    virtio_net_conf net;
+
+    V9fsConf fsconf;
+    virtio_serial_conf serial_conf;
+
+    /* Device parameters to be put into the device tree */
+    /*  [0] is config page and per-device irq, [1..] are vrings  */
+    int num_res;
+    uint32_t first_irq;
+    struct {
+        uint64_t addr;
+        uint32_t size;
+        qemu_irq qirq;
+    } *res;
+
+} VirtIOsPAPRDevice;
+
+static const VirtIOBindings spapr_virtio_bindings;
+
+static ram_addr_t spapr_virtio_device_num_vq(VirtIOsPAPRDevice *dev)
+{
+    VirtIODevice *vdev = dev->vdev;
+    int num_vq;
+
+    for (num_vq = 0; num_vq < VIRTIO_PCI_QUEUE_MAX; num_vq++) {
+        if (!virtio_queue_get_num(vdev, num_vq)) {
+            break;
+        }
+    }
+
+    return num_vq;
+}
+
+static int spapr_virtio_alloc_irqs(VirtIOsPAPRDevice *dev)
+{
+    VirtIOsPAPRBus *bus = (VirtIOsPAPRBus *) dev->qdev.parent_bus;
+    int i;
+
+    dev->first_irq = bus->current_irq;
+    for (i = 0; i < dev->num_res; ++i) {
+        dev->res[i].qirq = xics_find_qirq(bus->icp, dev->first_irq + i);
+        if (NULL == dev->res[i].qirq) {
+            printf("QEMU: xics_find_qirq failed\n");
+            return -1;
+        }
+        ++bus->current_irq;
+    }
+
+    return 0;
+}
+
+static int spapr_virtio_alloc_vrings(VirtIOsPAPRDevice *dev)
+{
+    int i;
+    unsigned aligned_size;
+    VirtIOsPAPRBus *bus;
+
+    bus = DO_UPCAST(VirtIOsPAPRBus, bus, dev->qdev.parent_bus);
+
+    dev->num_res = 1 + spapr_virtio_device_num_vq(dev);
+    dev->res = qemu_mallocz(sizeof(dev->res[0]) * dev->num_res);
+
+    for (i = 0; i < dev->num_res; i++) {
+        if (0 == i) {
+            dev->res[i].size = dev->vdev->config_len;
+         } else {
+            dev->res[i].size = virtio_queue_get_mem_size(dev->vdev, i - 1,
+                    TARGET_PAGE_SIZE);
+        }
+        aligned_size = TARGET_PAGE_ALIGN(dev->res[i].size);
+        dev->res[i].addr = bus->current_addr;
+
+        if (SPAPR_VIRTIO_SHARED_MEM_SIZE - bus->reg_mem_used < aligned_size) {
+            perror("No memory for virtio rings");
+            exit(-1);
+        }
+        bus->reg_mem_used += aligned_size;
+        bus->current_addr += aligned_size;
+    }
+    return 0;
+}
+
+static void spapr_virtio_device_sync_vq(VirtIOsPAPRDevice *dev)
+{
+    int i;
+
+    dev->vdev->config_vector = 0;
+    for (i = 1; i < dev->num_res; i++) {
+        virtio_queue_set_vector(dev->vdev, i - 1, i);
+        virtio_queue_set_addr(dev->vdev, i - 1, dev->res[i].addr);
+    }
+    if (0 < dev->vdev->config_len) {
+        if (dev->vdev->get_config) {
+            dev->vdev->get_config(dev->vdev, dev->vdev->config);
+        }
+        cpu_physical_memory_rw(dev->res[0].addr, dev->vdev->config,
+                dev->vdev->config_len, 1);
+    }
+}
+
+/* Common init function for virtio-spapr devices */
+static int spapr_virtio_init_device(VirtIOsPAPRDevice *v)
+{
+    virtio_bind_device(v->vdev, &spapr_virtio_bindings, v);
+    virtio_reset(v->vdev);
+
+    if (0 != spapr_virtio_alloc_vrings(v)) {
+        return -1;
+    }
+
+    spapr_virtio_device_sync_vq(v);
+
+    virtio_set_status(v->vdev, v->vdev->status | VIRTIO_CONFIG_S_DRIVER_OK);
+
+    if ((NULL == v->qdev.id) &&
+            (asprintf((char **)&v->qdev.id, "address@hidden",
+                    ((VirtIOsPAPRDeviceInfo *)v->qdev.info)->dt_name,
+                    (unsigned long long) (v->res[0].addr)) < 0)) {
+        printf("QEMU: asprintf failed\n");
+        return -1;
+    }
+    return spapr_virtio_alloc_irqs(v);
+}
+
+static int spapr_virtio_net_init(VirtIOsPAPRDevice *dev)
+{
+    dev->vdev = virtio_net_init((DeviceState *)dev, &dev->nic, &dev->net);
+    return (NULL == dev->vdev) ? -1 : spapr_virtio_init_device(dev);
+}
+
+static int spapr_virtio_blk_init(VirtIOsPAPRDevice *dev)
+{
+    dev->vdev = virtio_blk_init((DeviceState *)dev, &dev->block);
+    return (NULL == dev->vdev) ? -1 : spapr_virtio_init_device(dev);
+}
+
+static int spapr_virtio_serial_init(VirtIOsPAPRDevice *dev)
+{
+    dev->vdev = virtio_serial_init((DeviceState *)dev, &dev->serial_conf);
+    return (NULL == dev->vdev) ? -1 : spapr_virtio_init_device(dev);
+}
+
+static int spapr_virtio_balloon_init(VirtIOsPAPRDevice *dev)
+{
+    dev->vdev = virtio_balloon_init((DeviceState *)dev);
+    return (NULL == dev->vdev) ? -1 : spapr_virtio_init_device(dev);
+}
+
+static int spapr_virtio_9p_init(VirtIOsPAPRDevice *dev)
+{
+    dev->vdev = virtio_9p_init((DeviceState *) dev, &dev->fsconf);
+    return (NULL == dev->vdev) ? -1 : spapr_virtio_init_device(dev);
+}
+
+static VirtIOsPAPRDeviceInfo spapr_virtio_net = {
+    .init = spapr_virtio_net_init,
+    .dt_name = "net",
+    .qdev.name = "virtio-net-spapr",
+    .qdev.size = sizeof(VirtIOsPAPRDevice),
+    .qdev.props = (Property[]) {
+        DEFINE_NIC_PROPERTIES(VirtIOsPAPRDevice, nic),
+        DEFINE_PROP_UINT32("x-txtimer", VirtIOsPAPRDevice,
+                           net.txtimer, TX_TIMER_INTERVAL),
+        DEFINE_PROP_INT32("x-txburst", VirtIOsPAPRDevice,
+                          net.txburst, TX_BURST),
+        DEFINE_PROP_STRING("tx", VirtIOsPAPRDevice, net.tx),
+        DEFINE_PROP_END_OF_LIST(),
+    },
+};
+
+static VirtIOsPAPRDeviceInfo spapr_virtio_blk = {
+    .init = spapr_virtio_blk_init,
+    .dt_name = "blk",
+    .qdev.name = "virtio-blk-spapr",
+    .qdev.size = sizeof(VirtIOsPAPRDevice),
+    .qdev.props = (Property[]) {
+        DEFINE_BLOCK_PROPERTIES(VirtIOsPAPRDevice, block),
+        DEFINE_PROP_END_OF_LIST(),
+    },
+};
+
+static VirtIOsPAPRDeviceInfo spapr_virtio_serial = {
+    .init = spapr_virtio_serial_init,
+    .dt_name = "serial",
+    .qdev.name = "virtio-serial-spapr",
+    .qdev.size = sizeof(VirtIOsPAPRDevice),
+    .qdev.props = (Property[]) {
+        DEFINE_PROP_UINT32("max_ports", VirtIOsPAPRDevice,
+            serial_conf.max_virtserial_ports, 4),
+        DEFINE_PROP_END_OF_LIST(),
+    },
+};
+
+static VirtIOsPAPRDeviceInfo spapr_virtio_balloon = {
+    .init = spapr_virtio_balloon_init,
+    .dt_name = "balloon",
+    .qdev.name = "virtio-balloon-spapr",
+    .qdev.size = sizeof(VirtIOsPAPRDevice),
+    .qdev.props = (Property[]) {
+        DEFINE_PROP_END_OF_LIST(),
+    },
+};
+
+static VirtIOsPAPRDeviceInfo spapr_virtio_9p = {
+    .init = spapr_virtio_9p_init,
+    .dt_name = "9p",
+    .qdev.name = "virtio-9p-spapr",
+    .qdev.size = sizeof(VirtIOsPAPRDevice),
+    .qdev.props = (Property[]) {
+        DEFINE_PROP_STRING("mount_tag", VirtIOsPAPRDevice, fsconf.tag),
+        DEFINE_PROP_STRING("fsdev", VirtIOsPAPRDevice, fsconf.fsdev_id),
+        DEFINE_PROP_END_OF_LIST(),
+    },
+};
+
+static VirtIOsPAPRDevice *spapr_virtio_find(VirtIOsPAPRBus *bus,
+                                            target_ulong vqaddr, int *index)
+{
+    DeviceState *qdev;
+    VirtIOsPAPRDevice *dev;
+    int  i;
+
+    QLIST_FOREACH(qdev, &bus->bus.children, sibling) {
+
+        dev = (VirtIOsPAPRDevice *)qdev;
+        for (i = 0; i < dev->num_res; ++i) {
+
+            if (dev->res[i].addr == vqaddr) {
+                /* first "reg" belongs to the device itself */
+                if (index) {
+                    *index = i - 1;
+                }
+                return dev;
+            }
+        }
+    }
+    return NULL;
+}
+
+static target_ulong spapr_virtio_notify(CPUState *env,
+                                        sPAPREnvironment *spapr,
+                                        target_ulong opcode,
+                                        target_ulong *args)
+{
+    int index;
+    VirtIOsPAPRDevice *dev = spapr_virtio_find(spapr->virtio_bus, args[0],
+                                               &index);
+    if (NULL == dev) {
+        return H_PARAMETER;
+    }
+    virtio_queue_notify(dev->vdev, index);
+    return H_SUCCESS;
+}
+
+static target_ulong spapr_virtio_reset(CPUState *env,
+                                       sPAPREnvironment *spapr,
+                                       target_ulong opcode,
+                                       target_ulong *args)
+{
+    VirtIOsPAPRDevice *dev = spapr_virtio_find(spapr->virtio_bus, args[0],
+                                               NULL);
+    if (NULL == dev) {
+        return H_PARAMETER;
+    }
+    virtio_reset(dev->vdev);
+    spapr_virtio_device_sync_vq(dev);
+    return H_SUCCESS;
+}
+
+static target_ulong spapr_virtio_set_status(CPUState *env,
+                                            sPAPREnvironment *spapr,
+                                            target_ulong opcode,
+                                            target_ulong *args)
+{
+    VirtIOsPAPRDevice *dev = spapr_virtio_find(spapr->virtio_bus, args[0],
+                                               NULL);
+    if (NULL == dev) {
+        return H_PARAMETER;
+    }
+    virtio_set_status(dev->vdev, 0xFF & args[1]);
+    return H_SUCCESS;
+}
+
+static target_ulong spapr_virtio_get_status(CPUState *env,
+                                            sPAPREnvironment *spapr,
+                                            target_ulong opcode,
+                                            target_ulong *args)
+{
+    VirtIOsPAPRDevice *dev = spapr_virtio_find(spapr->virtio_bus, args[0],
+                                               NULL);
+    if (NULL == dev) {
+        return H_PARAMETER;
+    }
+    args[0] = dev->vdev->status;
+    return H_SUCCESS;
+}
+
+static target_ulong spapr_virtio_set_features(CPUState *env,
+                                              sPAPREnvironment *spapr,
+                                              target_ulong opcode,
+                                              target_ulong *args)
+{
+    VirtIOsPAPRDevice *dev = spapr_virtio_find(spapr->virtio_bus, args[0],
+                                               NULL);
+    if (NULL == dev) {
+        return H_PARAMETER;
+    }
+    if (0 != args[1]) {
+        printf("QEMU: only single cell features are supported\n");
+        return H_PARAMETER;
+    }
+    if (dev->vdev->set_features) {
+        dev->vdev->set_features(dev->vdev, (uint32_t)args[2]);
+    } else {
+        dev->vdev->guest_features = (uint32_t)args[2];
+    }
+    return H_SUCCESS;
+}
+
+static target_ulong spapr_virtio_get_features(CPUState *env,
+                                              sPAPREnvironment *spapr,
+                                              target_ulong opcode,
+                                              target_ulong *args)
+{
+    VirtIOsPAPRDevice *dev = spapr_virtio_find(spapr->virtio_bus, args[0],
+                                               NULL);
+    if (NULL == dev) {
+        return H_PARAMETER;
+    }
+    if (0 != args[1]) {
+        printf("QEMU: only single cell features are supported\n");
+        return H_PARAMETER;
+    }
+    if (dev->vdev->get_features) {
+        args[0] = dev->vdev->get_features(dev->vdev, 0);
+    } else {
+        args[0] = dev->vdev->guest_features;
+    }
+    return H_SUCCESS;
+}
+
+static target_ulong spapr_virtio_config_changed(CPUState *env,
+                                                sPAPREnvironment *spapr,
+                                                target_ulong opcode,
+                                                target_ulong *args)
+{
+    VirtIOsPAPRDevice *dev = spapr_virtio_find(spapr->virtio_bus, args[0],
+                                               NULL);
+    if (NULL == dev) {
+        return H_PARAMETER;
+    }
+    if (0 < dev->vdev->config_len) {
+        uint8_t tmp[dev->vdev->config_len];
+
+        cpu_physical_memory_rw(dev->res[0].addr, tmp, dev->vdev->config_len, 
0);
+
+        if (dev->vdev->set_config) {
+            dev->vdev->set_config(dev->vdev, tmp);
+        } else {
+            memcpy(dev->vdev->config, tmp, dev->vdev->config_len);
+        }
+    }
+    return H_SUCCESS;
+}
+
+/* Creates virtio-bus device */
+VirtIOsPAPRBus *spapr_virtio_bus_init(ram_addr_t ramsize,
+                                      struct icp_state *icp,
+                                      int start_irq,
+                                      int num_irq)
+{
+    VirtIOsPAPRBus *bus;
+    BusState *_bus;
+    DeviceState *dev;
+
+    /* Create bridge device */
+    dev = qdev_create(NULL, "spapr-virtio-bridge");
+    qdev_init_nofail(dev);
+
+    /* Create bus on bridge device */
+    _bus = qbus_create(&spapr_virtio_bus_info, dev, "spapr-virtio");
+    bus = DO_UPCAST(VirtIOsPAPRBus, bus, _bus);
+
+    /* ramsize is supposed to be 16M aligned and we keep here this alignment */
+    bus->current_addr = ramsize;
+    bus->reg_mem_used = 0;
+
+    /* Setup IRQ parameters */
+    bus->icp = icp;
+    bus->current_irq = start_irq;
+    bus->num_irq = num_irq;
+
+    /* Register virtio hcalls */
+    spapr_register_hypercall(KVMPPC_H_VIRTIO_QUEUE_NOTIFY, 
spapr_virtio_notify);
+    spapr_register_hypercall(KVMPPC_H_VIRTIO_RESET, spapr_virtio_reset);
+    spapr_register_hypercall(KVMPPC_H_VIRTIO_SET_STATUS,
+            spapr_virtio_set_status);
+    spapr_register_hypercall(KVMPPC_H_VIRTIO_GET_STATUS,
+            spapr_virtio_get_status);
+    spapr_register_hypercall(KVMPPC_H_VIRTIO_SET_FEATURES,
+            spapr_virtio_set_features);
+    spapr_register_hypercall(KVMPPC_H_VIRTIO_GET_FEATURES,
+            spapr_virtio_get_features);
+    spapr_register_hypercall(KVMPPC_H_VIRTIO_CONFIG_CHANGED,
+            spapr_virtio_config_changed);
+
+    return bus;
+}
+
+/* Creates virtio-net device */
+void spapr_virtio_create_nic(VirtIOsPAPRBus *bus, NICInfo *nd)
+{
+    VirtIOsPAPRDevice *v = (VirtIOsPAPRDevice *)
+            qdev_create(&bus->bus, "virtio-net-spapr");
+    if (NULL == v) {
+        return;
+    }
+    qdev_set_nic_properties(&v->qdev, nd);
+    qdev_init_nofail(&v->qdev);
+}
+
+/* Creates virtio-blk device */
+void spapr_virtio_create_blk(VirtIOsPAPRBus *bus, DriveInfo *dinfo)
+{
+    VirtIOsPAPRDevice *v = (VirtIOsPAPRDevice *)
+            qdev_create(&bus->bus, "virtio-blk-spapr");
+    if (NULL == v) {
+        return;
+    }
+    qdev_prop_set_drive_nofail(&v->qdev, "drive", dinfo->bdrv);
+    qdev_init_nofail(&v->qdev);
+}
+
+/* Populate the device tree */
+int spapr_populate_virtio_devices(VirtIOsPAPRBus *bus, void *fdt)
+{
+    int bus_off, node_off = 0, ret, cb, i;
+    DeviceState *qdev;
+    char tmp[256];
+
+    bus_off = fdt_path_offset(fdt, "/virtio-bus");
+    if (bus_off < 0) {
+        return bus_off;
+    }
+
+    QLIST_FOREACH(qdev, &bus->bus.children, sibling) {
+
+        VirtIOsPAPRDevice *dev = (VirtIOsPAPRDevice *)qdev;
+
+        node_off = fdt_add_subnode(fdt, bus_off, dev->qdev.id);
+        if (node_off < 0) {
+            return node_off;
+        }
+        sprintf(tmp, "qemu,virtio-spapr-%04X", dev->vdev->device_id);
+        cb = strlen(tmp) + 1;
+        strcpy(tmp + cb, "qemu,virtio-spapr");
+        cb += strlen(tmp + cb) + 1;
+
+        ret = fdt_setprop(fdt, node_off, "compatible", tmp, cb);
+        if (ret < 0) {
+            return ret;
+        }
+
+        if (0 != dev->num_res) {
+            struct {
+                uint64_t addr;
+                uint32_t size;
+            } __attribute__((packed)) reg[dev->num_res];
+            struct {
+                uint32_t irq;
+                uint32_t flags;
+            } __attribute__((packed)) irq[dev->num_res];
+
+            for (i = 0; i < dev->num_res; ++i) {
+                reg[i].addr = cpu_to_be64(dev->res[i].addr);
+                reg[i].size = cpu_to_be32(dev->res[i].size);
+                irq[i].irq = cpu_to_be32(dev->first_irq + i);
+                irq[i].flags = cpu_to_be32(0);
+            }
+
+            ret = fdt_setprop(fdt, node_off, "reg", reg, sizeof(reg));
+            if (ret < 0) {
+                return ret;
+            }
+            ret = fdt_setprop(fdt, node_off, "interrupts", irq, sizeof(irq));
+            if (ret < 0) {
+                return ret;
+            }
+        }
+    }
+    return node_off;
+}
+
+/**************** SPAPR Virtio Bus Device Descriptions *******************/
+
+static void spapr_notify_guest(void *opaque, uint16_t vector)
+{
+    VirtIOsPAPRDevice *dev = (VirtIOsPAPRDevice *)opaque;
+
+    if (VIRTIO_NO_VECTOR == vector) {
+        printf("QEMU: pulse_irq %u, no real pulse, dev=%p\n", vector, dev);
+        return;
+    }
+    if (NULL == dev->res[vector].qirq) {
+        printf("QEMU: trying to pulse uninitialized IRQ, vector=%u\n",
+            vector + 1);
+        return;
+    }
+
+    if ((dev->vdev->config_vector == vector) && (0 < dev->vdev->config_len)) {
+        if (dev->vdev->get_config) {
+            dev->vdev->get_config(dev->vdev, dev->vdev->config);
+        }
+        cpu_physical_memory_rw(dev->res[0].addr, dev->vdev->config,
+                dev->vdev->config_len, 1);
+    }
+#ifdef DEBUG
+    static unsigned int cntrs;
+    printf("QEMU: [%u] do pulse_irq for vector#%u\n", cntrs++, vector);
+#endif
+    qemu_irq_pulse(dev->res[vector].qirq);
+}
+
+static unsigned spapr_get_features(void *opaque)
+{
+    VirtIOsPAPRDevice *dev = (VirtIOsPAPRDevice *)opaque;
+    uint32_t host_features = dev->vdev->get_features(dev->vdev, 0);
+    return host_features;
+}
+
+static const VirtIOBindings spapr_virtio_bindings = {
+    .notify = spapr_notify_guest,
+    .get_features = spapr_get_features,
+};
+
+static int spapr_virtio_busdev_init(DeviceState *dev, DeviceInfo *info)
+{
+    VirtIOsPAPRDeviceInfo *_info = (VirtIOsPAPRDeviceInfo *)info;
+    VirtIOsPAPRDevice *_dev = (VirtIOsPAPRDevice *)dev;
+
+    return _info->init(_dev);
+}
+
+static void spapr_virtio_bus_register_withprop(VirtIOsPAPRDeviceInfo *info)
+{
+    info->qdev.init = spapr_virtio_busdev_init;
+    info->qdev.bus_info = &spapr_virtio_bus_info;
+
+    assert(info->qdev.size >= sizeof(VirtIOsPAPRDevice));
+    qdev_register(&info->qdev);
+}
+
+static void spapr_virtio_register(void)
+{
+    spapr_virtio_bus_register_withprop(&spapr_virtio_net);
+    spapr_virtio_bus_register_withprop(&spapr_virtio_blk);
+    spapr_virtio_bus_register_withprop(&spapr_virtio_serial);
+    spapr_virtio_bus_register_withprop(&spapr_virtio_balloon);
+    spapr_virtio_bus_register_withprop(&spapr_virtio_9p);
+}
+device_init(spapr_virtio_register);
+
+/* Only required to have the virtio bus as child in the system bus */
+static int spapr_virtio_bridge_init(SysBusDevice *dev)
+{
+    return 0;
+}
+
+static SysBusDeviceInfo spapr_virtio_bridge_info = {
+    .init = spapr_virtio_bridge_init,
+    .qdev.name  = "spapr-virtio-bridge",
+    .qdev.size  = sizeof(SysBusDevice),
+    .qdev.no_user = 1,
+};
+
+static void spapr_virtio_register_devices(void)
+{
+    sysbus_register_withprop(&spapr_virtio_bridge_info);
+}
+
+device_init(spapr_virtio_register_devices)
+
diff --git a/hw/spapr_virtio.h b/hw/spapr_virtio.h
new file mode 100644
index 0000000..7ecad57
--- /dev/null
+++ b/hw/spapr_virtio.h
@@ -0,0 +1,60 @@
+/*
+ * QEMU SPAPR VirtIO BUS definitions
+ *
+ * Copyright (c) 2011 Alexey Kardashevskiy <address@hidden>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+#if !defined(__HW_SPAPR_VIRTIO_H__)
+#define __HW_SPAPR_VIRTIO_H__
+
+#include "hw/xics.h"
+
+struct VirtIOsPAPRDevice;
+
+/*
+    Number of bytes behind main RAM to be used for sharing config space
+    and virtio's vrings
+*/
+#define SPAPR_VIRTIO_SHARED_MEM_SIZE    (16<<20)
+
+typedef struct VirtIOsPAPRBus {
+    BusState bus;
+    target_phys_addr_t current_addr;
+
+    struct icp_state *icp;
+    int current_irq;
+    int num_irq;
+
+/*
+    power-kvm does not support multiple memory regions so
+    the code places them right behind the main memory area.
+    This area hosts config and vring memory.
+*/
+    unsigned int reg_mem_used;
+} VirtIOsPAPRBus;
+
+VirtIOsPAPRBus *spapr_virtio_bus_init(ram_addr_t ramsize,
+                                      struct icp_state *icp,
+                                      int start_irq,
+                                      int num_irq);
+
+void spapr_virtio_create_nic(VirtIOsPAPRBus *bus, NICInfo *nd);
+
+void spapr_virtio_create_blk(VirtIOsPAPRBus *bus, DriveInfo *dinfo);
+
+int spapr_populate_virtio_devices(VirtIOsPAPRBus *bus, void *fdt);
+
+#endif /* __HW_SPAPR_VIRTIO_H__ */
+
-- 
1.7.4.4




reply via email to

[Prev in Thread] Current Thread [Next in Thread]