From b39ab2d98ce1f3e1674bd86a7d8a15034e411593 Mon Sep 17 00:00:00 2001 From: Li Wei Date: Sat, 3 Mar 2018 09:11:59 +0800 Subject: [PATCH] drivers:iommu: muti-domain for smmu on hikey970 platform On the hikey960 platform, only one domain is supported, and you can see that the media business is all used by the domain created by the ion. After adding IPU (AI) to Hikey970 platform, it uses its own domain, so the underlying layer needs to support multiple page mapping of domain, otherwise IPU and DRM mapping will conflict, resulting in IPU recognition of image failure. Signed-off-by: Li Wei --- .../drm/hisilicon/kirin960/kirin_drm_dss.c | 4 +- drivers/iommu/hisi_smmu.h | 15 +- drivers/iommu/hisi_smmu_lpae.c | 227 ++++++++---------- drivers/iommu/ion-iommu-map.c | 177 +++++++++----- .../staging/android/ion/hisi/of_hisi_ion.c | 6 +- include/linux/hisi/hisi-iommu.h | 11 +- include/linux/hisi/ion-iommu.h | 3 +- 7 files changed, 241 insertions(+), 202 deletions(-) diff --git a/drivers/gpu/drm/hisilicon/kirin960/kirin_drm_dss.c b/drivers/gpu/drm/hisilicon/kirin960/kirin_drm_dss.c index 123bf782a886..27b16307ef89 100755 --- a/drivers/gpu/drm/hisilicon/kirin960/kirin_drm_dss.c +++ b/drivers/gpu/drm/hisilicon/kirin960/kirin_drm_dss.c @@ -678,14 +678,12 @@ static int dss_enable_iommu(struct platform_device *pdev, struct dss_hw_ctx *ctx dev = &pdev->dev; /* create iommu domain */ - ctx->mmu_domain = iommu_domain_alloc(dev->bus); + ctx->mmu_domain = hisi_ion_enable_iommu(NULL); if (!ctx->mmu_domain) { pr_err("iommu_domain_alloc failed!\n"); return -EINVAL; } - iommu_attach_device(ctx->mmu_domain, dev); - return 0; } diff --git a/drivers/iommu/hisi_smmu.h b/drivers/iommu/hisi_smmu.h index 4637244dba6b..f4bbf5c54553 100644 --- a/drivers/iommu/hisi_smmu.h +++ b/drivers/iommu/hisi_smmu.h @@ -1,5 +1,6 @@ #ifndef HISI_SMMU_H #define HISI_SMMU_H +#include /*#define IOMMU_DEBUG*/ #ifdef IOMMU_DEBUG @@ -80,15 +81,19 @@ typedef u64 smmu_pgd_t; typedef u64 smmu_pmd_t; typedef u64 smmu_pte_t; +struct hisi_domain { + spinlock_t lock; + struct list_head list; + smmu_pgd_t *va_pgtable_addr; + smmu_pgd_t *va_pgtable_addr_orig; + phys_addr_t pa_pgtable_addr; + struct iommu_domain domain; +}; + /*smmu device object*/ struct hisi_smmu_device_lpae { struct device *dev ; struct list_head domain_list; - unsigned int ref_count; - spinlock_t lock; - unsigned long va_pgtable_addr; - phys_addr_t smmu_phy_pgtable_addr; - smmu_pgd_t *smmu_pgd; }; struct hisi_map_tile_position_lpae { diff --git a/drivers/iommu/hisi_smmu_lpae.c b/drivers/iommu/hisi_smmu_lpae.c index 0ccd5c9ffeb1..fb3150276980 100644 --- a/drivers/iommu/hisi_smmu_lpae.c +++ b/drivers/iommu/hisi_smmu_lpae.c @@ -31,6 +31,11 @@ struct hisi_smmu_device_lpae *hisi_smmu_dev; +static struct hisi_domain *to_hisi_domain(struct iommu_domain *dom) +{ + return container_of(dom, struct hisi_domain, domain); +} + /*transfer 64bit pte table pointer to struct page*/ static pgtable_t smmu_pgd_to_pte_lpae(unsigned int ppte_table) { @@ -57,41 +62,40 @@ static pgtable_t smmu_pmd_to_pte_lpae(unsigned long ppte_table) return table; } -static int get_domain_data_lpae(struct device_node *np, - struct iommu_domain_data *data) +int of_get_iova_info(struct device_node *np, unsigned long *iova_start, + unsigned long *iova_size, unsigned long *iova_align) { - unsigned long long align; - struct device_node *node = NULL; + struct device_node *node; int ret = 0; - data->phy_pgd_base = hisi_smmu_dev->smmu_phy_pgtable_addr; - if (np) { - node = of_find_node_by_name(np, "iommu_info"); - if (!node) { - dbg("find iommu_info node error\n"); - return -ENODEV; - } - ret = of_property_read_u32(node, "start-addr", - &data->iova_start); - if (ret) { - dbg("read iova start address error\n"); - goto read_error; - } - ret = of_property_read_u32(node, "size", &data->iova_size); - if (ret) { - dbg("read iova size error\n"); - goto read_error; - } - ret = of_property_read_u64(node, "iova-align", &align); - if (!ret) - data->iova_align = (unsigned long)align; - else - data->iova_align = SZ_256K; + if (!np) + return -ENODEV; - pr_err("%s:start_addr 0x%x, size 0x%x align 0x%lx\n", - __func__, data->iova_start, - data->iova_size, data->iova_align); + node = of_find_node_by_name(np, "iommu_info"); + if (!node) { + pr_err("find iommu_info node error\n"); + return -ENODEV; } + ret = of_property_read_u32(node, "start-addr", + (u32 *)iova_start); + if (ret) { + pr_err("read iova start address error\n"); + ret = -EINVAL; + goto read_error; + } + ret = of_property_read_u32(node, "size", (u32 *)iova_size); + if (ret) { + pr_err("read iova size error\n"); + ret = -EINVAL; + goto read_error; + } + ret = of_property_read_u64(node, "iova-align", (u64 *)iova_align); + if (ret) + *iova_align = SZ_256K; + + pr_err("%s:start_addr 0x%lx, size 0x%lx align 0x%lx\n", + __func__, *iova_start, *iova_size, + *iova_align); return 0; @@ -102,18 +106,32 @@ read_error: static struct iommu_domain *hisi_smmu_domain_alloc_lpae(unsigned iommu_domain_type) { - struct iommu_domain *domain; + struct hisi_domain *hisi_domain; if (iommu_domain_type != IOMMU_DOMAIN_UNMANAGED) return NULL; - domain = kzalloc(sizeof(*domain), GFP_KERNEL); - if (!domain) { - pr_err("%s: fail to kzalloc %lu bytes\n", - __func__, sizeof(*domain)); - } + hisi_domain = kzalloc(sizeof(*hisi_domain), GFP_KERNEL); + if (!hisi_domain) + return NULL; - return domain; + hisi_domain->va_pgtable_addr_orig = (smmu_pgd_t *)kzalloc(SZ_64, GFP_KERNEL | __GFP_DMA); + if (!hisi_domain->va_pgtable_addr_orig) + goto err_smmu_pgd; + + hisi_domain->va_pgtable_addr = (smmu_pgd_t *)(ALIGN((unsigned long) + (hisi_domain->va_pgtable_addr_orig), SZ_32)); + + hisi_domain->pa_pgtable_addr = virt_to_phys(hisi_domain->va_pgtable_addr); + spin_lock_init(&hisi_domain->lock); + + list_add(&hisi_domain->list, &hisi_smmu_dev->domain_list); + + return &hisi_domain->domain; + +err_smmu_pgd: + kfree(hisi_domain); + return NULL; } @@ -147,17 +165,19 @@ static void hisi_smmu_free_pmds_lpae(smmu_pgd_t pgd) smmu_set_pgd_lpae(&pgd, 0); } -static void hisi_smmu_free_pgtables_lpae(unsigned long *page_table_addr) +static void hisi_smmu_free_pgtables_lpae(struct iommu_domain *domain, + unsigned long *page_table_addr) { int i, j; smmu_pgd_t *pgd; smmu_pmd_t *pmd; unsigned long flags; + struct hisi_domain *hisi_domain = to_hisi_domain(domain); pgd = (smmu_pgd_t *)page_table_addr; pmd = (smmu_pmd_t *)page_table_addr; - spin_lock_irqsave(&hisi_smmu_dev->lock, flags); + spin_lock_irqsave(&hisi_domain->lock, flags); for (i = 0; i < SMMU_PTRS_PER_PGD; ++i) { if ((smmu_pgd_none_lpae(*pgd)) & (smmu_pmd_none_lpae(*pmd))) continue; @@ -169,38 +189,39 @@ static void hisi_smmu_free_pgtables_lpae(unsigned long *page_table_addr) pgd++; } memset((void *)page_table_addr, 0, PAGE_SIZE); - spin_unlock_irqrestore(&hisi_smmu_dev->lock, flags); + spin_unlock_irqrestore(&hisi_domain->lock, flags); } static void hisi_smmu_domain_free_lpae(struct iommu_domain *domain) { - if (list_empty(&hisi_smmu_dev->domain_list)) - hisi_smmu_free_pgtables_lpae((unsigned long *) - hisi_smmu_dev->va_pgtable_addr); - - kfree(domain); + struct hisi_domain *hisi_domain = to_hisi_domain(domain); + hisi_smmu_free_pgtables_lpae(domain, (unsigned long *) + hisi_domain->va_pgtable_addr); + list_del(&hisi_domain->list); + kfree(hisi_domain->va_pgtable_addr_orig); + kfree(hisi_domain); } -static int hisi_smmu_alloc_init_pte_lpae(smmu_pmd_t *ppmd, - unsigned long addr, unsigned long end, +static int hisi_smmu_alloc_init_pte_lpae(struct iommu_domain *domain, + smmu_pmd_t *ppmd, unsigned long addr, unsigned long end, unsigned long pfn, u64 prot, unsigned long *flags) { smmu_pte_t *pte, *start; pgtable_t table; u64 pteval = SMMU_PTE_TYPE; + struct hisi_domain *hisi_domain = to_hisi_domain(domain); if (!smmu_pmd_none_lpae(*ppmd)) goto pte_ready; /* Allocate a new set of tables */ - spin_unlock_irqrestore(&hisi_smmu_dev->lock, *flags); table = alloc_page(GFP_KERNEL | __GFP_ZERO | __GFP_DMA); - spin_lock_irqsave(&hisi_smmu_dev->lock, *flags); if (!table) { dbg("%s: alloc page fail\n", __func__); return -ENOMEM; } + spin_lock_irqsave(&hisi_domain->lock, *flags); if (smmu_pmd_none_lpae(*ppmd)) { hisi_smmu_flush_pgtable_lpae(page_address(table), @@ -209,6 +230,7 @@ static int hisi_smmu_alloc_init_pte_lpae(smmu_pmd_t *ppmd, hisi_smmu_flush_pgtable_lpae(ppmd, sizeof(*ppmd)); } else __free_page(table); + spin_unlock_irqrestore(&hisi_domain->lock, *flags); pte_ready: if (prot & IOMMU_SEC) @@ -261,27 +283,26 @@ pte_ready: return 0; } -static int hisi_smmu_alloc_init_pmd_lpae(smmu_pgd_t *ppgd, - unsigned long addr, unsigned long end, +static int hisi_smmu_alloc_init_pmd_lpae(struct iommu_domain *domain, + smmu_pgd_t *ppgd, unsigned long addr, unsigned long end, unsigned long paddr, int prot, unsigned long *flags) { int ret = 0; smmu_pmd_t *ppmd, *start; u64 next; pgtable_t table; + struct hisi_domain *hisi_domain = to_hisi_domain(domain); if (!smmu_pgd_none_lpae(*ppgd)) goto pmd_ready; /* Allocate a new set of tables */ - spin_unlock_irqrestore(&hisi_smmu_dev->lock, *flags); table = alloc_page(GFP_KERNEL | __GFP_ZERO | __GFP_DMA); - spin_lock_irqsave(&hisi_smmu_dev->lock, *flags); if (!table) { dbg("%s: alloc page fail\n", __func__); return -ENOMEM; } - + spin_lock_irqsave(&hisi_domain->lock, *flags); if (smmu_pgd_none_lpae(*ppgd)) { hisi_smmu_flush_pgtable_lpae(page_address(table), SMMU_PAGE_SIZE); @@ -289,6 +310,7 @@ static int hisi_smmu_alloc_init_pmd_lpae(smmu_pgd_t *ppgd, hisi_smmu_flush_pgtable_lpae(ppgd, sizeof(*ppgd)); } else __free_page(table); + spin_unlock_irqrestore(&hisi_domain->lock, *flags); pmd_ready: if (prot & IOMMU_SEC) @@ -299,7 +321,7 @@ pmd_ready: do { next = smmu_pmd_addr_end_lpae(addr, end); - ret = hisi_smmu_alloc_init_pte_lpae(ppmd, + ret = hisi_smmu_alloc_init_pte_lpae(domain, ppmd, addr, next, __phys_to_pfn(paddr), prot, flags); if (ret) goto error; @@ -318,7 +340,9 @@ int hisi_smmu_handle_mapping_lpae(struct iommu_domain *domain, unsigned long end; unsigned long next; unsigned long flags; - smmu_pgd_t *pgd = (smmu_pgd_t *)hisi_smmu_dev->va_pgtable_addr; + + struct hisi_domain *hisi_domain = to_hisi_domain(domain); + smmu_pgd_t *pgd = (smmu_pgd_t *)hisi_domain->va_pgtable_addr; if (!pgd) { dbg("pgd is null\n"); @@ -326,12 +350,11 @@ int hisi_smmu_handle_mapping_lpae(struct iommu_domain *domain, } iova = ALIGN(iova, SMMU_PAGE_SIZE); size = ALIGN(size, SMMU_PAGE_SIZE); - spin_lock_irqsave(&hisi_smmu_dev->lock, flags); pgd += smmu_pgd_index(iova); end = iova + size; do { next = smmu_pgd_addr_end_lpae(iova, end); - ret = hisi_smmu_alloc_init_pmd_lpae(pgd, + ret = hisi_smmu_alloc_init_pmd_lpae(domain, pgd, iova, next, paddr, prot, &flags); if (ret) goto out_unlock; @@ -339,7 +362,6 @@ int hisi_smmu_handle_mapping_lpae(struct iommu_domain *domain, iova = next; } while (pgd++, iova < end); out_unlock: - spin_unlock_irqrestore(&hisi_smmu_dev->lock, flags); return ret; } @@ -348,29 +370,15 @@ static int hisi_smmu_map_lpae(struct iommu_domain *domain, phys_addr_t paddr, size_t size, int prot) { - unsigned long max_iova; struct iommu_domain_data *data; if (!domain) { dbg("domain is null\n"); + data = NULL; return -ENODEV; } data = domain->priv; - max_iova = data->iova_start + data->iova_size; - if (iova < data->iova_start) { - dbg("iova failed: iova = 0x%lx, start = 0x%8x\n", - iova, data->iova_start); - goto error; - } - if ((iova+size) > max_iova) { - dbg("iova out of domain range, iova+size=0x%lx, end=0x%lx\n", - iova+size, max_iova); - goto error; - } return hisi_smmu_handle_mapping_lpae(domain, iova, paddr, size, prot); -error: - dbg("iova is not in this range\n"); - return -EINVAL; } static unsigned int hisi_smmu_clear_pte_lpae(smmu_pgd_t *pmdp, @@ -416,59 +424,42 @@ unsigned int hisi_smmu_handle_unmapping_lpae(struct iommu_domain *domain, unsigned int end = 0; unsigned int next = 0; unsigned int unmap_size = 0; - unsigned long flags; + struct hisi_domain *hisi_domain = to_hisi_domain(domain); iova = SMMU_PAGE_ALIGN(iova); size = SMMU_PAGE_ALIGN(size); - pgdp = (smmu_pgd_t *)hisi_smmu_dev->va_pgtable_addr; + pgdp = (smmu_pgd_t *)hisi_domain->va_pgtable_addr; end = iova + size; - dbg("%s:end=0x%x\n", __func__, end); pgdp += smmu_pgd_index(iova); - spin_lock_irqsave(&hisi_smmu_dev->lock, flags); do { next = smmu_pgd_addr_end_lpae(iova, end); unmap_size += hisi_smmu_clear_pmd_lpae(pgdp, iova, next); iova = next; - dbg("%s: pgdp=%p, iova=0x%lx\n", __func__, pgdp, iova); + dbg("%s: pgdp=%pK, iova=0x%lx\n", __func__, pgdp, iova); } while (pgdp++, iova < end); - spin_unlock_irqrestore(&hisi_smmu_dev->lock, flags); return unmap_size; } static size_t hisi_smmu_unmap_lpae(struct iommu_domain *domain, unsigned long iova, size_t size) { - unsigned long max_iova; unsigned int ret; struct iommu_domain_data *data; if (!domain) { dbg("domain is null\n"); - return -ENODEV; + return 0; } data = domain->priv; /*caculate the max io virtual address */ - max_iova = data->iova_start + data->iova_size; - /*check the iova */ - if (iova < data->iova_start) - goto error; - if ((iova+size) > max_iova) { - dbg("iova out of domain range, iova+size=0x%lx, end=0x%lx\n", - iova+size, max_iova); - goto error; - } /*unmapping the range of iova*/ ret = hisi_smmu_handle_unmapping_lpae(domain, iova, size); if (ret == size) { dbg("%s:unmap size:0x%x\n", __func__, (unsigned int)size); return size; - } else { + } else return 0; - } -error: - dbg("%s:the range of io address is wrong\n", __func__); - return -EINVAL; } static phys_addr_t hisi_smmu_iova_to_phys_lpae( @@ -478,7 +469,8 @@ static phys_addr_t hisi_smmu_iova_to_phys_lpae( smmu_pmd_t pmd; smmu_pte_t pte; - pgdp = (smmu_pgd_t *)hisi_smmu_dev->va_pgtable_addr; + struct hisi_domain *hisi_domain = to_hisi_domain(domain); + pgdp = (smmu_pgd_t *)hisi_domain->va_pgtable_addr; if (!pgdp) return 0; @@ -502,16 +494,17 @@ static int hisi_attach_dev_lpae(struct iommu_domain *domain, struct device *dev) { struct device_node *np = dev->of_node; int ret = 0; - struct iommu_domain_data *iommu_info = NULL; + struct hisi_domain *hisi_domain = to_hisi_domain(domain); + struct iommu_domain_data *domain_data = kzalloc(sizeof + (struct iommu_domain_data), GFP_KERNEL); + if (!domain_data) + return -ENOMEM; + + ret = of_get_iova_info(np, &domain_data->iova_start, &domain_data->iova_size, + &domain_data->iova_align); + domain_data->phy_pgd_base = hisi_domain->pa_pgtable_addr; + domain->priv = domain_data; - iommu_info = kzalloc(sizeof(struct iommu_domain_data), GFP_KERNEL); - if (!iommu_info) { - dbg("alloc iommu_domain_data fail\n"); - return -EINVAL; - } - list_add(&iommu_info->list, &hisi_smmu_dev->domain_list); - domain->priv = iommu_info; - ret = get_domain_data_lpae(np, domain->priv); return ret; } @@ -522,7 +515,6 @@ static void hisi_detach_dev_lpae(struct iommu_domain *domain, data = (struct iommu_domain_data *)domain->priv; if (data) { - list_del(&data->list); domain->priv = NULL; kfree(data); } else { @@ -783,28 +775,15 @@ static int hisi_smmu_probe_lpae(struct platform_device *pdev) struct device *dev = &pdev->dev; dbg("enter %s\n", __func__); + hisi_smmu_dev = devm_kzalloc(dev, sizeof(struct hisi_smmu_device_lpae), GFP_KERNEL); + if(!hisi_smmu_dev) + return -ENOMEM; - hisi_smmu_dev->smmu_pgd = devm_kzalloc(dev, SZ_64, GFP_KERNEL | __GFP_DMA); - if (!hisi_smmu_dev) - goto smmu_device_error; - hisi_smmu_dev->dev = dev; INIT_LIST_HEAD(&hisi_smmu_dev->domain_list); - spin_lock_init(&hisi_smmu_dev->lock); - - hisi_smmu_dev->smmu_pgd = (smmu_pgd_t *)(ALIGN((unsigned long)(hisi_smmu_dev->smmu_pgd), SZ_32)); - - hisi_smmu_dev->smmu_phy_pgtable_addr = - virt_to_phys(hisi_smmu_dev->smmu_pgd); - printk(KERN_ERR "%s, smmu_phy_pgtable_addr is = %llx\n", __func__, hisi_smmu_dev->smmu_phy_pgtable_addr); - - hisi_smmu_dev->va_pgtable_addr = (unsigned long)(hisi_smmu_dev->smmu_pgd); bus_set_iommu(&platform_bus_type, &hisi_smmu_ops); return 0; - -smmu_device_error: - return -ENOMEM; } static int hisi_smmu_remove_lpae(struct platform_device *pdev) @@ -838,7 +817,7 @@ static int __init hisi_smmu_init_lpae(void) static void __exit hisi_smmu_exit_lpae(void) { - return platform_driver_unregister(&hisi_smmu_driver_lpae); + platform_driver_unregister(&hisi_smmu_driver_lpae); } subsys_initcall(hisi_smmu_init_lpae); diff --git a/drivers/iommu/ion-iommu-map.c b/drivers/iommu/ion-iommu-map.c index 52aa9a7bf699..b25d1524c181 100644 --- a/drivers/iommu/ion-iommu-map.c +++ b/drivers/iommu/ion-iommu-map.c @@ -32,12 +32,8 @@ #include #include -#ifdef IOMMU_DEBUG -#define dbg(format, arg...) \ - pr_info("[hisi_iommu_domain]"format, ##arg) -#else -#define dbg(format, arg...) -#endif +#define MAX_IOVA_SIZE_2G 0x80000000UL +#define MAX_IOVA_START_ADDR_4G 0x100000000UL struct map_result { unsigned long iova_start; @@ -47,8 +43,8 @@ struct map_result { unsigned long is_tile; }; -struct iommu_domain_data *domain_info; -static struct hisi_iommu_domain *m_hisi_domain; +static struct iommu_domain_data *ion_domain_info; +static struct ion_iommu_domain *ion_iommu_domain; DEFINE_MUTEX(iova_pool_mutex); static unsigned long hisi_alloc_iova(struct gen_pool *pool, @@ -83,23 +79,23 @@ static void hisi_free_iova(struct gen_pool *pool, unsigned long hisi_iommu_alloc_iova(size_t size, unsigned long align) { - struct hisi_iommu_domain *hisi_domain = m_hisi_domain; + struct ion_iommu_domain *ion_domain = ion_iommu_domain; - return hisi_alloc_iova(hisi_domain->iova_pool, size, align); + return hisi_alloc_iova(ion_domain->iova_pool, size, align); } EXPORT_SYMBOL_GPL(hisi_iommu_alloc_iova); void hisi_iommu_free_iova(unsigned long iova, size_t size) { int ret; - struct hisi_iommu_domain *hisi_domain = m_hisi_domain; + struct ion_iommu_domain *ion_domain = ion_iommu_domain; - ret = addr_in_gen_pool(hisi_domain->iova_pool, iova, size); + ret = addr_in_gen_pool(ion_domain->iova_pool, iova, size); if(!ret) { pr_err("%s:illegal para!!iova = %lx, size = %lx\n", __func__, iova, size); } - hisi_free_iova(hisi_domain->iova_pool, iova, size); + hisi_free_iova(ion_domain->iova_pool, iova, size); } EXPORT_SYMBOL_GPL(hisi_iommu_free_iova); @@ -135,7 +131,41 @@ static void iova_pool_destroy(struct gen_pool *pool) gen_pool_destroy(pool); } -static int do_iommu_domain_map(struct hisi_iommu_domain *hisi_domain, +static int iommu_check_tile_format(struct iommu_map_format *format, + unsigned long phys_len) +{ + unsigned long header_size = format->header_size; + unsigned long phys_page_line = format->phys_page_line; + unsigned long virt_page_line = format->virt_page_line; + + /* check invalid args */ + if ((phys_len <= header_size) || !phys_page_line || !virt_page_line) { + pr_err("[%s]wrong arg:phys_len 0x%lx, header_size 0x%lx\n", + __func__, phys_len, header_size); + pr_err("[%s]wrong arg:phys line 0x%lx, virt line 0x%lx\n", + __func__, phys_page_line, virt_page_line); + return -EINVAL; + } + + /* virt_page_line <= 128 and + * virt_page_line / phys_page_line = 1 in normal process */ + if ((virt_page_line * PAGE_SIZE > SZ_512K) || + (virt_page_line / phys_page_line != 1)) { + pr_err("%s:virt line/phys line out of standard\n", __func__); + return -EINVAL; + } + + /* check overflow */ + if ((virt_page_line * PAGE_SIZE <= virt_page_line) || + (phys_page_line * PAGE_SIZE <= phys_page_line)) { + pr_err("%s:virt_line/phys_line too large!!!\n", __func__); + return -EINVAL; + } + + return 0; +} + +static int do_iommu_domain_map(struct ion_iommu_domain *ion_domain, struct scatterlist *sgl, struct iommu_map_format *format, struct map_result *result) { @@ -147,6 +177,12 @@ static int do_iommu_domain_map(struct hisi_iommu_domain *hisi_domain, struct iommu_domain *domain; struct scatterlist *sg; struct tile_format fmt; + + if (format->prot & IOMMU_SEC) { + pr_err("prot 0x%lx\n", format->prot); + return -EINVAL; + } + /* calculate whole phys mem length */ for (phys_len = 0, sg = sgl; sg; sg = sg_next(sg)) phys_len += (unsigned long)ALIGN(sg->length, PAGE_SIZE); @@ -156,6 +192,10 @@ static int do_iommu_domain_map(struct hisi_iommu_domain *hisi_domain, unsigned long lines; unsigned long body_size; + if (iommu_check_tile_format(format, phys_len)) { + pr_err("%s:check foramt fail\n", __func__); + return -EINVAL; + } body_size = phys_len - format->header_size; lines = body_size / (format->phys_page_line * PAGE_SIZE); @@ -169,15 +209,21 @@ static int do_iommu_domain_map(struct hisi_iommu_domain *hisi_domain, / (format->virt_page_line * PAGE_SIZE); } - iova_size = lines * format->virt_page_line * PAGE_SIZE; + iova_size = lines * format->virt_page_line * PAGE_SIZE - (format->virt_page_line - format->phys_page_line) * PAGE_SIZE; } else { iova_size = phys_len; } + /* Limit not exceeding 2G */ + if (MAX_IOVA_SIZE_2G < iova_size) { + pr_err("[%s]hisi_alloc_iova alloc size more than 2G(0x%lx).\n",__func__, iova_size); + return -EINVAL; + } + /* alloc iova */ - pool = hisi_domain->iova_pool; - domain = hisi_domain->domain; - iova_start = hisi_alloc_iova(pool, iova_size, hisi_domain->range.align); + pool = ion_domain->iova_pool; + domain = ion_domain->domain; + iova_start = hisi_alloc_iova(pool, iova_size, ion_domain->range.align); if (!iova_start) { pr_err("[%s]hisi_alloc_iova alloc size 0x%lx failed!" "hisi ion pool avail 0x%lx\n", @@ -185,7 +231,7 @@ static int do_iommu_domain_map(struct hisi_iommu_domain *hisi_domain, return -EINVAL; } - if (0x100000000 < (iova_start + iova_size)) { + if (MAX_IOVA_START_ADDR_4G < (iova_start + iova_size)) { pr_err("hisi iommu can not deal with iova 0x%lx size 0x%lx\n", iova_start, iova_size); } @@ -222,15 +268,15 @@ int hisi_iommu_map_domain(struct scatterlist *sgl, { int ret = 0; struct map_result result; - struct hisi_iommu_domain *hisi_domain; + struct ion_iommu_domain *ion_domain; - hisi_domain = m_hisi_domain; + ion_domain = ion_iommu_domain; memset(&result, 0, sizeof(result)); - ret = do_iommu_domain_map(hisi_domain, sgl, format, &result); + ret = do_iommu_domain_map(ion_domain, sgl, format, &result); if (ret) { - dbg("alloc iova fail\n"); + pr_err("alloc iova fail\n"); return ret; } format->iova_start = result.iova_start; @@ -245,8 +291,8 @@ static int do_iommu_domain_unmap(struct map_result *result) { int ret; unsigned long unmaped_size; - struct hisi_iommu_domain *hisi_domain = m_hisi_domain; - struct gen_pool *pool = hisi_domain->iova_pool; + struct ion_iommu_domain *ion_domain = ion_iommu_domain; + struct gen_pool *pool = ion_domain->iova_pool; /* never unmap a zero length address space */ if (!result->iova_size) { @@ -257,15 +303,15 @@ static int do_iommu_domain_unmap(struct map_result *result) /* unmap tile equals to unmpa range */ if (result->is_tile) { - unmaped_size = iommu_unmap_tile(hisi_domain->domain, + unmaped_size = iommu_unmap_tile(ion_domain->domain, result->iova_start, result->iova_size); } else { - unmaped_size = iommu_unmap(hisi_domain->domain, + unmaped_size = iommu_unmap(ion_domain->domain, result->iova_start, result->iova_size); } if (unmaped_size != result->iova_size) { - dbg("[%s]unmap failed!\n", __func__); + pr_err("[%s]unmap failed!\n", __func__); return -EINVAL; } /* free iova */ @@ -301,65 +347,70 @@ int hisi_iommu_unmap_domain(struct iommu_map_format *format) EXPORT_SYMBOL_GPL(hisi_iommu_unmap_domain); /*only used to test*/ -phys_addr_t hisi_iommu_domain_iova_to_phys(unsigned long iova) +phys_addr_t ion_iommu_domain_iova_to_phys(unsigned long iova) { struct iommu_domain *domain; - domain = m_hisi_domain->domain; + domain = ion_iommu_domain->domain; return iommu_iova_to_phys(domain, iova); } -EXPORT_SYMBOL_GPL(hisi_iommu_domain_iova_to_phys); +EXPORT_SYMBOL_GPL(ion_iommu_domain_iova_to_phys); -int hisi_ion_enable_iommu(struct platform_device *pdev) +struct iommu_domain *hisi_ion_enable_iommu(struct platform_device *pdev) { - int ret; struct device *dev = &pdev->dev; - struct hisi_iommu_domain *hisi_domain; + struct ion_iommu_domain *ion_domain; pr_info("in %s start\n", __func__); - hisi_domain = kzalloc(sizeof(*hisi_domain), GFP_KERNEL); - if (!hisi_domain) { - dbg("alloc hisi_domain object fail\n"); - return -ENOMEM; + if (ion_iommu_domain) { + pr_err("ion domain already init return domain\n"); + return ion_iommu_domain->domain; + } + + ion_domain = kzalloc(sizeof(*ion_domain), GFP_KERNEL); + if (!ion_domain) { + pr_err("alloc ion_domain object fail\n"); + return NULL; } if (!iommu_present(dev->bus)) { - dbg("iommu not found\n"); - kfree(hisi_domain); - return 0; + pr_err("iommu not found\n"); + kfree(ion_domain); + return NULL; } /* create iommu domain */ - hisi_domain->domain = iommu_domain_alloc(dev->bus); - if (!hisi_domain->domain) { - ret = -EINVAL; + ion_domain->domain = iommu_domain_alloc(dev->bus); + if (!ion_domain->domain) goto error; - } - iommu_attach_device(hisi_domain->domain, dev); - domain_info = (struct iommu_domain_data *)hisi_domain->domain->priv; + + iommu_attach_device(ion_domain->domain, dev); + ion_domain_info = (struct iommu_domain_data *)ion_domain->domain->priv; /** * Current align is 256K */ - hisi_domain->iova_pool = iova_pool_setup(domain_info->iova_start, - domain_info->iova_size, domain_info->iova_align); - if (!hisi_domain->iova_pool) { - ret = -EINVAL; + ion_domain->iova_pool = iova_pool_setup(ion_domain_info->iova_start, + ion_domain_info->iova_size, ion_domain_info->iova_align); + if (!ion_domain->iova_pool) goto error; - } - /* this is a global pointer */ - m_hisi_domain = hisi_domain; - dbg("in %s end\n", __func__); - return 0; + /* this is a global pointer */ + ion_iommu_domain = ion_domain; + + pr_info("in %s end\n", __func__); + return ion_iommu_domain->domain; error: - WARN(1, "hisi_iommu_domain_init failed!\n"); - if (hisi_domain->iova_pool) - iova_pool_destroy(hisi_domain->iova_pool); - if (hisi_domain->domain) - iommu_domain_free(hisi_domain->domain); - kfree(hisi_domain); + WARN(1, "ion_iommu_domain_init failed!\n"); + if (ion_domain->iova_pool) + iova_pool_destroy(ion_domain->iova_pool); - return ret; + if (ion_domain->domain) + iommu_domain_free(ion_domain->domain); + + kfree(ion_domain); + + ion_iommu_domain = NULL; + return NULL; } EXPORT_SYMBOL(hisi_ion_enable_iommu); diff --git a/drivers/staging/android/ion/hisi/of_hisi_ion.c b/drivers/staging/android/ion/hisi/of_hisi_ion.c index b9d2c886d677..f8a262b96e35 100644 --- a/drivers/staging/android/ion/hisi/of_hisi_ion.c +++ b/drivers/staging/android/ion/hisi/of_hisi_ion.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -165,7 +166,6 @@ static long hisi_ion_custom_ioctl(struct ion_client *client, return ret; } -extern int hisi_ion_enable_iommu(struct platform_device *pdev); static int get_type_by_name(const char *name, enum ion_heap_type *type) { @@ -233,7 +233,7 @@ static int hisi_set_platform_data(struct platform_device *pdev) } if (!prop->value || !prop->length) { - pr_err("%s %s %d, node %s, invalid phandle, value=%p,length=%d\n", + pr_err("%s %s %d, node %s, invalid phandle, value=%pK,length=%d\n", __FILE__,__FUNCTION__,__LINE__, np->name, prop->value, prop->length ); continue; @@ -337,7 +337,7 @@ static int hisi_ion_probe(struct platform_device *pdev) } /* FIXME will move to iommu driver*/ - if (hisi_ion_enable_iommu(pdev)) { + if (!hisi_ion_enable_iommu(pdev)) { dev_info(&pdev->dev, "enable iommu fail \n"); err = -EINVAL; goto err_free_idev; diff --git a/include/linux/hisi/hisi-iommu.h b/include/linux/hisi/hisi-iommu.h index 00dd5e97db59..ad6aab46b90d 100644 --- a/include/linux/hisi/hisi-iommu.h +++ b/include/linux/hisi/hisi-iommu.h @@ -2,9 +2,16 @@ #define _HI36XX_SMMU_H #include +#include +#include + +extern struct iommu_domain *hisi_ion_enable_iommu(struct platform_device *pdev); +extern int of_get_iova_info(struct device_node *np, unsigned long *iova_start, + unsigned long *iova_size, unsigned long *iova_align); + struct iommu_domain_data { - unsigned int iova_start; - unsigned int iova_size; + unsigned long iova_start; + unsigned long iova_size; phys_addr_t phy_pgd_base; unsigned long iova_align; struct list_head list; diff --git a/include/linux/hisi/ion-iommu.h b/include/linux/hisi/ion-iommu.h index 6e73ae3ff494..0c7fb360f1e8 100644 --- a/include/linux/hisi/ion-iommu.h +++ b/include/linux/hisi/ion-iommu.h @@ -13,7 +13,7 @@ struct section_info { unsigned int page_size; unsigned int align; }; -struct hisi_iommu_domain { +struct ion_iommu_domain { struct iommu_domain *domain; struct gen_pool *iova_pool; struct section_info range; @@ -26,7 +26,6 @@ struct hisi_iommu_domain * hisi_get_domain(void); size_t hisi_iommu_iova_size(void); size_t hisi_iommu_iova_available(void); void hisi_iommu_free_iova(unsigned long iova, size_t size); -unsigned long hisi_iommu_alloc_iova(size_t size, unsigned long align); int hisi_iommu_map_range(struct iommu_domain *domain,unsigned long iova_start, struct scatterlist *sgl, unsigned long iova_size,unsigned int prot); int hisi_iommu_unmap_range(struct iommu_domain *domain,unsigned long iova_start,unsigned long iova_size);