From mboxrd@z Thu Jan 1 00:00:00 1970 From: Jan Kiszka Subject: [PATCH 3/3] kvm-userspace: Enable user space injected NMI support Date: Mon, 15 Sep 2008 18:59:14 +0200 Message-ID: <48CE9462.1060900@siemens.com> References: <48CE8D24.9070007@siemens.com> Mime-Version: 1.0 Content-Type: text/plain; charset=ISO-8859-15 Content-Transfer-Encoding: 7bit Cc: "Yang, Sheng" , Avi Kivity To: kvm-devel Return-path: Received: from gecko.sbs.de ([194.138.37.40]:17905 "EHLO gecko.sbs.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752703AbYIORA3 (ORCPT ); Mon, 15 Sep 2008 13:00:29 -0400 In-Reply-To: <48CE8D24.9070007@siemens.com> Sender: kvm-owner@vger.kernel.org List-ID: 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 --- 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);