Commit b4ddfa73 authored by Eric Auger's avatar Eric Auger Committed by Zheng Zengkai
Browse files

iommu/smmuv3: Nested mode single MSI doorbell per domain enforcement

virt inclusion
category: feature
bugzilla: https://gitee.com/openeuler/kernel/issues/I401IF


CVE: NA

------------------------------

In nested mode we enforce the rule that all devices belonging
to the same iommu_domain share the same msi_domain.

Indeed if there were several physical MSI doorbells being used
within a single iommu_domain, it becomes really difficult to
resolve the nested stage mapping translating into the correct
physical doorbell. So let's forbid this situation.

Signed-off-by: default avatarEric Auger <eric.auger@redhat.com>
Signed-off-by: default avatarKunkun <Jiang&lt;jiangkunkun@huawei.com>
Reviewed-by: default avatarKeqian Zhu <zhukeqian1@huawei.com>
Signed-off-by: default avatarZheng Zengkai <zhengzengkai@huawei.com>
parent 15700dc0
Loading
Loading
Loading
Loading
+41 −0
Original line number Diff line number Diff line
@@ -2672,6 +2672,37 @@ static void arm_smmu_detach_dev(struct arm_smmu_master *master)
	arm_smmu_install_ste_for_dev(master);
}

static bool arm_smmu_share_msi_domain(struct iommu_domain *domain,
				      struct device *dev)
{
	struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
	struct irq_domain *irqd = dev_get_msi_domain(dev);
	struct arm_smmu_master *master;
	unsigned long flags;
	bool share = false;

	if (!irqd)
		return true;

	spin_lock_irqsave(&smmu_domain->devices_lock, flags);
	list_for_each_entry(master, &smmu_domain->devices, domain_head) {
		struct irq_domain *d = dev_get_msi_domain(master->dev);

		if (!d)
			continue;
		if (irqd != d) {
			dev_info(dev, "Nested mode forbids to attach devices "
				 "using different physical MSI doorbells "
				 "to the same iommu_domain");
			goto unlock;
		}
	}
	share = true;
unlock:
	spin_unlock_irqrestore(&smmu_domain->devices_lock, flags);
	return share;
}

static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
{
	int ret = 0;
@@ -2733,6 +2764,16 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
		ret = -EINVAL;
		goto out_unlock;
	}
	/*
	 * In nested mode we must check all devices belonging to the
	 * domain share the same physical MSI doorbell. Otherwise nested
	 * stage MSI binding is not supported.
	 */
	if (smmu_domain->stage == ARM_SMMU_DOMAIN_NESTED &&
		!arm_smmu_share_msi_domain(domain, dev)) {
		ret = -EINVAL;
		goto out_unlock;
	}

	master->domain = smmu_domain;