Loading arch/arm64/include/asm/dma-iommu.h +2 −0 Original line number Diff line number Diff line Loading @@ -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; }; Loading arch/arm64/mm/dma-mapping.c +58 −6 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); Loading @@ -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); Loading Loading @@ -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); Loading drivers/iommu/Makefile +1 −1 Original line number Diff line number Diff line Loading @@ -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 Loading drivers/iommu/arm-smmu-errata.c 0 → 100644 +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; } drivers/iommu/arm-smmu.c +16 −0 Original line number Diff line number Diff line Loading @@ -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); Loading Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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 Loading
arch/arm64/include/asm/dma-iommu.h +2 −0 Original line number Diff line number Diff line Loading @@ -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; }; Loading
arch/arm64/mm/dma-mapping.c +58 −6 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); Loading @@ -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); Loading Loading @@ -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); Loading
drivers/iommu/Makefile +1 −1 Original line number Diff line number Diff line Loading @@ -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 Loading
drivers/iommu/arm-smmu-errata.c 0 → 100644 +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; }
drivers/iommu/arm-smmu.c +16 −0 Original line number Diff line number Diff line Loading @@ -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); Loading Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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