diff mbox

[RFC,v2,3/7] PPC: e500: Support dynamically spawned sysbus devices

Message ID 1407510229-28167-4-git-send-email-eric.auger@linaro.org
State New
Headers show

Commit Message

Auger Eric Aug. 8, 2014, 3:03 p.m. UTC
From: Alexander Graf <agraf@suse.de>

For e500 our approach to supporting dynamically spawned sysbus devices is to
create a simple bus from the guest's point of view within which we map those
devices dynamically.

We allocate memory regions always within the "platform" hole in address
space and map IRQs to predetermined IRQ lines that are reserved for platform
device usage.

This maps really nicely into device tree logic, so we can just tell the
guest about our virtual simple bus in device tree as well.

Signed-off-by: Alexander Graf <agraf@suse.de>
Signed-off-by: Eric Auger <eric.auger@linaro.org>

---

v1 -> v2
[Eric Auger]
- use platform_bus_init_notify located in hw/misc/dyn_sysbus_binding.c
  and new DynSysbusParams that contains dynamic sysbus settings
- PPCE500Params includes a DynSysbusParams struct
- e500plat_init initializes this struct

v1
code written by Alex Graf
---
 hw/ppc/e500.c     | 92 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/ppc/e500.h     |  2 ++
 hw/ppc/e500plat.c |  9 ++++++
 3 files changed, 103 insertions(+)
diff mbox

Patch

diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c
index 1a5b30d..0ee8cbf 100644
--- a/hw/ppc/e500.c
+++ b/hw/ppc/e500.c
@@ -36,6 +36,8 @@ 
 #include "exec/address-spaces.h"
 #include "qemu/host-utils.h"
 #include "hw/pci-host/ppce500.h"
+#include "qemu/error-report.h"
+#include "hw/misc/dyn_sysbus_binding.h"
 
 #define EPAPR_MAGIC                (0x45504150)
 #define BINARY_DEVICE_TREE_FILE    "mpc8544ds.dtb"
@@ -47,6 +49,8 @@ 
 
 #define RAM_SIZES_ALIGN            (64UL << 20)
 
+#define E500_PLATFORM_BUS_PAGE_SHIFT 12
+
 /* TODO: parameterize */
 #define MPC8544_CCSRBAR_BASE       0xE0000000ULL
 #define MPC8544_CCSRBAR_SIZE       0x00100000ULL
@@ -122,6 +126,77 @@  static void dt_serial_create(void *fdt, unsigned long long offset,
     }
 }
 
+typedef struct PlatformDevtreeData {
+    void *fdt;
+    const char *mpic;
+    int irq_start;
+    const char *node;
+} PlatformDevtreeData;
+
+static int sysbus_device_create_devtree(Object *obj, void *opaque)
+{
+    PlatformDevtreeData *data = opaque;
+    Object *dev;
+    SysBusDevice *sbdev;
+    bool matched = false;
+
+    dev = object_dynamic_cast(obj, TYPE_SYS_BUS_DEVICE);
+    sbdev = (SysBusDevice *)dev;
+
+    if (!sbdev) {
+        /* Container, traverse it for children */
+        return object_child_foreach(obj, sysbus_device_create_devtree, data);
+    }
+
+    if (!matched) {
+        error_report("Device %s is not supported by this machine yet.",
+                     qdev_fw_name(DEVICE(dev)));
+        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->dyn_sysbus_params.platform_bus_base);
+    const char platcomp[] = "qemu,platform\0simple-bus";
+    PlatformDevtreeData data;
+    Object *container;
+    uint64_t addr = params->dyn_sysbus_params.platform_bus_base;
+    uint64_t size = params->dyn_sysbus_params.platform_bus_size;
+    int irq_start = params->dyn_sysbus_params.platform_bus_first_irq;
+
+    /* 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);
+
+    /* Loop through all devices and create nodes for known ones */
+
+    data.fdt = fdt;
+    data.mpic = mpic;
+    data.irq_start = irq_start;
+    data.node = node;
+
+    container = container_get(qdev_get_machine(), "/peripheral");
+    sysbus_device_create_devtree(container, &data);
+    container = container_get(qdev_get_machine(), "/peripheral-anon");
+    sysbus_device_create_devtree(container, &data);
+
+    g_free(node);
+}
+
 static int ppce500_load_device_tree(MachineState *machine,
                                     PPCE500Params *params,
                                     hwaddr addr,
@@ -379,6 +454,10 @@  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->dyn_sysbus_params.has_platform_bus) {
+        platform_bus_create_devtree(params, fdt, mpic);
+    }
+
     params->fixup_devtree(params, fdt);
 
     if (toplevel_compat) {
@@ -769,6 +848,19 @@  void ppce500_init(MachineState *machine, PPCE500Params *params)
         cur_base = (32 * 1024 * 1024);
     }
 
+    /* Platform Devices */
+    if (params->dyn_sysbus_params.has_platform_bus) {
+        DynSysbusNotifier *notifier = g_new(DynSysbusNotifier, 1);
+        params->dyn_sysbus_params.page_shift =
+            E500_PLATFORM_BUS_PAGE_SHIFT;
+
+        notifier->notifier.notify = platform_bus_init_notify;
+        notifier->address_space_mem = address_space_mem;
+        notifier->mpic = mpic;
+        notifier->params = params->dyn_sysbus_params;
+        qemu_add_machine_init_done_notifier(&notifier->notifier);
+    }
+
     /* Load kernel. */
     if (machine->kernel_filename) {
         kernel_base = cur_base;
diff --git a/hw/ppc/e500.h b/hw/ppc/e500.h
index 08b25fa..c191f9d 100644
--- a/hw/ppc/e500.h
+++ b/hw/ppc/e500.h
@@ -2,6 +2,7 @@ 
 #define PPCE500_H
 
 #include "hw/boards.h"
+#include "hw/misc/dyn_sysbus_binding.h"
 
 typedef struct PPCE500Params {
     int pci_first_slot;
@@ -11,6 +12,7 @@  typedef struct PPCE500Params {
     void (*fixup_devtree)(struct PPCE500Params *params, void *fdt);
 
     int mpic_version;
+    DynSysbusParams dyn_sysbus_params;
 } PPCE500Params;
 
 void ppce500_init(MachineState *machine, PPCE500Params *params);
diff --git a/hw/ppc/e500plat.c b/hw/ppc/e500plat.c
index 27df31d..4150a2c 100644
--- a/hw/ppc/e500plat.c
+++ b/hw/ppc/e500plat.c
@@ -35,6 +35,14 @@  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,
+        .dyn_sysbus_params = {
+            .has_platform_bus = true,
+            .platform_bus_base = 0xf00000000ULL,
+            .platform_bus_size = (128ULL * 1024 * 1024),
+            .platform_bus_first_irq = 5,
+            .platform_bus_num_irqs = 10,
+            .page_shift = 12 /* default */
+        }
     };
 
     /* Older KVM versions don't support EPR which breaks guests when we announce
@@ -51,6 +59,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)