Patchwork [v2,8/8] vexpress: Add virtio-mmio transports

login
register
mail settings
Submitter Peter Maydell
Date July 12, 2013, 8:37 p.m.
Message ID <1373661422-23606-9-git-send-email-peter.maydell@linaro.org>
Download mbox | patch
Permalink /patch/258802/
State New
Headers show

Comments

Peter Maydell - July 12, 2013, 8:37 p.m.
Add some virtio-mmio transports to the vexpress board model,
together with a modify_dtb hook which adds them to the device
tree so that the kernel will probe for them. We put them
in a reserved area of the address map.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 hw/arm/vexpress.c |   99 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 99 insertions(+)
Peter Crosthwaite - July 15, 2013, 12:17 a.m.
Hi,

On Sat, Jul 13, 2013 at 6:37 AM, Peter Maydell <peter.maydell@linaro.org> wrote:
> Add some virtio-mmio transports to the vexpress board model,
> together with a modify_dtb hook which adds them to the device
> tree so that the kernel will probe for them. We put them
> in a reserved area of the address map.
>
> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
> ---
>  hw/arm/vexpress.c |   99 +++++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 99 insertions(+)
>
> diff --git a/hw/arm/vexpress.c b/hw/arm/vexpress.c
> index d1c28aa..dcc86b9 100644
> --- a/hw/arm/vexpress.c
> +++ b/hw/arm/vexpress.c
> @@ -31,11 +31,18 @@
>  #include "exec/address-spaces.h"
>  #include "sysemu/blockdev.h"
>  #include "hw/block/flash.h"
> +#include "sysemu/device_tree.h"
> +#include <libfdt.h>
>
>  #define VEXPRESS_BOARD_ID 0x8e0
>  #define VEXPRESS_FLASH_SIZE (64 * 1024 * 1024)
>  #define VEXPRESS_FLASH_SECT_SIZE (256 * 1024)
>
> +/* Number of virtio transports to create (0..8; limited by
> + * number of available IRQ lines).
> + */
> +#define NUM_VIRTIO_TRANSPORTS 4
> +
>  /* Address maps for peripherals:
>   * the Versatile Express motherboard has two possible maps,
>   * the "legacy" one (used for A9) and the "Cortex-A Series"
> @@ -70,6 +77,7 @@ enum {
>      VE_ETHERNET,
>      VE_USB,
>      VE_DAPROM,
> +    VE_VIRTIO,
>  };
>
>  static hwaddr motherboard_legacy_map[] = {
> @@ -88,6 +96,7 @@ static hwaddr motherboard_legacy_map[] = {
>      [VE_WDT] = 0x1000f000,
>      [VE_TIMER01] = 0x10011000,
>      [VE_TIMER23] = 0x10012000,
> +    [VE_VIRTIO] = 0x10013000,
>      [VE_SERIALDVI] = 0x10016000,
>      [VE_RTC] = 0x10017000,
>      [VE_COMPACTFLASH] = 0x1001a000,
> @@ -132,6 +141,7 @@ static hwaddr motherboard_aseries_map[] = {
>      [VE_WDT] = 0x1c0f0000,
>      [VE_TIMER01] = 0x1c110000,
>      [VE_TIMER23] = 0x1c120000,
> +    [VE_VIRTIO] = 0x1c130000,
>      [VE_SERIALDVI] = 0x1c160000,
>      [VE_RTC] = 0x1c170000,
>      [VE_COMPACTFLASH] = 0x1c1a0000,
> @@ -392,6 +402,85 @@ static VEDBoardInfo a15_daughterboard = {
>      .init = a15_daughterboard_init,
>  };
>
> +static int add_virtio_mmio_node(void *fdt, uint32_t acells, uint32_t scells,
> +                                hwaddr addr, hwaddr size, uint32_t intc,
> +                                int irq)

This seems pretty general to me and maybe belongs in the device tree API. Some
of the PPC machines could make good use of this. From e500:

snprintf(mpic, sizeof(mpic), "%s/pic@%llx", soc, MPC8544_MPIC_REGS_OFFSET);
    qemu_devtree_add_subnode(fdt, mpic);
    qemu_devtree_setprop_string(fdt, mpic, "device_type", "open-pic");
    qemu_devtree_setprop_string(fdt, mpic, "compatible", "fsl,mpic");
    qemu_devtree_setprop_cells(fdt, mpic, "reg", MPC8544_MPIC_REGS_OFFSET,
                               0x40000);
    qemu_devtree_setprop_cell(fdt, mpic, "#address-cells", 0);
    qemu_devtree_setprop_cell(fdt, mpic, "#interrupt-cells", 2);

The nasty intc specific stuff is a problem, but perhaps at least the
boiler plate
creation+reg+compatibile can be factored out for all to use?

Regards,
Peter
Peter Maydell - July 15, 2013, 9:40 a.m.
On 15 July 2013 01:17, Peter Crosthwaite <peter.crosthwaite@xilinx.com> wrote:
> On Sat, Jul 13, 2013 at 6:37 AM, Peter Maydell <peter.maydell@linaro.org> wrote:
>> +static int add_virtio_mmio_node(void *fdt, uint32_t acells, uint32_t scells,
>> +                                hwaddr addr, hwaddr size, uint32_t intc,
>> +                                int irq)
>
> This seems pretty general to me and maybe belongs in the device tree API. Some
> of the PPC machines could make good use of this. From e500:
>
> snprintf(mpic, sizeof(mpic), "%s/pic@%llx", soc, MPC8544_MPIC_REGS_OFFSET);
>     qemu_devtree_add_subnode(fdt, mpic);
>     qemu_devtree_setprop_string(fdt, mpic, "device_type", "open-pic");
>     qemu_devtree_setprop_string(fdt, mpic, "compatible", "fsl,mpic");
>     qemu_devtree_setprop_cells(fdt, mpic, "reg", MPC8544_MPIC_REGS_OFFSET,
>                                0x40000);
>     qemu_devtree_setprop_cell(fdt, mpic, "#address-cells", 0);
>     qemu_devtree_setprop_cell(fdt, mpic, "#interrupt-cells", 2);
>
> The nasty intc specific stuff is a problem, but perhaps at least the
> boiler plate
> creation+reg+compatibile can be factored out for all to use?

Doesn't really seem worth doing to me -- all we're doing is
setting some string properties, and the reg property is tricky
because it's only the right thing in some cases (ie where you
know you're not putting the node inside some other container
that messes with ranges). The setprop_sized_cells is the part
I felt really merited being factored out.

thanks
-- PMM

Patch

diff --git a/hw/arm/vexpress.c b/hw/arm/vexpress.c
index d1c28aa..dcc86b9 100644
--- a/hw/arm/vexpress.c
+++ b/hw/arm/vexpress.c
@@ -31,11 +31,18 @@ 
 #include "exec/address-spaces.h"
 #include "sysemu/blockdev.h"
 #include "hw/block/flash.h"
+#include "sysemu/device_tree.h"
+#include <libfdt.h>
 
 #define VEXPRESS_BOARD_ID 0x8e0
 #define VEXPRESS_FLASH_SIZE (64 * 1024 * 1024)
 #define VEXPRESS_FLASH_SECT_SIZE (256 * 1024)
 
+/* Number of virtio transports to create (0..8; limited by
+ * number of available IRQ lines).
+ */
+#define NUM_VIRTIO_TRANSPORTS 4
+
 /* Address maps for peripherals:
  * the Versatile Express motherboard has two possible maps,
  * the "legacy" one (used for A9) and the "Cortex-A Series"
@@ -70,6 +77,7 @@  enum {
     VE_ETHERNET,
     VE_USB,
     VE_DAPROM,
+    VE_VIRTIO,
 };
 
 static hwaddr motherboard_legacy_map[] = {
@@ -88,6 +96,7 @@  static hwaddr motherboard_legacy_map[] = {
     [VE_WDT] = 0x1000f000,
     [VE_TIMER01] = 0x10011000,
     [VE_TIMER23] = 0x10012000,
+    [VE_VIRTIO] = 0x10013000,
     [VE_SERIALDVI] = 0x10016000,
     [VE_RTC] = 0x10017000,
     [VE_COMPACTFLASH] = 0x1001a000,
@@ -132,6 +141,7 @@  static hwaddr motherboard_aseries_map[] = {
     [VE_WDT] = 0x1c0f0000,
     [VE_TIMER01] = 0x1c110000,
     [VE_TIMER23] = 0x1c120000,
+    [VE_VIRTIO] = 0x1c130000,
     [VE_SERIALDVI] = 0x1c160000,
     [VE_RTC] = 0x1c170000,
     [VE_COMPACTFLASH] = 0x1c1a0000,
@@ -392,6 +402,85 @@  static VEDBoardInfo a15_daughterboard = {
     .init = a15_daughterboard_init,
 };
 
+static int add_virtio_mmio_node(void *fdt, uint32_t acells, uint32_t scells,
+                                hwaddr addr, hwaddr size, uint32_t intc,
+                                int irq)
+{
+    /* Add a virtio_mmio node to the device tree blob:
+     *   virtio_mmio@ADDRESS {
+     *       compatible = "virtio,mmio";
+     *       reg = <ADDRESS, SIZE>;
+     *       interrupt-parent = <&intc>;
+     *       interrupts = <0, irq, 1>;
+     *   }
+     * (Note that the format of the interrupts property is dependent on the
+     * interrupt controller that interrupt-parent points to; these are for
+     * the ARM GIC and indicate an SPI interrupt, rising-edge-triggered.)
+     */
+    int rc;
+    char *nodename = g_strdup_printf("/virtio_mmio@%" PRIx64, addr);
+
+    rc = qemu_devtree_add_subnode(fdt, nodename);
+    rc |= qemu_devtree_setprop_string(fdt, nodename,
+                                      "compatible", "virtio,mmio");
+    rc |= qemu_devtree_setprop_sized_cells(fdt, nodename, "reg",
+                                           acells, addr, scells, size);
+    qemu_devtree_setprop_cells(fdt, nodename, "interrupt-parent", intc);
+    qemu_devtree_setprop_cells(fdt, nodename, "interrupts", 0, irq, 1);
+    g_free(nodename);
+    if (rc) {
+        return -1;
+    }
+    return 0;
+}
+
+static uint32_t find_int_controller(void *fdt)
+{
+    /* Find the FDT node corresponding to the interrupt controller
+     * for virtio-mmio devices. We do this by scanning the fdt for
+     * a node with the right compatibility, since we know there is
+     * only one GIC on a vexpress board.
+     * We return the phandle of the node, or 0 if none was found.
+     */
+    const char *compat = "arm,cortex-a9-gic";
+    int offset;
+
+    offset = fdt_node_offset_by_compatible(fdt, -1, compat);
+    if (offset >= 0) {
+        return fdt_get_phandle(fdt, offset);
+    }
+    return 0;
+}
+
+static void vexpress_modify_dtb(const struct arm_boot_info *info, void *fdt)
+{
+    uint32_t acells, scells, intc;
+    const VEDBoardInfo *daughterboard = (const VEDBoardInfo *)info;
+
+    acells = qemu_devtree_getprop_cell(fdt, "/", "#address-cells");
+    scells = qemu_devtree_getprop_cell(fdt, "/", "#size-cells");
+    intc = find_int_controller(fdt);
+    if (!intc) {
+        /* Not fatal, we just won't provide virtio. This will
+         * happen with older device tree blobs.
+         */
+        fprintf(stderr, "QEMU: warning: couldn't find interrupt controller in "
+                "dtb; will not include virtio-mmio devices in the dtb.\n");
+    } else {
+        int i;
+        const hwaddr *map = daughterboard->motherboard_map;
+
+        /* We iterate backwards here because adding nodes
+         * to the dtb puts them in last-first.
+         */
+        for (i = NUM_VIRTIO_TRANSPORTS - 1; i >= 0; i--) {
+            add_virtio_mmio_node(fdt, acells, scells,
+                                 map[VE_VIRTIO] + 0x200 * i,
+                                 0x200, intc, 40 + i);
+        }
+    }
+}
+
 static void vexpress_common_init(VEDBoardInfo *daughterboard,
                                  QEMUMachineInitArgs *args)
 {
@@ -508,6 +597,15 @@  static void vexpress_common_init(VEDBoardInfo *daughterboard,
 
     /* VE_DAPROM: not modelled */
 
+    /* Create mmio transports, so the user can create virtio backends
+     * (which will be automatically plugged in to the transports). If
+     * no backend is created the transport will just sit harmlessly idle.
+     */
+    for (i = 0; i < NUM_VIRTIO_TRANSPORTS; i++) {
+        sysbus_create_simple("virtio-mmio", map[VE_VIRTIO] + 0x200 * i,
+                             pic[40 + i]);
+    }
+
     daughterboard->bootinfo.ram_size = args->ram_size;
     daughterboard->bootinfo.kernel_filename = args->kernel_filename;
     daughterboard->bootinfo.kernel_cmdline = args->kernel_cmdline;
@@ -518,6 +616,7 @@  static void vexpress_common_init(VEDBoardInfo *daughterboard,
     daughterboard->bootinfo.smp_loader_start = map[VE_SRAM];
     daughterboard->bootinfo.smp_bootreg_addr = map[VE_SYSREGS] + 0x30;
     daughterboard->bootinfo.gic_cpu_if_addr = daughterboard->gic_cpu_if_addr;
+    daughterboard->bootinfo.modify_dtb = vexpress_modify_dtb;
     arm_load_kernel(ARM_CPU(first_cpu), &daughterboard->bootinfo);
 }