Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

iommu/dma: Reserve IOVA for PCIe inaccessible DMA address

The dma_ranges list field of PCI host bridge structure has resource entries
in sorted order representing address ranges allowed for DMA transfers.

Process the list and reserve IOVA addresses that are not present in its
resource entries (ie DMA memory holes) to prevent allocating IOVA addresses
that cannot be accessed by PCI devices.

Based-on-a-patch-by: Oza Pawandeep <oza.oza@broadcom.com>
Signed-off-by: Srinath Mannam <srinath.mannam@broadcom.com>
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Oza Pawandeep <poza@codeaurora.org>
Acked-by: Robin Murphy <robin.murphy@arm.com>

authored by

Srinath Mannam and committed by
Bjorn Helgaas
aadad097 e80a91ad

+32 -3
+32 -3
drivers/iommu/dma-iommu.c
··· 206 206 return 0; 207 207 } 208 208 209 - static void iova_reserve_pci_windows(struct pci_dev *dev, 209 + static int iova_reserve_pci_windows(struct pci_dev *dev, 210 210 struct iova_domain *iovad) 211 211 { 212 212 struct pci_host_bridge *bridge = pci_find_host_bridge(dev->bus); 213 213 struct resource_entry *window; 214 214 unsigned long lo, hi; 215 + phys_addr_t start = 0, end; 215 216 216 217 resource_list_for_each_entry(window, &bridge->windows) { 217 218 if (resource_type(window->res) != IORESOURCE_MEM) ··· 222 221 hi = iova_pfn(iovad, window->res->end - window->offset); 223 222 reserve_iova(iovad, lo, hi); 224 223 } 224 + 225 + /* Get reserved DMA windows from host bridge */ 226 + resource_list_for_each_entry(window, &bridge->dma_ranges) { 227 + end = window->res->start - window->offset; 228 + resv_iova: 229 + if (end > start) { 230 + lo = iova_pfn(iovad, start); 231 + hi = iova_pfn(iovad, end); 232 + reserve_iova(iovad, lo, hi); 233 + } else { 234 + /* dma_ranges list should be sorted */ 235 + dev_err(&dev->dev, "Failed to reserve IOVA\n"); 236 + return -EINVAL; 237 + } 238 + 239 + start = window->res->end - window->offset + 1; 240 + /* If window is last entry */ 241 + if (window->node.next == &bridge->dma_ranges && 242 + end != ~(dma_addr_t)0) { 243 + end = ~(dma_addr_t)0; 244 + goto resv_iova; 245 + } 246 + } 247 + 248 + return 0; 225 249 } 226 250 227 251 static int iova_reserve_iommu_regions(struct device *dev, ··· 258 232 LIST_HEAD(resv_regions); 259 233 int ret = 0; 260 234 261 - if (dev_is_pci(dev)) 262 - iova_reserve_pci_windows(to_pci_dev(dev), iovad); 235 + if (dev_is_pci(dev)) { 236 + ret = iova_reserve_pci_windows(to_pci_dev(dev), iovad); 237 + if (ret) 238 + return ret; 239 + } 263 240 264 241 iommu_get_resv_regions(dev, &resv_regions); 265 242 list_for_each_entry(region, &resv_regions, list) {