@@ -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(¬ifier->notifier);
+ }
+
/* Load kernel. */
if (machine->kernel_filename) {
kernel_base = cur_base;
@@ -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);
@@ -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)