GICD_INMIRn marks SPI as NMI. GICR_INMIR0 marks SGI and PPI as NMI.
If GICR_INMIR0 or GICD_INMIRn is not migrated, the vNMI state will
be lost on the destination side.
Additionally, use a subsection to avoid migration failure when the
remote QEMU does not have GICR_INMIR0 support.
When migrating from a QEMU that saves GICR_INMIR0 or GICD_INMIRn
state to one that does not, the migration will fail with a graceful
error message.
When migrating from a QEMU that does not save GICR_INMIR0 or GICD_INMIRn
state to one that does, the migration will succeed, but the vNMI
state will be lost on the destination side since the source did
not save it.
Sign-off-by: Jinqian Yang <yangjinqian1(a)huawei.com>
---
hw/intc/arm_gicv3_common.c | 33 ++++++++++++++++++++++++++++++
hw/intc/arm_gicv3_kvm.c | 27 ++++++++++++++++++++++++
hw/intc/gicv3_internal.h | 4 ++++
include/hw/intc/arm_gicv3_common.h | 4 ++++
4 files changed, 68 insertions(+)
diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c
index 5667d9f40b..9a1fa2ff62 100644
--- a/hw/intc/arm_gicv3_common.c
+++ b/hw/intc/arm_gicv3_common.c
@@ -164,6 +164,24 @@ const VMStateDescription vmstate_gicv3_gicv4 = {
}
};
+static bool gicv3_nmi_needed(void *opaque)
+{
+ GICv3CPUState *cs = opaque;
+
+ return cs->gic->nmi_support;
+}
+
+const VMStateDescription vmstate_gicv3_gicr_nmi = {
+ .name = "arm_gicv3_cpu/gicr_nmi",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = gicv3_nmi_needed,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(gicr_inmir0, GICv3CPUState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
static const VMStateDescription vmstate_gicv3_cpu = {
.name = "arm_gicv3_cpu",
.version_id = 1,
@@ -196,6 +214,7 @@ static const VMStateDescription vmstate_gicv3_cpu = {
&vmstate_gicv3_cpu_virt,
&vmstate_gicv3_cpu_sre_el1,
&vmstate_gicv3_gicv4,
+ &vmstate_gicv3_gicr_nmi,
NULL
}
};
@@ -238,6 +257,17 @@ const VMStateDescription vmstate_gicv3_gicd_no_migration_shift_bug = {
}
};
+const VMStateDescription vmstate_gicv3_gicd_nmi = {
+ .name = "arm_gicv3/gicd_nmi",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = gicv3_nmi_needed,
+ .fields = (const VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(gicd_inmir, GICv3State, GICV3_BMP_SIZE),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
static const VMStateDescription vmstate_gicv3 = {
.name = "arm_gicv3",
.version_id = 1,
@@ -266,6 +296,7 @@ static const VMStateDescription vmstate_gicv3 = {
},
.subsections = (const VMStateDescription * []) {
&vmstate_gicv3_gicd_no_migration_shift_bug,
+ &vmstate_gicv3_gicd_nmi,
NULL
}
};
@@ -554,6 +585,7 @@ static void arm_gicv3_common_reset_hold(Object *obj)
cs->edge_trigger = 0xffff;
cs->gicr_igrpmodr0 = 0;
cs->gicr_nsacr = 0;
+ cs->gicr_inmir0 = 0;
memset(cs->gicr_ipriorityr, 0, sizeof(cs->gicr_ipriorityr));
cs->hppi.prio = 0xff;
@@ -585,6 +617,7 @@ static void arm_gicv3_common_reset_hold(Object *obj)
memset(s->gicd_ipriority, 0, sizeof(s->gicd_ipriority));
memset(s->gicd_irouter, 0, sizeof(s->gicd_irouter));
memset(s->gicd_nsacr, 0, sizeof(s->gicd_nsacr));
+ memset(s->gicd_inmir, 0, sizeof(s->gicd_inmir));
/* GICD_IROUTER are UNKNOWN at reset so in theory the guest must
* write these to get sane behaviour and we need not populate the
* pointer cache here; however having the cache be different for
diff --git a/hw/intc/arm_gicv3_kvm.c b/hw/intc/arm_gicv3_kvm.c
index dd2a60fa20..ba6d09a5b9 100644
--- a/hw/intc/arm_gicv3_kvm.c
+++ b/hw/intc/arm_gicv3_kvm.c
@@ -397,6 +397,9 @@ static void kvm_arm_gicv3_put(GICv3State *s)
reg = c->gicr_iactiver0;
kvm_gicr_access(s, GICR_ISACTIVER0, ncpu, ®, true);
+ reg = c->gicr_inmir0;
+ kvm_gicr_access(s, GICR_INMIR0, ncpu, ®, true);
+
for (i = 0; i < GIC_INTERNAL; i += 4) {
reg = c->gicr_ipriorityr[i] |
(c->gicr_ipriorityr[i + 1] << 8) |
@@ -561,6 +564,8 @@ static void kvm_arm_gicv3_get(GICv3State *s)
c->gicr_ipendr0 = reg;
kvm_gicr_access(s, GICR_ISACTIVER0, ncpu, ®, false);
c->gicr_iactiver0 = reg;
+ kvm_gicr_access(s, GICR_INMIR0, ncpu, ®, false);
+ c->gicr_inmir0 = reg;
for (i = 0; i < GIC_INTERNAL; i += 4) {
kvm_gicr_access(s, GICR_IPRIORITYR + i, ncpu, ®, false);
@@ -809,6 +814,22 @@ static void kvm_gicv3_init_cpu_reginfo(CPUState *cs)
define_arm_cp_regs(ARM_CPU(cs), gicv3_cpuif_reginfo);
}
+static bool kvm_gicv3_check_nmi(GICv3State *s)
+{
+ uint32_t typer = 0;
+
+ if (kvm_device_access(s->dev_fd,
+ KVM_DEV_ARM_VGIC_GRP_DIST_REGS,
+ KVM_VGIC_ATTR(GICD_TYPER, 0),
+ &typer,
+ false,
+ NULL)) {
+ return false;
+ }
+
+ return (typer & GICD_TYPER_NMI) != 0;
+}
+
static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp)
{
GICv3State *s = KVM_ARM_GICV3(dev);
@@ -918,6 +939,12 @@ static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp)
KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES)) {
qemu_add_vm_change_state_handler(vm_change_state_handler, s);
}
+
+ if (kvm_gicv3_check_nmi(s)) {
+ s->nmi_support = true;
+ } else {
+ s->nmi_support = false;
+ }
}
static void kvm_arm_gicv3_class_init(ObjectClass *klass, void *data)
diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h
index 0bed0f6e2a..938c7c5472 100644
--- a/hw/intc/gicv3_internal.h
+++ b/hw/intc/gicv3_internal.h
@@ -52,6 +52,7 @@
#define GICD_SGIR 0x0F00
#define GICD_CPENDSGIR 0x0F10
#define GICD_SPENDSGIR 0x0F20
+#define GICD_INMIR 0x0F80
#define GICD_IROUTER 0x6000
#define GICD_IDREGS 0xFFD0
@@ -68,6 +69,8 @@
#define GICD_CTLR_E1NWF (1U << 7)
#define GICD_CTLR_RWP (1U << 31)
+#define GICD_TYPER_NMI (1U << 9)
+
#define GICD_TYPER_LPIS_SHIFT 17
/* 16 bits EventId */
@@ -109,6 +112,7 @@
#define GICR_ICFGR1 (GICR_SGI_OFFSET + 0x0C04)
#define GICR_IGRPMODR0 (GICR_SGI_OFFSET + 0x0D00)
#define GICR_NSACR (GICR_SGI_OFFSET + 0x0E00)
+#define GICR_INMIR0 (GICR_SGI_OFFSET + 0x0F80)
/* VLPI redistributor registers, offsets from VLPI_base */
#define GICR_VPROPBASER (GICR_VLPI_OFFSET + 0x70)
diff --git a/include/hw/intc/arm_gicv3_common.h b/include/hw/intc/arm_gicv3_common.h
index b5f8ba17ff..48694fd757 100644
--- a/include/hw/intc/arm_gicv3_common.h
+++ b/include/hw/intc/arm_gicv3_common.h
@@ -173,6 +173,7 @@ struct GICv3CPUState {
uint32_t edge_trigger; /* ICFGR0 and ICFGR1 even bits */
uint32_t gicr_igrpmodr0;
uint32_t gicr_nsacr;
+ uint32_t gicr_inmir0;
uint8_t gicr_ipriorityr[GIC_INTERNAL];
/* VLPI_base page registers */
uint64_t gicr_vpropbaser;
@@ -279,11 +280,14 @@ struct GICv3State {
*/
GICv3CPUState *gicd_irouter_target[GICV3_MAXIRQ];
uint32_t gicd_nsacr[DIV_ROUND_UP(GICV3_MAXIRQ, 16)];
+ uint32_t gicd_inmir[GICV3_BMP_SIZE];
Notifier cpu_update_notifier;
GICv3CPUState *cpu;
/* List of all ITSes connected to this GIC */
GPtrArray *itslist;
+
+ bool nmi_support;
};
#define GICV3_BITMAP_ACCESSORS(BMP) \
--
2.33.0