diff options
author | Alexander Graf <agraf@suse.de> | 2009-06-15 09:21:25 -0400 |
---|---|---|
committer | Avi Kivity <avi@redhat.com> | 2009-09-10 01:32:59 -0400 |
commit | 219b65dcf6c0bad83d51bfa12e25891c02de2414 (patch) | |
tree | 1117880afaa755b936949701400401b01f09c5fb | |
parent | ff092385e8285c03d8b148f42f46f98c5f4becd5 (diff) |
KVM: SVM: Improve nested interrupt injection
While trying to get Hyper-V running, I realized that the interrupt injection
mechanisms that are in place right now are not 100% correct.
This patch makes nested SVM's interrupt injection behave more like on a
real machine.
Signed-off-by: Alexander Graf <agraf@suse.de>
Signed-off-by: Avi Kivity <avi@redhat.com>
-rw-r--r-- | arch/x86/kvm/svm.c | 39 |
1 files changed, 24 insertions, 15 deletions
diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c index be699795d70f..456666183770 100644 --- a/arch/x86/kvm/svm.c +++ b/arch/x86/kvm/svm.c | |||
@@ -1612,7 +1612,8 @@ static int nested_svm_vmexit_real(struct vcpu_svm *svm, void *arg1, | |||
1612 | /* Kill any pending exceptions */ | 1612 | /* Kill any pending exceptions */ |
1613 | if (svm->vcpu.arch.exception.pending == true) | 1613 | if (svm->vcpu.arch.exception.pending == true) |
1614 | nsvm_printk("WARNING: Pending Exception\n"); | 1614 | nsvm_printk("WARNING: Pending Exception\n"); |
1615 | svm->vcpu.arch.exception.pending = false; | 1615 | kvm_clear_exception_queue(&svm->vcpu); |
1616 | kvm_clear_interrupt_queue(&svm->vcpu); | ||
1616 | 1617 | ||
1617 | /* Restore selected save entries */ | 1618 | /* Restore selected save entries */ |
1618 | svm->vmcb->save.es = hsave->save.es; | 1619 | svm->vmcb->save.es = hsave->save.es; |
@@ -1680,7 +1681,8 @@ static int nested_svm_vmrun(struct vcpu_svm *svm, void *arg1, | |||
1680 | svm->nested_vmcb = svm->vmcb->save.rax; | 1681 | svm->nested_vmcb = svm->vmcb->save.rax; |
1681 | 1682 | ||
1682 | /* Clear internal status */ | 1683 | /* Clear internal status */ |
1683 | svm->vcpu.arch.exception.pending = false; | 1684 | kvm_clear_exception_queue(&svm->vcpu); |
1685 | kvm_clear_interrupt_queue(&svm->vcpu); | ||
1684 | 1686 | ||
1685 | /* Save the old vmcb, so we don't need to pick what we save, but | 1687 | /* Save the old vmcb, so we don't need to pick what we save, but |
1686 | can restore everything when a VMEXIT occurs */ | 1688 | can restore everything when a VMEXIT occurs */ |
@@ -2362,21 +2364,14 @@ static inline void svm_inject_irq(struct vcpu_svm *svm, int irq) | |||
2362 | ((/*control->int_vector >> 4*/ 0xf) << V_INTR_PRIO_SHIFT); | 2364 | ((/*control->int_vector >> 4*/ 0xf) << V_INTR_PRIO_SHIFT); |
2363 | } | 2365 | } |
2364 | 2366 | ||
2365 | static void svm_queue_irq(struct kvm_vcpu *vcpu, unsigned nr) | ||
2366 | { | ||
2367 | struct vcpu_svm *svm = to_svm(vcpu); | ||
2368 | |||
2369 | svm->vmcb->control.event_inj = nr | | ||
2370 | SVM_EVTINJ_VALID | SVM_EVTINJ_TYPE_INTR; | ||
2371 | } | ||
2372 | |||
2373 | static void svm_set_irq(struct kvm_vcpu *vcpu) | 2367 | static void svm_set_irq(struct kvm_vcpu *vcpu) |
2374 | { | 2368 | { |
2375 | struct vcpu_svm *svm = to_svm(vcpu); | 2369 | struct vcpu_svm *svm = to_svm(vcpu); |
2376 | 2370 | ||
2377 | nested_svm_intr(svm); | 2371 | BUG_ON(!(svm->vcpu.arch.hflags & HF_GIF_MASK)); |
2378 | 2372 | ||
2379 | svm_queue_irq(vcpu, vcpu->arch.interrupt.nr); | 2373 | svm->vmcb->control.event_inj = vcpu->arch.interrupt.nr | |
2374 | SVM_EVTINJ_VALID | SVM_EVTINJ_TYPE_INTR; | ||
2380 | } | 2375 | } |
2381 | 2376 | ||
2382 | static void update_cr8_intercept(struct kvm_vcpu *vcpu, int tpr, int irr) | 2377 | static void update_cr8_intercept(struct kvm_vcpu *vcpu, int tpr, int irr) |
@@ -2404,13 +2399,25 @@ static int svm_interrupt_allowed(struct kvm_vcpu *vcpu) | |||
2404 | struct vmcb *vmcb = svm->vmcb; | 2399 | struct vmcb *vmcb = svm->vmcb; |
2405 | return (vmcb->save.rflags & X86_EFLAGS_IF) && | 2400 | return (vmcb->save.rflags & X86_EFLAGS_IF) && |
2406 | !(vmcb->control.int_state & SVM_INTERRUPT_SHADOW_MASK) && | 2401 | !(vmcb->control.int_state & SVM_INTERRUPT_SHADOW_MASK) && |
2407 | (svm->vcpu.arch.hflags & HF_GIF_MASK); | 2402 | (svm->vcpu.arch.hflags & HF_GIF_MASK) && |
2403 | !is_nested(svm); | ||
2408 | } | 2404 | } |
2409 | 2405 | ||
2410 | static void enable_irq_window(struct kvm_vcpu *vcpu) | 2406 | static void enable_irq_window(struct kvm_vcpu *vcpu) |
2411 | { | 2407 | { |
2412 | svm_set_vintr(to_svm(vcpu)); | 2408 | struct vcpu_svm *svm = to_svm(vcpu); |
2413 | svm_inject_irq(to_svm(vcpu), 0x0); | 2409 | nsvm_printk("Trying to open IRQ window\n"); |
2410 | |||
2411 | nested_svm_intr(svm); | ||
2412 | |||
2413 | /* In case GIF=0 we can't rely on the CPU to tell us when | ||
2414 | * GIF becomes 1, because that's a separate STGI/VMRUN intercept. | ||
2415 | * The next time we get that intercept, this function will be | ||
2416 | * called again though and we'll get the vintr intercept. */ | ||
2417 | if (svm->vcpu.arch.hflags & HF_GIF_MASK) { | ||
2418 | svm_set_vintr(svm); | ||
2419 | svm_inject_irq(svm, 0x0); | ||
2420 | } | ||
2414 | } | 2421 | } |
2415 | 2422 | ||
2416 | static void enable_nmi_window(struct kvm_vcpu *vcpu) | 2423 | static void enable_nmi_window(struct kvm_vcpu *vcpu) |
@@ -2489,6 +2496,8 @@ static void svm_complete_interrupts(struct vcpu_svm *svm) | |||
2489 | case SVM_EXITINTINFO_TYPE_EXEPT: | 2496 | case SVM_EXITINTINFO_TYPE_EXEPT: |
2490 | /* In case of software exception do not reinject an exception | 2497 | /* In case of software exception do not reinject an exception |
2491 | vector, but re-execute and instruction instead */ | 2498 | vector, but re-execute and instruction instead */ |
2499 | if (is_nested(svm)) | ||
2500 | break; | ||
2492 | if (kvm_exception_is_soft(vector)) | 2501 | if (kvm_exception_is_soft(vector)) |
2493 | break; | 2502 | break; |
2494 | if (exitintinfo & SVM_EXITINTINFO_VALID_ERR) { | 2503 | if (exitintinfo & SVM_EXITINTINFO_VALID_ERR) { |