diff options
Diffstat (limited to 'drivers/base/ump/src')
-rwxr-xr-x | drivers/base/ump/src/Kbuild | 50 | ||||
-rwxr-xr-x | drivers/base/ump/src/Makefile | 81 | ||||
-rwxr-xr-x | drivers/base/ump/src/Makefile.common | 19 | ||||
-rwxr-xr-x | drivers/base/ump/src/arch-arm/config.h | 27 | ||||
-rwxr-xr-x | drivers/base/ump/src/arch-arm64/config.h | 27 | ||||
-rwxr-xr-x | drivers/base/ump/src/common/ump_kernel_core.c | 756 | ||||
-rwxr-xr-x | drivers/base/ump/src/common/ump_kernel_core.h | 228 | ||||
-rwxr-xr-x | drivers/base/ump/src/common/ump_kernel_descriptor_mapping.c | 162 | ||||
-rwxr-xr-x | drivers/base/ump/src/common/ump_kernel_descriptor_mapping.h | 94 | ||||
-rwxr-xr-x | drivers/base/ump/src/common/ump_kernel_priv.h | 80 | ||||
-rwxr-xr-x | drivers/base/ump/src/imports/ion/Makefile | 53 | ||||
-rwxr-xr-x | drivers/base/ump/src/imports/ion/ump_kernel_import_ion.c | 204 | ||||
-rwxr-xr-x | drivers/base/ump/src/linux/ump_kernel_linux.c | 831 | ||||
-rwxr-xr-x | drivers/base/ump/src/linux/ump_kernel_linux_mem.c | 250 | ||||
-rwxr-xr-x | drivers/base/ump/src/linux/ump_kernel_linux_mem.h | 26 | ||||
-rwxr-xr-x | drivers/base/ump/src/ump_arch.h | 42 |
16 files changed, 2930 insertions, 0 deletions
diff --git a/drivers/base/ump/src/Kbuild b/drivers/base/ump/src/Kbuild new file mode 100755 index 000000000000..de6d30770d15 --- /dev/null +++ b/drivers/base/ump/src/Kbuild @@ -0,0 +1,50 @@ +# +# (C) COPYRIGHT 2012 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. +# +# + + + +# Paths required for build +UMP_PATH = $(src)/../.. +UMP_DEVICEDRV_PATH = $(src)/. + +# Set up defaults if not defined by the user +MALI_UNIT_TEST ?= 0 + +SRC :=\ + common/ump_kernel_core.c \ + common/ump_kernel_descriptor_mapping.c \ + linux/ump_kernel_linux.c \ + linux/ump_kernel_linux_mem.c + +UNIT_TEST_DEFINES= +ifeq ($(MALI_UNIT_TEST), 1) + MALI_DEBUG ?= 1 + + UNIT_TEST_DEFINES = -DMALI_UNIT_TEST=1 \ + -DMALI_DEBUG=$(MALI_DEBUG) +endif + +# Use our defines when compiling +ccflags-y += -I$(UMP_PATH) -I$(UMP_DEVICEDRV_PATH) $(UNIT_TEST_DEFINES) + + +# Tell the Linux build system from which .o file to create the kernel module +obj-$(CONFIG_UMP) += ump.o +ifeq ($(CONFIG_ION),y) +ccflags-y += -I$(srctree)/drivers/staging/android/ion -I$(srctree)/include/linux +obj-$(CONFIG_UMP) += imports/ion/ump_kernel_import_ion.o +endif + +# Tell the Linux build system to enable building of our .c files +ump-y := $(SRC:.c=.o) diff --git a/drivers/base/ump/src/Makefile b/drivers/base/ump/src/Makefile new file mode 100755 index 000000000000..45428adbdf77 --- /dev/null +++ b/drivers/base/ump/src/Makefile @@ -0,0 +1,81 @@ +# +# (C) COPYRIGHT 2008-2014 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. +# +# + + + +ifneq ($(KBUILD_EXTMOD),) +include $(KBUILD_EXTMOD)/Makefile.common +else +include ./Makefile.common +endif + +# default to building for the host +ARCH ?= $(shell uname -m) + +# linux build system integration +RELATIVE_ROOT=../../../../.. +ROOT = $(CURDIR)/$(RELATIVE_ROOT) + +EXTRA_CFLAGS=-I$(CURDIR)/../../../../include + +ifeq ($(MALI_UNIT_TEST),1) + EXTRA_CFLAGS += -DMALI_UNIT_TEST=$(MALI_UNIT_TEST) +endif + +# Get any user defined KDIR-<names> or maybe even a hardcoded KDIR +-include KDIR_CONFIGURATION + +# Define host system directory +KDIR-$(shell uname -m):=/lib/modules/$(shell uname -r)/build + +CONFIG ?= $(ARCH) + +# default cpu to select +CPU ?= $(shell uname -m) + +# look up KDIR based om CPU selection +KDIR ?= $(KDIR-$(CPU)) + +ifeq ($(KDIR),) +$(error No KDIR found for platform $(CPU)) +endif + +# Validate selected config +ifneq ($(shell [ -d arch-$(CONFIG) ] && [ -f arch-$(CONFIG)/config.h ] && echo "OK"), OK) +$(warning Current directory is $(shell pwd)) +$(error No configuration found for config $(CONFIG). Check that arch-$(CONFIG)/config.h exists) +else +# Link arch to the selected arch-config directory +$(shell [ -L arch ] && rm arch) +$(shell ln -sf arch-$(CONFIG) arch) +$(shell touch arch/config.h) +endif + +EXTRA_SYMBOLS= + +ifeq ($(MALI_UNIT_TEST),1) + KBASE_PATH=$(ROOT)/kernel/drivers/gpu/arm/midgard + EXTRA_SYMBOLS+=$(KBASE_PATH)/tests/internal/src/kernel_assert_module/linux/Module.symvers +endif +KDS_PATH=$(ROOT)/kernel/drivers/base/kds +EXTRA_SYMBOLS+=$(KDS_PATH)/Module.symvers + +all: + $(MAKE) ARCH=$(ARCH) -C $(KDIR) M=$(CURDIR) EXTRA_CFLAGS="$(EXTRA_CFLAGS) $(SCONS_CFLAGS)" CONFIG_UMP=m KBUILD_EXTRA_SYMBOLS="$(EXTRA_SYMBOLS)" modules + +kernelrelease: + $(MAKE) -C $(KDIR) KBUILD_EXTRA_SYMBOLS="$(EXTRA_SYMBOLS)" kernelrelease + +clean: + $(MAKE) ARCH=$(ARCH) -C $(KDIR) M=$(CURDIR) clean diff --git a/drivers/base/ump/src/Makefile.common b/drivers/base/ump/src/Makefile.common new file mode 100755 index 000000000000..f29a4c1cffa5 --- /dev/null +++ b/drivers/base/ump/src/Makefile.common @@ -0,0 +1,19 @@ +# +# (C) COPYRIGHT 2008-2010, 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. +# +# + + + +SRC = $(UMP_FILE_PREFIX)/common/ump_kernel_core.c \ + $(UMP_FILE_PREFIX)/common/ump_kernel_descriptor_mapping.c + diff --git a/drivers/base/ump/src/arch-arm/config.h b/drivers/base/ump/src/arch-arm/config.h new file mode 100755 index 000000000000..152d98f38af8 --- /dev/null +++ b/drivers/base/ump/src/arch-arm/config.h @@ -0,0 +1,27 @@ +/* + * + * (C) COPYRIGHT 2008-2009, 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. + * + */ + + + + + +#ifndef __ARCH_CONFIG_H__ +#define __ARCH_CONFIG_H__ + +#define ARCH_UMP_BACKEND_DEFAULT 1 +#define ARCH_UMP_MEMORY_ADDRESS_DEFAULT 0x00000000 +#define ARCH_UMP_MEMORY_SIZE_DEFAULT 32UL * 1024UL * 1024UL + +#endif /* __ARCH_CONFIG_H__ */ diff --git a/drivers/base/ump/src/arch-arm64/config.h b/drivers/base/ump/src/arch-arm64/config.h new file mode 100755 index 000000000000..cfb14ca54876 --- /dev/null +++ b/drivers/base/ump/src/arch-arm64/config.h @@ -0,0 +1,27 @@ +/* + * + * (C) COPYRIGHT 2008-2009, 2013-2014 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. + * + */ + + + + + +#ifndef __ARCH_CONFIG_H__ +#define __ARCH_CONFIG_H__ + +#define ARCH_UMP_BACKEND_DEFAULT 1 +#define ARCH_UMP_MEMORY_ADDRESS_DEFAULT 0x00000000 +#define ARCH_UMP_MEMORY_SIZE_DEFAULT 32UL * 1024UL * 1024UL + +#endif /* __ARCH_CONFIG_H__ */ diff --git a/drivers/base/ump/src/common/ump_kernel_core.c b/drivers/base/ump/src/common/ump_kernel_core.c new file mode 100755 index 000000000000..07aa07739f9f --- /dev/null +++ b/drivers/base/ump/src/common/ump_kernel_core.c @@ -0,0 +1,756 @@ +/* + * + * (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. + * + */ + + + + + +/* module headers */ +#include <linux/ump.h> +#include <linux/ump-ioctl.h> + +/* local headers */ +#include <common/ump_kernel_core.h> +#include <common/ump_kernel_descriptor_mapping.h> +#include <ump_arch.h> +#include <common/ump_kernel_priv.h> + +#define UMP_FLAGS_RANGE ((UMP_PROT_SHAREABLE<<1) - 1u) + +static umpp_device device; + +ump_result umpp_core_constructor(void) +{ + mutex_init(&device.secure_id_map_lock); + device.secure_id_map = umpp_descriptor_mapping_create(UMP_EXPECTED_IDS, UMP_MAX_IDS); + if (NULL != device.secure_id_map) + { + if (UMP_OK == umpp_device_initialize()) + { + return UMP_OK; + } + umpp_descriptor_mapping_destroy(device.secure_id_map); + } + mutex_destroy(&device.secure_id_map_lock); + + return UMP_ERROR; +} + +void umpp_core_destructor(void) +{ + umpp_device_terminate(); + umpp_descriptor_mapping_destroy(device.secure_id_map); + mutex_destroy(&device.secure_id_map_lock); +} + +umpp_session *umpp_core_session_start(void) +{ + umpp_session * session; + + session = kzalloc(sizeof(*session), GFP_KERNEL); + if (NULL != session) + { + mutex_init(&session->session_lock); + + INIT_LIST_HEAD(&session->memory_usage); + + /* try to create import client session, not a failure if they fail to initialize */ + umpp_import_handlers_init(session); + } + + return session; +} + +void umpp_core_session_end(umpp_session *session) +{ + umpp_session_memory_usage * usage, *_usage; + UMP_ASSERT(session); + + list_for_each_entry_safe(usage, _usage, &session->memory_usage, link) + { + printk(KERN_WARNING "UMP: Memory usage cleanup, releasing secure ID %d\n", ump_dd_secure_id_get(usage->mem)); + ump_dd_release(usage->mem); + kfree(usage); + + } + + /* we should now not hold any imported memory objects, + * detatch all import handlers */ + umpp_import_handlers_term(session); + + mutex_destroy(&session->session_lock); + kfree(session); +} + +ump_dd_handle ump_dd_allocate_64(uint64_t size, ump_alloc_flags flags, ump_dd_security_filter filter_func, ump_dd_final_release_callback final_release_func, void* callback_data) +{ + umpp_allocation * alloc; + int i; + + UMP_ASSERT(size); + + if (flags & (~UMP_FLAGS_RANGE)) + { + printk(KERN_WARNING "UMP: allocation flags out of allowed bits range\n"); + return UMP_DD_INVALID_MEMORY_HANDLE; + } + + if( ( flags & (UMP_PROT_CPU_RD | UMP_PROT_W_RD | UMP_PROT_X_RD | UMP_PROT_Y_RD | UMP_PROT_Z_RD ) ) == 0 || + ( flags & (UMP_PROT_CPU_WR | UMP_PROT_W_WR | UMP_PROT_X_WR | UMP_PROT_Y_WR | UMP_PROT_Z_WR )) == 0 ) + { + printk(KERN_WARNING "UMP: allocation flags should have at least one read and one write permission bit set\n"); + return UMP_DD_INVALID_MEMORY_HANDLE; + } + + /*check permission flags to be set if hit flags are set too*/ + for (i = UMP_DEVICE_CPU_SHIFT; i<=UMP_DEVICE_Z_SHIFT; i+=4) + { + if (flags & (UMP_HINT_DEVICE_RD<<i)) + { + UMP_ASSERT(flags & (UMP_PROT_DEVICE_RD<<i)); + } + if (flags & (UMP_HINT_DEVICE_WR<<i)) + { + UMP_ASSERT(flags & (UMP_PROT_DEVICE_WR<<i)); + } + } + + alloc = kzalloc(sizeof(*alloc), GFP_KERNEL | __GFP_HARDWALL); + + if (NULL == alloc) + goto out1; + + alloc->flags = flags; + alloc->filter_func = filter_func; + alloc->final_release_func = final_release_func; + alloc->callback_data = callback_data; + alloc->size = size; + + mutex_init(&alloc->map_list_lock); + INIT_LIST_HEAD(&alloc->map_list); + atomic_set(&alloc->refcount, 1); + +#ifdef CONFIG_KDS + kds_resource_init(&alloc->kds_res); +#endif + + if (!(alloc->flags & UMP_PROT_SHAREABLE)) + { + alloc->owner = get_current()->pid; + } + + if (0 != umpp_phys_commit(alloc)) + { + goto out2; + } + + /* all set up, allocate an ID for it */ + + mutex_lock(&device.secure_id_map_lock); + alloc->id = umpp_descriptor_mapping_allocate(device.secure_id_map, (void*)alloc); + mutex_unlock(&device.secure_id_map_lock); + + if ((int)alloc->id == 0) + { + /* failed to allocate a secure_id */ + goto out3; + } + + return alloc; + +out3: + umpp_phys_free(alloc); +out2: + kfree(alloc); +out1: + return UMP_DD_INVALID_MEMORY_HANDLE; +} + +uint64_t ump_dd_size_get_64(const ump_dd_handle mem) +{ + umpp_allocation * alloc; + + UMP_ASSERT(mem); + + alloc = (umpp_allocation*)mem; + + return alloc->size; +} + +/* + * UMP v1 API + */ +unsigned long ump_dd_size_get(ump_dd_handle mem) +{ + umpp_allocation * alloc; + + UMP_ASSERT(mem); + + alloc = (umpp_allocation*)mem; + + UMP_ASSERT(alloc->flags & UMP_CONSTRAINT_32BIT_ADDRESSABLE); + UMP_ASSERT(alloc->size <= UMP_UINT32_MAX); + + return (unsigned long)alloc->size; +} + +ump_secure_id ump_dd_secure_id_get(const ump_dd_handle mem) +{ + umpp_allocation * alloc; + + UMP_ASSERT(mem); + + alloc = (umpp_allocation*)mem; + + return alloc->id; +} + +#ifdef CONFIG_KDS +struct kds_resource * ump_dd_kds_resource_get(const ump_dd_handle mem) +{ + umpp_allocation * alloc; + + UMP_ASSERT(mem); + + alloc = (umpp_allocation*)mem; + + return &alloc->kds_res; +} +#endif + +ump_alloc_flags ump_dd_allocation_flags_get(const ump_dd_handle mem) +{ + const umpp_allocation * alloc; + + UMP_ASSERT(mem); + alloc = (const umpp_allocation *)mem; + + return alloc->flags; +} + +ump_dd_handle ump_dd_from_secure_id(ump_secure_id secure_id) +{ + umpp_allocation * alloc = UMP_DD_INVALID_MEMORY_HANDLE; + + mutex_lock(&device.secure_id_map_lock); + + if (0 == umpp_descriptor_mapping_lookup(device.secure_id_map, secure_id, (void**)&alloc)) + { + if (NULL != alloc->filter_func) + { + if (!alloc->filter_func(secure_id, alloc, alloc->callback_data)) + { + alloc = UMP_DD_INVALID_MEMORY_HANDLE; /* the filter denied access */ + } + } + + /* check permission to access it */ + if ((UMP_DD_INVALID_MEMORY_HANDLE != alloc) && !(alloc->flags & UMP_PROT_SHAREABLE)) + { + if (alloc->owner != get_current()->pid) + { + alloc = UMP_DD_INVALID_MEMORY_HANDLE; /*no rights for the current process*/ + } + } + + if (UMP_DD_INVALID_MEMORY_HANDLE != alloc) + { + if( ump_dd_retain(alloc) != UMP_DD_SUCCESS) + { + alloc = UMP_DD_INVALID_MEMORY_HANDLE; + } + } + } + mutex_unlock(&device.secure_id_map_lock); + + return alloc; +} + +/* + * UMP v1 API + */ +ump_dd_handle ump_dd_handle_create_from_secure_id(ump_secure_id secure_id) +{ + return ump_dd_from_secure_id(secure_id); +} + +int ump_dd_retain(ump_dd_handle mem) +{ + umpp_allocation * alloc; + + UMP_ASSERT(mem); + + alloc = (umpp_allocation*)mem; + + /* check for overflow */ + while(1) + { + int refcnt = atomic_read(&alloc->refcount); + if (refcnt + 1 > 0) + { + if(atomic_cmpxchg(&alloc->refcount, refcnt, refcnt + 1) == refcnt) + { + return 0; + } + } + else + { + return -EBUSY; + } + } +} + +/* + * UMP v1 API + */ +void ump_dd_reference_add(ump_dd_handle mem) +{ + ump_dd_retain(mem); +} + + +void ump_dd_release(ump_dd_handle mem) +{ + umpp_allocation * alloc; + uint32_t new_cnt; + + UMP_ASSERT(mem); + + alloc = (umpp_allocation*)mem; + + /* secure the id for lookup while releasing */ + mutex_lock(&device.secure_id_map_lock); + + /* do the actual release */ + new_cnt = atomic_sub_return(1, &alloc->refcount); + if (0 == new_cnt) + { + /* remove from the table as this was the last ref */ + umpp_descriptor_mapping_remove(device.secure_id_map, alloc->id); + } + + /* release the lock as early as possible */ + mutex_unlock(&device.secure_id_map_lock); + + if (0 != new_cnt) + { + /* exit if still have refs */ + return; + } + + UMP_ASSERT(list_empty(&alloc->map_list)); + +#ifdef CONFIG_KDS + if (kds_resource_term(&alloc->kds_res)) + { + printk(KERN_ERR "ump_dd_release: kds_resource_term failed," + "unable to release UMP allocation\n"); + return; + } +#endif + /* cleanup */ + if (NULL != alloc->final_release_func) + { + alloc->final_release_func(alloc, alloc->callback_data); + } + + if (0 == (alloc->management_flags & UMP_MGMT_EXTERNAL)) + { + umpp_phys_free(alloc); + } + else + { + kfree(alloc->block_array); + } + + mutex_destroy(&alloc->map_list_lock); + + kfree(alloc); +} + +/* + * UMP v1 API + */ +void ump_dd_reference_release(ump_dd_handle mem) +{ + ump_dd_release(mem); +} + +void ump_dd_phys_blocks_get_64(const ump_dd_handle mem, uint64_t * const pCount, const ump_dd_physical_block_64 ** const pArray) +{ + const umpp_allocation * alloc; + UMP_ASSERT(pCount); + UMP_ASSERT(pArray); + UMP_ASSERT(mem); + alloc = (const umpp_allocation *)mem; + *pCount = alloc->blocksCount; + *pArray = alloc->block_array; +} + +/* + * UMP v1 API + */ +ump_dd_status_code ump_dd_phys_blocks_get(ump_dd_handle mem, ump_dd_physical_block * const blocks, unsigned long num_blocks) +{ + const umpp_allocation * alloc; + unsigned long i; + UMP_ASSERT(mem); + UMP_ASSERT(blocks); + UMP_ASSERT(num_blocks); + + alloc = (const umpp_allocation *)mem; + + UMP_ASSERT(alloc->flags & UMP_CONSTRAINT_32BIT_ADDRESSABLE); + + if((uint64_t)num_blocks != alloc->blocksCount) + { + return UMP_DD_INVALID; + } + + for( i = 0; i < num_blocks; i++) + { + UMP_ASSERT(alloc->block_array[i].addr <= UMP_UINT32_MAX); + UMP_ASSERT(alloc->block_array[i].size <= UMP_UINT32_MAX); + + blocks[i].addr = (unsigned long)alloc->block_array[i].addr; + blocks[i].size = (unsigned long)alloc->block_array[i].size; + } + + return UMP_DD_SUCCESS; +} +/* + * UMP v1 API + */ +ump_dd_status_code ump_dd_phys_block_get(ump_dd_handle mem, unsigned long index, ump_dd_physical_block * const block) +{ + const umpp_allocation * alloc; + UMP_ASSERT(mem); + UMP_ASSERT(block); + alloc = (const umpp_allocation *)mem; + + UMP_ASSERT(alloc->flags & UMP_CONSTRAINT_32BIT_ADDRESSABLE); + + UMP_ASSERT(alloc->block_array[index].addr <= UMP_UINT32_MAX); + UMP_ASSERT(alloc->block_array[index].size <= UMP_UINT32_MAX); + + block->addr = (unsigned long)alloc->block_array[index].addr; + block->size = (unsigned long)alloc->block_array[index].size; + + return UMP_DD_SUCCESS; +} + +/* + * UMP v1 API + */ +unsigned long ump_dd_phys_block_count_get(ump_dd_handle mem) +{ + const umpp_allocation * alloc; + UMP_ASSERT(mem); + alloc = (const umpp_allocation *)mem; + + UMP_ASSERT(alloc->flags & UMP_CONSTRAINT_32BIT_ADDRESSABLE); + UMP_ASSERT(alloc->blocksCount <= UMP_UINT32_MAX); + + return (unsigned long)alloc->blocksCount; +} + +umpp_cpu_mapping * umpp_dd_find_enclosing_mapping(umpp_allocation * alloc, void *uaddr, size_t size) +{ + umpp_cpu_mapping *map; + + void *target_first = uaddr; + void *target_last = (void*)((uintptr_t)uaddr - 1 + size); + + if (target_last < target_first) /* wrapped */ + { + return NULL; + } + + mutex_lock(&alloc->map_list_lock); + list_for_each_entry(map, &alloc->map_list, link) + { + if ( map->vaddr_start <= target_first && + (void*)((uintptr_t)map->vaddr_start + (map->nr_pages << PAGE_SHIFT) - 1) >= target_last) + { + goto out; + } + } + map = NULL; +out: + mutex_unlock(&alloc->map_list_lock); + + return map; +} + +void umpp_dd_add_cpu_mapping(umpp_allocation * alloc, umpp_cpu_mapping * map) +{ + UMP_ASSERT(alloc); + UMP_ASSERT(map); + mutex_lock(&alloc->map_list_lock); + list_add(&map->link, &alloc->map_list); + mutex_unlock(&alloc->map_list_lock); +} + +void umpp_dd_remove_cpu_mapping(umpp_allocation * alloc, umpp_cpu_mapping * target) +{ + umpp_cpu_mapping * map; + + UMP_ASSERT(alloc); + UMP_ASSERT(target); + + mutex_lock(&alloc->map_list_lock); + list_for_each_entry(map, &alloc->map_list, link) + { + if (map == target) + { + list_del(&target->link); + kfree(target); + mutex_unlock(&alloc->map_list_lock); + return; + } + } + + /* not found, error */ + UMP_ASSERT(0); +} + +int umpp_dd_find_start_block(const umpp_allocation * alloc, uint64_t offset, uint64_t * const block_index, uint64_t * const block_internal_offset) +{ + uint64_t i; + + for (i = 0 ; i < alloc->blocksCount; i++) + { + if (offset < alloc->block_array[i].size) + { + /* found the block_array element containing this offset */ + *block_index = i; + *block_internal_offset = offset; + return 0; + } + offset -= alloc->block_array[i].size; + } + + return -ENXIO; +} + +void umpp_dd_cpu_msync_now(ump_dd_handle mem, ump_cpu_msync_op op, void * address, size_t size) +{ + umpp_allocation * alloc; + void *vaddr; + umpp_cpu_mapping * mapping; + uint64_t virt_page_off; /* offset of given address from beginning of the virtual mapping */ + uint64_t phys_page_off; /* offset of the virtual mapping from the beginning of the physical buffer */ + uint64_t page_count; /* number of pages to sync */ + uint64_t i; + uint64_t block_idx; + uint64_t block_offset; + uint64_t paddr; + + UMP_ASSERT((UMP_MSYNC_CLEAN == op) || (UMP_MSYNC_CLEAN_AND_INVALIDATE == op)); + + alloc = (umpp_allocation*)mem; + vaddr = (void*)(uintptr_t)address; + + if((alloc->flags & UMP_CONSTRAINT_UNCACHED) != 0) + { + /* mapping is not cached */ + return; + } + + mapping = umpp_dd_find_enclosing_mapping(alloc, vaddr, size); + if (NULL == mapping) + { + printk(KERN_WARNING "UMP: Illegal cache sync address %lx\n", (uintptr_t)vaddr); + return; /* invalid pointer or size causes out-of-bounds */ + } + + /* we already know that address + size don't wrap around as umpp_dd_find_enclosing_mapping didn't fail */ + page_count = ((((((uintptr_t)address + size - 1) & PAGE_MASK) - ((uintptr_t)address & PAGE_MASK))) >> PAGE_SHIFT) + 1; + virt_page_off = (vaddr - mapping->vaddr_start) >> PAGE_SHIFT; + phys_page_off = mapping->page_off; + + if (umpp_dd_find_start_block(alloc, (virt_page_off + phys_page_off) << PAGE_SHIFT, &block_idx, &block_offset)) + { + /* should not fail as a valid mapping was found, so the phys mem must exists */ + printk(KERN_WARNING "UMP: Unable to find physical start block with offset %llx\n", virt_page_off + phys_page_off); + UMP_ASSERT(0); + return; + } + + paddr = alloc->block_array[block_idx].addr + block_offset + (((uintptr_t)vaddr) & ((1u << PAGE_SHIFT)-1)); + + for (i = 0; i < page_count; i++) + { + size_t offset = ((uintptr_t)vaddr) & ((1u << PAGE_SHIFT)-1); + size_t sz = min((size_t)PAGE_SIZE - offset, size); + + /* 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; + } + + if (UMP_MSYNC_CLEAN == op) + { + ump_sync_to_memory(paddr, vaddr, sz); + } + else /* (UMP_MSYNC_CLEAN_AND_INVALIDATE == op) already validated on entry */ + { + ump_sync_to_cpu(paddr, vaddr, sz); + } + + /* advance to next page */ + vaddr = (void*)((uintptr_t)vaddr + sz); + size -= sz; + paddr += sz; + } +} + +UMP_KERNEL_API_EXPORT ump_dd_handle ump_dd_create_from_phys_blocks_64(const ump_dd_physical_block_64 * blocks, uint64_t num_blocks, ump_alloc_flags flags, ump_dd_security_filter filter_func, ump_dd_final_release_callback final_release_func, void* callback_data) +{ + uint64_t size = 0; + uint64_t i; + umpp_allocation * alloc; + + UMP_ASSERT(blocks); + UMP_ASSERT(num_blocks); + + for (i = 0; i < num_blocks; i++) + { + size += blocks[i].size; + } + UMP_ASSERT(size); + + if (flags & (~UMP_FLAGS_RANGE)) + { + printk(KERN_WARNING "UMP: allocation flags out of allowed bits range\n"); + return UMP_DD_INVALID_MEMORY_HANDLE; + } + + if( ( flags & (UMP_PROT_CPU_RD | UMP_PROT_W_RD | UMP_PROT_X_RD | UMP_PROT_Y_RD | UMP_PROT_Z_RD + | UMP_PROT_CPU_WR | UMP_PROT_W_WR | UMP_PROT_X_WR | UMP_PROT_Y_WR | UMP_PROT_Z_WR )) == 0 ) + { + printk(KERN_WARNING "UMP: allocation flags should have at least one read or write permission bit set\n"); + return UMP_DD_INVALID_MEMORY_HANDLE; + } + + /*check permission flags to be set if hit flags are set too*/ + for (i = UMP_DEVICE_CPU_SHIFT; i<=UMP_DEVICE_Z_SHIFT; i+=4) + { + if (flags & (UMP_HINT_DEVICE_RD<<i)) + { + UMP_ASSERT(flags & (UMP_PROT_DEVICE_RD<<i)); + } + if (flags & (UMP_HINT_DEVICE_WR<<i)) + { + UMP_ASSERT(flags & (UMP_PROT_DEVICE_WR<<i)); + } + } + + alloc = kzalloc(sizeof(*alloc),__GFP_HARDWALL | GFP_KERNEL); + + if (NULL == alloc) + { + goto out1; + } + + alloc->block_array = kzalloc(sizeof(ump_dd_physical_block_64) * num_blocks,__GFP_HARDWALL | GFP_KERNEL); + if (NULL == alloc->block_array) + { + goto out2; + } + + memcpy(alloc->block_array, blocks, sizeof(ump_dd_physical_block_64) * num_blocks); + +#ifdef CONFIG_KDS + kds_resource_init(&alloc->kds_res); +#endif + alloc->size = size; + alloc->blocksCount = num_blocks; + alloc->flags = flags; + alloc->filter_func = filter_func; + alloc->final_release_func = final_release_func; + alloc->callback_data = callback_data; + + if (!(alloc->flags & UMP_PROT_SHAREABLE)) + { + alloc->owner = get_current()->pid; + } + + mutex_init(&alloc->map_list_lock); + INIT_LIST_HEAD(&alloc->map_list); + atomic_set(&alloc->refcount, 1); + + /* all set up, allocate an ID */ + + mutex_lock(&device.secure_id_map_lock); + alloc->id = umpp_descriptor_mapping_allocate(device.secure_id_map, (void*)alloc); + mutex_unlock(&device.secure_id_map_lock); + + if ((int)alloc->id == 0) + { + /* failed to allocate a secure_id */ + goto out3; + } + + alloc->management_flags |= UMP_MGMT_EXTERNAL; + + return alloc; + +out3: + kfree(alloc->block_array); +out2: + kfree(alloc); +out1: + return UMP_DD_INVALID_MEMORY_HANDLE; +} + + +/* + * UMP v1 API + */ +UMP_KERNEL_API_EXPORT ump_dd_handle ump_dd_handle_create_from_phys_blocks(ump_dd_physical_block * blocks, unsigned long num_blocks) +{ + ump_dd_handle mem; + ump_dd_physical_block_64 *block_64_array; + ump_alloc_flags flags = UMP_V1_API_DEFAULT_ALLOCATION_FLAGS; + unsigned long i; + + UMP_ASSERT(blocks); + UMP_ASSERT(num_blocks); + + block_64_array = kzalloc(num_blocks * sizeof(*block_64_array), __GFP_HARDWALL | GFP_KERNEL); + + if(block_64_array == NULL) + { + return UMP_DD_INVALID_MEMORY_HANDLE; + } + + /* copy physical blocks */ + for( i = 0; i < num_blocks; i++) + { + block_64_array[i].addr = blocks[i].addr; + block_64_array[i].size = blocks[i].size; + } + + mem = ump_dd_create_from_phys_blocks_64(block_64_array, num_blocks, flags, NULL, NULL, NULL); + + kfree(block_64_array); + + return mem; + +} diff --git a/drivers/base/ump/src/common/ump_kernel_core.h b/drivers/base/ump/src/common/ump_kernel_core.h new file mode 100755 index 000000000000..8cb424d2893a --- /dev/null +++ b/drivers/base/ump/src/common/ump_kernel_core.h @@ -0,0 +1,228 @@ +/* + * + * (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. + * + */ + + + + + +#ifndef _UMP_KERNEL_CORE_H_ +#define _UMP_KERNEL_CORE_H_ + + +#include <linux/mutex.h> +#include <linux/rwsem.h> +#include <asm/atomic.h> +#include <linux/slab.h> +#include <linux/list.h> +#include <linux/cred.h> +#include <asm/mmu_context.h> + +#ifdef CONFIG_KDS +#include <linux/kds.h> +#endif +#include <linux/ump-common.h> +#include <ump/src/common/ump_kernel_descriptor_mapping.h> + +/* forward decl */ +struct umpp_session; + +/** + * UMP handle metadata. + * Tracks various data about a handle not of any use to user space + */ +typedef enum +{ + UMP_MGMT_EXTERNAL = (1ul << 0) /**< Handle created via the ump_dd_create_from_phys_blocks interface */ + /* (1ul << 31) not to be used */ +} umpp_management_flags; + +/** + * Structure tracking the single global UMP device. + * Holds global data like the ID map + */ +typedef struct umpp_device +{ + struct mutex secure_id_map_lock; /**< Lock protecting access to the map */ + umpp_descriptor_mapping * secure_id_map; /**< Map of all known secure IDs on the system */ +} umpp_device; + +/** + * Structure tracking all memory allocations of a UMP allocation. + * Tracks info about an mapping so we can verify cache maintenace + * operations and help in the unmap cleanup. + */ +typedef struct umpp_cpu_mapping +{ + struct list_head link; /**< link to list of mappings for an allocation */ + void *vaddr_start; /**< CPU VA start of the mapping */ + size_t nr_pages; /**< Size (in pages) of the mapping */ + uint64_t page_off; /**< Offset (in pages) from start of the allocation where the mapping starts */ + ump_dd_handle handle; /**< Which handle this mapping is linked to */ + struct umpp_session * session; /**< Which session created the mapping */ +} umpp_cpu_mapping; + +/** + * Structure tracking UMP allocation. + * Represent a memory allocation with its ID. + * Tracks all needed meta-data about an allocation. + * */ +typedef struct umpp_allocation +{ + ump_secure_id id; /**< Secure ID of the allocation */ + atomic_t refcount; /**< Usage count */ + + ump_alloc_flags flags; /**< Flags for all supported devices */ + uint32_t management_flags; /**< Managment flags tracking */ + + pid_t owner; /**< The process ID owning the memory if not sharable */ + + ump_dd_security_filter filter_func; /**< Hook to verify use, called during retains from new clients */ + ump_dd_final_release_callback final_release_func; /**< Hook called when the last reference is removed */ + void* callback_data; /**< Additional data given to release hook */ + + uint64_t size; /**< Size (in bytes) of the allocation */ + uint64_t blocksCount; /**< Number of physsical blocks the allocation is built up of */ + ump_dd_physical_block_64 * block_array; /**< Array, one entry per block, describing block start and length */ + + struct mutex map_list_lock; /**< Lock protecting the map_list */ + struct list_head map_list; /**< Tracks all CPU VA mappings of this allocation */ + +#ifdef CONFIG_KDS + struct kds_resource kds_res; /**< The KDS resource controlling access to this allocation */ +#endif + + void * backendData; /**< Physical memory backend meta-data */ +} umpp_allocation; + +/** + * Structure tracking use of UMP memory by a session. + * Tracks the use of an allocation by a session so session termination can clean up any outstanding references. + * Also protects agains non-matched release calls from user space. + */ +typedef struct umpp_session_memory_usage +{ + ump_secure_id id; /**< ID being used. For quick look-up */ + ump_dd_handle mem; /**< Handle being used. */ + + /** + * Track how many times has the process retained this handle in the kernel. + * This should usually just be 1(allocated or resolved) or 2(mapped), + * but could be more if someone is playing with the low-level API + * */ + atomic_t process_usage_count; + + struct list_head link; /**< link to other usage trackers for a session */ +} umpp_session_memory_usage; + +/** + * Structure representing a session/client. + * Tracks the UMP allocations being used by this client. + */ +typedef struct umpp_session +{ + struct mutex session_lock; /**< Lock for memory usage manipulation */ + struct list_head memory_usage; /**< list of memory currently being used by the this session */ + void* import_handler_data[UMPP_EXTERNAL_MEM_COUNT]; /**< Import modules per-session data pointer */ +} umpp_session; + +/** + * UMP core setup. + * Called by any OS specific startup function to initialize the common part. + * @return UMP_OK if core initialized correctly, any other value for errors + */ +ump_result umpp_core_constructor(void); + +/** + * UMP core teardown. + * Called by any OS specific unload function to clean up the common part. + */ +void umpp_core_destructor(void); + +/** + * UMP session start. + * Called by any OS specific session handler when a new session is detected + * @return Non-NULL if a matching core session could be set up. NULL on failure + */ +umpp_session *umpp_core_session_start(void); + +/** + * UMP session end. + * Called by any OS specific session handler when a session is ended/terminated. + * @param session The core session object returned by ump_core_session_start + */ +void umpp_core_session_end(umpp_session *session); + +/** + * Find a mapping object (if any) for this allocation. + * Called by any function needing to identify a mapping from a user virtual address. + * Verifies that the whole range to be within a mapping object. + * @param alloc The UMP allocation to find a matching mapping object of + * @param uaddr User mapping address to find the mapping object for + * @param size Length of the mapping + * @return NULL on error (no match found), pointer to mapping object if match found + */ +umpp_cpu_mapping * umpp_dd_find_enclosing_mapping(umpp_allocation * alloc, void* uaddr, size_t size); + +/** + * Register a new mapping of an allocation. + * Called by functions creating a new mapping of an allocation, typically OS specific handlers. + * @param alloc The allocation object which has been mapped + * @param map Info about the mapping + */ +void umpp_dd_add_cpu_mapping(umpp_allocation * alloc, umpp_cpu_mapping * map); + +/** + * Remove and free mapping object from an allocation. + * @param alloc The allocation object to remove the mapping info from + * @param target The mapping object to remove + */ +void umpp_dd_remove_cpu_mapping(umpp_allocation * alloc, umpp_cpu_mapping * target); + +/** + * Helper to find a block in the blockArray which holds a given byte offset. + * @param alloc The allocation object to find the block in + * @param offset Offset (in bytes) from allocation start to find the block of + * @param[out] block_index Pointer to the index of the block matching + * @param[out] block_internal_offset Offset within the returned block of the searched offset + * @return 0 if a matching block was found, any other value for error + */ +int umpp_dd_find_start_block(const umpp_allocation * alloc, uint64_t offset, uint64_t * const block_index, uint64_t * const block_internal_offset); + +/** + * Cache maintenance helper. + * Performs the requested cache operation on the given handle. + * @param mem Allocation handle + * @param op Cache maintenance operation to perform + * @param address User mapping at which to do the operation + * @param size Length (in bytes) of the range to do the operation on + */ +void umpp_dd_cpu_msync_now(ump_dd_handle mem, ump_cpu_msync_op op, void * address, size_t size); + +/** + * Import module session early init. + * Calls session_begin on all installed import modules. + * @param session The core session object to initialized the import handler for + * */ +void umpp_import_handlers_init(umpp_session * session); + +/** + * Import module session cleanup. + * Calls session_end on all import modules bound to the session. + * @param session The core session object to initialized the import handler for + */ +void umpp_import_handlers_term(umpp_session * session); + +#endif /* _UMP_KERNEL_CORE_H_ */ + diff --git a/drivers/base/ump/src/common/ump_kernel_descriptor_mapping.c b/drivers/base/ump/src/common/ump_kernel_descriptor_mapping.c new file mode 100755 index 000000000000..c5b0d7440081 --- /dev/null +++ b/drivers/base/ump/src/common/ump_kernel_descriptor_mapping.c @@ -0,0 +1,162 @@ +/* + * + * (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 <common/ump_kernel_descriptor_mapping.h> +#include <common/ump_kernel_priv.h> + +#define MALI_PAD_INT(x) (((x) + (BITS_PER_LONG - 1)) & ~(BITS_PER_LONG - 1)) + +/** + * Allocate a descriptor table capable of holding 'count' mappings + * @param count Number of mappings in the table + * @return Pointer to a new table, NULL on error + */ +static umpp_descriptor_table * descriptor_table_alloc(unsigned int count); + +/** + * Free a descriptor table + * @param table The table to free + */ +static void descriptor_table_free(umpp_descriptor_table * table); + +umpp_descriptor_mapping * umpp_descriptor_mapping_create(unsigned int init_entries, unsigned int max_entries) +{ + umpp_descriptor_mapping * map = kzalloc(sizeof(umpp_descriptor_mapping), GFP_KERNEL); + + init_entries = MALI_PAD_INT(init_entries); + max_entries = MALI_PAD_INT(max_entries); + + if (NULL != map) + { + map->table = descriptor_table_alloc(init_entries); + if (NULL != map->table) + { + init_rwsem( &map->lock); + set_bit(0, map->table->usage); + map->max_nr_mappings_allowed = max_entries; + map->current_nr_mappings = init_entries; + return map; + + descriptor_table_free(map->table); + } + kfree(map); + } + return NULL; +} + +void umpp_descriptor_mapping_destroy(umpp_descriptor_mapping * map) +{ + UMP_ASSERT(NULL != map); + descriptor_table_free(map->table); + kfree(map); +} + +unsigned int umpp_descriptor_mapping_allocate(umpp_descriptor_mapping * map, void * target) +{ + int descriptor = 0; + UMP_ASSERT(NULL != map); + down_write( &map->lock); + descriptor = find_first_zero_bit(map->table->usage, map->current_nr_mappings); + if (descriptor == map->current_nr_mappings) + { + /* no free descriptor, try to expand the table */ + umpp_descriptor_table * new_table; + umpp_descriptor_table * old_table = map->table; + int nr_mappings_new = map->current_nr_mappings + BITS_PER_LONG; + + if (map->current_nr_mappings >= map->max_nr_mappings_allowed) + { + descriptor = 0; + goto unlock_and_exit; + } + + new_table = descriptor_table_alloc(nr_mappings_new); + if (NULL == new_table) + { + descriptor = 0; + goto unlock_and_exit; + } + + memcpy(new_table->usage, old_table->usage, (sizeof(unsigned long)*map->current_nr_mappings) / BITS_PER_LONG); + memcpy(new_table->mappings, old_table->mappings, map->current_nr_mappings * sizeof(void*)); + + map->table = new_table; + map->current_nr_mappings = nr_mappings_new; + descriptor_table_free(old_table); + } + + /* we have found a valid descriptor, set the value and usage bit */ + set_bit(descriptor, map->table->usage); + map->table->mappings[descriptor] = target; + +unlock_and_exit: + up_write(&map->lock); + return descriptor; +} + +int umpp_descriptor_mapping_lookup(umpp_descriptor_mapping * map, unsigned int descriptor, void** const target) +{ + int result = -EINVAL; + UMP_ASSERT(map); + UMP_ASSERT(target); + down_read(&map->lock); + if ( (descriptor > 0) && (descriptor < map->current_nr_mappings) && test_bit(descriptor, map->table->usage) ) + { + *target = map->table->mappings[descriptor]; + result = 0; + } + /* keep target untouched if the descriptor was not found */ + up_read(&map->lock); + return result; +} + +void umpp_descriptor_mapping_remove(umpp_descriptor_mapping * map, unsigned int descriptor) +{ + UMP_ASSERT(map); + down_write(&map->lock); + if ( (descriptor > 0) && (descriptor < map->current_nr_mappings) && test_bit(descriptor, map->table->usage) ) + { + map->table->mappings[descriptor] = NULL; + clear_bit(descriptor, map->table->usage); + } + up_write(&map->lock); +} + +static umpp_descriptor_table * descriptor_table_alloc(unsigned int count) +{ + umpp_descriptor_table * table; + + table = kzalloc(sizeof(umpp_descriptor_table) + ((sizeof(unsigned long) * count)/BITS_PER_LONG) + (sizeof(void*) * count), __GFP_HARDWALL | GFP_KERNEL ); + + if (NULL != table) + { + table->usage = (unsigned long*)((u8*)table + sizeof(umpp_descriptor_table)); + table->mappings = (void**)((u8*)table + sizeof(umpp_descriptor_table) + ((sizeof(unsigned long) * count)/BITS_PER_LONG)); + } + + return table; +} + +static void descriptor_table_free(umpp_descriptor_table * table) +{ + UMP_ASSERT(table); + kfree(table); +} + diff --git a/drivers/base/ump/src/common/ump_kernel_descriptor_mapping.h b/drivers/base/ump/src/common/ump_kernel_descriptor_mapping.h new file mode 100755 index 000000000000..d06c1455371a --- /dev/null +++ b/drivers/base/ump/src/common/ump_kernel_descriptor_mapping.h @@ -0,0 +1,94 @@ +/* + * + * (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. + * + */ + + + + + +/** + * @file ump_kernel_descriptor_mapping.h + */ + +#ifndef _UMP_KERNEL_DESCRIPTOR_MAPPING_H_ +#define _UMP_KERNEL_DESCRIPTOR_MAPPING_H_ + +#include <linux/rwsem.h> +#include <linux/slab.h> +/** + * The actual descriptor mapping table, never directly accessed by clients + */ +typedef struct umpp_descriptor_table +{ + /* keep as a unsigned long to rely on the OS's bitops support */ + unsigned long * usage; /**< Pointer to bitpattern indicating if a descriptor is valid/used(1) or not(0) */ + void** mappings; /**< Array of the pointers the descriptors map to */ +} umpp_descriptor_table; + +/** + * The descriptor mapping object + * Provides a separate namespace where we can map an integer to a pointer + */ +typedef struct umpp_descriptor_mapping +{ + struct rw_semaphore lock; /**< Lock protecting access to the mapping object */ + unsigned int max_nr_mappings_allowed; /**< Max number of mappings to support in this namespace */ + unsigned int current_nr_mappings; /**< Current number of possible mappings */ + umpp_descriptor_table * table; /**< Pointer to the current mapping table */ +} umpp_descriptor_mapping; + +/** + * Create a descriptor mapping object. + * Create a descriptor mapping capable of holding init_entries growable to max_entries. + * ID 0 is reserved so the number of available entries will be max - 1. + * @param init_entries Number of entries to preallocate memory for + * @param max_entries Number of entries to max support + * @return Pointer to a descriptor mapping object, NULL on failure + */ +umpp_descriptor_mapping * umpp_descriptor_mapping_create(unsigned int init_entries, unsigned int max_entries); + +/** + * Destroy a descriptor mapping object + * @param[in] map The map to free + */ +void umpp_descriptor_mapping_destroy(umpp_descriptor_mapping * map); + +/** + * Allocate a new mapping entry (descriptor ID) + * Allocates a new entry in the map. + * @param[in] map The map to allocate a new entry in + * @param[in] target The value to map to + * @return The descriptor allocated, ID 0 on failure. + */ +unsigned int umpp_descriptor_mapping_allocate(umpp_descriptor_mapping * map, void * target); + +/** + * Get the value mapped to by a descriptor ID + * @param[in] map The map to lookup the descriptor id in + * @param[in] descriptor The descriptor ID to lookup + * @param[out] target Pointer to a pointer which will receive the stored value + * + * @return 0 on success lookup, -EINVAL on lookup failure. + */ +int umpp_descriptor_mapping_lookup(umpp_descriptor_mapping * map, unsigned int descriptor, void** const target); + +/** + * Free the descriptor ID + * For the descriptor to be reused it has to be freed + * @param[in] map The map to free the descriptor from + * @param descriptor The descriptor ID to free + */ +void umpp_descriptor_mapping_remove(umpp_descriptor_mapping * map, unsigned int descriptor); + +#endif /* _UMP_KERNEL_DESCRIPTOR_MAPPING_H_ */ diff --git a/drivers/base/ump/src/common/ump_kernel_priv.h b/drivers/base/ump/src/common/ump_kernel_priv.h new file mode 100755 index 000000000000..38b6f1b197fb --- /dev/null +++ b/drivers/base/ump/src/common/ump_kernel_priv.h @@ -0,0 +1,80 @@ +/* + * + * (C) COPYRIGHT 2008-2014 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. + * + */ + + + + + +#ifndef _UMP_KERNEL_PRIV_H_ +#define _UMP_KERNEL_PRIV_H_ + +#ifdef __KERNEL__ +#include <linux/dma-mapping.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <asm/cacheflush.h> +#endif + + +#define UMP_EXPECTED_IDS 64 +#define UMP_MAX_IDS 32768 + +#ifdef __KERNEL__ +#define UMP_ASSERT(expr) \ + if (!(expr)) { \ + printk(KERN_ERR "UMP: Assertion failed! %s,%s,%s,line=%d\n",\ + #expr,__FILE__,__func__,__LINE__); \ + BUG(); \ + } + +static inline void ump_sync_to_memory(uint64_t paddr, void* vaddr, size_t sz) +{ +#ifdef CONFIG_ARM + __cpuc_flush_dcache_area(vaddr, sz); + outer_flush_range(paddr, paddr+sz); +#elif defined(CONFIG_ARM64) + /*TODO (MID64-46): There's no other suitable cache flush function for ARM64 */ + flush_cache_all(); +#elif defined(CONFIG_X86) + struct scatterlist scl = {0, }; + sg_set_page(&scl, pfn_to_page(PFN_DOWN(paddr)), sz, + paddr & (PAGE_SIZE -1 )); + dma_sync_sg_for_cpu(NULL, &scl, 1, DMA_TO_DEVICE); + mb(); /* for outer_sync (if needed) */ +#else +#error Implement cache maintenance for your architecture here +#endif +} + +static inline void ump_sync_to_cpu(uint64_t paddr, void* vaddr, size_t sz) +{ +#ifdef CONFIG_ARM + __cpuc_flush_dcache_area(vaddr, sz); + outer_flush_range(paddr, paddr+sz); +#elif defined(CONFIG_ARM64) + /* TODO (MID64-46): There's no other suitable cache flush function for ARM64 */ + flush_cache_all(); +#elif defined(CONFIG_X86) + struct scatterlist scl = {0, }; + sg_set_page(&scl, pfn_to_page(PFN_DOWN(paddr)), sz, + paddr & (PAGE_SIZE -1 )); + dma_sync_sg_for_cpu(NULL, &scl, 1, DMA_FROM_DEVICE); +#else +#error Implement cache maintenance for your architecture here +#endif +} +#endif /* __KERNEL__*/ +#endif /* _UMP_KERNEL_PRIV_H_ */ + diff --git a/drivers/base/ump/src/imports/ion/Makefile b/drivers/base/ump/src/imports/ion/Makefile new file mode 100755 index 000000000000..ef74b273f7ad --- /dev/null +++ b/drivers/base/ump/src/imports/ion/Makefile @@ -0,0 +1,53 @@ +# +# (C) COPYRIGHT 2011, 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. +# +# + + + + +# default to building for the host +ARCH ?= $(shell uname -m) + +# linux build system integration + +ifneq ($(KERNELRELEASE),) +# Inside the kernel build system + +EXTRA_CFLAGS += -I$(KBUILD_EXTMOD) -I$(KBUILD_EXTMOD)/../../../../.. +KBUILD_EXTRA_SYMBOLS += "$(KBUILD_EXTMOD)/../../Module.symvers" + +SRC += ump_kernel_import_ion.c + +MODULE:=ump_ion_import.ko + +obj-m := $(MODULE:.ko=.o) +$(MODULE:.ko=-y) := $(SRC:.c=.o) +$(MODULE:.ko=-objs) := $(SRC:.c=.o) + +else +# Outside the kernel build system +# +# + +ifeq ($(KDIR),) +$(error Must specify KDIR to point to the kernel to target)) +endif + +all: + $(MAKE) ARCH=$(ARCH) -C $(KDIR) M=$(CURDIR) + +clean: + $(MAKE) ARCH=$(ARCH) -C $(KDIR) M=$(CURDIR) clean + +endif + diff --git a/drivers/base/ump/src/imports/ion/ump_kernel_import_ion.c b/drivers/base/ump/src/imports/ion/ump_kernel_import_ion.c new file mode 100755 index 000000000000..12b2e325b45e --- /dev/null +++ b/drivers/base/ump/src/imports/ion/ump_kernel_import_ion.c @@ -0,0 +1,204 @@ +/* + * + * (C) COPYRIGHT 2011-2014 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/dma-mapping.h> +#include "ion.h" +#include <linux/slab.h> +#include <linux/uaccess.h> +#include <linux/vmalloc.h> + +struct ion_wrapping_info +{ + struct ion_client * ion_client; + struct ion_handle * ion_handle; + int num_phys_blocks; + struct scatterlist * sglist; +}; + +static struct ion_device * ion_device_get(void) +{ + /* < Customer to provide implementation > + * Return a pointer to the global ion_device on the system + */ + return NULL; +} + +static int import_ion_client_create(void** const custom_session_data) +{ + struct ion_client ** ion_client; + + ion_client = (struct ion_client**)custom_session_data; + + *ion_client = ion_client_create(ion_device_get(), "ump"); + + return PTR_RET(*ion_client); +} + + +static void import_ion_client_destroy(void* custom_session_data) +{ + struct ion_client * ion_client; + + ion_client = (struct ion_client*)custom_session_data; + BUG_ON(!ion_client); + + ion_client_destroy(ion_client); +} + + +static void import_ion_final_release_callback(const ump_dd_handle handle, void * info) +{ + struct ion_wrapping_info * ion_info; + + BUG_ON(!info); + + (void)handle; + ion_info = (struct ion_wrapping_info*)info; + + dma_unmap_sg(NULL, ion_info->sglist, ion_info->num_phys_blocks, DMA_BIDIRECTIONAL); + + ion_free(ion_info->ion_client, ion_info->ion_handle); + kfree(ion_info); + module_put(THIS_MODULE); +} + +static ump_dd_handle import_ion_import(void * custom_session_data, void * pfd, ump_alloc_flags flags) +{ + int fd; + ump_dd_handle ump_handle; + struct scatterlist * sg; + int num_dma_blocks; + ump_dd_physical_block_64 * phys_blocks; + unsigned long i; + struct sg_table * sgt; + + struct ion_wrapping_info * ion_info; + + BUG_ON(!custom_session_data); + BUG_ON(!pfd); + + ion_info = kzalloc(GFP_KERNEL, sizeof(*ion_info)); + if (NULL == ion_info) + { + return UMP_DD_INVALID_MEMORY_HANDLE; + } + + ion_info->ion_client = (struct ion_client*)custom_session_data; + + if (get_user(fd, (int*)pfd)) + { + goto out; + } + + ion_info->ion_handle = ion_import_dma_buf(ion_info->ion_client, fd); + + if (IS_ERR_OR_NULL(ion_info->ion_handle)) + { + goto out; + } + + sgt = ion_sg_table(ion_info->ion_client, ion_info->ion_handle); + if (IS_ERR_OR_NULL(sgt)) + { + goto ion_dma_map_failed; + } + + ion_info->sglist = sgt->sgl; + + sg = ion_info->sglist; + while (sg) + { + ion_info->num_phys_blocks++; + sg = sg_next(sg); + } + + num_dma_blocks = dma_map_sg(NULL, ion_info->sglist, ion_info->num_phys_blocks, DMA_BIDIRECTIONAL); + + if (0 == num_dma_blocks) + { + goto linux_dma_map_failed; + } + + phys_blocks = vmalloc(num_dma_blocks * sizeof(*phys_blocks)); + if (NULL == phys_blocks) + { + goto vmalloc_failed; + } + + for_each_sg(ion_info->sglist, sg, num_dma_blocks, i) + { + phys_blocks[i].addr = sg_phys(sg); + phys_blocks[i].size = sg_dma_len(sg); + } + + ump_handle = ump_dd_create_from_phys_blocks_64(phys_blocks, num_dma_blocks, flags, NULL, import_ion_final_release_callback, ion_info); + + vfree(phys_blocks); + + if (ump_handle != UMP_DD_INVALID_MEMORY_HANDLE) + { + /* + * As we have a final release callback installed + * we must keep the module locked until + * the callback has been triggered + * */ + __module_get(THIS_MODULE); + return ump_handle; + } + + /* failed*/ +vmalloc_failed: + dma_unmap_sg(NULL, ion_info->sglist, ion_info->num_phys_blocks, DMA_BIDIRECTIONAL); +linux_dma_map_failed: +ion_dma_map_failed: + ion_free(ion_info->ion_client, ion_info->ion_handle); +out: + kfree(ion_info); + return UMP_DD_INVALID_MEMORY_HANDLE; +} + +struct ump_import_handler import_handler_ion = +{ + .linux_module = THIS_MODULE, + .session_begin = import_ion_client_create, + .session_end = import_ion_client_destroy, + .import = import_ion_import +}; + +static int __init import_ion_initialize_module(void) +{ + /* register with UMP */ + return ump_import_module_register(UMP_EXTERNAL_MEM_TYPE_ION, &import_handler_ion); +} + +static void __exit import_ion_cleanup_module(void) +{ + /* unregister import handler */ + ump_import_module_unregister(UMP_EXTERNAL_MEM_TYPE_ION); +} + +/* Setup init and exit functions for this module */ +module_init(import_ion_initialize_module); +module_exit(import_ion_cleanup_module); + +/* And some module information */ +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("ARM Ltd."); +MODULE_VERSION("1.0"); diff --git a/drivers/base/ump/src/linux/ump_kernel_linux.c b/drivers/base/ump/src/linux/ump_kernel_linux.c new file mode 100755 index 000000000000..d6c3c5354907 --- /dev/null +++ b/drivers/base/ump/src/linux/ump_kernel_linux.c @@ -0,0 +1,831 @@ +/* + * + * (C) COPYRIGHT 2008-2014 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-ioctl.h> +#include <linux/ump.h> + +#include <asm/uaccess.h> /* copy_*_user */ +#include <linux/compat.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/device.h> /* class registration support */ + +#include <common/ump_kernel_core.h> + +#include "ump_kernel_linux_mem.h" +#include <ump_arch.h> + + +struct ump_linux_device +{ + struct cdev cdev; + struct class * ump_class; +}; + +/* Name of the UMP device driver */ +static char ump_dev_name[] = "ump"; /* should be const, but the functions we call requires non-cost */ + +/* Module parameter to control log level */ +int ump_debug_level = 2; +module_param(ump_debug_level, int, S_IRUSR | S_IWUSR | S_IWGRP | S_IRGRP | S_IROTH); /* rw-rw-r-- */ +MODULE_PARM_DESC(ump_debug_level, "Higher number, more dmesg output"); + +/* By default the module uses any available major, but it's possible to set it at load time to a specific number */ +int ump_major = 0; +module_param(ump_major, int, S_IRUGO); /* r--r--r-- */ +MODULE_PARM_DESC(ump_major, "Device major number"); + +#define UMP_REV_STRING "1.0" + +char * ump_revision = UMP_REV_STRING; +module_param(ump_revision, charp, S_IRUGO); /* r--r--r-- */ +MODULE_PARM_DESC(ump_revision, "Revision info"); + +static int umpp_linux_open(struct inode *inode, struct file *filp); +static int umpp_linux_release(struct inode *inode, struct file *filp); +#ifdef HAVE_UNLOCKED_IOCTL +static long umpp_linux_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); +#else +static int umpp_linux_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); +#endif + +/* This variable defines the file operations this UMP device driver offers */ +static struct file_operations ump_fops = +{ + .owner = THIS_MODULE, + .open = umpp_linux_open, + .release = umpp_linux_release, +#ifdef HAVE_UNLOCKED_IOCTL + .unlocked_ioctl = umpp_linux_ioctl, +#else + .ioctl = umpp_linux_ioctl, +#endif + .compat_ioctl = umpp_linux_ioctl, + .mmap = umpp_linux_mmap +}; + +/* import module handling */ +DEFINE_MUTEX(import_list_lock); +struct ump_import_handler * import_handlers[UMPP_EXTERNAL_MEM_COUNT]; + +/* The global variable containing the global device data */ +static struct ump_linux_device ump_linux_device; + +#define DBG_MSG(level, ...) do { \ +if ((level) <= ump_debug_level)\ +{\ +printk(KERN_DEBUG "UMP<" #level ">:\n" __VA_ARGS__);\ +} \ +} while (0) + +#define MSG_ERR(...) do{ \ +printk(KERN_ERR "UMP: ERR: %s\n %s()%4d\n", __FILE__, __func__ , __LINE__) ; \ +printk(KERN_ERR __VA_ARGS__); \ +printk(KERN_ERR "\n"); \ +} while(0) + +#define MSG(...) do{ \ +printk(KERN_INFO "UMP: " __VA_ARGS__);\ +} while (0) + +/* + * This function is called by Linux to initialize this module. + * All we do is initialize the UMP device driver. + */ +static int __init umpp_linux_initialize_module(void) +{ + ump_result err; + + err = umpp_core_constructor(); + if (UMP_OK != err) + { + MSG_ERR("UMP device driver init failed\n"); + return -ENOTTY; + } + + MSG("UMP device driver %s loaded\n", UMP_REV_STRING); + return 0; +} + + + +/* + * This function is called by Linux to unload/terminate/exit/cleanup this module. + * All we do is terminate the UMP device driver. + */ +static void __exit umpp_linux_cleanup_module(void) +{ + DBG_MSG(2, "Unloading UMP device driver\n"); + umpp_core_destructor(); + DBG_MSG(2, "Module unloaded\n"); +} + + + +/* + * Initialize the UMP device driver. + */ +ump_result umpp_device_initialize(void) +{ + int err; + dev_t dev = 0; + + if (0 == ump_major) + { + /* auto select a major */ + err = alloc_chrdev_region(&dev, 0, 1, ump_dev_name); + ump_major = MAJOR(dev); + } + else + { + /* use load time defined major number */ + dev = MKDEV(ump_major, 0); + err = register_chrdev_region(dev, 1, ump_dev_name); + } + + if (0 == err) + { + memset(&ump_linux_device, 0, sizeof(ump_linux_device)); + + /* initialize our char dev data */ + cdev_init(&ump_linux_device.cdev, &ump_fops); + ump_linux_device.cdev.owner = THIS_MODULE; + ump_linux_device.cdev.ops = &ump_fops; + + /* register char dev with the kernel */ + err = cdev_add(&ump_linux_device.cdev, dev, 1/*count*/); + if (0 == err) + { + + ump_linux_device.ump_class = class_create(THIS_MODULE, ump_dev_name); + if (IS_ERR(ump_linux_device.ump_class)) + { + err = PTR_ERR(ump_linux_device.ump_class); + } + else + { + struct device * mdev; + mdev = device_create(ump_linux_device.ump_class, NULL, dev, NULL, ump_dev_name); + if (!IS_ERR(mdev)) + { + return UMP_OK; + } + + err = PTR_ERR(mdev); + class_destroy(ump_linux_device.ump_class); + } + cdev_del(&ump_linux_device.cdev); + } + + unregister_chrdev_region(dev, 1); + } + + return UMP_ERROR; +} + + + +/* + * Terminate the UMP device driver + */ +void umpp_device_terminate(void) +{ + dev_t dev = MKDEV(ump_major, 0); + + device_destroy(ump_linux_device.ump_class, dev); + class_destroy(ump_linux_device.ump_class); + + /* unregister char device */ + cdev_del(&ump_linux_device.cdev); + + /* free major */ + unregister_chrdev_region(dev, 1); +} + + +static int umpp_linux_open(struct inode *inode, struct file *filp) +{ + umpp_session *session; + + session = umpp_core_session_start(); + if (NULL == session) + { + return -EFAULT; + } + + filp->private_data = session; + + return 0; +} + +static int umpp_linux_release(struct inode *inode, struct file *filp) +{ + umpp_session *session; + + session = filp->private_data; + + umpp_core_session_end(session); + + filp->private_data = NULL; + + return 0; +} + +/**************************/ +/*ioctl specific functions*/ +/**************************/ +static int do_ump_dd_allocate(umpp_session * session, ump_k_allocate * params) +{ + ump_dd_handle new_allocation; + new_allocation = ump_dd_allocate_64(params->size, params->alloc_flags, NULL, NULL, NULL); + + if (UMP_DD_INVALID_MEMORY_HANDLE != new_allocation) + { + umpp_session_memory_usage * tracker; + + tracker = kmalloc(sizeof(*tracker), GFP_KERNEL | __GFP_HARDWALL); + if (NULL != tracker) + { + /* update the return struct with the new ID */ + params->secure_id = ump_dd_secure_id_get(new_allocation); + + tracker->mem = new_allocation; + tracker->id = params->secure_id; + atomic_set(&tracker->process_usage_count, 1); + + /* link it into the session in-use list */ + mutex_lock(&session->session_lock); + list_add(&tracker->link, &session->memory_usage); + mutex_unlock(&session->session_lock); + + return 0; + } + ump_dd_release(new_allocation); + } + + printk(KERN_WARNING "UMP: Allocation FAILED\n"); + return -ENOMEM; +} + +static int do_ump_dd_retain(umpp_session * session, ump_k_retain * params) +{ + umpp_session_memory_usage * it; + + mutex_lock(&session->session_lock); + + /* try to find it on the session usage list */ + list_for_each_entry(it, &session->memory_usage, link) + { + if (it->id == params->secure_id) + { + /* found to already be in use */ + /* check for overflow */ + while(1) + { + int refcnt = atomic_read(&it->process_usage_count); + if (refcnt + 1 > 0) + { + /* add a process local ref */ + if(atomic_cmpxchg(&it->process_usage_count, refcnt, refcnt + 1) == refcnt) + { + mutex_unlock(&session->session_lock); + return 0; + } + } + else + { + /* maximum usage cap reached */ + mutex_unlock(&session->session_lock); + return -EBUSY; + } + } + } + } + /* try to look it up globally */ + + it = kmalloc(sizeof(*it), GFP_KERNEL); + + if (NULL != it) + { + it->mem = ump_dd_from_secure_id(params->secure_id); + if (UMP_DD_INVALID_MEMORY_HANDLE != it->mem) + { + /* found, add it to the session usage list */ + it->id = params->secure_id; + atomic_set(&it->process_usage_count, 1); + list_add(&it->link, &session->memory_usage); + } + else + { + /* not found */ + kfree(it); + it = NULL; + } + } + + mutex_unlock(&session->session_lock); + + return (NULL != it) ? 0 : -ENODEV; +} + + +static int do_ump_dd_release(umpp_session * session, ump_k_release * params) +{ + umpp_session_memory_usage * it; + int result = -ENODEV; + + mutex_lock(&session->session_lock); + + /* only do a release if found on the session list */ + list_for_each_entry(it, &session->memory_usage, link) + { + if (it->id == params->secure_id) + { + /* found, a valid call */ + result = 0; + + if (0 == atomic_sub_return(1, &it->process_usage_count)) + { + /* last ref in this process remove from the usage list and remove the underlying ref */ + list_del(&it->link); + ump_dd_release(it->mem); + kfree(it); + } + + break; + } + } + mutex_unlock(&session->session_lock); + + return result; +} + +static int do_ump_dd_sizequery(umpp_session * session, ump_k_sizequery * params) +{ + umpp_session_memory_usage * it; + int result = -ENODEV; + + mutex_lock(&session->session_lock); + + /* only valid if found on the session list */ + list_for_each_entry(it, &session->memory_usage, link) + { + if (it->id == params->secure_id) + { + /* found, a valid call */ + params->size = ump_dd_size_get_64(it->mem); + result = 0; + break; + } + + } + mutex_unlock(&session->session_lock); + + return result; +} + +static int do_ump_dd_allocation_flags_get(umpp_session * session, ump_k_allocation_flags * params) +{ + umpp_session_memory_usage * it; + int result = -ENODEV; + + mutex_lock(&session->session_lock); + + /* only valid if found on the session list */ + list_for_each_entry(it, &session->memory_usage, link) + { + if (it->id == params->secure_id) + { + /* found, a valid call */ + params->alloc_flags = ump_dd_allocation_flags_get(it->mem); + result = 0; + break; + } + + } + mutex_unlock(&session->session_lock); + + return result; +} + +static int do_ump_dd_msync_now(umpp_session * session, ump_k_msync * params) +{ + umpp_session_memory_usage * it; + int result = -ENODEV; + + mutex_lock(&session->session_lock); + + /* only valid if found on the session list */ + list_for_each_entry(it, &session->memory_usage, link) + { + if (it->id == params->secure_id) + { + /* found, do the cache op */ +#ifdef CONFIG_COMPAT + if (is_compat_task()) + { + umpp_dd_cpu_msync_now(it->mem, params->cache_operation, compat_ptr(params->mapped_ptr.compat_value), params->size); + result = 0; + } + else + { +#endif + umpp_dd_cpu_msync_now(it->mem, params->cache_operation, params->mapped_ptr.value, params->size); + result = 0; +#ifdef CONFIG_COMPAT + } +#endif + break; + } + } + mutex_unlock(&session->session_lock); + + return result; +} + + +void umpp_import_handlers_init(umpp_session * session) +{ + int i; + mutex_lock(&import_list_lock); + for ( i = 1; i < UMPP_EXTERNAL_MEM_COUNT; i++ ) + { + if (import_handlers[i]) + { + import_handlers[i]->session_begin(&session->import_handler_data[i]); + /* It is OK if session_begin returned an error. + * We won't do any import calls if so */ + } + } + mutex_unlock(&import_list_lock); +} + +void umpp_import_handlers_term(umpp_session * session) +{ + int i; + mutex_lock(&import_list_lock); + for ( i = 1; i < UMPP_EXTERNAL_MEM_COUNT; i++ ) + { + /* only call if session_begin succeeded */ + if (session->import_handler_data[i] != NULL) + { + /* if session_beging succeeded the handler + * should not have unregistered with us */ + BUG_ON(!import_handlers[i]); + import_handlers[i]->session_end(session->import_handler_data[i]); + session->import_handler_data[i] = NULL; + } + } + mutex_unlock(&import_list_lock); +} + +int ump_import_module_register(enum ump_external_memory_type type, struct ump_import_handler * handler) +{ + int res = -EEXIST; + + /* validate input */ + BUG_ON(type == 0 || type >= UMPP_EXTERNAL_MEM_COUNT); + BUG_ON(!handler); + BUG_ON(!handler->linux_module); + BUG_ON(!handler->session_begin); + BUG_ON(!handler->session_end); + BUG_ON(!handler->import); + + mutex_lock(&import_list_lock); + + if (!import_handlers[type]) + { + import_handlers[type] = handler; + res = 0; + } + + mutex_unlock(&import_list_lock); + + return res; +} + +void ump_import_module_unregister(enum ump_external_memory_type type) +{ + BUG_ON(type == 0 || type >= UMPP_EXTERNAL_MEM_COUNT); + + mutex_lock(&import_list_lock); + /* an error to call this if ump_import_module_register didn't succeed */ + BUG_ON(!import_handlers[type]); + import_handlers[type] = NULL; + mutex_unlock(&import_list_lock); +} + +static struct ump_import_handler * import_handler_get(unsigned int type_id) +{ + enum ump_external_memory_type type; + struct ump_import_handler * handler; + + /* validate and convert input */ + /* handle bad data here, not just BUG_ON */ + if (type_id == 0 || type_id >= UMPP_EXTERNAL_MEM_COUNT) + return NULL; + + type = (enum ump_external_memory_type)type_id; + + /* find the handler */ + mutex_lock(&import_list_lock); + + handler = import_handlers[type]; + + if (handler) + { + if (!try_module_get(handler->linux_module)) + { + handler = NULL; + } + } + + mutex_unlock(&import_list_lock); + + return handler; +} + +static void import_handler_put(struct ump_import_handler * handler) +{ + module_put(handler->linux_module); +} + +static int do_ump_dd_import(umpp_session * session, ump_k_import * params) +{ + ump_dd_handle new_allocation = UMP_DD_INVALID_MEMORY_HANDLE; + struct ump_import_handler * handler; + + handler = import_handler_get(params->type); + + if (handler) + { + /* try late binding if not already bound */ + if (!session->import_handler_data[params->type]) + { + handler->session_begin(&session->import_handler_data[params->type]); + } + + /* do we have a bound session? */ + if (session->import_handler_data[params->type]) + { + new_allocation = handler->import( session->import_handler_data[params->type], + params->phandle.value, + params->alloc_flags); + } + + /* done with the handler */ + import_handler_put(handler); + } + + /* did the import succeed? */ + if (UMP_DD_INVALID_MEMORY_HANDLE != new_allocation) + { + umpp_session_memory_usage * tracker; + + tracker = kmalloc(sizeof(*tracker), GFP_KERNEL | __GFP_HARDWALL); + if (NULL != tracker) + { + /* update the return struct with the new ID */ + params->secure_id = ump_dd_secure_id_get(new_allocation); + + tracker->mem = new_allocation; + tracker->id = params->secure_id; + atomic_set(&tracker->process_usage_count, 1); + + /* link it into the session in-use list */ + mutex_lock(&session->session_lock); + list_add(&tracker->link, &session->memory_usage); + mutex_unlock(&session->session_lock); + + return 0; + } + ump_dd_release(new_allocation); + } + + return -ENOMEM; + +} + +#ifdef HAVE_UNLOCKED_IOCTL +static long umpp_linux_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +#else +static int umpp_linux_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) +#endif +{ + int ret; + uint64_t msg[(UMP_CALL_MAX_SIZE+7)>>3]; /* alignment fixup */ + uint32_t size = _IOC_SIZE(cmd); + struct umpp_session *session = filp->private_data; + +#ifndef HAVE_UNLOCKED_IOCTL + (void)inode; /* unused arg */ +#endif + + /* + * extract the type and number bitfields, and don't decode + * wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok() + */ + if (_IOC_TYPE(cmd) != UMP_IOC_MAGIC) + { + return -ENOTTY; + + } + if (_IOC_NR(cmd) > UMP_IOC_MAXNR) + { + return -ENOTTY; + } + + switch(cmd) + { + case UMP_FUNC_ALLOCATE: + if (size != sizeof(ump_k_allocate)) + { + return -ENOTTY; + } + if (copy_from_user(&msg, (void __user *)arg, size)) + { + return -EFAULT; + } + ret = do_ump_dd_allocate(session, (ump_k_allocate *)&msg); + if (ret) + { + return ret; + } + if (copy_to_user((void *)arg, &msg, size)) + { + return -EFAULT; + } + return 0; + case UMP_FUNC_SIZEQUERY: + if (size != sizeof(ump_k_sizequery)) + { + return -ENOTTY; + } + if (copy_from_user(&msg, (void __user *)arg, size)) + { + return -EFAULT; + } + ret = do_ump_dd_sizequery(session,(ump_k_sizequery*) &msg); + if (ret) + { + return ret; + } + if (copy_to_user((void *)arg, &msg, size)) + { + return -EFAULT; + } + return 0; + case UMP_FUNC_MSYNC: + if (size != sizeof(ump_k_msync)) + { + return -ENOTTY; + } + if (copy_from_user(&msg, (void __user *)arg, size)) + { + return -EFAULT; + } + ret = do_ump_dd_msync_now(session,(ump_k_msync*) &msg); + if (ret) + { + return ret; + } + if (copy_to_user((void *)arg, &msg, size)) + { + return -EFAULT; + } + return 0; + case UMP_FUNC_IMPORT: + if (size != sizeof(ump_k_import)) + { + return -ENOTTY; + } + if (copy_from_user(&msg, (void __user*)arg, size)) + { + return -EFAULT; + } + ret = do_ump_dd_import(session, (ump_k_import*) &msg); + if (ret) + { + return ret; + } + if (copy_to_user((void *)arg, &msg, size)) + { + return -EFAULT; + } + return 0; + /* used only by v1 API */ + case UMP_FUNC_ALLOCATION_FLAGS_GET: + if (size != sizeof(ump_k_allocation_flags)) + { + return -ENOTTY; + } + if (copy_from_user(&msg, (void __user *)arg, size)) + { + return -EFAULT; + } + ret = do_ump_dd_allocation_flags_get(session,(ump_k_allocation_flags*) &msg); + if (ret) + { + return ret; + } + if (copy_to_user((void *)arg, &msg, size)) + { + return -EFAULT; + } + return 0; + case UMP_FUNC_RETAIN: + if (size != sizeof(ump_k_retain)) + { + return -ENOTTY; + } + if (copy_from_user(&msg, (void __user *)arg, size)) + { + return -EFAULT; + } + ret = do_ump_dd_retain(session,(ump_k_retain*) &msg); + if (ret) + { + return ret; + } + return 0; + case UMP_FUNC_RELEASE: + if (size != sizeof(ump_k_release)) + { + return -ENOTTY; + } + if (copy_from_user(&msg, (void __user *)arg, size)) + { + return -EFAULT; + } + ret = do_ump_dd_release(session,(ump_k_release*) &msg); + if (ret) + { + return ret; + } + return 0; + default: + /* not ours */ + return -ENOTTY; + } + /*redundant below*/ + return -ENOTTY; +} + + +/* Export UMP kernel space API functions */ +EXPORT_SYMBOL(ump_dd_allocate_64); +EXPORT_SYMBOL(ump_dd_allocation_flags_get); +EXPORT_SYMBOL(ump_dd_secure_id_get); +EXPORT_SYMBOL(ump_dd_from_secure_id); +EXPORT_SYMBOL(ump_dd_phys_blocks_get_64); +EXPORT_SYMBOL(ump_dd_size_get_64); +EXPORT_SYMBOL(ump_dd_retain); +EXPORT_SYMBOL(ump_dd_release); +EXPORT_SYMBOL(ump_dd_create_from_phys_blocks_64); +#ifdef CONFIG_KDS +EXPORT_SYMBOL(ump_dd_kds_resource_get); +#endif + +/* import API */ +EXPORT_SYMBOL(ump_import_module_register); +EXPORT_SYMBOL(ump_import_module_unregister); + + + +/* V1 API */ +EXPORT_SYMBOL(ump_dd_handle_create_from_secure_id); +EXPORT_SYMBOL(ump_dd_phys_block_count_get); +EXPORT_SYMBOL(ump_dd_phys_block_get); +EXPORT_SYMBOL(ump_dd_phys_blocks_get); +EXPORT_SYMBOL(ump_dd_size_get); +EXPORT_SYMBOL(ump_dd_reference_add); +EXPORT_SYMBOL(ump_dd_reference_release); +EXPORT_SYMBOL(ump_dd_handle_create_from_phys_blocks); + + +/* Setup init and exit functions for this module */ +module_init(umpp_linux_initialize_module); +module_exit(umpp_linux_cleanup_module); + +/* And some module informatio */ +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("ARM Ltd."); +MODULE_VERSION(UMP_REV_STRING); 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; +} + diff --git a/drivers/base/ump/src/linux/ump_kernel_linux_mem.h b/drivers/base/ump/src/linux/ump_kernel_linux_mem.h new file mode 100755 index 000000000000..4fbf3b2a9119 --- /dev/null +++ b/drivers/base/ump/src/linux/ump_kernel_linux_mem.h @@ -0,0 +1,26 @@ +/* + * + * (C) COPYRIGHT 2008-2011, 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. + * + */ + + + + + +#ifndef _UMP_KERNEL_LINUX_MEM_H_ +#define _UMP_KERNEL_LINUX_MEM_H_ + + +int umpp_linux_mmap(struct file * filp, struct vm_area_struct * vma); + +#endif /* _UMP_KERNEL_LINUX_MEM_H_ */ diff --git a/drivers/base/ump/src/ump_arch.h b/drivers/base/ump/src/ump_arch.h new file mode 100755 index 000000000000..2303d56505a7 --- /dev/null +++ b/drivers/base/ump/src/ump_arch.h @@ -0,0 +1,42 @@ +/* + * + * (C) COPYRIGHT 2010-2011, 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. + * + */ + + + + + +#ifndef _UMP_ARCH_H_ +#define _UMP_ARCH_H_ + +#include <common/ump_kernel_core.h> + +/** + * Device specific setup. + * Called by the UMP core code to to host OS/device specific setup. + * Typical use case is device node creation for talking to user space. + * @return UMP_OK on success, any other value on failure + */ +extern ump_result umpp_device_initialize(void); + +/** + * Device specific teardown. + * Undo any things done by ump_device_initialize. + */ +extern void umpp_device_terminate(void); + +extern int umpp_phys_commit(umpp_allocation * alloc); +extern void umpp_phys_free(umpp_allocation * alloc); + +#endif /* _UMP_ARCH_H_ */ |