Commit 278bd82c authored by Joerg Roedel's avatar Joerg Roedel
Browse files

Merge tag 'arm-smmu-updates' of...

Merge tag 'arm-smmu-updates' of git://git.kernel.org/pub/scm/linux/kernel/git/will/linux into arm/smmu

Arm SMMU updates for 6.10

- SMMUv2:
  * Support for fault debugging hardware on Qualcomm implementations
  * Re-land support for the ->domain_alloc_paging() callback

- SMMUv3:
  * Improve handling of MSI allocation failure
  * Drop support for the "disable_bypass" cmdline option
  * Major rework of the CD creation code, following on directly from the
    STE rework merged last time around.
  * Add unit tests for the new STE/CD manipulation logic
parents fec50db7 56e1a4cc
Loading
Loading
Loading
Loading
+69 −0
Original line number Diff line number Diff line
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iommu/qcom,tbu.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#

title: Qualcomm TBU (Translation Buffer Unit)

maintainers:
  - Georgi Djakov <quic_c_gdjako@quicinc.com>

description:
  The Qualcomm SMMU500 implementation consists of TCU and TBU. The TBU contains
  a Translation Lookaside Buffer (TLB) that caches page tables. TBUs provides
  debug features to trace and trigger debug transactions. There are multiple TBU
  instances with each client core.

properties:
  compatible:
    enum:
      - qcom,sc7280-tbu
      - qcom,sdm845-tbu

  reg:
    maxItems: 1

  clocks:
    maxItems: 1

  interconnects:
    maxItems: 1

  power-domains:
    maxItems: 1

  qcom,stream-id-range:
    description: |
      Phandle of a SMMU device and Stream ID range (address and size) that
      is assigned by the TBU
    $ref: /schemas/types.yaml#/definitions/phandle-array
    items:
      - items:
          - description: phandle of a smmu node
          - description: stream id base address
          - description: stream id size

required:
  - compatible
  - reg
  - qcom,stream-id-range

additionalProperties: false

examples:
  - |
    #include <dt-bindings/clock/qcom,gcc-sdm845.h>
    #include <dt-bindings/interconnect/qcom,icc.h>
    #include <dt-bindings/interconnect/qcom,sdm845.h>

    tbu@150e1000 {
        compatible = "qcom,sdm845-tbu";
        reg = <0x150e1000 0x1000>;
        clocks = <&gcc GCC_AGGRE_NOC_PCIE_TBU_CLK>;
        interconnects = <&system_noc MASTER_GNOC_SNOC QCOM_ICC_TAG_ACTIVE_ONLY
                         &config_noc SLAVE_IMEM_CFG QCOM_ICC_TAG_ACTIVE_ONLY>;
        power-domains = <&gcc HLOS1_VOTE_AGGRE_NOC_MMU_PCIE_TBU_GDSC>;
        qcom,stream-id-range = <&apps_smmu 0x1c00 0x400>;
    };
...
+20 −5
Original line number Diff line number Diff line
@@ -376,13 +376,17 @@ config ARM_SMMU_QCOM

config ARM_SMMU_QCOM_DEBUG
	bool "ARM SMMU QCOM implementation defined debug support"
	depends on ARM_SMMU_QCOM
	depends on ARM_SMMU_QCOM=y
	help
	  Support for implementation specific debug features in ARM SMMU
	  hardware found in QTI platforms.
	  hardware found in QTI platforms. This include support for
	  the Translation Buffer Units (TBU) that can be used to obtain
	  additional information when debugging memory management issues
	  like context faults.

	  Say Y here to enable debug for issues such as TLB sync timeouts
	  which requires implementation defined register dumps.
	  Say Y here to enable debug for issues such as context faults
	  or TLB sync timeouts which requires implementation defined
	  register dumps.

config ARM_SMMU_V3
	tristate "ARM Ltd. System MMU Version 3 (SMMUv3) Support"
@@ -397,9 +401,9 @@ config ARM_SMMU_V3
	  Say Y here if your system includes an IOMMU device implementing
	  the ARM SMMUv3 architecture.

if ARM_SMMU_V3
config ARM_SMMU_V3_SVA
	bool "Shared Virtual Addressing support for the ARM SMMUv3"
	depends on ARM_SMMU_V3
	select IOMMU_SVA
	select IOMMU_IOPF
	select MMU_NOTIFIER
@@ -410,6 +414,17 @@ config ARM_SMMU_V3_SVA
	  Say Y here if your system supports SVA extensions such as PCIe PASID
	  and PRI.

config ARM_SMMU_V3_KUNIT_TEST
	bool "KUnit tests for arm-smmu-v3 driver"  if !KUNIT_ALL_TESTS
	depends on KUNIT
	depends on ARM_SMMU_V3_SVA
	default KUNIT_ALL_TESTS
	help
	  Enable this option to unit-test arm-smmu-v3 driver functions.

	  If unsure, say N.
endif

config S390_IOMMU
	def_bool y if S390 && PCI
	depends on S390 && PCI
+1 −0
Original line number Diff line number Diff line
@@ -2,4 +2,5 @@
obj-$(CONFIG_ARM_SMMU_V3) += arm_smmu_v3.o
arm_smmu_v3-objs-y += arm-smmu-v3.o
arm_smmu_v3-objs-$(CONFIG_ARM_SMMU_V3_SVA) += arm-smmu-v3-sva.o
arm_smmu_v3-objs-$(CONFIG_ARM_SMMU_V3_KUNIT_TEST) += arm-smmu-v3-test.o
arm_smmu_v3-objs := $(arm_smmu_v3-objs-y)
+118 −49
Original line number Diff line number Diff line
@@ -8,6 +8,7 @@
#include <linux/mmu_notifier.h>
#include <linux/sched/mm.h>
#include <linux/slab.h>
#include <kunit/visibility.h>

#include "arm-smmu-v3.h"
#include "../../io-pgtable-arm.h"
@@ -34,21 +35,25 @@ struct arm_smmu_bond {

static DEFINE_MUTEX(sva_lock);

/*
 * Write the CD to the CD tables for all masters that this domain is attached
 * to. Note that this is only used to update existing CD entries in the target
 * CD table, for which it's assumed that arm_smmu_write_ctx_desc can't fail.
 */
static void arm_smmu_update_ctx_desc_devices(struct arm_smmu_domain *smmu_domain,
					   int ssid,
					   struct arm_smmu_ctx_desc *cd)
static void
arm_smmu_update_s1_domain_cd_entry(struct arm_smmu_domain *smmu_domain)
{
	struct arm_smmu_master *master;
	struct arm_smmu_cd target_cd;
	unsigned long flags;

	spin_lock_irqsave(&smmu_domain->devices_lock, flags);
	list_for_each_entry(master, &smmu_domain->devices, domain_head) {
		arm_smmu_write_ctx_desc(master, ssid, cd);
		struct arm_smmu_cd *cdptr;

		/* S1 domains only support RID attachment right now */
		cdptr = arm_smmu_get_cd_ptr(master, IOMMU_NO_PASID);
		if (WARN_ON(!cdptr))
			continue;

		arm_smmu_make_s1_cd(&target_cd, master, smmu_domain);
		arm_smmu_write_cd_entry(master, IOMMU_NO_PASID, cdptr,
					&target_cd);
	}
	spin_unlock_irqrestore(&smmu_domain->devices_lock, flags);
}
@@ -96,7 +101,7 @@ arm_smmu_share_asid(struct mm_struct *mm, u16 asid)
	 * be some overlap between use of both ASIDs, until we invalidate the
	 * TLB.
	 */
	arm_smmu_update_ctx_desc_devices(smmu_domain, IOMMU_NO_PASID, cd);
	arm_smmu_update_s1_domain_cd_entry(smmu_domain);

	/* Invalidate TLB entries previously associated with that context */
	arm_smmu_tlb_inv_asid(smmu, asid);
@@ -105,11 +110,86 @@ arm_smmu_share_asid(struct mm_struct *mm, u16 asid)
	return NULL;
}

static u64 page_size_to_cd(void)
{
	static_assert(PAGE_SIZE == SZ_4K || PAGE_SIZE == SZ_16K ||
		      PAGE_SIZE == SZ_64K);
	if (PAGE_SIZE == SZ_64K)
		return ARM_LPAE_TCR_TG0_64K;
	if (PAGE_SIZE == SZ_16K)
		return ARM_LPAE_TCR_TG0_16K;
	return ARM_LPAE_TCR_TG0_4K;
}

VISIBLE_IF_KUNIT
void arm_smmu_make_sva_cd(struct arm_smmu_cd *target,
			  struct arm_smmu_master *master, struct mm_struct *mm,
			  u16 asid)
{
	u64 par;

	memset(target, 0, sizeof(*target));

	par = cpuid_feature_extract_unsigned_field(
		read_sanitised_ftr_reg(SYS_ID_AA64MMFR0_EL1),
		ID_AA64MMFR0_EL1_PARANGE_SHIFT);

	target->data[0] = cpu_to_le64(
		CTXDESC_CD_0_TCR_EPD1 |
#ifdef __BIG_ENDIAN
		CTXDESC_CD_0_ENDI |
#endif
		CTXDESC_CD_0_V |
		FIELD_PREP(CTXDESC_CD_0_TCR_IPS, par) |
		CTXDESC_CD_0_AA64 |
		(master->stall_enabled ? CTXDESC_CD_0_S : 0) |
		CTXDESC_CD_0_R |
		CTXDESC_CD_0_A |
		CTXDESC_CD_0_ASET |
		FIELD_PREP(CTXDESC_CD_0_ASID, asid));

	/*
	 * If no MM is passed then this creates a SVA entry that faults
	 * everything. arm_smmu_write_cd_entry() can hitlessly go between these
	 * two entries types since TTB0 is ignored by HW when EPD0 is set.
	 */
	if (mm) {
		target->data[0] |= cpu_to_le64(
			FIELD_PREP(CTXDESC_CD_0_TCR_T0SZ,
				   64ULL - vabits_actual) |
			FIELD_PREP(CTXDESC_CD_0_TCR_TG0, page_size_to_cd()) |
			FIELD_PREP(CTXDESC_CD_0_TCR_IRGN0,
				   ARM_LPAE_TCR_RGN_WBWA) |
			FIELD_PREP(CTXDESC_CD_0_TCR_ORGN0,
				   ARM_LPAE_TCR_RGN_WBWA) |
			FIELD_PREP(CTXDESC_CD_0_TCR_SH0, ARM_LPAE_TCR_SH_IS));

		target->data[1] = cpu_to_le64(virt_to_phys(mm->pgd) &
					      CTXDESC_CD_1_TTB0_MASK);
	} else {
		target->data[0] |= cpu_to_le64(CTXDESC_CD_0_TCR_EPD0);

		/*
		 * Disable stall and immediately generate an abort if stall
		 * disable is permitted. This speeds up cleanup for an unclean
		 * exit if the device is still doing a lot of DMA.
		 */
		if (!(master->smmu->features & ARM_SMMU_FEAT_STALL_FORCE))
			target->data[0] &=
				cpu_to_le64(~(CTXDESC_CD_0_S | CTXDESC_CD_0_R));
	}

	/*
	 * MAIR value is pretty much constant and global, so we can just get it
	 * from the current CPU register
	 */
	target->data[3] = cpu_to_le64(read_sysreg(mair_el1));
}

static struct arm_smmu_ctx_desc *arm_smmu_alloc_shared_cd(struct mm_struct *mm)
{
	u16 asid;
	int err = 0;
	u64 tcr, par, reg;
	struct arm_smmu_ctx_desc *cd;
	struct arm_smmu_ctx_desc *ret = NULL;

@@ -143,39 +223,6 @@ static struct arm_smmu_ctx_desc *arm_smmu_alloc_shared_cd(struct mm_struct *mm)
	if (err)
		goto out_free_asid;

	tcr = FIELD_PREP(CTXDESC_CD_0_TCR_T0SZ, 64ULL - vabits_actual) |
	      FIELD_PREP(CTXDESC_CD_0_TCR_IRGN0, ARM_LPAE_TCR_RGN_WBWA) |
	      FIELD_PREP(CTXDESC_CD_0_TCR_ORGN0, ARM_LPAE_TCR_RGN_WBWA) |
	      FIELD_PREP(CTXDESC_CD_0_TCR_SH0, ARM_LPAE_TCR_SH_IS) |
	      CTXDESC_CD_0_TCR_EPD1 | CTXDESC_CD_0_AA64;

	switch (PAGE_SIZE) {
	case SZ_4K:
		tcr |= FIELD_PREP(CTXDESC_CD_0_TCR_TG0, ARM_LPAE_TCR_TG0_4K);
		break;
	case SZ_16K:
		tcr |= FIELD_PREP(CTXDESC_CD_0_TCR_TG0, ARM_LPAE_TCR_TG0_16K);
		break;
	case SZ_64K:
		tcr |= FIELD_PREP(CTXDESC_CD_0_TCR_TG0, ARM_LPAE_TCR_TG0_64K);
		break;
	default:
		WARN_ON(1);
		err = -EINVAL;
		goto out_free_asid;
	}

	reg = read_sanitised_ftr_reg(SYS_ID_AA64MMFR0_EL1);
	par = cpuid_feature_extract_unsigned_field(reg, ID_AA64MMFR0_EL1_PARANGE_SHIFT);
	tcr |= FIELD_PREP(CTXDESC_CD_0_TCR_IPS, par);

	cd->ttbr = virt_to_phys(mm->pgd);
	cd->tcr = tcr;
	/*
	 * MAIR value is pretty much constant and global, so we can just get it
	 * from the current CPU register
	 */
	cd->mair = read_sysreg(mair_el1);
	cd->asid = asid;
	cd->mm = mm;

@@ -253,6 +300,8 @@ static void arm_smmu_mm_release(struct mmu_notifier *mn, struct mm_struct *mm)
{
	struct arm_smmu_mmu_notifier *smmu_mn = mn_to_smmu(mn);
	struct arm_smmu_domain *smmu_domain = smmu_mn->domain;
	struct arm_smmu_master *master;
	unsigned long flags;

	mutex_lock(&sva_lock);
	if (smmu_mn->cleared) {
@@ -264,8 +313,19 @@ static void arm_smmu_mm_release(struct mmu_notifier *mn, struct mm_struct *mm)
	 * DMA may still be running. Keep the cd valid to avoid C_BAD_CD events,
	 * but disable translation.
	 */
	arm_smmu_update_ctx_desc_devices(smmu_domain, mm_get_enqcmd_pasid(mm),
					 &quiet_cd);
	spin_lock_irqsave(&smmu_domain->devices_lock, flags);
	list_for_each_entry(master, &smmu_domain->devices, domain_head) {
		struct arm_smmu_cd target;
		struct arm_smmu_cd *cdptr;

		cdptr = arm_smmu_get_cd_ptr(master, mm_get_enqcmd_pasid(mm));
		if (WARN_ON(!cdptr))
			continue;
		arm_smmu_make_sva_cd(&target, master, NULL, smmu_mn->cd->asid);
		arm_smmu_write_cd_entry(master, mm_get_enqcmd_pasid(mm), cdptr,
					&target);
	}
	spin_unlock_irqrestore(&smmu_domain->devices_lock, flags);

	arm_smmu_tlb_inv_asid(smmu_domain->smmu, smmu_mn->cd->asid);
	arm_smmu_atc_inv_domain(smmu_domain, mm_get_enqcmd_pasid(mm), 0, 0);
@@ -360,6 +420,8 @@ static int __arm_smmu_sva_bind(struct device *dev, ioasid_t pasid,
			       struct mm_struct *mm)
{
	int ret;
	struct arm_smmu_cd target;
	struct arm_smmu_cd *cdptr;
	struct arm_smmu_bond *bond;
	struct arm_smmu_master *master = dev_iommu_priv_get(dev);
	struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
@@ -386,9 +448,13 @@ static int __arm_smmu_sva_bind(struct device *dev, ioasid_t pasid,
		goto err_free_bond;
	}

	ret = arm_smmu_write_ctx_desc(master, pasid, bond->smmu_mn->cd);
	if (ret)
	cdptr = arm_smmu_alloc_cd_ptr(master, mm_get_enqcmd_pasid(mm));
	if (!cdptr) {
		ret = -ENOMEM;
		goto err_put_notifier;
	}
	arm_smmu_make_sva_cd(&target, master, mm, bond->smmu_mn->cd->asid);
	arm_smmu_write_cd_entry(master, pasid, cdptr, &target);

	list_add(&bond->list, &master->bonds);
	return 0;
@@ -546,7 +612,7 @@ void arm_smmu_sva_remove_dev_pasid(struct iommu_domain *domain,

	mutex_lock(&sva_lock);

	arm_smmu_write_ctx_desc(master, id, NULL);
	arm_smmu_clear_cd(master, id);

	list_for_each_entry(t, &master->bonds, list) {
		if (t->mm == mm) {
@@ -569,6 +635,9 @@ static int arm_smmu_sva_set_dev_pasid(struct iommu_domain *domain,
	int ret = 0;
	struct mm_struct *mm = domain->mm;

	if (mm_get_enqcmd_pasid(mm) != id)
		return -EINVAL;

	mutex_lock(&sva_lock);
	ret = __arm_smmu_sva_bind(dev, id, mm);
	mutex_unlock(&sva_lock);
+465 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading