From 015ab17dc2e9de805c26e74f498b12ee5e8de07e Mon Sep 17 00:00:00 2001 From: Mark McLoughlin Date: Thu, 20 Nov 2008 14:04:20 +0000 Subject: intel-iommu: remove some unused struct intel_iommu fields The seg, saved_msg and sysdev fields appear to be unused since before the code was first merged. linux/msi.h is not needed in linux/intel-iommu.h anymore since there is no longer a reference to struct msi_msg. The MSI code in drivers/pci/intel-iommu.c still has linux/msi.h included via linux/dmar.h. linux/sysdev.h isn't needed because there is no reference to struct sys_device. Signed-off-by: Mark McLoughlin Signed-off-by: David Woodhouse --- drivers/pci/intel-iommu.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 5c8baa43ac9c..8e5a445ba93a 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -27,7 +27,6 @@ #include #include #include -#include #include #include #include -- cgit v1.2.2 From 519a05491586dad04e687660e54c57882315b22b Mon Sep 17 00:00:00 2001 From: Mark McLoughlin Date: Thu, 20 Nov 2008 14:21:13 +0000 Subject: intel-iommu: make init_dmars() static init_dmars() is not used outside of drivers/pci/intel-iommu.c Signed-off-by: Mark McLoughlin Signed-off-by: David Woodhouse --- drivers/pci/intel-iommu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 8e5a445ba93a..95ae3a9aea8a 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -1589,7 +1589,7 @@ static inline void iommu_prepare_isa(void) } #endif /* !CONFIG_DMAR_FLPY_WA */ -int __init init_dmars(void) +static int __init init_dmars(void) { struct dmar_drhd_unit *drhd; struct dmar_rmrr_unit *rmrr; -- cgit v1.2.2 From f27be03b271851fd54529f292c0f25b4c1f1a553 Mon Sep 17 00:00:00 2001 From: Mark McLoughlin Date: Thu, 20 Nov 2008 15:49:43 +0000 Subject: intel-iommu: move DMA_32/64BIT_PFN into intel-iommu.c Signed-off-by: Mark McLoughlin Signed-off-by: David Woodhouse --- drivers/pci/intel-iommu.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 95ae3a9aea8a..6fadbb9bc180 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -53,6 +53,9 @@ #define DOMAIN_MAX_ADDR(gaw) ((((u64)1) << gaw) - 1) +#define IOVA_PFN(addr) ((addr) >> PAGE_SHIFT) +#define DMA_32BIT_PFN IOVA_PFN(DMA_32BIT_MASK) +#define DMA_64BIT_PFN IOVA_PFN(DMA_64BIT_MASK) static void flush_unmaps_timeout(unsigned long data); -- cgit v1.2.2 From 46b08e1a76b758193b0e7b889c6486a16eb1e9e2 Mon Sep 17 00:00:00 2001 From: Mark McLoughlin Date: Thu, 20 Nov 2008 15:49:44 +0000 Subject: intel-iommu: move root entry defs from dma_remapping.h We keep the struct root_entry forward declaration for the pointer in struct intel_iommu. Signed-off-by: Mark McLoughlin Signed-off-by: David Woodhouse --- drivers/pci/intel-iommu.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) (limited to 'drivers') diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 6fadbb9bc180..29bf2d8176e2 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -57,6 +57,39 @@ #define DMA_32BIT_PFN IOVA_PFN(DMA_32BIT_MASK) #define DMA_64BIT_PFN IOVA_PFN(DMA_64BIT_MASK) +/* + * 0: Present + * 1-11: Reserved + * 12-63: Context Ptr (12 - (haw-1)) + * 64-127: Reserved + */ +struct root_entry { + u64 val; + u64 rsvd1; +}; +#define ROOT_ENTRY_NR (VTD_PAGE_SIZE/sizeof(struct root_entry)) +static inline bool root_present(struct root_entry *root) +{ + return (root->val & 1); +} +static inline void set_root_present(struct root_entry *root) +{ + root->val |= 1; +} +static inline void set_root_value(struct root_entry *root, unsigned long value) +{ + root->val |= value & VTD_PAGE_MASK; +} + +static inline struct context_entry * +get_context_addr_from_root(struct root_entry *root) +{ + return (struct context_entry *) + (root_present(root)?phys_to_virt( + root->val & VTD_PAGE_MASK) : + NULL); +} + static void flush_unmaps_timeout(unsigned long data); DEFINE_TIMER(unmap_timer, flush_unmaps_timeout, 0, 0); -- cgit v1.2.2 From 7a8fc25e0cc6e75fa6fdb0a856490e324218550b Mon Sep 17 00:00:00 2001 From: Mark McLoughlin Date: Thu, 20 Nov 2008 15:49:45 +0000 Subject: intel-iommu: move context entry defs out from dma_remapping.h Signed-off-by: Mark McLoughlin Signed-off-by: David Woodhouse --- drivers/pci/intel-iommu.c | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) (limited to 'drivers') diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 29bf2d8176e2..9d06f4bb6b5e 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -90,6 +90,44 @@ get_context_addr_from_root(struct root_entry *root) NULL); } +/* + * low 64 bits: + * 0: present + * 1: fault processing disable + * 2-3: translation type + * 12-63: address space root + * high 64 bits: + * 0-2: address width + * 3-6: aval + * 8-23: domain id + */ +struct context_entry { + u64 lo; + u64 hi; +}; +#define context_present(c) ((c).lo & 1) +#define context_fault_disable(c) (((c).lo >> 1) & 1) +#define context_translation_type(c) (((c).lo >> 2) & 3) +#define context_address_root(c) ((c).lo & VTD_PAGE_MASK) +#define context_address_width(c) ((c).hi & 7) +#define context_domain_id(c) (((c).hi >> 8) & ((1 << 16) - 1)) + +#define context_set_present(c) do {(c).lo |= 1;} while (0) +#define context_set_fault_enable(c) \ + do {(c).lo &= (((u64)-1) << 2) | 1;} while (0) +#define context_set_translation_type(c, val) \ + do { \ + (c).lo &= (((u64)-1) << 4) | 3; \ + (c).lo |= ((val) & 3) << 2; \ + } while (0) +#define CONTEXT_TT_MULTI_LEVEL 0 +#define context_set_address_root(c, val) \ + do {(c).lo |= (val) & VTD_PAGE_MASK; } while (0) +#define context_set_address_width(c, val) do {(c).hi |= (val) & 7;} while (0) +#define context_set_domain_id(c, val) \ + do {(c).hi |= ((val) & ((1 << 16) - 1)) << 8;} while (0) +#define context_clear_entry(c) do {(c).lo = 0; (c).hi = 0;} while (0) + static void flush_unmaps_timeout(unsigned long data); DEFINE_TIMER(unmap_timer, flush_unmaps_timeout, 0, 0); -- cgit v1.2.2 From 622ba12a4c2148999bda9b891bfd0c6ddcb6c57e Mon Sep 17 00:00:00 2001 From: Mark McLoughlin Date: Thu, 20 Nov 2008 15:49:46 +0000 Subject: intel-iommu: move DMA PTE defs out of dma_remapping.h DMA_PTE_READ/WRITE are needed by kvm. Signed-off-by: Mark McLoughlin Signed-off-by: David Woodhouse --- drivers/pci/intel-iommu.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'drivers') diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 9d06f4bb6b5e..26c5402b6f7c 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -128,6 +128,28 @@ struct context_entry { do {(c).hi |= ((val) & ((1 << 16) - 1)) << 8;} while (0) #define context_clear_entry(c) do {(c).lo = 0; (c).hi = 0;} while (0) +/* + * 0: readable + * 1: writable + * 2-6: reserved + * 7: super page + * 8-11: available + * 12-63: Host physcial address + */ +struct dma_pte { + u64 val; +}; +#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_prot(p, prot) \ + do {(p).val = ((p).val & ~3) | ((prot) & 3); } while (0) +#define dma_pte_addr(p) ((p).val & VTD_PAGE_MASK) +#define dma_set_pte_addr(p, addr) do {\ + (p).val |= ((addr) & VTD_PAGE_MASK); } while (0) +#define dma_pte_present(p) (((p).val & 3) != 0) + static void flush_unmaps_timeout(unsigned long data); DEFINE_TIMER(unmap_timer, flush_unmaps_timeout, 0, 0); -- cgit v1.2.2 From 99126f7ce14aff5f9371b2fa81fddb82be815794 Mon Sep 17 00:00:00 2001 From: Mark McLoughlin Date: Thu, 20 Nov 2008 15:49:47 +0000 Subject: intel-iommu: move struct dmar_domain def out dma_remapping.h Signed-off-by: Mark McLoughlin Signed-off-by: David Woodhouse --- drivers/pci/intel-iommu.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'drivers') diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 26c5402b6f7c..97c36b2ee611 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -150,6 +150,24 @@ struct dma_pte { (p).val |= ((addr) & VTD_PAGE_MASK); } while (0) #define dma_pte_present(p) (((p).val & 3) != 0) +struct dmar_domain { + int id; /* domain id */ + struct intel_iommu *iommu; /* back pointer to owning iommu */ + + struct list_head devices; /* all devices' list */ + struct iova_domain iovad; /* iova's that belong to this domain */ + + struct dma_pte *pgd; /* virtual address */ + spinlock_t mapping_lock; /* page table lock */ + int gaw; /* max guest address width */ + + /* adjusted guest address width, 0 is level 2 30-bit */ + int agaw; + +#define DOMAIN_FLAG_MULTIPLE_DEVICES 1 + int flags; +}; + static void flush_unmaps_timeout(unsigned long data); DEFINE_TIMER(unmap_timer, flush_unmaps_timeout, 0, 0); -- cgit v1.2.2 From a647dacbb1389aa6a5fa631766c1eaea35905890 Mon Sep 17 00:00:00 2001 From: Mark McLoughlin Date: Thu, 20 Nov 2008 15:49:48 +0000 Subject: intel-iommu: move struct device_domain_info out of dma_remapping.h Signed-off-by: Mark McLoughlin Signed-off-by: David Woodhouse --- drivers/pci/intel-iommu.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers') diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 97c36b2ee611..f23a02054bf7 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -168,6 +168,16 @@ struct dmar_domain { int flags; }; +/* PCI domain-device relationship */ +struct device_domain_info { + struct list_head link; /* link to domain siblings */ + struct list_head global; /* link to global list */ + u8 bus; /* PCI bus numer */ + u8 devfn; /* PCI devfn number */ + struct pci_dev *dev; /* it's NULL for PCIE-to-PCI bridge */ + struct dmar_domain *domain; /* pointer to domain */ +}; + static void flush_unmaps_timeout(unsigned long data); DEFINE_TIMER(unmap_timer, flush_unmaps_timeout, 0, 0); -- cgit v1.2.2 From 2abd7e167c1b281f99bb58d302225872bfae9123 Mon Sep 17 00:00:00 2001 From: Mark McLoughlin Date: Thu, 20 Nov 2008 15:49:50 +0000 Subject: intel-iommu: move iommu_prepare_gfx_mapping() out of dma_remapping.h Signed-off-by: Mark McLoughlin Signed-off-by: David Woodhouse --- drivers/pci/intel-iommu.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index f23a02054bf7..c1c59a619650 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -1686,6 +1686,11 @@ static void __init iommu_prepare_gfx_mapping(void) printk(KERN_ERR "IOMMU: mapping reserved region failed\n"); } } +#else /* !CONFIG_DMAR_GFX_WA */ +static inline void iommu_prepare_gfx_mapping(void) +{ + return; +} #endif #ifdef CONFIG_DMAR_FLOPPY_WA -- cgit v1.2.2 From c07e7d217bef198422b7eface456ecfd4bb1ab87 Mon Sep 17 00:00:00 2001 From: Mark McLoughlin Date: Fri, 21 Nov 2008 16:54:46 +0000 Subject: intel-iommu: trivially inline context entry macros Some macros were unused, so I just dropped them: context_fault_disable context_translation_type context_address_root context_address_width context_domain_id Signed-off-by: Mark McLoughlin Signed-off-by: David Woodhouse --- drivers/pci/intel-iommu.c | 85 ++++++++++++++++++++++++++++++----------------- 1 file changed, 55 insertions(+), 30 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index c1c59a619650..3be931b3bf98 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -105,28 +105,53 @@ struct context_entry { u64 lo; u64 hi; }; -#define context_present(c) ((c).lo & 1) -#define context_fault_disable(c) (((c).lo >> 1) & 1) -#define context_translation_type(c) (((c).lo >> 2) & 3) -#define context_address_root(c) ((c).lo & VTD_PAGE_MASK) -#define context_address_width(c) ((c).hi & 7) -#define context_domain_id(c) (((c).hi >> 8) & ((1 << 16) - 1)) - -#define context_set_present(c) do {(c).lo |= 1;} while (0) -#define context_set_fault_enable(c) \ - do {(c).lo &= (((u64)-1) << 2) | 1;} while (0) -#define context_set_translation_type(c, val) \ - do { \ - (c).lo &= (((u64)-1) << 4) | 3; \ - (c).lo |= ((val) & 3) << 2; \ - } while (0) + +static inline bool context_present(struct context_entry *context) +{ + return (context->lo & 1); +} +static inline void context_set_present(struct context_entry *context) +{ + context->lo |= 1; +} + +static inline void context_set_fault_enable(struct context_entry *context) +{ + context->lo &= (((u64)-1) << 2) | 1; +} + #define CONTEXT_TT_MULTI_LEVEL 0 -#define context_set_address_root(c, val) \ - do {(c).lo |= (val) & VTD_PAGE_MASK; } while (0) -#define context_set_address_width(c, val) do {(c).hi |= (val) & 7;} while (0) -#define context_set_domain_id(c, val) \ - do {(c).hi |= ((val) & ((1 << 16) - 1)) << 8;} while (0) -#define context_clear_entry(c) do {(c).lo = 0; (c).hi = 0;} while (0) + +static inline void context_set_translation_type(struct context_entry *context, + unsigned long value) +{ + context->lo &= (((u64)-1) << 4) | 3; + context->lo |= (value & 3) << 2; +} + +static inline void context_set_address_root(struct context_entry *context, + unsigned long value) +{ + context->lo |= value & VTD_PAGE_MASK; +} + +static inline void context_set_address_width(struct context_entry *context, + unsigned long value) +{ + context->hi |= value & 7; +} + +static inline void context_set_domain_id(struct context_entry *context, + unsigned long value) +{ + context->hi |= (value & ((1 << 16) - 1)) << 8; +} + +static inline void context_clear_entry(struct context_entry *context) +{ + context->lo = 0; + context->hi = 0; +} /* * 0: readable @@ -349,7 +374,7 @@ static int device_context_mapped(struct intel_iommu *iommu, u8 bus, u8 devfn) ret = 0; goto out; } - ret = context_present(context[devfn]); + ret = context_present(&context[devfn]); out: spin_unlock_irqrestore(&iommu->lock, flags); return ret; @@ -365,7 +390,7 @@ static void clear_context_table(struct intel_iommu *iommu, u8 bus, u8 devfn) root = &iommu->root_entry[bus]; context = get_context_addr_from_root(root); if (context) { - context_clear_entry(context[devfn]); + context_clear_entry(&context[devfn]); __iommu_flush_cache(iommu, &context[devfn], \ sizeof(*context)); } @@ -1284,17 +1309,17 @@ static int domain_context_mapping_one(struct dmar_domain *domain, if (!context) return -ENOMEM; spin_lock_irqsave(&iommu->lock, flags); - if (context_present(*context)) { + if (context_present(context)) { spin_unlock_irqrestore(&iommu->lock, flags); return 0; } - context_set_domain_id(*context, domain->id); - context_set_address_width(*context, domain->agaw); - context_set_address_root(*context, virt_to_phys(domain->pgd)); - context_set_translation_type(*context, CONTEXT_TT_MULTI_LEVEL); - context_set_fault_enable(*context); - context_set_present(*context); + context_set_domain_id(context, domain->id); + context_set_address_width(context, domain->agaw); + context_set_address_root(context, virt_to_phys(domain->pgd)); + context_set_translation_type(context, CONTEXT_TT_MULTI_LEVEL); + context_set_fault_enable(context); + context_set_present(context); __iommu_flush_cache(iommu, context, sizeof(*context)); /* it's a non-present to present mapping */ -- cgit v1.2.2 From 19c239ce3d089fee339d1ab7e97b43d6f0557ce5 Mon Sep 17 00:00:00 2001 From: Mark McLoughlin Date: Fri, 21 Nov 2008 16:56:53 +0000 Subject: intel-iommu: trivially inline DMA PTE macros Signed-off-by: Mark McLoughlin Signed-off-by: David Woodhouse --- drivers/pci/intel-iommu.c | 71 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 48 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 3be931b3bf98..213a5c87fde2 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -164,16 +164,41 @@ static inline void context_clear_entry(struct context_entry *context) struct dma_pte { u64 val; }; -#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_prot(p, prot) \ - do {(p).val = ((p).val & ~3) | ((prot) & 3); } while (0) -#define dma_pte_addr(p) ((p).val & VTD_PAGE_MASK) -#define dma_set_pte_addr(p, addr) do {\ - (p).val |= ((addr) & VTD_PAGE_MASK); } while (0) -#define dma_pte_present(p) (((p).val & 3) != 0) +static inline void dma_clear_pte(struct dma_pte *pte) +{ + pte->val = 0; +} + +static inline void dma_set_pte_readable(struct dma_pte *pte) +{ + pte->val |= DMA_PTE_READ; +} + +static inline void dma_set_pte_writable(struct dma_pte *pte) +{ + pte->val |= DMA_PTE_WRITE; +} + +static inline void dma_set_pte_prot(struct dma_pte *pte, unsigned long prot) +{ + pte->val = (pte->val & ~3) | (prot & 3); +} + +static inline u64 dma_pte_addr(struct dma_pte *pte) +{ + return (pte->val & VTD_PAGE_MASK); +} + +static inline void dma_set_pte_addr(struct dma_pte *pte, u64 addr) +{ + pte->val |= (addr & VTD_PAGE_MASK); +} + +static inline bool dma_pte_present(struct dma_pte *pte) +{ + return (pte->val & 3) != 0; +} struct dmar_domain { int id; /* domain id */ @@ -487,7 +512,7 @@ static struct dma_pte * addr_to_dma_pte(struct dmar_domain *domain, u64 addr) if (level == 1) break; - if (!dma_pte_present(*pte)) { + if (!dma_pte_present(pte)) { tmp_page = alloc_pgtable_page(); if (!tmp_page) { @@ -497,16 +522,16 @@ static struct dma_pte * addr_to_dma_pte(struct dmar_domain *domain, u64 addr) } __iommu_flush_cache(domain->iommu, tmp_page, PAGE_SIZE); - dma_set_pte_addr(*pte, virt_to_phys(tmp_page)); + dma_set_pte_addr(pte, virt_to_phys(tmp_page)); /* * high level table always sets r/w, last level page * table control read/write */ - dma_set_pte_readable(*pte); - dma_set_pte_writable(*pte); + dma_set_pte_readable(pte); + dma_set_pte_writable(pte); __iommu_flush_cache(domain->iommu, pte, sizeof(*pte)); } - parent = phys_to_virt(dma_pte_addr(*pte)); + parent = phys_to_virt(dma_pte_addr(pte)); level--; } @@ -529,9 +554,9 @@ static struct dma_pte *dma_addr_level_pte(struct dmar_domain *domain, u64 addr, if (level == total) return pte; - if (!dma_pte_present(*pte)) + if (!dma_pte_present(pte)) break; - parent = phys_to_virt(dma_pte_addr(*pte)); + parent = phys_to_virt(dma_pte_addr(pte)); total--; } return NULL; @@ -546,7 +571,7 @@ static void dma_pte_clear_one(struct dmar_domain *domain, u64 addr) pte = dma_addr_level_pte(domain, addr, 1); if (pte) { - dma_clear_pte(*pte); + dma_clear_pte(pte); __iommu_flush_cache(domain->iommu, pte, sizeof(*pte)); } } @@ -593,8 +618,8 @@ static void dma_pte_free_pagetable(struct dmar_domain *domain, pte = dma_addr_level_pte(domain, tmp, level); if (pte) { free_pgtable_page( - phys_to_virt(dma_pte_addr(*pte))); - dma_clear_pte(*pte); + phys_to_virt(dma_pte_addr(pte))); + dma_clear_pte(pte); __iommu_flush_cache(domain->iommu, pte, sizeof(*pte)); } @@ -1421,9 +1446,9 @@ domain_page_mapping(struct dmar_domain *domain, dma_addr_t iova, /* We don't need lock here, nobody else * touches the iova range */ - BUG_ON(dma_pte_addr(*pte)); - dma_set_pte_addr(*pte, start_pfn << VTD_PAGE_SHIFT); - dma_set_pte_prot(*pte, prot); + BUG_ON(dma_pte_addr(pte)); + dma_set_pte_addr(pte, start_pfn << VTD_PAGE_SHIFT); + dma_set_pte_prot(pte, prot); __iommu_flush_cache(domain->iommu, pte, sizeof(*pte)); start_pfn++; index++; @@ -2584,7 +2609,7 @@ u64 intel_iommu_iova_to_pfn(struct dmar_domain *domain, u64 iova) pte = addr_to_dma_pte(domain, iova); if (pte) - pfn = dma_pte_addr(*pte); + pfn = dma_pte_addr(pte); return pfn >> VTD_PAGE_SHIFT; } -- cgit v1.2.2 From 2e824f79240476d57a8589f46232cabf151efe90 Mon Sep 17 00:00:00 2001 From: Yu Zhao Date: Mon, 22 Dec 2008 16:54:58 +0800 Subject: VT-d: fix segment number being ignored when searching DRHD On platforms with multiple PCI segments, any of the segments can have a DRHD with INCLUDE_PCI_ALL flag. So need to check the DRHD's segment number against the PCI device's when searching its DRHD. Signed-off-by: Yu Zhao Signed-off-by: David Woodhouse Signed-off-by: Joerg Roedel --- drivers/pci/dmar.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/dmar.c b/drivers/pci/dmar.c index 691b3adeb870..5f164ff3026e 100644 --- a/drivers/pci/dmar.c +++ b/drivers/pci/dmar.c @@ -191,26 +191,17 @@ dmar_parse_one_drhd(struct acpi_dmar_header *header) static int __init dmar_parse_dev(struct dmar_drhd_unit *dmaru) { struct acpi_dmar_hardware_unit *drhd; - static int include_all; int ret = 0; drhd = (struct acpi_dmar_hardware_unit *) dmaru->hdr; - if (!dmaru->include_all) - ret = dmar_parse_dev_scope((void *)(drhd + 1), + if (dmaru->include_all) + return 0; + + ret = dmar_parse_dev_scope((void *)(drhd + 1), ((void *)drhd) + drhd->header.length, &dmaru->devices_cnt, &dmaru->devices, drhd->segment); - else { - /* Only allow one INCLUDE_ALL */ - if (include_all) { - printk(KERN_WARNING PREFIX "Only one INCLUDE_ALL " - "device scope is allowed\n"); - ret = -EINVAL; - } - include_all = 1; - } - if (ret) { list_del(&dmaru->list); kfree(dmaru); @@ -384,12 +375,21 @@ int dmar_pci_device_match(struct pci_dev *devices[], int cnt, struct dmar_drhd_unit * dmar_find_matched_drhd_unit(struct pci_dev *dev) { - struct dmar_drhd_unit *drhd = NULL; + struct dmar_drhd_unit *dmaru = NULL; + struct acpi_dmar_hardware_unit *drhd; + + list_for_each_entry(dmaru, &dmar_drhd_units, list) { + drhd = container_of(dmaru->hdr, + struct acpi_dmar_hardware_unit, + header); + + if (dmaru->include_all && + drhd->segment == pci_domain_nr(dev->bus)) + return dmaru; - list_for_each_entry(drhd, &dmar_drhd_units, list) { - if (drhd->include_all || dmar_pci_device_match(drhd->devices, - drhd->devices_cnt, dev)) - return drhd; + if (dmar_pci_device_match(dmaru->devices, + dmaru->devices_cnt, dev)) + return dmaru; } return NULL; -- cgit v1.2.2 From d71a2f33ac466a437f316e7bb024d0175a7f3cd9 Mon Sep 17 00:00:00 2001 From: Weidong Han Date: Sun, 7 Dec 2008 21:13:41 +0800 Subject: Initialize domain flags to 0 It's random number after the domain is allocated by kmem_cache_alloc Signed-off-by: Weidong Han Signed-off-by: Joerg Roedel --- drivers/pci/intel-iommu.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 213a5c87fde2..65aa1d427f43 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -1180,6 +1180,7 @@ static struct dmar_domain * iommu_alloc_domain(struct intel_iommu *iommu) set_bit(num, iommu->domain_ids); domain->id = num; domain->iommu = iommu; + domain->flags = 0; iommu->domains[num] = domain; spin_unlock_irqrestore(&iommu->lock, flags); -- cgit v1.2.2 From 3b5410e735b093060b96664230c6f9f4fe80b251 Mon Sep 17 00:00:00 2001 From: Weidong Han Date: Mon, 8 Dec 2008 09:17:15 +0800 Subject: change P2P domain flags Signed-off-by: Weidong Han Signed-off-by: Joerg Roedel --- drivers/pci/intel-iommu.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 65aa1d427f43..22ad8851b3e0 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -200,6 +200,9 @@ static inline bool dma_pte_present(struct dma_pte *pte) return (pte->val & 3) != 0; } +/* devices under the same p2p bridge are owned in one domain */ +#define DOMAIN_FLAG_P2P_MULTIPLE_DEVICES (1 < 0) + struct dmar_domain { int id; /* domain id */ struct intel_iommu *iommu; /* back pointer to owning iommu */ @@ -214,8 +217,7 @@ struct dmar_domain { /* adjusted guest address width, 0 is level 2 30-bit */ int agaw; -#define DOMAIN_FLAG_MULTIPLE_DEVICES 1 - int flags; + int flags; /* flags to find out type of domain */ }; /* PCI domain-device relationship */ @@ -1574,7 +1576,7 @@ static struct dmar_domain *get_domain_for_dev(struct pci_dev *pdev, int gaw) info->dev = NULL; info->domain = domain; /* This domain is shared by devices under p2p bridge */ - domain->flags |= DOMAIN_FLAG_MULTIPLE_DEVICES; + domain->flags |= DOMAIN_FLAG_P2P_MULTIPLE_DEVICES; /* pcie-to-pci bridge already has a domain, uses it */ found = NULL; -- cgit v1.2.2 From d9630fe941769dd050fbc38fbbac20a708ab9461 Mon Sep 17 00:00:00 2001 From: Weidong Han Date: Mon, 8 Dec 2008 11:06:32 +0800 Subject: Add global iommu list Signed-off-by: Weidong Han Signed-off-by: Joerg Roedel --- drivers/pci/intel-iommu.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'drivers') diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 22ad8851b3e0..d2ffa7a6d723 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -57,6 +57,9 @@ #define DMA_32BIT_PFN IOVA_PFN(DMA_32BIT_MASK) #define DMA_64BIT_PFN IOVA_PFN(DMA_64BIT_MASK) +/* global iommu list, set NULL for ignored DMAR units */ +static struct intel_iommu **g_iommus; + /* * 0: Present * 1-11: Reserved @@ -1153,6 +1156,17 @@ void free_dmar_iommu(struct intel_iommu *iommu) kfree(iommu->domains); kfree(iommu->domain_ids); + g_iommus[iommu->seq_id] = NULL; + + /* if all iommus are freed, free g_iommus */ + for (i = 0; i < g_num_of_iommus; i++) { + if (g_iommus[i]) + break; + } + + if (i == g_num_of_iommus) + kfree(g_iommus); + /* free context mapping */ free_context_table(iommu); } @@ -1794,9 +1808,18 @@ static int __init init_dmars(void) */ } + g_iommus = kcalloc(g_num_of_iommus, sizeof(struct intel_iommu *), + GFP_KERNEL); + if (!g_iommus) { + printk(KERN_ERR "Allocating global iommu array failed\n"); + ret = -ENOMEM; + goto error; + } + deferred_flush = kzalloc(g_num_of_iommus * sizeof(struct deferred_flush_tables), GFP_KERNEL); if (!deferred_flush) { + kfree(g_iommus); ret = -ENOMEM; goto error; } @@ -1806,6 +1829,7 @@ static int __init init_dmars(void) continue; iommu = drhd->iommu; + g_iommus[iommu->seq_id] = iommu; ret = iommu_init_domains(iommu); if (ret) @@ -1918,6 +1942,7 @@ error: iommu = drhd->iommu; free_iommu(iommu); } + kfree(g_iommus); return ret; } -- cgit v1.2.2 From a2bb8459fe46e5aaad6637b31b5593d740097cba Mon Sep 17 00:00:00 2001 From: Weidong Han Date: Mon, 8 Dec 2008 11:24:12 +0800 Subject: Get iommu from g_iommus for deferred flush deferred_flush[] uses the iommu seq_id to index, so its iommu is fixed and can get it from g_iommus. Signed-off-by: Weidong Han Signed-off-by: Joerg Roedel --- drivers/pci/intel-iommu.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index d2ffa7a6d723..86b9f58a645e 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -2101,10 +2101,11 @@ static void flush_unmaps(void) /* just flush them all */ for (i = 0; i < g_num_of_iommus; i++) { - if (deferred_flush[i].next) { - struct intel_iommu *iommu = - deferred_flush[i].domain[0]->iommu; + struct intel_iommu *iommu = g_iommus[i]; + if (!iommu) + continue; + if (deferred_flush[i].next) { iommu->flush.flush_iotlb(iommu, 0, 0, 0, DMA_TLB_GLOBAL_FLUSH, 0); for (j = 0; j < deferred_flush[i].next; j++) { -- cgit v1.2.2 From 8c11e798eee2ce4475134eaf61302b28ea4f205d Mon Sep 17 00:00:00 2001 From: Weidong Han Date: Mon, 8 Dec 2008 15:29:22 +0800 Subject: iommu bitmap instead of iommu pointer in dmar_domain In order to support assigning multiple devices from different iommus to a domain, iommu bitmap is used to keep all iommus the domain are related to. Signed-off-by: Weidong Han Signed-off-by: Joerg Roedel --- drivers/pci/intel-iommu.c | 97 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 67 insertions(+), 30 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 86b9f58a645e..9dca689215eb 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -208,7 +208,7 @@ static inline bool dma_pte_present(struct dma_pte *pte) struct dmar_domain { int id; /* domain id */ - struct intel_iommu *iommu; /* back pointer to owning iommu */ + unsigned long iommu_bmp; /* bitmap of iommus this domain uses*/ struct list_head devices; /* all devices' list */ struct iova_domain iovad; /* iova's that belong to this domain */ @@ -362,6 +362,18 @@ void free_iova_mem(struct iova *iova) kmem_cache_free(iommu_iova_cache, iova); } +/* in native case, each domain is related to only one iommu */ +static struct intel_iommu *domain_get_iommu(struct dmar_domain *domain) +{ + int iommu_id; + + iommu_id = find_first_bit(&domain->iommu_bmp, g_num_of_iommus); + if (iommu_id < 0 || iommu_id >= g_num_of_iommus) + return NULL; + + return g_iommus[iommu_id]; +} + /* Gets context entry for a given bus and devfn */ static struct context_entry * device_to_context_entry(struct intel_iommu *iommu, u8 bus, u8 devfn) @@ -502,6 +514,7 @@ static struct dma_pte * addr_to_dma_pte(struct dmar_domain *domain, u64 addr) int level = agaw_to_level(domain->agaw); int offset; unsigned long flags; + struct intel_iommu *iommu = domain_get_iommu(domain); BUG_ON(!domain->pgd); @@ -525,7 +538,7 @@ static struct dma_pte * addr_to_dma_pte(struct dmar_domain *domain, u64 addr) flags); return NULL; } - __iommu_flush_cache(domain->iommu, tmp_page, + __iommu_flush_cache(iommu, tmp_page, PAGE_SIZE); dma_set_pte_addr(pte, virt_to_phys(tmp_page)); /* @@ -534,7 +547,7 @@ static struct dma_pte * addr_to_dma_pte(struct dmar_domain *domain, u64 addr) */ dma_set_pte_readable(pte); dma_set_pte_writable(pte); - __iommu_flush_cache(domain->iommu, pte, sizeof(*pte)); + __iommu_flush_cache(iommu, pte, sizeof(*pte)); } parent = phys_to_virt(dma_pte_addr(pte)); level--; @@ -571,13 +584,14 @@ static struct dma_pte *dma_addr_level_pte(struct dmar_domain *domain, u64 addr, static void dma_pte_clear_one(struct dmar_domain *domain, u64 addr) { struct dma_pte *pte = NULL; + struct intel_iommu *iommu = domain_get_iommu(domain); /* get last level pte */ pte = dma_addr_level_pte(domain, addr, 1); if (pte) { dma_clear_pte(pte); - __iommu_flush_cache(domain->iommu, pte, sizeof(*pte)); + __iommu_flush_cache(iommu, pte, sizeof(*pte)); } } @@ -608,6 +622,7 @@ static void dma_pte_free_pagetable(struct dmar_domain *domain, int total = agaw_to_level(domain->agaw); int level; u64 tmp; + struct intel_iommu *iommu = domain_get_iommu(domain); start &= (((u64)1) << addr_width) - 1; end &= (((u64)1) << addr_width) - 1; @@ -625,7 +640,7 @@ static void dma_pte_free_pagetable(struct dmar_domain *domain, free_pgtable_page( phys_to_virt(dma_pte_addr(pte))); dma_clear_pte(pte); - __iommu_flush_cache(domain->iommu, + __iommu_flush_cache(iommu, pte, sizeof(*pte)); } tmp += level_size(level); @@ -1195,7 +1210,8 @@ static struct dmar_domain * iommu_alloc_domain(struct intel_iommu *iommu) set_bit(num, iommu->domain_ids); domain->id = num; - domain->iommu = iommu; + memset(&domain->iommu_bmp, 0, sizeof(unsigned long)); + set_bit(iommu->seq_id, &domain->iommu_bmp); domain->flags = 0; iommu->domains[num] = domain; spin_unlock_irqrestore(&iommu->lock, flags); @@ -1206,10 +1222,13 @@ static struct dmar_domain * iommu_alloc_domain(struct intel_iommu *iommu) static void iommu_free_domain(struct dmar_domain *domain) { unsigned long flags; + struct intel_iommu *iommu; + + iommu = domain_get_iommu(domain); - spin_lock_irqsave(&domain->iommu->lock, flags); - clear_bit(domain->id, domain->iommu->domain_ids); - spin_unlock_irqrestore(&domain->iommu->lock, flags); + spin_lock_irqsave(&iommu->lock, flags); + clear_bit(domain->id, iommu->domain_ids); + spin_unlock_irqrestore(&iommu->lock, flags); } static struct iova_domain reserved_iova_list; @@ -1288,7 +1307,7 @@ static int domain_init(struct dmar_domain *domain, int guest_width) domain_reserve_special_ranges(domain); /* calculate AGAW */ - iommu = domain->iommu; + iommu = domain_get_iommu(domain); if (guest_width > cap_mgaw(iommu->cap)) guest_width = cap_mgaw(iommu->cap); domain->gaw = guest_width; @@ -1341,7 +1360,7 @@ static int domain_context_mapping_one(struct dmar_domain *domain, u8 bus, u8 devfn) { struct context_entry *context; - struct intel_iommu *iommu = domain->iommu; + struct intel_iommu *iommu = domain_get_iommu(domain); unsigned long flags; pr_debug("Set context mapping for %02x:%02x.%d\n", @@ -1413,8 +1432,9 @@ static int domain_context_mapped(struct dmar_domain *domain, { int ret; struct pci_dev *tmp, *parent; + struct intel_iommu *iommu = domain_get_iommu(domain); - ret = device_context_mapped(domain->iommu, + ret = device_context_mapped(iommu, pdev->bus->number, pdev->devfn); if (!ret) return ret; @@ -1425,17 +1445,17 @@ static int domain_context_mapped(struct dmar_domain *domain, /* Secondary interface's bus number and devfn 0 */ parent = pdev->bus->self; while (parent != tmp) { - ret = device_context_mapped(domain->iommu, parent->bus->number, + ret = device_context_mapped(iommu, parent->bus->number, parent->devfn); if (!ret) return ret; parent = parent->bus->self; } if (tmp->is_pcie) - return device_context_mapped(domain->iommu, + return device_context_mapped(iommu, tmp->subordinate->number, 0); else - return device_context_mapped(domain->iommu, + return device_context_mapped(iommu, tmp->bus->number, tmp->devfn); } @@ -1447,6 +1467,7 @@ domain_page_mapping(struct dmar_domain *domain, dma_addr_t iova, struct dma_pte *pte; int index; int addr_width = agaw_to_width(domain->agaw); + struct intel_iommu *iommu = domain_get_iommu(domain); hpa &= (((u64)1) << addr_width) - 1; @@ -1466,7 +1487,7 @@ domain_page_mapping(struct dmar_domain *domain, dma_addr_t iova, BUG_ON(dma_pte_addr(pte)); dma_set_pte_addr(pte, start_pfn << VTD_PAGE_SHIFT); dma_set_pte_prot(pte, prot); - __iommu_flush_cache(domain->iommu, pte, sizeof(*pte)); + __iommu_flush_cache(iommu, pte, sizeof(*pte)); start_pfn++; index++; } @@ -1475,10 +1496,12 @@ domain_page_mapping(struct dmar_domain *domain, dma_addr_t iova, static void detach_domain_for_dev(struct dmar_domain *domain, u8 bus, u8 devfn) { - clear_context_table(domain->iommu, bus, devfn); - domain->iommu->flush.flush_context(domain->iommu, 0, 0, 0, + struct intel_iommu *iommu = domain_get_iommu(domain); + + clear_context_table(iommu, bus, devfn); + iommu->flush.flush_context(iommu, 0, 0, 0, DMA_CCMD_GLOBAL_INVL, 0); - domain->iommu->flush.flush_iotlb(domain->iommu, 0, 0, 0, + iommu->flush.flush_iotlb(iommu, 0, 0, 0, DMA_TLB_GLOBAL_FLUSH, 0); } @@ -2033,6 +2056,7 @@ static dma_addr_t __intel_map_single(struct device *hwdev, phys_addr_t paddr, struct iova *iova; int prot = 0; int ret; + struct intel_iommu *iommu; BUG_ON(dir == DMA_NONE); if (pdev->dev.archdata.iommu == DUMMY_DEVICE_DOMAIN_INFO) @@ -2042,6 +2066,7 @@ static dma_addr_t __intel_map_single(struct device *hwdev, phys_addr_t paddr, if (!domain) return 0; + iommu = domain_get_iommu(domain); size = aligned_size((u64)paddr, size); iova = __intel_alloc_iova(hwdev, domain, size, pdev->dma_mask); @@ -2055,7 +2080,7 @@ static dma_addr_t __intel_map_single(struct device *hwdev, phys_addr_t paddr, * mappings.. */ if (dir == DMA_TO_DEVICE || dir == DMA_BIDIRECTIONAL || \ - !cap_zlr(domain->iommu->cap)) + !cap_zlr(iommu->cap)) prot |= DMA_PTE_READ; if (dir == DMA_FROM_DEVICE || dir == DMA_BIDIRECTIONAL) prot |= DMA_PTE_WRITE; @@ -2071,10 +2096,10 @@ static dma_addr_t __intel_map_single(struct device *hwdev, phys_addr_t paddr, goto error; /* it's a non-present to present mapping */ - ret = iommu_flush_iotlb_psi(domain->iommu, domain->id, + ret = iommu_flush_iotlb_psi(iommu, domain->id, start_paddr, size >> VTD_PAGE_SHIFT, 1); if (ret) - iommu_flush_write_buffer(domain->iommu); + iommu_flush_write_buffer(iommu); return start_paddr + ((u64)paddr & (~PAGE_MASK)); @@ -2132,12 +2157,14 @@ static void add_unmap(struct dmar_domain *dom, struct iova *iova) { unsigned long flags; int next, iommu_id; + struct intel_iommu *iommu; spin_lock_irqsave(&async_umap_flush_lock, flags); if (list_size == HIGH_WATER_MARK) flush_unmaps(); - iommu_id = dom->iommu->seq_id; + iommu = domain_get_iommu(dom); + iommu_id = iommu->seq_id; next = deferred_flush[iommu_id].next; deferred_flush[iommu_id].domain[next] = dom; @@ -2159,12 +2186,15 @@ void intel_unmap_single(struct device *dev, dma_addr_t dev_addr, size_t size, struct dmar_domain *domain; unsigned long start_addr; struct iova *iova; + struct intel_iommu *iommu; if (pdev->dev.archdata.iommu == DUMMY_DEVICE_DOMAIN_INFO) return; domain = find_domain(pdev); BUG_ON(!domain); + iommu = domain_get_iommu(domain); + iova = find_iova(&domain->iovad, IOVA_PFN(dev_addr)); if (!iova) return; @@ -2180,9 +2210,9 @@ void intel_unmap_single(struct device *dev, dma_addr_t dev_addr, size_t size, /* free page tables */ dma_pte_free_pagetable(domain, start_addr, start_addr + size); if (intel_iommu_strict) { - if (iommu_flush_iotlb_psi(domain->iommu, + if (iommu_flush_iotlb_psi(iommu, domain->id, start_addr, size >> VTD_PAGE_SHIFT, 0)) - iommu_flush_write_buffer(domain->iommu); + iommu_flush_write_buffer(iommu); /* free iova */ __free_iova(&domain->iovad, iova); } else { @@ -2243,11 +2273,15 @@ void intel_unmap_sg(struct device *hwdev, struct scatterlist *sglist, size_t size = 0; void *addr; struct scatterlist *sg; + struct intel_iommu *iommu; if (pdev->dev.archdata.iommu == DUMMY_DEVICE_DOMAIN_INFO) return; domain = find_domain(pdev); + BUG_ON(!domain); + + iommu = domain_get_iommu(domain); iova = find_iova(&domain->iovad, IOVA_PFN(sglist[0].dma_address)); if (!iova) @@ -2264,9 +2298,9 @@ void intel_unmap_sg(struct device *hwdev, struct scatterlist *sglist, /* free page tables */ dma_pte_free_pagetable(domain, start_addr, start_addr + size); - if (iommu_flush_iotlb_psi(domain->iommu, domain->id, start_addr, + if (iommu_flush_iotlb_psi(iommu, domain->id, start_addr, size >> VTD_PAGE_SHIFT, 0)) - iommu_flush_write_buffer(domain->iommu); + iommu_flush_write_buffer(iommu); /* free iova */ __free_iova(&domain->iovad, iova); @@ -2300,6 +2334,7 @@ int intel_map_sg(struct device *hwdev, struct scatterlist *sglist, int nelems, int ret; struct scatterlist *sg; unsigned long start_addr; + struct intel_iommu *iommu; BUG_ON(dir == DMA_NONE); if (pdev->dev.archdata.iommu == DUMMY_DEVICE_DOMAIN_INFO) @@ -2309,6 +2344,8 @@ int intel_map_sg(struct device *hwdev, struct scatterlist *sglist, int nelems, if (!domain) return 0; + iommu = domain_get_iommu(domain); + for_each_sg(sglist, sg, nelems, i) { addr = SG_ENT_VIRT_ADDRESS(sg); addr = (void *)virt_to_phys(addr); @@ -2326,7 +2363,7 @@ int intel_map_sg(struct device *hwdev, struct scatterlist *sglist, int nelems, * mappings.. */ if (dir == DMA_TO_DEVICE || dir == DMA_BIDIRECTIONAL || \ - !cap_zlr(domain->iommu->cap)) + !cap_zlr(iommu->cap)) prot |= DMA_PTE_READ; if (dir == DMA_FROM_DEVICE || dir == DMA_BIDIRECTIONAL) prot |= DMA_PTE_WRITE; @@ -2358,9 +2395,9 @@ int intel_map_sg(struct device *hwdev, struct scatterlist *sglist, int nelems, } /* it's a non-present to present mapping */ - if (iommu_flush_iotlb_psi(domain->iommu, domain->id, + if (iommu_flush_iotlb_psi(iommu, domain->id, start_addr, offset >> VTD_PAGE_SHIFT, 1)) - iommu_flush_write_buffer(domain->iommu); + iommu_flush_write_buffer(iommu); return nelems; } -- cgit v1.2.2 From 1b5736839ae13dadc5947940144f95dd0f4a4a8c Mon Sep 17 00:00:00 2001 From: Weidong Han Date: Mon, 8 Dec 2008 15:34:06 +0800 Subject: calculate agaw for each iommu "SAGAW" capability may be different across iommus. Use a default agaw, but if default agaw is not supported in some iommus, choose a less supported agaw. Signed-off-by: Weidong Han Signed-off-by: Joerg Roedel --- drivers/pci/dmar.c | 10 ++++++++++ drivers/pci/intel-iommu.c | 22 ++++++++++++++++++++++ 2 files changed, 32 insertions(+) (limited to 'drivers') diff --git a/drivers/pci/dmar.c b/drivers/pci/dmar.c index 5f164ff3026e..f5a662a50acb 100644 --- a/drivers/pci/dmar.c +++ b/drivers/pci/dmar.c @@ -491,6 +491,7 @@ int alloc_iommu(struct dmar_drhd_unit *drhd) int map_size; u32 ver; static int iommu_allocated = 0; + int agaw; iommu = kzalloc(sizeof(*iommu), GFP_KERNEL); if (!iommu) @@ -506,6 +507,15 @@ int alloc_iommu(struct dmar_drhd_unit *drhd) iommu->cap = dmar_readq(iommu->reg + DMAR_CAP_REG); iommu->ecap = dmar_readq(iommu->reg + DMAR_ECAP_REG); + agaw = iommu_calculate_agaw(iommu); + if (agaw < 0) { + printk(KERN_ERR + "Cannot get a valid agaw for iommu (seq_id = %d)\n", + iommu->seq_id); + goto error; + } + iommu->agaw = agaw; + /* the registers might be more than one page */ map_size = max_t(int, ecap_max_iotlb_offset(iommu->ecap), cap_max_fault_reg_offset(iommu->cap)); diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 9dca689215eb..3ecfa2304c2c 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -362,6 +362,28 @@ void free_iova_mem(struct iova *iova) kmem_cache_free(iommu_iova_cache, iova); } + +static inline int width_to_agaw(int width); + +/* calculate agaw for each iommu. + * "SAGAW" may be different across iommus, use a default agaw, and + * get a supported less agaw for iommus that don't support the default agaw. + */ +int iommu_calculate_agaw(struct intel_iommu *iommu) +{ + unsigned long sagaw; + int agaw = -1; + + sagaw = cap_sagaw(iommu->cap); + for (agaw = width_to_agaw(DEFAULT_DOMAIN_ADDRESS_WIDTH); + agaw >= 0; agaw--) { + if (test_bit(agaw, &sagaw)) + break; + } + + return agaw; +} + /* in native case, each domain is related to only one iommu */ static struct intel_iommu *domain_get_iommu(struct dmar_domain *domain) { -- cgit v1.2.2 From 8e604097ddc483eb1e6e99564953e4e937fe439a Mon Sep 17 00:00:00 2001 From: Weidong Han Date: Mon, 8 Dec 2008 15:49:06 +0800 Subject: iommu coherency In dmar_domain, more than one iommus may be included in iommu_bmp. Due to "Coherency" capability may be different across iommus, set this variable to indicate iommu access is coherent or not. Only when all related iommus in a dmar_domain are all coherent, iommu access of this domain is coherent. Signed-off-by: Weidong Han Signed-off-by: Joerg Roedel --- drivers/pci/intel-iommu.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'drivers') diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 3ecfa2304c2c..104e99df2ade 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -221,6 +221,8 @@ struct dmar_domain { int agaw; int flags; /* flags to find out type of domain */ + + int iommu_coherency;/* indicate coherency of iommu access */ }; /* PCI domain-device relationship */ @@ -396,6 +398,23 @@ static struct intel_iommu *domain_get_iommu(struct dmar_domain *domain) return g_iommus[iommu_id]; } +/* "Coherency" capability may be different across iommus */ +static void domain_update_iommu_coherency(struct dmar_domain *domain) +{ + int i; + + domain->iommu_coherency = 1; + + i = find_first_bit(&domain->iommu_bmp, g_num_of_iommus); + for (; i < g_num_of_iommus; ) { + if (!ecap_coherent(g_iommus[i]->ecap)) { + domain->iommu_coherency = 0; + break; + } + i = find_next_bit(&domain->iommu_bmp, g_num_of_iommus, i+1); + } +} + /* Gets context entry for a given bus and devfn */ static struct context_entry * device_to_context_entry(struct intel_iommu *iommu, u8 bus, u8 devfn) @@ -1346,6 +1365,11 @@ static int domain_init(struct dmar_domain *domain, int guest_width) domain->agaw = agaw; INIT_LIST_HEAD(&domain->devices); + if (ecap_coherent(iommu->ecap)) + domain->iommu_coherency = 1; + else + domain->iommu_coherency = 0; + /* always allocate the top pgd */ domain->pgd = (struct dma_pte *)alloc_pgtable_page(); if (!domain->pgd) -- cgit v1.2.2 From 1ce28feb22833645a0f3843cd873a0b56ed19ef0 Mon Sep 17 00:00:00 2001 From: Weidong Han Date: Mon, 8 Dec 2008 16:35:39 +0800 Subject: Add domain flag DOMAIN_FLAG_VIRTUAL_MACHINE Add this flag for VT-d used in virtual machine, like KVM. Signed-off-by: Weidong Han Signed-off-by: Joerg Roedel --- drivers/pci/intel-iommu.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 104e99df2ade..ffbe4c573729 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -206,6 +206,11 @@ static inline bool dma_pte_present(struct dma_pte *pte) /* devices under the same p2p bridge are owned in one domain */ #define DOMAIN_FLAG_P2P_MULTIPLE_DEVICES (1 < 0) +/* domain represents a virtual machine, more than one devices + * across iommus may be owned in one domain, e.g. kvm guest. + */ +#define DOMAIN_FLAG_VIRTUAL_MACHINE (1 << 1) + struct dmar_domain { int id; /* domain id */ unsigned long iommu_bmp; /* bitmap of iommus this domain uses*/ @@ -391,6 +396,8 @@ static struct intel_iommu *domain_get_iommu(struct dmar_domain *domain) { int iommu_id; + BUG_ON(domain->flags & DOMAIN_FLAG_VIRTUAL_MACHINE); + iommu_id = find_first_bit(&domain->iommu_bmp, g_num_of_iommus); if (iommu_id < 0 || iommu_id >= g_num_of_iommus) return NULL; -- cgit v1.2.2 From c7151a8dfefd11108de5b4293af2390962bcff71 Mon Sep 17 00:00:00 2001 From: Weidong Han Date: Mon, 8 Dec 2008 22:51:37 +0800 Subject: Add/remove domain device info for virtual machine domain Add iommu reference count in domain, and add a lock to protect iommu setting including iommu_bmp, iommu_count and iommu_coherency. virtual machine domain may have multiple devices from different iommus, so it needs to do more things when add/remove domain device info. Thus implement separate these functions for virtual machine domain. Signed-off-by: Weidong Han Signed-off-by: Joerg Roedel --- drivers/pci/intel-iommu.c | 171 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 166 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index ffbe4c573729..6ed18faa1198 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -228,6 +228,8 @@ struct dmar_domain { int flags; /* flags to find out type of domain */ int iommu_coherency;/* indicate coherency of iommu access */ + int iommu_count; /* reference count of iommu */ + spinlock_t iommu_lock; /* protect iommu set in domain */ }; /* PCI domain-device relationship */ @@ -422,6 +424,27 @@ static void domain_update_iommu_coherency(struct dmar_domain *domain) } } +static struct intel_iommu *device_to_iommu(u8 bus, u8 devfn) +{ + struct dmar_drhd_unit *drhd = NULL; + int i; + + for_each_drhd_unit(drhd) { + if (drhd->ignored) + continue; + + for (i = 0; i < drhd->devices_cnt; i++) + if (drhd->devices[i]->bus->number == bus && + drhd->devices[i]->devfn == devfn) + return drhd->iommu; + + if (drhd->include_all) + return drhd->iommu; + } + + return NULL; +} + /* Gets context entry for a given bus and devfn */ static struct context_entry * device_to_context_entry(struct intel_iommu *iommu, u8 bus, u8 devfn) @@ -1196,12 +1219,18 @@ void free_dmar_iommu(struct intel_iommu *iommu) { struct dmar_domain *domain; int i; + unsigned long flags; i = find_first_bit(iommu->domain_ids, cap_ndoms(iommu->cap)); for (; i < cap_ndoms(iommu->cap); ) { domain = iommu->domains[i]; clear_bit(i, iommu->domain_ids); - domain_exit(domain); + + spin_lock_irqsave(&domain->iommu_lock, flags); + if (--domain->iommu_count == 0) + domain_exit(domain); + spin_unlock_irqrestore(&domain->iommu_lock, flags); + i = find_next_bit(iommu->domain_ids, cap_ndoms(iommu->cap), i+1); } @@ -1351,6 +1380,7 @@ static int domain_init(struct dmar_domain *domain, int guest_width) init_iova_domain(&domain->iovad, DMA_32BIT_PFN); spin_lock_init(&domain->mapping_lock); + spin_lock_init(&domain->iommu_lock); domain_reserve_special_ranges(domain); @@ -1377,6 +1407,8 @@ static int domain_init(struct dmar_domain *domain, int guest_width) else domain->iommu_coherency = 0; + domain->iommu_count = 1; + /* always allocate the top pgd */ domain->pgd = (struct dma_pte *)alloc_pgtable_page(); if (!domain->pgd) @@ -1445,6 +1477,13 @@ static int domain_context_mapping_one(struct dmar_domain *domain, iommu->flush.flush_iotlb(iommu, 0, 0, 0, DMA_TLB_DSI_FLUSH, 0); spin_unlock_irqrestore(&iommu->lock, flags); + + spin_lock_irqsave(&domain->iommu_lock, flags); + if (!test_and_set_bit(iommu->seq_id, &domain->iommu_bmp)) { + domain->iommu_count++; + domain_update_iommu_coherency(domain); + } + spin_unlock_irqrestore(&domain->iommu_lock, flags); return 0; } @@ -1547,9 +1586,10 @@ domain_page_mapping(struct dmar_domain *domain, dma_addr_t iova, return 0; } -static void detach_domain_for_dev(struct dmar_domain *domain, u8 bus, u8 devfn) +static void iommu_detach_dev(struct intel_iommu *iommu, u8 bus, u8 devfn) { - struct intel_iommu *iommu = domain_get_iommu(domain); + if (!iommu) + return; clear_context_table(iommu, bus, devfn); iommu->flush.flush_context(iommu, 0, 0, 0, @@ -1562,6 +1602,7 @@ static void domain_remove_dev_info(struct dmar_domain *domain) { struct device_domain_info *info; unsigned long flags; + struct intel_iommu *iommu; spin_lock_irqsave(&device_domain_lock, flags); while (!list_empty(&domain->devices)) { @@ -1573,7 +1614,8 @@ static void domain_remove_dev_info(struct dmar_domain *domain) info->dev->dev.archdata.iommu = NULL; spin_unlock_irqrestore(&device_domain_lock, flags); - detach_domain_for_dev(info->domain, info->bus, info->devfn); + iommu = device_to_iommu(info->bus, info->devfn); + iommu_detach_dev(iommu, info->bus, info->devfn); free_devinfo_mem(info); spin_lock_irqsave(&device_domain_lock, flags); @@ -2625,6 +2667,122 @@ int __init intel_iommu_init(void) return 0; } +static int vm_domain_add_dev_info(struct dmar_domain *domain, + struct pci_dev *pdev) +{ + struct device_domain_info *info; + unsigned long flags; + + info = alloc_devinfo_mem(); + if (!info) + return -ENOMEM; + + info->bus = pdev->bus->number; + info->devfn = pdev->devfn; + info->dev = pdev; + info->domain = domain; + + spin_lock_irqsave(&device_domain_lock, flags); + list_add(&info->link, &domain->devices); + list_add(&info->global, &device_domain_list); + pdev->dev.archdata.iommu = info; + spin_unlock_irqrestore(&device_domain_lock, flags); + + return 0; +} + +static void vm_domain_remove_one_dev_info(struct dmar_domain *domain, + struct pci_dev *pdev) +{ + struct device_domain_info *info; + struct intel_iommu *iommu; + unsigned long flags; + int found = 0; + struct list_head *entry, *tmp; + + iommu = device_to_iommu(pdev->bus->number, pdev->devfn); + if (!iommu) + return; + + spin_lock_irqsave(&device_domain_lock, flags); + list_for_each_safe(entry, tmp, &domain->devices) { + info = list_entry(entry, struct device_domain_info, link); + if (info->bus == pdev->bus->number && + info->devfn == pdev->devfn) { + list_del(&info->link); + list_del(&info->global); + if (info->dev) + info->dev->dev.archdata.iommu = NULL; + spin_unlock_irqrestore(&device_domain_lock, flags); + + iommu_detach_dev(iommu, info->bus, info->devfn); + free_devinfo_mem(info); + + spin_lock_irqsave(&device_domain_lock, flags); + + if (found) + break; + else + continue; + } + + /* if there is no other devices under the same iommu + * owned by this domain, clear this iommu in iommu_bmp + * update iommu count and coherency + */ + if (device_to_iommu(info->bus, info->devfn) == iommu) + found = 1; + } + + if (found == 0) { + unsigned long tmp_flags; + spin_lock_irqsave(&domain->iommu_lock, tmp_flags); + clear_bit(iommu->seq_id, &domain->iommu_bmp); + domain->iommu_count--; + domain_update_iommu_coherency(domain); + spin_unlock_irqrestore(&domain->iommu_lock, tmp_flags); + } + + spin_unlock_irqrestore(&device_domain_lock, flags); +} + +static void vm_domain_remove_all_dev_info(struct dmar_domain *domain) +{ + struct device_domain_info *info; + struct intel_iommu *iommu; + unsigned long flags1, flags2; + + spin_lock_irqsave(&device_domain_lock, flags1); + while (!list_empty(&domain->devices)) { + info = list_entry(domain->devices.next, + struct device_domain_info, link); + list_del(&info->link); + list_del(&info->global); + if (info->dev) + info->dev->dev.archdata.iommu = NULL; + + spin_unlock_irqrestore(&device_domain_lock, flags1); + + iommu = device_to_iommu(info->bus, info->devfn); + iommu_detach_dev(iommu, info->bus, info->devfn); + + /* clear this iommu in iommu_bmp, update iommu count + * and coherency + */ + spin_lock_irqsave(&domain->iommu_lock, flags2); + if (test_and_clear_bit(iommu->seq_id, + &domain->iommu_bmp)) { + domain->iommu_count--; + domain_update_iommu_coherency(domain); + } + spin_unlock_irqrestore(&domain->iommu_lock, flags2); + + free_devinfo_mem(info); + spin_lock_irqsave(&device_domain_lock, flags1); + } + spin_unlock_irqrestore(&device_domain_lock, flags1); +} + void intel_iommu_domain_exit(struct dmar_domain *domain) { u64 end; @@ -2702,7 +2860,10 @@ EXPORT_SYMBOL_GPL(intel_iommu_page_mapping); void intel_iommu_detach_dev(struct dmar_domain *domain, u8 bus, u8 devfn) { - detach_domain_for_dev(domain, bus, devfn); + struct intel_iommu *iommu; + + iommu = device_to_iommu(bus, devfn); + iommu_detach_dev(iommu, bus, devfn); } EXPORT_SYMBOL_GPL(intel_iommu_detach_dev); -- cgit v1.2.2 From 5331fe6f5627e06eec7d0dc154a0a3a9c27813c5 Mon Sep 17 00:00:00 2001 From: Weidong Han Date: Mon, 8 Dec 2008 23:00:00 +0800 Subject: Add domain_flush_cache Because virtual machine domain may have multiple devices from different iommus, it cannot use __iommu_flush_cache. In some common low level functions, use domain_flush_cache instead of __iommu_flush_cache. On the other hand, in some functions, iommu can is specified or domain cannot be got, still use __iommu_flush_cache Signed-off-by: Weidong Han Signed-off-by: Joerg Roedel --- drivers/pci/intel-iommu.c | 43 ++++++++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 6ed18faa1198..f0a21995b135 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -445,6 +445,13 @@ static struct intel_iommu *device_to_iommu(u8 bus, u8 devfn) return NULL; } +static void domain_flush_cache(struct dmar_domain *domain, + void *addr, int size) +{ + if (!domain->iommu_coherency) + clflush_cache_range(addr, size); +} + /* Gets context entry for a given bus and devfn */ static struct context_entry * device_to_context_entry(struct intel_iommu *iommu, u8 bus, u8 devfn) @@ -585,7 +592,6 @@ static struct dma_pte * addr_to_dma_pte(struct dmar_domain *domain, u64 addr) int level = agaw_to_level(domain->agaw); int offset; unsigned long flags; - struct intel_iommu *iommu = domain_get_iommu(domain); BUG_ON(!domain->pgd); @@ -609,8 +615,7 @@ static struct dma_pte * addr_to_dma_pte(struct dmar_domain *domain, u64 addr) flags); return NULL; } - __iommu_flush_cache(iommu, tmp_page, - PAGE_SIZE); + domain_flush_cache(domain, tmp_page, PAGE_SIZE); dma_set_pte_addr(pte, virt_to_phys(tmp_page)); /* * high level table always sets r/w, last level page @@ -618,7 +623,7 @@ static struct dma_pte * addr_to_dma_pte(struct dmar_domain *domain, u64 addr) */ dma_set_pte_readable(pte); dma_set_pte_writable(pte); - __iommu_flush_cache(iommu, pte, sizeof(*pte)); + domain_flush_cache(domain, pte, sizeof(*pte)); } parent = phys_to_virt(dma_pte_addr(pte)); level--; @@ -655,14 +660,13 @@ static struct dma_pte *dma_addr_level_pte(struct dmar_domain *domain, u64 addr, static void dma_pte_clear_one(struct dmar_domain *domain, u64 addr) { struct dma_pte *pte = NULL; - struct intel_iommu *iommu = domain_get_iommu(domain); /* get last level pte */ pte = dma_addr_level_pte(domain, addr, 1); if (pte) { dma_clear_pte(pte); - __iommu_flush_cache(iommu, pte, sizeof(*pte)); + domain_flush_cache(domain, pte, sizeof(*pte)); } } @@ -693,7 +697,6 @@ static void dma_pte_free_pagetable(struct dmar_domain *domain, int total = agaw_to_level(domain->agaw); int level; u64 tmp; - struct intel_iommu *iommu = domain_get_iommu(domain); start &= (((u64)1) << addr_width) - 1; end &= (((u64)1) << addr_width) - 1; @@ -711,8 +714,7 @@ static void dma_pte_free_pagetable(struct dmar_domain *domain, free_pgtable_page( phys_to_virt(dma_pte_addr(pte))); dma_clear_pte(pte); - __iommu_flush_cache(iommu, - pte, sizeof(*pte)); + domain_flush_cache(domain, pte, sizeof(*pte)); } tmp += level_size(level); } @@ -1445,12 +1447,17 @@ static int domain_context_mapping_one(struct dmar_domain *domain, u8 bus, u8 devfn) { struct context_entry *context; - struct intel_iommu *iommu = domain_get_iommu(domain); unsigned long flags; + struct intel_iommu *iommu; pr_debug("Set context mapping for %02x:%02x.%d\n", bus, PCI_SLOT(devfn), PCI_FUNC(devfn)); BUG_ON(!domain->pgd); + + iommu = device_to_iommu(bus, devfn); + if (!iommu) + return -ENODEV; + context = device_to_context_entry(iommu, bus, devfn); if (!context) return -ENOMEM; @@ -1466,7 +1473,7 @@ static int domain_context_mapping_one(struct dmar_domain *domain, context_set_translation_type(context, CONTEXT_TT_MULTI_LEVEL); context_set_fault_enable(context); context_set_present(context); - __iommu_flush_cache(iommu, context, sizeof(*context)); + domain_flush_cache(domain, context, sizeof(*context)); /* it's a non-present to present mapping */ if (iommu->flush.flush_context(iommu, domain->id, @@ -1519,12 +1526,15 @@ domain_context_mapping(struct dmar_domain *domain, struct pci_dev *pdev) tmp->bus->number, tmp->devfn); } -static int domain_context_mapped(struct dmar_domain *domain, - struct pci_dev *pdev) +static int domain_context_mapped(struct pci_dev *pdev) { int ret; struct pci_dev *tmp, *parent; - struct intel_iommu *iommu = domain_get_iommu(domain); + struct intel_iommu *iommu; + + iommu = device_to_iommu(pdev->bus->number, pdev->devfn); + if (!iommu) + return -ENODEV; ret = device_context_mapped(iommu, pdev->bus->number, pdev->devfn); @@ -1559,7 +1569,6 @@ domain_page_mapping(struct dmar_domain *domain, dma_addr_t iova, struct dma_pte *pte; int index; int addr_width = agaw_to_width(domain->agaw); - struct intel_iommu *iommu = domain_get_iommu(domain); hpa &= (((u64)1) << addr_width) - 1; @@ -1579,7 +1588,7 @@ domain_page_mapping(struct dmar_domain *domain, dma_addr_t iova, BUG_ON(dma_pte_addr(pte)); dma_set_pte_addr(pte, start_pfn << VTD_PAGE_SHIFT); dma_set_pte_prot(pte, prot); - __iommu_flush_cache(iommu, pte, sizeof(*pte)); + domain_flush_cache(domain, pte, sizeof(*pte)); start_pfn++; index++; } @@ -2129,7 +2138,7 @@ get_valid_domain_for_dev(struct pci_dev *pdev) } /* make sure context mapping is ok */ - if (unlikely(!domain_context_mapped(domain, pdev))) { + if (unlikely(!domain_context_mapped(pdev))) { ret = domain_context_mapping(domain, pdev); if (ret) { printk(KERN_ERR -- cgit v1.2.2 From 5e98c4b1d6e89676193c355e430eddf369bcf195 Mon Sep 17 00:00:00 2001 From: Weidong Han Date: Mon, 8 Dec 2008 23:03:27 +0800 Subject: Allocation and free functions of virtual machine domain virtual machine domain is different from native DMA-API domain, implement separate allocation and free functions for virtual machine domain. Signed-off-by: Weidong Han Signed-off-by: Joerg Roedel --- drivers/pci/intel-iommu.c | 107 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 105 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index f0a21995b135..171f6c61fa1d 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -1216,6 +1216,7 @@ static int iommu_init_domains(struct intel_iommu *iommu) static void domain_exit(struct dmar_domain *domain); +static void vm_domain_exit(struct dmar_domain *domain); void free_dmar_iommu(struct intel_iommu *iommu) { @@ -1229,8 +1230,12 @@ void free_dmar_iommu(struct intel_iommu *iommu) clear_bit(i, iommu->domain_ids); spin_lock_irqsave(&domain->iommu_lock, flags); - if (--domain->iommu_count == 0) - domain_exit(domain); + if (--domain->iommu_count == 0) { + if (domain->flags & DOMAIN_FLAG_VIRTUAL_MACHINE) + vm_domain_exit(domain); + else + domain_exit(domain); + } spin_unlock_irqrestore(&domain->iommu_lock, flags); i = find_next_bit(iommu->domain_ids, @@ -2792,6 +2797,104 @@ static void vm_domain_remove_all_dev_info(struct dmar_domain *domain) spin_unlock_irqrestore(&device_domain_lock, flags1); } +/* domain id for virtual machine, it won't be set in context */ +static unsigned long vm_domid; + +static struct dmar_domain *iommu_alloc_vm_domain(void) +{ + struct dmar_domain *domain; + + domain = alloc_domain_mem(); + if (!domain) + return NULL; + + domain->id = vm_domid++; + memset(&domain->iommu_bmp, 0, sizeof(unsigned long)); + domain->flags = DOMAIN_FLAG_VIRTUAL_MACHINE; + + return domain; +} + +static int vm_domain_init(struct dmar_domain *domain, int guest_width) +{ + int adjust_width; + + init_iova_domain(&domain->iovad, DMA_32BIT_PFN); + spin_lock_init(&domain->mapping_lock); + spin_lock_init(&domain->iommu_lock); + + domain_reserve_special_ranges(domain); + + /* calculate AGAW */ + domain->gaw = guest_width; + adjust_width = guestwidth_to_adjustwidth(guest_width); + domain->agaw = width_to_agaw(adjust_width); + + INIT_LIST_HEAD(&domain->devices); + + domain->iommu_count = 0; + domain->iommu_coherency = 0; + + /* always allocate the top pgd */ + domain->pgd = (struct dma_pte *)alloc_pgtable_page(); + if (!domain->pgd) + return -ENOMEM; + domain_flush_cache(domain, domain->pgd, PAGE_SIZE); + return 0; +} + +static void iommu_free_vm_domain(struct dmar_domain *domain) +{ + unsigned long flags; + struct dmar_drhd_unit *drhd; + struct intel_iommu *iommu; + unsigned long i; + unsigned long ndomains; + + for_each_drhd_unit(drhd) { + if (drhd->ignored) + continue; + iommu = drhd->iommu; + + ndomains = cap_ndoms(iommu->cap); + i = find_first_bit(iommu->domain_ids, ndomains); + for (; i < ndomains; ) { + if (iommu->domains[i] == domain) { + spin_lock_irqsave(&iommu->lock, flags); + clear_bit(i, iommu->domain_ids); + iommu->domains[i] = NULL; + spin_unlock_irqrestore(&iommu->lock, flags); + break; + } + i = find_next_bit(iommu->domain_ids, ndomains, i+1); + } + } +} + +static void vm_domain_exit(struct dmar_domain *domain) +{ + u64 end; + + /* Domain 0 is reserved, so dont process it */ + if (!domain) + return; + + vm_domain_remove_all_dev_info(domain); + /* destroy iovas */ + put_iova_domain(&domain->iovad); + end = DOMAIN_MAX_ADDR(domain->gaw); + end = end & (~VTD_PAGE_MASK); + + /* clear ptes */ + dma_pte_clear_range(domain, 0, end); + + /* free page tables */ + dma_pte_free_pagetable(domain, 0, end); + + iommu_free_vm_domain(domain); + free_domain_mem(domain); +} + void intel_iommu_domain_exit(struct dmar_domain *domain) { u64 end; -- cgit v1.2.2 From ea6606b02fc3192f2edab2db669fa0b9756b4e67 Mon Sep 17 00:00:00 2001 From: Weidong Han Date: Mon, 8 Dec 2008 23:08:15 +0800 Subject: Change domain_context_mapping_one for virtual machine domain vm_domid won't be set in context, find available domain id for a device from its iommu. For a virtual machine domain, a default agaw will be set, and skip top levels of page tables for iommu which has less agaw than default. Signed-off-by: Weidong Han Signed-off-by: Joerg Roedel --- drivers/pci/intel-iommu.c | 55 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 52 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 171f6c61fa1d..8a204d5bb427 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -1454,6 +1454,11 @@ static int domain_context_mapping_one(struct dmar_domain *domain, struct context_entry *context; unsigned long flags; struct intel_iommu *iommu; + struct dma_pte *pgd; + unsigned long num; + unsigned long ndomains; + int id; + int agaw; pr_debug("Set context mapping for %02x:%02x.%d\n", bus, PCI_SLOT(devfn), PCI_FUNC(devfn)); @@ -1472,9 +1477,53 @@ static int domain_context_mapping_one(struct dmar_domain *domain, return 0; } - context_set_domain_id(context, domain->id); - context_set_address_width(context, domain->agaw); - context_set_address_root(context, virt_to_phys(domain->pgd)); + id = domain->id; + pgd = domain->pgd; + + if (domain->flags & DOMAIN_FLAG_VIRTUAL_MACHINE) { + int found = 0; + + /* find an available domain id for this device in iommu */ + ndomains = cap_ndoms(iommu->cap); + num = find_first_bit(iommu->domain_ids, ndomains); + for (; num < ndomains; ) { + if (iommu->domains[num] == domain) { + id = num; + found = 1; + break; + } + num = find_next_bit(iommu->domain_ids, + cap_ndoms(iommu->cap), num+1); + } + + if (found == 0) { + num = find_first_zero_bit(iommu->domain_ids, ndomains); + if (num >= ndomains) { + spin_unlock_irqrestore(&iommu->lock, flags); + printk(KERN_ERR "IOMMU: no free domain ids\n"); + return -EFAULT; + } + + set_bit(num, iommu->domain_ids); + iommu->domains[num] = domain; + id = num; + } + + /* Skip top levels of page tables for + * iommu which has less agaw than default. + */ + for (agaw = domain->agaw; agaw != iommu->agaw; agaw--) { + pgd = phys_to_virt(dma_pte_addr(pgd)); + if (!dma_pte_present(pgd)) { + spin_unlock_irqrestore(&iommu->lock, flags); + return -ENOMEM; + } + } + } + + context_set_domain_id(context, id); + context_set_address_width(context, iommu->agaw); + context_set_address_root(context, virt_to_phys(pgd)); context_set_translation_type(context, CONTEXT_TT_MULTI_LEVEL); context_set_fault_enable(context); context_set_present(context); -- cgit v1.2.2 From faa3d6f5ffe7bf60ebfd0d36513fbcda0eb0ea1a Mon Sep 17 00:00:00 2001 From: Weidong Han Date: Mon, 8 Dec 2008 23:09:29 +0800 Subject: Change intel iommu APIs of virtual machine domain These APIs are used by KVM to use VT-d Signed-off-by: Weidong Han Signed-off-by: Joerg Roedel --- drivers/pci/intel-iommu.c | 129 +++++++++++++++++++++------------------------- 1 file changed, 59 insertions(+), 70 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 8a204d5bb427..f1380269cabd 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -2944,96 +2944,87 @@ static void vm_domain_exit(struct dmar_domain *domain) free_domain_mem(domain); } -void intel_iommu_domain_exit(struct dmar_domain *domain) +struct dmar_domain *intel_iommu_alloc_domain(void) { - u64 end; - - /* Domain 0 is reserved, so dont process it */ - if (!domain) - return; - - end = DOMAIN_MAX_ADDR(domain->gaw); - end = end & (~VTD_PAGE_MASK); - - /* clear ptes */ - dma_pte_clear_range(domain, 0, end); - - /* free page tables */ - dma_pte_free_pagetable(domain, 0, end); - - iommu_free_domain(domain); - free_domain_mem(domain); -} -EXPORT_SYMBOL_GPL(intel_iommu_domain_exit); - -struct dmar_domain *intel_iommu_domain_alloc(struct pci_dev *pdev) -{ - struct dmar_drhd_unit *drhd; struct dmar_domain *domain; - struct intel_iommu *iommu; - drhd = dmar_find_matched_drhd_unit(pdev); - if (!drhd) { - printk(KERN_ERR "intel_iommu_domain_alloc: drhd == NULL\n"); - return NULL; - } - - iommu = drhd->iommu; - if (!iommu) { - printk(KERN_ERR - "intel_iommu_domain_alloc: iommu == NULL\n"); - return NULL; - } - domain = iommu_alloc_domain(iommu); + domain = iommu_alloc_vm_domain(); if (!domain) { printk(KERN_ERR "intel_iommu_domain_alloc: domain == NULL\n"); return NULL; } - if (domain_init(domain, DEFAULT_DOMAIN_ADDRESS_WIDTH)) { + if (vm_domain_init(domain, DEFAULT_DOMAIN_ADDRESS_WIDTH)) { printk(KERN_ERR "intel_iommu_domain_alloc: domain_init() failed\n"); - intel_iommu_domain_exit(domain); + vm_domain_exit(domain); return NULL; } + return domain; } -EXPORT_SYMBOL_GPL(intel_iommu_domain_alloc); +EXPORT_SYMBOL_GPL(intel_iommu_alloc_domain); -int intel_iommu_context_mapping( - struct dmar_domain *domain, struct pci_dev *pdev) +void intel_iommu_free_domain(struct dmar_domain *domain) { - int rc; - rc = domain_context_mapping(domain, pdev); - return rc; + vm_domain_exit(domain); } -EXPORT_SYMBOL_GPL(intel_iommu_context_mapping); +EXPORT_SYMBOL_GPL(intel_iommu_free_domain); -int intel_iommu_page_mapping( - struct dmar_domain *domain, dma_addr_t iova, - u64 hpa, size_t size, int prot) +int intel_iommu_attach_device(struct dmar_domain *domain, + struct pci_dev *pdev) { - int rc; - rc = domain_page_mapping(domain, iova, hpa, size, prot); - return rc; + int ret; + + /* normally pdev is not mapped */ + if (unlikely(domain_context_mapped(pdev))) { + struct dmar_domain *old_domain; + + old_domain = find_domain(pdev); + if (old_domain) { + if (domain->flags & DOMAIN_FLAG_VIRTUAL_MACHINE) + vm_domain_remove_one_dev_info(old_domain, pdev); + else + domain_remove_dev_info(old_domain); + } + } + + ret = domain_context_mapping(domain, pdev); + if (ret) + return ret; + + ret = vm_domain_add_dev_info(domain, pdev); + return ret; } -EXPORT_SYMBOL_GPL(intel_iommu_page_mapping); +EXPORT_SYMBOL_GPL(intel_iommu_attach_device); -void intel_iommu_detach_dev(struct dmar_domain *domain, u8 bus, u8 devfn) +void intel_iommu_detach_device(struct dmar_domain *domain, + struct pci_dev *pdev) { - struct intel_iommu *iommu; + vm_domain_remove_one_dev_info(domain, pdev); +} +EXPORT_SYMBOL_GPL(intel_iommu_detach_device); - iommu = device_to_iommu(bus, devfn); - iommu_detach_dev(iommu, bus, devfn); +int intel_iommu_map_address(struct dmar_domain *domain, dma_addr_t iova, + u64 hpa, size_t size, int prot) +{ + int ret; + ret = domain_page_mapping(domain, iova, hpa, size, prot); + return ret; } -EXPORT_SYMBOL_GPL(intel_iommu_detach_dev); +EXPORT_SYMBOL_GPL(intel_iommu_map_address); -struct dmar_domain * -intel_iommu_find_domain(struct pci_dev *pdev) +void intel_iommu_unmap_address(struct dmar_domain *domain, + dma_addr_t iova, size_t size) { - return find_domain(pdev); + dma_addr_t base; + + /* The address might not be aligned */ + base = iova & VTD_PAGE_MASK; + size = VTD_PAGE_ALIGN(size); + dma_pte_clear_range(domain, base, base + size); } -EXPORT_SYMBOL_GPL(intel_iommu_find_domain); +EXPORT_SYMBOL_GPL(intel_iommu_unmap_address); int intel_iommu_found(void) { @@ -3041,17 +3032,15 @@ int intel_iommu_found(void) } EXPORT_SYMBOL_GPL(intel_iommu_found); -u64 intel_iommu_iova_to_pfn(struct dmar_domain *domain, u64 iova) +u64 intel_iommu_iova_to_phys(struct dmar_domain *domain, u64 iova) { struct dma_pte *pte; - u64 pfn; + u64 phys = 0; - pfn = 0; pte = addr_to_dma_pte(domain, iova); - if (pte) - pfn = dma_pte_addr(pte); + phys = dma_pte_addr(pte); - return pfn >> VTD_PAGE_SHIFT; + return phys; } -EXPORT_SYMBOL_GPL(intel_iommu_iova_to_pfn); +EXPORT_SYMBOL_GPL(intel_iommu_iova_to_phys); -- cgit v1.2.2 From fe40f1e020d0923f5f35ca15f02a206c75a28053 Mon Sep 17 00:00:00 2001 From: Weidong Han Date: Mon, 8 Dec 2008 23:10:23 +0800 Subject: Check agaw is sufficient for mapped memory When domain is related to multiple iommus, need to check if the minimum agaw is sufficient for the mapped memory Signed-off-by: Weidong Han Signed-off-by: Joerg Roedel --- drivers/pci/intel-iommu.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) (limited to 'drivers') diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index f1380269cabd..772fb22e1be0 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -230,6 +230,7 @@ struct dmar_domain { int iommu_coherency;/* indicate coherency of iommu access */ int iommu_count; /* reference count of iommu */ spinlock_t iommu_lock; /* protect iommu set in domain */ + u64 max_addr; /* maximum mapped address */ }; /* PCI domain-device relationship */ @@ -2849,6 +2850,22 @@ static void vm_domain_remove_all_dev_info(struct dmar_domain *domain) /* domain id for virtual machine, it won't be set in context */ static unsigned long vm_domid; +static int vm_domain_min_agaw(struct dmar_domain *domain) +{ + int i; + int min_agaw = domain->agaw; + + i = find_first_bit(&domain->iommu_bmp, g_num_of_iommus); + for (; i < g_num_of_iommus; ) { + if (min_agaw > g_iommus[i]->agaw) + min_agaw = g_iommus[i]->agaw; + + i = find_next_bit(&domain->iommu_bmp, g_num_of_iommus, i+1); + } + + return min_agaw; +} + static struct dmar_domain *iommu_alloc_vm_domain(void) { struct dmar_domain *domain; @@ -2883,6 +2900,7 @@ static int vm_domain_init(struct dmar_domain *domain, int guest_width) domain->iommu_count = 0; domain->iommu_coherency = 0; + domain->max_addr = 0; /* always allocate the top pgd */ domain->pgd = (struct dma_pte *)alloc_pgtable_page(); @@ -2974,6 +2992,9 @@ EXPORT_SYMBOL_GPL(intel_iommu_free_domain); int intel_iommu_attach_device(struct dmar_domain *domain, struct pci_dev *pdev) { + struct intel_iommu *iommu; + int addr_width; + u64 end; int ret; /* normally pdev is not mapped */ @@ -2989,6 +3010,21 @@ int intel_iommu_attach_device(struct dmar_domain *domain, } } + iommu = device_to_iommu(pdev->bus->number, pdev->devfn); + if (!iommu) + return -ENODEV; + + /* check if this iommu agaw is sufficient for max mapped address */ + addr_width = agaw_to_width(iommu->agaw); + end = DOMAIN_MAX_ADDR(addr_width); + end = end & VTD_PAGE_MASK; + if (end < domain->max_addr) { + printk(KERN_ERR "%s: iommu agaw (%d) is not " + "sufficient for the mapped address (%llx)\n", + __func__, iommu->agaw, domain->max_addr); + return -EFAULT; + } + ret = domain_context_mapping(domain, pdev); if (ret) return ret; @@ -3008,7 +3044,29 @@ EXPORT_SYMBOL_GPL(intel_iommu_detach_device); int intel_iommu_map_address(struct dmar_domain *domain, dma_addr_t iova, u64 hpa, size_t size, int prot) { + u64 max_addr; + int addr_width; int ret; + + max_addr = (iova & VTD_PAGE_MASK) + VTD_PAGE_ALIGN(size); + if (domain->max_addr < max_addr) { + int min_agaw; + u64 end; + + /* check if minimum agaw is sufficient for mapped address */ + min_agaw = vm_domain_min_agaw(domain); + addr_width = agaw_to_width(min_agaw); + end = DOMAIN_MAX_ADDR(addr_width); + end = end & VTD_PAGE_MASK; + if (end < max_addr) { + printk(KERN_ERR "%s: iommu agaw (%d) is not " + "sufficient for the mapped address (%llx)\n", + __func__, min_agaw, max_addr); + return -EFAULT; + } + domain->max_addr = max_addr; + } + ret = domain_page_mapping(domain, iova, hpa, size, prot); return ret; } @@ -3023,6 +3081,9 @@ void intel_iommu_unmap_address(struct dmar_domain *domain, base = iova & VTD_PAGE_MASK; size = VTD_PAGE_ALIGN(size); dma_pte_clear_range(domain, base, base + size); + + if (domain->max_addr == base + size) + domain->max_addr = base; } EXPORT_SYMBOL_GPL(intel_iommu_unmap_address); -- cgit v1.2.2 From fc2100eb4d0960b56c2c705a97941c08fb1c0fd4 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Wed, 26 Nov 2008 17:21:24 +0100 Subject: add frontend implementation for the IOMMU API This API can be used by KVM for accessing different types of IOMMUs to do device passthrough to guests. Beside that this API can also be used by device drivers to map non-linear host memory into dma-linear addresses to prevent sgather-gather DMA. UIO may be another user for this API. Signed-off-by: Joerg Roedel Acked-by: Greg Kroah-Hartman --- drivers/base/iommu.c | 100 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 drivers/base/iommu.c (limited to 'drivers') diff --git a/drivers/base/iommu.c b/drivers/base/iommu.c new file mode 100644 index 000000000000..5e039d4f877c --- /dev/null +++ b/drivers/base/iommu.c @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2007-2008 Advanced Micro Devices, Inc. + * Author: Joerg Roedel + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include + +static struct iommu_ops *iommu_ops; + +void register_iommu(struct iommu_ops *ops) +{ + if (iommu_ops) + BUG(); + + iommu_ops = ops; +} + +bool iommu_found() +{ + return iommu_ops != NULL; +} +EXPORT_SYMBOL_GPL(iommu_found); + +struct iommu_domain *iommu_domain_alloc(void) +{ + struct iommu_domain *domain; + int ret; + + domain = kmalloc(sizeof(*domain), GFP_KERNEL); + if (!domain) + return NULL; + + ret = iommu_ops->domain_init(domain); + if (ret) + goto out_free; + + return domain; + +out_free: + kfree(domain); + + return NULL; +} +EXPORT_SYMBOL_GPL(iommu_domain_alloc); + +void iommu_domain_free(struct iommu_domain *domain) +{ + iommu_ops->domain_destroy(domain); + kfree(domain); +} +EXPORT_SYMBOL_GPL(iommu_domain_free); + +int iommu_attach_device(struct iommu_domain *domain, struct device *dev) +{ + return iommu_ops->attach_dev(domain, dev); +} +EXPORT_SYMBOL_GPL(iommu_attach_device); + +void iommu_detach_device(struct iommu_domain *domain, struct device *dev) +{ + iommu_ops->detach_dev(domain, dev); +} +EXPORT_SYMBOL_GPL(iommu_detach_device); + +int iommu_map_range(struct iommu_domain *domain, unsigned long iova, + phys_addr_t paddr, size_t size, int prot) +{ + return iommu_ops->map(domain, iova, paddr, size, prot); +} +EXPORT_SYMBOL_GPL(iommu_map_range); + +void iommu_unmap_range(struct iommu_domain *domain, unsigned long iova, + size_t size) +{ + iommu_ops->unmap(domain, iova, size); +} +EXPORT_SYMBOL_GPL(iommu_unmap_range); + +phys_addr_t iommu_iova_to_phys(struct iommu_domain *domain, + unsigned long iova) +{ + return iommu_ops->iova_to_phys(domain, iova); +} +EXPORT_SYMBOL_GPL(iommu_iova_to_phys); -- cgit v1.2.2 From 1aaf118352b85bb359ce28070bcc478f659a7031 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Wed, 26 Nov 2008 17:25:13 +0100 Subject: select IOMMU_API when DMAR and/or AMD_IOMMU is selected These two IOMMUs can implement the current version of this API. So select the API if one or both of these IOMMU drivers is selected. Signed-off-by: Joerg Roedel --- drivers/base/Makefile | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/base/Makefile b/drivers/base/Makefile index c66637392bbc..b5b8ba512b28 100644 --- a/drivers/base/Makefile +++ b/drivers/base/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_FW_LOADER) += firmware_class.o obj-$(CONFIG_NUMA) += node.o obj-$(CONFIG_MEMORY_HOTPLUG_SPARSE) += memory.o obj-$(CONFIG_SMP) += topology.o +obj-$(CONFIG_IOMMU_API) += iommu.o ifeq ($(CONFIG_SYSFS),y) obj-$(CONFIG_MODULES) += module.o endif -- cgit v1.2.2 From 5d450806eb0e569c5846a5825e7f535980b0da32 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Wed, 3 Dec 2008 14:52:32 +0100 Subject: VT-d: adapt domain init and destroy functions for IOMMU API Signed-off-by: Joerg Roedel --- drivers/pci/intel-iommu.c | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 772fb22e1be0..5c95a5a65440 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -2962,32 +2963,34 @@ static void vm_domain_exit(struct dmar_domain *domain) free_domain_mem(domain); } -struct dmar_domain *intel_iommu_alloc_domain(void) +static int intel_iommu_domain_init(struct iommu_domain *domain) { - struct dmar_domain *domain; + struct dmar_domain *dmar_domain; - domain = iommu_alloc_vm_domain(); - if (!domain) { + dmar_domain = iommu_alloc_vm_domain(); + if (!dmar_domain) { printk(KERN_ERR - "intel_iommu_domain_alloc: domain == NULL\n"); - return NULL; + "intel_iommu_domain_init: dmar_domain == NULL\n"); + return -ENOMEM; } - if (vm_domain_init(domain, DEFAULT_DOMAIN_ADDRESS_WIDTH)) { + if (vm_domain_init(dmar_domain, DEFAULT_DOMAIN_ADDRESS_WIDTH)) { printk(KERN_ERR - "intel_iommu_domain_alloc: domain_init() failed\n"); - vm_domain_exit(domain); - return NULL; + "intel_iommu_domain_init() failed\n"); + vm_domain_exit(dmar_domain); + return -ENOMEM; } + domain->priv = dmar_domain; - return domain; + return 0; } -EXPORT_SYMBOL_GPL(intel_iommu_alloc_domain); -void intel_iommu_free_domain(struct dmar_domain *domain) +static void intel_iommu_domain_destroy(struct iommu_domain *domain) { - vm_domain_exit(domain); + struct dmar_domain *dmar_domain = domain->priv; + + domain->priv = NULL; + vm_domain_exit(dmar_domain); } -EXPORT_SYMBOL_GPL(intel_iommu_free_domain); int intel_iommu_attach_device(struct dmar_domain *domain, struct pci_dev *pdev) -- cgit v1.2.2 From 4c5478c94eb29e6101f1f13175f7455bc8b5d953 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Wed, 3 Dec 2008 14:58:24 +0100 Subject: VT-d: adapt device attach and detach functions for IOMMU API Signed-off-by: Joerg Roedel --- drivers/pci/intel-iommu.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 5c95a5a65440..db9a26cfeb8f 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -2992,9 +2992,11 @@ static void intel_iommu_domain_destroy(struct iommu_domain *domain) vm_domain_exit(dmar_domain); } -int intel_iommu_attach_device(struct dmar_domain *domain, - struct pci_dev *pdev) +static int intel_iommu_attach_device(struct iommu_domain *domain, + struct device *dev) { + struct dmar_domain *dmar_domain = domain->priv; + struct pci_dev *pdev = to_pci_dev(dev); struct intel_iommu *iommu; int addr_width; u64 end; @@ -3006,7 +3008,7 @@ int intel_iommu_attach_device(struct dmar_domain *domain, old_domain = find_domain(pdev); if (old_domain) { - if (domain->flags & DOMAIN_FLAG_VIRTUAL_MACHINE) + if (dmar_domain->flags & DOMAIN_FLAG_VIRTUAL_MACHINE) vm_domain_remove_one_dev_info(old_domain, pdev); else domain_remove_dev_info(old_domain); @@ -3021,28 +3023,29 @@ int intel_iommu_attach_device(struct dmar_domain *domain, addr_width = agaw_to_width(iommu->agaw); end = DOMAIN_MAX_ADDR(addr_width); end = end & VTD_PAGE_MASK; - if (end < domain->max_addr) { + if (end < dmar_domain->max_addr) { printk(KERN_ERR "%s: iommu agaw (%d) is not " "sufficient for the mapped address (%llx)\n", - __func__, iommu->agaw, domain->max_addr); + __func__, iommu->agaw, dmar_domain->max_addr); return -EFAULT; } - ret = domain_context_mapping(domain, pdev); + ret = domain_context_mapping(dmar_domain, pdev); if (ret) return ret; - ret = vm_domain_add_dev_info(domain, pdev); + ret = vm_domain_add_dev_info(dmar_domain, pdev); return ret; } -EXPORT_SYMBOL_GPL(intel_iommu_attach_device); -void intel_iommu_detach_device(struct dmar_domain *domain, - struct pci_dev *pdev) +static void intel_iommu_detach_device(struct iommu_domain *domain, + struct device *dev) { - vm_domain_remove_one_dev_info(domain, pdev); + struct dmar_domain *dmar_domain = domain->priv; + struct pci_dev *pdev = to_pci_dev(dev); + + vm_domain_remove_one_dev_info(dmar_domain, pdev); } -EXPORT_SYMBOL_GPL(intel_iommu_detach_device); int intel_iommu_map_address(struct dmar_domain *domain, dma_addr_t iova, u64 hpa, size_t size, int prot) -- cgit v1.2.2 From dde57a210dcdce85e2813bab8f88687761d9f6a6 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Wed, 3 Dec 2008 15:04:09 +0100 Subject: VT-d: adapt domain map and unmap functions for IOMMU API Signed-off-by: Joerg Roedel --- drivers/pci/intel-iommu.c | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index db9a26cfeb8f..8af6c96f31b3 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -3047,20 +3047,28 @@ static void intel_iommu_detach_device(struct iommu_domain *domain, vm_domain_remove_one_dev_info(dmar_domain, pdev); } -int intel_iommu_map_address(struct dmar_domain *domain, dma_addr_t iova, - u64 hpa, size_t size, int prot) +static int intel_iommu_map_range(struct iommu_domain *domain, + unsigned long iova, phys_addr_t hpa, + size_t size, int iommu_prot) { + struct dmar_domain *dmar_domain = domain->priv; u64 max_addr; int addr_width; + int prot = 0; int ret; + if (iommu_prot & IOMMU_READ) + prot |= DMA_PTE_READ; + if (iommu_prot & IOMMU_WRITE) + prot |= DMA_PTE_WRITE; + max_addr = (iova & VTD_PAGE_MASK) + VTD_PAGE_ALIGN(size); - if (domain->max_addr < max_addr) { + if (dmar_domain->max_addr < max_addr) { int min_agaw; u64 end; /* check if minimum agaw is sufficient for mapped address */ - min_agaw = vm_domain_min_agaw(domain); + min_agaw = vm_domain_min_agaw(dmar_domain); addr_width = agaw_to_width(min_agaw); end = DOMAIN_MAX_ADDR(addr_width); end = end & VTD_PAGE_MASK; @@ -3070,28 +3078,27 @@ int intel_iommu_map_address(struct dmar_domain *domain, dma_addr_t iova, __func__, min_agaw, max_addr); return -EFAULT; } - domain->max_addr = max_addr; + dmar_domain->max_addr = max_addr; } - ret = domain_page_mapping(domain, iova, hpa, size, prot); + ret = domain_page_mapping(dmar_domain, iova, hpa, size, prot); return ret; } -EXPORT_SYMBOL_GPL(intel_iommu_map_address); -void intel_iommu_unmap_address(struct dmar_domain *domain, - dma_addr_t iova, size_t size) +static void intel_iommu_unmap_range(struct iommu_domain *domain, + unsigned long iova, size_t size) { + struct dmar_domain *dmar_domain = domain->priv; dma_addr_t base; /* The address might not be aligned */ base = iova & VTD_PAGE_MASK; size = VTD_PAGE_ALIGN(size); - dma_pte_clear_range(domain, base, base + size); + dma_pte_clear_range(dmar_domain, base, base + size); - if (domain->max_addr == base + size) - domain->max_addr = base; + if (dmar_domain->max_addr == base + size) + dmar_domain->max_addr = base; } -EXPORT_SYMBOL_GPL(intel_iommu_unmap_address); int intel_iommu_found(void) { -- cgit v1.2.2 From d14d65777c2491dd5baf1e17f444b8f653f3cbb1 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Wed, 3 Dec 2008 15:06:57 +0100 Subject: VT-d: adapt domain iova_to_phys function for IOMMU API Signed-off-by: Joerg Roedel --- drivers/pci/intel-iommu.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 8af6c96f31b3..712810598a2e 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -3106,15 +3106,16 @@ int intel_iommu_found(void) } EXPORT_SYMBOL_GPL(intel_iommu_found); -u64 intel_iommu_iova_to_phys(struct dmar_domain *domain, u64 iova) +static phys_addr_t intel_iommu_iova_to_phys(struct iommu_domain *domain, + unsigned long iova) { + struct dmar_domain *dmar_domain = domain->priv; struct dma_pte *pte; u64 phys = 0; - pte = addr_to_dma_pte(domain, iova); + pte = addr_to_dma_pte(dmar_domain, iova); if (pte) phys = dma_pte_addr(pte); return phys; } -EXPORT_SYMBOL_GPL(intel_iommu_iova_to_phys); -- cgit v1.2.2 From a8bcbb0de4a52f07fef7412ddc877348311ebf2a Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Wed, 3 Dec 2008 15:14:02 +0100 Subject: VT-d: register functions for the IOMMU API Signed-off-by: Joerg Roedel --- drivers/pci/intel-iommu.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'drivers') diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 712810598a2e..81e04ec85d97 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -277,6 +277,8 @@ static int intel_iommu_strict; static DEFINE_SPINLOCK(device_domain_lock); static LIST_HEAD(device_domain_list); +static struct iommu_ops intel_iommu_ops; + static int __init intel_iommu_setup(char *str) { if (!str) @@ -2729,6 +2731,9 @@ int __init intel_iommu_init(void) init_timer(&unmap_timer); force_iommu = 1; dma_ops = &intel_dma_ops; + + register_iommu(&intel_iommu_ops); + return 0; } @@ -3119,3 +3124,13 @@ static phys_addr_t intel_iommu_iova_to_phys(struct iommu_domain *domain, return phys; } + +static struct iommu_ops intel_iommu_ops = { + .domain_init = intel_iommu_domain_init, + .domain_destroy = intel_iommu_domain_destroy, + .attach_dev = intel_iommu_attach_device, + .detach_dev = intel_iommu_detach_device, + .map = intel_iommu_map_range, + .unmap = intel_iommu_unmap_range, + .iova_to_phys = intel_iommu_iova_to_phys, +}; -- cgit v1.2.2 From e4754c96cf8b82a754dc5ba791d6c0bf1fbe8e8e Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Wed, 3 Dec 2008 15:26:42 +0100 Subject: VT-d: remove now unused intel_iommu_found function Signed-off-by: Joerg Roedel --- drivers/pci/intel-iommu.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 81e04ec85d97..ecb5fd3b71f7 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -3105,12 +3105,6 @@ static void intel_iommu_unmap_range(struct iommu_domain *domain, dmar_domain->max_addr = base; } -int intel_iommu_found(void) -{ - return g_num_of_iommus; -} -EXPORT_SYMBOL_GPL(intel_iommu_found); - static phys_addr_t intel_iommu_iova_to_phys(struct iommu_domain *domain, unsigned long iova) { -- cgit v1.2.2 From cdc7b83726297b43deed0455d8732163cc59802a Mon Sep 17 00:00:00 2001 From: Mike Day Date: Fri, 12 Dec 2008 17:16:30 +0100 Subject: intel-iommu: fix bit shift at DOMAIN_FLAG_P2P_MULTIPLE_DEVICES Signed-off-by: Mike Day Signed-off-by: Joerg Roedel --- drivers/pci/intel-iommu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index ecb5fd3b71f7..235fb7a5a8a5 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -205,7 +205,7 @@ static inline bool dma_pte_present(struct dma_pte *pte) } /* devices under the same p2p bridge are owned in one domain */ -#define DOMAIN_FLAG_P2P_MULTIPLE_DEVICES (1 < 0) +#define DOMAIN_FLAG_P2P_MULTIPLE_DEVICES (1 << 0) /* domain represents a virtual machine, more than one devices * across iommus may be owned in one domain, e.g. kvm guest. -- cgit v1.2.2