]> Pileus Git - ~andy/linux/blobdiff - drivers/iommu/arm-smmu.c
iommu/arm-smmu: use VA_BITS to determine arm64 virtual address space
[~andy/linux] / drivers / iommu / arm-smmu.c
index 181c9ba929cdfa131123639862cdefb2587c8cf6..fa3371adea4fd2e3bd808471f9c9029b1bb5f8f0 100644 (file)
@@ -24,7 +24,7 @@
  *     - v7/v8 long-descriptor format
  *     - Non-secure access to the SMMU
  *     - 4k and 64k pages, with contiguous pte hints.
- *     - Up to 39-bit addressing
+ *     - Up to 42-bit addressing (dependent on VA_BITS)
  *     - Context fault reporting
  */
 
@@ -392,7 +392,7 @@ struct arm_smmu_domain {
        struct arm_smmu_cfg             root_cfg;
        phys_addr_t                     output_mask;
 
-       spinlock_t                      lock;
+       struct mutex                    lock;
 };
 
 static DEFINE_SPINLOCK(arm_smmu_devices_lock);
@@ -590,6 +590,9 @@ static irqreturn_t arm_smmu_context_fault(int irq, void *dev)
                ret = IRQ_HANDLED;
                resume = RESUME_RETRY;
        } else {
+               dev_err_ratelimited(smmu->dev,
+                   "Unhandled context fault: iova=0x%08lx, fsynr=0x%x, cb=%d\n",
+                   iova, fsynr, root_cfg->cbndx);
                ret = IRQ_NONE;
                resume = RESUME_TERMINATE;
        }
@@ -778,7 +781,7 @@ static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain)
 #ifdef __BIG_ENDIAN
        reg |= SCTLR_E;
 #endif
-       writel(reg, cb_base + ARM_SMMU_CB_SCTLR);
+       writel_relaxed(reg, cb_base + ARM_SMMU_CB_SCTLR);
 }
 
 static int arm_smmu_init_domain_context(struct iommu_domain *domain,
@@ -897,7 +900,7 @@ static int arm_smmu_domain_init(struct iommu_domain *domain)
                goto out_free_domain;
        smmu_domain->root_cfg.pgd = pgd;
 
-       spin_lock_init(&smmu_domain->lock);
+       mutex_init(&smmu_domain->lock);
        domain->priv = smmu_domain;
        return 0;
 
@@ -1134,7 +1137,7 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
         * Sanity check the domain. We don't currently support domains
         * that cross between different SMMU chains.
         */
-       spin_lock(&smmu_domain->lock);
+       mutex_lock(&smmu_domain->lock);
        if (!smmu_domain->leaf_smmu) {
                /* Now that we have a master, we can finalise the domain */
                ret = arm_smmu_init_domain_context(domain, dev);
@@ -1149,7 +1152,7 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
                        dev_name(device_smmu->dev));
                goto err_unlock;
        }
-       spin_unlock(&smmu_domain->lock);
+       mutex_unlock(&smmu_domain->lock);
 
        /* Looks ok, so add the device to the domain */
        master = find_smmu_master(smmu_domain->leaf_smmu, dev->of_node);
@@ -1159,7 +1162,7 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
        return arm_smmu_domain_add_master(smmu_domain, master);
 
 err_unlock:
-       spin_unlock(&smmu_domain->lock);
+       mutex_unlock(&smmu_domain->lock);
        return ret;
 }
 
@@ -1212,7 +1215,10 @@ static int arm_smmu_alloc_init_pte(struct arm_smmu_device *smmu, pmd_t *pmd,
 
                arm_smmu_flush_pgtable(smmu, page_address(table),
                                       ARM_SMMU_PTE_HWTABLE_SIZE);
-               pgtable_page_ctor(table);
+               if (!pgtable_page_ctor(table)) {
+                       __free_page(table);
+                       return -ENOMEM;
+               }
                pmd_populate(NULL, pmd, table);
                arm_smmu_flush_pgtable(smmu, pmd, sizeof(*pmd));
        }
@@ -1388,7 +1394,7 @@ static int arm_smmu_handle_mapping(struct arm_smmu_domain *smmu_domain,
        if (paddr & ~output_mask)
                return -ERANGE;
 
-       spin_lock(&smmu_domain->lock);
+       mutex_lock(&smmu_domain->lock);
        pgd += pgd_index(iova);
        end = iova + size;
        do {
@@ -1404,7 +1410,7 @@ static int arm_smmu_handle_mapping(struct arm_smmu_domain *smmu_domain,
        } while (pgd++, iova != end);
 
 out_unlock:
-       spin_unlock(&smmu_domain->lock);
+       mutex_unlock(&smmu_domain->lock);
 
        /* Ensure new page tables are visible to the hardware walker */
        if (smmu->features & ARM_SMMU_FEAT_COHERENT_WALK)
@@ -1417,9 +1423,8 @@ static int arm_smmu_map(struct iommu_domain *domain, unsigned long iova,
                        phys_addr_t paddr, size_t size, int flags)
 {
        struct arm_smmu_domain *smmu_domain = domain->priv;
-       struct arm_smmu_device *smmu = smmu_domain->leaf_smmu;
 
-       if (!smmu_domain || !smmu)
+       if (!smmu_domain)
                return -ENODEV;
 
        /* Check for silent address truncation up the SMMU chain. */
@@ -1443,44 +1448,34 @@ static size_t arm_smmu_unmap(struct iommu_domain *domain, unsigned long iova,
 static phys_addr_t arm_smmu_iova_to_phys(struct iommu_domain *domain,
                                         dma_addr_t iova)
 {
-       pgd_t *pgd;
-       pud_t *pud;
-       pmd_t *pmd;
-       pte_t *pte;
+       pgd_t *pgdp, pgd;
+       pud_t pud;
+       pmd_t pmd;
+       pte_t pte;
        struct arm_smmu_domain *smmu_domain = domain->priv;
        struct arm_smmu_cfg *root_cfg = &smmu_domain->root_cfg;
-       struct arm_smmu_device *smmu = root_cfg->smmu;
 
-       spin_lock(&smmu_domain->lock);
-       pgd = root_cfg->pgd;
-       if (!pgd)
-               goto err_unlock;
+       pgdp = root_cfg->pgd;
+       if (!pgdp)
+               return 0;
 
-       pgd += pgd_index(iova);
-       if (pgd_none_or_clear_bad(pgd))
-               goto err_unlock;
+       pgd = *(pgdp + pgd_index(iova));
+       if (pgd_none(pgd))
+               return 0;
 
-       pud = pud_offset(pgd, iova);
-       if (pud_none_or_clear_bad(pud))
-               goto err_unlock;
+       pud = *pud_offset(&pgd, iova);
+       if (pud_none(pud))
+               return 0;
 
-       pmd = pmd_offset(pud, iova);
-       if (pmd_none_or_clear_bad(pmd))
-               goto err_unlock;
+       pmd = *pmd_offset(&pud, iova);
+       if (pmd_none(pmd))
+               return 0;
 
-       pte = pmd_page_vaddr(*pmd) + pte_index(iova);
+       pte = *(pmd_page_vaddr(pmd) + pte_index(iova));
        if (pte_none(pte))
-               goto err_unlock;
-
-       spin_unlock(&smmu_domain->lock);
-       return __pfn_to_phys(pte_pfn(*pte)) | (iova & ~PAGE_MASK);
+               return 0;
 
-err_unlock:
-       spin_unlock(&smmu_domain->lock);
-       dev_warn(smmu->dev,
-                "invalid (corrupt?) page tables detected for iova 0x%llx\n",
-                (unsigned long long)iova);
-       return -EINVAL;
+       return __pfn_to_phys(pte_pfn(pte)) | (iova & ~PAGE_MASK);
 }
 
 static int arm_smmu_domain_has_cap(struct iommu_domain *domain,
@@ -1499,6 +1494,13 @@ static int arm_smmu_add_device(struct device *dev)
 {
        struct arm_smmu_device *child, *parent, *smmu;
        struct arm_smmu_master *master = NULL;
+       struct iommu_group *group;
+       int ret;
+
+       if (dev->archdata.iommu) {
+               dev_warn(dev, "IOMMU driver already assigned to device\n");
+               return -EINVAL;
+       }
 
        spin_lock(&arm_smmu_devices_lock);
        list_for_each_entry(parent, &arm_smmu_devices, list) {
@@ -1531,13 +1533,23 @@ static int arm_smmu_add_device(struct device *dev)
        if (!master)
                return -ENODEV;
 
+       group = iommu_group_alloc();
+       if (IS_ERR(group)) {
+               dev_err(dev, "Failed to allocate IOMMU group\n");
+               return PTR_ERR(group);
+       }
+
+       ret = iommu_group_add_device(group, dev);
+       iommu_group_put(group);
        dev->archdata.iommu = smmu;
-       return 0;
+
+       return ret;
 }
 
 static void arm_smmu_remove_device(struct device *dev)
 {
        dev->archdata.iommu = NULL;
+       iommu_group_remove_device(dev);
 }
 
 static struct iommu_ops arm_smmu_ops = {
@@ -1559,9 +1571,13 @@ static struct iommu_ops arm_smmu_ops = {
 static void arm_smmu_device_reset(struct arm_smmu_device *smmu)
 {
        void __iomem *gr0_base = ARM_SMMU_GR0(smmu);
-       void __iomem *sctlr_base = ARM_SMMU_CB_BASE(smmu) + ARM_SMMU_CB_SCTLR;
+       void __iomem *cb_base;
        int i = 0;
-       u32 scr0 = readl_relaxed(gr0_base + ARM_SMMU_GR0_sCR0);
+       u32 reg;
+
+       /* Clear Global FSR */
+       reg = readl_relaxed(gr0_base + ARM_SMMU_GR0_sGFSR);
+       writel(reg, gr0_base + ARM_SMMU_GR0_sGFSR);
 
        /* Mark all SMRn as invalid and all S2CRn as bypass */
        for (i = 0; i < smmu->num_mapping_groups; ++i) {
@@ -1569,33 +1585,38 @@ static void arm_smmu_device_reset(struct arm_smmu_device *smmu)
                writel_relaxed(S2CR_TYPE_BYPASS, gr0_base + ARM_SMMU_GR0_S2CR(i));
        }
 
-       /* Make sure all context banks are disabled */
-       for (i = 0; i < smmu->num_context_banks; ++i)
-               writel_relaxed(0, sctlr_base + ARM_SMMU_CB(smmu, i));
+       /* Make sure all context banks are disabled and clear CB_FSR  */
+       for (i = 0; i < smmu->num_context_banks; ++i) {
+               cb_base = ARM_SMMU_CB_BASE(smmu) + ARM_SMMU_CB(smmu, i);
+               writel_relaxed(0, cb_base + ARM_SMMU_CB_SCTLR);
+               writel_relaxed(FSR_FAULT, cb_base + ARM_SMMU_CB_FSR);
+       }
 
        /* Invalidate the TLB, just in case */
        writel_relaxed(0, gr0_base + ARM_SMMU_GR0_STLBIALL);
        writel_relaxed(0, gr0_base + ARM_SMMU_GR0_TLBIALLH);
        writel_relaxed(0, gr0_base + ARM_SMMU_GR0_TLBIALLNSNH);
 
+       reg = readl_relaxed(gr0_base + ARM_SMMU_GR0_sCR0);
+
        /* Enable fault reporting */
-       scr0 |= (sCR0_GFRE | sCR0_GFIE | sCR0_GCFGFRE | sCR0_GCFGFIE);
+       reg |= (sCR0_GFRE | sCR0_GFIE | sCR0_GCFGFRE | sCR0_GCFGFIE);
 
        /* Disable TLB broadcasting. */
-       scr0 |= (sCR0_VMIDPNE | sCR0_PTM);
+       reg |= (sCR0_VMIDPNE | sCR0_PTM);
 
        /* Enable client access, but bypass when no mapping is found */
-       scr0 &= ~(sCR0_CLIENTPD | sCR0_USFCFG);
+       reg &= ~(sCR0_CLIENTPD | sCR0_USFCFG);
 
        /* Disable forced broadcasting */
-       scr0 &= ~sCR0_FB;
+       reg &= ~sCR0_FB;
 
        /* Don't upgrade barriers */
-       scr0 &= ~(sCR0_BSU_MASK << sCR0_BSU_SHIFT);
+       reg &= ~(sCR0_BSU_MASK << sCR0_BSU_SHIFT);
 
        /* Push the button */
        arm_smmu_tlb_sync(smmu);
-       writel(scr0, gr0_base + ARM_SMMU_GR0_sCR0);
+       writel_relaxed(reg, gr0_base + ARM_SMMU_GR0_sCR0);
 }
 
 static int arm_smmu_id_size_to_bits(int size)
@@ -1700,13 +1721,12 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
        id = readl_relaxed(gr0_base + ARM_SMMU_GR0_ID1);
        smmu->pagesize = (id & ID1_PAGESIZE) ? SZ_64K : SZ_4K;
 
-       /* Check that we ioremapped enough */
+       /* Check for size mismatch of SMMU address space from mapped region */
        size = 1 << (((id >> ID1_NUMPAGENDXB_SHIFT) & ID1_NUMPAGENDXB_MASK) + 1);
        size *= (smmu->pagesize << 1);
-       if (smmu->size < size)
-               dev_warn(smmu->dev,
-                        "device is 0x%lx bytes but only mapped 0x%lx!\n",
-                        size, smmu->size);
+       if (smmu->size != size)
+               dev_warn(smmu->dev, "SMMU address space size (0x%lx) differs "
+                       "from mapped region size (0x%lx)!\n", size, smmu->size);
 
        smmu->num_s2_context_banks = (id >> ID1_NUMS2CB_SHIFT) &
                                      ID1_NUMS2CB_MASK;
@@ -1727,7 +1747,6 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
         * allocation (PTRS_PER_PGD).
         */
 #ifdef CONFIG_64BIT
-       /* Current maximum output size of 39 bits */
        smmu->s1_output_size = min(39UL, size);
 #else
        smmu->s1_output_size = min(32UL, size);
@@ -1742,7 +1761,7 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
        } else {
 #ifdef CONFIG_64BIT
                size = (id >> ID2_UBS_SHIFT) & ID2_UBS_MASK;
-               size = min(39, arm_smmu_id_size_to_bits(size));
+               size = min(VA_BITS, arm_smmu_id_size_to_bits(size));
 #else
                size = 32;
 #endif
@@ -1781,15 +1800,10 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
        smmu->dev = dev;
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!res) {
-               dev_err(dev, "missing base address/size\n");
-               return -ENODEV;
-       }
-
+       smmu->base = devm_ioremap_resource(dev, res);
+       if (IS_ERR(smmu->base))
+               return PTR_ERR(smmu->base);
        smmu->size = resource_size(res);
-       smmu->base = devm_request_and_ioremap(dev, res);
-       if (!smmu->base)
-               return -EADDRNOTAVAIL;
 
        if (of_property_read_u32(dev->of_node, "#global-interrupts",
                                 &smmu->num_global_irqs)) {
@@ -1804,12 +1818,11 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
                        smmu->num_context_irqs++;
        }
 
-       if (num_irqs < smmu->num_global_irqs) {
-               dev_warn(dev, "found %d interrupts but expected at least %d\n",
-                        num_irqs, smmu->num_global_irqs);
-               smmu->num_global_irqs = num_irqs;
+       if (!smmu->num_context_irqs) {
+               dev_err(dev, "found %d interrupts but expected at least %d\n",
+                       num_irqs, smmu->num_global_irqs + 1);
+               return -ENODEV;
        }
-       smmu->num_context_irqs = num_irqs - smmu->num_global_irqs;
 
        smmu->irqs = devm_kzalloc(dev, sizeof(*smmu->irqs) * num_irqs,
                                  GFP_KERNEL);
@@ -1855,6 +1868,7 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
                dev_err(dev,
                        "found only %d context interrupt(s) but %d required\n",
                        smmu->num_context_irqs, smmu->num_context_banks);
+               err = -ENODEV;
                goto out_put_parent;
        }
 
@@ -1933,7 +1947,7 @@ static int arm_smmu_device_remove(struct platform_device *pdev)
                free_irq(smmu->irqs[i], smmu);
 
        /* Turn the thing off */
-       writel(sCR0_CLIENTPD, ARM_SMMU_GR0(smmu) + ARM_SMMU_GR0_sCR0);
+       writel_relaxed(sCR0_CLIENTPD, ARM_SMMU_GR0(smmu) + ARM_SMMU_GR0_sCR0);
        return 0;
 }
 
@@ -1981,7 +1995,7 @@ static void __exit arm_smmu_exit(void)
        return platform_driver_unregister(&arm_smmu_driver);
 }
 
-module_init(arm_smmu_init);
+subsys_initcall(arm_smmu_init);
 module_exit(arm_smmu_exit);
 
 MODULE_DESCRIPTION("IOMMU API for ARM architected SMMU implementations");