Patchwork [3/3] powerpc-virtio: virtio support introduced (block, network, serial, balloon, 9p-fs), both fullemu and power-kvm

login
register
mail settings
Submitter David Gibson
Date May 17, 2011, 6:47 a.m.
Message ID <1305614826-1815-4-git-send-email-david@gibson.dropbear.id.au>
Download mbox | patch
Permalink /patch/95858/
State New
Headers show

Comments

David Gibson - May 17, 2011, 6:47 a.m.
From: Alexey Kardashevskiy <aik@ozlabs.ru>

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 <aik@ozlabs.ru>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
 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
Alexander Graf - May 17, 2011, 7:06 a.m.
On 17.05.2011, at 08:47, David Gibson wrote:

> From: Alexey Kardashevskiy <aik@ozlabs.ru>
> 
> 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.

Before including such a patch, we should really discuss the desired interface for virtio on sPAPR. I personally would prefer if we could have a generic MMIO hypercall that the guest issues, so that we can simply use virtio-pci on sPAPR (and all the other PCI hardware).

But at the end of the day, the steps should be as follows:

  1) Discuss this on the virtualization ML
  2) Send patches for the virtio documentation so the protocol has a spec (which we're lacking for s390 still)
  3) Implement Linux side, upstream it
  4) Upstream Qemu side

Since I haven't seen 1-3, I'd like to defer this patch until the other points are good :)


Alex
David Gibson - May 18, 2011, 3:27 a.m.
On Tue, May 17, 2011 at 09:06:41AM +0200, Alexander Graf wrote:
> On 17.05.2011, at 08:47, David Gibson wrote:
> > From: Alexey Kardashevskiy <aik@ozlabs.ru>
> > 
> > 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.
[snip]
> 
> Before including such a patch, we should really discuss the desired
> interface for virtio on sPAPR. I personally would prefer if we could
> have a generic MMIO hypercall that the guest issues, so that we can
> simply use virtio-pci on sPAPR (and all the other PCI hardware).

Hrm.  I'm not thrilled at that idea.  We would still need to advertise
in the device tree to use the MMIO hypercall, instead of going direct
- since we will be going direct for things like passthrough PCI
devices under KVM.

> But at the end of the day, the steps should be as follows:
> 
>   1) Discuss this on the virtualization ML

Ok, which list do you mean here?

>   2) Send patches for the virtio documentation so the protocol has a spec (which we're lacking for s390 still)
>   3) Implement Linux side, upstream it

I don't see why the Linux guest side needs to go before the qemu
side.  You can't use one without the other, so there's no clear order.

>   4) Upstream Qemu side
> 
> Since I haven't seen 1-3, I'd like to defer this patch until the
> other points are good :)

Hmm.  I'll see what I can do.
Michael S. Tsirkin - May 18, 2011, 8:58 a.m.
On Wed, May 18, 2011 at 01:27:45PM +1000, David Gibson wrote:
> On Tue, May 17, 2011 at 09:06:41AM +0200, Alexander Graf wrote:
> > On 17.05.2011, at 08:47, David Gibson wrote:
> > > From: Alexey Kardashevskiy <aik@ozlabs.ru>
> > > 
> > > 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.
> [snip]
> > 
> > Before including such a patch, we should really discuss the desired
> > interface for virtio on sPAPR. I personally would prefer if we could
> > have a generic MMIO hypercall that the guest issues, so that we can
> > simply use virtio-pci on sPAPR (and all the other PCI hardware).
> 
> Hrm.  I'm not thrilled at that idea.  We would still need to advertise
> in the device tree to use the MMIO hypercall, instead of going direct
> - since we will be going direct for things like passthrough PCI
> devices under KVM.
> 
> > But at the end of the day, the steps should be as follows:
> > 
> >   1) Discuss this on the virtualization ML
> 
> Ok, which list do you mean here?
> 
> >   2) Send patches for the virtio documentation so the protocol has a spec (which we're lacking for s390 still)
> >   3) Implement Linux side, upstream it
> 
> I don't see why the Linux guest side needs to go before the qemu
> side.  You can't use one without the other, so there's no clear order.

Agree here. I would further argue qemu should go in first,
this is what happens with real devices: first hardware, then
driver. Linux side just needs to be ready and available in the repository
somewhere.

> >   4) Upstream Qemu side
> > 
> > Since I haven't seen 1-3, I'd like to defer this patch until the
> > other points are good :)
> 
> Hmm.  I'll see what I can do.
> 
> -- 
> David Gibson			| I'll have my music baroque, and my code
> david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
> 				| _way_ _around_!
> http://www.ozlabs.org/~dgibson
Alexander Graf - May 18, 2011, 9:35 a.m.
On 18.05.2011, at 05:27, David Gibson wrote:

> On Tue, May 17, 2011 at 09:06:41AM +0200, Alexander Graf wrote:
>> On 17.05.2011, at 08:47, David Gibson wrote:
>>> From: Alexey Kardashevskiy <aik@ozlabs.ru>
>>> 
>>> 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.
> [snip]
>> 
>> Before including such a patch, we should really discuss the desired
>> interface for virtio on sPAPR. I personally would prefer if we could
>> have a generic MMIO hypercall that the guest issues, so that we can
>> simply use virtio-pci on sPAPR (and all the other PCI hardware).
> 
> Hrm.  I'm not thrilled at that idea.  We would still need to advertise
> in the device tree to use the MMIO hypercall, instead of going direct
> - since we will be going direct for things like passthrough PCI
> devices under KVM.

Yes, it's not an easy thing to solve. But not having the chance of emulating MMIO robs you off quite some possibilities and reimplementing yet another host abstraction layer for virtio will be more headaches than you can imagine :).

> 
>> But at the end of the day, the steps should be as follows:
>> 
>>  1) Discuss this on the virtualization ML
> 
> Ok, which list do you mean here?

The one I CC'ed :)

> 
>>  2) Send patches for the virtio documentation so the protocol has a spec (which we're lacking for s390 still)
>>  3) Implement Linux side, upstream it
> 
> I don't see why the Linux guest side needs to go before the qemu
> side.  You can't use one without the other, so there's no clear order.

Well, ok. I'd at least like to see the Linux side on the mailing list then before pulling the qemu side :).

> 
>>  4) Upstream Qemu side
>> 
>> Since I haven't seen 1-3, I'd like to defer this patch until the
>> other points are good :)
> 
> Hmm.  I'll see what I can do.

Thanks!

Alex

Patch

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 <aik@au1.ibm.com>
+ *
+ * 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, "%s@%llx",
+                    ((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 <aik@au1.ibm.com>
+ *
+ * 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__ */
+