316 lines
11 KiB
Diff
316 lines
11 KiB
Diff
# HG changeset patch
|
|
# User Keir Fraser <keir.fraser@citrix.com>
|
|
# Date 1232721749 0
|
|
# Node ID 175a425e9b55e63c240b0a2ad61f5ed251e85ead
|
|
# Parent f3240cd3cd2b9d48acf3d82caa2ca1cab1f66325
|
|
EPT/VT-d: Enhance MTRR/PAT virtualization when EPT/VT-d both enabled
|
|
|
|
Set effective memory type for EPT according to the VT-d snoop control
|
|
capability, and also includes some cleanups for EPT & VT-d both enabled.
|
|
|
|
Signed-off-by: Edwin Zhai <Edwin.Zhai@intel.com>
|
|
Signed-off-by: Xiaohui Xin <xiaohui.xin@intel.com>
|
|
|
|
--- a/xen/arch/x86/hvm/mtrr.c
|
|
+++ b/xen/arch/x86/hvm/mtrr.c
|
|
@@ -702,12 +702,15 @@ HVM_REGISTER_SAVE_RESTORE(MTRR, hvm_save
|
|
1, HVMSR_PER_VCPU);
|
|
|
|
uint8_t epte_get_entry_emt(
|
|
- struct domain *d, unsigned long gfn, unsigned long mfn)
|
|
+ struct domain *d, unsigned long gfn,
|
|
+ unsigned long mfn, uint8_t *igmt, int direct_mmio)
|
|
{
|
|
uint8_t gmtrr_mtype, hmtrr_mtype;
|
|
uint32_t type;
|
|
struct vcpu *v = current;
|
|
|
|
+ *igmt = 0;
|
|
+
|
|
if ( (current->domain != d) && ((v = d->vcpu[0]) == NULL) )
|
|
return MTRR_TYPE_WRBACK;
|
|
|
|
@@ -723,6 +726,21 @@ uint8_t epte_get_entry_emt(
|
|
if ( hvm_get_mem_pinned_cacheattr(d, gfn, &type) )
|
|
return type;
|
|
|
|
+ if ( !iommu_enabled )
|
|
+ {
|
|
+ *igmt = 1;
|
|
+ return MTRR_TYPE_WRBACK;
|
|
+ }
|
|
+
|
|
+ if ( direct_mmio )
|
|
+ return MTRR_TYPE_UNCACHABLE;
|
|
+
|
|
+ if ( iommu_snoop )
|
|
+ {
|
|
+ *igmt = 1;
|
|
+ return MTRR_TYPE_WRBACK;
|
|
+ }
|
|
+
|
|
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);
|
|
--- a/xen/arch/x86/mm/hap/p2m-ept.c
|
|
+++ b/xen/arch/x86/mm/hap/p2m-ept.c
|
|
@@ -66,6 +66,7 @@ static int ept_set_middle_entry(struct d
|
|
list_add_tail(&pg->list, &d->arch.p2m->pages);
|
|
|
|
ept_entry->emt = 0;
|
|
+ ept_entry->igmt = 0;
|
|
ept_entry->sp_avail = 0;
|
|
ept_entry->avail1 = 0;
|
|
ept_entry->mfn = page_to_mfn(pg);
|
|
@@ -114,9 +115,13 @@ static int ept_next_level(struct domain
|
|
}
|
|
}
|
|
|
|
+/*
|
|
+ * TODO: ept_set_entry() computes 'need_modify_vtd_table' for itself,
|
|
+ * by observing whether any gfn->mfn translations are modified.
|
|
+ */
|
|
static int
|
|
-ept_set_entry(struct domain *d, unsigned long gfn, mfn_t mfn,
|
|
- unsigned int order, p2m_type_t p2mt)
|
|
+_ept_set_entry(struct domain *d, unsigned long gfn, mfn_t mfn,
|
|
+ unsigned int order, p2m_type_t p2mt, int need_modify_vtd_table)
|
|
{
|
|
ept_entry_t *table = NULL;
|
|
unsigned long gfn_remainder = gfn, offset = 0;
|
|
@@ -124,6 +129,8 @@ ept_set_entry(struct domain *d, unsigned
|
|
u32 index;
|
|
int i, rv = 0, ret = 0;
|
|
int walk_level = order / EPT_TABLE_ORDER;
|
|
+ int direct_mmio = (p2mt == p2m_mmio_direct);
|
|
+ uint8_t igmt = 0;
|
|
|
|
/* we only support 4k and 2m pages now */
|
|
|
|
@@ -157,7 +164,9 @@ ept_set_entry(struct domain *d, unsigned
|
|
{
|
|
if ( mfn_valid(mfn_x(mfn)) || (p2mt == p2m_mmio_direct) )
|
|
{
|
|
- ept_entry->emt = epte_get_entry_emt(d, gfn, mfn_x(mfn));
|
|
+ ept_entry->emt = epte_get_entry_emt(d, gfn, mfn_x(mfn),
|
|
+ &igmt, direct_mmio);
|
|
+ ept_entry->igmt = igmt;
|
|
ept_entry->sp_avail = walk_level ? 1 : 0;
|
|
|
|
if ( ret == GUEST_TABLE_SUPER_PAGE )
|
|
@@ -208,7 +217,10 @@ ept_set_entry(struct domain *d, unsigned
|
|
{
|
|
split_ept_entry = split_table + i;
|
|
split_ept_entry->emt = epte_get_entry_emt(d,
|
|
- gfn-offset+i, split_mfn+i);
|
|
+ gfn-offset+i, split_mfn+i,
|
|
+ &igmt, direct_mmio);
|
|
+ split_ept_entry->igmt = igmt;
|
|
+
|
|
split_ept_entry->sp_avail = 0;
|
|
|
|
split_ept_entry->mfn = split_mfn+i;
|
|
@@ -223,7 +235,10 @@ ept_set_entry(struct domain *d, unsigned
|
|
|
|
/* Set the destinated 4k page as normal */
|
|
split_ept_entry = split_table + offset;
|
|
- split_ept_entry->emt = epte_get_entry_emt(d, gfn, mfn_x(mfn));
|
|
+ split_ept_entry->emt = epte_get_entry_emt(d, gfn, mfn_x(mfn),
|
|
+ &igmt, direct_mmio);
|
|
+ split_ept_entry->igmt = igmt;
|
|
+
|
|
split_ept_entry->mfn = mfn_x(mfn);
|
|
split_ept_entry->avail1 = p2mt;
|
|
ept_p2m_type_to_flags(split_ept_entry, p2mt);
|
|
@@ -246,7 +261,8 @@ out:
|
|
|
|
/* Now the p2m table is not shared with vt-d page table */
|
|
|
|
- if ( iommu_enabled && is_hvm_domain(d) )
|
|
+ if ( iommu_enabled && is_hvm_domain(d)
|
|
+ && need_modify_vtd_table )
|
|
{
|
|
if ( p2mt == p2m_ram_rw )
|
|
{
|
|
@@ -273,6 +289,17 @@ out:
|
|
return rv;
|
|
}
|
|
|
|
+static int
|
|
+ept_set_entry(struct domain *d, unsigned long gfn, mfn_t mfn,
|
|
+ unsigned int order, p2m_type_t p2mt)
|
|
+{
|
|
+ /* ept_set_entry() are called from set_entry(),
|
|
+ * We should always create VT-d page table acording
|
|
+ * to the gfn to mfn translations changes.
|
|
+ */
|
|
+ return _ept_set_entry(d, gfn, mfn, order, p2mt, 1);
|
|
+}
|
|
+
|
|
/* Read ept p2m entries */
|
|
static mfn_t ept_get_entry(struct domain *d, unsigned long gfn, p2m_type_t *t)
|
|
{
|
|
@@ -393,18 +420,30 @@ void ept_change_entry_emt_with_range(str
|
|
* Set emt for super page.
|
|
*/
|
|
order = EPT_TABLE_ORDER;
|
|
- ept_set_entry(d, gfn, _mfn(mfn), order, p2mt);
|
|
+ /* vmx_set_uc_mode() dont' touch the gfn to mfn
|
|
+ * translations, only modify the emt field of the EPT entries.
|
|
+ * so we need not modify the current VT-d page tables.
|
|
+ */
|
|
+ _ept_set_entry(d, gfn, _mfn(mfn), order, p2mt, 0);
|
|
gfn += 0x1FF;
|
|
}
|
|
else
|
|
{
|
|
- /* change emt for partial entries of the 2m area */
|
|
- ept_set_entry(d, gfn, _mfn(mfn), order, p2mt);
|
|
+ /* 1)change emt for partial entries of the 2m area.
|
|
+ * 2)vmx_set_uc_mode() dont' touch the gfn to mfn
|
|
+ * translations, only modify the emt field of the EPT entries.
|
|
+ * so we need not modify the current VT-d page tables.
|
|
+ */
|
|
+ _ept_set_entry(d, gfn, _mfn(mfn), order, p2mt,0);
|
|
gfn = ((gfn >> EPT_TABLE_ORDER) << EPT_TABLE_ORDER) + 0x1FF;
|
|
}
|
|
}
|
|
- else /* gfn assigned with 4k */
|
|
- ept_set_entry(d, gfn, _mfn(mfn), order, p2mt);
|
|
+ else /* 1)gfn assigned with 4k
|
|
+ * 2)vmx_set_uc_mode() dont' touch the gfn to mfn
|
|
+ * translations, only modify the emt field of the EPT entries.
|
|
+ * so we need not modify the current VT-d page tables.
|
|
+ */
|
|
+ _ept_set_entry(d, gfn, _mfn(mfn), order, p2mt, 0);
|
|
}
|
|
}
|
|
|
|
--- a/xen/drivers/passthrough/iommu.c
|
|
+++ b/xen/drivers/passthrough/iommu.c
|
|
@@ -40,6 +40,7 @@ int iommu_enabled = 0;
|
|
int iommu_pv_enabled = 0;
|
|
int force_iommu = 0;
|
|
int iommu_passthrough = 0;
|
|
+int iommu_snoop = 0;
|
|
|
|
static void __init parse_iommu_param(char *s)
|
|
{
|
|
--- a/xen/drivers/passthrough/vtd/dmar.c
|
|
+++ b/xen/drivers/passthrough/vtd/dmar.c
|
|
@@ -29,6 +29,7 @@
|
|
#include <xen/pci_regs.h>
|
|
#include <asm/string.h>
|
|
#include "dmar.h"
|
|
+#include "iommu.h"
|
|
|
|
int vtd_enabled = 1;
|
|
|
|
@@ -508,6 +509,8 @@ static int __init acpi_parse_dmar(struct
|
|
int acpi_dmar_init(void)
|
|
{
|
|
int rc;
|
|
+ struct acpi_drhd_unit *drhd;
|
|
+ struct iommu *iommu;
|
|
|
|
rc = -ENODEV;
|
|
if ( force_iommu )
|
|
@@ -524,7 +527,20 @@ int acpi_dmar_init(void)
|
|
if ( list_empty(&acpi_drhd_units) )
|
|
goto fail;
|
|
|
|
- printk("Intel VT-d has been enabled\n");
|
|
+ /* Giving that all devices within guest use same io page table,
|
|
+ * enable snoop control only if all VT-d engines support it.
|
|
+ */
|
|
+ iommu_snoop = 1;
|
|
+ for_each_drhd_unit ( drhd )
|
|
+ {
|
|
+ iommu = drhd->iommu;
|
|
+ if ( !ecap_snp_ctl(iommu->ecap) ) {
|
|
+ iommu_snoop = 0;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ printk("Intel VT-d has been enabled, snoop_control=%d.\n", iommu_snoop);
|
|
|
|
return 0;
|
|
|
|
--- a/xen/drivers/passthrough/vtd/iommu.c
|
|
+++ b/xen/drivers/passthrough/vtd/iommu.c
|
|
@@ -1495,6 +1495,11 @@ int intel_iommu_map_page(
|
|
pte_present = dma_pte_present(*pte);
|
|
dma_set_pte_addr(*pte, (paddr_t)mfn << PAGE_SHIFT_4K);
|
|
dma_set_pte_prot(*pte, DMA_PTE_READ | DMA_PTE_WRITE);
|
|
+
|
|
+ /* Set the SNP on leaf page table if Snoop Control available */
|
|
+ if ( iommu_snoop )
|
|
+ dma_set_pte_snp(*pte);
|
|
+
|
|
iommu_flush_cache_entry(pte);
|
|
unmap_vtd_domain_page(page);
|
|
|
|
--- a/xen/drivers/passthrough/vtd/iommu.h
|
|
+++ b/xen/drivers/passthrough/vtd/iommu.h
|
|
@@ -104,6 +104,7 @@
|
|
#define ecap_ext_intr(e) ((e >> 4) & 0x1)
|
|
#define ecap_cache_hints(e) ((e >> 5) & 0x1)
|
|
#define ecap_pass_thru(e) ((e >> 6) & 0x1)
|
|
+#define ecap_snp_ctl(e) ((e >> 7) & 0x1)
|
|
|
|
/* IOTLB_REG */
|
|
#define DMA_TLB_FLUSH_GRANU_OFFSET 60
|
|
@@ -260,10 +261,12 @@ struct dma_pte {
|
|
};
|
|
#define DMA_PTE_READ (1)
|
|
#define DMA_PTE_WRITE (2)
|
|
+#define DMA_PTE_SNP (1 << 11)
|
|
#define dma_clear_pte(p) do {(p).val = 0;} while(0)
|
|
#define dma_set_pte_readable(p) do {(p).val |= DMA_PTE_READ;} while(0)
|
|
#define dma_set_pte_writable(p) do {(p).val |= DMA_PTE_WRITE;} while(0)
|
|
#define dma_set_pte_superpage(p) do {(p).val |= (1 << 7);} while(0)
|
|
+#define dma_set_pte_snp(p) do {(p).val |= DMA_PTE_SNP;} while(0)
|
|
#define dma_set_pte_prot(p, prot) \
|
|
do {(p).val = ((p).val & ~3) | ((prot) & 3); } while (0)
|
|
#define dma_pte_addr(p) ((p).val & PAGE_MASK_4K)
|
|
--- a/xen/include/asm-x86/hvm/vmx/vmx.h
|
|
+++ b/xen/include/asm-x86/hvm/vmx/vmx.h
|
|
@@ -33,7 +33,8 @@ typedef union {
|
|
u64 r : 1,
|
|
w : 1,
|
|
x : 1,
|
|
- emt : 4,
|
|
+ emt : 3,
|
|
+ igmt : 1,
|
|
sp_avail : 1,
|
|
avail1 : 4,
|
|
mfn : 45,
|
|
--- a/xen/include/asm-x86/mtrr.h
|
|
+++ b/xen/include/asm-x86/mtrr.h
|
|
@@ -64,9 +64,11 @@ extern int mtrr_del_page(int reg, unsign
|
|
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 uint8_t epte_get_entry_emt(
|
|
+ struct domain *d, unsigned long gfn, unsigned long mfn,
|
|
+ uint8_t *igmt, int direct_mmio);
|
|
+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__ */
|
|
--- a/xen/include/xen/iommu.h
|
|
+++ b/xen/include/xen/iommu.h
|
|
@@ -32,6 +32,7 @@ extern int iommu_enabled;
|
|
extern int iommu_pv_enabled;
|
|
extern int force_iommu;
|
|
extern int iommu_passthrough;
|
|
+extern int iommu_snoop;
|
|
|
|
#define domain_hvm_iommu(d) (&d->arch.hvm_domain.hvm_iommu)
|
|
|