ia64/xen-unstable

changeset 19080:175a425e9b55

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>
author Keir Fraser <keir.fraser@citrix.com>
date Fri Jan 23 14:42:29 2009 +0000 (2009-01-23)
parents f3240cd3cd2b
children 257802327958
files xen/arch/x86/hvm/mtrr.c xen/arch/x86/mm/hap/p2m-ept.c xen/drivers/passthrough/iommu.c xen/drivers/passthrough/vtd/dmar.c xen/drivers/passthrough/vtd/iommu.c xen/drivers/passthrough/vtd/iommu.h xen/include/asm-x86/hvm/vmx/vmx.h xen/include/asm-x86/mtrr.h xen/include/xen/iommu.h
line diff
     1.1 --- a/xen/arch/x86/hvm/mtrr.c	Fri Jan 23 14:32:41 2009 +0000
     1.2 +++ b/xen/arch/x86/hvm/mtrr.c	Fri Jan 23 14:42:29 2009 +0000
     1.3 @@ -702,12 +702,15 @@ HVM_REGISTER_SAVE_RESTORE(MTRR, hvm_save
     1.4                            1, HVMSR_PER_VCPU);
     1.5  
     1.6  uint8_t epte_get_entry_emt(
     1.7 -    struct domain *d, unsigned long gfn, unsigned long mfn)
     1.8 +    struct domain *d, unsigned long gfn, 
     1.9 +    unsigned long mfn, uint8_t *igmt, int direct_mmio)
    1.10  {
    1.11      uint8_t gmtrr_mtype, hmtrr_mtype;
    1.12      uint32_t type;
    1.13      struct vcpu *v = current;
    1.14  
    1.15 +    *igmt = 0;
    1.16 +
    1.17      if ( (current->domain != d) && ((v = d->vcpu[0]) == NULL) )
    1.18          return MTRR_TYPE_WRBACK;
    1.19  
    1.20 @@ -723,6 +726,21 @@ uint8_t epte_get_entry_emt(
    1.21      if ( hvm_get_mem_pinned_cacheattr(d, gfn, &type) )
    1.22          return type;
    1.23  
    1.24 +    if ( !iommu_enabled )
    1.25 +    {
    1.26 +        *igmt = 1;
    1.27 +        return MTRR_TYPE_WRBACK;
    1.28 +    }
    1.29 +
    1.30 +    if ( direct_mmio )
    1.31 +        return MTRR_TYPE_UNCACHABLE;
    1.32 +
    1.33 +    if ( iommu_snoop )
    1.34 +    {
    1.35 +        *igmt = 1;
    1.36 +        return MTRR_TYPE_WRBACK;
    1.37 +    }
    1.38 +
    1.39      gmtrr_mtype = get_mtrr_type(&v->arch.hvm_vcpu.mtrr, (gfn << PAGE_SHIFT));
    1.40      hmtrr_mtype = get_mtrr_type(&mtrr_state, (mfn << PAGE_SHIFT));
    1.41      return ((gmtrr_mtype <= hmtrr_mtype) ? gmtrr_mtype : hmtrr_mtype);
     2.1 --- a/xen/arch/x86/mm/hap/p2m-ept.c	Fri Jan 23 14:32:41 2009 +0000
     2.2 +++ b/xen/arch/x86/mm/hap/p2m-ept.c	Fri Jan 23 14:42:29 2009 +0000
     2.3 @@ -66,6 +66,7 @@ static int ept_set_middle_entry(struct d
     2.4      list_add_tail(&pg->list, &d->arch.p2m->pages);
     2.5  
     2.6      ept_entry->emt = 0;
     2.7 +    ept_entry->igmt = 0;
     2.8      ept_entry->sp_avail = 0;
     2.9      ept_entry->avail1 = 0;
    2.10      ept_entry->mfn = page_to_mfn(pg);
    2.11 @@ -114,9 +115,13 @@ static int ept_next_level(struct domain 
    2.12      }
    2.13  }
    2.14  
    2.15 +/*
    2.16 + * TODO: ept_set_entry() computes 'need_modify_vtd_table' for itself,
    2.17 + * by observing whether any gfn->mfn translations are modified.
    2.18 + */
    2.19  static int
    2.20 -ept_set_entry(struct domain *d, unsigned long gfn, mfn_t mfn, 
    2.21 -              unsigned int order, p2m_type_t p2mt)
    2.22 +_ept_set_entry(struct domain *d, unsigned long gfn, mfn_t mfn, 
    2.23 +              unsigned int order, p2m_type_t p2mt, int need_modify_vtd_table)
    2.24  {
    2.25      ept_entry_t *table = NULL;
    2.26      unsigned long gfn_remainder = gfn, offset = 0;
    2.27 @@ -124,6 +129,8 @@ ept_set_entry(struct domain *d, unsigned
    2.28      u32 index;
    2.29      int i, rv = 0, ret = 0;
    2.30      int walk_level = order / EPT_TABLE_ORDER;
    2.31 +    int direct_mmio = (p2mt == p2m_mmio_direct);
    2.32 +    uint8_t igmt = 0;
    2.33  
    2.34      /* we only support 4k and 2m pages now */
    2.35  
    2.36 @@ -157,7 +164,9 @@ ept_set_entry(struct domain *d, unsigned
    2.37      {
    2.38          if ( mfn_valid(mfn_x(mfn)) || (p2mt == p2m_mmio_direct) )
    2.39          {
    2.40 -            ept_entry->emt = epte_get_entry_emt(d, gfn, mfn_x(mfn));
    2.41 +            ept_entry->emt = epte_get_entry_emt(d, gfn, mfn_x(mfn),
    2.42 +                                &igmt, direct_mmio);
    2.43 +            ept_entry->igmt = igmt;
    2.44              ept_entry->sp_avail = walk_level ? 1 : 0;
    2.45  
    2.46              if ( ret == GUEST_TABLE_SUPER_PAGE )
    2.47 @@ -208,7 +217,10 @@ ept_set_entry(struct domain *d, unsigned
    2.48          {
    2.49              split_ept_entry = split_table + i;
    2.50              split_ept_entry->emt = epte_get_entry_emt(d,
    2.51 -                                        gfn-offset+i, split_mfn+i);
    2.52 +                                        gfn-offset+i, split_mfn+i, 
    2.53 +                                        &igmt, direct_mmio);
    2.54 +            split_ept_entry->igmt = igmt;
    2.55 +
    2.56              split_ept_entry->sp_avail =  0;
    2.57  
    2.58              split_ept_entry->mfn = split_mfn+i;
    2.59 @@ -223,7 +235,10 @@ ept_set_entry(struct domain *d, unsigned
    2.60  
    2.61          /* Set the destinated 4k page as normal */
    2.62          split_ept_entry = split_table + offset;
    2.63 -        split_ept_entry->emt = epte_get_entry_emt(d, gfn, mfn_x(mfn));
    2.64 +        split_ept_entry->emt = epte_get_entry_emt(d, gfn, mfn_x(mfn), 
    2.65 +                                                &igmt, direct_mmio);
    2.66 +        split_ept_entry->igmt = igmt;
    2.67 +
    2.68          split_ept_entry->mfn = mfn_x(mfn);
    2.69          split_ept_entry->avail1 = p2mt;
    2.70          ept_p2m_type_to_flags(split_ept_entry, p2mt);
    2.71 @@ -246,7 +261,8 @@ out:
    2.72  
    2.73      /* Now the p2m table is not shared with vt-d page table */
    2.74  
    2.75 -    if ( iommu_enabled && is_hvm_domain(d) )
    2.76 +    if ( iommu_enabled && is_hvm_domain(d)  
    2.77 +             && need_modify_vtd_table )
    2.78      {
    2.79          if ( p2mt == p2m_ram_rw )
    2.80          {
    2.81 @@ -273,6 +289,17 @@ out:
    2.82      return rv;
    2.83  }
    2.84  
    2.85 +static int
    2.86 +ept_set_entry(struct domain *d, unsigned long gfn, mfn_t mfn,
    2.87 +              unsigned int order, p2m_type_t p2mt)
    2.88 +{
    2.89 +    /* ept_set_entry() are called from set_entry(),
    2.90 +     * We should always create VT-d page table acording 
    2.91 +     * to the gfn to mfn translations changes.
    2.92 +     */
    2.93 +    return _ept_set_entry(d, gfn, mfn, order, p2mt, 1); 
    2.94 +}
    2.95 +
    2.96  /* Read ept p2m entries */
    2.97  static mfn_t ept_get_entry(struct domain *d, unsigned long gfn, p2m_type_t *t,
    2.98      p2m_query_t q)
    2.99 @@ -395,18 +422,30 @@ void ept_change_entry_emt_with_range(str
   2.100                   * Set emt for super page.
   2.101                   */
   2.102                  order = EPT_TABLE_ORDER;
   2.103 -                ept_set_entry(d, gfn, _mfn(mfn), order, p2mt);
   2.104 +                /* vmx_set_uc_mode() dont' touch the gfn to mfn
   2.105 +                 * translations, only modify the emt field of the EPT entries.
   2.106 +                 * so we need not modify the current VT-d page tables.
   2.107 +                 */
   2.108 +                _ept_set_entry(d, gfn, _mfn(mfn), order, p2mt, 0);
   2.109                  gfn += 0x1FF;
   2.110              }
   2.111              else
   2.112              {
   2.113 -                /* change emt for partial entries of the 2m area */
   2.114 -                ept_set_entry(d, gfn, _mfn(mfn), order, p2mt);
   2.115 +                /* 1)change emt for partial entries of the 2m area.
   2.116 +                 * 2)vmx_set_uc_mode() dont' touch the gfn to mfn
   2.117 +                 * translations, only modify the emt field of the EPT entries.
   2.118 +                 * so we need not modify the current VT-d page tables.
   2.119 +                 */
   2.120 +                _ept_set_entry(d, gfn, _mfn(mfn), order, p2mt,0);
   2.121                  gfn = ((gfn >> EPT_TABLE_ORDER) << EPT_TABLE_ORDER) + 0x1FF;
   2.122              }
   2.123          }
   2.124 -        else /* gfn assigned with 4k */
   2.125 -            ept_set_entry(d, gfn, _mfn(mfn), order, p2mt);
   2.126 +        else /* 1)gfn assigned with 4k
   2.127 +              * 2)vmx_set_uc_mode() dont' touch the gfn to mfn
   2.128 +              * translations, only modify the emt field of the EPT entries.
   2.129 +              * so we need not modify the current VT-d page tables.
   2.130 +             */
   2.131 +            _ept_set_entry(d, gfn, _mfn(mfn), order, p2mt, 0);
   2.132      }
   2.133  }
   2.134  
     3.1 --- a/xen/drivers/passthrough/iommu.c	Fri Jan 23 14:32:41 2009 +0000
     3.2 +++ b/xen/drivers/passthrough/iommu.c	Fri Jan 23 14:42:29 2009 +0000
     3.3 @@ -39,6 +39,7 @@ int iommu_enabled = 0;
     3.4  int iommu_pv_enabled = 0;
     3.5  int force_iommu = 0;
     3.6  int iommu_passthrough = 0;
     3.7 +int iommu_snoop = 0;
     3.8  
     3.9  static void __init parse_iommu_param(char *s)
    3.10  {
     4.1 --- a/xen/drivers/passthrough/vtd/dmar.c	Fri Jan 23 14:32:41 2009 +0000
     4.2 +++ b/xen/drivers/passthrough/vtd/dmar.c	Fri Jan 23 14:42:29 2009 +0000
     4.3 @@ -29,6 +29,7 @@
     4.4  #include <xen/pci_regs.h>
     4.5  #include <asm/string.h>
     4.6  #include "dmar.h"
     4.7 +#include "iommu.h"
     4.8  
     4.9  int vtd_enabled = 1;
    4.10  
    4.11 @@ -508,6 +509,8 @@ static int __init acpi_parse_dmar(struct
    4.12  int acpi_dmar_init(void)
    4.13  {
    4.14      int rc;
    4.15 +    struct acpi_drhd_unit *drhd;
    4.16 +    struct iommu *iommu;
    4.17  
    4.18      rc = -ENODEV;
    4.19      if ( force_iommu )
    4.20 @@ -524,7 +527,20 @@ int acpi_dmar_init(void)
    4.21      if ( list_empty(&acpi_drhd_units) )
    4.22          goto fail;
    4.23  
    4.24 -    printk("Intel VT-d has been enabled\n");
    4.25 +    /* Giving that all devices within guest use same io page table,
    4.26 +     * enable snoop control only if all VT-d engines support it.
    4.27 +     */
    4.28 +    iommu_snoop = 1;
    4.29 +    for_each_drhd_unit ( drhd )
    4.30 +    {
    4.31 +        iommu = drhd->iommu;
    4.32 +        if ( !ecap_snp_ctl(iommu->ecap) ) {
    4.33 +            iommu_snoop = 0;
    4.34 +            break;
    4.35 +        }
    4.36 +    }
    4.37 +
    4.38 +    printk("Intel VT-d has been enabled, snoop_control=%d.\n", iommu_snoop);
    4.39  
    4.40      return 0;
    4.41  
     5.1 --- a/xen/drivers/passthrough/vtd/iommu.c	Fri Jan 23 14:32:41 2009 +0000
     5.2 +++ b/xen/drivers/passthrough/vtd/iommu.c	Fri Jan 23 14:42:29 2009 +0000
     5.3 @@ -1482,6 +1482,11 @@ int intel_iommu_map_page(
     5.4      pte_present = dma_pte_present(*pte);
     5.5      dma_set_pte_addr(*pte, (paddr_t)mfn << PAGE_SHIFT_4K);
     5.6      dma_set_pte_prot(*pte, DMA_PTE_READ | DMA_PTE_WRITE);
     5.7 +
     5.8 +    /* Set the SNP on leaf page table if Snoop Control available */
     5.9 +    if ( iommu_snoop )
    5.10 +        dma_set_pte_snp(*pte);
    5.11 +
    5.12      iommu_flush_cache_entry(pte);
    5.13      spin_unlock(&hd->mapping_lock);
    5.14      unmap_vtd_domain_page(page);
     6.1 --- a/xen/drivers/passthrough/vtd/iommu.h	Fri Jan 23 14:32:41 2009 +0000
     6.2 +++ b/xen/drivers/passthrough/vtd/iommu.h	Fri Jan 23 14:42:29 2009 +0000
     6.3 @@ -104,6 +104,7 @@
     6.4  #define ecap_ext_intr(e)         ((e >> 4) & 0x1)
     6.5  #define ecap_cache_hints(e)      ((e >> 5) & 0x1)
     6.6  #define ecap_pass_thru(e)        ((e >> 6) & 0x1)
     6.7 +#define ecap_snp_ctl(e)          ((e >> 7) & 0x1)
     6.8  
     6.9  /* IOTLB_REG */
    6.10  #define DMA_TLB_FLUSH_GRANU_OFFSET  60
    6.11 @@ -260,10 +261,12 @@ struct dma_pte {
    6.12  };
    6.13  #define DMA_PTE_READ (1)
    6.14  #define DMA_PTE_WRITE (2)
    6.15 +#define DMA_PTE_SNP  (1 << 11)
    6.16  #define dma_clear_pte(p)    do {(p).val = 0;} while(0)
    6.17  #define dma_set_pte_readable(p) do {(p).val |= DMA_PTE_READ;} while(0)
    6.18  #define dma_set_pte_writable(p) do {(p).val |= DMA_PTE_WRITE;} while(0)
    6.19  #define dma_set_pte_superpage(p) do {(p).val |= (1 << 7);} while(0)
    6.20 +#define dma_set_pte_snp(p)  do {(p).val |= DMA_PTE_SNP;} while(0)
    6.21  #define dma_set_pte_prot(p, prot) \
    6.22              do {(p).val = ((p).val & ~3) | ((prot) & 3); } while (0)
    6.23  #define dma_pte_addr(p) ((p).val & PAGE_MASK_4K)
     7.1 --- a/xen/include/asm-x86/hvm/vmx/vmx.h	Fri Jan 23 14:32:41 2009 +0000
     7.2 +++ b/xen/include/asm-x86/hvm/vmx/vmx.h	Fri Jan 23 14:42:29 2009 +0000
     7.3 @@ -33,7 +33,8 @@ typedef union {
     7.4          u64 r       :   1,
     7.5          w           :   1,
     7.6          x           :   1,
     7.7 -        emt         :   4,
     7.8 +        emt         :   3,
     7.9 +        igmt        :   1,
    7.10          sp_avail    :   1,
    7.11          avail1      :   4,
    7.12          mfn         :   45,
     8.1 --- a/xen/include/asm-x86/mtrr.h	Fri Jan 23 14:32:41 2009 +0000
     8.2 +++ b/xen/include/asm-x86/mtrr.h	Fri Jan 23 14:42:29 2009 +0000
     8.3 @@ -64,9 +64,11 @@ extern int mtrr_del_page(int reg, unsign
     8.4  extern void mtrr_centaur_report_mcr(int mcr, u32 lo, u32 hi);
     8.5  extern u32 get_pat_flags(struct vcpu *v, u32 gl1e_flags, paddr_t gpaddr,
     8.6                    paddr_t spaddr);
     8.7 -extern uint8_t epte_get_entry_emt(struct domain *d, unsigned long gfn, unsigned long mfn);
     8.8 -extern void ept_change_entry_emt_with_range(struct domain *d, unsigned long start_gfn,
     8.9 -                 unsigned long end_gfn);
    8.10 +extern uint8_t epte_get_entry_emt(
    8.11 +    struct domain *d, unsigned long gfn, unsigned long mfn,
    8.12 +    uint8_t *igmt, int direct_mmio);
    8.13 +extern void ept_change_entry_emt_with_range(
    8.14 +    struct domain *d, unsigned long start_gfn, unsigned long end_gfn);
    8.15  extern unsigned char pat_type_2_pte_flags(unsigned char pat_type);
    8.16  
    8.17  #endif /* __ASM_X86_MTRR_H__ */
     9.1 --- a/xen/include/xen/iommu.h	Fri Jan 23 14:32:41 2009 +0000
     9.2 +++ b/xen/include/xen/iommu.h	Fri Jan 23 14:42:29 2009 +0000
     9.3 @@ -31,6 +31,7 @@ extern int iommu_enabled;
     9.4  extern int iommu_pv_enabled;
     9.5  extern int force_iommu;
     9.6  extern int iommu_passthrough;
     9.7 +extern int iommu_snoop;
     9.8  
     9.9  #define domain_hvm_iommu(d)     (&d->arch.hvm_domain.hvm_iommu)
    9.10