@@ -71,6 +71,7 @@
#include "hw/mem/pc-dimm.h"
#include "hw/mem/nvdimm.h"
#include "hw/acpi/generic_event_device.h"
+#include "hw/nmi.h"
#define DEFINE_VIRT_MACHINE_LATEST(major, minor, latest) \
static void virt_##major##_##minor##_class_init(ObjectClass *oc, \
@@ -2026,10 +2027,25 @@ static int virt_kvm_type(MachineState *ms, const char *type_str)
return requested_pa_size > 40 ? requested_pa_size : 0;
}
+static void virt_nmi(NMIState *n, int cpu_index, Error **errp)
+{
+ ARMCPUClass *acc;
+ CPUState *cpu = first_cpu;
+
+ acc = ARM_CPU_GET_CLASS(OBJECT(cpu));
+ if (!acc->inject_nmi) {
+ error_setg(errp, "NMI injection not supported");
+ return;
+ }
+
+ acc->inject_nmi(cpu, errp);
+}
+
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.
@@ -2058,6 +2074,7 @@ static void virt_machine_class_init(ObjectClass *oc, void *data)
hc->unplug_request = virt_machine_device_unplug_request_cb;
mc->numa_mem_supported = true;
mc->auto_enable_numa_with_memhp = true;
+ nc->nmi_monitor_handler = virt_nmi;
}
static void virt_instance_init(Object *obj)
@@ -2141,6 +2158,7 @@ static const TypeInfo virt_machine_info = {
.instance_init = virt_instance_init,
.interfaces = (InterfaceInfo[]) {
{ TYPE_HOTPLUG_HANDLER },
+ { TYPE_NMI },
{ }
},
};
@@ -52,6 +52,7 @@ typedef struct ARMCPUClass {
const ARMCPUInfo *info;
DeviceRealize parent_realize;
void (*parent_reset)(CPUState *cpu);
+ void (*inject_nmi)(CPUState *cpu, Error **errp);
} ARMCPUClass;
typedef struct ARMCPU ARMCPU;
@@ -475,6 +475,44 @@ bool arm_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
return ret;
}
+#ifdef CONFIG_KVM
+static void do_inject_nmi_kvm(CPUState *cpu, run_on_cpu_data data)
+{
+ struct kvm_vcpu_events events;
+ Error **errp = data.host_ptr;
+ int ret;
+
+ if (!kvm_has_vcpu_events()) {
+ error_setg(errp, "vCPU events not supported");
+ return;
+ }
+
+ memset(&events, 0, sizeof(events));
+ events.exception.serror_pending = 1;
+ ret = kvm_vcpu_ioctl(CPU(cpu), KVM_SET_VCPU_EVENTS, &events);
+ if (ret) {
+ error_setg(errp, "Error to inject vCPU events");
+ }
+}
+#endif
+
+/* The guest is expected to crash once receiving the SError interrupt */
+static void do_inject_nmi(CPUState *cpu, run_on_cpu_data data)
+{
+ cpu_synchronize_state(cpu);
+
+#ifdef CONFIG_KVM
+ return do_inject_nmi_kvm(cpu, data);
+#endif
+
+ cpu_interrupt(cpu, CPU_INTERRUPT_SERROR);
+}
+
+void arm_cpu_inject_nmi(CPUState *cpu, Error **errp)
+{
+ async_run_on_cpu(cpu, do_inject_nmi, RUN_ON_CPU_HOST_PTR(errp));
+}
+
#if !defined(CONFIG_USER_ONLY) || !defined(TARGET_AARCH64)
static bool arm_v7m_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
{
@@ -2759,10 +2797,20 @@ static void arm_host_initfn(Object *obj)
arm_cpu_post_init(obj);
}
+#ifdef TARGET_AARCH64
+static void arm_host_class_init(ObjectClass *oc, void *data)
+{
+ ARMCPUClass *acc = ARM_CPU_CLASS(oc);
+
+ acc->inject_nmi = arm_cpu_inject_nmi;
+}
+#endif /* TARGET_AARCH64 */
+
static const TypeInfo host_arm_cpu_type_info = {
.name = TYPE_ARM_HOST_CPU,
#ifdef TARGET_AARCH64
.parent = TYPE_AARCH64_CPU,
+ .class_init = arm_host_class_init,
#else
.parent = TYPE_ARM_CPU,
#endif
@@ -21,6 +21,7 @@
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "cpu.h"
+#include "internals.h"
#include "qemu/module.h"
#if !defined(CONFIG_USER_ONLY)
#include "hw/loader.h"
@@ -713,6 +714,14 @@ static void aarch64_max_initfn(Object *obj)
cpu_max_set_sve_max_vq, NULL, NULL, &error_fatal);
}
+static void aarch64_max_class_init(ObjectClass *oc, void *data)
+{
+ ARMCPUClass *acc = ARM_CPU_CLASS(oc);
+
+ acc->inject_nmi = arm_cpu_inject_nmi;
+ acc->info = data;
+}
+
struct ARMCPUInfo {
const char *name;
void (*initfn)(Object *obj);
@@ -720,10 +729,18 @@ struct ARMCPUInfo {
};
static const ARMCPUInfo aarch64_cpus[] = {
- { .name = "cortex-a57", .initfn = aarch64_a57_initfn },
- { .name = "cortex-a53", .initfn = aarch64_a53_initfn },
- { .name = "cortex-a72", .initfn = aarch64_a72_initfn },
- { .name = "max", .initfn = aarch64_max_initfn },
+ { .name = "cortex-a57",
+ .class_init = NULL,
+ .initfn = aarch64_a57_initfn },
+ { .name = "cortex-a53",
+ .class_init = NULL,
+ .initfn = aarch64_a53_initfn },
+ { .name = "cortex-a72",
+ .class_init = NULL,
+ .initfn = aarch64_a72_initfn },
+ { .name = "max",
+ .class_init = aarch64_max_class_init,
+ .initfn = aarch64_max_initfn },
{ .name = NULL }
};
@@ -949,6 +949,14 @@ void arm_cpu_update_virq(ARMCPU *cpu);
*/
void arm_cpu_update_vfiq(ARMCPU *cpu);
+/**
+ * arm_cpu_inject_nmi: Inject SError exception
+ *
+ * Inject SError exception to trigger kernel panic inside the guest. That's
+ * the simulated behavior as to other architectures like x86.
+ */
+void arm_cpu_inject_nmi(CPUState *cpu, Error **errp);
+
/**
* arm_mmu_idx_el:
* @env: The cpu environment
This supports QMP/HMP "nmi" command by injecting SError interrupt to guest, which is expected to crash with that. Currently, It's supported on two CPU models: "host" and "max". Signed-off-by: Gavin Shan <gshan@redhat.com> --- hw/arm/virt.c | 18 ++++++++++++++++ target/arm/cpu-qom.h | 1 + target/arm/cpu.c | 48 ++++++++++++++++++++++++++++++++++++++++++ target/arm/cpu64.c | 25 ++++++++++++++++++---- target/arm/internals.h | 8 +++++++ 5 files changed, 96 insertions(+), 4 deletions(-)