diff options
Diffstat (limited to 'drivers/base/ump/src/linux/ump_kernel_linux_mem.c')
-rwxr-xr-x | drivers/base/ump/src/linux/ump_kernel_linux_mem.c | 250 |
1 files changed, 250 insertions, 0 deletions
diff --git a/drivers/base/ump/src/linux/ump_kernel_linux_mem.c b/drivers/base/ump/src/linux/ump_kernel_linux_mem.c new file mode 100755 index 000000000000..38db91e407ab --- /dev/null +++ b/drivers/base/ump/src/linux/ump_kernel_linux_mem.c @@ -0,0 +1,250 @@ +/* + * + * (C) COPYRIGHT 2008-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the + * GNU General Public License version 2 as published by the Free Software + * Foundation, and any use by you of this program is subject to the terms + * of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained + * from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + + + + + +#include <linux/ump.h> +#include <linux/ump-ioctl.h> + +#include <linux/version.h> +#include <linux/module.h> /* kernel module definitions */ +#include <linux/fs.h> /* file system operations */ +#include <linux/cdev.h> /* character device definitions */ +#include <linux/ioport.h> /* request_mem_region */ +#include <linux/mm.h> /* memory mananger definitions */ +#include <linux/pfn.h> +#include <linux/highmem.h> /*kmap*/ + +#include <linux/compat.h> /* is_compat_task */ + +#include <common/ump_kernel_core.h> +#include <ump_arch.h> +#include <common/ump_kernel_priv.h> + +static void umpp_vm_close(struct vm_area_struct *vma) +{ + umpp_cpu_mapping * mapping; + umpp_session * session; + ump_dd_handle handle; + + mapping = (umpp_cpu_mapping*)vma->vm_private_data; + UMP_ASSERT(mapping); + + session = mapping->session; + handle = mapping->handle; + + umpp_dd_remove_cpu_mapping(mapping->handle, mapping); /* will free the mapping object */ + ump_dd_release(handle); +} + + +static const struct vm_operations_struct umpp_vm_ops = { + .close = umpp_vm_close +}; + +int umpp_phys_commit(umpp_allocation * alloc) +{ + uint64_t i; + + /* round up to a page boundary */ + alloc->size = (alloc->size + PAGE_SIZE - 1) & ~((uint64_t)PAGE_SIZE-1) ; + /* calculate number of pages */ + alloc->blocksCount = alloc->size >> PAGE_SHIFT; + + if( (sizeof(ump_dd_physical_block_64) * alloc->blocksCount) > ((size_t)-1)) + { + printk(KERN_WARNING "UMP: umpp_phys_commit - trying to allocate more than possible\n"); + return -ENOMEM; + } + + alloc->block_array = kmalloc(sizeof(ump_dd_physical_block_64) * alloc->blocksCount, __GFP_HARDWALL | GFP_KERNEL | __GFP_NORETRY | __GFP_NOWARN); + if (NULL == alloc->block_array) + { + return -ENOMEM; + } + + for (i = 0; i < alloc->blocksCount; i++) + { + void * mp; + struct page * page = alloc_page(GFP_HIGHUSER | __GFP_NORETRY | __GFP_NOWARN | __GFP_COLD); + if (NULL == page) + { + break; + } + + alloc->block_array[i].addr = page_to_pfn(page) << PAGE_SHIFT; + alloc->block_array[i].size = PAGE_SIZE; + + mp = kmap(page); + if (NULL == mp) + { + __free_page(page); + break; + } + + memset(mp, 0x00, PAGE_SIZE); /* instead of __GFP_ZERO, so we can do cache maintenance */ + ump_sync_to_memory(PFN_PHYS(page_to_pfn(page)), mp, PAGE_SIZE); + kunmap(page); + } + + if (i == alloc->blocksCount) + { + return 0; + } + else + { + uint64_t j; + for (j = 0; j < i; j++) + { + struct page * page; + page = pfn_to_page(alloc->block_array[j].addr >> PAGE_SHIFT); + __free_page(page); + } + + kfree(alloc->block_array); + + return -ENOMEM; + } +} + +void umpp_phys_free(umpp_allocation * alloc) +{ + uint64_t i; + + for (i = 0; i < alloc->blocksCount; i++) + { + __free_page(pfn_to_page(alloc->block_array[i].addr >> PAGE_SHIFT)); + } + + kfree(alloc->block_array); +} + +int umpp_linux_mmap(struct file * filp, struct vm_area_struct * vma) +{ + ump_secure_id id; + ump_dd_handle h; + size_t offset; + int err = -EINVAL; + size_t length = vma->vm_end - vma->vm_start; + + umpp_cpu_mapping * map = NULL; + umpp_session *session = filp->private_data; + + if ( 0 == length ) + { + return -EINVAL; + } + + map = kzalloc(sizeof(*map), GFP_KERNEL); + if (NULL == map) + { + WARN_ON(1); + err = -ENOMEM; + goto out; + } + + /* unpack our arg */ +#if defined CONFIG_64BIT && CONFIG_64BIT + if (is_compat_task()) + { +#endif + id = vma->vm_pgoff >> UMP_LINUX_OFFSET_BITS_32; + offset = vma->vm_pgoff & UMP_LINUX_OFFSET_MASK_32; +#if defined CONFIG_64BIT && CONFIG_64BIT + } + else + { + id = vma->vm_pgoff >> UMP_LINUX_OFFSET_BITS_64; + offset = vma->vm_pgoff & UMP_LINUX_OFFSET_MASK_64; + } +#endif + + h = ump_dd_from_secure_id(id); + if (UMP_DD_INVALID_MEMORY_HANDLE != h) + { + uint64_t i; + uint64_t block_idx; + uint64_t block_offset; + uint64_t paddr; + umpp_allocation * alloc; + uint64_t last_byte; + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)) + vma->vm_flags |= VM_DONTCOPY | VM_DONTEXPAND | VM_IO | VM_MIXEDMAP | VM_DONTDUMP; +#else + vma->vm_flags |= VM_DONTCOPY | VM_DONTEXPAND | VM_RESERVED | VM_IO | VM_MIXEDMAP; +#endif + vma->vm_ops = &umpp_vm_ops; + vma->vm_private_data = map; + + alloc = (umpp_allocation*)h; + + if( (alloc->flags & UMP_CONSTRAINT_UNCACHED) != 0) + { + /* cache disabled flag set, disable caching for cpu mappings */ + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + } + + last_byte = length + (offset << PAGE_SHIFT) - 1; + if (last_byte >= alloc->size || last_byte < (offset << PAGE_SHIFT)) + { + goto err_out; + } + + if (umpp_dd_find_start_block(alloc, offset << PAGE_SHIFT, &block_idx, &block_offset)) + { + goto err_out; + } + + paddr = alloc->block_array[block_idx].addr + block_offset; + + for (i = 0; i < (length >> PAGE_SHIFT); i++) + { + /* check if we've overrrun the current block, if so move to the next block */ + if (paddr >= (alloc->block_array[block_idx].addr + alloc->block_array[block_idx].size)) + { + block_idx++; + UMP_ASSERT(block_idx < alloc->blocksCount); + paddr = alloc->block_array[block_idx].addr; + } + + err = vm_insert_mixed(vma, vma->vm_start + (i << PAGE_SHIFT), paddr >> PAGE_SHIFT); + paddr += PAGE_SIZE; + } + + map->vaddr_start = (void*)vma->vm_start; + map->nr_pages = length >> PAGE_SHIFT; + map->page_off = offset; + map->handle = h; + map->session = session; + + umpp_dd_add_cpu_mapping(h, map); + + return 0; + + err_out: + + ump_dd_release(h); + } + + kfree(map); + +out: + + return err; +} + |