# HG changeset patch
# User Keir Fraser <keir.fraser@xxxxxxxxxx>
# Date 1215423950 -3600
# Node ID 9b35ae586cb8587186f08be9acff2b76024ae65e
# Parent abd84464c09c1a43f3c756bdc7934ea9e99f375a
MTRR virtualization for Intel EPT
Signed-off-by: Xiaohui Xin <xiaohui.xin@xxxxxxxxx>
---
xen/arch/x86/hvm/hvm.c | 19 ++++---
xen/arch/x86/hvm/mtrr.c | 27 ++++++++++
xen/arch/x86/hvm/vmx/vmx.c | 11 ++++
xen/arch/x86/mm/hap/p2m-ept.c | 95 ++++++++++++++++++++++++++++++++++----
xen/include/asm-x86/hvm/hvm.h | 1
xen/include/asm-x86/hvm/vmx/vmx.h | 4 +
xen/include/asm-x86/mtrr.h | 3 +
7 files changed, 143 insertions(+), 17 deletions(-)
diff -r abd84464c09c -r 9b35ae586cb8 xen/arch/x86/hvm/hvm.c
--- a/xen/arch/x86/hvm/hvm.c Mon Jul 07 10:29:56 2008 +0100
+++ b/xen/arch/x86/hvm/hvm.c Mon Jul 07 10:45:50 2008 +0100
@@ -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);
}
}
diff -r abd84464c09c -r 9b35ae586cb8 xen/arch/x86/hvm/mtrr.c
--- a/xen/arch/x86/hvm/mtrr.c Mon Jul 07 10:29:56 2008 +0100
+++ b/xen/arch/x86/hvm/mtrr.c Mon Jul 07 10:45:50 2008 +0100
@@ -696,3 +696,30 @@ static int hvm_load_mtrr_msr(struct doma
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);
+}
diff -r abd84464c09c -r 9b35ae586cb8 xen/arch/x86/hvm/vmx/vmx.c
--- a/xen/arch/x86/hvm/vmx/vmx.c Mon Jul 07 10:29:56 2008 +0100
+++ b/xen/arch/x86/hvm/vmx/vmx.c Mon Jul 07 10:45:50 2008 +0100
@@ -1173,6 +1173,14 @@ static int vmx_do_pmu_interrupt(struct c
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_fun
.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;
diff -r abd84464c09c -r 9b35ae586cb8 xen/arch/x86/mm/hap/p2m-ept.c
--- a/xen/arch/x86/mm/hap/p2m-ept.c Mon Jul 07 10:29:56 2008 +0100
+++ b/xen/arch/x86/mm/hap/p2m-ept.c Mon Jul 07 10:45:50 2008 +0100
@@ -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
/* 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
/* 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
}
/* 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,9 +323,87 @@ static mfn_t ept_get_entry(struct domain
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
diff -r abd84464c09c -r 9b35ae586cb8 xen/include/asm-x86/hvm/hvm.h
--- a/xen/include/asm-x86/hvm/hvm.h Mon Jul 07 10:29:56 2008 +0100
+++ b/xen/include/asm-x86/hvm/hvm.h Mon Jul 07 10:45:50 2008 +0100
@@ -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;
diff -r abd84464c09c -r 9b35ae586cb8 xen/include/asm-x86/hvm/vmx/vmx.h
--- a/xen/include/asm-x86/hvm/vmx/vmx.h Mon Jul 07 10:29:56 2008 +0100
+++ b/xen/include/asm-x86/hvm/vmx/vmx.h Mon Jul 07 10:45:50 2008 +0100
@@ -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);
diff -r abd84464c09c -r 9b35ae586cb8 xen/include/asm-x86/mtrr.h
--- a/xen/include/asm-x86/mtrr.h Mon Jul 07 10:29:56 2008 +0100
+++ b/xen/include/asm-x86/mtrr.h Mon Jul 07 10:45:50 2008 +0100
@@ -64,6 +64,9 @@ extern void mtrr_centaur_report_mcr(int
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__ */
_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog
|