Skip to content

Commit aadad09

Browse files
mannamsbjorn-helgaas
authored andcommitted
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 <[email protected]> Signed-off-by: Srinath Mannam <[email protected]> Signed-off-by: Lorenzo Pieralisi <[email protected]> Signed-off-by: Bjorn Helgaas <[email protected]> Reviewed-by: Oza Pawandeep <[email protected]> Acked-by: Robin Murphy <[email protected]>
1 parent e80a91a commit aadad09

File tree

1 file changed

+32
-3
lines changed

1 file changed

+32
-3
lines changed

drivers/iommu/dma-iommu.c

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -206,12 +206,13 @@ static int cookie_init_hw_msi_region(struct iommu_dma_cookie *cookie,
206206
return 0;
207207
}
208208

209-
static void iova_reserve_pci_windows(struct pci_dev *dev,
209+
static int iova_reserve_pci_windows(struct pci_dev *dev,
210210
struct iova_domain *iovad)
211211
{
212212
struct pci_host_bridge *bridge = pci_find_host_bridge(dev->bus);
213213
struct resource_entry *window;
214214
unsigned long lo, hi;
215+
phys_addr_t start = 0, end;
215216

216217
resource_list_for_each_entry(window, &bridge->windows) {
217218
if (resource_type(window->res) != IORESOURCE_MEM)
@@ -221,6 +222,31 @@ static void iova_reserve_pci_windows(struct pci_dev *dev,
221222
hi = iova_pfn(iovad, window->res->end - window->offset);
222223
reserve_iova(iovad, lo, hi);
223224
}
225+
226+
/* Get reserved DMA windows from host bridge */
227+
resource_list_for_each_entry(window, &bridge->dma_ranges) {
228+
end = window->res->start - window->offset;
229+
resv_iova:
230+
if (end > start) {
231+
lo = iova_pfn(iovad, start);
232+
hi = iova_pfn(iovad, end);
233+
reserve_iova(iovad, lo, hi);
234+
} else {
235+
/* dma_ranges list should be sorted */
236+
dev_err(&dev->dev, "Failed to reserve IOVA\n");
237+
return -EINVAL;
238+
}
239+
240+
start = window->res->end - window->offset + 1;
241+
/* If window is last entry */
242+
if (window->node.next == &bridge->dma_ranges &&
243+
end != ~(dma_addr_t)0) {
244+
end = ~(dma_addr_t)0;
245+
goto resv_iova;
246+
}
247+
}
248+
249+
return 0;
224250
}
225251

226252
static int iova_reserve_iommu_regions(struct device *dev,
@@ -232,8 +258,11 @@ static int iova_reserve_iommu_regions(struct device *dev,
232258
LIST_HEAD(resv_regions);
233259
int ret = 0;
234260

235-
if (dev_is_pci(dev))
236-
iova_reserve_pci_windows(to_pci_dev(dev), iovad);
261+
if (dev_is_pci(dev)) {
262+
ret = iova_reserve_pci_windows(to_pci_dev(dev), iovad);
263+
if (ret)
264+
return ret;
265+
}
237266

238267
iommu_get_resv_regions(dev, &resv_regions);
239268
list_for_each_entry(region, &resv_regions, list) {

0 commit comments

Comments
 (0)