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 | } |
