222 lines
9.0 KiB
Diff
222 lines
9.0 KiB
Diff
|
|
From a68abeefcbd78daaf7179b922f6b9040b4b63101 Mon Sep 17 00:00:00 2001
|
||
|
|
From: Salil Mehta <salil.mehta@huawei.com>
|
||
|
|
Date: Sat, 9 May 2020 15:50:33 +0100
|
||
|
|
Subject: [PATCH] arm/virt: Changes to (un)wire GICC<->vCPU IRQs during
|
||
|
|
hot-(un)plug
|
||
|
|
|
||
|
|
Refactors the existing GIC create code to extract common code to wire the
|
||
|
|
vcpu<->gic interrupts. This function could be used with cold-plug case and also
|
||
|
|
used when vCPU is hot-plugged. It also introduces a new function to unwire the
|
||
|
|
vcpu<->gic interrupts for the vCPU hot-unplug cases.
|
||
|
|
|
||
|
|
Co-developed-by: Salil Mehta <salil.mehta@huawei.com>
|
||
|
|
Signed-off-by: Salil Mehta <salil.mehta@huawei.com>
|
||
|
|
Co-developed-by: Keqian Zhu <zhukeqian1@huawei.com>
|
||
|
|
Signed-off-by: Keqian Zhu <zhukeqian1@huawei.com>
|
||
|
|
Signed-off-by: Salil Mehta <salil.mehta@huawei.com>
|
||
|
|
---
|
||
|
|
hw/arm/virt.c | 138 ++++++++++++++++++++++++++++-------------
|
||
|
|
hw/core/gpio.c | 2 +-
|
||
|
|
include/hw/qdev-core.h | 2 +
|
||
|
|
3 files changed, 99 insertions(+), 43 deletions(-)
|
||
|
|
|
||
|
|
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
|
||
|
|
index ed354be326..97bf4cca11 100644
|
||
|
|
--- a/hw/arm/virt.c
|
||
|
|
+++ b/hw/arm/virt.c
|
||
|
|
@@ -798,6 +798,99 @@ static void create_v2m(VirtMachineState *vms)
|
||
|
|
vms->msi_controller = VIRT_MSI_CTRL_GICV2M;
|
||
|
|
}
|
||
|
|
|
||
|
|
+/*
|
||
|
|
+ * Mapping from the output timer irq lines from the CPU to the GIC PPI inputs
|
||
|
|
+ * we use for the virt board.
|
||
|
|
+ */
|
||
|
|
+const int timer_irq[] = {
|
||
|
|
+ [GTIMER_PHYS] = ARCH_TIMER_NS_EL1_IRQ,
|
||
|
|
+ [GTIMER_VIRT] = ARCH_TIMER_VIRT_IRQ,
|
||
|
|
+ [GTIMER_HYP] = ARCH_TIMER_NS_EL2_IRQ,
|
||
|
|
+ [GTIMER_SEC] = ARCH_TIMER_S_EL1_IRQ,
|
||
|
|
+};
|
||
|
|
+
|
||
|
|
+static void unwire_gic_cpu_irqs(VirtMachineState *vms, CPUState *cs)
|
||
|
|
+{
|
||
|
|
+ MachineState *ms = MACHINE(vms);
|
||
|
|
+ unsigned int max_cpus = ms->smp.max_cpus;
|
||
|
|
+ DeviceState *cpudev = DEVICE(cs);
|
||
|
|
+ DeviceState *gicdev = vms->gic;
|
||
|
|
+ int cpu = CPU(cs)->cpu_index;
|
||
|
|
+ int type = vms->gic_version;
|
||
|
|
+ int irq;
|
||
|
|
+
|
||
|
|
+ for (irq = 0; irq < ARRAY_SIZE(timer_irq); irq++) {
|
||
|
|
+ qdev_disconnect_gpio_out_named(cpudev, NULL, irq);
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ if (type != VIRT_GIC_VERSION_2) {
|
||
|
|
+ qdev_disconnect_gpio_out_named(cpudev, "gicv3-maintenance-interrupt",
|
||
|
|
+ 0);
|
||
|
|
+ } else if (vms->virt) {
|
||
|
|
+ qdev_disconnect_gpio_out_named(gicdev, SYSBUS_DEVICE_GPIO_IRQ,
|
||
|
|
+ cpu + 4 * max_cpus);
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ /*
|
||
|
|
+ * RFC: Question: This currently does not takes care of intimating the
|
||
|
|
+ * devices which might be sitting on system bus. Do we need a
|
||
|
|
+ * sysbus_disconnect_irq() which also does the job of notification beside
|
||
|
|
+ * disconnection?
|
||
|
|
+ */
|
||
|
|
+ qdev_disconnect_gpio_out_named(cpudev, "pmu-interrupt", 0);
|
||
|
|
+ qdev_disconnect_gpio_out_named(gicdev, SYSBUS_DEVICE_GPIO_IRQ, cpu);
|
||
|
|
+ qdev_disconnect_gpio_out_named(gicdev,
|
||
|
|
+ SYSBUS_DEVICE_GPIO_IRQ, cpu + max_cpus);
|
||
|
|
+ qdev_disconnect_gpio_out_named(gicdev, SYSBUS_DEVICE_GPIO_IRQ,
|
||
|
|
+ cpu + 2 * max_cpus);
|
||
|
|
+ qdev_disconnect_gpio_out_named(gicdev, SYSBUS_DEVICE_GPIO_IRQ,
|
||
|
|
+ cpu + 3 * max_cpus);
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
+static void wire_gic_cpu_irqs(VirtMachineState *vms, CPUState *cs)
|
||
|
|
+{
|
||
|
|
+ MachineState *ms = MACHINE(vms);
|
||
|
|
+ unsigned int max_cpus = ms->smp.max_cpus;
|
||
|
|
+ DeviceState *cpudev = DEVICE(cs);
|
||
|
|
+ DeviceState *gicdev = vms->gic;
|
||
|
|
+ int cpu = CPU(cs)->cpu_index;
|
||
|
|
+ int type = vms->gic_version;
|
||
|
|
+ SysBusDevice *gicbusdev;
|
||
|
|
+ int intidbase;
|
||
|
|
+ int irq;
|
||
|
|
+
|
||
|
|
+ intidbase = NUM_IRQS + cpu * GIC_INTERNAL;
|
||
|
|
+
|
||
|
|
+ for (irq = 0; irq < ARRAY_SIZE(timer_irq); irq++) {
|
||
|
|
+ qdev_connect_gpio_out(cpudev, irq,
|
||
|
|
+ qdev_get_gpio_in(gicdev,
|
||
|
|
+ intidbase + timer_irq[irq]));
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ gicbusdev = SYS_BUS_DEVICE(gicdev);
|
||
|
|
+ if (type != VIRT_GIC_VERSION_2) {
|
||
|
|
+ qemu_irq qirq = qdev_get_gpio_in(gicdev,
|
||
|
|
+ intidbase + ARCH_GIC_MAINT_IRQ);
|
||
|
|
+ qdev_connect_gpio_out_named(cpudev, "gicv3-maintenance-interrupt",
|
||
|
|
+ 0, qirq);
|
||
|
|
+ } else if (vms->virt) {
|
||
|
|
+ qemu_irq qirq = qdev_get_gpio_in(gicdev,
|
||
|
|
+ intidbase + ARCH_GIC_MAINT_IRQ);
|
||
|
|
+ sysbus_connect_irq(gicbusdev, cpu + 4 * max_cpus, qirq);
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ qdev_connect_gpio_out_named(cpudev, "pmu-interrupt", 0,
|
||
|
|
+ qdev_get_gpio_in(gicdev,
|
||
|
|
+ intidbase + VIRTUAL_PMU_IRQ));
|
||
|
|
+ sysbus_connect_irq(gicbusdev, cpu, qdev_get_gpio_in(cpudev, ARM_CPU_IRQ));
|
||
|
|
+ sysbus_connect_irq(gicbusdev, cpu + max_cpus,
|
||
|
|
+ qdev_get_gpio_in(cpudev, ARM_CPU_FIQ));
|
||
|
|
+ sysbus_connect_irq(gicbusdev, cpu + 2 * max_cpus,
|
||
|
|
+ qdev_get_gpio_in(cpudev, ARM_CPU_VIRQ));
|
||
|
|
+ sysbus_connect_irq(gicbusdev, cpu + 3 * max_cpus,
|
||
|
|
+ qdev_get_gpio_in(cpudev, ARM_CPU_VFIQ));
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
static void create_gic(VirtMachineState *vms, MemoryRegion *mem)
|
||
|
|
{
|
||
|
|
MachineState *ms = MACHINE(vms);
|
||
|
|
@@ -894,46 +987,7 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem)
|
||
|
|
* 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));
|
||
|
|
- int intidbase = NUM_IRQS + i * GIC_INTERNAL;
|
||
|
|
- /* Mapping from the output timer irq lines from the CPU to the
|
||
|
|
- * GIC PPI inputs we use for the virt board.
|
||
|
|
- */
|
||
|
|
- const int timer_irq[] = {
|
||
|
|
- [GTIMER_PHYS] = ARCH_TIMER_NS_EL1_IRQ,
|
||
|
|
- [GTIMER_VIRT] = ARCH_TIMER_VIRT_IRQ,
|
||
|
|
- [GTIMER_HYP] = ARCH_TIMER_NS_EL2_IRQ,
|
||
|
|
- [GTIMER_SEC] = ARCH_TIMER_S_EL1_IRQ,
|
||
|
|
- };
|
||
|
|
-
|
||
|
|
- for (unsigned irq = 0; irq < ARRAY_SIZE(timer_irq); irq++) {
|
||
|
|
- qdev_connect_gpio_out(cpudev, irq,
|
||
|
|
- qdev_get_gpio_in(vms->gic,
|
||
|
|
- intidbase + timer_irq[irq]));
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- if (vms->gic_version != VIRT_GIC_VERSION_2) {
|
||
|
|
- qemu_irq irq = qdev_get_gpio_in(vms->gic,
|
||
|
|
- intidbase + ARCH_GIC_MAINT_IRQ);
|
||
|
|
- qdev_connect_gpio_out_named(cpudev, "gicv3-maintenance-interrupt",
|
||
|
|
- 0, irq);
|
||
|
|
- } else if (vms->virt) {
|
||
|
|
- qemu_irq irq = qdev_get_gpio_in(vms->gic,
|
||
|
|
- intidbase + ARCH_GIC_MAINT_IRQ);
|
||
|
|
- sysbus_connect_irq(gicbusdev, i + 4 * max_cpus, irq);
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- qdev_connect_gpio_out_named(cpudev, "pmu-interrupt", 0,
|
||
|
|
- qdev_get_gpio_in(vms->gic, intidbase
|
||
|
|
- + VIRTUAL_PMU_IRQ));
|
||
|
|
-
|
||
|
|
- sysbus_connect_irq(gicbusdev, i, qdev_get_gpio_in(cpudev, ARM_CPU_IRQ));
|
||
|
|
- sysbus_connect_irq(gicbusdev, i + max_cpus,
|
||
|
|
- qdev_get_gpio_in(cpudev, ARM_CPU_FIQ));
|
||
|
|
- sysbus_connect_irq(gicbusdev, i + 2 * max_cpus,
|
||
|
|
- qdev_get_gpio_in(cpudev, ARM_CPU_VIRQ));
|
||
|
|
- sysbus_connect_irq(gicbusdev, i + 3 * max_cpus,
|
||
|
|
- qdev_get_gpio_in(cpudev, ARM_CPU_VFIQ));
|
||
|
|
+ wire_gic_cpu_irqs(vms, qemu_get_cpu(i));
|
||
|
|
}
|
||
|
|
|
||
|
|
fdt_add_gic_node(vms);
|
||
|
|
@@ -3162,7 +3216,7 @@ static void virt_cpu_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
|
||
|
|
*/
|
||
|
|
if (vms->acpi_dev) {
|
||
|
|
/* TODO: update GIC about this hotplug change here */
|
||
|
|
- /* TODO: wire the GIC<->CPU irqs */
|
||
|
|
+ wire_gic_cpu_irqs(vms, cs);
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
@@ -3246,7 +3300,7 @@ static void virt_cpu_unplug(HotplugHandler *hotplug_dev, DeviceState *dev,
|
||
|
|
|
||
|
|
/* TODO: update the acpi cpu hotplug state for cpu hot-unplug */
|
||
|
|
|
||
|
|
- /* TODO: unwire the gic-cpu irqs here */
|
||
|
|
+ unwire_gic_cpu_irqs(vms, cs);
|
||
|
|
/* TODO: update the GIC about this hot unplug change */
|
||
|
|
|
||
|
|
/* TODO: unregister cpu for reset & update F/W info for the next boot */
|
||
|
|
diff --git a/hw/core/gpio.c b/hw/core/gpio.c
|
||
|
|
index 80d07a6ec9..abb164d5c0 100644
|
||
|
|
--- a/hw/core/gpio.c
|
||
|
|
+++ b/hw/core/gpio.c
|
||
|
|
@@ -143,7 +143,7 @@ qemu_irq qdev_get_gpio_out_connector(DeviceState *dev, const char *name, int n)
|
||
|
|
|
||
|
|
/* disconnect a GPIO output, returning the disconnected input (if any) */
|
||
|
|
|
||
|
|
-static qemu_irq qdev_disconnect_gpio_out_named(DeviceState *dev,
|
||
|
|
+qemu_irq qdev_disconnect_gpio_out_named(DeviceState *dev,
|
||
|
|
const char *name, int n)
|
||
|
|
{
|
||
|
|
char *propname = g_strdup_printf("%s[%d]",
|
||
|
|
diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h
|
||
|
|
index 151d968238..2d3661d6cd 100644
|
||
|
|
--- a/include/hw/qdev-core.h
|
||
|
|
+++ b/include/hw/qdev-core.h
|
||
|
|
@@ -739,6 +739,8 @@ qemu_irq qdev_get_gpio_out_connector(DeviceState *dev, const char *name, int n);
|
||
|
|
*/
|
||
|
|
qemu_irq qdev_intercept_gpio_out(DeviceState *dev, qemu_irq icpt,
|
||
|
|
const char *name, int n);
|
||
|
|
+qemu_irq qdev_disconnect_gpio_out_named(DeviceState *dev,
|
||
|
|
+ const char *name, int n);
|
||
|
|
|
||
|
|
BusState *qdev_get_child_bus(DeviceState *dev, const char *name);
|
||
|
|
|
||
|
|
--
|
||
|
|
2.27.0
|
||
|
|
|