]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blobdiff - drivers/iommu/arm-smmu-v3.c
Merge branches 'arm/exynos', 'arm/omap', 'arm/rockchip', 'arm/mediatek', 'arm/smmu...
[mirror_ubuntu-artful-kernel.git] / drivers / iommu / arm-smmu-v3.c
index 56401e6d2e68c7108983f7f0ba82b351629f6052..380969aa60d5a6070765d4eaae3532c1b3324a8c 100644 (file)
@@ -554,9 +554,14 @@ struct arm_smmu_s2_cfg {
 };
 
 struct arm_smmu_strtab_ent {
-       bool                            valid;
-
-       bool                            bypass; /* Overrides s1/s2 config */
+       /*
+        * An STE is "assigned" if the master emitting the corresponding SID
+        * is attached to a domain. The behaviour of an unassigned STE is
+        * determined by the disable_bypass parameter, whereas an assigned
+        * STE behaves according to s1_cfg/s2_cfg, which themselves are
+        * configured according to the domain type.
+        */
+       bool                            assigned;
        struct arm_smmu_s1_cfg          *s1_cfg;
        struct arm_smmu_s2_cfg          *s2_cfg;
 };
@@ -632,6 +637,7 @@ enum arm_smmu_domain_stage {
        ARM_SMMU_DOMAIN_S1 = 0,
        ARM_SMMU_DOMAIN_S2,
        ARM_SMMU_DOMAIN_NESTED,
+       ARM_SMMU_DOMAIN_BYPASS,
 };
 
 struct arm_smmu_domain {
@@ -1005,9 +1011,9 @@ static void arm_smmu_write_strtab_ent(struct arm_smmu_device *smmu, u32 sid,
         * This is hideously complicated, but we only really care about
         * three cases at the moment:
         *
-        * 1. Invalid (all zero) -> bypass  (init)
-        * 2. Bypass -> translation (attach)
-        * 3. Translation -> bypass (detach)
+        * 1. Invalid (all zero) -> bypass/fault (init)
+        * 2. Bypass/fault -> translation/bypass (attach)
+        * 3. Translation/bypass -> bypass/fault (detach)
         *
         * Given that we can't update the STE atomically and the SMMU
         * doesn't read the thing in a defined order, that leaves us
@@ -1046,11 +1052,15 @@ static void arm_smmu_write_strtab_ent(struct arm_smmu_device *smmu, u32 sid,
        }
 
        /* Nuke the existing STE_0 value, as we're going to rewrite it */
-       val = ste->valid ? STRTAB_STE_0_V : 0;
+       val = STRTAB_STE_0_V;
+
+       /* Bypass/fault */
+       if (!ste->assigned || !(ste->s1_cfg || ste->s2_cfg)) {
+               if (!ste->assigned && disable_bypass)
+                       val |= STRTAB_STE_0_CFG_ABORT;
+               else
+                       val |= STRTAB_STE_0_CFG_BYPASS;
 
-       if (ste->bypass) {
-               val |= disable_bypass ? STRTAB_STE_0_CFG_ABORT
-                                     : STRTAB_STE_0_CFG_BYPASS;
                dst[0] = cpu_to_le64(val);
                dst[1] = cpu_to_le64(STRTAB_STE_1_SHCFG_INCOMING
                         << STRTAB_STE_1_SHCFG_SHIFT);
@@ -1111,10 +1121,7 @@ static void arm_smmu_write_strtab_ent(struct arm_smmu_device *smmu, u32 sid,
 static void arm_smmu_init_bypass_stes(u64 *strtab, unsigned int nent)
 {
        unsigned int i;
-       struct arm_smmu_strtab_ent ste = {
-               .valid  = true,
-               .bypass = true,
-       };
+       struct arm_smmu_strtab_ent ste = { .assigned = false };
 
        for (i = 0; i < nent; ++i) {
                arm_smmu_write_strtab_ent(NULL, -1, strtab, &ste);
@@ -1378,7 +1385,9 @@ static struct iommu_domain *arm_smmu_domain_alloc(unsigned type)
 {
        struct arm_smmu_domain *smmu_domain;
 
-       if (type != IOMMU_DOMAIN_UNMANAGED && type != IOMMU_DOMAIN_DMA)
+       if (type != IOMMU_DOMAIN_UNMANAGED &&
+           type != IOMMU_DOMAIN_DMA &&
+           type != IOMMU_DOMAIN_IDENTITY)
                return NULL;
 
        /*
@@ -1509,6 +1518,11 @@ static int arm_smmu_domain_finalise(struct iommu_domain *domain)
        struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
        struct arm_smmu_device *smmu = smmu_domain->smmu;
 
+       if (domain->type == IOMMU_DOMAIN_IDENTITY) {
+               smmu_domain->stage = ARM_SMMU_DOMAIN_BYPASS;
+               return 0;
+       }
+
        /* Restrict the stage to what we can actually support */
        if (!(smmu->features & ARM_SMMU_FEAT_TRANS_S1))
                smmu_domain->stage = ARM_SMMU_DOMAIN_S2;
@@ -1579,7 +1593,7 @@ static __le64 *arm_smmu_get_step_for_sid(struct arm_smmu_device *smmu, u32 sid)
        return step;
 }
 
-static int arm_smmu_install_ste_for_dev(struct iommu_fwspec *fwspec)
+static void arm_smmu_install_ste_for_dev(struct iommu_fwspec *fwspec)
 {
        int i;
        struct arm_smmu_master_data *master = fwspec->iommu_priv;
@@ -1591,17 +1605,14 @@ static int arm_smmu_install_ste_for_dev(struct iommu_fwspec *fwspec)
 
                arm_smmu_write_strtab_ent(smmu, sid, step, &master->ste);
        }
-
-       return 0;
 }
 
 static void arm_smmu_detach_dev(struct device *dev)
 {
        struct arm_smmu_master_data *master = dev->iommu_fwspec->iommu_priv;
 
-       master->ste.bypass = true;
-       if (arm_smmu_install_ste_for_dev(dev->iommu_fwspec) < 0)
-               dev_warn(dev, "failed to install bypass STE\n");
+       master->ste.assigned = false;
+       arm_smmu_install_ste_for_dev(dev->iommu_fwspec);
 }
 
 static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
@@ -1620,7 +1631,7 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
        ste = &master->ste;
 
        /* Already attached to a different domain? */
-       if (!ste->bypass)
+       if (ste->assigned)
                arm_smmu_detach_dev(dev);
 
        mutex_lock(&smmu_domain->init_mutex);
@@ -1641,10 +1652,12 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
                goto out_unlock;
        }
 
-       ste->bypass = false;
-       ste->valid = true;
+       ste->assigned = true;
 
-       if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1) {
+       if (smmu_domain->stage == ARM_SMMU_DOMAIN_BYPASS) {
+               ste->s1_cfg = NULL;
+               ste->s2_cfg = NULL;
+       } else if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1) {
                ste->s1_cfg = &smmu_domain->s1_cfg;
                ste->s2_cfg = NULL;
                arm_smmu_write_ctx_desc(smmu, ste->s1_cfg);
@@ -1653,10 +1666,7 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
                ste->s2_cfg = &smmu_domain->s2_cfg;
        }
 
-       ret = arm_smmu_install_ste_for_dev(dev->iommu_fwspec);
-       if (ret < 0)
-               ste->valid = false;
-
+       arm_smmu_install_ste_for_dev(dev->iommu_fwspec);
 out_unlock:
        mutex_unlock(&smmu_domain->init_mutex);
        return ret;
@@ -1704,6 +1714,9 @@ arm_smmu_iova_to_phys(struct iommu_domain *domain, dma_addr_t iova)
        struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
        struct io_pgtable_ops *ops = smmu_domain->pgtbl_ops;
 
+       if (domain->type == IOMMU_DOMAIN_IDENTITY)
+               return iova;
+
        if (!ops)
                return 0;
 
@@ -1807,7 +1820,7 @@ static void arm_smmu_remove_device(struct device *dev)
 
        master = fwspec->iommu_priv;
        smmu = master->smmu;
-       if (master && master->ste.valid)
+       if (master && master->ste.assigned)
                arm_smmu_detach_dev(dev);
        iommu_group_remove_device(dev);
        iommu_device_unlink(&smmu->iommu, dev);
@@ -1837,6 +1850,9 @@ static int arm_smmu_domain_get_attr(struct iommu_domain *domain,
 {
        struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
 
+       if (domain->type != IOMMU_DOMAIN_UNMANAGED)
+               return -EINVAL;
+
        switch (attr) {
        case DOMAIN_ATTR_NESTING:
                *(int *)data = (smmu_domain->stage == ARM_SMMU_DOMAIN_NESTED);
@@ -1852,6 +1868,9 @@ static int arm_smmu_domain_set_attr(struct iommu_domain *domain,
        int ret = 0;
        struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
 
+       if (domain->type != IOMMU_DOMAIN_UNMANAGED)
+               return -EINVAL;
+
        mutex_lock(&smmu_domain->init_mutex);
 
        switch (attr) {