This patch extends the qemu-kvm state sync logic with the event substate from the new VCPU state interface, giving access to yet missing exception, interrupt and NMI states. The patch does not switch the rest of qemu-kvm's code to the new interface as it is expected to be morphed into upstream's version anyway. Instead, a full conversion will be submitted for upstream. Signed-off-by: Jan Kiszka <jan.kiszka@xxxxxxxxxxx> --- Changes from v1: - added support for has_error_code - properly clear exception_index and interrupt_injected in case the kernel does not support KVM_GET_VCPU_STATE qemu-kvm-x86.c | 86 +++++++++++++++++++++++++++++++++++++++++++++++++ target-i386/cpu.h | 5 +++ target-i386/machine.c | 5 +++ 3 files changed, 96 insertions(+), 0 deletions(-) diff --git a/qemu-kvm-x86.c b/qemu-kvm-x86.c index e03a4ba..dc3e6cb 100644 --- a/qemu-kvm-x86.c +++ b/qemu-kvm-x86.c @@ -903,6 +903,82 @@ static void get_seg(SegmentCache *lhs, const struct kvm_segment *rhs) | (rhs->avl * DESC_AVL_MASK); } +static void kvm_get_events(CPUState *env) +{ +#ifdef KVM_CAP_VCPU_STATE + struct { + struct kvm_vcpu_state header; + struct kvm_vcpu_substate substates[1]; + } request; + struct kvm_x86_event_state events; + int r; + + request.header.nsubstates = 1; + request.header.substates[0].type = KVM_X86_VCPU_STATE_EVENTS; + request.header.substates[0].offset = (size_t)&events - (size_t)&request; + r = kvm_vcpu_ioctl(env, KVM_GET_VCPU_STATE, &request); + if (r == 0) { + env->exception_index = + events.exception.injected ? events.exception.nr : -1; + env->has_error_code = events.exception.has_error_code; + env->error_code = events.exception.error_code; + + env->interrupt_injected = + events.interrupt.injected ? events.interrupt.nr : -1; + env->soft_interrupt = events.interrupt.soft; + + env->nmi_injected = events.nmi.injected; + env->nmi_pending = events.nmi.pending; + if (events.nmi.masked) { + env->hflags2 |= HF2_NMI_MASK; + } else { + env->hflags2 &= ~HF2_NMI_MASK; + } + + env->sipi_vector = events.sipi_vector; + + return; + } +#endif + env->exception_index = -1; + env->interrupt_injected = -1; + env->nmi_injected = 0; + env->nmi_pending = 0; + env->hflags2 &= ~HF2_NMI_MASK; +} + +static void kvm_set_events(CPUState *env) +{ +#ifdef KVM_CAP_VCPU_STATE + struct { + struct kvm_vcpu_state header; + struct kvm_vcpu_substate substates[1]; + } request; + struct kvm_x86_event_state events; + + request.header.nsubstates = 1; + request.header.substates[0].type = KVM_X86_VCPU_STATE_EVENTS; + request.header.substates[0].offset = (size_t)&events - (size_t)&request; + + events.exception.injected = (env->exception_index >= 0); + events.exception.nr = env->exception_index; + events.exception.has_error_code = env->has_error_code; + events.exception.error_code = env->error_code; + + events.interrupt.injected = (env->interrupt_injected >= 0); + events.interrupt.nr = env->interrupt_injected; + events.interrupt.soft = env->soft_interrupt; + + events.nmi.injected = env->nmi_injected; + events.nmi.pending = env->nmi_pending; + events.nmi.masked = !!(env->hflags2 & HF2_NMI_MASK); + + events.sipi_vector = env->sipi_vector; + + kvm_vcpu_ioctl(env, KVM_SET_VCPU_STATE, &request); +#endif +} + void kvm_arch_load_regs(CPUState *env) { struct kvm_regs regs; @@ -1019,6 +1095,8 @@ void kvm_arch_load_regs(CPUState *env) rc = kvm_set_msrs(env, msrs, n); if (rc == -1) perror("kvm_set_msrs FAILED"); + + kvm_set_events(env); } void kvm_load_tsc(CPUState *env) @@ -1215,6 +1293,8 @@ void kvm_arch_save_regs(CPUState *env) return; } } + + kvm_get_events(env); } static void do_cpuid_ent(struct kvm_cpuid_entry2 *e, uint32_t function, @@ -1383,7 +1463,10 @@ int kvm_arch_init_vcpu(CPUState *cenv) kvm_tpr_vcpu_start(cenv); #endif + cenv->exception_index = -1; cenv->interrupt_injected = -1; + cenv->nmi_injected = 0; + cenv->nmi_pending = 0; return 0; } @@ -1453,7 +1536,10 @@ void kvm_arch_push_nmi(void *opaque) void kvm_arch_cpu_reset(CPUState *env) { + env->exception_index = -1; env->interrupt_injected = -1; + env->nmi_injected = 0; + env->nmi_pending = 0; kvm_arch_load_regs(env); if (!cpu_is_bsp(env)) { if (kvm_irqchip_in_kernel()) { diff --git a/target-i386/cpu.h b/target-i386/cpu.h index a638e70..31412a8 100644 --- a/target-i386/cpu.h +++ b/target-i386/cpu.h @@ -711,6 +711,11 @@ typedef struct CPUX86State { /* For KVM */ uint32_t mp_state; int32_t interrupt_injected; + uint8_t soft_interrupt; + uint8_t nmi_injected; + uint8_t nmi_pending; + uint8_t has_error_code; + uint32_t sipi_vector; /* 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 6bd447f..1eda7c5 100644 --- a/target-i386/machine.c +++ b/target-i386/machine.c @@ -452,6 +452,11 @@ static const VMStateDescription vmstate_cpu = { VMSTATE_INT32_V(interrupt_injected, CPUState, 9), VMSTATE_UINT32_V(mp_state, CPUState, 9), VMSTATE_UINT64_V(tsc, CPUState, 9), + VMSTATE_UINT8_V(soft_interrupt, CPUState, 11), + VMSTATE_UINT8_V(nmi_injected, CPUState, 11), + VMSTATE_UINT8_V(nmi_pending, CPUState, 11), + VMSTATE_UINT8_V(has_error_code, CPUState, 11), + VMSTATE_UINT32_V(sipi_vector, 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