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

iommu/io-pgtable-arm: Make pgtable walker more generic



We can re-use this basic pgtable walk logic in a few places.

Signed-off-by: default avatarRob Clark <robdclark@chromium.org>
Reviewed-by: default avatarMostafa Saleh <smostafa@google.com>
Link: https://lore.kernel.org/r/20241210165127.600817-2-robdclark@gmail.com


Signed-off-by: default avatarWill Deacon <will@kernel.org>
parent 3e35c3e7
Loading
Loading
Loading
Loading
+43 −24
Original line number Diff line number Diff line
@@ -741,33 +741,33 @@ static phys_addr_t arm_lpae_iova_to_phys(struct io_pgtable_ops *ops,
}

struct io_pgtable_walk_data {
	struct iommu_dirty_bitmap	*dirty;
	struct io_pgtable		*iop;
	void				*data;
	int (*visit)(struct io_pgtable_walk_data *walk_data, int lvl,
		     arm_lpae_iopte *ptep, size_t size);
	unsigned long			flags;
	u64				addr;
	const u64			end;
};

static int __arm_lpae_iopte_walk_dirty(struct arm_lpae_io_pgtable *data,
static int __arm_lpae_iopte_walk(struct arm_lpae_io_pgtable *data,
				 struct io_pgtable_walk_data *walk_data,
				 arm_lpae_iopte *ptep,
				 int lvl);

static int io_pgtable_visit_dirty(struct arm_lpae_io_pgtable *data,
static int io_pgtable_visit(struct arm_lpae_io_pgtable *data,
			    struct io_pgtable_walk_data *walk_data,
			    arm_lpae_iopte *ptep, int lvl)
{
	struct io_pgtable *iop = &data->iop;
	arm_lpae_iopte pte = READ_ONCE(*ptep);

	if (iopte_leaf(pte, lvl, iop->fmt)) {
	size_t size = ARM_LPAE_BLOCK_SIZE(lvl, data);
	int ret = walk_data->visit(walk_data, lvl, ptep, size);
	if (ret)
		return ret;

		if (iopte_writeable_dirty(pte)) {
			iommu_dirty_bitmap_record(walk_data->dirty,
						  walk_data->addr, size);
			if (!(walk_data->flags & IOMMU_DIRTY_NO_CLEAR))
				iopte_set_writeable_clean(ptep);
		}
	if (iopte_leaf(pte, lvl, iop->fmt)) {
		walk_data->addr += size;
		return 0;
	}
@@ -776,10 +776,10 @@ static int io_pgtable_visit_dirty(struct arm_lpae_io_pgtable *data,
		return -EINVAL;

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

static int __arm_lpae_iopte_walk_dirty(struct arm_lpae_io_pgtable *data,
static int __arm_lpae_iopte_walk(struct arm_lpae_io_pgtable *data,
				 struct io_pgtable_walk_data *walk_data,
				 arm_lpae_iopte *ptep,
				 int lvl)
@@ -797,7 +797,7 @@ static int __arm_lpae_iopte_walk_dirty(struct arm_lpae_io_pgtable *data,

	for (idx = ARM_LPAE_LVL_IDX(walk_data->addr, lvl, data);
	     (idx < max_entries) && (walk_data->addr < walk_data->end); ++idx) {
		ret = io_pgtable_visit_dirty(data, walk_data, ptep + idx, lvl);
		ret = io_pgtable_visit(data, walk_data, ptep + idx, lvl);
		if (ret)
			return ret;
	}
@@ -805,6 +805,23 @@ static int __arm_lpae_iopte_walk_dirty(struct arm_lpae_io_pgtable *data,
	return 0;
}

static int visit_dirty(struct io_pgtable_walk_data *walk_data, int lvl,
		       arm_lpae_iopte *ptep, size_t size)
{
	struct iommu_dirty_bitmap *dirty = walk_data->data;

	if (!iopte_leaf(*ptep, lvl, walk_data->iop->fmt))
		return 0;

	if (iopte_writeable_dirty(*ptep)) {
		iommu_dirty_bitmap_record(dirty, walk_data->addr, size);
		if (!(walk_data->flags & IOMMU_DIRTY_NO_CLEAR))
			iopte_set_writeable_clean(ptep);
	}

	return 0;
}

static int arm_lpae_read_and_clear_dirty(struct io_pgtable_ops *ops,
					 unsigned long iova, size_t size,
					 unsigned long flags,
@@ -813,7 +830,9 @@ static int arm_lpae_read_and_clear_dirty(struct io_pgtable_ops *ops,
	struct arm_lpae_io_pgtable *data = io_pgtable_ops_to_data(ops);
	struct io_pgtable_cfg *cfg = &data->iop.cfg;
	struct io_pgtable_walk_data walk_data = {
		.dirty = dirty,
		.iop = &data->iop,
		.data = dirty,
		.visit = visit_dirty,
		.flags = flags,
		.addr = iova,
		.end = iova + size,
@@ -828,7 +847,7 @@ static int arm_lpae_read_and_clear_dirty(struct io_pgtable_ops *ops,
	if (data->iop.fmt != ARM_64_LPAE_S1)
		return -EINVAL;

	return __arm_lpae_iopte_walk_dirty(data, &walk_data, ptep, lvl);
	return __arm_lpae_iopte_walk(data, &walk_data, ptep, lvl);
}

static void arm_lpae_restrict_pgsizes(struct io_pgtable_cfg *cfg)