qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Alexander Graf <agraf@suse.de>
To: qemu-ppc@nongnu.org
Cc: peter.maydell@linaro.org, peter.crosthwaite@xilinx.com,
	eric.auger@linaro.org, qemu-devel@nongnu.org,
	sean.stalley@intel.com, pbonzini@redhat.com, afaerber@suse.de
Subject: [Qemu-devel] [PATCH 5/6] PPC: e500: Support dynamically spawned sysbus devices
Date: Tue,  1 Jul 2014 23:49:37 +0200	[thread overview]
Message-ID: <1404251378-5242-6-git-send-email-agraf@suse.de> (raw)
In-Reply-To: <1404251378-5242-1-git-send-email-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>
---
 hw/ppc/e500.c     | 251 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/ppc/e500.h     |   1 +
 hw/ppc/e500plat.c |   1 +
 3 files changed, 253 insertions(+)

diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c
index bb2e75f..bf704b0 100644
--- a/hw/ppc/e500.c
+++ b/hw/ppc/e500.c
@@ -36,6 +36,7 @@
 #include "exec/address-spaces.h"
 #include "qemu/host-utils.h"
 #include "hw/pci-host/ppce500.h"
+#include "qemu/error-report.h"
 
 #define EPAPR_MAGIC                (0x45504150)
 #define BINARY_DEVICE_TREE_FILE    "mpc8544ds.dtb"
@@ -47,6 +48,14 @@
 
 #define RAM_SIZES_ALIGN            (64UL << 20)
 
+#define E500_PLATFORM_BASE         0xF0000000ULL
+#define E500_PLATFORM_HOLE         (128ULL * 1024 * 1024) /* 128 MB */
+#define E500_PLATFORM_PAGE_SHIFT   12
+#define E500_PLATFORM_HOLE_PAGES   (E500_PLATFORM_HOLE >> \
+                                    E500_PLATFORM_PAGE_SHIFT)
+#define E500_PLATFORM_FIRST_IRQ    5
+#define E500_PLATFORM_NUM_IRQS     10
+
 /* TODO: parameterize */
 #define MPC8544_CCSRBAR_BASE       0xE0000000ULL
 #define MPC8544_CCSRBAR_SIZE       0x00100000ULL
@@ -122,6 +131,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;
+    int id;
+} 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) {
+        data->id++;
+    } else {
+        error_report("Device %s is not supported by this machine yet.",
+                     qdev_fw_name(DEVICE(dev)));
+        exit(1);
+    }
+
+    return 0;
+}
+
+static void platform_create_devtree(void *fdt, const char *node, uint64_t addr,
+                                    const char *mpic, int irq_start,
+                                    int nr_irqs)
+{
+    const char platcomp[] = "qemu,platform\0simple-bus";
+    PlatformDevtreeData data;
+    Object *container;
+
+    /* 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));
+    qemu_fdt_setprop_string(fdt, node, "device_type", "platform");
+
+    /* Our platform hole 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,
+                           E500_PLATFORM_HOLE);
+
+    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;
+    data.id = 0;
+
+    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);
+}
+
 static int ppce500_load_device_tree(MachineState *machine,
                                     PPCE500Params *params,
                                     hwaddr addr,
@@ -379,6 +459,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_platform) {
+        gchar *node = g_strdup_printf("/platform@%llx", E500_PLATFORM_BASE);
+        platform_create_devtree(fdt, node, E500_PLATFORM_BASE, mpic,
+                                E500_PLATFORM_FIRST_IRQ,
+                                E500_PLATFORM_NUM_IRQS);
+        g_free(node);
+    }
+
     params->fixup_devtree(params, fdt);
 
     if (toplevel_compat) {
@@ -618,6 +706,155 @@ static qemu_irq *ppce500_init_mpic(PPCE500Params *params, MemoryRegion *ccsr,
     return mpic;
 }
 
+typedef struct PlatformNotifier {
+    Notifier notifier;
+    MemoryRegion *address_space_mem;
+    qemu_irq *mpic;
+} PlatformNotifier;
+
+typedef struct PlatformInitData {
+    unsigned long *used_irqs;
+    unsigned long *used_mem;
+    MemoryRegion *mem;
+    qemu_irq *irqs;
+    int device_count;
+} PlatformInitData;
+
+static int platform_map_irq(SysBusDevice *sbdev, int n, unsigned long *used_irqs,
+                            qemu_irq *platform_irqs)
+{
+    int max_irqs = E500_PLATFORM_NUM_IRQS;
+    int irqn = sbdev->user_irqs[n];
+
+    if (irqn == (uint32_t)SYSBUS_DYNAMIC) {
+        /* Find the first available IRQ */
+        irqn = find_first_zero_bit(used_irqs, max_irqs);
+    }
+
+    if ((irqn >= max_irqs) || test_and_set_bit(irqn, used_irqs)) {
+        hw_error("e500: IRQ %d is already allocated or no free IRQ left", irqn);
+    }
+
+    sysbus_connect_irq(sbdev, n, platform_irqs[irqn]);
+    sbdev->user_irqs[n] = irqn;
+
+    return 0;
+}
+
+static int platform_map_mmio(SysBusDevice *sbdev, int n, unsigned long *used_mem,
+                             MemoryRegion *pmem)
+{
+    MemoryRegion *device_mem = sbdev->mmio[n].memory;
+    uint64_t size = memory_region_size(device_mem);
+    uint64_t page_size = (1 << E500_PLATFORM_PAGE_SHIFT);
+    uint64_t page_mask = page_size - 1;
+    uint64_t size_pages = (size + page_mask) >> E500_PLATFORM_PAGE_SHIFT;
+    hwaddr addr = sbdev->user_mmios[n];
+    int page;
+    int i;
+
+    page = addr >> E500_PLATFORM_PAGE_SHIFT;
+    if (addr == (uint64_t)SYSBUS_DYNAMIC) {
+        uint64_t size_pages_align;
+
+        /* Align the region to at least its own size granularity */
+        if (is_power_of_2(size_pages)) {
+            size_pages_align = size_pages;
+        } else {
+            size_pages_align = pow2floor(size_pages) << 1;
+        }
+
+        /* Find the first available region that fits */
+        page = bitmap_find_next_zero_area(used_mem, E500_PLATFORM_HOLE_PAGES, 0,
+                                          size_pages, size_pages_align);
+
+        addr = (uint64_t)page << E500_PLATFORM_PAGE_SHIFT;
+    }
+
+    if (page >= E500_PLATFORM_HOLE_PAGES || test_bit(page, used_mem) ||
+        (find_next_bit(used_mem, E500_PLATFORM_HOLE_PAGES, page) < size_pages)) {
+        hw_error("e500: Memory [%"PRIx64":%"PRIx64" is already allocated or "
+                 "no slot left", addr, size);
+    }
+
+    for (i = page; i < (page + size_pages); i++) {
+        set_bit(i, used_mem);
+    }
+
+    memory_region_add_subregion(pmem, addr, device_mem);
+    sbdev->mmio[n].addr = addr;
+    sbdev->user_mmios[n] = addr;
+
+    return 0;
+}
+
+static int sysbus_device_check(Object *obj, void *opaque)
+{
+    PlatformInitData *init = opaque;
+    Object *dev;
+    SysBusDevice *sbdev;
+    int i;
+
+    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_check, opaque);
+    }
+
+    /* Connect sysbus device to virtual platform bus */
+    for (i = 0; i < sbdev->num_irq; i++) {
+        if (!sbdev->irqp[i]) {
+            /* This IRQ is an incoming IRQ, we can't wire those here */
+            continue;
+        }
+        platform_map_irq(sbdev, i, init->used_irqs, init->irqs);
+    }
+
+    for (i = 0; i < sbdev->num_mmio; i++) {
+        platform_map_mmio(sbdev, i, init->used_mem, init->mem);
+    }
+
+    return 0;
+}
+
+static void sysbus_devices_init(MemoryRegion *address_space_mem,
+                                  qemu_irq *mpic)
+{
+    DECLARE_BITMAP(used_irqs, E500_PLATFORM_NUM_IRQS);
+    DECLARE_BITMAP(used_mem, E500_PLATFORM_HOLE_PAGES);
+    MemoryRegion *platform_region = g_new(MemoryRegion, 1);
+    Object *container;
+    PlatformInitData init = {
+        .used_irqs = used_irqs,
+        .used_mem = used_mem,
+        .mem = platform_region,
+        .irqs = &mpic[E500_PLATFORM_FIRST_IRQ],
+    };
+
+    memory_region_init(platform_region, NULL, "platform devices",
+                       E500_PLATFORM_HOLE);
+
+    bitmap_clear(used_irqs, 0, E500_PLATFORM_NUM_IRQS);
+    bitmap_clear(used_mem, 0, E500_PLATFORM_HOLE_PAGES);
+
+    /* Loop through all sysbus devices that were spawened outside the machine */
+    container = container_get(qdev_get_machine(), "/peripheral");
+    sysbus_device_check(container, &init);
+    container = container_get(qdev_get_machine(), "/peripheral-anon");
+    sysbus_device_check(container, &init);
+
+    memory_region_add_subregion(address_space_mem, E500_PLATFORM_BASE,
+                                platform_region);
+}
+
+static void sysbus_devices_init_notify(Notifier *notifier, void *data)
+{
+    PlatformNotifier *pn = (PlatformNotifier *)notifier;
+    sysbus_devices_init(pn->address_space_mem, pn->mpic);
+}
+
 void ppce500_init(MachineState *machine, PPCE500Params *params)
 {
     MemoryRegion *address_space_mem = get_system_memory();
@@ -770,6 +1007,20 @@ void ppce500_init(MachineState *machine, PPCE500Params *params)
         cur_base = (32 * 1024 * 1024);
     }
 
+    /* Platform Devices */
+    if (params->has_platform) {
+        PlatformNotifier *notifier = g_new(PlatformNotifier, 1);
+        Object *obj = OBJECT(machine);
+
+        notifier->notifier.notify = sysbus_devices_init_notify;
+        notifier->address_space_mem = address_space_mem;
+        notifier->mpic = mpic;
+        qemu_add_machine_init_done_notifier(&notifier->notifier);
+
+        /* Promote the machine as dynamic sysbus capable */
+        object_property_add_bool(obj, "has-dynamic-sysbus", NULL, NULL, NULL);
+    }
+
     /* Load kernel. */
     if (machine->kernel_filename) {
         kernel_base = cur_base;
diff --git a/hw/ppc/e500.h b/hw/ppc/e500.h
index 08b25fa..3a588ed 100644
--- a/hw/ppc/e500.h
+++ b/hw/ppc/e500.h
@@ -11,6 +11,7 @@ typedef struct PPCE500Params {
     void (*fixup_devtree)(struct PPCE500Params *params, void *fdt);
 
     int mpic_version;
+    bool has_platform;
 } PPCE500Params;
 
 void ppce500_init(MachineState *machine, PPCE500Params *params);
diff --git a/hw/ppc/e500plat.c b/hw/ppc/e500plat.c
index 27df31d..bb84283 100644
--- a/hw/ppc/e500plat.c
+++ b/hw/ppc/e500plat.c
@@ -35,6 +35,7 @@ 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_platform = true,
     };
 
     /* Older KVM versions don't support EPR which breaks guests when we announce
-- 
1.8.1.4

  parent reply	other threads:[~2014-07-01 21:49 UTC|newest]

Thread overview: 33+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-07-01 21:49 [Qemu-devel] [PATCH 0/6] Dynamic sysbus device allocation support Alexander Graf
2014-07-01 21:49 ` [Qemu-devel] [PATCH 1/6] qom: macroify integer property helpers Alexander Graf
2014-07-02  3:29   ` Peter Crosthwaite
2014-07-02  7:39     ` Alexander Graf
2014-07-01 21:49 ` [Qemu-devel] [PATCH 2/6] qom: Allow to make integer qom properties writeable Alexander Graf
2014-07-02  3:48   ` Peter Crosthwaite
2014-07-02  7:46     ` Alexander Graf
2014-07-01 21:49 ` [Qemu-devel] [PATCH 3/6] sysbus: Add user map hints Alexander Graf
2014-07-02  4:12   ` Peter Crosthwaite
2014-07-02  8:24     ` Alexander Graf
2014-07-02  8:26       ` Paolo Bonzini
2014-07-02  9:03       ` Peter Crosthwaite
2014-07-02  9:07         ` Alexander Graf
2014-07-02  9:17           ` Paolo Bonzini
2014-07-02  9:19             ` Alexander Graf
2014-07-02  9:26               ` Paolo Bonzini
2014-07-01 21:49 ` [Qemu-devel] [PATCH 4/6] sysbus: Make devices spawnable via -device Alexander Graf
2014-07-02  6:32   ` Paolo Bonzini
2014-07-02 15:36     ` Alexander Graf
2014-07-01 21:49 ` Alexander Graf [this message]
2014-07-01 22:50   ` [Qemu-devel] [PATCH 5/6] PPC: e500: Support dynamically spawned sysbus devices Scott Wood
2014-07-02 17:12     ` Alexander Graf
2014-07-02 17:26       ` Scott Wood
2014-07-02 17:30         ` Alexander Graf
2014-07-02 17:52           ` Scott Wood
2014-07-02 17:59             ` Alexander Graf
2014-07-02 19:34               ` Scott Wood
2014-07-02 20:59                 ` Alexander Graf
2014-07-01 21:49 ` [Qemu-devel] [PATCH 6/6] e500: Add support for eTSEC in device tree Alexander Graf
2014-07-01 22:56   ` Scott Wood
2014-07-02 17:24     ` Alexander Graf
2014-07-02 17:32       ` Scott Wood
2014-07-02 17:34         ` Alexander Graf

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1404251378-5242-6-git-send-email-agraf@suse.de \
    --to=agraf@suse.de \
    --cc=afaerber@suse.de \
    --cc=eric.auger@linaro.org \
    --cc=pbonzini@redhat.com \
    --cc=peter.crosthwaite@xilinx.com \
    --cc=peter.maydell@linaro.org \
    --cc=qemu-devel@nongnu.org \
    --cc=qemu-ppc@nongnu.org \
    --cc=sean.stalley@intel.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).