From: Keir Fraser Date: Sat, 24 May 2008 08:37:35 +0000 (+0100) Subject: Enable IOMMU for PV guests X-Git-Tag: archive/raspbian/4.8.0-1+rpi1~1^2~14207^2~39 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=9df55200f749250f6befbef3d255f6b009e1f14d;p=xen.git Enable IOMMU for PV guests 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 --- diff --git a/xen/drivers/passthrough/iommu.c b/xen/drivers/passthrough/iommu.c index ea5d0468e7..a41972360c 100644 --- a/xen/drivers/passthrough/iommu.c +++ b/xen/drivers/passthrough/iommu.c @@ -15,15 +15,20 @@ #include #include +#include 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: + */ diff --git a/xen/drivers/passthrough/vtd/iommu.c b/xen/drivers/passthrough/vtd/iommu.c index e967188084..0e5186e255 100644 --- a/xen/drivers/passthrough/vtd/iommu.c +++ b/xen/drivers/passthrough/vtd/iommu.c @@ -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); diff --git a/xen/include/xen/hvm/iommu.h b/xen/include/xen/hvm/iommu.h index 1577ad4255..333d8816c3 100644 --- a/xen/include/xen/hvm/iommu.h +++ b/xen/include/xen/hvm/iommu.h @@ -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__ */ diff --git a/xen/include/xen/iommu.h b/xen/include/xen/iommu.h index 689cd9bc1f..c1454471b0 100644 --- a/xen/include/xen/iommu.h +++ b/xen/include/xen/iommu.h @@ -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) diff --git a/xen/include/xen/sched.h b/xen/include/xen/sched.h index 05977e054b..937ebb295e 100644 --- a/xen/include/xen/sched.h +++ b/xen/include/xen/sched.h @@ -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