Reserving an IOVA address using "iommu-addresses" requires it to be placed in the reserved-memory node. But when "iommu-addresses" is the only property being described and there is no backing "reg" (i.e. no actual reserved system memory), it does not really belong under /reserved-memory. A device IOVA range is specific to its own address space, and does not describe the system physical memory to make it qualify under reserved-memory. Given this, place "iommu-addresses" inside reserved-memory only when it is paired with a "reg", otherwise, define it within the device own node. It was discussed here: https://lore.kernel.org/all/[email protected]
Existing "iommu-addresses" property expects a phandle, which does not make sense when the property is defined within the device node. "iommu-ranges" was introduced to specify the device specific IOVA ranges when there is no backing "reg". Suggested-by: Rob Herring <[email protected]> Reviewed-by: Dmitry Baryshkov <[email protected]> Co-developed-by: Vishnu Reddy <[email protected]> Signed-off-by: Vishnu Reddy <[email protected]> Signed-off-by: Vikash Garodia <[email protected]> --- drivers/iommu/of_iommu.c | 107 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c index 00b02c19f27e195fc36c4bc1d91253255147c9ab..e133d3b346feecac7e2bfaec44e4882060ee89ec 100644 --- a/drivers/iommu/of_iommu.c +++ b/drivers/iommu/of_iommu.c @@ -183,6 +183,111 @@ iommu_resv_region_get_type(struct device *dev, return IOMMU_RESV_RESERVED; } +/** + * of_iommu_derive_resv_regions - derive reserved regions which + * are outside of iommu-ranges + * @dev: device for which to get reserved regions + * @list: reserved region list + * + * A device can describe its own usable IOVA ranges directly on its node + * via "iommu-ranges". Everything not under those ranges is derived + * as a reserved region so the IOMMU allocator won't use it. Entries may + * appear in any order in the property. + */ +static void of_iommu_derive_resv_regions(struct device *dev, struct list_head *list) +{ + struct of_iommu_range { + struct list_head node; + phys_addr_t start; + phys_addr_t end; + } *pos, *new, *next_range; + int size, prot = IOMMU_READ | IOMMU_WRITE; + struct iommu_resv_region *region; + const __be32 *maps, *end; + struct list_head *prev; + phys_addr_t next = 0; + LIST_HEAD(ranges); + + maps = of_get_property(dev->of_node, "iommu-ranges", &size); + if (!maps) + return; + + end = maps + size / sizeof(__be32); + + while (maps < end) { + phys_addr_t iova, iova_end; + resource_size_t length; + + maps = of_translate_dma_region(dev->of_node, maps, &iova, &length); + if (!maps) { + dev_err(dev, "%pOF: failed to parse iommu-ranges\n", + dev->of_node); + goto exit; + } + + if (!length) + continue; + + iova_end = iova + length - 1; + + if (iova_end < iova) { + dev_err(dev, "%pOF: iommu-ranges overflows address space\n", + dev->of_node); + goto exit; + } + + prev = &ranges; + list_for_each_entry(pos, &ranges, node) { + if (pos->start > iova) + break; + prev = &pos->node; + } + + new = kmalloc_obj(*new); + if (!new) + goto exit; + + new->start = iova; + new->end = iova_end; + list_add(&new->node, prev); + } + + if (list_empty(&ranges)) + return; + + if (of_dma_is_coherent(dev->of_node)) + prot |= IOMMU_CACHE; + + list_for_each_entry_safe(pos, next_range, &ranges, node) { + if (pos->start > next) { + region = iommu_alloc_resv_region(next, pos->start - next, prot, + IOMMU_RESV_RESERVED, GFP_KERNEL); + if (region) + list_add_tail(®ion->list, list); + } + + if (pos->end >= PHYS_ADDR_MAX - 1) + goto exit; + + if (pos->end >= next) + next = pos->end + 1; + + list_del(&pos->node); + kfree(pos); + } + + region = iommu_alloc_resv_region(next, PHYS_ADDR_MAX - next, + prot, IOMMU_RESV_RESERVED, GFP_KERNEL); + if (region) + list_add_tail(®ion->list, list); + +exit: + list_for_each_entry_safe(pos, next_range, &ranges, node) { + list_del(&pos->node); + kfree(pos); + } +} + /** * of_iommu_get_resv_regions - reserved region driver helper for device tree * @dev: device for which to get reserved regions @@ -262,5 +367,7 @@ void of_iommu_get_resv_regions(struct device *dev, struct list_head *list) } } } + + of_iommu_derive_resv_regions(dev, list); } EXPORT_SYMBOL(of_iommu_get_resv_regions); -- 2.34.1
