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

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

Merge "iommu: Support MMU500 Deep Prefetch errata workaround"

parents cc1618fb 23301488
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -23,6 +23,8 @@ struct dma_iommu_mapping {
	void			*bitmap;
	size_t			bits;
	dma_addr_t		base;
	u32			min_iova_align;
	struct page		*guard_page;

	struct dma_fast_smmu_mapping *fast;
};
+58 −6
Original line number Diff line number Diff line
@@ -37,7 +37,8 @@
#include <asm/dma-iommu.h>
#include <linux/dma-mapping-fast.h>
#include <linux/msm_dma_iommu_mapping.h>

#include <linux/arm-smmu-errata.h>
#include <soc/qcom/secure_buffer.h>


static int swiotlb __ro_after_init;
@@ -1165,15 +1166,24 @@ static void __dma_clear_buffer(struct page *page, size_t size,
static inline dma_addr_t __alloc_iova(struct dma_iommu_mapping *mapping,
				      size_t size)
{
	unsigned int order = get_order(size);
	unsigned int order;
	unsigned int align = 0;
	unsigned int count, start;
	unsigned long flags;
	dma_addr_t iova;
	size_t guard_len;

	size = PAGE_ALIGN(size);
	if (mapping->min_iova_align)
		guard_len = ALIGN(size, mapping->min_iova_align) - size;
	else
		guard_len = 0;

	order = get_order(size + guard_len);
	if (order > CONFIG_ARM64_DMA_IOMMU_ALIGNMENT)
		order = CONFIG_ARM64_DMA_IOMMU_ALIGNMENT;

	count = PAGE_ALIGN(size) >> PAGE_SHIFT;
	count = PAGE_ALIGN(size + guard_len) >> PAGE_SHIFT;
	align = (1 << order) - 1;

	spin_lock_irqsave(&mapping->lock, flags);
@@ -1187,16 +1197,41 @@ static inline dma_addr_t __alloc_iova(struct dma_iommu_mapping *mapping,
	bitmap_set(mapping->bitmap, start, count);
	spin_unlock_irqrestore(&mapping->lock, flags);

	return mapping->base + (start << PAGE_SHIFT);
	iova = mapping->base + (start << PAGE_SHIFT);

	if (guard_len &&
		iommu_map(mapping->domain, iova + size,
			page_to_phys(mapping->guard_page),
			guard_len, ARM_SMMU_GUARD_PROT)) {

		spin_lock_irqsave(&mapping->lock, flags);
		bitmap_clear(mapping->bitmap, start, count);
		spin_unlock_irqrestore(&mapping->lock, flags);
		return DMA_ERROR_CODE;
	}

	return iova;
}

static inline void __free_iova(struct dma_iommu_mapping *mapping,
			       dma_addr_t addr, size_t size)
{
	unsigned int start = (addr - mapping->base) >> PAGE_SHIFT;
	unsigned int count = size >> PAGE_SHIFT;
	unsigned int start;
	unsigned int count;
	unsigned long flags;
	size_t guard_len;

	addr = addr & PAGE_MASK;
	size = PAGE_ALIGN(size);
	if (mapping->min_iova_align)
		guard_len = ALIGN(size, mapping->min_iova_align) - size;
	else
		guard_len = 0;

	iommu_unmap(mapping->domain, addr + size, guard_len);

	start = (addr - mapping->base) >> PAGE_SHIFT;
	count = (size + guard_len) >> PAGE_SHIFT;
	spin_lock_irqsave(&mapping->lock, flags);
	bitmap_clear(mapping->bitmap, start, count);
	spin_unlock_irqrestore(&mapping->lock, flags);
@@ -1942,6 +1977,23 @@ static int
bitmap_iommu_init_mapping(struct device *dev, struct dma_iommu_mapping *mapping)
{
	unsigned int bitmap_size = BITS_TO_LONGS(mapping->bits) * sizeof(long);
	int vmid = VMID_HLOS;
	bool min_iova_align = 0;

	iommu_domain_get_attr(mapping->domain,
			DOMAIN_ATTR_QCOM_MMU500_ERRATA_MIN_ALIGN,
			&min_iova_align);
	iommu_domain_get_attr(mapping->domain,
			DOMAIN_ATTR_SECURE_VMID, &vmid);
	if (vmid >= VMID_LAST || vmid < 0)
		vmid = VMID_HLOS;

	if (min_iova_align) {
		mapping->min_iova_align = ARM_SMMU_MIN_IOVA_ALIGN;
		mapping->guard_page = arm_smmu_errata_get_guard_page(vmid);
		if (!mapping->guard_page)
			return -ENOMEM;
	}

	mapping->bitmap = kzalloc(bitmap_size, GFP_KERNEL | __GFP_NOWARN |
							__GFP_NORETRY);
+1 −1
Original line number Diff line number Diff line
@@ -13,7 +13,7 @@ obj-$(CONFIG_IOMMU_DEBUG) += iommu-debug.o
obj-$(CONFIG_MSM_IOMMU) += msm_iommu.o
obj-$(CONFIG_AMD_IOMMU) += amd_iommu.o amd_iommu_init.o
obj-$(CONFIG_AMD_IOMMU_V2) += amd_iommu_v2.o
obj-$(CONFIG_ARM_SMMU) += arm-smmu.o
obj-$(CONFIG_ARM_SMMU) += arm-smmu.o arm-smmu-errata.o
obj-$(CONFIG_ARM_SMMU_V3) += arm-smmu-v3.o
obj-$(CONFIG_DMAR_TABLE) += dmar.o
obj-$(CONFIG_INTEL_IOMMU) += intel-iommu.o
+53 −0
Original line number Diff line number Diff line
/*
 * Copyright (c) 2017, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
 * only version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 */

#include <linux/kernel.h>
#include <soc/qcom/secure_buffer.h>
#include <linux/arm-smmu-errata.h>

static struct page *guard_pages[VMID_LAST];
static DEFINE_MUTEX(guard_page_lock);

struct page *arm_smmu_errata_get_guard_page(int vmid)
{
	struct page *page;
	int ret;
	int source_vm = VMID_HLOS;
	int dest_vm = vmid;
	int dest_perm = PERM_READ | PERM_WRITE | PERM_EXEC;
	size_t size = ARM_SMMU_MIN_IOVA_ALIGN;

	mutex_lock(&guard_page_lock);
	page = guard_pages[vmid];
	if (page)
		goto out;

	page = alloc_pages(GFP_KERNEL, get_order(size));
	if (!page)
		goto out;

	if (vmid != VMID_HLOS) {
		ret = hyp_assign_phys(page_to_phys(page), PAGE_ALIGN(size),
				&source_vm, 1,
				&dest_vm, &dest_perm, 1);
		if (ret) {
			__free_pages(page, get_order(size));
			page = NULL;
		}
	}
	guard_pages[vmid] = page;
out:
	mutex_unlock(&guard_page_lock);
	return page;
}
+16 −0
Original line number Diff line number Diff line
@@ -536,6 +536,7 @@ struct arm_smmu_domain {

	bool				qsmmuv500_errata1_init;
	bool				qsmmuv500_errata1_client;
	bool				qsmmuv500_errata2_min_align;
};

static DEFINE_SPINLOCK(arm_smmu_devices_lock);
@@ -2898,6 +2899,10 @@ static int arm_smmu_domain_get_attr(struct iommu_domain *domain,
			& (1 << DOMAIN_ATTR_CB_STALL_DISABLE));
		ret = 0;
		break;
	case DOMAIN_ATTR_QCOM_MMU500_ERRATA_MIN_ALIGN:
		*((int *)data) = smmu_domain->qsmmuv500_errata2_min_align;
		ret = 0;
		break;
	default:
		ret = -ENODEV;
		break;
@@ -4395,6 +4400,9 @@ IOMMU_OF_DECLARE(cavium_smmuv2, "cavium,smmu-v2", arm_smmu_of_init);

#define TBU_DBG_TIMEOUT_US		30000

#define QSMMUV500_ACTLR_DEEP_PREFETCH_MASK	0x3
#define QSMMUV500_ACTLR_DEEP_PREFETCH_SHIFT	0x8


struct actlr_setting {
	struct arm_smmu_smr smr;
@@ -4872,6 +4880,14 @@ static void qsmmuv500_init_cb(struct arm_smmu_domain *smmu_domain,

	writel_relaxed(iommudata->actlr, cb_base + ARM_SMMU_CB_ACTLR);

	/*
	 * Prefetch only works properly if the start and end of all
	 * buffers in the page table are aligned to 16 Kb.
	 */
	if ((iommudata->actlr >> QSMMUV500_ACTLR_DEEP_PREFETCH_SHIFT) &&
			QSMMUV500_ACTLR_DEEP_PREFETCH_MASK)
		smmu_domain->qsmmuv500_errata2_min_align = true;

	/*
	 * Flush the context bank after modifying ACTLR to ensure there
	 * are no cache entries with stale state
Loading