@@ -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
@@ -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;
@@ -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;
new file mode 100644
@@ -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)
+
new file mode 100644
@@ -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__ */
+