diff options
author | Gleb Natapov <gleb@redhat.com> | 2012-12-20 09:57:45 -0500 |
---|---|---|
committer | Marcelo Tosatti <mtosatti@redhat.com> | 2013-01-02 16:36:29 -0500 |
commit | d99e415275dd3f757b75981adad8645cdc26da45 (patch) | |
tree | 5a5c5c21c22aba3a33d60a6e3ea50f7d4a81453a /arch/x86/kvm/vmx.c | |
parent | 89efbed02cfd7e9ce3324de0b44a70ee1c716fac (diff) |
KVM: VMX: fix emulation of invalid guest state.
Currently when emulation of invalid guest state is enable
(emulate_invalid_guest_state=1) segment registers are still fixed for
entry to vm86 mode some times. Segment register fixing is avoided in
enter_rmode(), but vmx_set_segment() still does it unconditionally.
The patch fixes it.
Signed-off-by: Gleb Natapov <gleb@redhat.com>
Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com>
Diffstat (limited to 'arch/x86/kvm/vmx.c')
-rw-r--r-- | arch/x86/kvm/vmx.c | 122 |
1 files changed, 68 insertions, 54 deletions
diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index 7ebcac25725b..9dff310a2e95 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c | |||
@@ -624,6 +624,8 @@ static void vmx_set_segment(struct kvm_vcpu *vcpu, | |||
624 | struct kvm_segment *var, int seg); | 624 | struct kvm_segment *var, int seg); |
625 | static void vmx_get_segment(struct kvm_vcpu *vcpu, | 625 | static void vmx_get_segment(struct kvm_vcpu *vcpu, |
626 | struct kvm_segment *var, int seg); | 626 | struct kvm_segment *var, int seg); |
627 | static bool guest_state_valid(struct kvm_vcpu *vcpu); | ||
628 | static u32 vmx_segment_access_rights(struct kvm_segment *var); | ||
627 | 629 | ||
628 | static DEFINE_PER_CPU(struct vmcs *, vmxarea); | 630 | static DEFINE_PER_CPU(struct vmcs *, vmxarea); |
629 | static DEFINE_PER_CPU(struct vmcs *, current_vmcs); | 631 | static DEFINE_PER_CPU(struct vmcs *, current_vmcs); |
@@ -2758,18 +2760,23 @@ static __exit void hardware_unsetup(void) | |||
2758 | free_kvm_area(); | 2760 | free_kvm_area(); |
2759 | } | 2761 | } |
2760 | 2762 | ||
2761 | static void fix_pmode_dataseg(struct kvm_vcpu *vcpu, int seg, struct kvm_segment *save) | 2763 | static void fix_pmode_dataseg(struct kvm_vcpu *vcpu, int seg, |
2764 | struct kvm_segment *save) | ||
2762 | { | 2765 | { |
2763 | const struct kvm_vmx_segment_field *sf = &kvm_vmx_segment_fields[seg]; | 2766 | if (!emulate_invalid_guest_state) { |
2764 | struct kvm_segment tmp = *save; | 2767 | /* |
2765 | 2768 | * CS and SS RPL should be equal during guest entry according | |
2766 | if (!(vmcs_readl(sf->base) == tmp.base && tmp.s)) { | 2769 | * to VMX spec, but in reality it is not always so. Since vcpu |
2767 | tmp.base = vmcs_readl(sf->base); | 2770 | * is in the middle of the transition from real mode to |
2768 | tmp.selector = vmcs_read16(sf->selector); | 2771 | * protected mode it is safe to assume that RPL 0 is a good |
2769 | tmp.dpl = tmp.selector & SELECTOR_RPL_MASK; | 2772 | * default value. |
2770 | tmp.s = 1; | 2773 | */ |
2774 | if (seg == VCPU_SREG_CS || seg == VCPU_SREG_SS) | ||
2775 | save->selector &= ~SELECTOR_RPL_MASK; | ||
2776 | save->dpl = save->selector & SELECTOR_RPL_MASK; | ||
2777 | save->s = 1; | ||
2771 | } | 2778 | } |
2772 | vmx_set_segment(vcpu, &tmp, seg); | 2779 | vmx_set_segment(vcpu, save, seg); |
2773 | } | 2780 | } |
2774 | 2781 | ||
2775 | static void enter_pmode(struct kvm_vcpu *vcpu) | 2782 | static void enter_pmode(struct kvm_vcpu *vcpu) |
@@ -2777,6 +2784,17 @@ static void enter_pmode(struct kvm_vcpu *vcpu) | |||
2777 | unsigned long flags; | 2784 | unsigned long flags; |
2778 | struct vcpu_vmx *vmx = to_vmx(vcpu); | 2785 | struct vcpu_vmx *vmx = to_vmx(vcpu); |
2779 | 2786 | ||
2787 | /* | ||
2788 | * Update real mode segment cache. It may be not up-to-date if sement | ||
2789 | * register was written while vcpu was in a guest mode. | ||
2790 | */ | ||
2791 | vmx_get_segment(vcpu, &vmx->rmode.segs[VCPU_SREG_ES], VCPU_SREG_ES); | ||
2792 | vmx_get_segment(vcpu, &vmx->rmode.segs[VCPU_SREG_DS], VCPU_SREG_DS); | ||
2793 | vmx_get_segment(vcpu, &vmx->rmode.segs[VCPU_SREG_FS], VCPU_SREG_FS); | ||
2794 | vmx_get_segment(vcpu, &vmx->rmode.segs[VCPU_SREG_GS], VCPU_SREG_GS); | ||
2795 | vmx_get_segment(vcpu, &vmx->rmode.segs[VCPU_SREG_SS], VCPU_SREG_SS); | ||
2796 | vmx_get_segment(vcpu, &vmx->rmode.segs[VCPU_SREG_CS], VCPU_SREG_CS); | ||
2797 | |||
2780 | vmx->emulation_required = 1; | 2798 | vmx->emulation_required = 1; |
2781 | vmx->rmode.vm86_active = 0; | 2799 | vmx->rmode.vm86_active = 0; |
2782 | 2800 | ||
@@ -2794,22 +2812,12 @@ static void enter_pmode(struct kvm_vcpu *vcpu) | |||
2794 | 2812 | ||
2795 | update_exception_bitmap(vcpu); | 2813 | update_exception_bitmap(vcpu); |
2796 | 2814 | ||
2797 | if (emulate_invalid_guest_state) | 2815 | fix_pmode_dataseg(vcpu, VCPU_SREG_CS, &vmx->rmode.segs[VCPU_SREG_CS]); |
2798 | return; | 2816 | fix_pmode_dataseg(vcpu, VCPU_SREG_SS, &vmx->rmode.segs[VCPU_SREG_SS]); |
2799 | |||
2800 | fix_pmode_dataseg(vcpu, VCPU_SREG_ES, &vmx->rmode.segs[VCPU_SREG_ES]); | 2817 | fix_pmode_dataseg(vcpu, VCPU_SREG_ES, &vmx->rmode.segs[VCPU_SREG_ES]); |
2801 | fix_pmode_dataseg(vcpu, VCPU_SREG_DS, &vmx->rmode.segs[VCPU_SREG_DS]); | 2818 | fix_pmode_dataseg(vcpu, VCPU_SREG_DS, &vmx->rmode.segs[VCPU_SREG_DS]); |
2802 | fix_pmode_dataseg(vcpu, VCPU_SREG_FS, &vmx->rmode.segs[VCPU_SREG_FS]); | 2819 | fix_pmode_dataseg(vcpu, VCPU_SREG_FS, &vmx->rmode.segs[VCPU_SREG_FS]); |
2803 | fix_pmode_dataseg(vcpu, VCPU_SREG_GS, &vmx->rmode.segs[VCPU_SREG_GS]); | 2820 | fix_pmode_dataseg(vcpu, VCPU_SREG_GS, &vmx->rmode.segs[VCPU_SREG_GS]); |
2804 | |||
2805 | vmx_segment_cache_clear(vmx); | ||
2806 | |||
2807 | vmcs_write16(GUEST_SS_SELECTOR, 0); | ||
2808 | vmcs_write32(GUEST_SS_AR_BYTES, 0x93); | ||
2809 | |||
2810 | vmcs_write16(GUEST_CS_SELECTOR, | ||
2811 | vmcs_read16(GUEST_CS_SELECTOR) & ~SELECTOR_RPL_MASK); | ||
2812 | vmcs_write32(GUEST_CS_AR_BYTES, 0x9b); | ||
2813 | } | 2821 | } |
2814 | 2822 | ||
2815 | static gva_t rmode_tss_base(struct kvm *kvm) | 2823 | static gva_t rmode_tss_base(struct kvm *kvm) |
@@ -2831,22 +2839,40 @@ static gva_t rmode_tss_base(struct kvm *kvm) | |||
2831 | static void fix_rmode_seg(int seg, struct kvm_segment *save) | 2839 | static void fix_rmode_seg(int seg, struct kvm_segment *save) |
2832 | { | 2840 | { |
2833 | const struct kvm_vmx_segment_field *sf = &kvm_vmx_segment_fields[seg]; | 2841 | const struct kvm_vmx_segment_field *sf = &kvm_vmx_segment_fields[seg]; |
2842 | struct kvm_segment var = *save; | ||
2834 | 2843 | ||
2835 | vmcs_write16(sf->selector, save->base >> 4); | 2844 | var.dpl = 0x3; |
2836 | vmcs_write32(sf->base, save->base & 0xffff0); | 2845 | if (seg == VCPU_SREG_CS) |
2837 | vmcs_write32(sf->limit, 0xffff); | 2846 | var.type = 0x3; |
2838 | vmcs_write32(sf->ar_bytes, 0xf3); | 2847 | |
2839 | if (save->base & 0xf) | 2848 | if (!emulate_invalid_guest_state) { |
2840 | printk_once(KERN_WARNING "kvm: segment base is not paragraph" | 2849 | var.selector = var.base >> 4; |
2841 | " aligned when entering protected mode (seg=%d)", | 2850 | var.base = var.base & 0xffff0; |
2842 | seg); | 2851 | var.limit = 0xffff; |
2852 | var.g = 0; | ||
2853 | var.db = 0; | ||
2854 | var.present = 1; | ||
2855 | var.s = 1; | ||
2856 | var.l = 0; | ||
2857 | var.unusable = 0; | ||
2858 | var.type = 0x3; | ||
2859 | var.avl = 0; | ||
2860 | if (save->base & 0xf) | ||
2861 | printk_once(KERN_WARNING "kvm: segment base is not " | ||
2862 | "paragraph aligned when entering " | ||
2863 | "protected mode (seg=%d)", seg); | ||
2864 | } | ||
2865 | |||
2866 | vmcs_write16(sf->selector, var.selector); | ||
2867 | vmcs_write32(sf->base, var.base); | ||
2868 | vmcs_write32(sf->limit, var.limit); | ||
2869 | vmcs_write32(sf->ar_bytes, vmx_segment_access_rights(&var)); | ||
2843 | } | 2870 | } |
2844 | 2871 | ||
2845 | static void enter_rmode(struct kvm_vcpu *vcpu) | 2872 | static void enter_rmode(struct kvm_vcpu *vcpu) |
2846 | { | 2873 | { |
2847 | unsigned long flags; | 2874 | unsigned long flags; |
2848 | struct vcpu_vmx *vmx = to_vmx(vcpu); | 2875 | struct vcpu_vmx *vmx = to_vmx(vcpu); |
2849 | struct kvm_segment var; | ||
2850 | 2876 | ||
2851 | if (enable_unrestricted_guest) | 2877 | if (enable_unrestricted_guest) |
2852 | return; | 2878 | return; |
@@ -2862,7 +2888,6 @@ static void enter_rmode(struct kvm_vcpu *vcpu) | |||
2862 | vmx->emulation_required = 1; | 2888 | vmx->emulation_required = 1; |
2863 | vmx->rmode.vm86_active = 1; | 2889 | vmx->rmode.vm86_active = 1; |
2864 | 2890 | ||
2865 | |||
2866 | /* | 2891 | /* |
2867 | * Very old userspace does not call KVM_SET_TSS_ADDR before entering | 2892 | * Very old userspace does not call KVM_SET_TSS_ADDR before entering |
2868 | * vcpu. Call it here with phys address pointing 16M below 4G. | 2893 | * vcpu. Call it here with phys address pointing 16M below 4G. |
@@ -2890,28 +2915,13 @@ static void enter_rmode(struct kvm_vcpu *vcpu) | |||
2890 | vmcs_writel(GUEST_CR4, vmcs_readl(GUEST_CR4) | X86_CR4_VME); | 2915 | vmcs_writel(GUEST_CR4, vmcs_readl(GUEST_CR4) | X86_CR4_VME); |
2891 | update_exception_bitmap(vcpu); | 2916 | update_exception_bitmap(vcpu); |
2892 | 2917 | ||
2893 | if (emulate_invalid_guest_state) | 2918 | fix_rmode_seg(VCPU_SREG_SS, &vmx->rmode.segs[VCPU_SREG_SS]); |
2894 | goto continue_rmode; | 2919 | fix_rmode_seg(VCPU_SREG_CS, &vmx->rmode.segs[VCPU_SREG_CS]); |
2895 | 2920 | fix_rmode_seg(VCPU_SREG_ES, &vmx->rmode.segs[VCPU_SREG_ES]); | |
2896 | vmx_get_segment(vcpu, &var, VCPU_SREG_SS); | 2921 | fix_rmode_seg(VCPU_SREG_DS, &vmx->rmode.segs[VCPU_SREG_DS]); |
2897 | vmx_set_segment(vcpu, &var, VCPU_SREG_SS); | 2922 | fix_rmode_seg(VCPU_SREG_GS, &vmx->rmode.segs[VCPU_SREG_GS]); |
2923 | fix_rmode_seg(VCPU_SREG_FS, &vmx->rmode.segs[VCPU_SREG_FS]); | ||
2898 | 2924 | ||
2899 | vmx_get_segment(vcpu, &var, VCPU_SREG_CS); | ||
2900 | vmx_set_segment(vcpu, &var, VCPU_SREG_CS); | ||
2901 | |||
2902 | vmx_get_segment(vcpu, &var, VCPU_SREG_ES); | ||
2903 | vmx_set_segment(vcpu, &var, VCPU_SREG_ES); | ||
2904 | |||
2905 | vmx_get_segment(vcpu, &var, VCPU_SREG_DS); | ||
2906 | vmx_set_segment(vcpu, &var, VCPU_SREG_DS); | ||
2907 | |||
2908 | vmx_get_segment(vcpu, &var, VCPU_SREG_GS); | ||
2909 | vmx_set_segment(vcpu, &var, VCPU_SREG_GS); | ||
2910 | |||
2911 | vmx_get_segment(vcpu, &var, VCPU_SREG_FS); | ||
2912 | vmx_set_segment(vcpu, &var, VCPU_SREG_FS); | ||
2913 | |||
2914 | continue_rmode: | ||
2915 | kvm_mmu_reset_context(vcpu); | 2925 | kvm_mmu_reset_context(vcpu); |
2916 | } | 2926 | } |
2917 | 2927 | ||
@@ -3278,7 +3288,7 @@ static void vmx_set_segment(struct kvm_vcpu *vcpu, | |||
3278 | vmcs_write16(sf->selector, var->selector); | 3288 | vmcs_write16(sf->selector, var->selector); |
3279 | else if (var->s) | 3289 | else if (var->s) |
3280 | fix_rmode_seg(seg, &vmx->rmode.segs[seg]); | 3290 | fix_rmode_seg(seg, &vmx->rmode.segs[seg]); |
3281 | return; | 3291 | goto out; |
3282 | } | 3292 | } |
3283 | 3293 | ||
3284 | vmcs_writel(sf->base, var->base); | 3294 | vmcs_writel(sf->base, var->base); |
@@ -3300,6 +3310,10 @@ static void vmx_set_segment(struct kvm_vcpu *vcpu, | |||
3300 | var->type |= 0x1; /* Accessed */ | 3310 | var->type |= 0x1; /* Accessed */ |
3301 | 3311 | ||
3302 | vmcs_write32(sf->ar_bytes, vmx_segment_access_rights(var)); | 3312 | vmcs_write32(sf->ar_bytes, vmx_segment_access_rights(var)); |
3313 | |||
3314 | out: | ||
3315 | if (!vmx->emulation_required) | ||
3316 | vmx->emulation_required = !guest_state_valid(vcpu); | ||
3303 | } | 3317 | } |
3304 | 3318 | ||
3305 | static void vmx_get_cs_db_l_bits(struct kvm_vcpu *vcpu, int *db, int *l) | 3319 | static void vmx_get_cs_db_l_bits(struct kvm_vcpu *vcpu, int *db, int *l) |