Commit 81648517 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'iommu-fixes-v6.15-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/iommu/linux

Pull iommu fixes from Joerg Roedel:
 "ARM-SMMU fixes:
   - Fix broken detection of the S2FWB feature
   - Ensure page-size bitmap is initialised for SVA domains
   - Fix handling of SMMU client devices with duplicate Stream IDs
   - Don't fail SMMU probe if Stream IDs are aliased across clients

  Intel VT-d fixes:
   - Add quirk for IGFX device
   - Revert an ATS change to fix a boot failure

  AMD IOMMU:
   - Fix potential buffer overflow

  Core:
   - Fix for iommu_copy_struct_from_user()"

* tag 'iommu-fixes-v6.15-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/iommu/linux:
  iommu/vt-d: Apply quirk_iommu_igfx for 8086:0044 (QM57/QS57)
  iommu/vt-d: Revert ATS timing change to fix boot failure
  iommu: Fix two issues in iommu_copy_struct_from_user()
  iommu/amd: Fix potential buffer overflow in parse_ivrs_acpihid
  iommu/arm-smmu-v3: Fail aliasing StreamIDs more gracefully
  iommu/arm-smmu-v3: Fix iommu_device_probe bug due to duplicated stream ids
  iommu/arm-smmu-v3: Fix pgsize_bit for sva domains
  iommu/arm-smmu-v3: Add missing S2FWB feature detection
parents 9910affe 2c8a7c66
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -3664,6 +3664,14 @@ static int __init parse_ivrs_acpihid(char *str)
	while (*uid == '0' && *(uid + 1))
		uid++;

	if (strlen(hid) >= ACPIHID_HID_LEN) {
		pr_err("Invalid command line: hid is too long\n");
		return 1;
	} else if (strlen(uid) >= ACPIHID_UID_LEN) {
		pr_err("Invalid command line: uid is too long\n");
		return 1;
	}

	i = early_acpihid_map_size++;
	memcpy(early_acpihid_map[i].hid, hid, strlen(hid));
	memcpy(early_acpihid_map[i].uid, uid, strlen(uid));
+6 −0
Original line number Diff line number Diff line
@@ -411,6 +411,12 @@ struct iommu_domain *arm_smmu_sva_domain_alloc(struct device *dev,
		return ERR_CAST(smmu_domain);
	smmu_domain->domain.type = IOMMU_DOMAIN_SVA;
	smmu_domain->domain.ops = &arm_smmu_sva_domain_ops;

	/*
	 * Choose page_size as the leaf page size for invalidation when
	 * ARM_SMMU_FEAT_RANGE_INV is present
	 */
	smmu_domain->domain.pgsize_bitmap = PAGE_SIZE;
	smmu_domain->smmu = smmu;

	ret = xa_alloc(&arm_smmu_asid_xa, &asid, smmu_domain,
+18 −5
Original line number Diff line number Diff line
@@ -3388,6 +3388,7 @@ static int arm_smmu_insert_master(struct arm_smmu_device *smmu,
	mutex_lock(&smmu->streams_mutex);
	for (i = 0; i < fwspec->num_ids; i++) {
		struct arm_smmu_stream *new_stream = &master->streams[i];
		struct rb_node *existing;
		u32 sid = fwspec->ids[i];

		new_stream->id = sid;
@@ -3398,11 +3399,21 @@ static int arm_smmu_insert_master(struct arm_smmu_device *smmu,
			break;

		/* Insert into SID tree */
		if (rb_find_add(&new_stream->node, &smmu->streams,
				arm_smmu_streams_cmp_node)) {
			dev_warn(master->dev, "stream %u already in tree\n",
				 sid);
			ret = -EINVAL;
		existing = rb_find_add(&new_stream->node, &smmu->streams,
				       arm_smmu_streams_cmp_node);
		if (existing) {
			struct arm_smmu_master *existing_master =
				rb_entry(existing, struct arm_smmu_stream, node)
					->master;

			/* Bridged PCI devices may end up with duplicated IDs */
			if (existing_master == master)
				continue;

			dev_warn(master->dev,
				 "Aliasing StreamID 0x%x (from %s) unsupported, expect DMA to be broken\n",
				 sid, dev_name(existing_master->dev));
			ret = -ENODEV;
			break;
		}
	}
@@ -4429,6 +4440,8 @@ static int arm_smmu_device_hw_probe(struct arm_smmu_device *smmu)
	reg = readl_relaxed(smmu->base + ARM_SMMU_IDR3);
	if (FIELD_GET(IDR3_RIL, reg))
		smmu->features |= ARM_SMMU_FEAT_RANGE_INV;
	if (FIELD_GET(IDR3_FWB, reg))
		smmu->features |= ARM_SMMU_FEAT_S2FWB;

	/* IDR5 */
	reg = readl_relaxed(smmu->base + ARM_SMMU_IDR5);
+22 −13
Original line number Diff line number Diff line
@@ -3785,6 +3785,22 @@ static struct iommu_device *intel_iommu_probe_device(struct device *dev)

	intel_iommu_debugfs_create_dev(info);

	return &iommu->iommu;
free_table:
	intel_pasid_free_table(dev);
clear_rbtree:
	device_rbtree_remove(info);
free:
	kfree(info);

	return ERR_PTR(ret);
}

static void intel_iommu_probe_finalize(struct device *dev)
{
	struct device_domain_info *info = dev_iommu_priv_get(dev);
	struct intel_iommu *iommu = info->iommu;

	/*
	 * The PCIe spec, in its wisdom, declares that the behaviour of the
	 * device is undefined if you enable PASID support after ATS support.
@@ -3792,22 +3808,12 @@ static struct iommu_device *intel_iommu_probe_device(struct device *dev)
	 * we can't yet know if we're ever going to use it.
	 */
	if (info->pasid_supported &&
	    !pci_enable_pasid(pdev, info->pasid_supported & ~1))
	    !pci_enable_pasid(to_pci_dev(dev), info->pasid_supported & ~1))
		info->pasid_enabled = 1;

	if (sm_supported(iommu))
	if (sm_supported(iommu) && !dev_is_real_dma_subdevice(dev))
		iommu_enable_pci_ats(info);
	iommu_enable_pci_pri(info);

	return &iommu->iommu;
free_table:
	intel_pasid_free_table(dev);
clear_rbtree:
	device_rbtree_remove(info);
free:
	kfree(info);

	return ERR_PTR(ret);
}

static void intel_iommu_release_device(struct device *dev)
@@ -4391,6 +4397,7 @@ const struct iommu_ops intel_iommu_ops = {
	.domain_alloc_sva	= intel_svm_domain_alloc,
	.domain_alloc_nested	= intel_iommu_domain_alloc_nested,
	.probe_device		= intel_iommu_probe_device,
	.probe_finalize		= intel_iommu_probe_finalize,
	.release_device		= intel_iommu_release_device,
	.get_resv_regions	= intel_iommu_get_resv_regions,
	.device_group		= intel_iommu_device_group,
@@ -4432,6 +4439,9 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2e30, quirk_iommu_igfx);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2e40, quirk_iommu_igfx);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2e90, quirk_iommu_igfx);

/* QM57/QS57 integrated gfx malfunctions with dmar */
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x0044, quirk_iommu_igfx);

/* Broadwell igfx malfunctions with dmar */
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1606, quirk_iommu_igfx);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x160B, quirk_iommu_igfx);
@@ -4509,7 +4519,6 @@ static void quirk_calpella_no_shadow_gtt(struct pci_dev *dev)
	}
}
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x0040, quirk_calpella_no_shadow_gtt);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x0044, quirk_calpella_no_shadow_gtt);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x0062, quirk_calpella_no_shadow_gtt);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x006a, quirk_calpella_no_shadow_gtt);

+4 −4
Original line number Diff line number Diff line
@@ -440,10 +440,10 @@ static inline int __iommu_copy_struct_from_user(
	void *dst_data, const struct iommu_user_data *src_data,
	unsigned int data_type, size_t data_len, size_t min_len)
{
	if (src_data->type != data_type)
		return -EINVAL;
	if (WARN_ON(!dst_data || !src_data))
		return -EINVAL;
	if (src_data->type != data_type)
		return -EINVAL;
	if (src_data->len < min_len || data_len < src_data->len)
		return -EINVAL;
	return copy_struct_from_user(dst_data, data_len, src_data->uptr,
@@ -456,8 +456,8 @@ static inline int __iommu_copy_struct_from_user(
 *        include/uapi/linux/iommufd.h
 * @user_data: Pointer to a struct iommu_user_data for user space data info
 * @data_type: The data type of the @kdst. Must match with @user_data->type
 * @min_last: The last memember of the data structure @kdst points in the
 *            initial version.
 * @min_last: The last member of the data structure @kdst points in the initial
 *            version.
 * Return 0 for success, otherwise -error.
 */
#define iommu_copy_struct_from_user(kdst, user_data, data_type, min_last) \