Merge branches 'iommu/fixes', 'x86/vt-d', 'x86/amd', 'arm/smmu', 'arm/tegra' and...
[pandora-kernel.git] / drivers / iommu / amd_iommu.c
index e8c412d..e43d489 100644 (file)
@@ -33,6 +33,7 @@
 #include <linux/export.h>
 #include <linux/irq.h>
 #include <linux/msi.h>
+#include <linux/dma-contiguous.h>
 #include <asm/irq_remapping.h>
 #include <asm/io_apic.h>
 #include <asm/apic.h>
@@ -1326,7 +1327,9 @@ static u64 *alloc_pte(struct protection_domain *domain,
  * This function checks if there is a PTE for a given dma address. If
  * there is one, it returns the pointer to it.
  */
-static u64 *fetch_pte(struct protection_domain *domain, unsigned long address)
+static u64 *fetch_pte(struct protection_domain *domain,
+                     unsigned long address,
+                     unsigned long *page_size)
 {
        int level;
        u64 *pte;
@@ -1334,8 +1337,9 @@ static u64 *fetch_pte(struct protection_domain *domain, unsigned long address)
        if (address > PM_LEVEL_SIZE(domain->mode))
                return NULL;
 
-       level   =  domain->mode - 1;
-       pte     = &domain->pt_root[PM_LEVEL_INDEX(level, address)];
+       level      =  domain->mode - 1;
+       pte        = &domain->pt_root[PM_LEVEL_INDEX(level, address)];
+       *page_size =  PTE_LEVEL_PAGE_SIZE(level);
 
        while (level > 0) {
 
@@ -1344,19 +1348,9 @@ static u64 *fetch_pte(struct protection_domain *domain, unsigned long address)
                        return NULL;
 
                /* Large PTE */
-               if (PM_PTE_LEVEL(*pte) == 0x07) {
-                       unsigned long pte_mask, __pte;
-
-                       /*
-                        * If we have a series of large PTEs, make
-                        * sure to return a pointer to the first one.
-                        */
-                       pte_mask = PTE_PAGE_SIZE(*pte);
-                       pte_mask = ~((PAGE_SIZE_PTE_COUNT(pte_mask) << 3) - 1);
-                       __pte    = ((unsigned long)pte) & pte_mask;
-
-                       return (u64 *)__pte;
-               }
+               if (PM_PTE_LEVEL(*pte) == 7 ||
+                   PM_PTE_LEVEL(*pte) == 0)
+                       break;
 
                /* No level skipping support yet */
                if (PM_PTE_LEVEL(*pte) != level)
@@ -1365,8 +1359,21 @@ static u64 *fetch_pte(struct protection_domain *domain, unsigned long address)
                level -= 1;
 
                /* Walk to the next level */
-               pte = IOMMU_PTE_PAGE(*pte);
-               pte = &pte[PM_LEVEL_INDEX(level, address)];
+               pte        = IOMMU_PTE_PAGE(*pte);
+               pte        = &pte[PM_LEVEL_INDEX(level, address)];
+               *page_size = PTE_LEVEL_PAGE_SIZE(level);
+       }
+
+       if (PM_PTE_LEVEL(*pte) == 0x07) {
+               unsigned long pte_mask;
+
+               /*
+                * If we have a series of large PTEs, make
+                * sure to return a pointer to the first one.
+                */
+               *page_size = pte_mask = PTE_PAGE_SIZE(*pte);
+               pte_mask   = ~((PAGE_SIZE_PTE_COUNT(pte_mask) << 3) - 1);
+               pte        = (u64 *)(((unsigned long)pte) & pte_mask);
        }
 
        return pte;
@@ -1388,13 +1395,14 @@ static int iommu_map_page(struct protection_domain *dom,
        u64 __pte, *pte;
        int i, count;
 
+       BUG_ON(!IS_ALIGNED(bus_addr, page_size));
+       BUG_ON(!IS_ALIGNED(phys_addr, page_size));
+
        if (!(prot & IOMMU_PROT_MASK))
                return -EINVAL;
 
-       bus_addr  = PAGE_ALIGN(bus_addr);
-       phys_addr = PAGE_ALIGN(phys_addr);
-       count     = PAGE_SIZE_PTE_COUNT(page_size);
-       pte       = alloc_pte(dom, bus_addr, page_size, NULL, GFP_KERNEL);
+       count = PAGE_SIZE_PTE_COUNT(page_size);
+       pte   = alloc_pte(dom, bus_addr, page_size, NULL, GFP_KERNEL);
 
        if (!pte)
                return -ENOMEM;
@@ -1403,7 +1411,7 @@ static int iommu_map_page(struct protection_domain *dom,
                if (IOMMU_PTE_PRESENT(pte[i]))
                        return -EBUSY;
 
-       if (page_size > PAGE_SIZE) {
+       if (count > 1) {
                __pte = PAGE_SIZE_PTE(phys_addr, page_size);
                __pte |= PM_LEVEL_ENC(7) | IOMMU_PTE_P | IOMMU_PTE_FC;
        } else
@@ -1426,7 +1434,8 @@ static unsigned long iommu_unmap_page(struct protection_domain *dom,
                                      unsigned long bus_addr,
                                      unsigned long page_size)
 {
-       unsigned long long unmap_size, unmapped;
+       unsigned long long unmapped;
+       unsigned long unmap_size;
        u64 *pte;
 
        BUG_ON(!is_power_of_2(page_size));
@@ -1435,28 +1444,12 @@ static unsigned long iommu_unmap_page(struct protection_domain *dom,
 
        while (unmapped < page_size) {
 
-               pte = fetch_pte(dom, bus_addr);
-
-               if (!pte) {
-                       /*
-                        * No PTE for this address
-                        * move forward in 4kb steps
-                        */
-                       unmap_size = PAGE_SIZE;
-               } else if (PM_PTE_LEVEL(*pte) == 0) {
-                       /* 4kb PTE found for this address */
-                       unmap_size = PAGE_SIZE;
-                       *pte       = 0ULL;
-               } else {
-                       int count, i;
-
-                       /* Large PTE found which maps this address */
-                       unmap_size = PTE_PAGE_SIZE(*pte);
-
-                       /* Only unmap from the first pte in the page */
-                       if ((unmap_size - 1) & bus_addr)
-                               break;
-                       count      = PAGE_SIZE_PTE_COUNT(unmap_size);
+               pte = fetch_pte(dom, bus_addr, &unmap_size);
+
+               if (pte) {
+                       int i, count;
+
+                       count = PAGE_SIZE_PTE_COUNT(unmap_size);
                        for (i = 0; i < count; i++)
                                pte[i] = 0ULL;
                }
@@ -1604,7 +1597,7 @@ static int alloc_new_range(struct dma_ops_domain *dma_dom,
 {
        int index = dma_dom->aperture_size >> APERTURE_RANGE_SHIFT;
        struct amd_iommu *iommu;
-       unsigned long i, old_size;
+       unsigned long i, old_size, pte_pgsize;
 
 #ifdef CONFIG_IOMMU_STRESS
        populate = false;
@@ -1677,12 +1670,13 @@ static int alloc_new_range(struct dma_ops_domain *dma_dom,
         */
        for (i = dma_dom->aperture[index]->offset;
             i < dma_dom->aperture_size;
-            i += PAGE_SIZE) {
-               u64 *pte = fetch_pte(&dma_dom->domain, i);
+            i += pte_pgsize) {
+               u64 *pte = fetch_pte(&dma_dom->domain, i, &pte_pgsize);
                if (!pte || !IOMMU_PTE_PRESENT(*pte))
                        continue;
 
-               dma_ops_reserve_addresses(dma_dom, i >> PAGE_SHIFT, 1);
+               dma_ops_reserve_addresses(dma_dom, i >> PAGE_SHIFT,
+                                         pte_pgsize >> 12);
        }
 
        update_domain(&dma_dom->domain);
@@ -2427,16 +2421,6 @@ static int device_change_notifier(struct notifier_block *nb,
        dev_data = get_dev_data(dev);
 
        switch (action) {
-       case BUS_NOTIFY_UNBOUND_DRIVER:
-
-               domain = domain_for_device(dev);
-
-               if (!domain)
-                       goto out;
-               if (dev_data->passthrough)
-                       break;
-               detach_device(dev);
-               break;
        case BUS_NOTIFY_ADD_DEVICE:
 
                iommu_init_device(dev);
@@ -2472,7 +2456,7 @@ static int device_change_notifier(struct notifier_block *nb,
                dev->archdata.dma_ops = &amd_iommu_dma_ops;
 
                break;
-       case BUS_NOTIFY_DEL_DEVICE:
+       case BUS_NOTIFY_REMOVED_DEVICE:
 
                iommu_uninit_device(dev);
 
@@ -2928,38 +2912,42 @@ static void *alloc_coherent(struct device *dev, size_t size,
                            dma_addr_t *dma_addr, gfp_t flag,
                            struct dma_attrs *attrs)
 {
-       unsigned long flags;
-       void *virt_addr;
-       struct protection_domain *domain;
-       phys_addr_t paddr;
        u64 dma_mask = dev->coherent_dma_mask;
+       struct protection_domain *domain;
+       unsigned long flags;
+       struct page *page;
 
        INC_STATS_COUNTER(cnt_alloc_coherent);
 
        domain = get_domain(dev);
        if (PTR_ERR(domain) == -EINVAL) {
-               virt_addr = (void *)__get_free_pages(flag, get_order(size));
-               *dma_addr = __pa(virt_addr);
-               return virt_addr;
+               page = alloc_pages(flag, get_order(size));
+               *dma_addr = page_to_phys(page);
+               return page_address(page);
        } else if (IS_ERR(domain))
                return NULL;
 
+       size      = PAGE_ALIGN(size);
        dma_mask  = dev->coherent_dma_mask;
        flag     &= ~(__GFP_DMA | __GFP_HIGHMEM | __GFP_DMA32);
-       flag     |= __GFP_ZERO;
 
-       virt_addr = (void *)__get_free_pages(flag, get_order(size));
-       if (!virt_addr)
-               return NULL;
+       page = alloc_pages(flag | __GFP_NOWARN,  get_order(size));
+       if (!page) {
+               if (!(flag & __GFP_WAIT))
+                       return NULL;
 
-       paddr = virt_to_phys(virt_addr);
+               page = dma_alloc_from_contiguous(dev, size >> PAGE_SHIFT,
+                                                get_order(size));
+               if (!page)
+                       return NULL;
+       }
 
        if (!dma_mask)
                dma_mask = *dev->dma_mask;
 
        spin_lock_irqsave(&domain->lock, flags);
 
-       *dma_addr = __map_single(dev, domain->priv, paddr,
+       *dma_addr = __map_single(dev, domain->priv, page_to_phys(page),
                                 size, DMA_BIDIRECTIONAL, true, dma_mask);
 
        if (*dma_addr == DMA_ERROR_CODE) {
@@ -2971,11 +2959,12 @@ static void *alloc_coherent(struct device *dev, size_t size,
 
        spin_unlock_irqrestore(&domain->lock, flags);
 
-       return virt_addr;
+       return page_address(page);
 
 out_free:
 
-       free_pages((unsigned long)virt_addr, get_order(size));
+       if (!dma_release_from_contiguous(dev, page, size >> PAGE_SHIFT))
+               __free_pages(page, get_order(size));
 
        return NULL;
 }
@@ -2987,11 +2976,15 @@ static void free_coherent(struct device *dev, size_t size,
                          void *virt_addr, dma_addr_t dma_addr,
                          struct dma_attrs *attrs)
 {
-       unsigned long flags;
        struct protection_domain *domain;
+       unsigned long flags;
+       struct page *page;
 
        INC_STATS_COUNTER(cnt_free_coherent);
 
+       page = virt_to_page(virt_addr);
+       size = PAGE_ALIGN(size);
+
        domain = get_domain(dev);
        if (IS_ERR(domain))
                goto free_mem;
@@ -3005,7 +2998,8 @@ static void free_coherent(struct device *dev, size_t size,
        spin_unlock_irqrestore(&domain->lock, flags);
 
 free_mem:
-       free_pages((unsigned long)virt_addr, get_order(size));
+       if (!dma_release_from_contiguous(dev, page, size >> PAGE_SHIFT))
+               __free_pages(page, get_order(size));
 }
 
 /*
@@ -3387,27 +3381,21 @@ static phys_addr_t amd_iommu_iova_to_phys(struct iommu_domain *dom,
                                          dma_addr_t iova)
 {
        struct protection_domain *domain = to_pdomain(dom);
-       unsigned long offset_mask;
-       phys_addr_t paddr;
+       unsigned long offset_mask, pte_pgsize;
        u64 *pte, __pte;
 
        if (domain->mode == PAGE_MODE_NONE)
                return iova;
 
-       pte = fetch_pte(domain, iova);
+       pte = fetch_pte(domain, iova, &pte_pgsize);
 
        if (!pte || !IOMMU_PTE_PRESENT(*pte))
                return 0;
 
-       if (PM_PTE_LEVEL(*pte) == 0)
-               offset_mask = PAGE_SIZE - 1;
-       else
-               offset_mask = PTE_PAGE_SIZE(*pte) - 1;
-
-       __pte = *pte & PM_ADDR_MASK;
-       paddr = (__pte & ~offset_mask) | (iova & offset_mask);
+       offset_mask = pte_pgsize - 1;
+       __pte       = *pte & PM_ADDR_MASK;
 
-       return paddr;
+       return (__pte & ~offset_mask) | (iova & offset_mask);
 }
 
 static bool amd_iommu_capable(enum iommu_cap cap)