Staging: hv: clean up NetVsc.h
[safe/jmp/linux-2.6] / drivers / pci / intel-iommu.c
index 40ce5a0..562221e 100644 (file)
@@ -37,6 +37,7 @@
 #include <linux/iommu.h>
 #include <linux/intel-iommu.h>
 #include <linux/sysdev.h>
+#include <linux/tboot.h>
 #include <asm/cacheflush.h>
 #include <asm/iommu.h>
 #include "pci.h"
@@ -222,7 +223,12 @@ static inline void dma_set_pte_prot(struct dma_pte *pte, unsigned long prot)
 
 static inline u64 dma_pte_addr(struct dma_pte *pte)
 {
-       return (pte->val & VTD_PAGE_MASK);
+#ifdef CONFIG_64BIT
+       return pte->val & VTD_PAGE_MASK;
+#else
+       /* Must have a full atomic 64-bit read */
+       return  __cmpxchg64(pte, 0ULL, 0ULL) & VTD_PAGE_MASK;
+#endif
 }
 
 static inline void dma_set_pte_pfn(struct dma_pte *pte, unsigned long pfn)
@@ -235,6 +241,11 @@ static inline bool dma_pte_present(struct dma_pte *pte)
        return (pte->val & 3) != 0;
 }
 
+static inline int first_pte_in_page(struct dma_pte *pte)
+{
+       return !((unsigned long)pte & ~VTD_PAGE_MASK);
+}
+
 /*
  * This domain is a statically identity mapping domain.
  *     1. This domain creats a static 1:1 mapping to all usable memory.
@@ -262,7 +273,6 @@ struct dmar_domain {
        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 */
@@ -696,13 +706,11 @@ static struct dma_pte *pfn_to_dma_pte(struct dmar_domain *domain,
        struct dma_pte *parent, *pte = NULL;
        int level = agaw_to_level(domain->agaw);
        int offset;
-       unsigned long flags;
 
        BUG_ON(!domain->pgd);
        BUG_ON(addr_width < BITS_PER_LONG && pfn >> addr_width);
        parent = domain->pgd;
 
-       spin_lock_irqsave(&domain->mapping_lock, flags);
        while (level > 0) {
                void *tmp_page;
 
@@ -712,28 +720,27 @@ static struct dma_pte *pfn_to_dma_pte(struct dmar_domain *domain,
                        break;
 
                if (!dma_pte_present(pte)) {
+                       uint64_t pteval;
+
                        tmp_page = alloc_pgtable_page();
 
-                       if (!tmp_page) {
-                               spin_unlock_irqrestore(&domain->mapping_lock,
-                                       flags);
+                       if (!tmp_page)
                                return NULL;
+
+                       domain_flush_cache(domain, tmp_page, VTD_PAGE_SIZE);
+                       pteval = (virt_to_dma_pfn(tmp_page) << VTD_PAGE_SHIFT) | DMA_PTE_READ | DMA_PTE_WRITE;
+                       if (cmpxchg64(&pte->val, 0ULL, pteval)) {
+                               /* Someone else set it while we were thinking; use theirs. */
+                               free_pgtable_page(tmp_page);
+                       } else {
+                               dma_pte_addr(pte);
+                               domain_flush_cache(domain, pte, sizeof(*pte));
                        }
-                       domain_flush_cache(domain, tmp_page, PAGE_SIZE);
-                       dma_set_pte_pfn(pte, virt_to_dma_pfn(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);
-                       domain_flush_cache(domain, pte, sizeof(*pte));
                }
                parent = phys_to_virt(dma_pte_addr(pte));
                level--;
        }
 
-       spin_unlock_irqrestore(&domain->mapping_lock, flags);
        return pte;
 }
 
@@ -779,13 +786,12 @@ static void dma_pte_clear_range(struct dmar_domain *domain,
                        start_pfn = align_to_level(start_pfn + 1, 2);
                        continue;
                }
-               while (start_pfn <= last_pfn &&
-                      (unsigned long)pte >> VTD_PAGE_SHIFT ==
-                      (unsigned long)first_pte >> VTD_PAGE_SHIFT) {
+               do { 
                        dma_clear_pte(pte);
                        start_pfn++;
                        pte++;
-               }
+               } while (start_pfn <= last_pfn && !first_pte_in_page(pte));
+
                domain_flush_cache(domain, first_pte,
                                   (void *)pte - (void *)first_pte);
        }
@@ -797,7 +803,7 @@ static void dma_pte_free_pagetable(struct dmar_domain *domain,
                                   unsigned long last_pfn)
 {
        int addr_width = agaw_to_width(domain->agaw) - VTD_PAGE_SHIFT;
-       struct dma_pte *pte;
+       struct dma_pte *first_pte, *pte;
        int total = agaw_to_level(domain->agaw);
        int level;
        unsigned long tmp;
@@ -805,25 +811,34 @@ static void dma_pte_free_pagetable(struct dmar_domain *domain,
        BUG_ON(addr_width < BITS_PER_LONG && start_pfn >> addr_width);
        BUG_ON(addr_width < BITS_PER_LONG && last_pfn >> addr_width);
 
-       /* we don't need lock here, nobody else touches the iova range */
+       /* We don't need lock here; nobody else touches the iova range */
        level = 2;
        while (level <= total) {
                tmp = align_to_level(start_pfn, level);
 
-               /* Only clear this pte/pmd if we're asked to clear its
-                  _whole_ range */
+               /* If we can't even clear one PTE at this level, we're done */
                if (tmp + level_size(level) - 1 > last_pfn)
                        return;
 
-               while (tmp <= last_pfn) {
-                       pte = dma_pfn_level_pte(domain, tmp, level);
-                       if (pte) {
-                               free_pgtable_page(
-                                       phys_to_virt(dma_pte_addr(pte)));
-                               dma_clear_pte(pte);
-                               domain_flush_cache(domain, pte, sizeof(*pte));
+               while (tmp + level_size(level) - 1 <= last_pfn) {
+                       first_pte = pte = dma_pfn_level_pte(domain, tmp, level);
+                       if (!pte) {
+                               tmp = align_to_level(tmp + 1, level + 1);
+                               continue;
                        }
-                       tmp += level_size(level);
+                       do {
+                               if (dma_pte_present(pte)) {
+                                       free_pgtable_page(phys_to_virt(dma_pte_addr(pte)));
+                                       dma_clear_pte(pte);
+                               }
+                               pte++;
+                               tmp += level_size(level);
+                       } while (!first_pte_in_page(pte) &&
+                                tmp + level_size(level) - 1 <= last_pfn);
+
+                       domain_flush_cache(domain, first_pte,
+                                          (void *)pte - (void *)first_pte);
+                       
                }
                level++;
        }
@@ -1361,7 +1376,6 @@ static int domain_init(struct dmar_domain *domain, int guest_width)
        unsigned long sagaw;
 
        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);
@@ -1492,7 +1506,6 @@ static int domain_context_mapping_one(struct dmar_domain *domain, int segment,
                        }
 
                        set_bit(num, iommu->domain_ids);
-                       set_bit(iommu->seq_id, &domain->iommu_bmp);
                        iommu->domains[num] = domain;
                        id = num;
                }
@@ -1635,6 +1648,14 @@ static int domain_context_mapped(struct pci_dev *pdev)
                                             tmp->devfn);
 }
 
+/* Returns a number of VTD pages, but aligned to MM page size */
+static inline unsigned long aligned_nrpages(unsigned long host_addr,
+                                           size_t size)
+{
+       host_addr &= ~PAGE_MASK;
+       return PAGE_ALIGN(host_addr + size) >> VTD_PAGE_SHIFT;
+}
+
 static int __domain_mapping(struct dmar_domain *domain, unsigned long iov_pfn,
                            struct scatterlist *sg, unsigned long phys_pfn,
                            unsigned long nr_pages, int prot)
@@ -1659,8 +1680,10 @@ static int __domain_mapping(struct dmar_domain *domain, unsigned long iov_pfn,
        }
 
        while (nr_pages--) {
+               uint64_t tmp;
+
                if (!sg_res) {
-                       sg_res = (sg->offset + sg->length + VTD_PAGE_SIZE - 1) >> VTD_PAGE_SHIFT;
+                       sg_res = aligned_nrpages(sg->offset, sg->length);
                        sg->dma_address = ((dma_addr_t)iov_pfn << VTD_PAGE_SHIFT) + sg->offset;
                        sg->dma_length = sg->length;
                        pteval = page_to_phys(sg_page(sg)) | prot;
@@ -1673,21 +1696,19 @@ static int __domain_mapping(struct dmar_domain *domain, unsigned long iov_pfn,
                /* We don't need lock here, nobody else
                 * touches the iova range
                 */
-               if (unlikely(dma_pte_addr(pte))) {
+               tmp = cmpxchg64_local(&pte->val, 0ULL, pteval);
+               if (tmp) {
                        static int dumps = 5;
-                       printk(KERN_CRIT "ERROR: DMA PTE for vPFN 0x%lx already set (to %llx)\n",
-                              iov_pfn, pte->val);
+                       printk(KERN_CRIT "ERROR: DMA PTE for vPFN 0x%lx already set (to %llx not %llx)\n",
+                              iov_pfn, tmp, (unsigned long long)pteval);
                        if (dumps) {
                                dumps--;
                                debug_dma_dump_mappings(NULL);
                        }
                        WARN_ON(1);
                }
-               pte->val = pteval;
                pte++;
-               if (!nr_pages ||
-                   (unsigned long)pte >> VTD_PAGE_SHIFT !=
-                   (unsigned long)first_pte >> VTD_PAGE_SHIFT) {
+               if (!nr_pages || first_pte_in_page(pte)) {
                        domain_flush_cache(domain, first_pte,
                                           (void *)pte - (void *)first_pte);
                        pte = NULL;
@@ -2104,6 +2125,47 @@ static int domain_add_dev_info(struct dmar_domain *domain,
        return 0;
 }
 
+static int iommu_should_identity_map(struct pci_dev *pdev, int startup)
+{
+       if (iommu_identity_mapping == 2)
+               return IS_GFX_DEVICE(pdev);
+
+       /*
+        * We want to start off with all devices in the 1:1 domain, and
+        * take them out later if we find they can't access all of memory.
+        *
+        * However, we can't do this for PCI devices behind bridges,
+        * because all PCI devices behind the same bridge will end up
+        * with the same source-id on their transactions.
+        *
+        * Practically speaking, we can't change things around for these
+        * devices at run-time, because we can't be sure there'll be no
+        * DMA transactions in flight for any of their siblings.
+        * 
+        * So PCI devices (unless they're on the root bus) as well as
+        * their parent PCI-PCI or PCIe-PCI bridges must be left _out_ of
+        * the 1:1 domain, just in _case_ one of their siblings turns out
+        * not to be able to map all of memory.
+        */
+       if (!pdev->is_pcie) {
+               if (!pci_is_root_bus(pdev->bus))
+                       return 0;
+               if (pdev->class >> 8 == PCI_CLASS_BRIDGE_PCI)
+                       return 0;
+       } else if (pdev->pcie_type == PCI_EXP_TYPE_PCI_BRIDGE)
+               return 0;
+
+       /* 
+        * At boot time, we don't yet know if devices will be 64-bit capable.
+        * Assume that they will -- if they turn out not to be, then we can 
+        * take them out of the 1:1 domain later.
+        */
+       if (!startup)
+               return pdev->dma_mask > DMA_BIT_MASK(32);
+
+       return 1;
+}
+
 static int iommu_prepare_static_identity_mapping(void)
 {
        struct pci_dev *pdev = NULL;
@@ -2114,16 +2176,18 @@ static int iommu_prepare_static_identity_mapping(void)
                return -EFAULT;
 
        for_each_pci_dev(pdev) {
-               printk(KERN_INFO "IOMMU: identity mapping for device %s\n",
-                      pci_name(pdev));
+               if (iommu_should_identity_map(pdev, 1)) {
+                       printk(KERN_INFO "IOMMU: identity mapping for device %s\n",
+                              pci_name(pdev));
 
-               ret = domain_context_mapping(si_domain, pdev,
-                                            CONTEXT_TT_MULTI_LEVEL);
-               if (ret)
-                       return ret;
-               ret = domain_add_dev_info(si_domain, pdev);
-               if (ret)
-                       return ret;
+                       ret = domain_context_mapping(si_domain, pdev,
+                                                    CONTEXT_TT_MULTI_LEVEL);
+                       if (ret)
+                               return ret;
+                       ret = domain_add_dev_info(si_domain, pdev);
+                       if (ret)
+                               return ret;
+               }
        }
 
        return 0;
@@ -2278,6 +2342,10 @@ int __init init_dmars(void)
         * identity mapping if iommu_identity_mapping is set.
         */
        if (!iommu_pass_through) {
+#ifdef CONFIG_DMAR_BROKEN_GFX_WA
+               if (!iommu_identity_mapping)
+                       iommu_identity_mapping = 2;
+#endif
                if (iommu_identity_mapping)
                        iommu_prepare_static_identity_mapping();
                /*
@@ -2355,15 +2423,7 @@ error:
        return ret;
 }
 
-static inline unsigned long aligned_nrpages(unsigned long host_addr,
-                                           size_t size)
-{
-       host_addr &= ~PAGE_MASK;
-       host_addr += size + PAGE_SIZE - 1;
-
-       return host_addr >> VTD_PAGE_SHIFT;
-}
-
+/* This takes a number of _MM_ pages, not VTD pages */
 static struct iova *intel_alloc_iova(struct device *dev,
                                     struct dmar_domain *domain,
                                     unsigned long nrpages, uint64_t dma_mask)
@@ -2430,16 +2490,24 @@ static int iommu_dummy(struct pci_dev *pdev)
 }
 
 /* Check if the pdev needs to go through non-identity map and unmap process.*/
-static int iommu_no_mapping(struct pci_dev *pdev)
+static int iommu_no_mapping(struct device *dev)
 {
+       struct pci_dev *pdev;
        int found;
 
+       if (unlikely(dev->bus != &pci_bus_type))
+               return 1;
+
+       pdev = to_pci_dev(dev);
+       if (iommu_dummy(pdev))
+               return 1;
+
        if (!iommu_identity_mapping)
-               return iommu_dummy(pdev);
+               return 0;
 
        found = identity_mapping(pdev);
        if (found) {
-               if (pdev->dma_mask > DMA_BIT_MASK(32))
+               if (iommu_should_identity_map(pdev, 0))
                        return 1;
                else {
                        /*
@@ -2456,9 +2524,12 @@ static int iommu_no_mapping(struct pci_dev *pdev)
                 * In case of a detached 64 bit DMA device from vm, the device
                 * is put into si_domain for identity mapping.
                 */
-               if (pdev->dma_mask > DMA_BIT_MASK(32)) {
+               if (iommu_should_identity_map(pdev, 0)) {
                        int ret;
                        ret = domain_add_dev_info(si_domain, pdev);
+                       if (ret)
+                               return 0;
+                       ret = domain_context_mapping(si_domain, pdev, CONTEXT_TT_MULTI_LEVEL);
                        if (!ret) {
                                printk(KERN_INFO "64bit %s uses identity mapping\n",
                                       pci_name(pdev));
@@ -2467,7 +2538,7 @@ static int iommu_no_mapping(struct pci_dev *pdev)
                }
        }
 
-       return iommu_dummy(pdev);
+       return 0;
 }
 
 static dma_addr_t __intel_map_single(struct device *hwdev, phys_addr_t paddr,
@@ -2480,10 +2551,11 @@ static dma_addr_t __intel_map_single(struct device *hwdev, phys_addr_t paddr,
        int prot = 0;
        int ret;
        struct intel_iommu *iommu;
+       unsigned long paddr_pfn = paddr >> PAGE_SHIFT;
 
        BUG_ON(dir == DMA_NONE);
 
-       if (iommu_no_mapping(pdev))
+       if (iommu_no_mapping(hwdev))
                return paddr;
 
        domain = get_valid_domain_for_dev(pdev);
@@ -2493,7 +2565,8 @@ static dma_addr_t __intel_map_single(struct device *hwdev, phys_addr_t paddr,
        iommu = domain_get_iommu(domain);
        size = aligned_nrpages(paddr, size);
 
-       iova = intel_alloc_iova(hwdev, domain, size, pdev->dma_mask);
+       iova = intel_alloc_iova(hwdev, domain, dma_to_mm_pfn(size),
+                               pdev->dma_mask);
        if (!iova)
                goto error;
 
@@ -2513,7 +2586,7 @@ static dma_addr_t __intel_map_single(struct device *hwdev, phys_addr_t paddr,
         * is not a big problem
         */
        ret = domain_pfn_mapping(domain, mm_to_dma_pfn(iova->pfn_lo),
-                                paddr >> VTD_PAGE_SHIFT, size, prot);
+                                mm_to_dma_pfn(paddr_pfn), size, prot);
        if (ret)
                goto error;
 
@@ -2622,7 +2695,7 @@ static void intel_unmap_page(struct device *dev, dma_addr_t dev_addr,
        struct iova *iova;
        struct intel_iommu *iommu;
 
-       if (iommu_no_mapping(pdev))
+       if (iommu_no_mapping(dev))
                return;
 
        domain = find_domain(pdev);
@@ -2631,7 +2704,8 @@ static void intel_unmap_page(struct device *dev, dma_addr_t dev_addr,
        iommu = domain_get_iommu(domain);
 
        iova = find_iova(&domain->iovad, IOVA_PFN(dev_addr));
-       if (!iova)
+       if (WARN_ONCE(!iova, "Driver unmaps unmatched page at PFN %llx\n",
+                     (unsigned long long)dev_addr))
                return;
 
        start_pfn = mm_to_dma_pfn(iova->pfn_lo);
@@ -2712,7 +2786,7 @@ static void intel_unmap_sg(struct device *hwdev, struct scatterlist *sglist,
        struct iova *iova;
        struct intel_iommu *iommu;
 
-       if (iommu_no_mapping(pdev))
+       if (iommu_no_mapping(hwdev))
                return;
 
        domain = find_domain(pdev);
@@ -2721,7 +2795,8 @@ static void intel_unmap_sg(struct device *hwdev, struct scatterlist *sglist,
        iommu = domain_get_iommu(domain);
 
        iova = find_iova(&domain->iovad, IOVA_PFN(sglist[0].dma_address));
-       if (!iova)
+       if (WARN_ONCE(!iova, "Driver unmaps unmatched sglist at PFN %llx\n",
+                     (unsigned long long)sglist[0].dma_address))
                return;
 
        start_pfn = mm_to_dma_pfn(iova->pfn_lo);
@@ -2770,7 +2845,7 @@ static int intel_map_sg(struct device *hwdev, struct scatterlist *sglist, int ne
        struct intel_iommu *iommu;
 
        BUG_ON(dir == DMA_NONE);
-       if (iommu_no_mapping(pdev))
+       if (iommu_no_mapping(hwdev))
                return intel_nontranslate_map_sg(hwdev, sglist, nelems, dir);
 
        domain = get_valid_domain_for_dev(pdev);
@@ -2782,7 +2857,8 @@ static int intel_map_sg(struct device *hwdev, struct scatterlist *sglist, int ne
        for_each_sg(sglist, sg, nelems, i)
                size += aligned_nrpages(sg->offset, sg->length);
 
-       iova = intel_alloc_iova(hwdev, domain, size, pdev->dma_mask);
+       iova = intel_alloc_iova(hwdev, domain, dma_to_mm_pfn(size),
+                               pdev->dma_mask);
        if (!iova) {
                sglist->dma_length = 0;
                return 0;
@@ -2800,7 +2876,7 @@ static int intel_map_sg(struct device *hwdev, struct scatterlist *sglist, int ne
 
        start_vpfn = mm_to_dma_pfn(iova->pfn_lo);
 
-       ret = domain_sg_mapping(domain, start_vpfn, sglist, mm_to_dma_pfn(size), prot);
+       ret = domain_sg_mapping(domain, start_vpfn, sglist, size, prot);
        if (unlikely(ret)) {
                /*  clear the page */
                dma_pte_clear_range(domain, start_vpfn,
@@ -3108,12 +3184,22 @@ static int __init init_iommu_sysfs(void)
 int __init intel_iommu_init(void)
 {
        int ret = 0;
+       int force_on = 0;
+
+       /* VT-d is required for a TXT/tboot launch, so enforce that */
+       force_on = tboot_force_iommu();
 
-       if (dmar_table_init())
+       if (dmar_table_init()) {
+               if (force_on)
+                       panic("tboot: Failed to initialize DMAR table\n");
                return  -ENODEV;
+       }
 
-       if (dmar_dev_scope_init())
+       if (dmar_dev_scope_init()) {
+               if (force_on)
+                       panic("tboot: Failed to initialize DMAR device scope\n");
                return  -ENODEV;
+       }
 
        /*
         * Check the need for DMA-remapping initialization now.
@@ -3129,6 +3215,8 @@ int __init intel_iommu_init(void)
 
        ret = init_dmars();
        if (ret) {
+               if (force_on)
+                       panic("tboot: Failed to initialize DMARs\n");
                printk(KERN_ERR "IOMMU: dmar init failed\n");
                put_iova_domain(&reserved_iova_list);
                iommu_exit_mempool();
@@ -3320,7 +3408,6 @@ static int md_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);
@@ -3334,6 +3421,7 @@ static int md_domain_init(struct dmar_domain *domain, int guest_width)
 
        domain->iommu_count = 0;
        domain->iommu_coherency = 0;
+       domain->iommu_snooping = 0;
        domain->max_addr = 0;
 
        /* always allocate the top pgd */
@@ -3526,6 +3614,9 @@ static void intel_iommu_unmap_range(struct iommu_domain *domain,
 {
        struct dmar_domain *dmar_domain = domain->priv;
 
+       if (!size)
+               return;
+
        dma_pte_clear_range(dmar_domain, iova >> VTD_PAGE_SHIFT,
                            (iova + size - 1) >> VTD_PAGE_SHIFT);