KVM: MTRR: do not map huge page for non-consistent range
authorXiao Guangrong <guangrong.xiao@linux.intel.com>
Mon, 15 Jun 2015 08:55:35 +0000 (16:55 +0800)
committerPaolo Bonzini <pbonzini@redhat.com>
Fri, 19 Jun 2015 15:16:29 +0000 (17:16 +0200)
Based on Intel's SDM, mapping huge page which do not have consistent
memory cache for each 4k page will cause undefined behavior

In order to avoiding this kind of undefined behavior, we force to use
4k pages under this case

Signed-off-by: Xiao Guangrong <guangrong.xiao@linux.intel.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
arch/x86/kvm/mmu.c
arch/x86/kvm/mtrr.c
arch/x86/kvm/x86.h

index 532aad251cca00ad38843de9050a3e1b0c8df3a9..f807496b62c2cc76e82a60cd58ee187f0cdc77c2 100644 (file)
@@ -3446,6 +3446,16 @@ static bool try_async_pf(struct kvm_vcpu *vcpu, bool prefault, gfn_t gfn,
        return false;
 }
 
+static bool
+check_hugepage_cache_consistency(struct kvm_vcpu *vcpu, gfn_t gfn, int level)
+{
+       int page_num = KVM_PAGES_PER_HPAGE(level);
+
+       gfn &= ~(page_num - 1);
+
+       return kvm_mtrr_check_gfn_range_consistency(vcpu, gfn, page_num);
+}
+
 static int tdp_page_fault(struct kvm_vcpu *vcpu, gva_t gpa, u32 error_code,
                          bool prefault)
 {
@@ -3471,9 +3481,17 @@ static int tdp_page_fault(struct kvm_vcpu *vcpu, gva_t gpa, u32 error_code,
        if (r)
                return r;
 
-       force_pt_level = mapping_level_dirty_bitmap(vcpu, gfn);
+       if (mapping_level_dirty_bitmap(vcpu, gfn) ||
+           !check_hugepage_cache_consistency(vcpu, gfn, PT_DIRECTORY_LEVEL))
+               force_pt_level = 1;
+       else
+               force_pt_level = 0;
+
        if (likely(!force_pt_level)) {
                level = mapping_level(vcpu, gfn);
+               if (level > PT_DIRECTORY_LEVEL &&
+                   !check_hugepage_cache_consistency(vcpu, gfn, level))
+                       level = PT_DIRECTORY_LEVEL;
                gfn &= ~(KVM_PAGES_PER_HPAGE(level) - 1);
        } else
                level = PT_PAGE_TABLE_LEVEL;
index 1445c4a03a92c4636d37e05e1de59ea34a3d0af9..de1d2d8062e24048232af909d684f0c2ed9e21bc 100644 (file)
@@ -668,3 +668,32 @@ u8 kvm_mtrr_get_guest_memory_type(struct kvm_vcpu *vcpu, gfn_t gfn)
        return type;
 }
 EXPORT_SYMBOL_GPL(kvm_mtrr_get_guest_memory_type);
+
+bool kvm_mtrr_check_gfn_range_consistency(struct kvm_vcpu *vcpu, gfn_t gfn,
+                                         int page_num)
+{
+       struct kvm_mtrr *mtrr_state = &vcpu->arch.mtrr_state;
+       struct mtrr_iter iter;
+       u64 start, end;
+       int type = -1;
+
+       start = gfn_to_gpa(gfn);
+       end = gfn_to_gpa(gfn + page_num);
+       mtrr_for_each_mem_type(&iter, mtrr_state, start, end) {
+               if (type == -1) {
+                       type = iter.mem_type;
+                       continue;
+               }
+
+               if (type != iter.mem_type)
+                       return false;
+       }
+
+       if (!iter.partial_map)
+               return true;
+
+       if (type == -1)
+               return true;
+
+       return type == mtrr_default_type(mtrr_state);
+}
index 0e4727c49279e3b0e1a28bf1bd735e9cb683a6c8..edc8cdcd786b00627a1029c8bb3b2aedcb291d09 100644 (file)
@@ -167,6 +167,8 @@ u8 kvm_mtrr_get_guest_memory_type(struct kvm_vcpu *vcpu, gfn_t gfn);
 bool kvm_mtrr_valid(struct kvm_vcpu *vcpu, u32 msr, u64 data);
 int kvm_mtrr_set_msr(struct kvm_vcpu *vcpu, u32 msr, u64 data);
 int kvm_mtrr_get_msr(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata);
+bool kvm_mtrr_check_gfn_range_consistency(struct kvm_vcpu *vcpu, gfn_t gfn,
+                                         int page_num);
 
 #define KVM_SUPPORTED_XCR0     (XSTATE_FP | XSTATE_SSE | XSTATE_YMM \
                                | XSTATE_BNDREGS | XSTATE_BNDCSR \