diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 39ab5f47e0..fc58ee70b4 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -71,6 +71,8 @@
#include "hw/mem/pc-dimm.h"
#include "hw/mem/nvdimm.h"
#include "hw/acpi/generic_event_device.h"
+#include "hw/nmi.h"
+#include "hw/intc/arm_gicv3.h"
#define DEFINE_VIRT_MACHINE_LATEST(major, minor, latest) \
static void virt_##major##_##minor##_class_init(ObjectClass *oc, \
@@ -1980,6 +1982,25 @@ static void
virt_machine_device_unplug_request_cb(HotplugHandler *hotplug_dev,
" type: %s", object_get_typename(OBJECT(dev)));
}
+static void virt_nmi(NMIState *n, int cpu_index, Error **errp)
+{
+ VirtMachineState *vms = VIRT_MACHINE(n);
+ ARMGICv3CommonClass *agcc;
+
+ if (vms->gic_version != 3) {
+ error_setg(errp, "NMI is only supported by GICv3");
+ return;
+ }
+
+ agcc = ARM_GICV3_COMMON_GET_CLASS(vms->gic);
+ if (agcc->inject_nmi) {
+ agcc->inject_nmi(vms->gic, cpu_index, errp);
+ } else {
+ error_setg(errp, "NMI injection isn't supported by %s",
+ object_get_typename(OBJECT(vms->gic)));
+ }
+}
+
static HotplugHandler *virt_machine_get_hotplug_handler(MachineState *machine,
DeviceState *dev)
{
@@ -2025,6 +2046,7 @@ static void virt_machine_class_init(ObjectClass *oc, void
*data)
{
MachineClass *mc = MACHINE_CLASS(oc);
HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc);
+ NMIClass *nc = NMI_CLASS(oc);
mc->init = machvirt_init;
/* Start with max_cpus set to 512, which is the maximum supported by KVM.
@@ -2051,6 +2073,7 @@ static void virt_machine_class_init(ObjectClass *oc, void
*data)
hc->pre_plug = virt_machine_device_pre_plug_cb;
hc->plug = virt_machine_device_plug_cb;
hc->unplug_request = virt_machine_device_unplug_request_cb;
+ nc->nmi_monitor_handler = virt_nmi;
mc->numa_mem_supported = true;
mc->auto_enable_numa_with_memhp = true;
}
@@ -2136,6 +2159,7 @@ static const TypeInfo virt_machine_info = {
.instance_init = virt_instance_init,
.interfaces = (InterfaceInfo[]) {
{ TYPE_HOTPLUG_HANDLER },
+ { TYPE_NMI },
{ }
},
};
diff --git a/hw/intc/arm_gicv3.c b/hw/intc/arm_gicv3.c
index 66eaa97198..d3409cb6ef 100644
--- a/hw/intc/arm_gicv3.c
+++ b/hw/intc/arm_gicv3.c
@@ -338,6 +338,81 @@ static void gicv3_set_irq(void *opaque, int irq, int level)
}
}
+static bool arm_gicv3_inject_nmi_once(GICv3State*s, int start, int end)
+{
+ GICv3CPUState *cs;
+ int irq_count = (s->num_irq + (GIC_INTERNAL * (s->num_cpu - 1)));
+ int i, cpu, irq;
+
+ /* SPIs */
+ for (i = start; (i < end) && (i < (s->num_irq - GIC_INTERNAL)); i++) {
+ if (gicv3_gicd_enabled_test(s, i + GIC_INTERNAL) &&
+ s->gicd_ipriority[i + GIC_INTERNAL] == 0x20) {
+
+ /*
+ * Reset the level and toggling the pending bit will ensure
+ * the interrupt is queued.
+ */
+ if (gicv3_gicd_level_test(s, i + GIC_INTERNAL)) {
+ gicv3_set_irq(s, i, false);
+ }
+
+ gicv3_gicd_pending_set(s, i + GIC_INTERNAL);
+ gicv3_set_irq(s, i, true);
+
+ s->last_nmi_index = (i + 1);
+ return true;
+ }
+ }
+
+ /* PPIs */
+ if (start < (s->num_irq - GIC_INTERNAL)) {
+ start = (s->num_irq - GIC_INTERNAL);
+ }
+
+ for (i = start; (i < end) && (i < irq_count); i++) {
+ cpu = (i - ((s->num_irq - GIC_INTERNAL))) / GIC_INTERNAL;
+ irq = (i - ((s->num_irq - GIC_INTERNAL))) % GIC_INTERNAL;
+ cs = &s->cpu[cpu];
+
+ if ((cs->gicr_ienabler0 & (1 << irq)) &&
+ cs->gicr_ipriorityr[irq] == 0x20) {
+
+ if (extract32(cs->level, irq, 1)) {
+ gicv3_set_irq(s, i, false);
+ }
+
+ deposit32(cs->gicr_ipendr0, irq, 1, 1);
+ gicv3_set_irq(s, i, true);
+
+ s->last_nmi_index = (i + 1);
+ if (s->last_nmi_index > irq_count) {
+ s->last_nmi_index = 0;
+ }
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static void arm_gicv3_inject_nmi(DeviceState *dev, int cpu_index, Error **errp)
+{
+ GICv3State *s = ARM_GICV3(dev);
+ int irq_count = (s->num_irq + (GIC_INTERNAL * (s->num_cpu - 1)));
+ bool injected;
+
+ injected = arm_gicv3_inject_nmi_once(s, s->last_nmi_index, irq_count);
+ if (!injected) {
+ injected = arm_gicv3_inject_nmi_once(s, 0, s->last_nmi_index);
+ }
+
+ if (!injected) {
+ error_setg(errp, "No NMI found");
+ }
+}
+
static void arm_gicv3_post_load(GICv3State *s)
{
/* Recalculate our cached idea of the current highest priority
@@ -395,6 +470,7 @@ static void arm_gicv3_class_init(ObjectClass *klass, void
*data)
ARMGICv3CommonClass *agcc = ARM_GICV3_COMMON_CLASS(klass);
ARMGICv3Class *agc = ARM_GICV3_CLASS(klass);
+ agcc->inject_nmi = arm_gicv3_inject_nmi;
agcc->post_load = arm_gicv3_post_load;
device_class_set_parent_realize(dc, arm_gic_realize,
&agc->parent_realize);
}
diff --git a/hw/intc/arm_gicv3_kvm.c b/hw/intc/arm_gicv3_kvm.c
index 9c7f4ab871..b076d67c52 100644
--- a/hw/intc/arm_gicv3_kvm.c
+++ b/hw/intc/arm_gicv3_kvm.c
@@ -31,6 +31,7 @@
#include "gicv3_internal.h"
#include "vgic_common.h"
#include "migration/blocker.h"
+#include "sysemu/cpus.h"
#ifdef DEBUG_GICV3_KVM
#define DPRINTF(fmt, ...) \
@@ -506,6 +507,96 @@ static void kvm_arm_gicv3_put(GICv3State *s)
}
}
+static bool kvm_arm_gicv3_inject_nmi_once(GICv3State *s, int start, int end)
+{
+ GICv3CPUState *cs;
+ int irq_count = (s->num_irq + (GIC_INTERNAL * (s->num_cpu - 1)));
+ int i, cpu, irq;
+
+ /* SPIs */
+ for (i = start; (i < end) && (i < (s->num_irq - GIC_INTERNAL)); i++) {
+ if (gicv3_gicd_enabled_test(s, i + GIC_INTERNAL) &&
+ s->gicd_ipriority[i + GIC_INTERNAL] == 0x20) {
+ kvm_arm_gicv3_set_irq(s, i, true);
+
+ s->last_nmi_index = (i + 1);
+ return true;
+ }
+ }
+
+ /* PPIs */
+ if (start < (s->num_irq - GIC_INTERNAL)) {
+ start = (s->num_irq - GIC_INTERNAL);
+ }
+
+ for (i = start; (i < end) && (i < irq_count); i++) {
+ cpu = (i - ((s->num_irq - GIC_INTERNAL))) / GIC_INTERNAL;
+ irq = (i - ((s->num_irq - GIC_INTERNAL))) % GIC_INTERNAL;
+ cs = &s->cpu[cpu];
+
+ if ((cs->gicr_ienabler0 & (1 << irq)) &&
+ cs->gicr_ipriorityr[irq] == 0x20) {
+ kvm_arm_gicv3_set_irq(s, i, true);
+
+ s->last_nmi_index = (i + 1);
+ if (s->last_nmi_index > irq_count) {
+ s->last_nmi_index = 0;
+ }
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static void kvm_arm_gicv3_snapshot(GICv3State *s)
+{
+ GICv3CPUState *c;
+ uint32_t val;
+ int i, j;
+
+ pause_all_vcpus();
+
+ kvm_dist_getbmp(s, GICD_ISENABLER, s->enabled);
+ kvm_dist_get_priority(s, GICD_IPRIORITYR, s->gicd_ipriority);
+ for (i = 0; i < s->num_cpu; i++) {
+ c = &s->cpu[i];
+
+ kvm_gicr_access(s, GICR_ISENABLER0, i, &val, false);
+ c->gicr_ienabler0 = val;
+
+ for (j = 0; j < GIC_INTERNAL; j += 4) {
+ kvm_gicr_access(s, GICR_IPRIORITYR + j, i, &val, false);
+ c->gicr_ipriorityr[j] = extract32(val, 0, 8);
+ c->gicr_ipriorityr[j + 1] = extract32(val, 8, 8);
+ c->gicr_ipriorityr[j + 2] = extract32(val, 16, 8);
+ c->gicr_ipriorityr[j + 3] = extract32(val, 24, 8);
+ }
+ }
+
+ resume_all_vcpus();
+}
+
+static void kvm_arm_gicv3_inject_nmi(DeviceState *dev,
+ int cpu_index, Error **errp)
+{
+ GICv3State *s = KVM_ARM_GICV3(dev);
+ int irq_count = (s->num_irq + (GIC_INTERNAL * (s->num_cpu - 1)));
+ bool injected;
+
+ kvm_arm_gicv3_snapshot(s);
+
+ injected = kvm_arm_gicv3_inject_nmi_once(s, s->last_nmi_index, irq_count);
+ if (!injected) {
+ injected = kvm_arm_gicv3_inject_nmi_once(s, 0, s->last_nmi_index);
+ }
+
+ if (!injected) {
+ error_setg(errp, "No NMI found");
+ }
+}
+
static void kvm_arm_gicv3_get(GICv3State *s)
{
uint32_t regl, regh, reg;
@@ -882,6 +973,7 @@ static void kvm_arm_gicv3_class_init(ObjectClass *klass,
void *data)
ARMGICv3CommonClass *agcc = ARM_GICV3_COMMON_CLASS(klass);
KVMARMGICv3Class *kgc = KVM_ARM_GICV3_CLASS(klass);
+ agcc->inject_nmi = kvm_arm_gicv3_inject_nmi;
agcc->pre_save = kvm_arm_gicv3_get;
agcc->post_load = kvm_arm_gicv3_put;
device_class_set_parent_realize(dc, kvm_arm_gicv3_realize,
diff --git a/include/hw/intc/arm_gicv3_common.h
b/include/hw/intc/arm_gicv3_common.h
index 31ec9a1ae4..0ae9c45aa2 100644
--- a/include/hw/intc/arm_gicv3_common.h
+++ b/include/hw/intc/arm_gicv3_common.h
@@ -225,6 +225,7 @@ struct GICv3State {
int dev_fd; /* kvm device fd if backed by kvm vgic support */
Error *migration_blocker;
+ int last_nmi_index;
/* Distributor */
@@ -291,6 +292,7 @@ typedef struct ARMGICv3CommonClass {
SysBusDeviceClass parent_class;
/*< public >*/
+ void (*inject_nmi)(DeviceState *dev, int cpu_index, Error **errp);
void (*pre_save)(GICv3State *s);
void (*post_load)(GICv3State *s);
} ARMGICv3CommonClass;