#include "qemu/osdep.h"
#include "qapi/error.h"
-#include "qom/cpu.h"
+#include "qemu/module.h"
+#include "hw/core/cpu.h"
#include "hw/intc/arm_gicv3_common.h"
+#include "hw/qdev-properties.h"
+#include "migration/vmstate.h"
#include "gicv3_internal.h"
#include "hw/arm/linux-boot-if.h"
+#include "sysemu/kvm.h"
-static void gicv3_pre_save(void *opaque)
+
+static void gicv3_gicd_no_migration_shift_bug_post_load(GICv3State *cs)
+{
+ if (cs->gicd_no_migration_shift_bug) {
+ return;
+ }
+
+ /* Older versions of QEMU had a bug in the handling of state save/restore
+ * to the KVM GICv3: they got the offset in the bitmap arrays wrong,
+ * so that instead of the data for external interrupts 32 and up
+ * starting at bit position 32 in the bitmap, it started at bit
+ * position 64. If we're receiving data from a QEMU with that bug,
+ * we must move the data down into the right place.
+ */
+ memmove(cs->group, (uint8_t *)cs->group + GIC_INTERNAL / 8,
+ sizeof(cs->group) - GIC_INTERNAL / 8);
+ memmove(cs->grpmod, (uint8_t *)cs->grpmod + GIC_INTERNAL / 8,
+ sizeof(cs->grpmod) - GIC_INTERNAL / 8);
+ memmove(cs->enabled, (uint8_t *)cs->enabled + GIC_INTERNAL / 8,
+ sizeof(cs->enabled) - GIC_INTERNAL / 8);
+ memmove(cs->pending, (uint8_t *)cs->pending + GIC_INTERNAL / 8,
+ sizeof(cs->pending) - GIC_INTERNAL / 8);
+ memmove(cs->active, (uint8_t *)cs->active + GIC_INTERNAL / 8,
+ sizeof(cs->active) - GIC_INTERNAL / 8);
+ memmove(cs->edge_trigger, (uint8_t *)cs->edge_trigger + GIC_INTERNAL / 8,
+ sizeof(cs->edge_trigger) - GIC_INTERNAL / 8);
+
+ /*
+ * While this new version QEMU doesn't have this kind of bug as we fix it,
+ * so it needs to set the flag to true to indicate that and it's necessary
+ * for next migration to work from this new version QEMU.
+ */
+ cs->gicd_no_migration_shift_bug = true;
+}
+
+static int gicv3_pre_save(void *opaque)
{
GICv3State *s = (GICv3State *)opaque;
ARMGICv3CommonClass *c = ARM_GICV3_COMMON_GET_CLASS(s);
if (c->pre_save) {
c->pre_save(s);
}
+
+ return 0;
}
static int gicv3_post_load(void *opaque, int version_id)
GICv3State *s = (GICv3State *)opaque;
ARMGICv3CommonClass *c = ARM_GICV3_COMMON_GET_CLASS(s);
+ gicv3_gicd_no_migration_shift_bug_post_load(s);
+
if (c->post_load) {
c->post_load(s);
}
}
};
+static int vmstate_gicv3_cpu_pre_load(void *opaque)
+{
+ GICv3CPUState *cs = opaque;
+
+ /*
+ * If the sre_el1 subsection is not transferred this
+ * means SRE_EL1 is 0x7 (which might not be the same as
+ * our reset value).
+ */
+ cs->icc_sre_el1 = 0x7;
+ return 0;
+}
+
+static bool icc_sre_el1_reg_needed(void *opaque)
+{
+ GICv3CPUState *cs = opaque;
+
+ return cs->icc_sre_el1 != 7;
+}
+
+const VMStateDescription vmstate_gicv3_cpu_sre_el1 = {
+ .name = "arm_gicv3_cpu/sre_el1",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = icc_sre_el1_reg_needed,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT64(icc_sre_el1, GICv3CPUState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
static const VMStateDescription vmstate_gicv3_cpu = {
.name = "arm_gicv3_cpu",
.version_id = 1,
.minimum_version_id = 1,
+ .pre_load = vmstate_gicv3_cpu_pre_load,
.fields = (VMStateField[]) {
VMSTATE_UINT32(level, GICv3CPUState),
VMSTATE_UINT32(gicr_ctlr, GICv3CPUState),
},
.subsections = (const VMStateDescription * []) {
&vmstate_gicv3_cpu_virt,
+ &vmstate_gicv3_cpu_sre_el1,
NULL
}
};
+static int gicv3_pre_load(void *opaque)
+{
+ GICv3State *cs = opaque;
+
+ /*
+ * The gicd_no_migration_shift_bug flag is used for migration compatibility
+ * for old version QEMU which may have the GICD bmp shift bug under KVM mode.
+ * Strictly, what we want to know is whether the migration source is using
+ * KVM. Since we don't have any way to determine that, we look at whether the
+ * destination is using KVM; this is close enough because for the older QEMU
+ * versions with this bug KVM -> TCG migration didn't work anyway. If the
+ * source is a newer QEMU without this bug it will transmit the migration
+ * subsection which sets the flag to true; otherwise it will remain set to
+ * the value we select here.
+ */
+ if (kvm_enabled()) {
+ cs->gicd_no_migration_shift_bug = false;
+ }
+
+ return 0;
+}
+
+static bool needed_always(void *opaque)
+{
+ return true;
+}
+
+const VMStateDescription vmstate_gicv3_gicd_no_migration_shift_bug = {
+ .name = "arm_gicv3/gicd_no_migration_shift_bug",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = needed_always,
+ .fields = (VMStateField[]) {
+ VMSTATE_BOOL(gicd_no_migration_shift_bug, GICv3State),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
static const VMStateDescription vmstate_gicv3 = {
.name = "arm_gicv3",
.version_id = 1,
.minimum_version_id = 1,
+ .pre_load = gicv3_pre_load,
.pre_save = gicv3_pre_save,
.post_load = gicv3_post_load,
+ .priority = MIG_PRI_GICV3,
.fields = (VMStateField[]) {
VMSTATE_UINT32(gicd_ctlr, GICv3State),
VMSTATE_UINT32_ARRAY(gicd_statusr, GICv3State, 2),
VMSTATE_STRUCT_VARRAY_POINTER_UINT32(cpu, GICv3State, num_cpu,
vmstate_gicv3_cpu, GICv3CPUState),
VMSTATE_END_OF_LIST()
+ },
+ .subsections = (const VMStateDescription * []) {
+ &vmstate_gicv3_gicd_no_migration_shift_bug,
+ NULL
}
};
{
SysBusDevice *sbd = SYS_BUS_DEVICE(s);
int i;
+ int cpuidx;
/* For the GIC, also expose incoming GPIO lines for PPIs for each CPU.
* GPIO array layout is thus:
memory_region_init_io(&s->iomem_dist, OBJECT(s), ops, s,
"gicv3_dist", 0x10000);
- memory_region_init_io(&s->iomem_redist, OBJECT(s), ops ? &ops[1] : NULL, s,
- "gicv3_redist", 0x20000 * s->num_cpu);
-
sysbus_init_mmio(sbd, &s->iomem_dist);
- sysbus_init_mmio(sbd, &s->iomem_redist);
+
+ s->redist_regions = g_new0(GICv3RedistRegion, s->nb_redist_regions);
+ cpuidx = 0;
+ for (i = 0; i < s->nb_redist_regions; i++) {
+ char *name = g_strdup_printf("gicv3_redist_region[%d]", i);
+ GICv3RedistRegion *region = &s->redist_regions[i];
+
+ region->gic = s;
+ region->cpuidx = cpuidx;
+ cpuidx += s->redist_region_count[i];
+
+ memory_region_init_io(®ion->iomem, OBJECT(s),
+ ops ? &ops[1] : NULL, region, name,
+ s->redist_region_count[i] * GICV3_REDIST_SIZE);
+ sysbus_init_mmio(sbd, ®ion->iomem);
+ g_free(name);
+ }
}
static void arm_gicv3_common_realize(DeviceState *dev, Error **errp)
{
GICv3State *s = ARM_GICV3_COMMON(dev);
- int i;
+ int i, rdist_capacity, cpuidx;
/* revision property is actually reserved and currently used only in order
* to keep the interface compatible with GICv2 code, avoiding extra
s->num_irq, GIC_INTERNAL);
return;
}
+ if (s->num_cpu == 0) {
+ error_setg(errp, "num-cpu must be at least 1");
+ return;
+ }
/* ITLinesNumber is represented as (N / 32) - 1, so this is an
* implementation imposed restriction, not an architectural one,
return;
}
+ if (s->lpi_enable && !s->dma) {
+ error_setg(errp, "Redist-ITS: Guest 'sysmem' reference link not set");
+ return;
+ }
+
+ rdist_capacity = 0;
+ for (i = 0; i < s->nb_redist_regions; i++) {
+ rdist_capacity += s->redist_region_count[i];
+ }
+ if (rdist_capacity != s->num_cpu) {
+ error_setg(errp, "Capacity of the redist regions(%d) "
+ "does not match the number of vcpus(%d)",
+ rdist_capacity, s->num_cpu);
+ return;
+ }
+
+ if (s->lpi_enable) {
+ address_space_init(&s->dma_as, s->dma,
+ "gicv3-its-sysmem");
+ }
+
s->cpu = g_new0(GICv3CPUState, s->num_cpu);
for (i = 0; i < s->num_cpu; i++) {
CPUState *cpu = qemu_get_cpu(i);
uint64_t cpu_affid;
- int last;
s->cpu[i].cpu = cpu;
s->cpu[i].gic = s;
+ /* Store GICv3CPUState in CPUARMState gicv3state pointer */
+ gicv3_set_gicv3state(cpu, &s->cpu[i]);
/* Pre-construct the GICR_TYPER:
* For our implementation:
* VLPIS == 0 (virtual LPIs not supported)
* PLPIS == 0 (physical LPIs not supported)
*/
- cpu_affid = object_property_get_int(OBJECT(cpu), "mp-affinity", NULL);
- last = (i == s->num_cpu - 1);
+ cpu_affid = object_property_get_uint(OBJECT(cpu), "mp-affinity", NULL);
/* The CPU mp-affinity property is in MPIDR register format; squash
* the affinity bytes into 32 bits as the GICR_TYPER has them.
(cpu_affid & 0xFFFFFF);
s->cpu[i].gicr_typer = (cpu_affid << 32) |
(1 << 24) |
- (i << 8) |
- (last << 4);
+ (i << 8);
+
+ if (s->lpi_enable) {
+ s->cpu[i].gicr_typer |= GICR_TYPER_PLPIS;
+ }
+ }
+
+ /*
+ * Now go through and set GICR_TYPER.Last for the final
+ * redistributor in each region.
+ */
+ cpuidx = 0;
+ for (i = 0; i < s->nb_redist_regions; i++) {
+ cpuidx += s->redist_region_count[i];
+ s->cpu[cpuidx - 1].gicr_typer |= GICR_TYPER_LAST;
}
}
+static void arm_gicv3_finalize(Object *obj)
+{
+ GICv3State *s = ARM_GICV3_COMMON(obj);
+
+ g_free(s->redist_region_count);
+}
+
static void arm_gicv3_common_reset(DeviceState *dev)
{
GICv3State *s = ARM_GICV3_COMMON(dev);
cs->level = 0;
cs->gicr_ctlr = 0;
+ if (s->lpi_enable) {
+ /* Our implementation supports clearing GICR_CTLR.EnableLPIs */
+ cs->gicr_ctlr |= GICR_CTLR_CES;
+ }
cs->gicr_statusr[GICV3_S] = 0;
cs->gicr_statusr[GICV3_NS] = 0;
cs->gicr_waker = GICR_WAKER_ProcessorSleep | GICR_WAKER_ChildrenAsleep;
memset(cs->gicr_ipriorityr, 0, sizeof(cs->gicr_ipriorityr));
cs->hppi.prio = 0xff;
+ cs->hpplpi.prio = 0xff;
/* State in the CPU interface must *not* be reset here, because it
* is part of the CPU's reset domain, not the GIC device's.
gicv3_gicd_group_set(s, i);
}
}
+ s->gicd_no_migration_shift_bug = true;
}
static void arm_gic_common_linux_init(ARMLinuxBootIf *obj,
DEFINE_PROP_UINT32("num-cpu", GICv3State, num_cpu, 1),
DEFINE_PROP_UINT32("num-irq", GICv3State, num_irq, 32),
DEFINE_PROP_UINT32("revision", GICv3State, revision, 3),
+ DEFINE_PROP_BOOL("has-lpi", GICv3State, lpi_enable, 0),
DEFINE_PROP_BOOL("has-security-extensions", GICv3State, security_extn, 0),
+ DEFINE_PROP_ARRAY("redist-region-count", GICv3State, nb_redist_regions,
+ redist_region_count, qdev_prop_uint32, uint32_t),
+ DEFINE_PROP_LINK("sysmem", GICv3State, dma, TYPE_MEMORY_REGION,
+ MemoryRegion *),
DEFINE_PROP_END_OF_LIST(),
};
dc->reset = arm_gicv3_common_reset;
dc->realize = arm_gicv3_common_realize;
- dc->props = arm_gicv3_common_properties;
+ device_class_set_props(dc, arm_gicv3_common_properties);
dc->vmsd = &vmstate_gicv3;
albifc->arm_linux_init = arm_gic_common_linux_init;
}
.instance_size = sizeof(GICv3State),
.class_size = sizeof(ARMGICv3CommonClass),
.class_init = arm_gicv3_common_class_init,
+ .instance_finalize = arm_gicv3_finalize,
.abstract = true,
.interfaces = (InterfaceInfo []) {
{ TYPE_ARM_LINUX_BOOT_IF },