[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Qemu-devel] [PATCH 4/5] PPC: e500: Support platform devices
From: |
address@hidden |
Subject: |
Re: [Qemu-devel] [PATCH 4/5] PPC: e500: Support platform devices |
Date: |
Fri, 13 Jun 2014 08:58:17 +0000 |
> -----Original Message-----
> From: address@hidden [mailto:qemu-
> address@hidden On Behalf Of Alexander
> Graf
> Sent: Wednesday, June 04, 2014 5:59 PM
> To: address@hidden
> Cc: address@hidden; address@hidden
> Subject: [Qemu-devel] [PATCH 4/5] PPC: e500: Support platform devices
>
> For e500 our approach to supporting platform devices is to create a simple
> bus from the guest's point of view within which we map platform 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 <address@hidden>
> ---
> default-configs/ppc-softmmu.mak | 1 +
> default-configs/ppc64-softmmu.mak | 1 +
> hw/ppc/e500.c | 221
> ++++++++++++++++++++++++++++++++++++++
> hw/ppc/e500.h | 1 +
> hw/ppc/e500plat.c | 1 +
> 5 files changed, 225 insertions(+)
>
> diff --git a/default-configs/ppc-softmmu.mak b/default-configs/ppc-softmmu.mak
> index 33f8d84..d6ec8b9 100644
> --- a/default-configs/ppc-softmmu.mak
> +++ b/default-configs/ppc-softmmu.mak
> @@ -45,6 +45,7 @@ CONFIG_PREP=y
> CONFIG_MAC=y
> CONFIG_E500=y
> CONFIG_OPENPIC_KVM=$(and $(CONFIG_E500),$(CONFIG_KVM))
> +CONFIG_PLATFORM=y
> # For PReP
> CONFIG_MC146818RTC=y
> CONFIG_ETSEC=y
> diff --git a/default-configs/ppc64-softmmu.mak b/default-configs/ppc64-
> softmmu.mak
> index 37a15b7..06677bf 100644
> --- a/default-configs/ppc64-softmmu.mak
> +++ b/default-configs/ppc64-softmmu.mak
> @@ -45,6 +45,7 @@ CONFIG_PSERIES=y
> CONFIG_PREP=y
> CONFIG_MAC=y
> CONFIG_E500=y
> +CONFIG_PLATFORM=y
> CONFIG_OPENPIC_KVM=$(and $(CONFIG_E500),$(CONFIG_KVM))
> # For pSeries
> CONFIG_XICS=$(CONFIG_PSERIES)
> diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c
> index 33d54b3..bc26215 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 "hw/platform/device.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
How we come to value "5" ?
How it is ensured that this does not overlap with IRQ numbers taken by PCI
devices (with no-msi case)?
> +#define E500_PLATFORM_NUM_IRQS 10
> +
> /* TODO: parameterize */
> #define MPC8544_CCSRBAR_BASE 0xE0000000ULL
> #define MPC8544_CCSRBAR_SIZE 0x00100000ULL
> @@ -122,6 +131,62 @@ 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 platform_device_create_devtree(Object *obj, void *opaque)
> +{
> + PlatformDevtreeData *data = opaque;
> + Object *dev;
> + PlatformDeviceState *pdev;
> +
> + dev = object_dynamic_cast(obj, TYPE_PLATFORM_DEVICE);
> + pdev = (PlatformDeviceState *)dev;
> +
> + if (!pdev) {
> + /* Container, traverse it for children */
> + return object_child_foreach(obj, platform_device_create_devtree,
> data);
> + }
> +
> + 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;
> +
> + /* 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;
> +
> + platform_device_create_devtree(qdev_get_machine(), &data);
So I see dma device with two dm-channel then h/w device tree looks like :
address@hidden {
#address-cells = <0x1>;
#size-cells = <0x1>;
compatible = "fsl,elo3-dma";
reg = <0x100300 0x4 0x100600 0x4>;
ranges = <0x0 0x100100 0x500>;
address@hidden {
compatible = "fsl,eloplus-dma-channel";
reg = <0x0 0x80>;
interrupts = <0x1c 0x2 0x0 0x0>;
};
address@hidden {
compatible = "fsl,eloplus-dma-channel";
reg = <0x80 0x80>;
interrupts = <0x1d 0x2 0x0 0x0>;
};
}
Now for assigning same device to guest, We will unbind dma@ device from kernel
and then bind with vfio-platform.
1) What will be my qemu command line for this ? I think this will be like:
-device vfio-platform,vfio_device="address@hidden",compat="fsl,elo3-dma"
Then how similar device tree will be created? It can happen then some dma have
1 channel while other have more "n" channel.
That also reminds me that should we update documents for same "
docs/qdev-device-use.txt"
> +}
> +
> static int ppce500_load_device_tree(MachineState *machine,
> PPCE500Params *params,
> hwaddr addr,
> @@ -379,6 +444,12 @@ 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) {
> + platform_create_devtree(fdt, "/platform", E500_PLATFORM_BASE,
> + mpic, E500_PLATFORM_FIRST_IRQ,
> + E500_PLATFORM_NUM_IRQS);
> + }
> +
> params->fixup_devtree(params, fdt);
>
> if (toplevel_compat) {
> @@ -618,6 +689,147 @@ 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(unsigned long *used_irqs, qemu_irq
> *platform_irqs,
> + uint32_t *device_irqn, qemu_irq *device_irq)
> +{
> + int max_irqs = E500_PLATFORM_NUM_IRQS;
> + int irqn = *device_irqn;
> +
> + if (irqn == (uint32_t)PLATFORM_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);
> + }
> +
> + *device_irq = platform_irqs[irqn];
> + *device_irqn = irqn;
> +
> + return 0;
> +}
> +
> +static int platform_map_region(unsigned long *used_mem, MemoryRegion *pmem,
> + uint64_t *device_addr, MemoryRegion
> *device_mem)
> +{
> + 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 = *device_addr;
> + int page;
> + int i;
> +
> + page = addr >> E500_PLATFORM_PAGE_SHIFT;
> + if (addr == (uint64_t)PLATFORM_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);
> + *device_addr = addr;
> +
> + return 0;
> +}
> +
> +static int platform_device_check(Object *obj, void *opaque)
> +{
> + PlatformInitData *init = opaque;
> + Object *dev;
> + PlatformDeviceState *pdev;
> + int i;
> +
> + dev = object_dynamic_cast(obj, TYPE_PLATFORM_DEVICE);
> + pdev = (PlatformDeviceState *)dev;
> +
> + if (!pdev) {
> + /* Container, traverse it for children */
> + return object_child_foreach(obj, platform_device_check, opaque);
> + }
> +
> + /* Connect platform device to machine */
> + for (i = 0; i < pdev->num_irqs; i++) {
> + platform_map_irq(init->used_irqs, init->irqs, &pdev->plat_irqs[i],
> + pdev->irqs[i]);
> + }
> +
> + for (i = 0; i < pdev->num_regions; i++) {
> + platform_map_region(init->used_mem, init->mem,
> + &pdev->plat_region_addrs[i], pdev->regions[i]);
> + }
> +
> + return 0;
> +}
> +
> +static void platform_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);
> + 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 our internal device tree and attach any dangling device
> */
> + platform_device_check(qdev_get_machine(), &init);
Can you please describe what we are trying to do here, what could be a
"dangling device" ?
Thanks
-Bharat
> +
> + memory_region_add_subregion(address_space_mem, E500_PLATFORM_BASE,
> + platform_region);
> +}
> +
> +static void platform_devices_init_notify(Notifier *notifier, void *data)
> +{
> + PlatformNotifier *pn = (PlatformNotifier *)notifier;
> + platform_devices_init(pn->address_space_mem, pn->mpic);
> +}
> +
> void ppce500_init(MachineState *machine, PPCE500Params *params)
> {
> MemoryRegion *address_space_mem = get_system_memory();
> @@ -770,6 +982,15 @@ void ppce500_init(MachineState *machine, PPCE500Params
> *params)
> cur_base = (32 * 1024 * 1024);
> }
>
> + /* Platform Devices */
> + if (params->has_platform) {
> + PlatformNotifier *notifier = g_new(PlatformNotifier, 1);
> + notifier->notifier.notify = platform_devices_init_notify;
> + notifier->address_space_mem = address_space_mem;
> + notifier->mpic = mpic;
> + qemu_add_machine_init_done_notifier(¬ifier->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..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
>