]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
iommu/tegra241-cmdqv: Read SMMU IDR1.CMDQS instead of hardcoding
authorNicolin Chen <nicolinc@nvidia.com>
Thu, 19 Dec 2024 05:14:21 +0000 (21:14 -0800)
committerWill Deacon <will@kernel.org>
Thu, 19 Dec 2024 15:44:11 +0000 (15:44 +0000)
The hardware limitation "max=19" actually comes from SMMU Command Queue.
So, it'd be more natural for tegra241-cmdqv driver to read it out rather
than hardcoding it itself.

This is not an issue yet for a kernel on a baremetal system, but a guest
kernel setting the queue base/size in form of IPA/gPA might result in a
noncontiguous queue in the physical address space, if underlying physical
pages backing up the guest RAM aren't contiguous entirely: e.g. 2MB-page
backed guest RAM cannot guarantee a contiguous queue if it is 8MB (capped
to VCMDQ_LOG2SIZE_MAX=19). This might lead to command errors when HW does
linear-read from a noncontiguous queue memory.

Adding this extra IDR1.CMDQS cap (in the guest kernel) allows VMM to set
SMMU's IDR1.CMDQS=17 for the case mentioned above, so a guest-level queue
will be capped to maximum 2MB, ensuring a contiguous queue memory.

Fixes: a3799717b881 ("iommu/tegra241-cmdqv: Fix alignment failure at max_n_shift")
Reported-by: Ian Kalinowski <ikalinowski@nvidia.com>
Cc: stable@vger.kernel.org
Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>
Link: https://lore.kernel.org/r/20241219051421.1850267-1-nicolinc@nvidia.com
Signed-off-by: Will Deacon <will@kernel.org>
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c

index c8ec74f089f3d66645f58987a68a0b9feaf14bf5..dc7af970e9d0a9cd5772c97eadb6a994ca64ef98 100644 (file)
@@ -79,7 +79,6 @@
 #define TEGRA241_VCMDQ_PAGE1(q)                (TEGRA241_VCMDQ_PAGE1_BASE + 0x80*(q))
 #define  VCMDQ_ADDR                    GENMASK(47, 5)
 #define  VCMDQ_LOG2SIZE                        GENMASK(4, 0)
-#define  VCMDQ_LOG2SIZE_MAX            19
 
 #define TEGRA241_VCMDQ_BASE            0x00000
 #define TEGRA241_VCMDQ_CONS_INDX_BASE  0x00008
@@ -505,12 +504,15 @@ static int tegra241_vcmdq_alloc_smmu_cmdq(struct tegra241_vcmdq *vcmdq)
        struct arm_smmu_cmdq *cmdq = &vcmdq->cmdq;
        struct arm_smmu_queue *q = &cmdq->q;
        char name[16];
+       u32 regval;
        int ret;
 
        snprintf(name, 16, "vcmdq%u", vcmdq->idx);
 
-       /* Queue size, capped to ensure natural alignment */
-       q->llq.max_n_shift = min_t(u32, CMDQ_MAX_SZ_SHIFT, VCMDQ_LOG2SIZE_MAX);
+       /* Cap queue size to SMMU's IDR1.CMDQS and ensure natural alignment */
+       regval = readl_relaxed(smmu->base + ARM_SMMU_IDR1);
+       q->llq.max_n_shift =
+               min_t(u32, CMDQ_MAX_SZ_SHIFT, FIELD_GET(IDR1_CMDQS, regval));
 
        /* Use the common helper to init the VCMDQ, and then... */
        ret = arm_smmu_init_one_queue(smmu, q, vcmdq->page0,