aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
Diffstat (limited to 'hw')
-rw-r--r--hw/core/Makefile.objs1
-rw-r--r--hw/core/machine.c34
-rw-r--r--hw/core/platform-bus.c253
-rw-r--r--hw/core/qdev.c11
-rw-r--r--hw/core/sysbus.c79
-rw-r--r--hw/gpio/Makefile.objs1
-rw-r--r--hw/gpio/mpc8xxx.c217
-rw-r--r--hw/intc/openpic_kvm.c19
-rw-r--r--hw/nvram/spapr_nvram.c81
-rw-r--r--hw/ppc/Makefile.objs2
-rw-r--r--hw/ppc/e500.c199
-rw-r--r--hw/ppc/e500.h6
-rw-r--r--hw/ppc/e500plat.c7
-rw-r--r--hw/ppc/ppc4xx_pci.c24
-rw-r--r--hw/ppc/spapr.c43
-rw-r--r--hw/ppc/spapr_pci.c28
16 files changed, 941 insertions, 64 deletions
diff --git a/hw/core/Makefile.objs b/hw/core/Makefile.objs
index 17845df3f0..9dce1bc53c 100644
--- a/hw/core/Makefile.objs
+++ b/hw/core/Makefile.objs
@@ -14,3 +14,4 @@ common-obj-$(CONFIG_SOFTMMU) += machine.o
common-obj-$(CONFIG_SOFTMMU) += null-machine.o
common-obj-$(CONFIG_SOFTMMU) += loader.o
common-obj-$(CONFIG_SOFTMMU) += qdev-properties-system.o
+common-obj-$(CONFIG_SOFTMMU) += platform-bus.o
diff --git a/hw/core/machine.c b/hw/core/machine.c
index 7f3418c5af..19d3e3a707 100644
--- a/hw/core/machine.c
+++ b/hw/core/machine.c
@@ -12,6 +12,9 @@
#include "hw/boards.h"
#include "qapi/visitor.h"
+#include "hw/sysbus.h"
+#include "sysemu/sysemu.h"
+#include "qemu/error-report.h"
static char *machine_get_accel(Object *obj, Error **errp)
{
@@ -257,8 +260,35 @@ static void machine_set_iommu(Object *obj, bool value, Error **errp)
ms->iommu = value;
}
+static int error_on_sysbus_device(SysBusDevice *sbdev, void *opaque)
+{
+ error_report("Option '-device %s' cannot be handled by this machine",
+ object_class_get_name(object_get_class(OBJECT(sbdev))));
+ exit(1);
+}
+
+static void machine_init_notify(Notifier *notifier, void *data)
+{
+ Object *machine = qdev_get_machine();
+ ObjectClass *oc = object_get_class(machine);
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ if (mc->has_dynamic_sysbus) {
+ /* Our machine can handle dynamic sysbus devices, we're all good */
+ return;
+ }
+
+ /*
+ * Loop through all dynamically created devices and check whether there
+ * are sysbus devices among them. If there are, error out.
+ */
+ foreach_dynamic_sysbus_device(error_on_sysbus_device, NULL);
+}
+
static void machine_initfn(Object *obj)
{
+ MachineState *ms = MACHINE(obj);
+
object_property_add_str(obj, "accel",
machine_get_accel, machine_set_accel, NULL);
object_property_add_bool(obj, "kernel-irqchip",
@@ -303,6 +333,10 @@ static void machine_initfn(Object *obj)
object_property_add_bool(obj, "iommu",
machine_get_iommu,
machine_set_iommu, NULL);
+
+ /* Register notifier when init is done for sysbus sanity checks */
+ ms->sysbus_notifier.notify = machine_init_notify;
+ qemu_add_machine_init_done_notifier(&ms->sysbus_notifier);
}
static void machine_finalize(Object *obj)
diff --git a/hw/core/platform-bus.c b/hw/core/platform-bus.c
new file mode 100644
index 0000000000..0f052b3338
--- /dev/null
+++ b/hw/core/platform-bus.c
@@ -0,0 +1,253 @@
+/*
+ * Platform Bus device to support dynamic Sysbus devices
+ *
+ * Copyright (C) 2014 Freescale Semiconductor, Inc. All rights reserved.
+ *
+ * Author: Alexander Graf, <agraf@suse.de>
+ *
+ * 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/platform-bus.h"
+#include "monitor/monitor.h"
+#include "exec/address-spaces.h"
+#include "sysemu/sysemu.h"
+
+
+/*
+ * Returns the PlatformBus IRQ number for a SysBusDevice irq number or -1 if
+ * the IRQ is not mapped on this Platform bus.
+ */
+int platform_bus_get_irqn(PlatformBusDevice *pbus, SysBusDevice *sbdev,
+ int n)
+{
+ qemu_irq sbirq = sysbus_get_connected_irq(sbdev, n);
+ int i;
+
+ for (i = 0; i < pbus->num_irqs; i++) {
+ if (pbus->irqs[i] == sbirq) {
+ return i;
+ }
+ }
+
+ /* IRQ not mapped on platform bus */
+ return -1;
+}
+
+/*
+ * Returns the PlatformBus MMIO region offset for Region n of a SysBusDevice or
+ * -1 if the region is not mapped on this Platform bus.
+ */
+hwaddr platform_bus_get_mmio_addr(PlatformBusDevice *pbus, SysBusDevice *sbdev,
+ int n)
+{
+ MemoryRegion *pbus_mr = &pbus->mmio;
+ MemoryRegion *sbdev_mr = sysbus_mmio_get_region(sbdev, n);
+ Object *pbus_mr_obj = OBJECT(pbus_mr);
+ Object *parent_mr;
+
+ if (!memory_region_is_mapped(sbdev_mr)) {
+ /* Region is not mapped? */
+ return -1;
+ }
+
+ parent_mr = object_property_get_link(OBJECT(sbdev_mr), "container", NULL);
+
+ assert(parent_mr);
+ if (parent_mr != pbus_mr_obj) {
+ /* MMIO region is not mapped on platform bus */
+ return -1;
+ }
+
+ return object_property_get_int(OBJECT(sbdev_mr), "addr", NULL);
+}
+
+static int platform_bus_count_irqs(SysBusDevice *sbdev, void *opaque)
+{
+ PlatformBusDevice *pbus = opaque;
+ qemu_irq sbirq;
+ int n, i;
+
+ for (n = 0; ; n++) {
+ if (!sysbus_has_irq(sbdev, n)) {
+ break;
+ }
+
+ sbirq = sysbus_get_connected_irq(sbdev, n);
+ for (i = 0; i < pbus->num_irqs; i++) {
+ if (pbus->irqs[i] == sbirq) {
+ bitmap_set(pbus->used_irqs, i, 1);
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Loop through all sysbus devices and look for unassigned IRQ lines as well as
+ * unassociated MMIO regions. Connect them to the platform bus if available.
+ */
+static void plaform_bus_refresh_irqs(PlatformBusDevice *pbus)
+{
+ bitmap_zero(pbus->used_irqs, pbus->num_irqs);
+ foreach_dynamic_sysbus_device(platform_bus_count_irqs, pbus);
+ pbus->done_gathering = true;
+}
+
+static int platform_bus_map_irq(PlatformBusDevice *pbus, SysBusDevice *sbdev,
+ int n)
+{
+ int max_irqs = pbus->num_irqs;
+ int irqn;
+
+ if (sysbus_is_irq_connected(sbdev, n)) {
+ /* IRQ is already mapped, nothing to do */
+ return 0;
+ }
+
+ irqn = find_first_zero_bit(pbus->used_irqs, max_irqs);
+ if (irqn >= max_irqs) {
+ hw_error("Platform Bus: Can not fit IRQ line");
+ return -1;
+ }
+
+ set_bit(irqn, pbus->used_irqs);
+ sysbus_connect_irq(sbdev, n, pbus->irqs[irqn]);
+
+ return 0;
+}
+
+static int platform_bus_map_mmio(PlatformBusDevice *pbus, SysBusDevice *sbdev,
+ int n)
+{
+ MemoryRegion *sbdev_mr = sysbus_mmio_get_region(sbdev, n);
+ uint64_t size = memory_region_size(sbdev_mr);
+ uint64_t alignment = (1ULL << (63 - clz64(size + size - 1)));
+ uint64_t off;
+ bool found_region = false;
+
+ if (memory_region_is_mapped(sbdev_mr)) {
+ /* Region is already mapped, nothing to do */
+ return 0;
+ }
+
+ /*
+ * Look for empty space in the MMIO space that is naturally aligned with
+ * the target device's memory region
+ */
+ for (off = 0; off < pbus->mmio_size; off += alignment) {
+ if (!memory_region_find(&pbus->mmio, off, size).mr) {
+ found_region = true;
+ break;
+ }
+ }
+
+ if (!found_region) {
+ hw_error("Platform Bus: Can not fit MMIO region of size %"PRIx64, size);
+ }
+
+ /* Map the device's region into our Platform Bus MMIO space */
+ memory_region_add_subregion(&pbus->mmio, off, sbdev_mr);
+
+ return 0;
+}
+
+/*
+ * For each sysbus device, look for unassigned IRQ lines as well as
+ * unassociated MMIO regions. Connect them to the platform bus if available.
+ */
+static int link_sysbus_device(SysBusDevice *sbdev, void *opaque)
+{
+ PlatformBusDevice *pbus = opaque;
+ int i;
+
+ for (i = 0; sysbus_has_irq(sbdev, i); i++) {
+ platform_bus_map_irq(pbus, sbdev, i);
+ }
+
+ for (i = 0; sysbus_has_mmio(sbdev, i); i++) {
+ platform_bus_map_mmio(pbus, sbdev, i);
+ }
+
+ return 0;
+}
+
+static void platform_bus_init_notify(Notifier *notifier, void *data)
+{
+ PlatformBusDevice *pb = container_of(notifier, PlatformBusDevice, notifier);
+
+ /*
+ * Generate a bitmap of used IRQ lines, as the user might have specified
+ * them on the command line.
+ */
+ plaform_bus_refresh_irqs(pb);
+
+ foreach_dynamic_sysbus_device(link_sysbus_device, pb);
+}
+
+static void platform_bus_realize(DeviceState *dev, Error **errp)
+{
+ PlatformBusDevice *pbus;
+ SysBusDevice *d;
+ int i;
+
+ d = SYS_BUS_DEVICE(dev);
+ pbus = PLATFORM_BUS_DEVICE(dev);
+
+ memory_region_init(&pbus->mmio, NULL, "platform bus", pbus->mmio_size);
+ sysbus_init_mmio(d, &pbus->mmio);
+
+ pbus->used_irqs = bitmap_new(pbus->num_irqs);
+ pbus->irqs = g_new0(qemu_irq, pbus->num_irqs);
+ for (i = 0; i < pbus->num_irqs; i++) {
+ sysbus_init_irq(d, &pbus->irqs[i]);
+ }
+
+ /*
+ * Register notifier that allows us to gather dangling devices once the
+ * machine is completely assembled
+ */
+ pbus->notifier.notify = platform_bus_init_notify;
+ qemu_add_machine_init_done_notifier(&pbus->notifier);
+}
+
+static Property platform_bus_properties[] = {
+ DEFINE_PROP_UINT32("num_irqs", PlatformBusDevice, num_irqs, 0),
+ DEFINE_PROP_UINT32("mmio_size", PlatformBusDevice, mmio_size, 0),
+ DEFINE_PROP_END_OF_LIST()
+};
+
+static void platform_bus_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->realize = platform_bus_realize;
+ dc->props = platform_bus_properties;
+}
+
+static const TypeInfo platform_bus_info = {
+ .name = TYPE_PLATFORM_BUS_DEVICE,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(PlatformBusDevice),
+ .class_init = platform_bus_class_init,
+};
+
+static void platform_bus_register_types(void)
+{
+ type_register_static(&platform_bus_info);
+}
+
+type_init(platform_bus_register_types)
diff --git a/hw/core/qdev.c b/hw/core/qdev.c
index b3d519645a..413b41376f 100644
--- a/hw/core/qdev.c
+++ b/hw/core/qdev.c
@@ -453,6 +453,17 @@ void qdev_connect_gpio_out_named(DeviceState *dev, const char *name, int n,
g_free(propname);
}
+qemu_irq qdev_get_gpio_out_connector(DeviceState *dev, const char *name, int n)
+{
+ char *propname = g_strdup_printf("%s[%d]",
+ name ? name : "unnamed-gpio-out", n);
+
+ qemu_irq ret = (qemu_irq)object_property_get_link(OBJECT(dev), propname,
+ NULL);
+
+ return ret;
+}
+
/* disconnect a GPIO ouput, returning the disconnected input (if any) */
static qemu_irq qdev_disconnect_gpio_out_named(DeviceState *dev,
diff --git a/hw/core/sysbus.c b/hw/core/sysbus.c
index e55c3c1d6d..84af59379d 100644
--- a/hw/core/sysbus.c
+++ b/hw/core/sysbus.c
@@ -24,6 +24,51 @@
static void sysbus_dev_print(Monitor *mon, DeviceState *dev, int indent);
static char *sysbus_get_fw_dev_path(DeviceState *dev);
+typedef struct SysBusFind {
+ void *opaque;
+ FindSysbusDeviceFunc *func;
+} SysBusFind;
+
+/* Run func() for every sysbus device, traverse the tree for everything else */
+static int find_sysbus_device(Object *obj, void *opaque)
+{
+ SysBusFind *find = opaque;
+ Object *dev;
+ SysBusDevice *sbdev;
+
+ dev = object_dynamic_cast(obj, TYPE_SYS_BUS_DEVICE);
+ sbdev = (SysBusDevice *)dev;
+
+ if (!sbdev) {
+ /* Container, traverse it for children */
+ return object_child_foreach(obj, find_sysbus_device, opaque);
+ }
+
+ find->func(sbdev, find->opaque);
+
+ return 0;
+}
+
+/*
+ * Loop through all dynamically created sysbus devices and call
+ * func() for each instance.
+ */
+void foreach_dynamic_sysbus_device(FindSysbusDeviceFunc *func, void *opaque)
+{
+ Object *container;
+ SysBusFind find = {
+ .func = func,
+ .opaque = opaque,
+ };
+
+ /* Loop through all sysbus devices that were spawened outside the machine */
+ container = container_get(qdev_get_machine(), "/peripheral");
+ find_sysbus_device(container, &find);
+ container = container_get(qdev_get_machine(), "/peripheral-anon");
+ find_sysbus_device(container, &find);
+}
+
+
static void system_bus_class_init(ObjectClass *klass, void *data)
{
BusClass *k = BUS_CLASS(klass);
@@ -39,11 +84,38 @@ static const TypeInfo system_bus_info = {
.class_init = system_bus_class_init,
};
+/* Check whether an IRQ source exists */
+bool sysbus_has_irq(SysBusDevice *dev, int n)
+{
+ char *prop = g_strdup_printf("%s[%d]", SYSBUS_DEVICE_GPIO_IRQ, n);
+ ObjectProperty *r;
+
+ r = object_property_find(OBJECT(dev), prop, NULL);
+ return (r != NULL);
+}
+
+bool sysbus_is_irq_connected(SysBusDevice *dev, int n)
+{
+ return !!sysbus_get_connected_irq(dev, n);
+}
+
+qemu_irq sysbus_get_connected_irq(SysBusDevice *dev, int n)
+{
+ DeviceState *d = DEVICE(dev);
+ return qdev_get_gpio_out_connector(d, SYSBUS_DEVICE_GPIO_IRQ, n);
+}
+
void sysbus_connect_irq(SysBusDevice *dev, int n, qemu_irq irq)
{
qdev_connect_gpio_out_named(DEVICE(dev), SYSBUS_DEVICE_GPIO_IRQ, n, irq);
}
+/* Check whether an MMIO region exists */
+bool sysbus_has_mmio(SysBusDevice *dev, unsigned int n)
+{
+ return (n < dev->num_mmio);
+}
+
static void sysbus_mmio_map_common(SysBusDevice *dev, int n, hwaddr addr,
bool may_overlap, int priority)
{
@@ -238,13 +310,6 @@ static void sysbus_device_class_init(ObjectClass *klass, void *data)
DeviceClass *k = DEVICE_CLASS(klass);
k->init = sysbus_device_init;
k->bus_type = TYPE_SYSTEM_BUS;
- /*
- * device_add plugs devices into suitable bus. For "real" buses,
- * that actually connects the device. For sysbus, the connections
- * need to be made separately, and device_add can't do that. The
- * device would be left unconnected, and could not possibly work.
- */
- k->cannot_instantiate_with_device_add_yet = true;
}
static const TypeInfo sysbus_device_type_info = {
diff --git a/hw/gpio/Makefile.objs b/hw/gpio/Makefile.objs
index 2c8b51f09a..1abcf17988 100644
--- a/hw/gpio/Makefile.objs
+++ b/hw/gpio/Makefile.objs
@@ -2,5 +2,6 @@ common-obj-$(CONFIG_MAX7310) += max7310.o
common-obj-$(CONFIG_PL061) += pl061.o
common-obj-$(CONFIG_PUV3) += puv3_gpio.o
common-obj-$(CONFIG_ZAURUS) += zaurus.o
+common-obj-$(CONFIG_E500) += mpc8xxx.o
obj-$(CONFIG_OMAP) += omap_gpio.o
diff --git a/hw/gpio/mpc8xxx.c b/hw/gpio/mpc8xxx.c
new file mode 100644
index 0000000000..1aeaaaaf03
--- /dev/null
+++ b/hw/gpio/mpc8xxx.c
@@ -0,0 +1,217 @@
+/*
+ * GPIO Controller for a lot of Freescale SoCs
+ *
+ * Copyright (C) 2014 Freescale Semiconductor, Inc. All rights reserved.
+ *
+ * Author: Alexander Graf, <agraf@suse.de>
+ *
+ * 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/sysbus.h"
+
+#define TYPE_MPC8XXX_GPIO "mpc8xxx_gpio"
+#define MPC8XXX_GPIO(obj) OBJECT_CHECK(MPC8XXXGPIOState, (obj), TYPE_MPC8XXX_GPIO)
+
+typedef struct MPC8XXXGPIOState {
+ SysBusDevice parent_obj;
+
+ MemoryRegion iomem;
+ qemu_irq irq;
+ qemu_irq out[32];
+
+ uint32_t dir;
+ uint32_t odr;
+ uint32_t dat;
+ uint32_t ier;
+ uint32_t imr;
+ uint32_t icr;
+} MPC8XXXGPIOState;
+
+static const VMStateDescription vmstate_mpc8xxx_gpio = {
+ .name = "mpc8xxx_gpio",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(dir, MPC8XXXGPIOState),
+ VMSTATE_UINT32(odr, MPC8XXXGPIOState),
+ VMSTATE_UINT32(dat, MPC8XXXGPIOState),
+ VMSTATE_UINT32(ier, MPC8XXXGPIOState),
+ VMSTATE_UINT32(imr, MPC8XXXGPIOState),
+ VMSTATE_UINT32(icr, MPC8XXXGPIOState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void mpc8xxx_gpio_update(MPC8XXXGPIOState *s)
+{
+ qemu_set_irq(s->irq, !!(s->ier & s->imr));
+}
+
+static uint64_t mpc8xxx_gpio_read(void *opaque, hwaddr offset,
+ unsigned size)
+{
+ MPC8XXXGPIOState *s = (MPC8XXXGPIOState *)opaque;
+
+ if (size != 4) {
+ /* All registers are 32bit */
+ return 0;
+ }
+
+ switch (offset) {
+ case 0x0: /* Direction */
+ return s->dir;
+ case 0x4: /* Open Drain */
+ return s->odr;
+ case 0x8: /* Data */
+ return s->dat;
+ case 0xC: /* Interrupt Event */
+ return s->ier;
+ case 0x10: /* Interrupt Mask */
+ return s->imr;
+ case 0x14: /* Interrupt Control */
+ return s->icr;
+ default:
+ return 0;
+ }
+}
+
+static void mpc8xxx_write_data(MPC8XXXGPIOState *s, uint32_t new_data)
+{
+ uint32_t old_data = s->dat;
+ uint32_t diff = old_data ^ new_data;
+ int i;
+
+ for (i = 0; i < 32; i++) {
+ uint32_t mask = 0x80000000 >> i;
+ if (!(diff & mask)) {
+ continue;
+ }
+
+ if (s->dir & mask) {
+ /* Output */
+ qemu_set_irq(s->out[i], (new_data & mask) != 0);
+ }
+ }
+
+ s->dat = new_data;
+}
+
+static void mpc8xxx_gpio_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ MPC8XXXGPIOState *s = (MPC8XXXGPIOState *)opaque;
+
+ if (size != 4) {
+ /* All registers are 32bit */
+ return;
+ }
+
+ switch (offset) {
+ case 0x0: /* Direction */
+ s->dir = value;
+ break;
+ case 0x4: /* Open Drain */
+ s->odr = value;
+ break;
+ case 0x8: /* Data */
+ mpc8xxx_write_data(s, value);
+ break;
+ case 0xC: /* Interrupt Event */
+ s->ier &= ~value;
+ break;
+ case 0x10: /* Interrupt Mask */
+ s->imr = value;
+ break;
+ case 0x14: /* Interrupt Control */
+ s->icr = value;
+ break;
+ }
+
+ mpc8xxx_gpio_update(s);
+}
+
+static void mpc8xxx_gpio_reset(MPC8XXXGPIOState *s)
+{
+ s->dir = 0;
+ s->odr = 0;
+ s->dat = 0;
+ s->ier = 0;
+ s->imr = 0;
+ s->icr = 0;
+}
+
+static void mpc8xxx_gpio_set_irq(void * opaque, int irq, int level)
+{
+ MPC8XXXGPIOState *s = (MPC8XXXGPIOState *)opaque;
+ uint32_t mask;
+
+ mask = 0x80000000 >> irq;
+ if ((s->dir & mask) == 0) {
+ uint32_t old_value = s->dat & mask;
+
+ s->dat &= ~mask;
+ if (level)
+ s->dat |= mask;
+
+ if (!(s->icr & irq) || (old_value && !level)) {
+ s->ier |= mask;
+ }
+
+ mpc8xxx_gpio_update(s);
+ }
+}
+
+static const MemoryRegionOps mpc8xxx_gpio_ops = {
+ .read = mpc8xxx_gpio_read,
+ .write = mpc8xxx_gpio_write,
+ .endianness = DEVICE_BIG_ENDIAN,
+};
+
+static int mpc8xxx_gpio_initfn(SysBusDevice *sbd)
+{
+ DeviceState *dev = DEVICE(sbd);
+ MPC8XXXGPIOState *s = MPC8XXX_GPIO(dev);
+
+ memory_region_init_io(&s->iomem, OBJECT(s), &mpc8xxx_gpio_ops, s, "mpc8xxx_gpio", 0x1000);
+ sysbus_init_mmio(sbd, &s->iomem);
+ sysbus_init_irq(sbd, &s->irq);
+ qdev_init_gpio_in(dev, mpc8xxx_gpio_set_irq, 32);
+ qdev_init_gpio_out(dev, s->out, 32);
+ mpc8xxx_gpio_reset(s);
+ return 0;
+}
+
+static void mpc8xxx_gpio_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = mpc8xxx_gpio_initfn;
+ dc->vmsd = &vmstate_mpc8xxx_gpio;
+}
+
+static const TypeInfo mpc8xxx_gpio_info = {
+ .name = TYPE_MPC8XXX_GPIO,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(MPC8XXXGPIOState),
+ .class_init = mpc8xxx_gpio_class_init,
+};
+
+static void mpc8xxx_gpio_register_types(void)
+{
+ type_register_static(&mpc8xxx_gpio_info);
+}
+
+type_init(mpc8xxx_gpio_register_types)
diff --git a/hw/intc/openpic_kvm.c b/hw/intc/openpic_kvm.c
index e3bce043a3..3e2cd189ff 100644
--- a/hw/intc/openpic_kvm.c
+++ b/hw/intc/openpic_kvm.c
@@ -45,6 +45,7 @@ typedef struct KVMOpenPICState {
MemoryListener mem_listener;
uint32_t fd;
uint32_t model;
+ hwaddr mapped;
} KVMOpenPICState;
static void kvm_openpic_set_irq(void *opaque, int n_IRQ, int level)
@@ -128,7 +129,16 @@ static void kvm_openpic_region_add(MemoryListener *listener,
return;
}
+ if (opp->mapped) {
+ /*
+ * We can only map the MPIC once. Since we are already mapped,
+ * the best we can do is ignore new maps.
+ */
+ return;
+ }
+
reg_base = section->offset_within_address_space;
+ opp->mapped = reg_base;
attr.group = KVM_DEV_MPIC_GRP_MISC;
attr.attr = KVM_DEV_MPIC_BASE_ADDR;
@@ -155,6 +165,15 @@ static void kvm_openpic_region_del(MemoryListener *listener,
return;
}
+ if (section->offset_within_address_space != opp->mapped) {
+ /*
+ * We can only map the MPIC once. This mapping was a secondary
+ * one that we couldn't fulfill. Ignore it.
+ */
+ return;
+ }
+ opp->mapped = 0;
+
attr.group = KVM_DEV_MPIC_GRP_MISC;
attr.attr = KVM_DEV_MPIC_BASE_ADDR;
attr.addr = (uint64_t)(unsigned long)&reg_base;
diff --git a/hw/nvram/spapr_nvram.c b/hw/nvram/spapr_nvram.c
index 10b5b2e86b..35dc6d5684 100644
--- a/hw/nvram/spapr_nvram.c
+++ b/hw/nvram/spapr_nvram.c
@@ -52,7 +52,6 @@ static void rtas_nvram_fetch(PowerPCCPU *cpu, sPAPREnvironment *spapr,
{
sPAPRNVRAM *nvram = spapr->nvram;
hwaddr offset, buffer, len;
- int alen;
void *membuf;
if ((nargs != 3) || (nret != 2)) {
@@ -77,19 +76,14 @@ static void rtas_nvram_fetch(PowerPCCPU *cpu, sPAPREnvironment *spapr,
return;
}
- membuf = cpu_physical_memory_map(buffer, &len, 1);
- if (nvram->blk) {
- alen = blk_pread(nvram->blk, offset, membuf, len);
- } else {
- assert(nvram->buf);
+ assert(nvram->buf);
- memcpy(membuf, nvram->buf + offset, len);
- alen = len;
- }
+ membuf = cpu_physical_memory_map(buffer, &len, 1);
+ memcpy(membuf, nvram->buf + offset, len);
cpu_physical_memory_unmap(membuf, len, 1, len);
- rtas_st(rets, 0, (alen < len) ? RTAS_OUT_HW_ERROR : RTAS_OUT_SUCCESS);
- rtas_st(rets, 1, (alen < 0) ? 0 : alen);
+ rtas_st(rets, 0, RTAS_OUT_SUCCESS);
+ rtas_st(rets, 1, len);
}
static void rtas_nvram_store(PowerPCCPU *cpu, sPAPREnvironment *spapr,
@@ -123,14 +117,15 @@ static void rtas_nvram_store(PowerPCCPU *cpu, sPAPREnvironment *spapr,
}
membuf = cpu_physical_memory_map(buffer, &len, 0);
+
+ alen = len;
if (nvram->blk) {
alen = blk_pwrite(nvram->blk, offset, membuf, len);
- } else {
- assert(nvram->buf);
-
- memcpy(nvram->buf + offset, membuf, len);
- alen = len;
}
+
+ assert(nvram->buf);
+ memcpy(nvram->buf + offset, membuf, len);
+
cpu_physical_memory_unmap(membuf, len, 0, len);
rtas_st(rets, 0, (alen < len) ? RTAS_OUT_HW_ERROR : RTAS_OUT_SUCCESS);
@@ -145,15 +140,24 @@ static int spapr_nvram_init(VIOsPAPRDevice *dev)
nvram->size = blk_getlength(nvram->blk);
} else {
nvram->size = DEFAULT_NVRAM_SIZE;
- nvram->buf = g_malloc0(nvram->size);
}
+ nvram->buf = g_malloc0(nvram->size);
+
if ((nvram->size < MIN_NVRAM_SIZE) || (nvram->size > MAX_NVRAM_SIZE)) {
fprintf(stderr, "spapr-nvram must be between %d and %d bytes in size\n",
MIN_NVRAM_SIZE, MAX_NVRAM_SIZE);
return -1;
}
+ if (nvram->blk) {
+ int alen = blk_pread(nvram->blk, 0, nvram->buf, nvram->size);
+
+ if (alen != nvram->size) {
+ return -1;
+ }
+ }
+
spapr_rtas_register(RTAS_NVRAM_FETCH, "nvram-fetch", rtas_nvram_fetch);
spapr_rtas_register(RTAS_NVRAM_STORE, "nvram-store", rtas_nvram_store);
@@ -167,6 +171,48 @@ static int spapr_nvram_devnode(VIOsPAPRDevice *dev, void *fdt, int node_off)
return fdt_setprop_cell(fdt, node_off, "#bytes", nvram->size);
}
+static int spapr_nvram_pre_load(void *opaque)
+{
+ sPAPRNVRAM *nvram = VIO_SPAPR_NVRAM(opaque);
+
+ g_free(nvram->buf);
+ nvram->buf = NULL;
+ nvram->size = 0;
+
+ return 0;
+}
+
+static int spapr_nvram_post_load(void *opaque, int version_id)
+{
+ sPAPRNVRAM *nvram = VIO_SPAPR_NVRAM(opaque);
+
+ if (nvram->blk) {
+ int alen = blk_pwrite(nvram->blk, 0, nvram->buf, nvram->size);
+
+ if (alen < 0) {
+ return alen;
+ }
+ if (alen != nvram->size) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static const VMStateDescription vmstate_spapr_nvram = {
+ .name = "spapr_nvram",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .pre_load = spapr_nvram_pre_load,
+ .post_load = spapr_nvram_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(size, sPAPRNVRAM),
+ VMSTATE_VBUFFER_ALLOC_UINT32(buf, sPAPRNVRAM, 1, NULL, 0, size),
+ VMSTATE_END_OF_LIST()
+ },
+};
+
static Property spapr_nvram_properties[] = {
DEFINE_SPAPR_PROPERTIES(sPAPRNVRAM, sdev),
DEFINE_PROP_DRIVE("drive", sPAPRNVRAM, blk),
@@ -185,6 +231,7 @@ static void spapr_nvram_class_init(ObjectClass *klass, void *data)
k->dt_compatible = "qemu,spapr-nvram";
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
dc->props = spapr_nvram_properties;
+ dc->vmsd = &vmstate_spapr_nvram;
}
static const TypeInfo spapr_nvram_type_info = {
diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs
index edd44d03e7..19d99200ae 100644
--- a/hw/ppc/Makefile.objs
+++ b/hw/ppc/Makefile.objs
@@ -20,4 +20,4 @@ obj-$(CONFIG_MAC) += mac_newworld.o
obj-$(CONFIG_E500) += e500.o mpc8544ds.o e500plat.o
obj-$(CONFIG_E500) += mpc8544_guts.o ppce500_spin.o
# PowerPC 440 Xilinx ML507 reference board.
-obj-y += virtex_ml507.o
+obj-$(CONFIG_XILINX) += virtex_ml507.o
diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c
index 2157d87c19..2832fc0da4 100644
--- a/hw/ppc/e500.c
+++ b/hw/ppc/e500.c
@@ -36,6 +36,9 @@
#include "exec/address-spaces.h"
#include "qemu/host-utils.h"
#include "hw/pci-host/ppce500.h"
+#include "qemu/error-report.h"
+#include "hw/platform-bus.h"
+#include "hw/net/fsl_etsec/etsec.h"
#define EPAPR_MAGIC (0x45504150)
#define BINARY_DEVICE_TREE_FILE "mpc8544ds.dtb"
@@ -61,6 +64,8 @@
#define MPC8544_PCI_IO 0xE1000000ULL
#define MPC8544_UTIL_OFFSET 0xe0000ULL
#define MPC8544_SPIN_BASE 0xEF000000ULL
+#define MPC8XXX_GPIO_OFFSET 0x000FF000ULL
+#define MPC8XXX_GPIO_IRQ 43
struct boot_info
{
@@ -122,6 +127,142 @@ static void dt_serial_create(void *fdt, unsigned long long offset,
}
}
+static void create_dt_mpc8xxx_gpio(void *fdt, const char *soc, const char *mpic)
+{
+ hwaddr mmio0 = MPC8XXX_GPIO_OFFSET;
+ int irq0 = MPC8XXX_GPIO_IRQ;
+ gchar *node = g_strdup_printf("%s/gpio@%"PRIx64, soc, mmio0);
+ gchar *poweroff = g_strdup_printf("%s/power-off", soc);
+ int gpio_ph;
+
+ qemu_fdt_add_subnode(fdt, node);
+ qemu_fdt_setprop_string(fdt, node, "compatible", "fsl,qoriq-gpio");
+ qemu_fdt_setprop_cells(fdt, node, "reg", mmio0, 0x1000);
+ qemu_fdt_setprop_cells(fdt, node, "interrupts", irq0, 0x2);
+ qemu_fdt_setprop_phandle(fdt, node, "interrupt-parent", mpic);
+ qemu_fdt_setprop_cells(fdt, node, "#gpio-cells", 2);
+ qemu_fdt_setprop(fdt, node, "gpio-controller", NULL, 0);
+ gpio_ph = qemu_fdt_alloc_phandle(fdt);
+ qemu_fdt_setprop_cell(fdt, node, "phandle", gpio_ph);
+ qemu_fdt_setprop_cell(fdt, node, "linux,phandle", gpio_ph);
+
+ /* Power Off Pin */
+ qemu_fdt_add_subnode(fdt, poweroff);
+ qemu_fdt_setprop_string(fdt, poweroff, "compatible", "gpio-poweroff");
+ qemu_fdt_setprop_cells(fdt, poweroff, "gpios", gpio_ph, 0, 0);
+
+ g_free(node);
+ g_free(poweroff);
+}
+
+typedef struct PlatformDevtreeData {
+ void *fdt;
+ const char *mpic;
+ int irq_start;
+ const char *node;
+ PlatformBusDevice *pbus;
+} PlatformDevtreeData;
+
+static int create_devtree_etsec(SysBusDevice *sbdev, PlatformDevtreeData *data)
+{
+ eTSEC *etsec = ETSEC_COMMON(sbdev);
+ PlatformBusDevice *pbus = data->pbus;
+ hwaddr mmio0 = platform_bus_get_mmio_addr(pbus, sbdev, 0);
+ int irq0 = platform_bus_get_irqn(pbus, sbdev, 0);
+ int irq1 = platform_bus_get_irqn(pbus, sbdev, 1);
+ int irq2 = platform_bus_get_irqn(pbus, sbdev, 2);
+ gchar *node = g_strdup_printf("/platform/ethernet@%"PRIx64, mmio0);
+ gchar *group = g_strdup_printf("%s/queue-group", node);
+ void *fdt = data->fdt;
+
+ assert((int64_t)mmio0 >= 0);
+ assert(irq0 >= 0);
+ assert(irq1 >= 0);
+ assert(irq2 >= 0);
+
+ qemu_fdt_add_subnode(fdt, node);
+ qemu_fdt_setprop_string(fdt, node, "device_type", "network");
+ qemu_fdt_setprop_string(fdt, node, "compatible", "fsl,etsec2");
+ qemu_fdt_setprop_string(fdt, node, "model", "eTSEC");
+ qemu_fdt_setprop(fdt, node, "local-mac-address", etsec->conf.macaddr.a, 6);
+ qemu_fdt_setprop_cells(fdt, node, "fixed-link", 0, 1, 1000, 0, 0);
+
+ qemu_fdt_add_subnode(fdt, group);
+ qemu_fdt_setprop_cells(fdt, group, "reg", mmio0, 0x1000);
+ qemu_fdt_setprop_cells(fdt, group, "interrupts",
+ data->irq_start + irq0, 0x2,
+ data->irq_start + irq1, 0x2,
+ data->irq_start + irq2, 0x2);
+
+ g_free(node);
+ g_free(group);
+
+ return 0;
+}
+
+static int sysbus_device_create_devtree(SysBusDevice *sbdev, void *opaque)
+{
+ PlatformDevtreeData *data = opaque;
+ bool matched = false;
+
+ if (object_dynamic_cast(OBJECT(sbdev), TYPE_ETSEC_COMMON)) {
+ create_devtree_etsec(sbdev, data);
+ matched = true;
+ }
+
+ if (!matched) {
+ error_report("Device %s is not supported by this machine yet.",
+ qdev_fw_name(DEVICE(sbdev)));
+ exit(1);
+ }
+
+ return 0;
+}
+
+static void platform_bus_create_devtree(PPCE500Params *params, void *fdt,
+ const char *mpic)
+{
+ gchar *node = g_strdup_printf("/platform@%"PRIx64, params->platform_bus_base);
+ const char platcomp[] = "qemu,platform\0simple-bus";
+ uint64_t addr = params->platform_bus_base;
+ uint64_t size = params->platform_bus_size;
+ int irq_start = params->platform_bus_first_irq;
+ PlatformBusDevice *pbus;
+ DeviceState *dev;
+
+ /* Create a /platform node that we can put all devices into */
+
+ qemu_fdt_add_subnode(fdt, node);
+ qemu_fdt_setprop(fdt, node, "compatible", platcomp, sizeof(platcomp));
+
+ /* Our platform bus region is less than 32bit big, so 1 cell is enough for
+ address and size */
+ qemu_fdt_setprop_cells(fdt, node, "#size-cells", 1);
+ qemu_fdt_setprop_cells(fdt, node, "#address-cells", 1);
+ qemu_fdt_setprop_cells(fdt, node, "ranges", 0, addr >> 32, addr, size);
+
+ qemu_fdt_setprop_phandle(fdt, node, "interrupt-parent", mpic);
+
+ dev = qdev_find_recursive(sysbus_get_default(), TYPE_PLATFORM_BUS_DEVICE);
+ pbus = PLATFORM_BUS_DEVICE(dev);
+
+ /* We can only create dt nodes for dynamic devices when they're ready */
+ if (pbus->done_gathering) {
+ PlatformDevtreeData data = {
+ .fdt = fdt,
+ .mpic = mpic,
+ .irq_start = irq_start,
+ .node = node,
+ .pbus = pbus,
+ };
+
+ /* Loop through all dynamic sysbus devices and create nodes for them */
+ foreach_dynamic_sysbus_device(sysbus_device_create_devtree, &data);
+ }
+
+ g_free(node);
+}
+
static int ppce500_load_device_tree(MachineState *machine,
PPCE500Params *params,
hwaddr addr,
@@ -379,6 +520,14 @@ static int ppce500_load_device_tree(MachineState *machine,
qemu_fdt_setprop_cell(fdt, pci, "#address-cells", 3);
qemu_fdt_setprop_string(fdt, "/aliases", "pci0", pci);
+ if (params->has_mpc8xxx_gpio) {
+ create_dt_mpc8xxx_gpio(fdt, soc, mpic);
+ }
+
+ if (params->has_platform_bus) {
+ platform_bus_create_devtree(params, fdt, mpic);
+ }
+
params->fixup_devtree(params, fdt);
if (toplevel_compat) {
@@ -407,6 +556,7 @@ typedef struct DeviceTreeParams {
hwaddr initrd_size;
hwaddr kernel_base;
hwaddr kernel_size;
+ Notifier notifier;
} DeviceTreeParams;
static void ppce500_reset_device_tree(void *opaque)
@@ -417,6 +567,12 @@ static void ppce500_reset_device_tree(void *opaque)
false);
}
+static void ppce500_init_notify(Notifier *notifier, void *data)
+{
+ DeviceTreeParams *p = container_of(notifier, DeviceTreeParams, notifier);
+ ppce500_reset_device_tree(p);
+}
+
static int ppce500_prep_device_tree(MachineState *machine,
PPCE500Params *params,
hwaddr addr,
@@ -435,6 +591,8 @@ static int ppce500_prep_device_tree(MachineState *machine,
p->kernel_size = kernel_size;
qemu_register_reset(ppce500_reset_device_tree, p);
+ p->notifier.notify = ppce500_init_notify;
+ qemu_add_machine_init_done_notifier(&p->notifier);
/* Issue the device tree loader once, so that we get the size of the blob */
return ppce500_load_device_tree(machine, params, addr, initrd_base,
@@ -618,6 +776,13 @@ static qemu_irq *ppce500_init_mpic(PPCE500Params *params, MemoryRegion *ccsr,
return mpic;
}
+static void ppce500_power_off(void *opaque, int line, int on)
+{
+ if (on) {
+ qemu_system_shutdown_request();
+ }
+}
+
void ppce500_init(MachineState *machine, PPCE500Params *params)
{
MemoryRegion *address_space_mem = get_system_memory();
@@ -769,6 +934,40 @@ void ppce500_init(MachineState *machine, PPCE500Params *params)
cur_base = (32 * 1024 * 1024);
}
+ if (params->has_mpc8xxx_gpio) {
+ qemu_irq poweroff_irq;
+
+ dev = qdev_create(NULL, "mpc8xxx_gpio");
+ s = SYS_BUS_DEVICE(dev);
+ qdev_init_nofail(dev);
+ sysbus_connect_irq(s, 0, mpic[MPC8XXX_GPIO_IRQ]);
+ memory_region_add_subregion(ccsr_addr_space, MPC8XXX_GPIO_OFFSET,
+ sysbus_mmio_get_region(s, 0));
+
+ /* Power Off GPIO at Pin 0 */
+ poweroff_irq = qemu_allocate_irq(ppce500_power_off, NULL, 0);
+ qdev_connect_gpio_out(dev, 0, poweroff_irq);
+ }
+
+ /* Platform Bus Device */
+ if (params->has_platform_bus) {
+ dev = qdev_create(NULL, TYPE_PLATFORM_BUS_DEVICE);
+ dev->id = TYPE_PLATFORM_BUS_DEVICE;
+ qdev_prop_set_uint32(dev, "num_irqs", params->platform_bus_num_irqs);
+ qdev_prop_set_uint32(dev, "mmio_size", params->platform_bus_size);
+ qdev_init_nofail(dev);
+ s = SYS_BUS_DEVICE(dev);
+
+ for (i = 0; i < params->platform_bus_num_irqs; i++) {
+ int irqn = params->platform_bus_first_irq + i;
+ sysbus_connect_irq(s, i, mpic[irqn]);
+ }
+
+ memory_region_add_subregion(address_space_mem,
+ params->platform_bus_base,
+ sysbus_mmio_get_region(s, 0));
+ }
+
/* Load kernel. */
if (machine->kernel_filename) {
kernel_base = cur_base;
diff --git a/hw/ppc/e500.h b/hw/ppc/e500.h
index 08b25fab49..9f61ab2b1c 100644
--- a/hw/ppc/e500.h
+++ b/hw/ppc/e500.h
@@ -11,6 +11,12 @@ typedef struct PPCE500Params {
void (*fixup_devtree)(struct PPCE500Params *params, void *fdt);
int mpic_version;
+ bool has_mpc8xxx_gpio;
+ bool has_platform_bus;
+ hwaddr platform_bus_base;
+ hwaddr platform_bus_size;
+ int platform_bus_first_irq;
+ int platform_bus_num_irqs;
} PPCE500Params;
void ppce500_init(MachineState *machine, PPCE500Params *params);
diff --git a/hw/ppc/e500plat.c b/hw/ppc/e500plat.c
index 27df31ddb0..d50ae000ee 100644
--- a/hw/ppc/e500plat.c
+++ b/hw/ppc/e500plat.c
@@ -35,6 +35,12 @@ static void e500plat_init(MachineState *machine)
.pci_nr_slots = PCI_SLOT_MAX - 1,
.fixup_devtree = e500plat_fixup_devtree,
.mpic_version = OPENPIC_MODEL_FSL_MPIC_42,
+ .has_mpc8xxx_gpio = true,
+ .has_platform_bus = true,
+ .platform_bus_base = 0xf00000000ULL,
+ .platform_bus_size = (128ULL * 1024 * 1024),
+ .platform_bus_first_irq = 5,
+ .platform_bus_num_irqs = 10,
};
/* Older KVM versions don't support EPR which breaks guests when we announce
@@ -51,6 +57,7 @@ static QEMUMachine e500plat_machine = {
.desc = "generic paravirt e500 platform",
.init = e500plat_init,
.max_cpus = 32,
+ .has_dynamic_sysbus = true,
};
static void e500plat_machine_init(void)
diff --git a/hw/ppc/ppc4xx_pci.c b/hw/ppc/ppc4xx_pci.c
index 55a3cabee5..0bb3cdb46e 100644
--- a/hw/ppc/ppc4xx_pci.c
+++ b/hw/ppc/ppc4xx_pci.c
@@ -92,30 +92,6 @@ typedef struct PPC4xxPCIState PPC4xxPCIState;
#define PCI_ALL_SIZE (PCI_REG_BASE + PCI_REG_SIZE)
-static uint64_t pci4xx_cfgaddr_read(void *opaque, hwaddr addr,
- unsigned size)
-{
- PPC4xxPCIState *ppc4xx_pci = opaque;
- PCIHostState *phb = PCI_HOST_BRIDGE(ppc4xx_pci);
-
- return phb->config_reg;
-}
-
-static void pci4xx_cfgaddr_write(void *opaque, hwaddr addr,
- uint64_t value, unsigned size)
-{
- PPC4xxPCIState *ppc4xx_pci = opaque;
- PCIHostState *phb = PCI_HOST_BRIDGE(ppc4xx_pci);
-
- phb->config_reg = value & ~0x3;
-}
-
-static const MemoryRegionOps pci4xx_cfgaddr_ops = {
- .read = pci4xx_cfgaddr_read,
- .write = pci4xx_cfgaddr_write,
- .endianness = DEVICE_LITTLE_ENDIAN,
-};
-
static void ppc4xx_pci_reg_write4(void *opaque, hwaddr offset,
uint64_t value, unsigned size)
{
diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
index 0a2bfe6565..30de25de5c 100644
--- a/hw/ppc/spapr.c
+++ b/hw/ppc/spapr.c
@@ -850,11 +850,31 @@ static void spapr_reset_htab(sPAPREnvironment *spapr)
}
}
+static int find_unknown_sysbus_device(SysBusDevice *sbdev, void *opaque)
+{
+ bool matched = false;
+
+ if (object_dynamic_cast(OBJECT(sbdev), TYPE_SPAPR_PCI_HOST_BRIDGE)) {
+ matched = true;
+ }
+
+ if (!matched) {
+ error_report("Device %s is not supported by this machine yet.",
+ qdev_fw_name(DEVICE(sbdev)));
+ exit(1);
+ }
+
+ return 0;
+}
+
static void ppc_spapr_reset(void)
{
PowerPCCPU *first_ppc_cpu;
uint32_t rtas_limit;
+ /* Check for unknown sysbus devices */
+ foreach_dynamic_sysbus_device(find_unknown_sysbus_device, NULL);
+
/* Reset the hash table & recalc the RMA */
spapr_reset_htab(spapr);
@@ -1660,9 +1680,6 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data)
FWPathProviderClass *fwc = FW_PATH_PROVIDER_CLASS(oc);
NMIClass *nc = NMI_CLASS(oc);
- mc->name = "pseries";
- mc->desc = "pSeries Logical Partition (PAPR compliant)";
- mc->is_default = 1;
mc->init = ppc_spapr_init;
mc->reset = ppc_spapr_reset;
mc->block_default_type = IF_SCSI;
@@ -1670,6 +1687,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data)
mc->no_parallel = 1;
mc->default_boot_order = NULL;
mc->kvm_type = spapr_kvm_type;
+ mc->has_dynamic_sysbus = true;
fwc->get_dev_path = spapr_get_fw_dev_path;
nc->nmi_monitor_handler = spapr_nmi;
@@ -1678,6 +1696,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data)
static const TypeInfo spapr_machine_info = {
.name = TYPE_SPAPR_MACHINE,
.parent = TYPE_MACHINE,
+ .abstract = true,
.instance_size = sizeof(sPAPRMachineState),
.instance_init = spapr_machine_initfn,
.class_init = spapr_machine_class_init,
@@ -1698,7 +1717,6 @@ static void spapr_machine_2_1_class_init(ObjectClass *oc, void *data)
mc->name = "pseries-2.1";
mc->desc = "pSeries Logical Partition (PAPR compliant) v2.1";
- mc->is_default = 0;
mc->compat_props = compat_props;
}
@@ -1708,10 +1726,27 @@ static const TypeInfo spapr_machine_2_1_info = {
.class_init = spapr_machine_2_1_class_init,
};
+static void spapr_machine_2_2_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->name = "pseries-2.2";
+ mc->desc = "pSeries Logical Partition (PAPR compliant) v2.2";
+ mc->alias = "pseries";
+ mc->is_default = 1;
+}
+
+static const TypeInfo spapr_machine_2_2_info = {
+ .name = TYPE_SPAPR_MACHINE "2.2",
+ .parent = TYPE_SPAPR_MACHINE,
+ .class_init = spapr_machine_2_2_class_init,
+};
+
static void spapr_machine_register_types(void)
{
type_register_static(&spapr_machine_info);
type_register_static(&spapr_machine_2_1_info);
+ type_register_static(&spapr_machine_2_2_info);
}
type_init(spapr_machine_register_types)
diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c
index ad0da7fdc4..21b95b342c 100644
--- a/hw/ppc/spapr_pci.c
+++ b/hw/ppc/spapr_pci.c
@@ -704,28 +704,34 @@ static const VMStateDescription vmstate_spapr_pci_msi = {
},
};
+static void spapr_pci_fill_msi_devs(gpointer key, gpointer value,
+ gpointer opaque)
+{
+ sPAPRPHBState *sphb = opaque;
+
+ sphb->msi_devs[sphb->msi_devs_num].key = *(uint32_t *)key;
+ sphb->msi_devs[sphb->msi_devs_num].value = *(spapr_pci_msi *)value;
+ sphb->msi_devs_num++;
+}
+
static void spapr_pci_pre_save(void *opaque)
{
sPAPRPHBState *sphb = opaque;
- GHashTableIter iter;
- gpointer key, value;
- int i;
+ int msi_devs_num;
if (sphb->msi_devs) {
g_free(sphb->msi_devs);
sphb->msi_devs = NULL;
}
- sphb->msi_devs_num = g_hash_table_size(sphb->msi);
- if (!sphb->msi_devs_num) {
+ sphb->msi_devs_num = 0;
+ msi_devs_num = g_hash_table_size(sphb->msi);
+ if (!msi_devs_num) {
return;
}
- sphb->msi_devs = g_malloc(sphb->msi_devs_num * sizeof(spapr_pci_msi_mig));
+ sphb->msi_devs = g_malloc(msi_devs_num * sizeof(spapr_pci_msi_mig));
- g_hash_table_iter_init(&iter, sphb->msi);
- for (i = 0; g_hash_table_iter_next(&iter, &key, &value); ++i) {
- sphb->msi_devs[i].key = *(uint32_t *) key;
- sphb->msi_devs[i].value = *(spapr_pci_msi *) value;
- }
+ g_hash_table_foreach(sphb->msi, spapr_pci_fill_msi_devs, sphb);
+ assert(sphb->msi_devs_num == msi_devs_num);
}
static int spapr_pci_post_load(void *opaque, int version_id)