Skip to contentSkip to conversation

iommu/arm-smmu: Add support for TTBR1

RFC New Jordan Crouse 1 participant 1 message Sep 26, 2017

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

RFC 1/1 iommu/arm-smmu: Add support for TTBR1

(Reupping this from a few months back as requested for discussion)

Allow a domain to opt into allocating and maintaining a TTBR1 pagetable. The size of the TTBR1 region will be the same as the TTBR0 size with the sign extension bit set on the highest bit in the region.

By example, given a TTBR0/TTBR1 virtual address range of 36 bits the memory map will look like this:

TTBR0 [0x0000000000000000:0x00000007FFFFFFFF]
TTBR1 [0xFFFFFFF800000000:0xFFFFFFFFFFFFFFFF]

The map/unmap operations will automatically use the appropriate pagetable for the given iova.

---
 drivers/iommu/arm-smmu.c       |  40 +++++++++-
 drivers/iommu/io-pgtable-arm.c | 162 +++++++++++++++++++++++++++++++++++++----
 drivers/iommu/io-pgtable.h     |   5 ++
 include/linux/iommu.h          |   1 +
 4 files changed, 191 insertions(+), 17 deletions(-)
drivers/iommu/arm-smmu.c +38 -2
index bc89b4d..9a1f522 100644
@@ -389,6 +389,8 @@ struct arm_smmu_device {
 	struct arm_smmu_s2cr		*s2crs;
 	struct mutex			stream_map_mutex;
 
+	u32				ubs;
+
 	unsigned long			va_size;
 	unsigned long			ipa_size;
 	unsigned long			pa_size;
@@ -438,6 +440,7 @@ struct arm_smmu_domain {
 	struct mutex			init_mutex; /* Protects smmu pointer */
 	spinlock_t			cb_lock; /* Serialises ATS1* ops */
 	struct iommu_domain		domain;
+	u32				attributes;
 };
 
 struct arm_smmu_option_prop {
@@ -821,7 +824,6 @@ static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain,
 		} else {
 			reg = pgtbl_cfg->arm_lpae_s1_cfg.tcr;
 			reg2 = pgtbl_cfg->arm_lpae_s1_cfg.tcr >> 32;
-			reg2 |= TTBCR2_SEP_UPSTREAM;
 			if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH64)
 				reg2 |= TTBCR2_AS;
 		}
@@ -881,6 +883,7 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
 {
 	int irq, start, ret = 0;
 	unsigned long ias, oas;
+	int sep = 0;
 	struct io_pgtable_ops *pgtbl_ops;
 	struct io_pgtable_cfg pgtbl_cfg;
 	enum io_pgtable_fmt fmt;
@@ -955,6 +958,25 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
 		oas = smmu->ipa_size;
 		if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH64) {
 			fmt = ARM_64_LPAE_S1;
+
+			if (smmu_domain->attributes &
+				(1 << DOMAIN_ATTR_ENABLE_TTBR1)) {
+
+				/*
+				 * When the UBS id is 5 we know that the bus
+				 * size is 49 bits and that bit 48 is the fixed
+				 * sign extension bit.  For any other bus size
+				 * we need to specify the sign extension bit
+				 * and adjust the input size accordingly
+				 */
+
+				if (smmu->ubs == 5) {
+					sep = 48;
+				} else {
+					sep = ias - 1;
+					ias--;
+				}
+			}
 		} else if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH32_L) {
 			fmt = ARM_32_LPAE_S1;
 			ias = min(ias, 32UL);
@@ -1014,6 +1036,7 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
 		.pgsize_bitmap	= smmu->pgsize_bitmap,
 		.ias		= ias,
 		.oas		= oas,
+		.sep		= sep,
 		.tlb		= tlb_ops,
 		.iommu_dev	= smmu->dev,
 	};
@@ -1021,6 +1044,10 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
 	if (smmu->features & ARM_SMMU_FEAT_COHERENT_WALK)
 		pgtbl_cfg.quirks = IO_PGTABLE_QUIRK_NO_DMA;
 
+
+	if (smmu_domain->attributes & (1 << DOMAIN_ATTR_ENABLE_TTBR1))
+		pgtbl_cfg.quirks |= IO_PGTABLE_QUIRK_ARM_TTBR1;
+
 	smmu_domain->smmu = smmu;
 	pgtbl_ops = alloc_io_pgtable_ops(fmt, &pgtbl_cfg, smmu_domain);
 	if (!pgtbl_ops) {
@@ -1621,6 +1648,10 @@ static int arm_smmu_domain_get_attr(struct iommu_domain *domain,
 	case DOMAIN_ATTR_NESTING:
 		*(int *)data = (smmu_domain->stage == ARM_SMMU_DOMAIN_NESTED);
 		return 0;
+	case DOMAIN_ATTR_ENABLE_TTBR1:
+		*((int *)data) = !!(smmu_domain->attributes
+					& (1 << DOMAIN_ATTR_ENABLE_TTBR1));
+		return 0;
 	default:
 		return -ENODEV;
 	}
@@ -1650,6 +1681,11 @@ static int arm_smmu_domain_set_attr(struct iommu_domain *domain,
 			smmu_domain->stage = ARM_SMMU_DOMAIN_S1;
 
 		break;
+	case DOMAIN_ATTR_ENABLE_TTBR1:
+		if (*((int *)data))
+			smmu_domain->attributes |=
+				1 << DOMAIN_ATTR_ENABLE_TTBR1;
+		break;
 	default:
 		ret = -ENODEV;
 	}
@@ -1991,7 +2027,7 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
 		if (smmu->version == ARM_SMMU_V1_64K)
 			smmu->features |= ARM_SMMU_FEAT_FMT_AARCH64_64K;
 	} else {
-		size = (id >> ID2_UBS_SHIFT) & ID2_UBS_MASK;
+		smmu->ubs = (id >> ID2_UBS_SHIFT) & ID2_UBS_MASK;
 		smmu->va_size = arm_smmu_id_size_to_bits(size);
 		if (id & ID2_PTFS_4K)
 			smmu->features |= ARM_SMMU_FEAT_FMT_AARCH64_4K;
drivers/iommu/io-pgtable-arm.c +147 -15

269 lines not on this page · raw message

drivers/iommu/io-pgtable.h +5 -0
index 524263a..efcf39a 100644
@@ -70,16 +70,21 @@ struct io_pgtable_cfg {
 	 *	be accessed by a fully cache-coherent IOMMU or CPU (e.g. for a
 	 *	software-emulated IOMMU), such that pagetable updates need not
 	 *	be treated as explicit DMA data.
+	 *
+	 * IO_PGTABLE_QUIRK_ARM_TTBR1: Allocate and configure a supervisor
+	 *	(TTBR1) pagetable as well as the user (TTBR0) table
 	 */
 	#define IO_PGTABLE_QUIRK_ARM_NS		BIT(0)
 	#define IO_PGTABLE_QUIRK_NO_PERMS	BIT(1)
 	#define IO_PGTABLE_QUIRK_TLBI_ON_MAP	BIT(2)
 	#define IO_PGTABLE_QUIRK_ARM_MTK_4GB	BIT(3)
 	#define IO_PGTABLE_QUIRK_NO_DMA		BIT(4)
+	#define IO_PGTABLE_QUIRK_ARM_TTBR1	BIT(5)   /* Allocate TTBR1 PT */
 	unsigned long			quirks;
 	unsigned long			pgsize_bitmap;
 	unsigned int			ias;
 	unsigned int			oas;
+	int				sep;
 	const struct iommu_gather_ops	*tlb;
 	struct device			*iommu_dev;
 
include/linux/iommu.h +1 -0
index 2cb54ad..e91d054 100644
@@ -124,6 +124,7 @@ enum iommu_attr {
 	DOMAIN_ATTR_FSL_PAMU_ENABLE,
 	DOMAIN_ATTR_FSL_PAMUV1,
 	DOMAIN_ATTR_NESTING,	/* two stages of translation */
+	DOMAIN_ATTR_ENABLE_TTBR1,
 	DOMAIN_ATTR_MAX,
 };
 
Signature · 6 lines
1.9.1

_______________________________________________
Freedreno mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/freedreno