diff options
| -rw-r--r-- | drivers/pci/intel-iommu.c | 83 |
1 files changed, 62 insertions, 21 deletions
diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 11a23201445a..28bd5f2d78fc 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c | |||
| @@ -1635,6 +1635,56 @@ static int domain_context_mapped(struct pci_dev *pdev) | |||
| 1635 | tmp->devfn); | 1635 | tmp->devfn); |
| 1636 | } | 1636 | } |
| 1637 | 1637 | ||
| 1638 | static int domain_sg_mapping(struct dmar_domain *domain, unsigned long iov_pfn, | ||
| 1639 | struct scatterlist *sg, unsigned long nr_pages, | ||
| 1640 | int prot) | ||
| 1641 | { | ||
| 1642 | struct dma_pte *first_pte = NULL, *pte = NULL; | ||
| 1643 | uint64_t pteval; | ||
| 1644 | int addr_width = agaw_to_width(domain->agaw) - VTD_PAGE_SHIFT; | ||
| 1645 | unsigned long sg_res = 0; | ||
| 1646 | |||
| 1647 | BUG_ON(addr_width < BITS_PER_LONG && (iov_pfn + nr_pages - 1) >> addr_width); | ||
| 1648 | |||
| 1649 | if ((prot & (DMA_PTE_READ|DMA_PTE_WRITE)) == 0) | ||
| 1650 | return -EINVAL; | ||
| 1651 | |||
| 1652 | prot &= DMA_PTE_READ | DMA_PTE_WRITE | DMA_PTE_SNP; | ||
| 1653 | |||
| 1654 | while (nr_pages--) { | ||
| 1655 | if (!sg_res) { | ||
| 1656 | sg_res = (sg->offset + sg->length + VTD_PAGE_SIZE - 1) >> VTD_PAGE_SHIFT; | ||
| 1657 | sg->dma_address = ((dma_addr_t)iov_pfn << VTD_PAGE_SHIFT) + sg->offset; | ||
| 1658 | sg->dma_length = sg->length; | ||
| 1659 | pteval = page_to_phys(sg_page(sg)) | prot; | ||
| 1660 | } | ||
| 1661 | if (!pte) { | ||
| 1662 | first_pte = pte = pfn_to_dma_pte(domain, iov_pfn); | ||
| 1663 | if (!pte) | ||
| 1664 | return -ENOMEM; | ||
| 1665 | } | ||
| 1666 | /* We don't need lock here, nobody else | ||
| 1667 | * touches the iova range | ||
| 1668 | */ | ||
| 1669 | BUG_ON(dma_pte_addr(pte)); | ||
| 1670 | pte->val = pteval; | ||
| 1671 | pte++; | ||
| 1672 | if (!nr_pages || | ||
| 1673 | (unsigned long)pte >> VTD_PAGE_SHIFT != | ||
| 1674 | (unsigned long)first_pte >> VTD_PAGE_SHIFT) { | ||
| 1675 | domain_flush_cache(domain, first_pte, | ||
| 1676 | (void *)pte - (void *)first_pte); | ||
| 1677 | pte = NULL; | ||
| 1678 | } | ||
| 1679 | iov_pfn++; | ||
| 1680 | pteval += VTD_PAGE_SIZE; | ||
| 1681 | sg_res--; | ||
| 1682 | if (!sg_res) | ||
| 1683 | sg = sg_next(sg); | ||
| 1684 | } | ||
| 1685 | return 0; | ||
| 1686 | } | ||
| 1687 | |||
| 1638 | static int domain_pfn_mapping(struct dmar_domain *domain, unsigned long iov_pfn, | 1688 | static int domain_pfn_mapping(struct dmar_domain *domain, unsigned long iov_pfn, |
| 1639 | unsigned long phys_pfn, unsigned long nr_pages, | 1689 | unsigned long phys_pfn, unsigned long nr_pages, |
| 1640 | int prot) | 1690 | int prot) |
| @@ -2758,27 +2808,18 @@ static int intel_map_sg(struct device *hwdev, struct scatterlist *sglist, int ne | |||
| 2758 | prot |= DMA_PTE_WRITE; | 2808 | prot |= DMA_PTE_WRITE; |
| 2759 | 2809 | ||
| 2760 | start_vpfn = mm_to_dma_pfn(iova->pfn_lo); | 2810 | start_vpfn = mm_to_dma_pfn(iova->pfn_lo); |
| 2761 | offset_pfn = 0; | 2811 | |
| 2762 | for_each_sg(sglist, sg, nelems, i) { | 2812 | ret = domain_sg_mapping(domain, start_vpfn, sglist, mm_to_dma_pfn(size), prot); |
| 2763 | int nr_pages = aligned_nrpages(sg->offset, sg->length); | 2813 | if (unlikely(ret)) { |
| 2764 | ret = domain_pfn_mapping(domain, start_vpfn + offset_pfn, | 2814 | /* clear the page */ |
| 2765 | page_to_dma_pfn(sg_page(sg)), | 2815 | dma_pte_clear_range(domain, start_vpfn, |
| 2766 | nr_pages, prot); | 2816 | start_vpfn + size - 1); |
| 2767 | if (ret) { | 2817 | /* free page tables */ |
| 2768 | /* clear the page */ | 2818 | dma_pte_free_pagetable(domain, start_vpfn, |
| 2769 | dma_pte_clear_range(domain, start_vpfn, | 2819 | start_vpfn + size - 1); |
| 2770 | start_vpfn + offset_pfn); | 2820 | /* free iova */ |
| 2771 | /* free page tables */ | 2821 | __free_iova(&domain->iovad, iova); |
| 2772 | dma_pte_free_pagetable(domain, start_vpfn, | 2822 | return 0; |
| 2773 | start_vpfn + offset_pfn); | ||
| 2774 | /* free iova */ | ||
| 2775 | __free_iova(&domain->iovad, iova); | ||
| 2776 | return 0; | ||
| 2777 | } | ||
| 2778 | sg->dma_address = ((dma_addr_t)(start_vpfn + offset_pfn) | ||
| 2779 | << VTD_PAGE_SHIFT) + sg->offset; | ||
| 2780 | sg->dma_length = sg->length; | ||
| 2781 | offset_pfn += nr_pages; | ||
| 2782 | } | 2823 | } |
| 2783 | 2824 | ||
| 2784 | /* it's a non-present to present mapping. Only flush if caching mode */ | 2825 | /* it's a non-present to present mapping. Only flush if caching mode */ |
