MTRR virtualization for Intel EPT
authorKeir Fraser <keir.fraser@citrix.com>
Mon, 7 Jul 2008 09:45:50 +0000 (10:45 +0100)
committerKeir Fraser <keir.fraser@citrix.com>
Mon, 7 Jul 2008 09:45:50 +0000 (10:45 +0100)
Signed-off-by: Xiaohui Xin <xiaohui.xin@intel.com>
xen/arch/x86/hvm/hvm.c
xen/arch/x86/hvm/mtrr.c
xen/arch/x86/hvm/vmx/vmx.c
xen/arch/x86/mm/hap/p2m-ept.c
xen/include/asm-x86/hvm/hvm.h
xen/include/asm-x86/hvm/vmx/vmx.h
xen/include/asm-x86/mtrr.h

index cac26e0d039ce862e5e14cb415e60ec951dc3f70..252a05f066df9eba4702c9fa6435ececfc5b8a12 100644 (file)
@@ -836,6 +836,14 @@ static void local_flush_cache(void *info)
     wbinvd();
 }
 
+static void hvm_set_uc_mode(struct vcpu *v, bool_t is_in_uc_mode)
+{
+    v->domain->arch.hvm_domain.is_in_uc_mode = is_in_uc_mode;
+    shadow_blow_tables_per_domain(v->domain);
+    if ( hvm_funcs.set_uc_mode )
+        return hvm_funcs.set_uc_mode(v);
+}
+
 int hvm_set_cr0(unsigned long value)
 {
     struct vcpu *v = current;
@@ -923,9 +931,7 @@ int hvm_set_cr0(unsigned long value)
             {
                 /* Flush physical caches. */
                 on_each_cpu(local_flush_cache, NULL, 1, 1);
-                /* Shadow pagetables must recognise UC mode. */
-                v->domain->arch.hvm_domain.is_in_uc_mode = 1;
-                shadow_blow_tables_per_domain(v->domain);
+                hvm_set_uc_mode(v, 1);
             }
             spin_unlock(&v->domain->arch.hvm_domain.uc_lock);
         }
@@ -937,11 +943,8 @@ int hvm_set_cr0(unsigned long value)
             v->arch.hvm_vcpu.cache_mode = NORMAL_CACHE_MODE;
 
             if ( domain_exit_uc_mode(v) )
-            {
-                /* Shadow pagetables must recognise normal caching mode. */
-                v->domain->arch.hvm_domain.is_in_uc_mode = 0;
-                shadow_blow_tables_per_domain(v->domain);
-            }
+                hvm_set_uc_mode(v, 0);
+
             spin_unlock(&v->domain->arch.hvm_domain.uc_lock);
         }
     }
index 863e87075fa41adf49366146e78841ef9a443913..77f31a6f7f3e62ca2eb01c4e835a1af23422d96c 100644 (file)
@@ -696,3 +696,30 @@ static int hvm_load_mtrr_msr(struct domain *d, hvm_domain_context_t *h)
 
 HVM_REGISTER_SAVE_RESTORE(MTRR, hvm_save_mtrr_msr, hvm_load_mtrr_msr,
                           1, HVMSR_PER_VCPU);
+
+uint8_t epte_get_entry_emt(
+    struct domain *d, unsigned long gfn, unsigned long mfn)
+{
+    uint8_t gmtrr_mtype, hmtrr_mtype;
+    uint32_t type;
+    struct vcpu *v = current;
+
+    if ( (current->domain != d) && ((v = d->vcpu[0]) == NULL) )
+        return MTRR_TYPE_WRBACK;
+
+    if ( !v->domain->arch.hvm_domain.params[HVM_PARAM_IDENT_PT] )
+        return MTRR_TYPE_WRBACK;
+
+    if ( (v == current) && v->domain->arch.hvm_domain.is_in_uc_mode )
+        return MTRR_TYPE_UNCACHABLE;
+
+    if ( !mfn_valid(mfn) )
+        return MTRR_TYPE_UNCACHABLE;
+
+    if ( hvm_get_mem_pinned_cacheattr(d, gfn, &type) )
+        return type;
+
+    gmtrr_mtype = get_mtrr_type(&v->arch.hvm_vcpu.mtrr, (gfn << PAGE_SHIFT));
+    hmtrr_mtype = get_mtrr_type(&mtrr_state, (mfn << PAGE_SHIFT));
+    return ((gmtrr_mtype <= hmtrr_mtype) ? gmtrr_mtype : hmtrr_mtype);
+}
index a59685e8283b6da5590d2011b11ab1ee58c3a7fb..8bf842d29cbf3087b571d0540cf87c38768ad932 100644 (file)
@@ -1173,6 +1173,14 @@ static int vmx_do_pmu_interrupt(struct cpu_user_regs *regs)
     return vpmu_do_interrupt(regs);
 }
 
+static void vmx_set_uc_mode(struct vcpu *v)
+{
+    if ( paging_mode_hap(v->domain) )
+        ept_change_entry_emt_with_range(
+            v->domain, 0, v->domain->arch.p2m->max_mapped_pfn);
+    vpid_sync_all();
+}
+
 static struct hvm_function_table vmx_function_table = {
     .name                 = "VMX",
     .domain_initialise    = vmx_domain_initialise,
@@ -1202,7 +1210,8 @@ static struct hvm_function_table vmx_function_table = {
     .fpu_dirty_intercept  = vmx_fpu_dirty_intercept,
     .msr_read_intercept   = vmx_msr_read_intercept,
     .msr_write_intercept  = vmx_msr_write_intercept,
-    .invlpg_intercept     = vmx_invlpg_intercept
+    .invlpg_intercept     = vmx_invlpg_intercept,
+    .set_uc_mode          = vmx_set_uc_mode
 };
 
 static unsigned long *vpid_bitmap;
index 7a8b317397d07804eb38e569fca2aac389b84a77..8ed9c08f1fde8c8498e7f21c029857b3794d2324 100644 (file)
@@ -26,6 +26,8 @@
 #include <asm/p2m.h>
 #include <asm/hvm/vmx/vmx.h>
 #include <xen/iommu.h>
+#include <asm/mtrr.h>
+#include <asm/hvm/cacheattr.h>
 
 static void ept_p2m_type_to_flags(ept_entry_t *entry, p2m_type_t type)
 {
@@ -158,8 +160,7 @@ ept_set_entry(struct domain *d, unsigned long gfn, mfn_t mfn,
             /* Track the highest gfn for which we have ever had a valid mapping */
             if ( gfn > d->arch.p2m->max_mapped_pfn )
                 d->arch.p2m->max_mapped_pfn = gfn;
-
-            ept_entry->emt = EPT_DEFAULT_MT;
+            ept_entry->emt = epte_get_entry_emt(d, gfn, mfn_x(mfn));
             ept_entry->sp_avail = walk_level ? 1 : 0;
 
             if ( ret == GUEST_TABLE_SUPER_PAGE )
@@ -204,11 +205,13 @@ ept_set_entry(struct domain *d, unsigned long gfn, mfn_t mfn,
         /* split the super page before to 4k pages */
 
         split_table = map_domain_page(ept_entry->mfn);
+        offset = gfn & ((1 << EPT_TABLE_ORDER) - 1);
 
         for ( i = 0; i < 512; i++ )
         {
             split_ept_entry = split_table + i;
-            split_ept_entry->emt = EPT_DEFAULT_MT;
+            split_ept_entry->emt = epte_get_entry_emt(d,
+                                        gfn-offset+i, split_mfn+i);
             split_ept_entry->sp_avail =  0;
 
             split_ept_entry->mfn = split_mfn+i;
@@ -222,15 +225,13 @@ ept_set_entry(struct domain *d, unsigned long gfn, mfn_t mfn,
         }
 
         /* Set the destinated 4k page as normal */
-
-        offset = gfn & ((1 << EPT_TABLE_ORDER) - 1);
         split_ept_entry = split_table + offset;
+        split_ept_entry->emt = epte_get_entry_emt(d, gfn, mfn_x(mfn));
         split_ept_entry->mfn = mfn_x(mfn);
         split_ept_entry->avail1 = p2mt;
         ept_p2m_type_to_flags(split_ept_entry, p2mt);
 
         unmap_domain_page(split_table);
-
     }
 
     /* Success */
@@ -249,7 +250,7 @@ out:
         {
             if ( order == EPT_TABLE_ORDER )
             {
-                for ( i = 0; i < 512; i++ )
+                for ( i = 0; i < ( 1 << order ); i++ )
                     iommu_map_page(d, gfn-offset+i, mfn_x(mfn)-offset+i);
             }
             else if ( !order )
@@ -259,7 +260,7 @@ out:
         {
             if ( order == EPT_TABLE_ORDER )
             {
-                for ( i = 0; i < 512; i++ )
+                for ( i = 0; i < ( 1 << order ); i++ )
                     iommu_unmap_page(d, gfn-offset+i);
             }
             else if ( !order )
@@ -322,11 +323,89 @@ static mfn_t ept_get_entry(struct domain *d, unsigned long gfn, p2m_type_t *t)
     return mfn;
 }
 
+static uint64_t ept_get_entry_content(struct domain *d, unsigned long gfn)
+{
+    ept_entry_t *table =
+        map_domain_page(mfn_x(pagetable_get_mfn(d->arch.phys_table)));
+    unsigned long gfn_remainder = gfn;
+    ept_entry_t *ept_entry;
+    uint64_t content = 0;
+
+    u32 index;
+    int i, ret=0;
+
+    /* This pfn is higher than the highest the p2m map currently holds */
+    if ( gfn > d->arch.p2m->max_mapped_pfn )
+        goto out;
+
+    for ( i = EPT_DEFAULT_GAW; i > 0; i-- )
+    {
+        ret = ept_next_level(d, 1, &table, &gfn_remainder,
+                             i * EPT_TABLE_ORDER, 0);
+        if ( !ret )
+            goto out;
+        else if ( ret == GUEST_TABLE_SUPER_PAGE )
+            break;
+    }
+
+    index = gfn_remainder >> ( i * EPT_TABLE_ORDER);
+    ept_entry = table + index;
+    content = ept_entry->epte;
+
+ out:
+    unmap_domain_page(table);
+    return content;
+}
+
 static mfn_t ept_get_entry_current(unsigned long gfn, p2m_type_t *t)
 {
     return ept_get_entry(current->domain, gfn, t);
 }
 
+void ept_change_entry_emt_with_range(struct domain *d, unsigned long start_gfn,
+                 unsigned long end_gfn)
+{
+    unsigned long gfn;
+    p2m_type_t p2mt;
+    uint64_t epte;
+    int order = 0;
+    unsigned long mfn;
+
+    for ( gfn = start_gfn; gfn <= end_gfn; gfn++ )
+    {
+        epte = ept_get_entry_content(d, gfn);
+        if ( epte == 0 )
+            continue;
+        mfn = (epte & EPTE_MFN_MASK) >> PAGE_SHIFT;
+        if ( !mfn_valid(mfn) )
+            continue;
+        p2mt = (epte & EPTE_AVAIL1_MASK) >> 8;
+        order = 0;
+
+        if ( epte & EPTE_SUPER_PAGE_MASK )
+        {
+            if ( !(gfn & ( (1 << EPT_TABLE_ORDER) - 1)) &&
+              ((gfn + 0x1FF) <= end_gfn) )
+            {
+                /* gfn assigned with 2M, and the end covers more than 2m areas.
+                 * Set emt for super page.
+                 */
+                order = EPT_TABLE_ORDER;
+                ept_set_entry(d, gfn, _mfn(mfn), order, p2mt);
+                gfn += 0x1FF;
+            }
+            else
+            {
+                /* change emt for partial entries of the 2m area */
+                ept_set_entry(d, gfn, _mfn(mfn), order, p2mt);
+                gfn = ((gfn >> EPT_TABLE_ORDER) << EPT_TABLE_ORDER) + 0x1FF;
+            }
+        }
+        else /* gfn assigned with 4k */
+            ept_set_entry(d, gfn, _mfn(mfn), order, p2mt);
+    }
+}
+
 /* Walk the whole p2m table, changing any entries of the old type
  * to the new type.  This is used in hardware-assisted paging to
  * quickly enable or diable log-dirty tracking */
index e871bf8be79c94ecae70e001b18b507edf1372b2..0d7791ec3907f6bd2c6a76864a4ce17d919ccc6d 100644 (file)
@@ -127,6 +127,7 @@ struct hvm_function_table {
     int (*msr_read_intercept)(struct cpu_user_regs *regs);
     int (*msr_write_intercept)(struct cpu_user_regs *regs);
     void (*invlpg_intercept)(unsigned long vaddr);
+    void (*set_uc_mode)(struct vcpu *v);
 };
 
 extern struct hvm_function_table hvm_funcs;
index 35f7aa76fcd3fc5b4eaf543659dde4a269bb5dca..0430a46c1b47adb60410cbf8c0a536fce1195ee1 100644 (file)
@@ -44,6 +44,10 @@ typedef union {
 } ept_entry_t;
 
 #define EPT_TABLE_ORDER     9
+#define EPTE_SUPER_PAGE_MASK    0x80
+#define EPTE_MFN_MASK           0x1fffffffffff000
+#define EPTE_AVAIL1_MASK        0xF00
+#define EPTE_EMT_MASK           0x78
 
 void vmx_asm_vmexit_handler(struct cpu_user_regs);
 void vmx_asm_do_vmentry(void);
index 7d5ddbb54bb6915ff5a6860d309336f86dfe6829..625a06bdeb1382dd0ce26628ca74625ffde3273d 100644 (file)
@@ -64,6 +64,9 @@ extern int mtrr_del_page(int reg, unsigned long base, unsigned long size);
 extern void mtrr_centaur_report_mcr(int mcr, u32 lo, u32 hi);
 extern u32 get_pat_flags(struct vcpu *v, u32 gl1e_flags, paddr_t gpaddr,
                   paddr_t spaddr);
+extern uint8_t epte_get_entry_emt(struct domain *d, unsigned long gfn, unsigned long mfn);
+extern void ept_change_entry_emt_with_range(struct domain *d, unsigned long start_gfn,
+                 unsigned long end_gfn);
 extern unsigned char pat_type_2_pte_flags(unsigned char pat_type);
 
 #endif /* __ASM_X86_MTRR_H__ */