| /* |
| * drivers/staging/android/ion/ion_cma_secure_heap.c |
| * |
| * Copyright (C) Linaro 2012 |
| * Author: <benjamin.gaignard@linaro.org> for ST-Ericsson. |
| * Copyright (c) 2013-2018, The Linux Foundation. All rights reserved. |
| * |
| * This software is licensed under the terms of the GNU General Public |
| * License version 2, as published by the Free Software Foundation, and |
| * may be copied, distributed, and modified under those terms. |
| * |
| * 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/device.h> |
| #include <linux/ion.h> |
| #include <linux/slab.h> |
| #include <linux/errno.h> |
| #include <linux/err.h> |
| #include <linux/dma-mapping.h> |
| #include <linux/msm_ion.h> |
| #include <trace/events/kmem.h> |
| |
| #include <soc/qcom/secure_buffer.h> |
| #include <asm/cacheflush.h> |
| |
| /* for ion_heap_ops structure */ |
| #include "ion.h" |
| |
| #define ION_CMA_ALLOCATE_FAILED NULL |
| |
| struct ion_secure_cma_non_contig_info { |
| dma_addr_t phys; |
| int len; |
| struct list_head entry; |
| }; |
| |
| struct ion_secure_cma_buffer_info { |
| dma_addr_t phys; |
| struct sg_table *table; |
| bool is_cached; |
| int len; |
| struct list_head non_contig_list; |
| unsigned long ncelems; |
| }; |
| |
| struct ion_cma_alloc_chunk { |
| void *cpu_addr; |
| struct list_head entry; |
| dma_addr_t handle; |
| unsigned long chunk_size; |
| atomic_t cnt; |
| }; |
| |
| struct ion_cma_secure_heap { |
| struct device *dev; |
| /* |
| * Protects against races between threads allocating memory/adding to |
| * pool at the same time. (e.g. thread 1 adds to pool, thread 2 |
| * allocates thread 1's memory before thread 1 knows it needs to |
| * allocate more. |
| * Admittedly this is fairly coarse grained right now but the chance for |
| * contention on this lock is unlikely right now. This can be changed if |
| * this ever changes in the future |
| */ |
| struct mutex alloc_lock; |
| /* |
| * protects the list of memory chunks in this pool |
| */ |
| struct mutex chunk_lock; |
| struct ion_heap heap; |
| /* |
| * Bitmap for allocation. This contains the aggregate of all chunks. |
| */ |
| unsigned long *bitmap; |
| /* |
| * List of all allocated chunks |
| * |
| * This is where things get 'clever'. Individual allocations from |
| * dma_alloc_coherent must be allocated and freed in one chunk. |
| * We don't just want to limit the allocations to those confined |
| * within a single chunk (if clients allocate n small chunks we would |
| * never be able to use the combined size). The bitmap allocator is |
| * used to find the contiguous region and the parts of the chunks are |
| * marked off as used. The chunks won't be freed in the shrinker until |
| * the usage is actually zero. |
| */ |
| struct list_head chunks; |
| int npages; |
| phys_addr_t base; |
| struct work_struct work; |
| unsigned long last_alloc; |
| struct shrinker shrinker; |
| atomic_t total_allocated; |
| atomic_t total_pool_size; |
| atomic_t total_leaked; |
| unsigned long heap_size; |
| unsigned long default_prefetch_size; |
| }; |
| |
| static void ion_secure_pool_pages(struct work_struct *work); |
| |
| static int ion_heap_allow_secure_allocation(enum ion_heap_type type) |
| { |
| return type == ((enum ion_heap_type)ION_HEAP_TYPE_SECURE_DMA); |
| } |
| |
| /* |
| * Create scatter-list for the already allocated DMA buffer. |
| * This function could be replace by dma_common_get_sgtable |
| * as soon as it will avalaible. |
| */ |
| static int ion_secure_cma_get_sgtable(struct device *dev, struct sg_table *sgt, |
| dma_addr_t handle, size_t size) |
| { |
| struct page *page = pfn_to_page(PFN_DOWN(handle)); |
| int ret; |
| |
| ret = sg_alloc_table(sgt, 1, GFP_KERNEL); |
| if (unlikely(ret)) |
| return ret; |
| |
| sg_set_page(sgt->sgl, page, PAGE_ALIGN(size), 0); |
| sg_dma_address(sgt->sgl) = handle; |
| return 0; |
| } |
| |
| static int ion_secure_cma_add_to_pool( |
| struct ion_cma_secure_heap *sheap, |
| unsigned long len, |
| bool prefetch) |
| { |
| void *cpu_addr; |
| dma_addr_t handle; |
| unsigned long attrs = 0; |
| int ret = 0; |
| struct ion_cma_alloc_chunk *chunk; |
| |
| trace_ion_secure_cma_add_to_pool_start(len, |
| atomic_read(&sheap->total_pool_size), |
| prefetch); |
| mutex_lock(&sheap->chunk_lock); |
| |
| chunk = kzalloc(sizeof(*chunk), GFP_KERNEL); |
| if (!chunk) { |
| ret = -ENOMEM; |
| goto out; |
| } |
| |
| attrs = DMA_ATTR_NO_KERNEL_MAPPING | DMA_ATTR_SKIP_ZEROING; |
| |
| cpu_addr = dma_alloc_attrs(sheap->dev, len, &handle, GFP_KERNEL, |
| attrs); |
| |
| if (!cpu_addr) { |
| ret = -ENOMEM; |
| goto out_free; |
| } |
| |
| chunk->cpu_addr = cpu_addr; |
| chunk->handle = handle; |
| chunk->chunk_size = len; |
| atomic_set(&chunk->cnt, 0); |
| list_add(&chunk->entry, &sheap->chunks); |
| atomic_add(len, &sheap->total_pool_size); |
| /* clear the bitmap to indicate this region can be allocated from */ |
| bitmap_clear(sheap->bitmap, (handle - sheap->base) >> PAGE_SHIFT, |
| len >> PAGE_SHIFT); |
| goto out; |
| |
| out_free: |
| kfree(chunk); |
| out: |
| mutex_unlock(&sheap->chunk_lock); |
| |
| trace_ion_secure_cma_add_to_pool_end(len, |
| atomic_read(&sheap->total_pool_size), |
| prefetch); |
| |
| return ret; |
| } |
| |
| static void ion_secure_pool_pages(struct work_struct *work) |
| { |
| struct ion_cma_secure_heap *sheap = container_of(work, |
| struct ion_cma_secure_heap, work); |
| |
| ion_secure_cma_add_to_pool(sheap, sheap->last_alloc, true); |
| } |
| |
| /* |
| * @s1: start of the first region |
| * @l1: length of the first region |
| * @s2: start of the second region |
| * @l2: length of the second region |
| * |
| * Returns the total number of bytes that intersect. |
| * |
| * s1 is the region we are trying to clear so s2 may be subsumed by s1 but the |
| * maximum size to clear should only ever be l1 |
| * |
| */ |
| static unsigned int intersect(unsigned long s1, unsigned long l1, |
| unsigned long s2, unsigned long l2) |
| { |
| unsigned long base1 = s1; |
| unsigned long end1 = s1 + l1; |
| unsigned long base2 = s2; |
| unsigned long end2 = s2 + l2; |
| |
| /* Case 0: The regions don't overlap at all */ |
| if (!(base1 < end2 && base2 < end1)) |
| return 0; |
| |
| /* Case 1: region 2 is subsumed by region 1 */ |
| if (base1 <= base2 && end2 <= end1) |
| return l2; |
| |
| /* case 2: region 1 is subsumed by region 2 */ |
| if (base2 <= base1 && end1 <= end2) |
| return l1; |
| |
| /* case 3: region1 overlaps region2 on the bottom */ |
| if (base2 < end1 && base2 > base1) |
| return end1 - base2; |
| |
| /* case 4: region 2 overlaps region1 on the bottom */ |
| if (base1 < end2 && base1 > base2) |
| return end2 - base1; |
| |
| pr_err("Bad math! Did not detect chunks correctly! %lx %lx %lx %lx\n", |
| s1, l1, s2, l2); |
| WARN_ON(1); |
| /* retrun max intersection value, so that it will fail later*/ |
| return (unsigned int)(~0); |
| } |
| |
| int ion_secure_cma_prefetch(struct ion_heap *heap, void *data) |
| { |
| unsigned long len = (unsigned long)data; |
| struct ion_cma_secure_heap *sheap = |
| container_of(heap, struct ion_cma_secure_heap, heap); |
| unsigned long diff; |
| |
| if ((int)heap->type != ION_HEAP_TYPE_SECURE_DMA) |
| return -EINVAL; |
| |
| if (len == 0) |
| len = sheap->default_prefetch_size; |
| |
| /* |
| * Only prefetch as much space as there is left in the pool so |
| * check against the current free size of the heap. |
| * This is slightly racy if someone else is allocating at the same |
| * time. CMA has a restricted size for the heap so worst case |
| * the prefetch doesn't work because the allocation fails. |
| */ |
| diff = sheap->heap_size - atomic_read(&sheap->total_pool_size); |
| |
| if (len > diff) |
| len = diff; |
| |
| sheap->last_alloc = len; |
| trace_ion_prefetching(sheap->last_alloc); |
| schedule_work(&sheap->work); |
| |
| return 0; |
| } |
| |
| static void bad_math_dump(unsigned long len, int total_overlap, |
| struct ion_cma_secure_heap *sheap, |
| bool alloc, dma_addr_t paddr) |
| { |
| struct list_head *entry; |
| |
| pr_err("Bad math! expected total was %lx actual was %x\n", |
| len, total_overlap); |
| pr_err("attempted %s address was %pa len %lx\n", |
| alloc ? "allocation" : "free", &paddr, len); |
| pr_err("chunks:\n"); |
| list_for_each(entry, &sheap->chunks) { |
| struct ion_cma_alloc_chunk *chunk = |
| container_of(entry, |
| struct ion_cma_alloc_chunk, entry); |
| pr_info("--- pa %pa len %lx\n", |
| &chunk->handle, chunk->chunk_size); |
| } |
| WARN(1, "mismatch in the sizes of secure cma chunks\n"); |
| } |
| |
| static int ion_secure_cma_alloc_from_pool( |
| struct ion_cma_secure_heap *sheap, |
| dma_addr_t *phys, |
| unsigned long len) |
| { |
| dma_addr_t paddr; |
| unsigned long page_no; |
| int ret = 0; |
| int total_overlap = 0; |
| struct list_head *entry; |
| |
| mutex_lock(&sheap->chunk_lock); |
| |
| page_no = bitmap_find_next_zero_area(sheap->bitmap, |
| sheap->npages, 0, |
| len >> PAGE_SHIFT, 0); |
| if (page_no >= sheap->npages) { |
| ret = -ENOMEM; |
| goto out; |
| } |
| bitmap_set(sheap->bitmap, page_no, len >> PAGE_SHIFT); |
| paddr = sheap->base + (page_no << PAGE_SHIFT); |
| |
| list_for_each(entry, &sheap->chunks) { |
| struct ion_cma_alloc_chunk *chunk = container_of(entry, |
| struct ion_cma_alloc_chunk, entry); |
| int overlap = intersect(chunk->handle, |
| chunk->chunk_size, paddr, len); |
| |
| atomic_add(overlap, &chunk->cnt); |
| total_overlap += overlap; |
| } |
| |
| if (total_overlap != len) { |
| bad_math_dump(len, total_overlap, sheap, 1, paddr); |
| ret = -EINVAL; |
| goto out; |
| } |
| |
| *phys = paddr; |
| out: |
| mutex_unlock(&sheap->chunk_lock); |
| return ret; |
| } |
| |
| static void ion_secure_cma_free_chunk(struct ion_cma_secure_heap *sheap, |
| struct ion_cma_alloc_chunk *chunk) |
| { |
| unsigned long attrs = 0; |
| |
| attrs = DMA_ATTR_NO_KERNEL_MAPPING; |
| /* This region is 'allocated' and not available to allocate from */ |
| bitmap_set(sheap->bitmap, (chunk->handle - sheap->base) >> PAGE_SHIFT, |
| chunk->chunk_size >> PAGE_SHIFT); |
| dma_free_attrs(sheap->dev, chunk->chunk_size, chunk->cpu_addr, |
| chunk->handle, attrs); |
| atomic_sub(chunk->chunk_size, &sheap->total_pool_size); |
| list_del(&chunk->entry); |
| kfree(chunk); |
| } |
| |
| static unsigned long |
| __ion_secure_cma_shrink_pool(struct ion_cma_secure_heap *sheap, int max_nr) |
| { |
| struct list_head *entry, *_n; |
| unsigned long drained_size = 0, skipped_size = 0; |
| |
| trace_ion_secure_cma_shrink_pool_start(drained_size, skipped_size); |
| |
| list_for_each_safe(entry, _n, &sheap->chunks) { |
| struct ion_cma_alloc_chunk *chunk = container_of(entry, |
| struct ion_cma_alloc_chunk, entry); |
| |
| if (max_nr < 0) |
| break; |
| |
| if (atomic_read(&chunk->cnt) == 0) { |
| max_nr -= chunk->chunk_size; |
| drained_size += chunk->chunk_size; |
| ion_secure_cma_free_chunk(sheap, chunk); |
| } else { |
| skipped_size += chunk->chunk_size; |
| } |
| } |
| |
| trace_ion_secure_cma_shrink_pool_end(drained_size, skipped_size); |
| return drained_size; |
| } |
| |
| int ion_secure_cma_drain_pool(struct ion_heap *heap, void *unused) |
| { |
| struct ion_cma_secure_heap *sheap = |
| container_of(heap, struct ion_cma_secure_heap, heap); |
| |
| mutex_lock(&sheap->chunk_lock); |
| __ion_secure_cma_shrink_pool(sheap, INT_MAX); |
| mutex_unlock(&sheap->chunk_lock); |
| |
| return 0; |
| } |
| |
| static unsigned long ion_secure_cma_shrinker(struct shrinker *shrinker, |
| struct shrink_control *sc) |
| { |
| unsigned long freed; |
| struct ion_cma_secure_heap *sheap = container_of(shrinker, |
| struct ion_cma_secure_heap, shrinker); |
| int nr_to_scan = sc->nr_to_scan; |
| |
| /* |
| * Allocation path may invoke the shrinker. Proceeding any further |
| * would cause a deadlock in several places so don't shrink if that |
| * happens. |
| */ |
| if (!mutex_trylock(&sheap->chunk_lock)) |
| return -EAGAIN; |
| |
| freed = __ion_secure_cma_shrink_pool(sheap, nr_to_scan); |
| |
| mutex_unlock(&sheap->chunk_lock); |
| |
| return freed; |
| } |
| |
| static unsigned long ion_secure_cma_shrinker_count(struct shrinker *shrinker, |
| struct shrink_control *sc) |
| { |
| struct ion_cma_secure_heap *sheap = container_of(shrinker, |
| struct ion_cma_secure_heap, shrinker); |
| return atomic_read(&sheap->total_pool_size); |
| } |
| |
| static void ion_secure_cma_free_from_pool(struct ion_cma_secure_heap *sheap, |
| dma_addr_t handle, |
| unsigned long len) |
| { |
| struct list_head *entry, *_n; |
| int total_overlap = 0; |
| |
| mutex_lock(&sheap->chunk_lock); |
| bitmap_clear(sheap->bitmap, (handle - sheap->base) >> PAGE_SHIFT, |
| len >> PAGE_SHIFT); |
| |
| list_for_each_safe(entry, _n, &sheap->chunks) { |
| struct ion_cma_alloc_chunk *chunk = container_of(entry, |
| struct ion_cma_alloc_chunk, entry); |
| int overlap = intersect(chunk->handle, |
| chunk->chunk_size, handle, len); |
| |
| /* |
| * Don't actually free this from the pool list yet, let either |
| * an explicit drain call or the shrinkers take care of the |
| * pool. |
| */ |
| atomic_sub_return(overlap, &chunk->cnt); |
| if (atomic_read(&chunk->cnt) < 0) { |
| WARN(1, "Invalid chunk size of %d\n", |
| atomic_read(&chunk->cnt)); |
| goto out; |
| } |
| |
| total_overlap += overlap; |
| } |
| |
| if (atomic_read(&sheap->total_pool_size) < 0) { |
| WARN(1, "total pool size of %d is unexpected\n", |
| atomic_read(&sheap->total_pool_size)); |
| goto out; |
| } |
| |
| if (total_overlap != len) |
| bad_math_dump(len, total_overlap, sheap, 0, handle); |
| out: |
| mutex_unlock(&sheap->chunk_lock); |
| } |
| |
| /* ION CMA heap operations functions */ |
| static struct ion_secure_cma_buffer_info *__ion_secure_cma_allocate( |
| struct ion_heap *heap, struct ion_buffer *buffer, |
| unsigned long len, |
| unsigned long flags) |
| { |
| struct ion_cma_secure_heap *sheap = |
| container_of(heap, struct ion_cma_secure_heap, heap); |
| struct ion_secure_cma_buffer_info *info; |
| int ret; |
| |
| dev_dbg(sheap->dev, "Request buffer allocation len %ld\n", len); |
| |
| info = kzalloc(sizeof(*info), GFP_KERNEL); |
| if (!info) |
| return ION_CMA_ALLOCATE_FAILED; |
| |
| mutex_lock(&sheap->alloc_lock); |
| ret = ion_secure_cma_alloc_from_pool(sheap, &info->phys, len); |
| |
| if (ret) { |
| retry: |
| ret = ion_secure_cma_add_to_pool(sheap, len, false); |
| if (ret) { |
| mutex_unlock(&sheap->alloc_lock); |
| dev_err(sheap->dev, "Fail to allocate buffer\n"); |
| goto err; |
| } |
| ret = ion_secure_cma_alloc_from_pool(sheap, &info->phys, len); |
| if (ret) { |
| /* |
| * Lost the race with the shrinker, try again |
| */ |
| goto retry; |
| } |
| } |
| mutex_unlock(&sheap->alloc_lock); |
| |
| atomic_add(len, &sheap->total_allocated); |
| info->table = kmalloc(sizeof(*info->table), GFP_KERNEL); |
| if (!info->table) { |
| dev_err(sheap->dev, "Fail to allocate sg table\n"); |
| goto err; |
| } |
| |
| info->len = len; |
| ion_secure_cma_get_sgtable(sheap->dev, |
| info->table, info->phys, len); |
| |
| /* keep this for memory release */ |
| buffer->priv_virt = info; |
| dev_dbg(sheap->dev, "Allocate buffer %pK\n", buffer); |
| return info; |
| |
| err: |
| kfree(info); |
| return ION_CMA_ALLOCATE_FAILED; |
| } |
| |
| static void __ion_secure_cma_free_non_contig(struct ion_cma_secure_heap *sheap, |
| struct ion_secure_cma_buffer_info |
| *info) |
| { |
| struct ion_secure_cma_non_contig_info *nc_info, *temp; |
| |
| list_for_each_entry_safe(nc_info, temp, &info->non_contig_list, entry) { |
| ion_secure_cma_free_from_pool(sheap, nc_info->phys, |
| nc_info->len); |
| list_del(&nc_info->entry); |
| kfree(nc_info); |
| } |
| } |
| |
| static void __ion_secure_cma_free(struct ion_cma_secure_heap *sheap, |
| struct ion_secure_cma_buffer_info *info, |
| bool release_memory) |
| { |
| if (release_memory) { |
| if (info->ncelems) |
| __ion_secure_cma_free_non_contig(sheap, info); |
| else |
| ion_secure_cma_free_from_pool(sheap, info->phys, |
| info->len); |
| } |
| sg_free_table(info->table); |
| kfree(info->table); |
| kfree(info); |
| } |
| |
| static struct ion_secure_cma_buffer_info *__ion_secure_cma_allocate_non_contig( |
| struct ion_heap *heap, struct ion_buffer *buffer, |
| unsigned long len, |
| unsigned long flags) |
| { |
| struct ion_cma_secure_heap *sheap = |
| container_of(heap, struct ion_cma_secure_heap, heap); |
| struct ion_secure_cma_buffer_info *info; |
| int ret; |
| unsigned long alloc_size = len; |
| struct ion_secure_cma_non_contig_info *nc_info, *temp; |
| unsigned long ncelems = 0; |
| struct scatterlist *sg; |
| unsigned long total_allocated = 0; |
| |
| dev_dbg(sheap->dev, "Request buffer allocation len %ld\n", len); |
| |
| info = kzalloc(sizeof(*info), GFP_KERNEL); |
| if (!info) |
| return ION_CMA_ALLOCATE_FAILED; |
| |
| INIT_LIST_HEAD(&info->non_contig_list); |
| info->table = kmalloc(sizeof(*info->table), GFP_KERNEL); |
| if (!info->table) { |
| dev_err(sheap->dev, "Fail to allocate sg table\n"); |
| goto err; |
| } |
| mutex_lock(&sheap->alloc_lock); |
| while (total_allocated < len) { |
| if (alloc_size < SZ_1M) { |
| pr_err("Cannot allocate less than 1MB\n"); |
| goto err2; |
| } |
| nc_info = kzalloc(sizeof(*nc_info), GFP_KERNEL); |
| if (!nc_info) |
| goto err2; |
| |
| ret = ion_secure_cma_alloc_from_pool(sheap, &nc_info->phys, |
| alloc_size); |
| if (ret) { |
| retry: |
| ret = ion_secure_cma_add_to_pool(sheap, alloc_size, |
| false); |
| if (ret) { |
| alloc_size = alloc_size / 2; |
| if (!IS_ALIGNED(alloc_size, SZ_1M)) |
| alloc_size = round_down(alloc_size, |
| SZ_1M); |
| kfree(nc_info); |
| continue; |
| } |
| ret = ion_secure_cma_alloc_from_pool(sheap, |
| &nc_info->phys, |
| alloc_size); |
| if (ret) { |
| /* |
| * Lost the race with the shrinker, try again |
| */ |
| goto retry; |
| } |
| } |
| nc_info->len = alloc_size; |
| list_add_tail(&nc_info->entry, &info->non_contig_list); |
| ncelems++; |
| total_allocated += alloc_size; |
| alloc_size = min(alloc_size, len - total_allocated); |
| } |
| mutex_unlock(&sheap->alloc_lock); |
| atomic_add(total_allocated, &sheap->total_allocated); |
| |
| nc_info = list_first_entry_or_null(&info->non_contig_list, |
| struct |
| ion_secure_cma_non_contig_info, |
| entry); |
| if (!nc_info) { |
| pr_err("%s: Unable to find first entry of non contig list\n", |
| __func__); |
| goto err1; |
| } |
| info->phys = nc_info->phys; |
| info->len = total_allocated; |
| info->ncelems = ncelems; |
| |
| ret = sg_alloc_table(info->table, ncelems, GFP_KERNEL); |
| if (unlikely(ret)) |
| goto err1; |
| |
| sg = info->table->sgl; |
| list_for_each_entry(nc_info, &info->non_contig_list, entry) { |
| sg_set_page(sg, phys_to_page(nc_info->phys), nc_info->len, 0); |
| sg_dma_address(sg) = nc_info->phys; |
| sg = sg_next(sg); |
| } |
| buffer->priv_virt = info; |
| dev_dbg(sheap->dev, "Allocate buffer %pK\n", buffer); |
| return info; |
| |
| err2: |
| mutex_unlock(&sheap->alloc_lock); |
| err1: |
| list_for_each_entry_safe(nc_info, temp, &info->non_contig_list, |
| entry) { |
| list_del(&nc_info->entry); |
| kfree(nc_info); |
| } |
| kfree(info->table); |
| err: |
| kfree(info); |
| return ION_CMA_ALLOCATE_FAILED; |
| } |
| |
| static int ion_secure_cma_allocate(struct ion_heap *heap, |
| struct ion_buffer *buffer, |
| unsigned long len, |
| unsigned long flags) |
| { |
| unsigned long secure_allocation = flags & ION_FLAG_SECURE; |
| struct ion_secure_cma_buffer_info *buf = NULL; |
| unsigned long allow_non_contig = flags & ION_FLAG_ALLOW_NON_CONTIG; |
| |
| if (!secure_allocation && |
| !ion_heap_allow_secure_allocation(heap->type)) { |
| pr_err("%s: non-secure allocation disallowed from heap %s %lx\n", |
| __func__, heap->name, flags); |
| return -ENOMEM; |
| } |
| |
| if (ION_IS_CACHED(flags)) { |
| pr_err("%s: cannot allocate cached memory from secure heap %s\n", |
| __func__, heap->name); |
| return -ENOMEM; |
| } |
| |
| if (!IS_ALIGNED(len, SZ_1M)) { |
| pr_err("%s: length of allocation from %s must be a multiple of 1MB\n", |
| __func__, heap->name); |
| return -ENOMEM; |
| } |
| trace_ion_secure_cma_allocate_start(heap->name, len, flags); |
| if (!allow_non_contig) |
| buf = __ion_secure_cma_allocate(heap, buffer, len, |
| flags); |
| else |
| buf = __ion_secure_cma_allocate_non_contig(heap, buffer, len, |
| flags); |
| trace_ion_secure_cma_allocate_end(heap->name, len, flags); |
| if (buf) { |
| int ret; |
| |
| if (!msm_secure_v2_is_supported()) { |
| pr_err("%s: securing buffers from clients is not supported on this platform\n", |
| __func__); |
| ret = 1; |
| } else { |
| trace_ion_cp_secure_buffer_start(heap->name, len, |
| flags); |
| ret = msm_secure_table(buf->table); |
| trace_ion_cp_secure_buffer_end(heap->name, len, |
| flags); |
| } |
| if (ret) { |
| struct ion_cma_secure_heap *sheap = |
| container_of(buffer->heap, |
| struct ion_cma_secure_heap, heap); |
| |
| pr_err("%s: failed to secure buffer\n", __func__); |
| __ion_secure_cma_free(sheap, buf, true); |
| } |
| return ret; |
| } else { |
| return -ENOMEM; |
| } |
| } |
| |
| static void ion_secure_cma_free(struct ion_buffer *buffer) |
| { |
| struct ion_cma_secure_heap *sheap = |
| container_of(buffer->heap, struct ion_cma_secure_heap, heap); |
| struct ion_secure_cma_buffer_info *info = buffer->priv_virt; |
| int ret = 0; |
| |
| dev_dbg(sheap->dev, "Release buffer %pK\n", buffer); |
| if (msm_secure_v2_is_supported()) |
| ret = msm_unsecure_table(info->table); |
| atomic_sub(buffer->size, &sheap->total_allocated); |
| if (atomic_read(&sheap->total_allocated) < 0) { |
| WARN(1, "no memory is allocated from this pool\n"); |
| return; |
| } |
| |
| /* release memory */ |
| if (ret) { |
| WARN(1, "Unsecure failed, can't free the memory. Leaking it!"); |
| atomic_add(buffer->size, &sheap->total_leaked); |
| } |
| |
| __ion_secure_cma_free(sheap, info, ret ? false : true); |
| } |
| |
| static int ion_secure_cma_mmap(struct ion_heap *mapper, |
| struct ion_buffer *buffer, |
| struct vm_area_struct *vma) |
| { |
| pr_info("%s: mmaping from secure heap %s disallowed\n", |
| __func__, mapper->name); |
| return -EINVAL; |
| } |
| |
| static void *ion_secure_cma_map_kernel(struct ion_heap *heap, |
| struct ion_buffer *buffer) |
| { |
| pr_info("%s: kernel mapping from secure heap %s disallowed\n", |
| __func__, heap->name); |
| return ERR_PTR(-EINVAL); |
| } |
| |
| static void ion_secure_cma_unmap_kernel(struct ion_heap *heap, |
| struct ion_buffer *buffer) |
| { |
| } |
| |
| static struct ion_heap_ops ion_secure_cma_ops = { |
| .allocate = ion_secure_cma_allocate, |
| .free = ion_secure_cma_free, |
| .map_user = ion_secure_cma_mmap, |
| .map_kernel = ion_secure_cma_map_kernel, |
| .unmap_kernel = ion_secure_cma_unmap_kernel, |
| }; |
| |
| struct ion_heap *ion_secure_cma_heap_create(struct ion_platform_heap *data) |
| { |
| struct ion_cma_secure_heap *sheap; |
| int map_size = BITS_TO_LONGS(data->size >> PAGE_SHIFT) * sizeof(long); |
| |
| sheap = kzalloc(sizeof(*sheap), GFP_KERNEL); |
| if (!sheap) |
| return ERR_PTR(-ENOMEM); |
| |
| sheap->dev = data->priv; |
| mutex_init(&sheap->chunk_lock); |
| mutex_init(&sheap->alloc_lock); |
| sheap->heap.ops = &ion_secure_cma_ops; |
| sheap->heap.type = (enum ion_heap_type)ION_HEAP_TYPE_SECURE_DMA; |
| sheap->npages = data->size >> PAGE_SHIFT; |
| sheap->base = data->base; |
| sheap->heap_size = data->size; |
| sheap->bitmap = kmalloc(map_size, GFP_KERNEL); |
| INIT_LIST_HEAD(&sheap->chunks); |
| INIT_WORK(&sheap->work, ion_secure_pool_pages); |
| sheap->shrinker.seeks = DEFAULT_SEEKS; |
| sheap->shrinker.batch = 0; |
| sheap->shrinker.scan_objects = ion_secure_cma_shrinker; |
| sheap->shrinker.count_objects = ion_secure_cma_shrinker_count; |
| sheap->default_prefetch_size = sheap->heap_size; |
| register_shrinker(&sheap->shrinker); |
| |
| if (!sheap->bitmap) { |
| kfree(sheap); |
| return ERR_PTR(-ENOMEM); |
| } |
| |
| /* |
| * we initially mark everything in the allocator as being free so that |
| * allocations can come in later |
| */ |
| bitmap_fill(sheap->bitmap, sheap->npages); |
| |
| return &sheap->heap; |
| } |
| |
| void ion_secure_cma_heap_destroy(struct ion_heap *heap) |
| { |
| struct ion_cma_secure_heap *sheap = |
| container_of(heap, struct ion_cma_secure_heap, heap); |
| |
| kfree(sheap); |
| } |