[PATCH 3/3] kvm-userspace: Enable user space injected NMI support

Jan Kiszka
2008-09-15T17:00:29Z

Make use of the new KVM_NMI IOCTL to push NMIs into the KVM guest if the
user space APIC emulation or some other source raised them.

Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
---
libkvm/libkvm.c | 31 +++++++++++++++++++++++++++++++
libkvm/libkvm.h | 23 +++++++++++++++++++++++
qemu/qemu-kvm-x86.c | 26 +++++++++++++++++++++++---
qemu/qemu-kvm.c | 8 +++++++-
qemu/qemu-kvm.h | 1 +
5 files changed, 85 insertions(+), 4 deletions(-)

Index: b/libkvm/libkvm.c
===================================================================
--- a/libkvm/libkvm.c
+++ b/libkvm/libkvm.c
@@ -811,6 +811,11 @@ int try_push_interrupts(kvm_context_t kv
return kvm->callbacks->try_push_interrupts(kvm->opaque);
}

+int try_push_nmi(kvm_context_t kvm)
+{
+ return kvm->callbacks->try_push_nmi(kvm->opaque);
+}
+
void post_kvm_run(kvm_context_t kvm, int vcpu)
{
kvm->callbacks->post_kvm_run(kvm->opaque, vcpu);
@@ -835,6 +840,17 @@ int kvm_is_ready_for_interrupt_injection
return run->ready_for_interrupt_injection;
}

+int kvm_is_ready_for_nmi_injection(kvm_context_t kvm, int vcpu)
+{
+#ifdef KVM_CAP_NMI
+ struct kvm_run *run = kvm->run[vcpu];
+
+ return run->ready_for_nmi_injection;
+#else
+ return 0;
+#endif
+}
+
int kvm_run(kvm_context_t kvm, int vcpu)
{
int r;
@@ -842,6 +858,9 @@ int kvm_run(kvm_context_t kvm, int vcpu)
struct kvm_run *run = kvm->run[vcpu];

again:
+#ifdef KVM_CAP_NMI
+ run->request_nmi_window = try_push_nmi(kvm);
+#endif
#if !defined(__s390__)
if (!kvm->irqchip_in_kernel)
run->request_interrupt_window = try_push_interrupts(kvm);
@@ -917,6 +936,9 @@ again:
r = handle_halt(kvm, vcpu);
break;
case KVM_EXIT_IRQ_WINDOW_OPEN:
+#ifdef KVM_CAP_NMI
+ case KVM_EXIT_NMI_WINDOW_OPEN:
+#endif
break;
case KVM_EXIT_SHUTDOWN:
r = handle_shutdown(kvm, vcpu);
@@ -1001,6 +1023,15 @@ int kvm_has_sync_mmu(kvm_context_t kvm)
return r;
}

+int kvm_inject_nmi(kvm_context_t kvm, int vcpu)
+{
+#ifdef KVM_CAP_NMI
+ return ioctl(kvm->vcpu_fd[vcpu], KVM_NMI);
+#else
+ return -ENOSYS;
+#endif
+}
+
int kvm_init_coalesced_mmio(kvm_context_t kvm)
{
int r = 0;
Index: b/libkvm/libkvm.h
===================================================================
--- a/libkvm/libkvm.h
+++ b/libkvm/libkvm.h
@@ -66,6 +66,7 @@ struct kvm_callbacks {
int (*shutdown)(void *opaque, int vcpu);
int (*io_window)(void *opaque);
int (*try_push_interrupts)(void *opaque);
+ int (*try_push_nmi)(void *opaque);
void (*post_kvm_run)(void *opaque, int vcpu);
int (*pre_kvm_run)(void *opaque, int vcpu);
int (*tpr_access)(void *opaque, int vcpu, uint64_t rip, int is_write);
@@ -216,6 +217,17 @@ uint64_t kvm_get_apic_base(kvm_context_t
int kvm_is_ready_for_interrupt_injection(kvm_context_t kvm, int vcpu);

/*!
+ * \brief Check if a vcpu is ready for NMI injection
+ *
+ * This checks if vcpu is not already running in NMI context.
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param vcpu Which virtual CPU should get dumped
+ * \return boolean indicating NMI injection readiness
+ */
+int kvm_is_ready_for_nmi_injection(kvm_context_t kvm, int vcpu);
+
+/*!
* \brief Read VCPU registers
*
* This gets the GP registers from the VCPU and outputs them
@@ -579,6 +591,17 @@ int kvm_set_lapic(kvm_context_t kvm, int

#endif

+/*!
+ * \brief Simulate an NMI
+ *
+ * This allows you to simulate a non-maskable interrupt.
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param vcpu Which virtual CPU should get dumped
+ * \return 0 on success
+ */
+int kvm_inject_nmi(kvm_context_t kvm, int vcpu);
+
#endif

/*!
Index: b/qemu/qemu-kvm-x86.c
===================================================================
--- a/qemu/qemu-kvm-x86.c
+++ b/qemu/qemu-kvm-x86.c
@@ -598,7 +598,8 @@ int kvm_arch_halt(void *opaque, int vcpu
CPUState *env = cpu_single_env;

if (!((env->interrupt_request & CPU_INTERRUPT_HARD) &&
- (env->eflags & IF_MASK))) {
+ (env->eflags & IF_MASK)) &&
+ !(env->interrupt_request & CPU_INTERRUPT_NMI)) {
env->halted = 1;
env->exception_index = EXCP_HLT;
}
@@ -627,8 +628,9 @@ void kvm_arch_post_kvm_run(void *opaque,

int kvm_arch_has_work(CPUState *env)
{
- if ((env->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_EXIT)) &&
- (env->eflags & IF_MASK))
+ if (((env->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_EXIT)) &&
+ (env->eflags & IF_MASK)) ||
+ (env->interrupt_request & CPU_INTERRUPT_NMI))
return 1;
return 0;
}
@@ -653,6 +655,24 @@ int kvm_arch_try_push_interrupts(void *o
return (env->interrupt_request & CPU_INTERRUPT_HARD) != 0;
}

+int kvm_arch_try_push_nmi(void *opaque)
+{
+ CPUState *env = cpu_single_env;
+ int r;
+
+ if (likely(!(env->interrupt_request & CPU_INTERRUPT_NMI)))
+ return 0;
+
+ if (kvm_is_ready_for_nmi_injection(kvm_context, env->cpu_index)) {
+ env->interrupt_request &= ~CPU_INTERRUPT_NMI;
+ r = kvm_inject_nmi(kvm_context, env->cpu_index);
+ if (r < 0)
+ printf("cpu %d fail inject NMI\n", env->cpu_index);
+ }
+
+ return (env->interrupt_request & CPU_INTERRUPT_NMI) != 0;
+}
+
void kvm_arch_update_regs_for_sipi(CPUState *env)
{
SegmentCache cs = env->segs[R_CS];
Index: b/qemu/qemu-kvm.c
===================================================================
--- a/qemu/qemu-kvm.c
+++ b/qemu/qemu-kvm.c
@@ -167,6 +167,11 @@ static int try_push_interrupts(void *opa
return kvm_arch_try_push_interrupts(opaque);
}

+static int try_push_nmi(void *opaque)
+{
+ return kvm_arch_try_push_nmi(opaque);
+}
+
static void post_kvm_run(void *opaque, int vcpu)
{

@@ -398,7 +403,7 @@ static int kvm_main_loop_cpu(CPUState *e
while (1) {
while (!has_work(env))
kvm_main_loop_wait(env, 1000);
- if (env->interrupt_request & CPU_INTERRUPT_HARD)
+ if (env->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI))
env->halted = 0;
if (!kvm_irqchip_in_kernel(kvm_context) && info->sipi_needed)
update_regs_for_sipi(env);
@@ -724,6 +729,7 @@ static struct kvm_callbacks qemu_kvm_ops
.shutdown = kvm_shutdown,
.io_window = kvm_io_window,
.try_push_interrupts = try_push_interrupts,
+ .try_push_nmi = try_push_nmi,
.post_kvm_run = post_kvm_run,
.pre_kvm_run = pre_kvm_run,
#ifdef TARGET_I386
Index: b/qemu/qemu-kvm.h
===================================================================
--- a/qemu/qemu-kvm.h
+++ b/qemu/qemu-kvm.h
@@ -57,6 +57,7 @@ void kvm_arch_pre_kvm_run(void *opaque,
void kvm_arch_post_kvm_run(void *opaque, int vcpu);
int kvm_arch_has_work(CPUState *env);
int kvm_arch_try_push_interrupts(void *opaque);
+int kvm_arch_try_push_nmi(void *opaque);
void kvm_arch_update_regs_for_sipi(CPUState *env);
void kvm_arch_cpu_reset(CPUState *env);