18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Implementation of the IOMMU SVA API for the ARM SMMUv3 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/mm.h> 78c2ecf20Sopenharmony_ci#include <linux/mmu_context.h> 88c2ecf20Sopenharmony_ci#include <linux/slab.h> 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include "arm-smmu-v3.h" 118c2ecf20Sopenharmony_ci#include "../../io-pgtable-arm.h" 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(sva_lock); 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci/* 168c2ecf20Sopenharmony_ci * Check if the CPU ASID is available on the SMMU side. If a private context 178c2ecf20Sopenharmony_ci * descriptor is using it, try to replace it. 188c2ecf20Sopenharmony_ci */ 198c2ecf20Sopenharmony_cistatic struct arm_smmu_ctx_desc * 208c2ecf20Sopenharmony_ciarm_smmu_share_asid(struct mm_struct *mm, u16 asid) 218c2ecf20Sopenharmony_ci{ 228c2ecf20Sopenharmony_ci int ret; 238c2ecf20Sopenharmony_ci u32 new_asid; 248c2ecf20Sopenharmony_ci struct arm_smmu_ctx_desc *cd; 258c2ecf20Sopenharmony_ci struct arm_smmu_device *smmu; 268c2ecf20Sopenharmony_ci struct arm_smmu_domain *smmu_domain; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci cd = xa_load(&arm_smmu_asid_xa, asid); 298c2ecf20Sopenharmony_ci if (!cd) 308c2ecf20Sopenharmony_ci return NULL; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci if (cd->mm) { 338c2ecf20Sopenharmony_ci if (WARN_ON(cd->mm != mm)) 348c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 358c2ecf20Sopenharmony_ci /* All devices bound to this mm use the same cd struct. */ 368c2ecf20Sopenharmony_ci refcount_inc(&cd->refs); 378c2ecf20Sopenharmony_ci return cd; 388c2ecf20Sopenharmony_ci } 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci smmu_domain = container_of(cd, struct arm_smmu_domain, s1_cfg.cd); 418c2ecf20Sopenharmony_ci smmu = smmu_domain->smmu; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci ret = xa_alloc(&arm_smmu_asid_xa, &new_asid, cd, 448c2ecf20Sopenharmony_ci XA_LIMIT(1, (1 << smmu->asid_bits) - 1), GFP_KERNEL); 458c2ecf20Sopenharmony_ci if (ret) 468c2ecf20Sopenharmony_ci return ERR_PTR(-ENOSPC); 478c2ecf20Sopenharmony_ci /* 488c2ecf20Sopenharmony_ci * Race with unmap: TLB invalidations will start targeting the new ASID, 498c2ecf20Sopenharmony_ci * which isn't assigned yet. We'll do an invalidate-all on the old ASID 508c2ecf20Sopenharmony_ci * later, so it doesn't matter. 518c2ecf20Sopenharmony_ci */ 528c2ecf20Sopenharmony_ci cd->asid = new_asid; 538c2ecf20Sopenharmony_ci /* 548c2ecf20Sopenharmony_ci * Update ASID and invalidate CD in all associated masters. There will 558c2ecf20Sopenharmony_ci * be some overlap between use of both ASIDs, until we invalidate the 568c2ecf20Sopenharmony_ci * TLB. 578c2ecf20Sopenharmony_ci */ 588c2ecf20Sopenharmony_ci arm_smmu_write_ctx_desc(smmu_domain, 0, cd); 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci /* Invalidate TLB entries previously associated with that context */ 618c2ecf20Sopenharmony_ci arm_smmu_tlb_inv_asid(smmu, asid); 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci xa_erase(&arm_smmu_asid_xa, asid); 648c2ecf20Sopenharmony_ci return NULL; 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci__maybe_unused 688c2ecf20Sopenharmony_cistatic struct arm_smmu_ctx_desc *arm_smmu_alloc_shared_cd(struct mm_struct *mm) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci u16 asid; 718c2ecf20Sopenharmony_ci int err = 0; 728c2ecf20Sopenharmony_ci u64 tcr, par, reg; 738c2ecf20Sopenharmony_ci struct arm_smmu_ctx_desc *cd; 748c2ecf20Sopenharmony_ci struct arm_smmu_ctx_desc *ret = NULL; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci asid = arm64_mm_context_get(mm); 778c2ecf20Sopenharmony_ci if (!asid) 788c2ecf20Sopenharmony_ci return ERR_PTR(-ESRCH); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci cd = kzalloc(sizeof(*cd), GFP_KERNEL); 818c2ecf20Sopenharmony_ci if (!cd) { 828c2ecf20Sopenharmony_ci err = -ENOMEM; 838c2ecf20Sopenharmony_ci goto out_put_context; 848c2ecf20Sopenharmony_ci } 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci refcount_set(&cd->refs, 1); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci mutex_lock(&arm_smmu_asid_lock); 898c2ecf20Sopenharmony_ci ret = arm_smmu_share_asid(mm, asid); 908c2ecf20Sopenharmony_ci if (ret) { 918c2ecf20Sopenharmony_ci mutex_unlock(&arm_smmu_asid_lock); 928c2ecf20Sopenharmony_ci goto out_free_cd; 938c2ecf20Sopenharmony_ci } 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci err = xa_insert(&arm_smmu_asid_xa, asid, cd, GFP_KERNEL); 968c2ecf20Sopenharmony_ci mutex_unlock(&arm_smmu_asid_lock); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci if (err) 998c2ecf20Sopenharmony_ci goto out_free_asid; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci tcr = FIELD_PREP(CTXDESC_CD_0_TCR_T0SZ, 64ULL - vabits_actual) | 1028c2ecf20Sopenharmony_ci FIELD_PREP(CTXDESC_CD_0_TCR_IRGN0, ARM_LPAE_TCR_RGN_WBWA) | 1038c2ecf20Sopenharmony_ci FIELD_PREP(CTXDESC_CD_0_TCR_ORGN0, ARM_LPAE_TCR_RGN_WBWA) | 1048c2ecf20Sopenharmony_ci FIELD_PREP(CTXDESC_CD_0_TCR_SH0, ARM_LPAE_TCR_SH_IS) | 1058c2ecf20Sopenharmony_ci CTXDESC_CD_0_TCR_EPD1 | CTXDESC_CD_0_AA64; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci switch (PAGE_SIZE) { 1088c2ecf20Sopenharmony_ci case SZ_4K: 1098c2ecf20Sopenharmony_ci tcr |= FIELD_PREP(CTXDESC_CD_0_TCR_TG0, ARM_LPAE_TCR_TG0_4K); 1108c2ecf20Sopenharmony_ci break; 1118c2ecf20Sopenharmony_ci case SZ_16K: 1128c2ecf20Sopenharmony_ci tcr |= FIELD_PREP(CTXDESC_CD_0_TCR_TG0, ARM_LPAE_TCR_TG0_16K); 1138c2ecf20Sopenharmony_ci break; 1148c2ecf20Sopenharmony_ci case SZ_64K: 1158c2ecf20Sopenharmony_ci tcr |= FIELD_PREP(CTXDESC_CD_0_TCR_TG0, ARM_LPAE_TCR_TG0_64K); 1168c2ecf20Sopenharmony_ci break; 1178c2ecf20Sopenharmony_ci default: 1188c2ecf20Sopenharmony_ci WARN_ON(1); 1198c2ecf20Sopenharmony_ci err = -EINVAL; 1208c2ecf20Sopenharmony_ci goto out_free_asid; 1218c2ecf20Sopenharmony_ci } 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci reg = read_sanitised_ftr_reg(SYS_ID_AA64MMFR0_EL1); 1248c2ecf20Sopenharmony_ci par = cpuid_feature_extract_unsigned_field(reg, ID_AA64MMFR0_PARANGE_SHIFT); 1258c2ecf20Sopenharmony_ci tcr |= FIELD_PREP(CTXDESC_CD_0_TCR_IPS, par); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci cd->ttbr = virt_to_phys(mm->pgd); 1288c2ecf20Sopenharmony_ci cd->tcr = tcr; 1298c2ecf20Sopenharmony_ci /* 1308c2ecf20Sopenharmony_ci * MAIR value is pretty much constant and global, so we can just get it 1318c2ecf20Sopenharmony_ci * from the current CPU register 1328c2ecf20Sopenharmony_ci */ 1338c2ecf20Sopenharmony_ci cd->mair = read_sysreg(mair_el1); 1348c2ecf20Sopenharmony_ci cd->asid = asid; 1358c2ecf20Sopenharmony_ci cd->mm = mm; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci return cd; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ciout_free_asid: 1408c2ecf20Sopenharmony_ci arm_smmu_free_asid(cd); 1418c2ecf20Sopenharmony_ciout_free_cd: 1428c2ecf20Sopenharmony_ci kfree(cd); 1438c2ecf20Sopenharmony_ciout_put_context: 1448c2ecf20Sopenharmony_ci arm64_mm_context_put(mm); 1458c2ecf20Sopenharmony_ci return err < 0 ? ERR_PTR(err) : ret; 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci__maybe_unused 1498c2ecf20Sopenharmony_cistatic void arm_smmu_free_shared_cd(struct arm_smmu_ctx_desc *cd) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci if (arm_smmu_free_asid(cd)) { 1528c2ecf20Sopenharmony_ci /* Unpin ASID */ 1538c2ecf20Sopenharmony_ci arm64_mm_context_put(cd->mm); 1548c2ecf20Sopenharmony_ci kfree(cd); 1558c2ecf20Sopenharmony_ci } 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cibool arm_smmu_sva_supported(struct arm_smmu_device *smmu) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci unsigned long reg, fld; 1618c2ecf20Sopenharmony_ci unsigned long oas; 1628c2ecf20Sopenharmony_ci unsigned long asid_bits; 1638c2ecf20Sopenharmony_ci u32 feat_mask = ARM_SMMU_FEAT_BTM | ARM_SMMU_FEAT_COHERENCY; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci if (vabits_actual == 52) 1668c2ecf20Sopenharmony_ci feat_mask |= ARM_SMMU_FEAT_VAX; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci if ((smmu->features & feat_mask) != feat_mask) 1698c2ecf20Sopenharmony_ci return false; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci if (!(smmu->pgsize_bitmap & PAGE_SIZE)) 1728c2ecf20Sopenharmony_ci return false; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci /* 1758c2ecf20Sopenharmony_ci * Get the smallest PA size of all CPUs (sanitized by cpufeature). We're 1768c2ecf20Sopenharmony_ci * not even pretending to support AArch32 here. Abort if the MMU outputs 1778c2ecf20Sopenharmony_ci * addresses larger than what we support. 1788c2ecf20Sopenharmony_ci */ 1798c2ecf20Sopenharmony_ci reg = read_sanitised_ftr_reg(SYS_ID_AA64MMFR0_EL1); 1808c2ecf20Sopenharmony_ci fld = cpuid_feature_extract_unsigned_field(reg, ID_AA64MMFR0_PARANGE_SHIFT); 1818c2ecf20Sopenharmony_ci oas = id_aa64mmfr0_parange_to_phys_shift(fld); 1828c2ecf20Sopenharmony_ci if (smmu->oas < oas) 1838c2ecf20Sopenharmony_ci return false; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci /* We can support bigger ASIDs than the CPU, but not smaller */ 1868c2ecf20Sopenharmony_ci fld = cpuid_feature_extract_unsigned_field(reg, ID_AA64MMFR0_ASID_SHIFT); 1878c2ecf20Sopenharmony_ci asid_bits = fld ? 16 : 8; 1888c2ecf20Sopenharmony_ci if (smmu->asid_bits < asid_bits) 1898c2ecf20Sopenharmony_ci return false; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci /* 1928c2ecf20Sopenharmony_ci * See max_pinned_asids in arch/arm64/mm/context.c. The following is 1938c2ecf20Sopenharmony_ci * generally the maximum number of bindable processes. 1948c2ecf20Sopenharmony_ci */ 1958c2ecf20Sopenharmony_ci if (arm64_kernel_unmapped_at_el0()) 1968c2ecf20Sopenharmony_ci asid_bits--; 1978c2ecf20Sopenharmony_ci dev_dbg(smmu->dev, "%d shared contexts\n", (1 << asid_bits) - 1988c2ecf20Sopenharmony_ci num_possible_cpus() - 2); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci return true; 2018c2ecf20Sopenharmony_ci} 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cistatic bool arm_smmu_iopf_supported(struct arm_smmu_master *master) 2048c2ecf20Sopenharmony_ci{ 2058c2ecf20Sopenharmony_ci return false; 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_cibool arm_smmu_master_sva_supported(struct arm_smmu_master *master) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci if (!(master->smmu->features & ARM_SMMU_FEAT_SVA)) 2118c2ecf20Sopenharmony_ci return false; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci /* SSID and IOPF support are mandatory for the moment */ 2148c2ecf20Sopenharmony_ci return master->ssid_bits && arm_smmu_iopf_supported(master); 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_cibool arm_smmu_master_sva_enabled(struct arm_smmu_master *master) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci bool enabled; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci mutex_lock(&sva_lock); 2228c2ecf20Sopenharmony_ci enabled = master->sva_enabled; 2238c2ecf20Sopenharmony_ci mutex_unlock(&sva_lock); 2248c2ecf20Sopenharmony_ci return enabled; 2258c2ecf20Sopenharmony_ci} 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ciint arm_smmu_master_enable_sva(struct arm_smmu_master *master) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci mutex_lock(&sva_lock); 2308c2ecf20Sopenharmony_ci master->sva_enabled = true; 2318c2ecf20Sopenharmony_ci mutex_unlock(&sva_lock); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci return 0; 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ciint arm_smmu_master_disable_sva(struct arm_smmu_master *master) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci mutex_lock(&sva_lock); 2398c2ecf20Sopenharmony_ci if (!list_empty(&master->bonds)) { 2408c2ecf20Sopenharmony_ci dev_err(master->dev, "cannot disable SVA, device is bound\n"); 2418c2ecf20Sopenharmony_ci mutex_unlock(&sva_lock); 2428c2ecf20Sopenharmony_ci return -EBUSY; 2438c2ecf20Sopenharmony_ci } 2448c2ecf20Sopenharmony_ci master->sva_enabled = false; 2458c2ecf20Sopenharmony_ci mutex_unlock(&sva_lock); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci return 0; 2488c2ecf20Sopenharmony_ci} 249