#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)
-static struct arm_boot_info vexpress_binfo;
+/* 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,
VE_CLCD,
VE_NORFLASH0,
VE_NORFLASH1,
+ VE_NORFLASHALIAS,
VE_SRAM,
VE_VIDEORAM,
VE_ETHERNET,
VE_USB,
VE_DAPROM,
+ VE_VIRTIO,
};
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,
[VE_VIDEORAM] = 0x4c000000,
[VE_ETHERNET] = 0x4e000000,
[VE_USB] = 0x4f000000,
+ [VE_NORFLASHALIAS] = -1, /* not present */
};
static hwaddr motherboard_aseries_map[] = {
+ [VE_NORFLASHALIAS] = 0,
/* CS0: 0x08000000 .. 0x0c000000 */
[VE_NORFLASH0] = 0x08000000,
/* CS4: 0x0c000000 .. 0x10000000 */
[VE_WDT] = 0x1c0f0000,
[VE_TIMER01] = 0x1c110000,
[VE_TIMER23] = 0x1c120000,
+ [VE_VIRTIO] = 0x1c130000,
[VE_SERIALDVI] = 0x1c160000,
[VE_RTC] = 0x1c170000,
[VE_COMPACTFLASH] = 0x1c1a0000,
qemu_irq *pic);
struct VEDBoardInfo {
+ struct arm_boot_info bootinfo;
const hwaddr *motherboard_map;
hwaddr loader_start;
const hwaddr gic_cpu_if_addr;
MemoryRegion *lowram = g_new(MemoryRegion, 1);
DeviceState *dev;
SysBusDevice *busdev;
- qemu_irq *irqp;
int n;
qemu_irq cpu_irq[4];
ram_addr_t low_ram_size;
fprintf(stderr, "Unable to find CPU definition\n");
exit(1);
}
- irqp = arm_pic_init_cpu(cpu);
- cpu_irq[n] = irqp[ARM_PIC_CPU_IRQ];
+ cpu_irq[n] = qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_IRQ);
}
if (ram_size > 0x40000000) {
66670000, /* Test chip reference clock: 66.67MHz */
};
-static const VEDBoardInfo a9_daughterboard = {
+static VEDBoardInfo a9_daughterboard = {
.motherboard_map = motherboard_legacy_map,
.loader_start = 0x60000000,
.gic_cpu_if_addr = 0x1e000100,
for (n = 0; n < smp_cpus; n++) {
ARMCPU *cpu;
- qemu_irq *irqp;
cpu = cpu_arm_init(cpu_model);
if (!cpu) {
fprintf(stderr, "Unable to find CPU definition\n");
exit(1);
}
- irqp = arm_pic_init_cpu(cpu);
- cpu_irq[n] = irqp[ARM_PIC_CPU_IRQ];
+ cpu_irq[n] = qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_IRQ);
}
{
40000000, /* OSCCLK8: 40MHz : DDR2 PLL reference */
};
-static const VEDBoardInfo a15_daughterboard = {
+static VEDBoardInfo a15_daughterboard = {
.motherboard_map = motherboard_aseries_map,
.loader_start = 0x80000000,
.gic_cpu_if_addr = 0x2c002000,
.init = a15_daughterboard_init,
};
-static void vexpress_common_init(const VEDBoardInfo *daughterboard,
+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)
{
DeviceState *dev, *sysctl, *pl041;
qemu_irq pic[64];
uint32_t sys_id;
DriveInfo *dinfo;
+ pflash_t *pflash0;
ram_addr_t vram_size, sram_size;
MemoryRegion *sysmem = get_system_memory();
MemoryRegion *vram = g_new(MemoryRegion, 1);
MemoryRegion *sram = g_new(MemoryRegion, 1);
+ MemoryRegion *flashalias = g_new(MemoryRegion, 1);
+ MemoryRegion *flash0mem;
const hwaddr *map = daughterboard->motherboard_map;
int i;
sysbus_create_simple("pl111", map[VE_CLCD], pic[14]);
dinfo = drive_get_next(IF_PFLASH);
- if (!pflash_cfi01_register(map[VE_NORFLASH0], NULL, "vexpress.flash0",
+ pflash0 = pflash_cfi01_register(map[VE_NORFLASH0], NULL, "vexpress.flash0",
VEXPRESS_FLASH_SIZE, dinfo ? dinfo->bdrv : NULL,
VEXPRESS_FLASH_SECT_SIZE,
VEXPRESS_FLASH_SIZE / VEXPRESS_FLASH_SECT_SIZE, 4,
- 0x00, 0x89, 0x00, 0x18, 0)) {
+ 0x00, 0x89, 0x00, 0x18, 0);
+ if (!pflash0) {
fprintf(stderr, "vexpress: error registering flash 0.\n");
exit(1);
}
+ if (map[VE_NORFLASHALIAS] != -1) {
+ /* Map flash 0 as an alias into low memory */
+ flash0mem = sysbus_mmio_get_region(SYS_BUS_DEVICE(pflash0), 0);
+ memory_region_init_alias(flashalias, NULL, "vexpress.flashalias",
+ flash0mem, 0, VEXPRESS_FLASH_SIZE);
+ memory_region_add_subregion(sysmem, map[VE_NORFLASHALIAS], flashalias);
+ }
+
dinfo = drive_get_next(IF_PFLASH);
if (!pflash_cfi01_register(map[VE_NORFLASH1], NULL, "vexpress.flash1",
VEXPRESS_FLASH_SIZE, dinfo ? dinfo->bdrv : NULL,
/* VE_DAPROM: not modelled */
- vexpress_binfo.ram_size = args->ram_size;
- vexpress_binfo.kernel_filename = args->kernel_filename;
- vexpress_binfo.kernel_cmdline = args->kernel_cmdline;
- vexpress_binfo.initrd_filename = args->initrd_filename;
- vexpress_binfo.nb_cpus = smp_cpus;
- vexpress_binfo.board_id = VEXPRESS_BOARD_ID;
- vexpress_binfo.loader_start = daughterboard->loader_start;
- vexpress_binfo.smp_loader_start = map[VE_SRAM];
- vexpress_binfo.smp_bootreg_addr = map[VE_SYSREGS] + 0x30;
- vexpress_binfo.gic_cpu_if_addr = daughterboard->gic_cpu_if_addr;
- arm_load_kernel(ARM_CPU(first_cpu), &vexpress_binfo);
+ /* 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;
+ daughterboard->bootinfo.initrd_filename = args->initrd_filename;
+ daughterboard->bootinfo.nb_cpus = smp_cpus;
+ daughterboard->bootinfo.board_id = VEXPRESS_BOARD_ID;
+ daughterboard->bootinfo.loader_start = daughterboard->loader_start;
+ 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);
}
static void vexpress_a9_init(QEMUMachineInitArgs *args)
.init = vexpress_a9_init,
.block_default_type = IF_SCSI,
.max_cpus = 4,
- DEFAULT_MACHINE_OPTIONS,
};
static QEMUMachine vexpress_a15_machine = {
.init = vexpress_a15_init,
.block_default_type = IF_SCSI,
.max_cpus = 4,
- DEFAULT_MACHINE_OPTIONS,
};
static void vexpress_machine_init(void)