Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5d9adef9 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "iommu/arm-smmu: Add support for page table donation"

parents e0e448f2 2c8583e7
Loading
Loading
Loading
Loading
+85 −1
Original line number Diff line number Diff line
@@ -44,6 +44,7 @@

#include <linux/amba/bus.h>
#include <soc/qcom/msm_tz_smmu.h>
#include <soc/qcom/secure_buffer.h>
#include <linux/msm_pcie.h>
#include <asm/cacheflush.h>

@@ -407,6 +408,11 @@ enum arm_smmu_domain_stage {
	ARM_SMMU_DOMAIN_NESTED,
};

struct arm_smmu_pte_info {
	phys_addr_t phys_addr;
	struct list_head entry;
};

struct arm_smmu_domain {
	struct arm_smmu_device		*smmu;
	struct io_pgtable_ops		*pgtbl_ops;
@@ -417,6 +423,8 @@ struct arm_smmu_domain {
	struct mutex			lock;
	struct mutex			init_mutex; /* Protects smmu pointer */
	u32				attributes;
	u32				secure_vmid;
	struct list_head		pte_info_list;
};

static struct iommu_ops arm_smmu_ops;
@@ -911,11 +919,16 @@ static void arm_smmu_flush_pgtable(void *addr, size_t size, void *cookie)
	}
}

static void arm_smmu_prepare_pgtable(void *addr, void *cookie);
static void arm_smmu_unprepare_pgtable(void *cookie, void *addr, size_t size);

static struct iommu_gather_ops arm_smmu_gather_ops = {
	.tlb_flush_all	= arm_smmu_tlb_inv_context,
	.tlb_add_flush	= arm_smmu_tlb_inv_range_nosync,
	.tlb_sync	= arm_smmu_tlb_sync,
	.flush_pgtable	= arm_smmu_flush_pgtable,
	.prepare_pgtable = arm_smmu_prepare_pgtable,
	.unprepare_pgtable = arm_smmu_unprepare_pgtable,
};

static phys_addr_t arm_smmu_iova_to_phys(struct iommu_domain *domain,
@@ -1319,6 +1332,8 @@ static int arm_smmu_domain_init(struct iommu_domain *domain)
	if (!smmu_domain)
		return -ENOMEM;

	smmu_domain->secure_vmid = VMID_INVAL;
	INIT_LIST_HEAD(&smmu_domain->pte_info_list);
	mutex_init(&smmu_domain->lock);
	mutex_init(&smmu_domain->init_mutex);
	spin_lock_init(&smmu_domain->pgtbl_lock);
@@ -1613,6 +1628,63 @@ static void arm_smmu_detach_dev(struct iommu_domain *domain, struct device *dev)
	mutex_unlock(&smmu_domain->init_mutex);
}

static void arm_smmu_assign_table(struct arm_smmu_domain *smmu_domain)
{
	int ret;
	int dest_vmids[2] = {VMID_HLOS, smmu_domain->secure_vmid};
	int dest_perms[2] = {PERM_READ | PERM_WRITE, PERM_READ};
	int source_vmid = VMID_HLOS;
	struct arm_smmu_pte_info *pte_info, *temp;

	if (smmu_domain->secure_vmid == VMID_INVAL)
		return;

	list_for_each_entry(pte_info, &smmu_domain->pte_info_list,
								entry) {
		ret = hyp_assign_phys(pte_info->phys_addr, PAGE_SIZE,
				&source_vmid, 1, dest_vmids, dest_perms, 2);
		if (WARN_ON(ret))
			break;
	}

	list_for_each_entry_safe(pte_info, temp, &smmu_domain->pte_info_list,
								entry) {
		list_del(&pte_info->entry);
		kfree(pte_info);
	}
}

static void arm_smmu_unprepare_pgtable(void *cookie, void *addr, size_t size)
{
	struct arm_smmu_domain *smmu_domain = cookie;
	int ret;
	int dest_vmids = VMID_HLOS;
	int dest_perms = PERM_READ | PERM_WRITE;
	int source_vmlist[2] = {VMID_HLOS, smmu_domain->secure_vmid};

	if (smmu_domain->secure_vmid == VMID_INVAL)
		return;

	ret = hyp_assign_phys((phys_addr_t)virt_to_phys(addr), size,
			source_vmlist, 2, &dest_vmids, &dest_perms, 1);
	WARN_ON(ret);
}

static void arm_smmu_prepare_pgtable(void *addr, void *cookie)
{
	struct arm_smmu_domain *smmu_domain = cookie;
	struct arm_smmu_pte_info *pte_info;

	if (smmu_domain->secure_vmid == VMID_INVAL)
		return;

	pte_info = kzalloc(sizeof(struct arm_smmu_pte_info), GFP_ATOMIC);
	if (!pte_info)
		return;
	pte_info->phys_addr = (phys_addr_t)virt_to_phys(addr);
	list_add_tail(&pte_info->entry, &smmu_domain->pte_info_list);
}

static int arm_smmu_map(struct iommu_domain *domain, unsigned long iova,
			phys_addr_t paddr, size_t size, int prot)
{
@@ -1628,6 +1700,8 @@ static int arm_smmu_map(struct iommu_domain *domain, unsigned long iova,
	ret = ops->map(ops, iova, paddr, size, prot);
	spin_unlock_irqrestore(&smmu_domain->pgtbl_lock, flags);

	arm_smmu_assign_table(smmu_domain);

	return ret;
}

@@ -1646,6 +1720,8 @@ static size_t arm_smmu_map_sg(struct iommu_domain *domain, unsigned long iova,
	ret = ops->map_sg(ops, iova, sg, nents, prot);
	spin_unlock_irqrestore(&smmu_domain->pgtbl_lock, flags);

	arm_smmu_assign_table(smmu_domain);

	return ret;
}

@@ -1686,6 +1762,13 @@ static size_t arm_smmu_unmap(struct iommu_domain *domain, unsigned long iova,
	ret = ops->unmap(ops, iova, size);
	spin_unlock_irqrestore(&smmu_domain->pgtbl_lock, flags);

	/*
	 * While splitting up block mappings, we might allocate page table
	 * memory during unmap, so the vmids needs to be assigned to the
	 * memory here as well.
	 */
	arm_smmu_assign_table(smmu_domain);

	if (atomic_ctx) {
		arm_smmu_disable_clocks_atomic(smmu_domain->smmu);
	} else {
@@ -1865,7 +1948,8 @@ static int arm_smmu_domain_set_attr(struct iommu_domain *domain,
		break;
	}
	case DOMAIN_ATTR_SECURE_VMID:
		ret = 0;
		BUG_ON(smmu_domain->secure_vmid != VMID_INVAL);
		smmu_domain->secure_vmid = *((int *)data);
		break;
	case DOMAIN_ATTR_ATOMIC:
	{
+5 −2
Original line number Diff line number Diff line
@@ -261,6 +261,7 @@ static int __arm_lpae_map(struct arm_lpae_io_pgtable *data, unsigned long iova,
			pte |= ARM_LPAE_PTE_NSTABLE;
		*ptep = pte;
		data->iop.cfg.tlb->flush_pgtable(ptep, sizeof(*ptep), cookie);
		data->iop.cfg.tlb->prepare_pgtable(cptep, cookie);
	} else {
		cptep = iopte_deref(pte, data);
	}
@@ -412,6 +413,8 @@ static void __arm_lpae_free_pgtable(struct arm_lpae_io_pgtable *data, int lvl,
		__arm_lpae_free_pgtable(data, lvl + 1, iopte_deref(pte, data));
	}

	data->iop.cfg.tlb->unprepare_pgtable(data->iop.cookie, start,
								table_size);
	free_pages_exact(start, table_size);
}

@@ -745,7 +748,7 @@ arm_64_lpae_alloc_pgtable_s1(struct io_pgtable_cfg *cfg, void *cookie)
		goto out_free_data;

	cfg->tlb->flush_pgtable(data->pgd, data->pgd_size, cookie);

	cfg->tlb->prepare_pgtable(data->pgd, cookie);
	/* TTBRs */
	cfg->arm_lpae_s1_cfg.ttbr[0] = virt_to_phys(data->pgd);
	cfg->arm_lpae_s1_cfg.ttbr[1] = 0;
@@ -833,7 +836,7 @@ arm_64_lpae_alloc_pgtable_s2(struct io_pgtable_cfg *cfg, void *cookie)
		goto out_free_data;

	cfg->tlb->flush_pgtable(data->pgd, data->pgd_size, cookie);

	cfg->tlb->prepare_pgtable(data->pgd, cookie);
	/* VTTBR */
	cfg->arm_lpae_s2_cfg.vttbr = virt_to_phys(data->pgd);
	return &data->iop;
+4 −0
Original line number Diff line number Diff line
@@ -21,6 +21,8 @@ enum io_pgtable_fmt {
 * @tlb_add_flush: Queue up a TLB invalidation for a virtual address range.
 * @tlb_sync:      Ensure any queue TLB invalidation has taken effect.
 * @flush_pgtable: Ensure page table updates are visible to the IOMMU.
 * @prepare_pgtable: Do necessary fixup for newly allocated page table memory
 * @unprepare_pgtable: Undo fixups done during @prepare_pgtable
 *
 * Note that these can all be called in atomic context and must therefore
 * not block.
@@ -31,6 +33,8 @@ struct iommu_gather_ops {
			      void *cookie);
	void (*tlb_sync)(void *cookie);
	void (*flush_pgtable)(void *ptr, size_t size, void *cookie);
	void (*prepare_pgtable)(void *addr, void *cookie);
	void (*unprepare_pgtable)(void *cookie, void *addr, size_t size);
};

/**
+4 −7
Original line number Diff line number Diff line
@@ -318,12 +318,11 @@ err1:
	return ret;
}

int hyp_assign_phys(phys_addr_t addr, u64 size,
			int *dest_vmids, int *dest_perms,
			int dest_nelems)
int hyp_assign_phys(phys_addr_t addr, u64 size, u32 *source_vm_list,
			int source_nelems, int *dest_vmids,
			int *dest_perms, int dest_nelems)
{
	struct sg_table *table;
	u32 source_vm;
	int ret;

	table = kzalloc(sizeof(struct sg_table), GFP_KERNEL);
@@ -335,9 +334,7 @@ int hyp_assign_phys(phys_addr_t addr, u64 size,

	sg_set_page(table->sgl, phys_to_page(addr), size, 0);

	source_vm = VMID_HLOS;

	ret = hyp_assign_table(table, &source_vm, 1, dest_vmids,
	ret = hyp_assign_table(table, source_vm_list, source_nelems, dest_vmids,
						dest_perms, dest_nelems);
	if (ret)
		goto err2;
+4 −4
Original line number Diff line number Diff line
@@ -38,8 +38,8 @@ int hyp_assign_table(struct sg_table *table,
			int *dest_vmids, int *dest_perms,
			int dest_nelems);
int hyp_assign_phys(phys_addr_t addr, u64 size,
			int *dest_vmids, int *dest_perms,
			int dest_nelems);
			u32 *source_vmlist, int source_nelems,
			int *dest_vmids, int *dest_perms, int dest_nelems);
bool msm_secure_v2_is_supported(void);
#else
static inline int msm_secure_table(struct sg_table *table)
@@ -58,8 +58,8 @@ static inline int hyp_assign_table(struct sg_table *table,
	return -ENOSYS;
}
static inline int hyp_assign_phys(phys_addr_t addr, u64 size,
			int *dest_vmids, int *dest_perms,
			int dest_nelems)
			u32 *source_vmlist, int source_nelems,
			int *dest_vmids, int *dest_perms, int dest_nelems)
{
	return -ENOSYS;
}