diff options
-rw-r--r-- | hw/arm/vexpress.c | 99 |
1 files changed, 99 insertions, 0 deletions
diff --git a/hw/arm/vexpress.c b/hw/arm/vexpress.c index 0f15337c73..9586e3880e 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" @@ -71,6 +78,7 @@ enum { VE_ETHERNET, VE_USB, VE_DAPROM, + VE_VIRTIO, }; static hwaddr motherboard_legacy_map[] = { @@ -89,6 +97,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, @@ -135,6 +144,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, @@ -395,6 +405,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) { @@ -523,6 +612,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; @@ -533,6 +631,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); } |