Enable IOMMU for PV guests
authorKeir Fraser <keir.fraser@citrix.com>
Sat, 24 May 2008 08:37:35 +0000 (09:37 +0100)
committerKeir Fraser <keir.fraser@citrix.com>
Sat, 24 May 2008 08:37:35 +0000 (09:37 +0100)
Introduce 'iommu_pv' boot parameter (default off).  Added a need_iommu
flag which is set if guest has PCI devices assigned.  IOMMU page
tables are populated with current guest memory when IOMMU is first
enabled for the guest.

Signed-off-by: Espen Skoglund <espen.skoglund@netronome.com>
xen/drivers/passthrough/iommu.c
xen/drivers/passthrough/vtd/iommu.c
xen/include/xen/hvm/iommu.h
xen/include/xen/iommu.h
xen/include/xen/sched.h

index ea5d0468e7da9c40741175c1e6d7d738bf8550bd..a41972360c5a315dba39d9bc427380c3bf82eeb9 100644 (file)
 
 #include <xen/sched.h>
 #include <xen/iommu.h>
+#include <xen/paging.h>
 
 extern struct iommu_ops intel_iommu_ops;
 extern struct iommu_ops amd_iommu_ops;
+static int iommu_populate_page_table(struct domain *d);
 int intel_vtd_setup(void);
 int amd_iov_detect(void);
 
 int iommu_enabled = 1;
 boolean_param("iommu", iommu_enabled);
 
+int iommu_pv_enabled = 0;
+boolean_param("iommu_pv", iommu_pv_enabled);
+
 int iommu_domain_init(struct domain *domain)
 {
     struct hvm_iommu *hd = domain_hvm_iommu(domain);
@@ -54,11 +59,46 @@ int iommu_domain_init(struct domain *domain)
 int assign_device(struct domain *d, u8 bus, u8 devfn)
 {
     struct hvm_iommu *hd = domain_hvm_iommu(d);
+    int rc;
 
     if ( !iommu_enabled || !hd->platform_ops )
         return 0;
 
-    return hd->platform_ops->assign_device(d, bus, devfn);
+    if ( (rc = hd->platform_ops->assign_device(d, bus, devfn)) )
+        return rc;
+
+    if ( has_iommu_pdevs(d) && !need_iommu(d) )
+    {
+        d->need_iommu = 1;
+        return iommu_populate_page_table(d);
+    }
+    return 0;
+}
+
+static int iommu_populate_page_table(struct domain *d)
+{
+    struct hvm_iommu *hd = domain_hvm_iommu(d);
+    struct page_info *page;
+    int rc;
+
+    spin_lock(&d->page_alloc_lock);
+
+    list_for_each_entry ( page, &d->page_list, list )
+    {
+        if ( (page->u.inuse.type_info & PGT_type_mask) == PGT_writable_page )
+        {
+            rc = hd->platform_ops->map_page(
+                d, mfn_to_gmfn(d, page_to_mfn(page)), page_to_mfn(page));
+            if (rc)
+            {
+                spin_unlock(&d->page_alloc_lock);
+                hd->platform_ops->teardown(d);
+                return rc;
+            }
+        }
+    }
+    spin_unlock(&d->page_alloc_lock);
+    return 0;
 }
 
 void iommu_domain_destroy(struct domain *d)
@@ -137,7 +177,13 @@ void deassign_device(struct domain *d, u8 bus, u8 devfn)
     if ( !iommu_enabled || !hd->platform_ops )
         return;
 
-    return hd->platform_ops->reassign_device(d, dom0, bus, devfn);
+    hd->platform_ops->reassign_device(d, dom0, bus, devfn);
+
+    if ( !has_iommu_pdevs(d) && need_iommu(d) )
+    {
+        d->need_iommu = 0;
+        hd->platform_ops->teardown(d);
+    }
 }
 
 static int iommu_setup(void)
@@ -160,7 +206,22 @@ static int iommu_setup(void)
     iommu_enabled = (rc == 0);
 
  out:
+    if ( !iommu_enabled || !vtd_enabled )
+        iommu_pv_enabled = 0;
     printk("I/O virtualisation %sabled\n", iommu_enabled ? "en" : "dis");
+    if (iommu_enabled)
+        printk("I/O virtualisation for PV guests %sabled\n",
+               iommu_pv_enabled ? "en" : "dis");
     return rc;
 }
 __initcall(iommu_setup);
+
+
+/*
+ * Local variables:
+ * mode: C
+ * c-set-style: "BSD"
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
index e967188084a88b8f48fbb5f06c1d68e72ba9fee1..0e5186e2557e4d6033ae32430af1ebc87acd4246 100644 (file)
@@ -1138,26 +1138,35 @@ static int domain_context_mapping_one(
     }
 
     spin_lock_irqsave(&iommu->lock, flags);
-    /*
-     * domain_id 0 is not valid on Intel's IOMMU, force domain_id to
-     * be 1 based as required by intel's iommu hw.
-     */
-    context_set_domain_id(context, domain);
-    context_set_address_width(*context, hd->agaw);
 
-    if ( ecap_pass_thru(iommu->ecap) )
-        context_set_translation_type(*context, CONTEXT_TT_PASS_THRU);
 #ifdef CONTEXT_PASSTHRU
+    if ( ecap_pass_thru(iommu->ecap) && (domain->domain_id == 0) )
+        context_set_translation_type(*context, CONTEXT_TT_PASS_THRU);
     else
     {
 #endif
-        ASSERT(hd->pgd_maddr != 0);
+        if ( hd->pgd_maddr == 0 )
+        {
+            hd->pgd_maddr = alloc_pgtable_maddr();
+            if ( hd->pgd_maddr == 0 )
+            {
+                unmap_vtd_domain_page(context_entries);
+                spin_unlock_irqrestore(&iommu->lock, flags);
+                return -ENOMEM;
+            }
+        }
         context_set_address_root(*context, hd->pgd_maddr);
         context_set_translation_type(*context, CONTEXT_TT_MULTI_LEVEL);
 #ifdef CONTEXT_PASSTHRU
     }
 #endif
 
+    /*
+     * domain_id 0 is not valid on Intel's IOMMU, force domain_id to
+     * be 1 based as required by intel's iommu hw.
+     */
+    context_set_domain_id(context, domain);
+    context_set_address_width(*context, hd->agaw);
     context_set_fault_enable(*context);
     context_set_present(*context);
     iommu_flush_cache_entry(iommu, context);
index 1577ad4255c1a014608c67d9df7c0fd1e6b71ea0..333d8816c3bfae0bee55403179bcd77df848ce62 100644 (file)
@@ -54,4 +54,7 @@ struct hvm_iommu {
     struct iommu_ops *platform_ops;
 };
 
+#define has_iommu_pdevs(domain) \
+    (!list_empty(&(domain->arch.hvm_domain.hvm_iommu.pdev_list)))
+
 #endif /* __ASM_X86_HVM_IOMMU_H__ */
index 689cd9bc1ff4bf88311c2d089cb2ed31a2e5fe17..c1454471b0fe4138251f468736bbac44db01b573 100644 (file)
@@ -29,6 +29,7 @@
 
 extern int vtd_enabled;
 extern int iommu_enabled;
+extern int iommu_pv_enabled;
 
 #define domain_hvm_iommu(d)     (&d->arch.hvm_domain.hvm_iommu)
 #define domain_vmx_iommu(d)     (&d->arch.hvm_domain.hvm_iommu.vmx_iommu)
index 05977e054ba1f6ba2a28501d7d6c99a25fc40da1..937ebb295ef3f4e4afe9e21f52c8c049761e825c 100644 (file)
@@ -186,6 +186,8 @@ struct domain
 
     /* Is this an HVM guest? */
     bool_t           is_hvm;
+    /* Does this guest need iommu mappings? */
+    bool_t           need_iommu;
     /* Is this guest fully privileged (aka dom0)? */
     bool_t           is_privileged;
     /* Which guest this guest has privileges on */
@@ -515,6 +517,7 @@ static inline void vcpu_unblock(struct vcpu *v)
 
 #define is_hvm_domain(d) ((d)->is_hvm)
 #define is_hvm_vcpu(v)   (is_hvm_domain(v->domain))
+#define need_iommu(d)    ((d)->need_iommu && !(d)->is_hvm)
 
 extern enum cpufreq_controller {
     FREQCTL_none, FREQCTL_dom0_kernel