iommu/arm-smmu-v3: Propagate ssid_bits
Now that we support substream IDs, initialize s1cdmax with the number of SSID bits supported by a master and the SMMU. Context descriptor tables are allocated once for the first master attached to a domain. Therefore attaching multiple devices with different SSID sizes is tricky, and we currently don't support it. As a future improvement it would be nice to at least support attaching a SSID-capable device to a domain that isn't using SSID, by reallocating the SSID table. This would allow supporting a SSID-capable device that is in the same IOMMU group as a bridge, for example. Varying SSID size is less of a concern, since the PCIe specification "highly recommends" that devices supporting PASID implement all 20 bits of it. Tested-by: Zhangfei Gao <zhangfei.gao@linaro.org> Reviewed-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com> Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org> Signed-off-by: Will Deacon <will@kernel.org>
This commit is contained in:
parent
87f42391f6
commit
2505ec6f85
@ -2250,6 +2250,7 @@ static void arm_smmu_domain_free(struct iommu_domain *domain)
|
||||
}
|
||||
|
||||
static int arm_smmu_domain_finalise_s1(struct arm_smmu_domain *smmu_domain,
|
||||
struct arm_smmu_master *master,
|
||||
struct io_pgtable_cfg *pgtbl_cfg)
|
||||
{
|
||||
int ret;
|
||||
@ -2262,6 +2263,8 @@ static int arm_smmu_domain_finalise_s1(struct arm_smmu_domain *smmu_domain,
|
||||
if (asid < 0)
|
||||
return asid;
|
||||
|
||||
cfg->s1cdmax = master->ssid_bits;
|
||||
|
||||
ret = arm_smmu_alloc_cd_tables(smmu_domain);
|
||||
if (ret)
|
||||
goto out_free_asid;
|
||||
@ -2284,6 +2287,7 @@ out_free_asid:
|
||||
}
|
||||
|
||||
static int arm_smmu_domain_finalise_s2(struct arm_smmu_domain *smmu_domain,
|
||||
struct arm_smmu_master *master,
|
||||
struct io_pgtable_cfg *pgtbl_cfg)
|
||||
{
|
||||
int vmid;
|
||||
@ -2308,7 +2312,8 @@ static int arm_smmu_domain_finalise_s2(struct arm_smmu_domain *smmu_domain,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int arm_smmu_domain_finalise(struct iommu_domain *domain)
|
||||
static int arm_smmu_domain_finalise(struct iommu_domain *domain,
|
||||
struct arm_smmu_master *master)
|
||||
{
|
||||
int ret;
|
||||
unsigned long ias, oas;
|
||||
@ -2316,6 +2321,7 @@ static int arm_smmu_domain_finalise(struct iommu_domain *domain)
|
||||
struct io_pgtable_cfg pgtbl_cfg;
|
||||
struct io_pgtable_ops *pgtbl_ops;
|
||||
int (*finalise_stage_fn)(struct arm_smmu_domain *,
|
||||
struct arm_smmu_master *,
|
||||
struct io_pgtable_cfg *);
|
||||
struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
|
||||
struct arm_smmu_device *smmu = smmu_domain->smmu;
|
||||
@ -2370,7 +2376,7 @@ static int arm_smmu_domain_finalise(struct iommu_domain *domain)
|
||||
domain->geometry.aperture_end = (1UL << pgtbl_cfg.ias) - 1;
|
||||
domain->geometry.force_aperture = true;
|
||||
|
||||
ret = finalise_stage_fn(smmu_domain, &pgtbl_cfg);
|
||||
ret = finalise_stage_fn(smmu_domain, master, &pgtbl_cfg);
|
||||
if (ret < 0) {
|
||||
free_io_pgtable_ops(pgtbl_ops);
|
||||
return ret;
|
||||
@ -2523,7 +2529,7 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
|
||||
|
||||
if (!smmu_domain->smmu) {
|
||||
smmu_domain->smmu = smmu;
|
||||
ret = arm_smmu_domain_finalise(domain);
|
||||
ret = arm_smmu_domain_finalise(domain, master);
|
||||
if (ret) {
|
||||
smmu_domain->smmu = NULL;
|
||||
goto out_unlock;
|
||||
@ -2535,6 +2541,13 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
|
||||
dev_name(smmu->dev));
|
||||
ret = -ENXIO;
|
||||
goto out_unlock;
|
||||
} else if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1 &&
|
||||
master->ssid_bits != smmu_domain->s1_cfg.s1cdmax) {
|
||||
dev_err(dev,
|
||||
"cannot attach to incompatible domain (%u SSID bits != %u)\n",
|
||||
smmu_domain->s1_cfg.s1cdmax, master->ssid_bits);
|
||||
ret = -EINVAL;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
master->domain = smmu_domain;
|
||||
|
Loading…
Reference in New Issue
Block a user