Commit d9e589e6 authored by Rob Clark's avatar Rob Clark Committed by Will Deacon
Browse files

iommu/io-pgtable-arm: Re-use the pgtable walk for iova_to_phys

parent 821500d5
Loading
Loading
Loading
Loading
+37 −37
Original line number Diff line number Diff line
@@ -704,42 +704,6 @@ static size_t arm_lpae_unmap_pages(struct io_pgtable_ops *ops, unsigned long iov
				data->start_level, ptep);
}

static phys_addr_t arm_lpae_iova_to_phys(struct io_pgtable_ops *ops,
					 unsigned long iova)
{
	struct arm_lpae_io_pgtable *data = io_pgtable_ops_to_data(ops);
	arm_lpae_iopte pte, *ptep = data->pgd;
	int lvl = data->start_level;

	do {
		/* Valid IOPTE pointer? */
		if (!ptep)
			return 0;

		/* Grab the IOPTE we're interested in */
		ptep += ARM_LPAE_LVL_IDX(iova, lvl, data);
		pte = READ_ONCE(*ptep);

		/* Valid entry? */
		if (!pte)
			return 0;

		/* Leaf entry? */
		if (iopte_leaf(pte, lvl, data->iop.fmt))
			goto found_translation;

		/* Take it to the next level */
		ptep = iopte_deref(pte, data);
	} while (++lvl < ARM_LPAE_MAX_LEVELS);

	/* Ran out of page tables to walk */
	return 0;

found_translation:
	iova &= (ARM_LPAE_BLOCK_SIZE(lvl, data) - 1);
	return iopte_to_paddr(pte, data) | iova;
}

struct io_pgtable_walk_data {
	struct io_pgtable		*iop;
	void				*data;
@@ -755,6 +719,41 @@ static int __arm_lpae_iopte_walk(struct arm_lpae_io_pgtable *data,
				 arm_lpae_iopte *ptep,
				 int lvl);

struct iova_to_phys_data {
	arm_lpae_iopte pte;
	int lvl;
};

static int visit_iova_to_phys(struct io_pgtable_walk_data *walk_data, int lvl,
			      arm_lpae_iopte *ptep, size_t size)
{
	struct iova_to_phys_data *data = walk_data->data;
	data->pte = *ptep;
	data->lvl = lvl;
	return 0;
}

static phys_addr_t arm_lpae_iova_to_phys(struct io_pgtable_ops *ops,
					 unsigned long iova)
{
	struct arm_lpae_io_pgtable *data = io_pgtable_ops_to_data(ops);
	struct iova_to_phys_data d;
	struct io_pgtable_walk_data walk_data = {
		.data = &d,
		.visit = visit_iova_to_phys,
		.addr = iova,
		.end = iova + 1,
	};
	int ret;

	ret = __arm_lpae_iopte_walk(data, &walk_data, data->pgd, data->start_level);
	if (ret)
		return 0;

	iova &= (ARM_LPAE_BLOCK_SIZE(d.lvl, data) - 1);
	return iopte_to_paddr(d.pte, data) | iova;
}

static int io_pgtable_visit(struct arm_lpae_io_pgtable *data,
			    struct io_pgtable_walk_data *walk_data,
			    arm_lpae_iopte *ptep, int lvl)
@@ -772,8 +771,9 @@ static int io_pgtable_visit(struct arm_lpae_io_pgtable *data,
		return 0;
	}

	if (WARN_ON(!iopte_table(pte, lvl)))
	if (!iopte_table(pte, lvl)) {
		return -EINVAL;
	}

	ptep = iopte_deref(pte, data);
	return __arm_lpae_iopte_walk(data, walk_data, ptep, lvl + 1);