Re: [PATCH v4 1/3] iommu_pt: support small VA for AMDv1

From: Jason Gunthorpe

Date: Mon Apr 06 2026 - 09:28:26 EST


On Wed, Apr 01, 2026 at 02:43:55PM +0000, Ankit Soni wrote:
> When hardware/VM request a small VA limit, the generic page table code
> clears PT_FEAT_DYNAMIC_TOP. This later causes domain initialization to
> fail with -EOPNOTSUPP.
> To properly enforce the domain VA limit, clamp amdv1pt_possible_sizes
> using the requested max_vasz_lg2.
>
> Signed-off-by: Ankit Soni <Ankit.Soni@xxxxxxx>
> ---
> drivers/iommu/generic_pt/fmt/amdv1.h | 4 +++-
> drivers/iommu/generic_pt/iommu_pt.h | 4 ----
> 2 files changed, 3 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/iommu/generic_pt/fmt/amdv1.h b/drivers/iommu/generic_pt/fmt/amdv1.h
> index 3b2c41d9654d..bc04d482c12f 100644
> --- a/drivers/iommu/generic_pt/fmt/amdv1.h
> +++ b/drivers/iommu/generic_pt/fmt/amdv1.h
> @@ -156,6 +156,7 @@ static inline unsigned int amdv1pt_num_items_lg2(const struct pt_state *pts)
> static inline pt_vaddr_t amdv1pt_possible_sizes(const struct pt_state *pts)
> {
> unsigned int isz_lg2 = pt_table_item_lg2sz(pts);
> + pt_vaddr_t raw;
>
> if (!amdv1pt_can_have_leaf(pts))
> return 0;
> @@ -168,8 +169,9 @@ static inline pt_vaddr_t amdv1pt_possible_sizes(const struct pt_state *pts)
> * 512GB Pages are not supported due to a hardware bug.
> * Otherwise every power of two size is supported.
> */
> - return GENMASK_ULL(min(51, isz_lg2 + amdv1pt_num_items_lg2(pts) - 1),
> + raw = GENMASK_ULL(min(51, isz_lg2 + amdv1pt_num_items_lg2(pts) - 1),
> isz_lg2) & ~SZ_512G;
> + return fvalog2_mod(raw, pts->range->common->max_vasz_lg2);
> }
> #define pt_possible_sizes amdv1pt_possible_sizes

This is a generic issue, I think it should be fixed like this instead:

@@ -1153,8 +1153,12 @@ static void NS(get_info)(struct pt_iommu *iommu_table,
pgsize_bitmap |= pt_possible_sizes(&pts);
}

- /* Hide page sizes larger than the maximum OA */
- info->pgsize_bitmap = oalog2_mod(pgsize_bitmap, common->max_oasz_lg2);
+ /*
+ * Hide page sizes larger than the maximum. -1 because a whole table
+ * pgsize is not allowed
+ */
+ info->pgsize_bitmap = log2_mod(pgsize_bitmap, common->max_vasz_lg2 - 1);
+ info->pgsize_bitmap = oalog2_mod(info->pgsize_bitmap, common->max_oasz_lg2);
}

pt_possible_sizes() always has to be masked with the pgsize_bitmap, so
fixing it here is enough to remove those sizes from all the other
places..

Then one of the tests isn't following that rule, so it needs a fix too:

@@ -438,6 +440,9 @@ static void test_lvl_possible_sizes(struct kunit *test, struct pt_state *pts,
{
unsigned int num_items_lg2 = safe_pt_num_items_lg2(pts);
pt_vaddr_t pgsize_bitmap = pt_possible_sizes(pts);
+ /* Matches get_info() */
+ pt_vaddr_t limited_pgsize_bitmap =
+ log2_mod(pgsize_bitmap, pts->range->common->max_vasz_lg2 - 1);
unsigned int isz_lg2 = pt_table_item_lg2sz(pts);

if (!pt_can_have_leaf(pts)) {
@@ -448,7 +453,8 @@ static void test_lvl_possible_sizes(struct kunit *test, struct pt_state *pts,
/* No bits for sizes that would be outside this table */
KUNIT_ASSERT_EQ(test, log2_mod(pgsize_bitmap, isz_lg2), 0);
KUNIT_ASSERT_EQ(
- test, fvalog2_div(pgsize_bitmap, num_items_lg2 + isz_lg2), 0);
+ test,
+ fvalog2_div(limited_pgsize_bitmap, num_items_lg2 + isz_lg2), 0);

/*
* Non contiguous must be supported. AMDv1 has a HW bug where it does
@@ -463,8 +469,8 @@ static void test_lvl_possible_sizes(struct kunit *test, struct pt_state *pts,
/* A contiguous entry should not span the whole table */
if (num_items_lg2 + isz_lg2 != PT_VADDR_MAX_LG2)
KUNIT_ASSERT_FALSE(
- test,
- pgsize_bitmap & log2_to_int(num_items_lg2 + isz_lg2));
+ test, limited_pgsize_bitmap &
+ log2_to_int(num_items_lg2 + isz_lg2));
}

And it should go in its own patch: "fix pgsize_bitmap calculation in
get_info for smaller vasz's"

Jason