| /* |
| * |
| * (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); |