| From a80774a7a81f431ca6b0a39b100acd803c5e3cd4 Mon Sep 17 00:00:00 2001 |
| From: Rob Clark <robdclark@chromium.org> |
| Date: Wed, 22 Sep 2021 15:30:21 -0700 |
| Subject: [PATCH] FROMLIST: iommu/io-pgtable-arm: Add way to debug pgtable walk |
| |
| Add an io-pgtable method to retrieve the raw PTEs that would be |
| traversed for a given iova access. |
| |
| Signed-off-by: Rob Clark <robdclark@chromium.org> |
| (am from https://patchwork.freedesktop.org/patch/455308/) |
| (also found at https://lore.kernel.org/r/20210922223029.495772-2-robdclark@gmail.com) |
| |
| BUG=b:201293781 |
| TEST=boot coachz |
| |
| Change-Id: I4186dd89942c3c2c8635e4102104e6e2b99e8a37 |
| Disallow-Recycled-Builds: test-failures |
| Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/kernel/+/3251518 |
| Reviewed-by: Sean Paul <seanpaul@chromium.org> |
| Reviewed-by: Kristian H. Kristensen <hoegsberg@chromium.org> |
| Tested-by: Rob Clark <robdclark@chromium.org> |
| Auto-Submit: Rob Clark <robdclark@chromium.org> |
| Commit-Queue: Kristian H. Kristensen <hoegsberg@chromium.org> |
| --- |
| drivers/iommu/io-pgtable-arm.c | 40 +++++++++++++++++++++++++++------- |
| include/linux/io-pgtable.h | 9 ++++++++ |
| 2 files changed, 41 insertions(+), 8 deletions(-) |
| |
| diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c |
| index 72dcdd468cf30d6ec32eaf0795463549076309b0..4ff8c11b1847d45ad685f2e278f5555d44e7cb66 100644 |
| --- a/drivers/iommu/io-pgtable-arm.c |
| +++ b/drivers/iommu/io-pgtable-arm.c |
| @@ -675,38 +675,61 @@ 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) |
| +static int arm_lpae_pgtable_walk(struct io_pgtable_ops *ops, unsigned long iova, |
| + void *_ptes, int *num_ptes) |
| { |
| struct arm_lpae_io_pgtable *data = io_pgtable_ops_to_data(ops); |
| arm_lpae_iopte pte, *ptep = data->pgd; |
| + arm_lpae_iopte *ptes = _ptes; |
| + int max_ptes = *num_ptes; |
| int lvl = data->start_level; |
| |
| + *num_ptes = 0; |
| + |
| do { |
| + if (*num_ptes >= max_ptes) |
| + return -ENOSPC; |
| + |
| /* Valid IOPTE pointer? */ |
| if (!ptep) |
| - return 0; |
| + return -EFAULT; |
| |
| /* Grab the IOPTE we're interested in */ |
| ptep += ARM_LPAE_LVL_IDX(iova, lvl, data); |
| pte = READ_ONCE(*ptep); |
| |
| + ptes[(*num_ptes)++] = pte; |
| + |
| /* Valid entry? */ |
| if (!pte) |
| - return 0; |
| + return -EFAULT; |
| |
| /* Leaf entry? */ |
| if (iopte_leaf(pte, lvl, data->iop.fmt)) |
| - goto found_translation; |
| + return 0; |
| |
| /* 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; |
| + return -EFAULT; |
| +} |
| + |
| +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, ptes[ARM_LPAE_MAX_LEVELS]; |
| + int lvl, num_ptes = ARM_LPAE_MAX_LEVELS; |
| + int ret; |
| + |
| + ret = arm_lpae_pgtable_walk(ops, iova, ptes, &num_ptes); |
| + if (ret) |
| + return 0; |
| + |
| + pte = ptes[num_ptes - 1]; |
| + lvl = num_ptes - 1 + data->start_level; |
| |
| -found_translation: |
| iova &= (ARM_LPAE_BLOCK_SIZE(lvl, data) - 1); |
| return iopte_to_paddr(pte, data) | iova; |
| } |
| @@ -789,6 +812,7 @@ arm_lpae_alloc_pgtable(struct io_pgtable_cfg *cfg) |
| .map_pages = arm_lpae_map_pages, |
| .unmap_pages = arm_lpae_unmap_pages, |
| .iova_to_phys = arm_lpae_iova_to_phys, |
| + .pgtable_walk = arm_lpae_pgtable_walk, |
| }; |
| |
| return data; |
| diff --git a/include/linux/io-pgtable.h b/include/linux/io-pgtable.h |
| index 25142a0e2fc2c51d4c7807a1fb87cc21b16a163b..103d1640098400a217c3f308fe30b26c55ef6b32 100644 |
| --- a/include/linux/io-pgtable.h |
| +++ b/include/linux/io-pgtable.h |
| @@ -153,6 +153,13 @@ struct io_pgtable_cfg { |
| * @map_pages: Map a physically contiguous range of pages of the same size. |
| * @unmap_pages: Unmap a range of virtually contiguous pages of the same size. |
| * @iova_to_phys: Translate iova to physical address. |
| + * @pgtable_walk: Return details of a page table walk for a given iova. |
| + * This returns the array of PTEs in a format that is |
| + * specific to the page table format. The number of |
| + * PTEs can be format specific. The num_ptes parameter |
| + * on input specifies the size of the ptes array, and |
| + * on output the number of PTEs filled in (which depends |
| + * on the number of PTEs walked to resolve the iova) |
| * |
| * These functions map directly onto the iommu_ops member functions with |
| * the same names. |
| @@ -170,6 +177,8 @@ struct io_pgtable_ops { |
| unsigned long iova, size_t size, |
| unsigned long flags, |
| struct iommu_dirty_bitmap *dirty); |
| + int (*pgtable_walk)(struct io_pgtable_ops *ops, unsigned long iova, |
| + void *ptes, int *num_ptes); |
| }; |
| |
| /** |
| -- |
| 2.43.0.rc2.451.g8631bc7472-goog |
| |