diff options
Diffstat (limited to 'hw/arm/virt.c')
-rw-r--r-- | hw/arm/virt.c | 88 |
1 files changed, 77 insertions, 11 deletions
diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 7a03f84051..6c9e8985bf 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -167,7 +167,6 @@ static const char *valid_cpus[] = { "cortex-a53", "cortex-a57", "host", - NULL }; static bool cpuname_valid(const char *cpu) @@ -230,9 +229,19 @@ static void fdt_add_psci_node(const VirtMachineState *vms) uint32_t migrate_fn; void *fdt = vms->fdt; ARMCPU *armcpu = ARM_CPU(qemu_get_cpu(0)); + const char *psci_method; - if (!vms->using_psci) { + switch (vms->psci_conduit) { + case QEMU_PSCI_CONDUIT_DISABLED: return; + case QEMU_PSCI_CONDUIT_HVC: + psci_method = "hvc"; + break; + case QEMU_PSCI_CONDUIT_SMC: + psci_method = "smc"; + break; + default: + g_assert_not_reached(); } qemu_fdt_add_subnode(fdt, "/psci"); @@ -264,7 +273,7 @@ static void fdt_add_psci_node(const VirtMachineState *vms) * However, the device tree binding uses 'method' instead, so that is * what we should use here. */ - qemu_fdt_setprop_string(fdt, "/psci", "method", "hvc"); + qemu_fdt_setprop_string(fdt, "/psci", "method", psci_method); qemu_fdt_setprop_cell(fdt, "/psci", "cpu_suspend", cpu_suspend_fn); qemu_fdt_setprop_cell(fdt, "/psci", "cpu_off", cpu_off_fn); @@ -366,7 +375,8 @@ static void fdt_add_cpu_nodes(const VirtMachineState *vms) qemu_fdt_setprop_string(vms->fdt, nodename, "compatible", armcpu->dtb_compatible); - if (vms->using_psci && vms->smp_cpus > 1) { + if (vms->psci_conduit != QEMU_PSCI_CONDUIT_DISABLED + && vms->smp_cpus > 1) { qemu_fdt_setprop_string(vms->fdt, nodename, "enable-method", "psci"); } @@ -433,6 +443,11 @@ static void fdt_add_gic_node(VirtMachineState *vms) 2, vms->memmap[VIRT_GIC_DIST].size, 2, vms->memmap[VIRT_GIC_REDIST].base, 2, vms->memmap[VIRT_GIC_REDIST].size); + if (vms->virt) { + qemu_fdt_setprop_cells(vms->fdt, "/intc", "interrupts", + GIC_FDT_IRQ_TYPE_PPI, ARCH_GICV3_MAINT_IRQ, + GIC_FDT_IRQ_FLAGS_LEVEL_HI); + } } else { /* 'cortex-a15-gic' means 'GIC v2' */ qemu_fdt_setprop_string(vms->fdt, "/intc", "compatible", @@ -547,9 +562,9 @@ static void create_gic(VirtMachineState *vms, qemu_irq *pic) sysbus_mmio_map(gicbusdev, 1, vms->memmap[VIRT_GIC_CPU].base); } - /* Wire the outputs from each CPU's generic timer to the - * appropriate GIC PPI inputs, and the GIC's IRQ output to - * the CPU's IRQ input. + /* Wire the outputs from each CPU's generic timer and the GICv3 + * maintenance interrupt signal to the appropriate GIC PPI inputs, + * and the GIC's IRQ/FIQ/VIRQ/VFIQ interrupt outputs to the CPU's inputs. */ for (i = 0; i < smp_cpus; i++) { DeviceState *cpudev = DEVICE(qemu_get_cpu(i)); @@ -571,9 +586,17 @@ static void create_gic(VirtMachineState *vms, qemu_irq *pic) ppibase + timer_irq[irq])); } + qdev_connect_gpio_out_named(cpudev, "gicv3-maintenance-interrupt", 0, + qdev_get_gpio_in(gicdev, ppibase + + ARCH_GICV3_MAINT_IRQ)); + sysbus_connect_irq(gicbusdev, i, qdev_get_gpio_in(cpudev, ARM_CPU_IRQ)); sysbus_connect_irq(gicbusdev, i + smp_cpus, qdev_get_gpio_in(cpudev, ARM_CPU_FIQ)); + sysbus_connect_irq(gicbusdev, i + 2 * smp_cpus, + qdev_get_gpio_in(cpudev, ARM_CPU_VIRQ)); + sysbus_connect_irq(gicbusdev, i + 3 * smp_cpus, + qdev_get_gpio_in(cpudev, ARM_CPU_VFIQ)); } for (i = 0; i < NUM_IRQS; i++) { @@ -1221,9 +1244,18 @@ static void machvirt_init(MachineState *machine) * so it doesn't get in the way. Instead of starting secondary * CPUs in PSCI powerdown state we will start them all running and * let the boot ROM sort them out. - * The usual case is that we do use QEMU's PSCI implementation. + * The usual case is that we do use QEMU's PSCI implementation; + * if the guest has EL2 then we will use SMC as the conduit, + * and otherwise we will use HVC (for backwards compatibility and + * because if we're using KVM then we must use HVC). */ - vms->using_psci = !(vms->secure && firmware_loaded); + if (vms->secure && firmware_loaded) { + vms->psci_conduit = QEMU_PSCI_CONDUIT_DISABLED; + } else if (vms->virt) { + vms->psci_conduit = QEMU_PSCI_CONDUIT_SMC; + } else { + vms->psci_conduit = QEMU_PSCI_CONDUIT_HVC; + } /* The maximum number of CPUs depends on the GIC version, or on how * many redistributors we can fit into the memory map. @@ -1250,6 +1282,12 @@ static void machvirt_init(MachineState *machine) exit(1); } + if (vms->virt && kvm_enabled()) { + error_report("mach-virt: KVM does not support providing " + "Virtualization extensions to the guest CPU"); + exit(1); + } + if (vms->secure) { if (kvm_enabled()) { error_report("mach-virt: KVM does not support Security extensions"); @@ -1306,8 +1344,12 @@ static void machvirt_init(MachineState *machine) object_property_set_bool(cpuobj, false, "has_el3", NULL); } - if (vms->using_psci) { - object_property_set_int(cpuobj, QEMU_PSCI_CONDUIT_HVC, + if (!vms->virt && object_property_find(cpuobj, "has_el2", NULL)) { + object_property_set_bool(cpuobj, false, "has_el2", NULL); + } + + if (vms->psci_conduit != QEMU_PSCI_CONDUIT_DISABLED) { + object_property_set_int(cpuobj, vms->psci_conduit, "psci-conduit", NULL); /* Secondary CPUs start in PSCI powered-down state */ @@ -1408,6 +1450,20 @@ static void virt_set_secure(Object *obj, bool value, Error **errp) vms->secure = value; } +static bool virt_get_virt(Object *obj, Error **errp) +{ + VirtMachineState *vms = VIRT_MACHINE(obj); + + return vms->virt; +} + +static void virt_set_virt(Object *obj, bool value, Error **errp) +{ + VirtMachineState *vms = VIRT_MACHINE(obj); + + vms->virt = value; +} + static bool virt_get_highmem(Object *obj, Error **errp) { VirtMachineState *vms = VIRT_MACHINE(obj); @@ -1495,6 +1551,16 @@ static void virt_2_9_instance_init(Object *obj) "Security Extensions (TrustZone)", NULL); + /* EL2 is also disabled by default, for similar reasons */ + vms->virt = false; + object_property_add_bool(obj, "virtualization", virt_get_virt, + virt_set_virt, NULL); + object_property_set_description(obj, "virtualization", + "Set on/off to enable/disable emulating a " + "guest CPU which implements the ARM " + "Virtualization Extensions", + NULL); + /* High memory is enabled by default */ vms->highmem = true; object_property_add_bool(obj, "highmem", virt_get_highmem, |