From c2e909ac17c220522d845b96913f951af86a8fec Mon Sep 17 00:00:00 2001 From: Prakash Gupta Date: Thu, 29 Mar 2018 11:23:06 +0530 Subject: [PATCH] iommu: add support for force guard page Add support for force guard page. This is added at the end of iova map range. Clients needs to set_attr DOMAIN_ATTR_FORCE_IOVA_GUARD_PAGE to achieve the same. Change-Id: I2b3d221b5f2331f35518612a87997d3acc873821 Signed-off-by: Prakash Gupta --- arch/arm64/include/asm/dma-iommu.h | 1 + arch/arm64/mm/dma-mapping.c | 36 ++++++++++++++++--------- drivers/iommu/arm-smmu.c | 29 +++++++++++++++++++++ drivers/iommu/dma-iommu.c | 39 ++++++++++++++++++--------- drivers/iommu/dma-mapping-fast.c | 42 ++++++++++++++++++++---------- include/linux/dma-mapping-fast.h | 3 ++- include/linux/iommu.h | 1 + 7 files changed, 112 insertions(+), 39 deletions(-) diff --git a/arch/arm64/include/asm/dma-iommu.h b/arch/arm64/include/asm/dma-iommu.h index cfd49b29978c..fefec0baf9d4 100644 --- a/arch/arm64/include/asm/dma-iommu.h +++ b/arch/arm64/include/asm/dma-iommu.h @@ -25,6 +25,7 @@ struct dma_iommu_mapping { dma_addr_t base; u32 min_iova_align; struct page *guard_page; + u32 force_guard_page_len; struct dma_fast_smmu_mapping *fast; }; diff --git a/arch/arm64/mm/dma-mapping.c b/arch/arm64/mm/dma-mapping.c index 31d468415cc3..42dde2d64166 100644 --- a/arch/arm64/mm/dma-mapping.c +++ b/arch/arm64/mm/dma-mapping.c @@ -1183,7 +1183,8 @@ static inline dma_addr_t __alloc_iova(struct dma_iommu_mapping *mapping, size = PAGE_ALIGN(size); if (mapping->min_iova_align) - guard_len = ALIGN(size, mapping->min_iova_align) - size; + guard_len = ALIGN(size + mapping->force_guard_page_len, + mapping->min_iova_align) - size; else guard_len = 0; @@ -1231,12 +1232,14 @@ static inline void __free_iova(struct dma_iommu_mapping *mapping, addr = addr & PAGE_MASK; size = PAGE_ALIGN(size); - if (mapping->min_iova_align) { - guard_len = ALIGN(size, mapping->min_iova_align) - size; - iommu_unmap(mapping->domain, addr + size, guard_len); - } else { + if (mapping->min_iova_align) + guard_len = ALIGN(size + mapping->force_guard_page_len, + mapping->min_iova_align) - size; + else guard_len = 0; - } + + if (guard_len) + iommu_unmap(mapping->domain, addr + size, guard_len); start = (addr - mapping->base) >> PAGE_SHIFT; count = (size + guard_len) >> PAGE_SHIFT; @@ -1987,21 +1990,30 @@ 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; int min_iova_align = 0; + int force_iova_guard_page = 0; iommu_domain_get_attr(mapping->domain, DOMAIN_ATTR_MMU500_ERRATA_MIN_ALIGN, &min_iova_align); iommu_domain_get_attr(mapping->domain, DOMAIN_ATTR_SECURE_VMID, &vmid); + iommu_domain_get_attr(mapping->domain, + DOMAIN_ATTR_FORCE_IOVA_GUARD_PAGE, + &force_iova_guard_page); + 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->min_iova_align = (min_iova_align) ? ARM_SMMU_MIN_IOVA_ALIGN : + PAGE_SIZE; + + if (force_iova_guard_page) + mapping->force_guard_page_len = PAGE_SIZE; + + 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); diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index 40d4a2c4a63e..3f203645b13f 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -542,6 +542,7 @@ struct arm_smmu_domain { bool qsmmuv500_errata1_init; bool qsmmuv500_errata1_client; bool qsmmuv500_errata2_min_align; + bool is_force_guard_page; }; static DEFINE_SPINLOCK(arm_smmu_devices_lock); @@ -3245,6 +3246,12 @@ static int arm_smmu_domain_get_attr(struct iommu_domain *domain, *((int *)data) = smmu_domain->qsmmuv500_errata2_min_align; ret = 0; break; + case DOMAIN_ATTR_FORCE_IOVA_GUARD_PAGE: + *((int *)data) = !!(smmu_domain->attributes + & (1 << DOMAIN_ATTR_FORCE_IOVA_GUARD_PAGE)); + ret = 0; + break; + default: ret = -ENODEV; break; @@ -3447,6 +3454,28 @@ static int arm_smmu_domain_set_attr(struct iommu_domain *domain, 1 << DOMAIN_ATTR_CB_STALL_DISABLE; ret = 0; break; + + case DOMAIN_ATTR_FORCE_IOVA_GUARD_PAGE: { + int force_iova_guard_page = *((int *)data); + + if (smmu_domain->smmu != NULL) { + dev_err(smmu_domain->smmu->dev, + "cannot change force guard page attribute while attached\n"); + ret = -EBUSY; + break; + } + + if (force_iova_guard_page) + smmu_domain->attributes |= + 1 << DOMAIN_ATTR_FORCE_IOVA_GUARD_PAGE; + else + smmu_domain->attributes &= + ~(1 << DOMAIN_ATTR_FORCE_IOVA_GUARD_PAGE); + + ret = 0; + break; + } + default: ret = -ENODEV; } diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c index 57ae0dd4b013..36c84df078c7 100644 --- a/drivers/iommu/dma-iommu.c +++ b/drivers/iommu/dma-iommu.c @@ -45,6 +45,7 @@ struct iommu_dma_cookie { spinlock_t msi_lock; u32 min_iova_align; struct page *guard_page; + u32 force_guard_page_len; }; static inline struct iova_domain *cookie_iovad(struct iommu_domain *domain) @@ -130,20 +131,31 @@ static int iommu_dma_arm_smmu_errata_init(struct iommu_domain *domain) struct iommu_dma_cookie *cookie = domain->iova_cookie; int vmid = VMID_HLOS; int min_iova_align = 0; + int force_iova_guard_page = 0; + iommu_domain_get_attr(domain, DOMAIN_ATTR_MMU500_ERRATA_MIN_ALIGN, &min_iova_align); iommu_domain_get_attr(domain, DOMAIN_ATTR_SECURE_VMID, &vmid); + iommu_domain_get_attr(domain, + DOMAIN_ATTR_FORCE_IOVA_GUARD_PAGE, + &force_iova_guard_page); + if (vmid >= VMID_LAST || vmid < 0) vmid = VMID_HLOS; - if (min_iova_align) { - cookie->min_iova_align = ARM_SMMU_MIN_IOVA_ALIGN; - cookie->guard_page = arm_smmu_errata_get_guard_page(vmid); - if (!cookie->guard_page) - return -ENOMEM; - } + cookie->min_iova_align = (min_iova_align) ? ARM_SMMU_MIN_IOVA_ALIGN : + PAGE_SIZE; + + if (force_iova_guard_page) + cookie->force_guard_page_len = PAGE_SIZE; + + cookie->guard_page = + arm_smmu_errata_get_guard_page(vmid); + if (!cookie->guard_page) + return -ENOMEM; + return 0; } @@ -244,7 +256,8 @@ static dma_addr_t iommu_dma_alloc_iova(struct iommu_domain *domain, dma_addr_t ret_iova; if (cookie->min_iova_align) - guard_len = ALIGN(size, cookie->min_iova_align) - size; + guard_len = ALIGN(size + cookie->force_guard_page_len, + cookie->min_iova_align) - size; else guard_len = 0; iova_len = (size + guard_len) >> shift; @@ -290,12 +303,14 @@ static void iommu_dma_free_iova(struct iommu_domain *domain, unsigned long shift = iova_shift(iovad); unsigned long guard_len; - if (cookie->min_iova_align) { - guard_len = ALIGN(size, cookie->min_iova_align) - size; - iommu_unmap(domain, iova + size, guard_len); - } else { + if (cookie->min_iova_align) + guard_len = ALIGN(size + cookie->force_guard_page_len, + cookie->min_iova_align) - size; + else guard_len = 0; - } + + if (guard_len) + iommu_unmap(domain, iova + size, guard_len); free_iova_fast(iovad, iova >> shift, (size + guard_len) >> shift); } diff --git a/drivers/iommu/dma-mapping-fast.c b/drivers/iommu/dma-mapping-fast.c index eac7b410a688..7e6287c986ef 100644 --- a/drivers/iommu/dma-mapping-fast.c +++ b/drivers/iommu/dma-mapping-fast.c @@ -163,7 +163,8 @@ static dma_addr_t __fast_smmu_alloc_iova(struct dma_fast_smmu_mapping *mapping, dma_addr_t iova; if (mapping->min_iova_align) - guard_len = ALIGN(size, mapping->min_iova_align) - size; + guard_len = ALIGN(size + mapping->force_guard_page_len, + mapping->min_iova_align) - size; else guard_len = 0; @@ -311,12 +312,15 @@ static void __fast_smmu_free_iova(struct dma_fast_smmu_mapping *mapping, unsigned long nbits; unsigned long guard_len; - if (mapping->min_iova_align) { - guard_len = ALIGN(size, mapping->min_iova_align) - size; - iommu_unmap(mapping->domain, iova + size, guard_len); - } else { + if (mapping->min_iova_align) + guard_len = ALIGN(size + mapping->force_guard_page_len, + mapping->min_iova_align) - size; + else guard_len = 0; - } + + if (guard_len) + iommu_unmap(mapping->domain, iova + size, guard_len); + nbits = (size + guard_len) >> FAST_PAGE_SHIFT; @@ -898,20 +902,30 @@ static int fast_smmu_errata_init(struct dma_iommu_mapping *mapping) struct dma_fast_smmu_mapping *fast = mapping->fast; int vmid = VMID_HLOS; int min_iova_align = 0; + int force_iova_guard_page = 0; iommu_domain_get_attr(mapping->domain, - DOMAIN_ATTR_MMU500_ERRATA_MIN_ALIGN, - &min_iova_align); + DOMAIN_ATTR_MMU500_ERRATA_MIN_ALIGN, + &min_iova_align); iommu_domain_get_attr(mapping->domain, DOMAIN_ATTR_SECURE_VMID, &vmid); + iommu_domain_get_attr(mapping->domain, + DOMAIN_ATTR_FORCE_IOVA_GUARD_PAGE, + &force_iova_guard_page); + if (vmid >= VMID_LAST || vmid < 0) vmid = VMID_HLOS; - if (min_iova_align) { - fast->min_iova_align = ARM_SMMU_MIN_IOVA_ALIGN; - fast->guard_page = arm_smmu_errata_get_guard_page(vmid); - if (!fast->guard_page) - return -ENOMEM; - } + fast->min_iova_align = (min_iova_align) ? ARM_SMMU_MIN_IOVA_ALIGN : + PAGE_SIZE; + + if (force_iova_guard_page) + fast->force_guard_page_len = PAGE_SIZE; + + fast->guard_page = + arm_smmu_errata_get_guard_page(vmid); + if (!fast->guard_page) + return -ENOMEM; + return 0; } diff --git a/include/linux/dma-mapping-fast.h b/include/linux/dma-mapping-fast.h index e9dabab33a48..e370b4355f75 100644 --- a/include/linux/dma-mapping-fast.h +++ b/include/linux/dma-mapping-fast.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2018, 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 @@ -27,6 +27,7 @@ struct dma_fast_smmu_mapping { u32 min_iova_align; struct page *guard_page; + u32 force_guard_page_len; unsigned int bitmap_size; unsigned long *bitmap; diff --git a/include/linux/iommu.h b/include/linux/iommu.h index f25acfc93248..acbc605d509b 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -148,6 +148,7 @@ enum iommu_attr { DOMAIN_ATTR_CB_STALL_DISABLE, DOMAIN_ATTR_UPSTREAM_IOVA_ALLOCATOR, DOMAIN_ATTR_MMU500_ERRATA_MIN_ALIGN, + DOMAIN_ATTR_FORCE_IOVA_GUARD_PAGE, DOMAIN_ATTR_MAX, };