diff options
Diffstat (limited to 'arch/x86/kvm/lapic.c')
-rw-r--r-- | arch/x86/kvm/lapic.c | 58 |
1 files changed, 50 insertions, 8 deletions
diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c index 0fc3cab48943..afac68c0815c 100644 --- a/arch/x86/kvm/lapic.c +++ b/arch/x86/kvm/lapic.c | |||
@@ -130,6 +130,11 @@ static inline int apic_lvtt_period(struct kvm_lapic *apic) | |||
130 | return apic_get_reg(apic, APIC_LVTT) & APIC_LVT_TIMER_PERIODIC; | 130 | return apic_get_reg(apic, APIC_LVTT) & APIC_LVT_TIMER_PERIODIC; |
131 | } | 131 | } |
132 | 132 | ||
133 | static inline int apic_lvt_nmi_mode(u32 lvt_val) | ||
134 | { | ||
135 | return (lvt_val & (APIC_MODE_MASK | APIC_LVT_MASKED)) == APIC_DM_NMI; | ||
136 | } | ||
137 | |||
133 | static unsigned int apic_lvt_mask[APIC_LVT_NUM] = { | 138 | static unsigned int apic_lvt_mask[APIC_LVT_NUM] = { |
134 | LVT_MASK | APIC_LVT_TIMER_PERIODIC, /* LVTT */ | 139 | LVT_MASK | APIC_LVT_TIMER_PERIODIC, /* LVTT */ |
135 | LVT_MASK | APIC_MODE_MASK, /* LVTTHMR */ | 140 | LVT_MASK | APIC_MODE_MASK, /* LVTTHMR */ |
@@ -354,6 +359,7 @@ static int __apic_accept_irq(struct kvm_lapic *apic, int delivery_mode, | |||
354 | 359 | ||
355 | case APIC_DM_NMI: | 360 | case APIC_DM_NMI: |
356 | kvm_inject_nmi(vcpu); | 361 | kvm_inject_nmi(vcpu); |
362 | kvm_vcpu_kick(vcpu); | ||
357 | break; | 363 | break; |
358 | 364 | ||
359 | case APIC_DM_INIT: | 365 | case APIC_DM_INIT: |
@@ -380,6 +386,14 @@ static int __apic_accept_irq(struct kvm_lapic *apic, int delivery_mode, | |||
380 | } | 386 | } |
381 | break; | 387 | break; |
382 | 388 | ||
389 | case APIC_DM_EXTINT: | ||
390 | /* | ||
391 | * Should only be called by kvm_apic_local_deliver() with LVT0, | ||
392 | * before NMI watchdog was enabled. Already handled by | ||
393 | * kvm_apic_accept_pic_intr(). | ||
394 | */ | ||
395 | break; | ||
396 | |||
383 | default: | 397 | default: |
384 | printk(KERN_ERR "TODO: unsupported delivery mode %x\n", | 398 | printk(KERN_ERR "TODO: unsupported delivery mode %x\n", |
385 | delivery_mode); | 399 | delivery_mode); |
@@ -663,6 +677,20 @@ static void start_apic_timer(struct kvm_lapic *apic) | |||
663 | apic->timer.period))); | 677 | apic->timer.period))); |
664 | } | 678 | } |
665 | 679 | ||
680 | static void apic_manage_nmi_watchdog(struct kvm_lapic *apic, u32 lvt0_val) | ||
681 | { | ||
682 | int nmi_wd_enabled = apic_lvt_nmi_mode(apic_get_reg(apic, APIC_LVT0)); | ||
683 | |||
684 | if (apic_lvt_nmi_mode(lvt0_val)) { | ||
685 | if (!nmi_wd_enabled) { | ||
686 | apic_debug("Receive NMI setting on APIC_LVT0 " | ||
687 | "for cpu %d\n", apic->vcpu->vcpu_id); | ||
688 | apic->vcpu->kvm->arch.vapics_in_nmi_mode++; | ||
689 | } | ||
690 | } else if (nmi_wd_enabled) | ||
691 | apic->vcpu->kvm->arch.vapics_in_nmi_mode--; | ||
692 | } | ||
693 | |||
666 | static void apic_mmio_write(struct kvm_io_device *this, | 694 | static void apic_mmio_write(struct kvm_io_device *this, |
667 | gpa_t address, int len, const void *data) | 695 | gpa_t address, int len, const void *data) |
668 | { | 696 | { |
@@ -743,10 +771,11 @@ static void apic_mmio_write(struct kvm_io_device *this, | |||
743 | apic_set_reg(apic, APIC_ICR2, val & 0xff000000); | 771 | apic_set_reg(apic, APIC_ICR2, val & 0xff000000); |
744 | break; | 772 | break; |
745 | 773 | ||
774 | case APIC_LVT0: | ||
775 | apic_manage_nmi_watchdog(apic, val); | ||
746 | case APIC_LVTT: | 776 | case APIC_LVTT: |
747 | case APIC_LVTTHMR: | 777 | case APIC_LVTTHMR: |
748 | case APIC_LVTPC: | 778 | case APIC_LVTPC: |
749 | case APIC_LVT0: | ||
750 | case APIC_LVT1: | 779 | case APIC_LVT1: |
751 | case APIC_LVTERR: | 780 | case APIC_LVTERR: |
752 | /* TODO: Check vector */ | 781 | /* TODO: Check vector */ |
@@ -961,12 +990,26 @@ int apic_has_pending_timer(struct kvm_vcpu *vcpu) | |||
961 | return 0; | 990 | return 0; |
962 | } | 991 | } |
963 | 992 | ||
964 | static int __inject_apic_timer_irq(struct kvm_lapic *apic) | 993 | static int kvm_apic_local_deliver(struct kvm_lapic *apic, int lvt_type) |
994 | { | ||
995 | u32 reg = apic_get_reg(apic, lvt_type); | ||
996 | int vector, mode, trig_mode; | ||
997 | |||
998 | if (apic_hw_enabled(apic) && !(reg & APIC_LVT_MASKED)) { | ||
999 | vector = reg & APIC_VECTOR_MASK; | ||
1000 | mode = reg & APIC_MODE_MASK; | ||
1001 | trig_mode = reg & APIC_LVT_LEVEL_TRIGGER; | ||
1002 | return __apic_accept_irq(apic, mode, vector, 1, trig_mode); | ||
1003 | } | ||
1004 | return 0; | ||
1005 | } | ||
1006 | |||
1007 | void kvm_apic_nmi_wd_deliver(struct kvm_vcpu *vcpu) | ||
965 | { | 1008 | { |
966 | int vector; | 1009 | struct kvm_lapic *apic = vcpu->arch.apic; |
967 | 1010 | ||
968 | vector = apic_lvt_vector(apic, APIC_LVTT); | 1011 | if (apic) |
969 | return __apic_accept_irq(apic, APIC_DM_FIXED, vector, 1, 0); | 1012 | kvm_apic_local_deliver(apic, APIC_LVT0); |
970 | } | 1013 | } |
971 | 1014 | ||
972 | static enum hrtimer_restart apic_timer_fn(struct hrtimer *data) | 1015 | static enum hrtimer_restart apic_timer_fn(struct hrtimer *data) |
@@ -1061,9 +1104,8 @@ void kvm_inject_apic_timer_irqs(struct kvm_vcpu *vcpu) | |||
1061 | { | 1104 | { |
1062 | struct kvm_lapic *apic = vcpu->arch.apic; | 1105 | struct kvm_lapic *apic = vcpu->arch.apic; |
1063 | 1106 | ||
1064 | if (apic && apic_lvt_enabled(apic, APIC_LVTT) && | 1107 | if (apic && atomic_read(&apic->timer.pending) > 0) { |
1065 | atomic_read(&apic->timer.pending) > 0) { | 1108 | if (kvm_apic_local_deliver(apic, APIC_LVTT)) |
1066 | if (__inject_apic_timer_irq(apic)) | ||
1067 | atomic_dec(&apic->timer.pending); | 1109 | atomic_dec(&apic->timer.pending); |
1068 | } | 1110 | } |
1069 | } | 1111 | } |