]> Gentwo Git Trees - linux/.git/commitdiff
iommupt/vtd: Support mgaw's less than a 4 level walk for first stage
authorJason Gunthorpe <jgg@nvidia.com>
Thu, 27 Nov 2025 23:54:08 +0000 (19:54 -0400)
committerJoerg Roedel <joerg.roedel@amd.com>
Fri, 28 Nov 2025 07:43:55 +0000 (08:43 +0100)
If the IOVA is limited to less than 48 the page table will be constructed
with a 3 level configuration which is unsupported by hardware.

Like the second stage the caller needs to pass in both the top_level an
the vasz to specify a table that has more levels than required to hold the
IOVA range.

Fixes: 6cbc09b7719e ("iommu/vt-d: Restore previous domain::aperture_end calculation")
Reported-by: Calvin Owens <calvin@wbinvd.org>
Closes: https://lore.kernel.org/r/8f257d2651eb8a4358fcbd47b0145002e5f1d638.1764237717.git.calvin@wbinvd.org
Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
Reviewed-by: Lu Baolu <baolu.lu@linux.intel.com>
Tested-by: Calvin Owens <calvin@wbinvd.org>
Signed-off-by: Joerg Roedel <joerg.roedel@amd.com>
drivers/iommu/amd/iommu.c
drivers/iommu/generic_pt/fmt/x86_64.h
drivers/iommu/intel/iommu.c
include/linux/generic_pt/iommu.h

index 48bca4dc8eb61fdc2d5b332265df4640dc7ae9a7..273951b4501cd571115af9a4024542a44619a216 100644 (file)
@@ -2708,10 +2708,13 @@ static struct iommu_domain *amd_iommu_domain_alloc_paging_v2(struct device *dev,
         * in both modes the top bit is removed and PT_FEAT_SIGN_EXTEND is not
         * set which creates a table that is compatible in both modes.
         */
-       if (amd_iommu_gpt_level == PAGE_MODE_5_LEVEL)
+       if (amd_iommu_gpt_level == PAGE_MODE_5_LEVEL) {
                cfg.common.hw_max_vasz_lg2 = 56;
-       else
+               cfg.top_level = 4;
+       } else {
                cfg.common.hw_max_vasz_lg2 = 47;
+               cfg.top_level = 3;
+       }
        cfg.common.hw_max_oasz_lg2 = 52;
        domain->domain.ops = &amdv2_ops;
 
index 507abf2c934ccb2266f530ea33f658109ef1a566..210748d9d6e8aa68bbae8f15072a8ab169b02b9d 100644 (file)
@@ -241,13 +241,10 @@ x86_64_pt_iommu_fmt_init(struct pt_iommu_x86_64 *iommu_table,
 {
        struct pt_x86_64 *table = &iommu_table->x86_64_pt;
 
-       if (cfg->common.hw_max_vasz_lg2 < 31 ||
-           cfg->common.hw_max_vasz_lg2 > 57)
-               return -EINVAL;
+       if (cfg->top_level < 3 || cfg->top_level > 4)
+               return -EOPNOTSUPP;
 
-       /* Top of 2, 3, 4 */
-       pt_top_set_level(&table->common,
-                        (cfg->common.hw_max_vasz_lg2 - 31) / 9 + 2);
+       pt_top_set_level(&table->common, cfg->top_level);
 
        table->common.max_oasz_lg2 =
                min(PT_MAX_OUTPUT_ADDRESS_LG2, cfg->common.hw_max_oasz_lg2);
@@ -269,12 +266,12 @@ x86_64_pt_iommu_fmt_hw_info(struct pt_iommu_x86_64 *table,
 #if defined(GENERIC_PT_KUNIT)
 static const struct pt_iommu_x86_64_cfg x86_64_kunit_fmt_cfgs[] = {
        [0] = { .common.features = BIT(PT_FEAT_SIGN_EXTEND),
-               .common.hw_max_vasz_lg2 = 48 },
+               .common.hw_max_vasz_lg2 = 48, .top_level = 3 },
        [1] = { .common.features = BIT(PT_FEAT_SIGN_EXTEND),
-               .common.hw_max_vasz_lg2 = 57 },
+               .common.hw_max_vasz_lg2 = 57, .top_level = 4 },
        /* AMD IOMMU PASID 0 formats with no SIGN_EXTEND */
-       [2] = { .common.hw_max_vasz_lg2 = 47 },
-       [3] = { .common.hw_max_vasz_lg2 = 56 },
+       [2] = { .common.hw_max_vasz_lg2 = 47, .top_level = 3 },
+       [3] = { .common.hw_max_vasz_lg2 = 56, .top_level = 4},
 };
 #define kunit_fmt_cfgs x86_64_kunit_fmt_cfgs
 enum { KUNIT_FMT_FEATURES =  BIT(PT_FEAT_SIGN_EXTEND)};
index f117349d67dbf8b6255769a13e16d046e4ef1cf5..4e888867e85c03bad20ef002895a2f7805077e18 100644 (file)
@@ -2794,6 +2794,28 @@ static struct dmar_domain *paging_domain_alloc(void)
        return domain;
 }
 
+static unsigned int compute_vasz_lg2_fs(struct intel_iommu *iommu,
+                                       unsigned int *top_level)
+{
+       unsigned int mgaw = cap_mgaw(iommu->cap);
+
+       /*
+        * Spec 3.6 First-Stage Translation:
+        *
+        * Software must limit addresses to less than the minimum of MGAW
+        * and the lower canonical address width implied by FSPM (i.e.,
+        * 47-bit when FSPM is 4-level and 56-bit when FSPM is 5-level).
+        */
+       if (mgaw > 48 && cap_fl5lp_support(iommu->cap)) {
+               *top_level = 4;
+               return min(57, mgaw);
+       }
+
+       /* Four level is always supported */
+       *top_level = 3;
+       return min(48, mgaw);
+}
+
 static struct iommu_domain *
 intel_iommu_domain_alloc_first_stage(struct device *dev,
                                     struct intel_iommu *iommu, u32 flags)
@@ -2813,20 +2835,8 @@ intel_iommu_domain_alloc_first_stage(struct device *dev,
        if (IS_ERR(dmar_domain))
                return ERR_CAST(dmar_domain);
 
-       if (cap_fl5lp_support(iommu->cap))
-               cfg.common.hw_max_vasz_lg2 = 57;
-       else
-               cfg.common.hw_max_vasz_lg2 = 48;
-
-       /*
-        * Spec 3.6 First-Stage Translation:
-        *
-        * Software must limit addresses to less than the minimum of MGAW
-        * and the lower canonical address width implied by FSPM (i.e.,
-        * 47-bit when FSPM is 4-level and 56-bit when FSPM is 5-level).
-        */
-       cfg.common.hw_max_vasz_lg2 = min(cap_mgaw(iommu->cap),
-                                        cfg.common.hw_max_vasz_lg2);
+       cfg.common.hw_max_vasz_lg2 =
+               compute_vasz_lg2_fs(iommu, &cfg.top_level);
        cfg.common.hw_max_oasz_lg2 = 52;
        cfg.common.features = BIT(PT_FEAT_SIGN_EXTEND) |
                              BIT(PT_FEAT_FLUSH_RANGE);
index c134132ed10f8938d2549eda45d55f62c6e4fb63..9eefbb74efd087815c8d2e4d34f0b9c7a33de68d 100644 (file)
@@ -277,6 +277,8 @@ IOMMU_FORMAT(vtdss, vtdss_pt);
 
 struct pt_iommu_x86_64_cfg {
        struct pt_iommu_cfg common;
+       /* 4 is a 57 bit 5 level table */
+       unsigned int top_level;
 };
 
 struct pt_iommu_x86_64_hw_info {