diff options
author | Sheng Yang <sheng.yang@intel.com> | 2008-05-15 06:23:25 -0400 |
---|---|---|
committer | Avi Kivity <avi@qumranet.com> | 2008-07-20 05:42:26 -0400 |
commit | f08864b42a45581a64558aa5b6b673c77b97ee5d (patch) | |
tree | d104bf34c951beffebb7c2402329f236a7bad7ad /arch/x86/kvm/vmx.c | |
parent | 3419ffc8e45a5344abc87684cbca6cdc5c9c8a01 (diff) |
KVM: VMX: Enable NMI with in-kernel irqchip
Signed-off-by: Sheng Yang <sheng.yang@intel.com>
Signed-off-by: Avi Kivity <avi@qumranet.com>
Diffstat (limited to 'arch/x86/kvm/vmx.c')
-rw-r--r-- | arch/x86/kvm/vmx.c | 124 |
1 files changed, 106 insertions, 18 deletions
diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index b99bb37e5dec..1bb994657208 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c | |||
@@ -264,6 +264,11 @@ static inline int cpu_has_vmx_vpid(void) | |||
264 | SECONDARY_EXEC_ENABLE_VPID); | 264 | SECONDARY_EXEC_ENABLE_VPID); |
265 | } | 265 | } |
266 | 266 | ||
267 | static inline int cpu_has_virtual_nmis(void) | ||
268 | { | ||
269 | return vmcs_config.pin_based_exec_ctrl & PIN_BASED_VIRTUAL_NMIS; | ||
270 | } | ||
271 | |||
267 | static int __find_msr_index(struct vcpu_vmx *vmx, u32 msr) | 272 | static int __find_msr_index(struct vcpu_vmx *vmx, u32 msr) |
268 | { | 273 | { |
269 | int i; | 274 | int i; |
@@ -1088,7 +1093,7 @@ static __init int setup_vmcs_config(struct vmcs_config *vmcs_conf) | |||
1088 | u32 _vmentry_control = 0; | 1093 | u32 _vmentry_control = 0; |
1089 | 1094 | ||
1090 | min = PIN_BASED_EXT_INTR_MASK | PIN_BASED_NMI_EXITING; | 1095 | min = PIN_BASED_EXT_INTR_MASK | PIN_BASED_NMI_EXITING; |
1091 | opt = 0; | 1096 | opt = PIN_BASED_VIRTUAL_NMIS; |
1092 | if (adjust_vmx_controls(min, opt, MSR_IA32_VMX_PINBASED_CTLS, | 1097 | if (adjust_vmx_controls(min, opt, MSR_IA32_VMX_PINBASED_CTLS, |
1093 | &_pin_based_exec_control) < 0) | 1098 | &_pin_based_exec_control) < 0) |
1094 | return -EIO; | 1099 | return -EIO; |
@@ -2130,6 +2135,13 @@ static void vmx_inject_irq(struct kvm_vcpu *vcpu, int irq) | |||
2130 | irq | INTR_TYPE_EXT_INTR | INTR_INFO_VALID_MASK); | 2135 | irq | INTR_TYPE_EXT_INTR | INTR_INFO_VALID_MASK); |
2131 | } | 2136 | } |
2132 | 2137 | ||
2138 | static void vmx_inject_nmi(struct kvm_vcpu *vcpu) | ||
2139 | { | ||
2140 | vmcs_write32(VM_ENTRY_INTR_INFO_FIELD, | ||
2141 | INTR_TYPE_NMI_INTR | INTR_INFO_VALID_MASK | NMI_VECTOR); | ||
2142 | vcpu->arch.nmi_pending = 0; | ||
2143 | } | ||
2144 | |||
2133 | static void kvm_do_inject_irq(struct kvm_vcpu *vcpu) | 2145 | static void kvm_do_inject_irq(struct kvm_vcpu *vcpu) |
2134 | { | 2146 | { |
2135 | int word_index = __ffs(vcpu->arch.irq_summary); | 2147 | int word_index = __ffs(vcpu->arch.irq_summary); |
@@ -2653,6 +2665,19 @@ static int handle_ept_violation(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run) | |||
2653 | return 1; | 2665 | return 1; |
2654 | } | 2666 | } |
2655 | 2667 | ||
2668 | static int handle_nmi_window(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run) | ||
2669 | { | ||
2670 | u32 cpu_based_vm_exec_control; | ||
2671 | |||
2672 | /* clear pending NMI */ | ||
2673 | cpu_based_vm_exec_control = vmcs_read32(CPU_BASED_VM_EXEC_CONTROL); | ||
2674 | cpu_based_vm_exec_control &= ~CPU_BASED_VIRTUAL_NMI_PENDING; | ||
2675 | vmcs_write32(CPU_BASED_VM_EXEC_CONTROL, cpu_based_vm_exec_control); | ||
2676 | ++vcpu->stat.nmi_window_exits; | ||
2677 | |||
2678 | return 1; | ||
2679 | } | ||
2680 | |||
2656 | /* | 2681 | /* |
2657 | * The exit handlers return 1 if the exit was handled fully and guest execution | 2682 | * The exit handlers return 1 if the exit was handled fully and guest execution |
2658 | * may resume. Otherwise they set the kvm_run parameter to indicate what needs | 2683 | * may resume. Otherwise they set the kvm_run parameter to indicate what needs |
@@ -2663,6 +2688,7 @@ static int (*kvm_vmx_exit_handlers[])(struct kvm_vcpu *vcpu, | |||
2663 | [EXIT_REASON_EXCEPTION_NMI] = handle_exception, | 2688 | [EXIT_REASON_EXCEPTION_NMI] = handle_exception, |
2664 | [EXIT_REASON_EXTERNAL_INTERRUPT] = handle_external_interrupt, | 2689 | [EXIT_REASON_EXTERNAL_INTERRUPT] = handle_external_interrupt, |
2665 | [EXIT_REASON_TRIPLE_FAULT] = handle_triple_fault, | 2690 | [EXIT_REASON_TRIPLE_FAULT] = handle_triple_fault, |
2691 | [EXIT_REASON_NMI_WINDOW] = handle_nmi_window, | ||
2666 | [EXIT_REASON_IO_INSTRUCTION] = handle_io, | 2692 | [EXIT_REASON_IO_INSTRUCTION] = handle_io, |
2667 | [EXIT_REASON_CR_ACCESS] = handle_cr, | 2693 | [EXIT_REASON_CR_ACCESS] = handle_cr, |
2668 | [EXIT_REASON_DR_ACCESS] = handle_dr, | 2694 | [EXIT_REASON_DR_ACCESS] = handle_dr, |
@@ -2750,17 +2776,52 @@ static void enable_irq_window(struct kvm_vcpu *vcpu) | |||
2750 | vmcs_write32(CPU_BASED_VM_EXEC_CONTROL, cpu_based_vm_exec_control); | 2776 | vmcs_write32(CPU_BASED_VM_EXEC_CONTROL, cpu_based_vm_exec_control); |
2751 | } | 2777 | } |
2752 | 2778 | ||
2779 | static void enable_nmi_window(struct kvm_vcpu *vcpu) | ||
2780 | { | ||
2781 | u32 cpu_based_vm_exec_control; | ||
2782 | |||
2783 | if (!cpu_has_virtual_nmis()) | ||
2784 | return; | ||
2785 | |||
2786 | cpu_based_vm_exec_control = vmcs_read32(CPU_BASED_VM_EXEC_CONTROL); | ||
2787 | cpu_based_vm_exec_control |= CPU_BASED_VIRTUAL_NMI_PENDING; | ||
2788 | vmcs_write32(CPU_BASED_VM_EXEC_CONTROL, cpu_based_vm_exec_control); | ||
2789 | } | ||
2790 | |||
2791 | static int vmx_nmi_enabled(struct kvm_vcpu *vcpu) | ||
2792 | { | ||
2793 | u32 guest_intr = vmcs_read32(GUEST_INTERRUPTIBILITY_INFO); | ||
2794 | return !(guest_intr & (GUEST_INTR_STATE_NMI | | ||
2795 | GUEST_INTR_STATE_MOV_SS | | ||
2796 | GUEST_INTR_STATE_STI)); | ||
2797 | } | ||
2798 | |||
2799 | static int vmx_irq_enabled(struct kvm_vcpu *vcpu) | ||
2800 | { | ||
2801 | u32 guest_intr = vmcs_read32(GUEST_INTERRUPTIBILITY_INFO); | ||
2802 | return (!(guest_intr & (GUEST_INTR_STATE_MOV_SS | | ||
2803 | GUEST_INTR_STATE_STI)) && | ||
2804 | (vmcs_readl(GUEST_RFLAGS) & X86_EFLAGS_IF)); | ||
2805 | } | ||
2806 | |||
2807 | static void enable_intr_window(struct kvm_vcpu *vcpu) | ||
2808 | { | ||
2809 | if (vcpu->arch.nmi_pending) | ||
2810 | enable_nmi_window(vcpu); | ||
2811 | else if (kvm_cpu_has_interrupt(vcpu)) | ||
2812 | enable_irq_window(vcpu); | ||
2813 | } | ||
2814 | |||
2753 | static void vmx_intr_assist(struct kvm_vcpu *vcpu) | 2815 | static void vmx_intr_assist(struct kvm_vcpu *vcpu) |
2754 | { | 2816 | { |
2755 | struct vcpu_vmx *vmx = to_vmx(vcpu); | 2817 | struct vcpu_vmx *vmx = to_vmx(vcpu); |
2756 | u32 idtv_info_field, intr_info_field; | 2818 | u32 idtv_info_field, intr_info_field, exit_intr_info_field; |
2757 | int has_ext_irq, interrupt_window_open; | ||
2758 | int vector; | 2819 | int vector; |
2759 | 2820 | ||
2760 | update_tpr_threshold(vcpu); | 2821 | update_tpr_threshold(vcpu); |
2761 | 2822 | ||
2762 | has_ext_irq = kvm_cpu_has_interrupt(vcpu); | ||
2763 | intr_info_field = vmcs_read32(VM_ENTRY_INTR_INFO_FIELD); | 2823 | intr_info_field = vmcs_read32(VM_ENTRY_INTR_INFO_FIELD); |
2824 | exit_intr_info_field = vmcs_read32(VM_EXIT_INTR_INFO); | ||
2764 | idtv_info_field = vmx->idt_vectoring_info; | 2825 | idtv_info_field = vmx->idt_vectoring_info; |
2765 | if (intr_info_field & INTR_INFO_VALID_MASK) { | 2826 | if (intr_info_field & INTR_INFO_VALID_MASK) { |
2766 | if (idtv_info_field & INTR_INFO_VALID_MASK) { | 2827 | if (idtv_info_field & INTR_INFO_VALID_MASK) { |
@@ -2768,8 +2829,7 @@ static void vmx_intr_assist(struct kvm_vcpu *vcpu) | |||
2768 | if (printk_ratelimit()) | 2829 | if (printk_ratelimit()) |
2769 | printk(KERN_ERR "Fault when IDT_Vectoring\n"); | 2830 | printk(KERN_ERR "Fault when IDT_Vectoring\n"); |
2770 | } | 2831 | } |
2771 | if (has_ext_irq) | 2832 | enable_intr_window(vcpu); |
2772 | enable_irq_window(vcpu); | ||
2773 | return; | 2833 | return; |
2774 | } | 2834 | } |
2775 | if (unlikely(idtv_info_field & INTR_INFO_VALID_MASK)) { | 2835 | if (unlikely(idtv_info_field & INTR_INFO_VALID_MASK)) { |
@@ -2779,30 +2839,56 @@ static void vmx_intr_assist(struct kvm_vcpu *vcpu) | |||
2779 | u8 vect = idtv_info_field & VECTORING_INFO_VECTOR_MASK; | 2839 | u8 vect = idtv_info_field & VECTORING_INFO_VECTOR_MASK; |
2780 | 2840 | ||
2781 | vmx_inject_irq(vcpu, vect); | 2841 | vmx_inject_irq(vcpu, vect); |
2782 | if (unlikely(has_ext_irq)) | 2842 | enable_intr_window(vcpu); |
2783 | enable_irq_window(vcpu); | ||
2784 | return; | 2843 | return; |
2785 | } | 2844 | } |
2786 | 2845 | ||
2787 | KVMTRACE_1D(REDELIVER_EVT, vcpu, idtv_info_field, handler); | 2846 | KVMTRACE_1D(REDELIVER_EVT, vcpu, idtv_info_field, handler); |
2788 | 2847 | ||
2789 | vmcs_write32(VM_ENTRY_INTR_INFO_FIELD, idtv_info_field); | 2848 | /* |
2849 | * SDM 3: 25.7.1.2 | ||
2850 | * Clear bit "block by NMI" before VM entry if a NMI delivery | ||
2851 | * faulted. | ||
2852 | */ | ||
2853 | if ((idtv_info_field & VECTORING_INFO_TYPE_MASK) | ||
2854 | == INTR_TYPE_NMI_INTR && cpu_has_virtual_nmis()) | ||
2855 | vmcs_write32(GUEST_INTERRUPTIBILITY_INFO, | ||
2856 | vmcs_read32(GUEST_INTERRUPTIBILITY_INFO) & | ||
2857 | ~GUEST_INTR_STATE_NMI); | ||
2858 | |||
2859 | vmcs_write32(VM_ENTRY_INTR_INFO_FIELD, idtv_info_field | ||
2860 | & ~INTR_INFO_RESVD_BITS_MASK); | ||
2790 | vmcs_write32(VM_ENTRY_INSTRUCTION_LEN, | 2861 | vmcs_write32(VM_ENTRY_INSTRUCTION_LEN, |
2791 | vmcs_read32(VM_EXIT_INSTRUCTION_LEN)); | 2862 | vmcs_read32(VM_EXIT_INSTRUCTION_LEN)); |
2792 | 2863 | ||
2793 | if (unlikely(idtv_info_field & INTR_INFO_DELIVER_CODE_MASK)) | 2864 | if (unlikely(idtv_info_field & INTR_INFO_DELIVER_CODE_MASK)) |
2794 | vmcs_write32(VM_ENTRY_EXCEPTION_ERROR_CODE, | 2865 | vmcs_write32(VM_ENTRY_EXCEPTION_ERROR_CODE, |
2795 | vmcs_read32(IDT_VECTORING_ERROR_CODE)); | 2866 | vmcs_read32(IDT_VECTORING_ERROR_CODE)); |
2796 | if (unlikely(has_ext_irq)) | 2867 | enable_intr_window(vcpu); |
2797 | enable_irq_window(vcpu); | ||
2798 | return; | 2868 | return; |
2799 | } | 2869 | } |
2800 | if (!has_ext_irq) | 2870 | if (cpu_has_virtual_nmis()) { |
2871 | /* | ||
2872 | * SDM 3: 25.7.1.2 | ||
2873 | * Re-set bit "block by NMI" before VM entry if vmexit caused by | ||
2874 | * a guest IRET fault. | ||
2875 | */ | ||
2876 | if ((exit_intr_info_field & INTR_INFO_UNBLOCK_NMI) && | ||
2877 | (exit_intr_info_field & INTR_INFO_VECTOR_MASK) != 8) | ||
2878 | vmcs_write32(GUEST_INTERRUPTIBILITY_INFO, | ||
2879 | vmcs_read32(GUEST_INTERRUPTIBILITY_INFO) | | ||
2880 | GUEST_INTR_STATE_NMI); | ||
2881 | else if (vcpu->arch.nmi_pending) { | ||
2882 | if (vmx_nmi_enabled(vcpu)) | ||
2883 | vmx_inject_nmi(vcpu); | ||
2884 | enable_intr_window(vcpu); | ||
2885 | return; | ||
2886 | } | ||
2887 | |||
2888 | } | ||
2889 | if (!kvm_cpu_has_interrupt(vcpu)) | ||
2801 | return; | 2890 | return; |
2802 | interrupt_window_open = | 2891 | if (vmx_irq_enabled(vcpu)) { |
2803 | ((vmcs_readl(GUEST_RFLAGS) & X86_EFLAGS_IF) && | ||
2804 | (vmcs_read32(GUEST_INTERRUPTIBILITY_INFO) & 3) == 0); | ||
2805 | if (interrupt_window_open) { | ||
2806 | vector = kvm_cpu_get_interrupt(vcpu); | 2892 | vector = kvm_cpu_get_interrupt(vcpu); |
2807 | vmx_inject_irq(vcpu, vector); | 2893 | vmx_inject_irq(vcpu, vector); |
2808 | kvm_timer_intr_post(vcpu, vector); | 2894 | kvm_timer_intr_post(vcpu, vector); |
@@ -2963,7 +3049,8 @@ static void vmx_vcpu_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run) | |||
2963 | fixup_rmode_irq(vmx); | 3049 | fixup_rmode_irq(vmx); |
2964 | 3050 | ||
2965 | vcpu->arch.interrupt_window_open = | 3051 | vcpu->arch.interrupt_window_open = |
2966 | (vmcs_read32(GUEST_INTERRUPTIBILITY_INFO) & 3) == 0; | 3052 | (vmcs_read32(GUEST_INTERRUPTIBILITY_INFO) & |
3053 | (GUEST_INTR_STATE_STI | GUEST_INTR_STATE_MOV_SS)) == 0; | ||
2967 | 3054 | ||
2968 | asm("mov %0, %%ds; mov %0, %%es" : : "r"(__USER_DS)); | 3055 | asm("mov %0, %%ds; mov %0, %%es" : : "r"(__USER_DS)); |
2969 | vmx->launched = 1; | 3056 | vmx->launched = 1; |
@@ -2971,7 +3058,8 @@ static void vmx_vcpu_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run) | |||
2971 | intr_info = vmcs_read32(VM_EXIT_INTR_INFO); | 3058 | intr_info = vmcs_read32(VM_EXIT_INTR_INFO); |
2972 | 3059 | ||
2973 | /* We need to handle NMIs before interrupts are enabled */ | 3060 | /* We need to handle NMIs before interrupts are enabled */ |
2974 | if ((intr_info & INTR_INFO_INTR_TYPE_MASK) == 0x200) { /* nmi */ | 3061 | if ((intr_info & INTR_INFO_INTR_TYPE_MASK) == 0x200 && |
3062 | (intr_info & INTR_INFO_VALID_MASK)) { | ||
2975 | KVMTRACE_0D(NMI, vcpu, handler); | 3063 | KVMTRACE_0D(NMI, vcpu, handler); |
2976 | asm("int $2"); | 3064 | asm("int $2"); |
2977 | } | 3065 | } |