| From cd86262b3ef0714451db4b56253fd7330c3a5da2 Mon Sep 17 00:00:00 2001 |
| From: Thierry Reding <treding@nvidia.com> |
| Date: Wed, 16 Oct 2019 13:50:26 +0200 |
| Subject: [PATCH] iommu/tegra-smmu: Fix page tables in > 4 GiB memory |
| |
| commit 96d3ab802e4930a29a33934373157d6dff1b2c7e upstream. |
| |
| Page tables that reside in physical memory beyond the 4 GiB boundary are |
| currently not working properly. The reason is that when the physical |
| address for page directory entries is read, it gets truncated at 32 bits |
| and can cause crashes when passing that address to the DMA API. |
| |
| Fix this by first casting the PDE value to a dma_addr_t and then using |
| the page frame number mask for the SMMU instance to mask out the invalid |
| bits, which are typically used for mapping attributes, etc. |
| |
| Signed-off-by: Thierry Reding <treding@nvidia.com> |
| Signed-off-by: Joerg Roedel <jroedel@suse.de> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c |
| index c4a652b227f8..200d6afc2188 100644 |
| --- a/drivers/iommu/tegra-smmu.c |
| +++ b/drivers/iommu/tegra-smmu.c |
| @@ -159,9 +159,9 @@ static bool smmu_dma_addr_valid(struct tegra_smmu *smmu, dma_addr_t addr) |
| return (addr & smmu->pfn_mask) == addr; |
| } |
| |
| -static dma_addr_t smmu_pde_to_dma(u32 pde) |
| +static dma_addr_t smmu_pde_to_dma(struct tegra_smmu *smmu, u32 pde) |
| { |
| - return pde << 12; |
| + return (dma_addr_t)(pde & smmu->pfn_mask) << 12; |
| } |
| |
| static void smmu_flush_ptc_all(struct tegra_smmu *smmu) |
| @@ -549,6 +549,7 @@ static u32 *tegra_smmu_pte_lookup(struct tegra_smmu_as *as, unsigned long iova, |
| dma_addr_t *dmap) |
| { |
| unsigned int pd_index = iova_pd_index(iova); |
| + struct tegra_smmu *smmu = as->smmu; |
| struct page *pt_page; |
| u32 *pd; |
| |
| @@ -557,7 +558,7 @@ static u32 *tegra_smmu_pte_lookup(struct tegra_smmu_as *as, unsigned long iova, |
| return NULL; |
| |
| pd = page_address(as->pd); |
| - *dmap = smmu_pde_to_dma(pd[pd_index]); |
| + *dmap = smmu_pde_to_dma(smmu, pd[pd_index]); |
| |
| return tegra_smmu_pte_offset(pt_page, iova); |
| } |
| @@ -599,7 +600,7 @@ static u32 *as_get_pte(struct tegra_smmu_as *as, dma_addr_t iova, |
| } else { |
| u32 *pd = page_address(as->pd); |
| |
| - *dmap = smmu_pde_to_dma(pd[pde]); |
| + *dmap = smmu_pde_to_dma(smmu, pd[pde]); |
| } |
| |
| return tegra_smmu_pte_offset(as->pts[pde], iova); |
| @@ -624,7 +625,7 @@ static void tegra_smmu_pte_put_use(struct tegra_smmu_as *as, unsigned long iova) |
| if (--as->count[pde] == 0) { |
| struct tegra_smmu *smmu = as->smmu; |
| u32 *pd = page_address(as->pd); |
| - dma_addr_t pte_dma = smmu_pde_to_dma(pd[pde]); |
| + dma_addr_t pte_dma = smmu_pde_to_dma(smmu, pd[pde]); |
| |
| tegra_smmu_set_pde(as, iova, 0); |
| |
| -- |
| 2.7.4 |
| |