This adds the required bit to retrieve and set the so far hidden NMI pending and NMI masked states of the KVM kernel side. It also extends CPU VMState for proper saving/restoring. We can now savely reset a VM while NMIs are on the fly, and we can live migrate etc. too. Fortunately, the probability that this deficit bit normal VMs in practice was very low. Signed-off-by: Jan Kiszka <jan.kiszka@xxxxxxxxxxx> --- qemu-kvm-x86.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++++ target-i386/cpu.h | 1 + target-i386/machine.c | 1 + 3 files changed, 54 insertions(+), 0 deletions(-) diff --git a/qemu-kvm-x86.c b/qemu-kvm-x86.c index acb1b91..86fd341 100644 --- a/qemu-kvm-x86.c +++ b/qemu-kvm-x86.c @@ -901,6 +901,53 @@ static void get_seg(SegmentCache *lhs, const struct kvm_segment *rhs) | (rhs->avl * DESC_AVL_MASK); } +static void kvm_get_nmi_state(CPUState *env) +{ +#ifdef KVM_CAP_VCPU_STATE + kvm_vcpu_context_t vcpu = env->kvm_cpu_state.vcpu_ctx; + struct { + struct kvm_vcpu_state header; + struct kvm_vcpu_substate substates[1]; + } request; + struct kvm_nmi_state nmi_state; + int r; + + request.header.nsubstates = 1; + request.header.substates[0].type = KVM_X86_VCPU_NMI; + request.header.substates[0].offset = (size_t)&nmi_state - (size_t)&request; + r = ioctl(vcpu->fd, KVM_GET_VCPU_STATE, &request); + if (r == 0) { + env->nmi_pending = nmi_state.pending; + if (nmi_state.masked) { + env->hflags2 |= HF2_NMI_MASK; + } else { + env->hflags2 &= ~HF2_NMI_MASK; + } + } +#endif + env->nmi_pending = 0; + env->hflags2 &= ~HF2_NMI_MASK; +} + +static void kvm_set_nmi_state(CPUState *env) +{ +#ifdef KVM_CAP_VCPU_STATE + kvm_vcpu_context_t vcpu = env->kvm_cpu_state.vcpu_ctx; + struct { + struct kvm_vcpu_state header; + struct kvm_vcpu_substate substates[1]; + } request; + struct kvm_nmi_state nmi_state; + + request.header.nsubstates = 1; + request.header.substates[0].type = KVM_X86_VCPU_NMI; + request.header.substates[0].offset = (size_t)&nmi_state - (size_t)&request; + nmi_state.pending = env->nmi_pending; + nmi_state.masked = !!(env->hflags2 & HF2_NMI_MASK); + ioctl(vcpu->fd, KVM_SET_VCPU_STATE, &request); +#endif +} + void kvm_arch_load_regs(CPUState *env) { struct kvm_regs regs; @@ -1010,6 +1057,8 @@ void kvm_arch_load_regs(CPUState *env) rc = kvm_set_msrs(env->kvm_cpu_state.vcpu_ctx, msrs, n); if (rc == -1) perror("kvm_set_msrs FAILED"); + + kvm_set_nmi_state(env); } void kvm_load_tsc(CPUState *env) @@ -1195,6 +1244,8 @@ void kvm_arch_save_regs(CPUState *env) return; } } + + kvm_get_nmi_state(env); } static void do_cpuid_ent(struct kvm_cpuid_entry2 *e, uint32_t function, @@ -1438,6 +1489,7 @@ void kvm_arch_push_nmi(void *opaque) void kvm_arch_cpu_reset(CPUState *env) { + env->nmi_pending = 0; kvm_arch_load_regs(env); if (!cpu_is_bsp(env)) { if (kvm_irqchip_in_kernel(kvm_context)) { diff --git a/target-i386/cpu.h b/target-i386/cpu.h index 278d3e3..620822a 100644 --- a/target-i386/cpu.h +++ b/target-i386/cpu.h @@ -709,6 +709,7 @@ typedef struct CPUX86State { /* For KVM */ uint64_t interrupt_bitmap[256 / 64]; uint32_t mp_state; + uint32_t nmi_pending; /* in order to simplify APIC support, we leave this pointer to the user */ diff --git a/target-i386/machine.c b/target-i386/machine.c index e640dad..5c290f3 100644 --- a/target-i386/machine.c +++ b/target-i386/machine.c @@ -469,6 +469,7 @@ const VMStateDescription vmstate_cpu = { VMSTATE_INT32_V(pending_irq_vmstate, CPUState, 9), VMSTATE_UINT32_V(mp_state, CPUState, 9), VMSTATE_UINT64_V(tsc, CPUState, 9), + VMSTATE_UINT32_V(nmi_pending, CPUState, 11), /* MCE */ VMSTATE_UINT64_V(mcg_cap, CPUState, 10), VMSTATE_UINT64_V(mcg_status, CPUState, 10), -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html