diff options
author | Yang Zhang <yang.z.zhang@intel.com> | 2013-01-24 21:18:49 -0500 |
---|---|---|
committer | Gleb Natapov <gleb@redhat.com> | 2013-01-29 03:47:54 -0500 |
commit | 83d4c286931c9d28c5be21bac3c73a2332cab681 (patch) | |
tree | 524885d4cad7e7f1d98357711eff11b8224dc01e /arch | |
parent | d47510e295c0f82699192a61d715351cf00f65de (diff) |
x86, apicv: add APICv register virtualization support
- APIC read doesn't cause VM-Exit
- APIC write becomes trap-like
Reviewed-by: Marcelo Tosatti <mtosatti@redhat.com>
Signed-off-by: Kevin Tian <kevin.tian@intel.com>
Signed-off-by: Yang Zhang <yang.z.zhang@intel.com>
Signed-off-by: Gleb Natapov <gleb@redhat.com>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/x86/include/asm/vmx.h | 2 | ||||
-rw-r--r-- | arch/x86/kvm/lapic.c | 15 | ||||
-rw-r--r-- | arch/x86/kvm/lapic.h | 2 | ||||
-rw-r--r-- | arch/x86/kvm/vmx.c | 33 |
4 files changed, 51 insertions, 1 deletions
diff --git a/arch/x86/include/asm/vmx.h b/arch/x86/include/asm/vmx.h index e385df97bfdc..44c3f7eb4532 100644 --- a/arch/x86/include/asm/vmx.h +++ b/arch/x86/include/asm/vmx.h | |||
@@ -66,6 +66,7 @@ | |||
66 | #define EXIT_REASON_EPT_MISCONFIG 49 | 66 | #define EXIT_REASON_EPT_MISCONFIG 49 |
67 | #define EXIT_REASON_WBINVD 54 | 67 | #define EXIT_REASON_WBINVD 54 |
68 | #define EXIT_REASON_XSETBV 55 | 68 | #define EXIT_REASON_XSETBV 55 |
69 | #define EXIT_REASON_APIC_WRITE 56 | ||
69 | #define EXIT_REASON_INVPCID 58 | 70 | #define EXIT_REASON_INVPCID 58 |
70 | 71 | ||
71 | #define VMX_EXIT_REASONS \ | 72 | #define VMX_EXIT_REASONS \ |
@@ -141,6 +142,7 @@ | |||
141 | #define SECONDARY_EXEC_ENABLE_VPID 0x00000020 | 142 | #define SECONDARY_EXEC_ENABLE_VPID 0x00000020 |
142 | #define SECONDARY_EXEC_WBINVD_EXITING 0x00000040 | 143 | #define SECONDARY_EXEC_WBINVD_EXITING 0x00000040 |
143 | #define SECONDARY_EXEC_UNRESTRICTED_GUEST 0x00000080 | 144 | #define SECONDARY_EXEC_UNRESTRICTED_GUEST 0x00000080 |
145 | #define SECONDARY_EXEC_APIC_REGISTER_VIRT 0x00000100 | ||
144 | #define SECONDARY_EXEC_PAUSE_LOOP_EXITING 0x00000400 | 146 | #define SECONDARY_EXEC_PAUSE_LOOP_EXITING 0x00000400 |
145 | #define SECONDARY_EXEC_ENABLE_INVPCID 0x00001000 | 147 | #define SECONDARY_EXEC_ENABLE_INVPCID 0x00001000 |
146 | 148 | ||
diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c index 9392f527f107..0664c138e860 100644 --- a/arch/x86/kvm/lapic.c +++ b/arch/x86/kvm/lapic.c | |||
@@ -1212,6 +1212,21 @@ void kvm_lapic_set_eoi(struct kvm_vcpu *vcpu) | |||
1212 | } | 1212 | } |
1213 | EXPORT_SYMBOL_GPL(kvm_lapic_set_eoi); | 1213 | EXPORT_SYMBOL_GPL(kvm_lapic_set_eoi); |
1214 | 1214 | ||
1215 | /* emulate APIC access in a trap manner */ | ||
1216 | void kvm_apic_write_nodecode(struct kvm_vcpu *vcpu, u32 offset) | ||
1217 | { | ||
1218 | u32 val = 0; | ||
1219 | |||
1220 | /* hw has done the conditional check and inst decode */ | ||
1221 | offset &= 0xff0; | ||
1222 | |||
1223 | apic_reg_read(vcpu->arch.apic, offset, 4, &val); | ||
1224 | |||
1225 | /* TODO: optimize to just emulate side effect w/o one more write */ | ||
1226 | apic_reg_write(vcpu->arch.apic, offset, val); | ||
1227 | } | ||
1228 | EXPORT_SYMBOL_GPL(kvm_apic_write_nodecode); | ||
1229 | |||
1215 | void kvm_free_lapic(struct kvm_vcpu *vcpu) | 1230 | void kvm_free_lapic(struct kvm_vcpu *vcpu) |
1216 | { | 1231 | { |
1217 | struct kvm_lapic *apic = vcpu->arch.apic; | 1232 | struct kvm_lapic *apic = vcpu->arch.apic; |
diff --git a/arch/x86/kvm/lapic.h b/arch/x86/kvm/lapic.h index e5ebf9f3571f..9a8ee22bc7a3 100644 --- a/arch/x86/kvm/lapic.h +++ b/arch/x86/kvm/lapic.h | |||
@@ -64,6 +64,8 @@ int kvm_lapic_find_highest_irr(struct kvm_vcpu *vcpu); | |||
64 | u64 kvm_get_lapic_tscdeadline_msr(struct kvm_vcpu *vcpu); | 64 | u64 kvm_get_lapic_tscdeadline_msr(struct kvm_vcpu *vcpu); |
65 | void kvm_set_lapic_tscdeadline_msr(struct kvm_vcpu *vcpu, u64 data); | 65 | void kvm_set_lapic_tscdeadline_msr(struct kvm_vcpu *vcpu, u64 data); |
66 | 66 | ||
67 | void kvm_apic_write_nodecode(struct kvm_vcpu *vcpu, u32 offset); | ||
68 | |||
67 | void kvm_lapic_set_vapic_addr(struct kvm_vcpu *vcpu, gpa_t vapic_addr); | 69 | void kvm_lapic_set_vapic_addr(struct kvm_vcpu *vcpu, gpa_t vapic_addr); |
68 | void kvm_lapic_sync_from_vapic(struct kvm_vcpu *vcpu); | 70 | void kvm_lapic_sync_from_vapic(struct kvm_vcpu *vcpu); |
69 | void kvm_lapic_sync_to_vapic(struct kvm_vcpu *vcpu); | 71 | void kvm_lapic_sync_to_vapic(struct kvm_vcpu *vcpu); |
diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index 02eeba86328d..5ad7c8531083 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c | |||
@@ -84,6 +84,9 @@ module_param(vmm_exclusive, bool, S_IRUGO); | |||
84 | static bool __read_mostly fasteoi = 1; | 84 | static bool __read_mostly fasteoi = 1; |
85 | module_param(fasteoi, bool, S_IRUGO); | 85 | module_param(fasteoi, bool, S_IRUGO); |
86 | 86 | ||
87 | static bool __read_mostly enable_apicv_reg = 1; | ||
88 | module_param(enable_apicv_reg, bool, S_IRUGO); | ||
89 | |||
87 | /* | 90 | /* |
88 | * If nested=1, nested virtualization is supported, i.e., guests may use | 91 | * If nested=1, nested virtualization is supported, i.e., guests may use |
89 | * VMX and be a hypervisor for its own guests. If nested=0, guests may not | 92 | * VMX and be a hypervisor for its own guests. If nested=0, guests may not |
@@ -764,6 +767,12 @@ static inline bool cpu_has_vmx_virtualize_apic_accesses(void) | |||
764 | SECONDARY_EXEC_VIRTUALIZE_APIC_ACCESSES; | 767 | SECONDARY_EXEC_VIRTUALIZE_APIC_ACCESSES; |
765 | } | 768 | } |
766 | 769 | ||
770 | static inline bool cpu_has_vmx_apic_register_virt(void) | ||
771 | { | ||
772 | return vmcs_config.cpu_based_2nd_exec_ctrl & | ||
773 | SECONDARY_EXEC_APIC_REGISTER_VIRT; | ||
774 | } | ||
775 | |||
767 | static inline bool cpu_has_vmx_flexpriority(void) | 776 | static inline bool cpu_has_vmx_flexpriority(void) |
768 | { | 777 | { |
769 | return cpu_has_vmx_tpr_shadow() && | 778 | return cpu_has_vmx_tpr_shadow() && |
@@ -2540,7 +2549,8 @@ static __init int setup_vmcs_config(struct vmcs_config *vmcs_conf) | |||
2540 | SECONDARY_EXEC_UNRESTRICTED_GUEST | | 2549 | SECONDARY_EXEC_UNRESTRICTED_GUEST | |
2541 | SECONDARY_EXEC_PAUSE_LOOP_EXITING | | 2550 | SECONDARY_EXEC_PAUSE_LOOP_EXITING | |
2542 | SECONDARY_EXEC_RDTSCP | | 2551 | SECONDARY_EXEC_RDTSCP | |
2543 | SECONDARY_EXEC_ENABLE_INVPCID; | 2552 | SECONDARY_EXEC_ENABLE_INVPCID | |
2553 | SECONDARY_EXEC_APIC_REGISTER_VIRT; | ||
2544 | if (adjust_vmx_controls(min2, opt2, | 2554 | if (adjust_vmx_controls(min2, opt2, |
2545 | MSR_IA32_VMX_PROCBASED_CTLS2, | 2555 | MSR_IA32_VMX_PROCBASED_CTLS2, |
2546 | &_cpu_based_2nd_exec_control) < 0) | 2556 | &_cpu_based_2nd_exec_control) < 0) |
@@ -2551,6 +2561,11 @@ static __init int setup_vmcs_config(struct vmcs_config *vmcs_conf) | |||
2551 | SECONDARY_EXEC_VIRTUALIZE_APIC_ACCESSES)) | 2561 | SECONDARY_EXEC_VIRTUALIZE_APIC_ACCESSES)) |
2552 | _cpu_based_exec_control &= ~CPU_BASED_TPR_SHADOW; | 2562 | _cpu_based_exec_control &= ~CPU_BASED_TPR_SHADOW; |
2553 | #endif | 2563 | #endif |
2564 | |||
2565 | if (!(_cpu_based_exec_control & CPU_BASED_TPR_SHADOW)) | ||
2566 | _cpu_based_2nd_exec_control &= ~( | ||
2567 | SECONDARY_EXEC_APIC_REGISTER_VIRT); | ||
2568 | |||
2554 | if (_cpu_based_2nd_exec_control & SECONDARY_EXEC_ENABLE_EPT) { | 2569 | if (_cpu_based_2nd_exec_control & SECONDARY_EXEC_ENABLE_EPT) { |
2555 | /* CR3 accesses and invlpg don't need to cause VM Exits when EPT | 2570 | /* CR3 accesses and invlpg don't need to cause VM Exits when EPT |
2556 | enabled */ | 2571 | enabled */ |
@@ -2748,6 +2763,9 @@ static __init int hardware_setup(void) | |||
2748 | if (!cpu_has_vmx_ple()) | 2763 | if (!cpu_has_vmx_ple()) |
2749 | ple_gap = 0; | 2764 | ple_gap = 0; |
2750 | 2765 | ||
2766 | if (!cpu_has_vmx_apic_register_virt()) | ||
2767 | enable_apicv_reg = 0; | ||
2768 | |||
2751 | if (nested) | 2769 | if (nested) |
2752 | nested_vmx_setup_ctls_msrs(); | 2770 | nested_vmx_setup_ctls_msrs(); |
2753 | 2771 | ||
@@ -3829,6 +3847,8 @@ static u32 vmx_secondary_exec_control(struct vcpu_vmx *vmx) | |||
3829 | exec_control &= ~SECONDARY_EXEC_UNRESTRICTED_GUEST; | 3847 | exec_control &= ~SECONDARY_EXEC_UNRESTRICTED_GUEST; |
3830 | if (!ple_gap) | 3848 | if (!ple_gap) |
3831 | exec_control &= ~SECONDARY_EXEC_PAUSE_LOOP_EXITING; | 3849 | exec_control &= ~SECONDARY_EXEC_PAUSE_LOOP_EXITING; |
3850 | if (!enable_apicv_reg || !irqchip_in_kernel(vmx->vcpu.kvm)) | ||
3851 | exec_control &= ~SECONDARY_EXEC_APIC_REGISTER_VIRT; | ||
3832 | return exec_control; | 3852 | return exec_control; |
3833 | } | 3853 | } |
3834 | 3854 | ||
@@ -4787,6 +4807,16 @@ static int handle_apic_access(struct kvm_vcpu *vcpu) | |||
4787 | return emulate_instruction(vcpu, 0) == EMULATE_DONE; | 4807 | return emulate_instruction(vcpu, 0) == EMULATE_DONE; |
4788 | } | 4808 | } |
4789 | 4809 | ||
4810 | static int handle_apic_write(struct kvm_vcpu *vcpu) | ||
4811 | { | ||
4812 | unsigned long exit_qualification = vmcs_readl(EXIT_QUALIFICATION); | ||
4813 | u32 offset = exit_qualification & 0xfff; | ||
4814 | |||
4815 | /* APIC-write VM exit is trap-like and thus no need to adjust IP */ | ||
4816 | kvm_apic_write_nodecode(vcpu, offset); | ||
4817 | return 1; | ||
4818 | } | ||
4819 | |||
4790 | static int handle_task_switch(struct kvm_vcpu *vcpu) | 4820 | static int handle_task_switch(struct kvm_vcpu *vcpu) |
4791 | { | 4821 | { |
4792 | struct vcpu_vmx *vmx = to_vmx(vcpu); | 4822 | struct vcpu_vmx *vmx = to_vmx(vcpu); |
@@ -5721,6 +5751,7 @@ static int (*const kvm_vmx_exit_handlers[])(struct kvm_vcpu *vcpu) = { | |||
5721 | [EXIT_REASON_VMON] = handle_vmon, | 5751 | [EXIT_REASON_VMON] = handle_vmon, |
5722 | [EXIT_REASON_TPR_BELOW_THRESHOLD] = handle_tpr_below_threshold, | 5752 | [EXIT_REASON_TPR_BELOW_THRESHOLD] = handle_tpr_below_threshold, |
5723 | [EXIT_REASON_APIC_ACCESS] = handle_apic_access, | 5753 | [EXIT_REASON_APIC_ACCESS] = handle_apic_access, |
5754 | [EXIT_REASON_APIC_WRITE] = handle_apic_write, | ||
5724 | [EXIT_REASON_WBINVD] = handle_wbinvd, | 5755 | [EXIT_REASON_WBINVD] = handle_wbinvd, |
5725 | [EXIT_REASON_XSETBV] = handle_xsetbv, | 5756 | [EXIT_REASON_XSETBV] = handle_xsetbv, |
5726 | [EXIT_REASON_TASK_SWITCH] = handle_task_switch, | 5757 | [EXIT_REASON_TASK_SWITCH] = handle_task_switch, |