drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-sva.c

Source file repositories/reference/linux-study-clean/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-sva.c

File Facts

System
Linux kernel
Corpus path
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-sva.c
Extension
.c
Size
10591 bytes
Lines
359
Domain
Driver Families
Bucket
drivers/iommu
Inferred role
Driver Families: exported/initcall integration point
Status
integration implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

// SPDX-License-Identifier: GPL-2.0
/*
 * Implementation of the IOMMU SVA API for the ARM SMMUv3
 */

#include <linux/mm.h>
#include <linux/mmu_context.h>
#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"

static void __maybe_unused
arm_smmu_update_s1_domain_cd_entry(struct arm_smmu_domain *smmu_domain)
{
	struct arm_smmu_master_domain *master_domain;
	struct arm_smmu_cd target_cd;
	unsigned long flags;

	spin_lock_irqsave(&smmu_domain->devices_lock, flags);
	list_for_each_entry(master_domain, &smmu_domain->devices, devices_elm) {
		struct arm_smmu_master *master = master_domain->master;
		struct arm_smmu_cd *cdptr;

		cdptr = arm_smmu_get_cd_ptr(master, master_domain->ssid);
		if (WARN_ON(!cdptr))
			continue;

		arm_smmu_make_s1_cd(&target_cd, master, smmu_domain);
		arm_smmu_write_cd_entry(master, master_domain->ssid, cdptr,
					&target_cd);
	}
	spin_unlock_irqrestore(&smmu_domain->devices_lock, flags);
}

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) |

Annotation

Implementation Notes