# Commit 3272230848f36eb5bbb660216898a90048a81d9f
# Date 2016-01-21 16:11:04 +0100
# Author Jan Beulich <jbeulich@suse.com>
# Committer Jan Beulich <jbeulich@suse.com>
x86: fix (and simplify) MTRR overlap checking

Obtaining one individual range per variable range register (via
get_mtrr_range()) was bogus from the beginning, as these registers may
cover multiple disjoint ranges. Do away with that, in favor of simply
comparing masked addresses.

Also, for is_var_mtrr_overlapped()'s result to be correct when called
from mtrr_wrmsr(), generic_set_mtrr() must update saved state first.

As minor cleanup changes, constify is_var_mtrr_overlapped()'s parameter
and make mtrr_wrmsr() static.

Signed-off-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>

--- a/xen/arch/x86/cpu/mtrr/generic.c
+++ b/xen/arch/x86/cpu/mtrr/generic.c
@@ -105,7 +105,7 @@ void __init mtrr_state_warn(void)
 /* Doesn't attempt to pass an error out to MTRR users
    because it's quite complicated in some cases and probably not
    worth it because the best error handling is to ignore it. */
-void mtrr_wrmsr(unsigned int msr, uint64_t msr_content)
+static void mtrr_wrmsr(unsigned int msr, uint64_t msr_content)
 {
 	if (wrmsr_safe(msr, msr_content) < 0)
 		printk(KERN_ERR
@@ -380,8 +380,8 @@ static void generic_set_mtrr(unsigned in
 	if (size == 0) {
 		/* The invalid bit is kept in the mask, so we simply clear the
 		   relevant mask register to disable a range. */
+		memset(vr, 0, sizeof(*vr));
 		mtrr_wrmsr(MTRRphysMask_MSR(reg), 0);
-		memset(vr, 0, sizeof(struct mtrr_var_range));
 	} else {
 		uint32_t base_lo, base_hi, mask_lo, mask_hi;
 
--- a/xen/arch/x86/cpu/mtrr/mtrr.h
+++ b/xen/arch/x86/cpu/mtrr/mtrr.h
@@ -81,7 +81,6 @@ extern const struct mtrr_ops *mtrr_if;
 extern unsigned int num_var_ranges;
 
 void mtrr_state_warn(void);
-void mtrr_wrmsr(unsigned int msr, uint64_t msr_content);
 
 extern int amd_init_mtrr(void);
 extern int cyrix_init_mtrr(void);
--- a/xen/arch/x86/hvm/mtrr.c
+++ b/xen/arch/x86/hvm/mtrr.c
@@ -28,8 +28,6 @@
 #include <asm/hvm/support.h>
 #include <asm/hvm/cacheattr.h>
 
-static uint32_t size_or_mask;
-
 /* Get page attribute fields (PAn) from PAT MSR. */
 #define pat_cr_2_paf(pat_cr,n)  ((((uint64_t)pat_cr) >> ((n)<<3)) & 0xff)
 
@@ -69,61 +67,28 @@ static uint8_t mtrr_epat_tbl[MTRR_NUM_TY
 /* Lookup table for PAT entry of a given PAT value in host PAT. */
 static uint8_t pat_entry_tbl[PAT_TYPE_NUMS];
 
-static void get_mtrr_range(uint64_t base_msr, uint64_t mask_msr,
-                           uint64_t *base, uint64_t *end)
+bool_t is_var_mtrr_overlapped(const struct mtrr_state *m)
 {
-    uint32_t mask_lo = (uint32_t)mask_msr;
-    uint32_t mask_hi = (uint32_t)(mask_msr >> 32);
-    uint32_t base_lo = (uint32_t)base_msr;
-    uint32_t base_hi = (uint32_t)(base_msr >> 32);
-    uint32_t size;
-
-    if ( (mask_lo & 0x800) == 0 )
-    {
-        /* Invalid (i.e. free) range */
-        *base = 0;
-        *end = 0;
-        return;
-    }
-
-    /* Work out the shifted address mask. */
-    mask_lo = (size_or_mask | (mask_hi << (32 - PAGE_SHIFT)) |
-               (mask_lo >> PAGE_SHIFT));
-
-    /* This works correctly if size is a power of two (a contiguous range). */
-    size = -mask_lo;
-    *base = base_hi << (32 - PAGE_SHIFT) | base_lo >> PAGE_SHIFT;
-    *end = *base + size - 1;
-}
-
-bool_t is_var_mtrr_overlapped(struct mtrr_state *m)
-{
-    int32_t seg, i;
-    uint64_t phys_base, phys_mask, phys_base_pre, phys_mask_pre;
-    uint64_t base_pre, end_pre, base, end;
-    uint8_t num_var_ranges = (uint8_t)m->mtrr_cap;
+    unsigned int seg, i;
+    unsigned int num_var_ranges = (uint8_t)m->mtrr_cap;
 
     for ( i = 0; i < num_var_ranges; i++ )
     {
-        phys_base_pre = ((uint64_t*)m->var_ranges)[i*2];
-        phys_mask_pre = ((uint64_t*)m->var_ranges)[i*2 + 1];
+        uint64_t base1 = m->var_ranges[i].base >> PAGE_SHIFT;
+        uint64_t mask1 = m->var_ranges[i].mask >> PAGE_SHIFT;
 
-        get_mtrr_range(phys_base_pre, phys_mask_pre,
-                        &base_pre, &end_pre);
+        if ( !(m->var_ranges[i].mask & 0x800) )
+            continue;
 
         for ( seg = i + 1; seg < num_var_ranges; seg ++ )
         {
-            phys_base = ((uint64_t*)m->var_ranges)[seg*2];
-            phys_mask = ((uint64_t*)m->var_ranges)[seg*2 + 1];
+            uint64_t base2 = m->var_ranges[seg].base >> PAGE_SHIFT;
+            uint64_t mask2 = m->var_ranges[seg].mask >> PAGE_SHIFT;
 
-            get_mtrr_range(phys_base, phys_mask,
-                            &base, &end);
+            if ( !(m->var_ranges[seg].mask & 0x800) )
+                continue;
 
-            if ( ((base_pre != end_pre) && (base != end))
-                 || ((base >= base_pre) && (base <= end_pre))
-                 || ((end >= base_pre) && (end <= end_pre))
-                 || ((base_pre >= base) && (base_pre <= end))
-                 || ((end_pre >= base) && (end_pre <= end)) )
+            if ( (base1 & mask1 & mask2) == (base2 & mask2 & mask1) )
             {
                 /* MTRR is overlapped. */
                 return 1;
@@ -172,8 +137,6 @@ static int hvm_mtrr_pat_init(void)
         }
     }
 
-    size_or_mask = ~((1 << (paddr_bits - PAGE_SHIFT)) - 1);
-
     return 0;
 }
 __initcall(hvm_mtrr_pat_init);
--- a/xen/include/asm-x86/mtrr.h
+++ b/xen/include/asm-x86/mtrr.h
@@ -83,7 +83,7 @@ extern bool_t mtrr_fix_range_msr_set(str
 extern bool_t mtrr_def_type_msr_set(struct mtrr_state *v, uint64_t msr_content);
 extern bool_t pat_msr_set(uint64_t *pat, uint64_t msr);
 
-bool_t is_var_mtrr_overlapped(struct mtrr_state *m);
+bool_t is_var_mtrr_overlapped(const struct mtrr_state *m);
 bool_t mtrr_pat_not_equal(struct vcpu *vd, struct vcpu *vs);
 
 #endif /* __ASM_X86_MTRR_H__ */
