Commit f3b273b7 authored by Jason Gunthorpe's avatar Jason Gunthorpe Committed by Will Deacon
Browse files

iommu/arm-smmu-v3: Allow setting a S1 domain to a PASID



The SVA cleanup made the SSID logic entirely general so all we need to do
is call it with the correct cd table entry for a S1 domain.

This is slightly tricky because of the ASID and how the locking works, the
simple fix is to just update the ASID once we get the right locks.

Tested-by: default avatarNicolin Chen <nicolinc@nvidia.com>
Tested-by: default avatarShameer Kolothum <shameerali.kolothum.thodi@huawei.com>
Reviewed-by: default avatarNicolin Chen <nicolinc@nvidia.com>
Reviewed-by: default avatarJerry Snitselaar <jsnitsel@redhat.com>
Signed-off-by: default avatarJason Gunthorpe <jgg@nvidia.com>
Link: https://lore.kernel.org/r/14-v9-5cd718286059+79186-smmuv3_newapi_p2b_jgg@nvidia.com


Signed-off-by: default avatarWill Deacon <will@kernel.org>
parent 8ee9175c
Loading
Loading
Loading
Loading
+40 −1
Original line number Diff line number Diff line
@@ -2798,6 +2798,36 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
	return 0;
}

static int arm_smmu_s1_set_dev_pasid(struct iommu_domain *domain,
				      struct device *dev, ioasid_t id)
{
	struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
	struct arm_smmu_master *master = dev_iommu_priv_get(dev);
	struct arm_smmu_device *smmu = master->smmu;
	struct arm_smmu_cd target_cd;
	int ret = 0;

	mutex_lock(&smmu_domain->init_mutex);
	if (!smmu_domain->smmu)
		ret = arm_smmu_domain_finalise(smmu_domain, smmu);
	else if (smmu_domain->smmu != smmu)
		ret = -EINVAL;
	mutex_unlock(&smmu_domain->init_mutex);
	if (ret)
		return ret;

	if (smmu_domain->stage != ARM_SMMU_DOMAIN_S1)
		return -EINVAL;

	/*
	 * We can read cd.asid outside the lock because arm_smmu_set_pasid()
	 * will fix it
	 */
	arm_smmu_make_s1_cd(&target_cd, master, smmu_domain);
	return arm_smmu_set_pasid(master, to_smmu_domain(domain), id,
				  &target_cd);
}

static void arm_smmu_update_ste(struct arm_smmu_master *master,
				struct iommu_domain *sid_domain,
				bool ats_enabled)
@@ -2825,7 +2855,7 @@ static void arm_smmu_update_ste(struct arm_smmu_master *master,

int arm_smmu_set_pasid(struct arm_smmu_master *master,
		       struct arm_smmu_domain *smmu_domain, ioasid_t pasid,
		       const struct arm_smmu_cd *cd)
		       struct arm_smmu_cd *cd)
{
	struct iommu_domain *sid_domain = iommu_get_domain_for_dev(master->dev);
	struct arm_smmu_attach_state state = {
@@ -2858,6 +2888,14 @@ int arm_smmu_set_pasid(struct arm_smmu_master *master,
	if (ret)
		goto out_unlock;

	/*
	 * We don't want to obtain to the asid_lock too early, so fix up the
	 * caller set ASID under the lock in case it changed.
	 */
	cd->data[0] &= ~cpu_to_le64(CTXDESC_CD_0_ASID);
	cd->data[0] |= cpu_to_le64(
		FIELD_PREP(CTXDESC_CD_0_ASID, smmu_domain->cd.asid));

	arm_smmu_write_cd_entry(master, pasid, cdptr, cd);
	arm_smmu_update_ste(master, sid_domain, state.ats_enabled);

@@ -3376,6 +3414,7 @@ static struct iommu_ops arm_smmu_ops = {
	.owner			= THIS_MODULE,
	.default_domain_ops = &(const struct iommu_domain_ops) {
		.attach_dev		= arm_smmu_attach_dev,
		.set_dev_pasid		= arm_smmu_s1_set_dev_pasid,
		.map_pages		= arm_smmu_map_pages,
		.unmap_pages		= arm_smmu_unmap_pages,
		.flush_iotlb_all	= arm_smmu_flush_iotlb_all,
+1 −1
Original line number Diff line number Diff line
@@ -801,7 +801,7 @@ void arm_smmu_write_cd_entry(struct arm_smmu_master *master, int ssid,

int arm_smmu_set_pasid(struct arm_smmu_master *master,
		       struct arm_smmu_domain *smmu_domain, ioasid_t pasid,
		       const struct arm_smmu_cd *cd);
		       struct arm_smmu_cd *cd);

void arm_smmu_tlb_inv_asid(struct arm_smmu_device *smmu, u16 asid);
void arm_smmu_tlb_inv_range_asid(unsigned long iova, size_t size, int asid,