3 * (C) COPYRIGHT 2008-2012 ARM Limited. All rights reserved.
5 * This program is free software and is provided to you under the terms of the GNU General Public License version 2
6 * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence.
8 * A copy of the licence is included with the program, and can also be obtained from Free Software
9 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15 #if UMP_LICENSE_IS_GPL
16 #define UMP_KERNEL_LINUX_LICENSE "GPL"
18 #define UMP_KERNEL_LINUX_LICENSE "Proprietary"
21 #include <ump/src/ump_ioctl.h>
22 #include <ump/ump_kernel_interface.h>
24 #include <asm/uaccess.h> /* copy_*_user */
25 #include <linux/module.h> /* kernel module definitions */
26 #include <linux/fs.h> /* file system operations */
27 #include <linux/cdev.h> /* character device definitions */
28 #include <linux/ioport.h> /* request_mem_region */
30 #if UMP_LICENSE_IS_GPL
31 #include <linux/device.h> /* class registration support */
34 #include <common/ump_kernel_core.h>
36 #include "ump_kernel_linux_mem.h"
40 struct ump_linux_device
43 #if UMP_LICENSE_IS_GPL
44 struct class * ump_class;
48 /* Name of the UMP device driver */
49 static char ump_dev_name[] = "ump"; /* should be const, but the functions we call requires non-cost */
51 /* Module parameter to control log level */
52 int ump_debug_level = 2;
53 module_param(ump_debug_level, int, S_IRUSR | S_IWUSR | S_IWGRP | S_IRGRP | S_IROTH); /* rw-rw-r-- */
54 MODULE_PARM_DESC(ump_debug_level, "Higher number, more dmesg output");
56 /* By default the module uses any available major, but it's possible to set it at load time to a specific number */
58 module_param(ump_major, int, S_IRUGO); /* r--r--r-- */
59 MODULE_PARM_DESC(ump_major, "Device major number");
61 char * ump_revision = UMP_SVN_REV_STRING;
62 module_param(ump_revision, charp, S_IRUGO); /* r--r--r-- */
63 MODULE_PARM_DESC(ump_revision, "Revision info");
65 static int umpp_linux_open(struct inode *inode, struct file *filp);
66 static int umpp_linux_release(struct inode *inode, struct file *filp);
67 #ifdef HAVE_UNLOCKED_IOCTL
68 static long umpp_linux_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
70 static int umpp_linux_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);
73 /* This variable defines the file operations this UMP device driver offers */
74 static struct file_operations ump_fops =
77 .open = umpp_linux_open,
78 .release = umpp_linux_release,
79 #ifdef HAVE_UNLOCKED_IOCTL
80 .unlocked_ioctl = umpp_linux_ioctl,
82 .ioctl = umpp_linux_ioctl,
84 .compat_ioctl = umpp_linux_ioctl,
85 .mmap = umpp_linux_mmap
88 /* import module handling */
89 DEFINE_MUTEX(import_list_lock);
90 struct ump_import_handler * import_handlers[UMPP_EXTERNAL_MEM_COUNT];
92 /* The global variable containing the global device data */
93 static struct ump_linux_device ump_linux_device;
95 #define DBG_MSG(level, ...) do { \
96 if ((level) <= ump_debug_level)\
98 printk(KERN_DEBUG "UMP<" #level ">:\n" __VA_ARGS__);\
102 #define MSG_ERR(...) do{ \
103 printk(KERN_ERR "UMP: ERR: %s\n %s()%4d\n", __FILE__, __func__ , __LINE__) ; \
104 printk(KERN_ERR __VA_ARGS__); \
105 printk(KERN_ERR "\n"); \
108 #define MSG(...) do{ \
109 printk(KERN_INFO "UMP: " __VA_ARGS__);\
113 * This function is called by Linux to initialize this module.
114 * All we do is initialize the UMP device driver.
116 static int __init umpp_linux_initialize_module(void)
120 DBG_MSG(2, "Inserting UMP device driver. Compiled: %s, time: %s\n", __DATE__, __TIME__);
122 err = umpp_core_constructor();
125 MSG_ERR("UMP device driver init failed\n");
129 MSG("UMP device driver %s loaded\n", UMP_SVN_REV_STRING);
137 * This function is called by Linux to unload/terminate/exit/cleanup this module.
138 * All we do is terminate the UMP device driver.
140 static void __exit umpp_linux_cleanup_module(void)
142 DBG_MSG(2, "Unloading UMP device driver\n");
143 umpp_core_destructor();
144 DBG_MSG(2, "Module unloaded\n");
150 * Initialize the UMP device driver.
152 ump_result umpp_device_initialize(void)
159 /* auto select a major */
160 err = alloc_chrdev_region(&dev, 0, 1, ump_dev_name);
161 ump_major = MAJOR(dev);
165 /* use load time defined major number */
166 dev = MKDEV(ump_major, 0);
167 err = register_chrdev_region(dev, 1, ump_dev_name);
172 memset(&ump_linux_device, 0, sizeof(ump_linux_device));
174 /* initialize our char dev data */
175 cdev_init(&ump_linux_device.cdev, &ump_fops);
176 ump_linux_device.cdev.owner = THIS_MODULE;
177 ump_linux_device.cdev.ops = &ump_fops;
179 /* register char dev with the kernel */
180 err = cdev_add(&ump_linux_device.cdev, dev, 1/*count*/);
184 #if UMP_LICENSE_IS_GPL
185 ump_linux_device.ump_class = class_create(THIS_MODULE, ump_dev_name);
186 if (IS_ERR(ump_linux_device.ump_class))
188 err = PTR_ERR(ump_linux_device.ump_class);
192 struct device * mdev;
193 mdev = device_create(ump_linux_device.ump_class, NULL, dev, NULL, ump_dev_name);
200 class_destroy(ump_linux_device.ump_class);
202 cdev_del(&ump_linux_device.cdev);
208 unregister_chrdev_region(dev, 1);
217 * Terminate the UMP device driver
219 void umpp_device_terminate(void)
221 dev_t dev = MKDEV(ump_major, 0);
223 #if UMP_LICENSE_IS_GPL
224 device_destroy(ump_linux_device.ump_class, dev);
225 class_destroy(ump_linux_device.ump_class);
228 /* unregister char device */
229 cdev_del(&ump_linux_device.cdev);
232 unregister_chrdev_region(dev, 1);
236 static int umpp_linux_open(struct inode *inode, struct file *filp)
238 umpp_session *session;
240 session = umpp_core_session_start();
246 filp->private_data = session;
251 static int umpp_linux_release(struct inode *inode, struct file *filp)
253 umpp_session *session;
255 session = filp->private_data;
257 umpp_core_session_end(session);
259 filp->private_data = NULL;
264 /**************************/
265 /*ioctl specific functions*/
266 /**************************/
267 static int do_ump_dd_allocate(umpp_session * session, ump_k_allocate * params)
269 ump_dd_handle new_allocation;
270 new_allocation = ump_dd_allocate_64(params->size, params->alloc_flags, NULL, NULL, NULL);
272 if (UMP_DD_INVALID_MEMORY_HANDLE != new_allocation)
274 umpp_session_memory_usage * tracker;
276 tracker = kmalloc(sizeof(*tracker), GFP_KERNEL | __GFP_HARDWALL);
279 /* update the return struct with the new ID */
280 params->secure_id = ump_dd_secure_id_get(new_allocation);
282 tracker->mem = new_allocation;
283 tracker->id = params->secure_id;
284 atomic_set(&tracker->process_usage_count, 1);
286 /* link it into the session in-use list */
287 mutex_lock(&session->session_lock);
288 list_add(&tracker->link, &session->memory_usage);
289 mutex_unlock(&session->session_lock);
293 ump_dd_release(new_allocation);
296 printk(KERN_WARNING "UMP: Allocation FAILED\n");
300 static int do_ump_dd_retain(umpp_session * session, ump_k_retain * params)
302 umpp_session_memory_usage * it;
304 mutex_lock(&session->session_lock);
306 /* try to find it on the session usage list */
307 list_for_each_entry(it, &session->memory_usage, link)
309 if (it->id == params->secure_id)
311 /* found to already be in use */
312 /* check for overflow */
315 int refcnt = atomic_read(&it->process_usage_count);
318 /* add a process local ref */
319 if(atomic_cmpxchg(&it->process_usage_count, refcnt, refcnt + 1) == refcnt)
321 mutex_unlock(&session->session_lock);
327 /* maximum usage cap reached */
328 mutex_unlock(&session->session_lock);
334 /* try to look it up globally */
336 it = kmalloc(sizeof(*it), GFP_KERNEL);
340 it->mem = ump_dd_from_secure_id(params->secure_id);
341 if (UMP_DD_INVALID_MEMORY_HANDLE != it->mem)
343 /* found, add it to the session usage list */
344 it->id = params->secure_id;
345 atomic_set(&it->process_usage_count, 1);
346 list_add(&it->link, &session->memory_usage);
356 mutex_unlock(&session->session_lock);
358 return (NULL != it) ? 0 : -ENODEV;
362 static int do_ump_dd_release(umpp_session * session, ump_k_release * params)
364 umpp_session_memory_usage * it;
365 int result = -ENODEV;
367 mutex_lock(&session->session_lock);
369 /* only do a release if found on the session list */
370 list_for_each_entry(it, &session->memory_usage, link)
372 if (it->id == params->secure_id)
374 /* found, a valid call */
377 if (0 == atomic_sub_return(1, &it->process_usage_count))
379 /* last ref in this process remove from the usage list and remove the underlying ref */
381 ump_dd_release(it->mem);
388 mutex_unlock(&session->session_lock);
393 static int do_ump_dd_sizequery(umpp_session * session, ump_k_sizequery * params)
395 umpp_session_memory_usage * it;
396 int result = -ENODEV;
398 mutex_lock(&session->session_lock);
400 /* only valid if found on the session list */
401 list_for_each_entry(it, &session->memory_usage, link)
403 if (it->id == params->secure_id)
405 /* found, a valid call */
406 params->size = ump_dd_size_get_64(it->mem);
412 mutex_unlock(&session->session_lock);
417 static int do_ump_dd_allocation_flags_get(umpp_session * session, ump_k_allocation_flags * params)
419 umpp_session_memory_usage * it;
420 int result = -ENODEV;
422 mutex_lock(&session->session_lock);
424 /* only valid if found on the session list */
425 list_for_each_entry(it, &session->memory_usage, link)
427 if (it->id == params->secure_id)
429 /* found, a valid call */
430 params->alloc_flags = ump_dd_allocation_flags_get(it->mem);
436 mutex_unlock(&session->session_lock);
441 static int do_ump_dd_msync_now(umpp_session * session, ump_k_msync * params)
443 umpp_session_memory_usage * it;
444 int result = -ENODEV;
446 mutex_lock(&session->session_lock);
448 /* only valid if found on the session list */
449 list_for_each_entry(it, &session->memory_usage, link)
451 if (it->id == params->secure_id)
453 /* found, do the cache op */
454 #if defined CONFIG_64BIT && CONFIG_64BIT
455 if (is_compat_task())
457 umpp_dd_cpu_msync_now(it->mem, params->cache_operation, params->mapped_ptr.compat_value, params->size);
463 umpp_dd_cpu_msync_now(it->mem, params->cache_operation, params->mapped_ptr.value, params->size);
465 #if defined CONFIG_64BIT && CONFIG_64BIT
471 mutex_unlock(&session->session_lock);
477 void umpp_import_handlers_init(umpp_session * session)
480 mutex_lock(&import_list_lock);
481 for ( i = 1; i < UMPP_EXTERNAL_MEM_COUNT; i++ )
483 if (import_handlers[i])
485 import_handlers[i]->session_begin(&session->import_handler_data[i]);
486 /* It is OK if session_begin returned an error.
487 * We won't do any import calls if so */
490 mutex_unlock(&import_list_lock);
493 void umpp_import_handlers_term(umpp_session * session)
496 mutex_lock(&import_list_lock);
497 for ( i = 1; i < UMPP_EXTERNAL_MEM_COUNT; i++ )
499 /* only call if session_begin succeeded */
500 if (session->import_handler_data[i] != NULL)
502 /* if session_beging succeeded the handler
503 * should not have unregistered with us */
504 BUG_ON(!import_handlers[i]);
505 import_handlers[i]->session_end(session->import_handler_data[i]);
506 session->import_handler_data[i] = NULL;
509 mutex_unlock(&import_list_lock);
512 int ump_import_module_register(enum ump_external_memory_type type, struct ump_import_handler * handler)
517 BUG_ON(type == 0 || type >= UMPP_EXTERNAL_MEM_COUNT);
519 #ifndef CONFIG_VITHAR
520 BUG_ON(!handler->linux_module);
522 BUG_ON(!handler->session_begin);
523 BUG_ON(!handler->session_end);
524 BUG_ON(!handler->import);
526 mutex_lock(&import_list_lock);
528 if (!import_handlers[type])
530 import_handlers[type] = handler;
534 mutex_unlock(&import_list_lock);
539 void ump_import_module_unregister(enum ump_external_memory_type type)
541 BUG_ON(type == 0 || type >= UMPP_EXTERNAL_MEM_COUNT);
543 mutex_lock(&import_list_lock);
544 /* an error to call this if ump_import_module_register didn't succeed */
545 BUG_ON(!import_handlers[type]);
546 import_handlers[type] = NULL;
547 mutex_unlock(&import_list_lock);
550 static struct ump_import_handler * import_handler_get(int type_id)
552 enum ump_external_memory_type type;
553 struct ump_import_handler * handler;
555 /* validate and convert input */
556 /* handle bad data here, not just BUG_ON */
557 if (type_id == 0 || type_id >= UMPP_EXTERNAL_MEM_COUNT)
560 type = (enum ump_external_memory_type)type_id;
562 /* find the handler */
563 mutex_lock(&import_list_lock);
565 handler = import_handlers[type];
569 if (!try_module_get(handler->linux_module))
575 mutex_unlock(&import_list_lock);
580 static void import_handler_put(struct ump_import_handler * handler)
582 module_put(handler->linux_module);
585 static int do_ump_dd_import(umpp_session * session, ump_k_import * params)
587 ump_dd_handle new_allocation = UMP_DD_INVALID_MEMORY_HANDLE;
588 struct ump_import_handler * handler;
590 handler = import_handler_get(params->type);
594 /* try late binding if not already bound */
595 if (!session->import_handler_data[params->type])
597 handler->session_begin(&session->import_handler_data[params->type]);
600 /* do we have a bound session? */
601 if (session->import_handler_data[params->type])
603 new_allocation = handler->import( session->import_handler_data[params->type],
604 params->phandle.value,
605 params->alloc_flags);
608 /* done with the handler */
609 import_handler_put(handler);
612 /* did the import succeed? */
613 if (UMP_DD_INVALID_MEMORY_HANDLE != new_allocation)
615 umpp_session_memory_usage * tracker;
617 tracker = kmalloc(sizeof(*tracker), GFP_KERNEL | __GFP_HARDWALL);
620 /* update the return struct with the new ID */
621 params->secure_id = ump_dd_secure_id_get(new_allocation);
623 tracker->mem = new_allocation;
624 tracker->id = params->secure_id;
625 atomic_set(&tracker->process_usage_count, 1);
627 /* link it into the session in-use list */
628 mutex_lock(&session->session_lock);
629 list_add(&tracker->link, &session->memory_usage);
630 mutex_unlock(&session->session_lock);
634 ump_dd_release(new_allocation);
641 #ifdef HAVE_UNLOCKED_IOCTL
642 static long umpp_linux_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
644 static int umpp_linux_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
648 uint64_t msg[(UMP_CALL_MAX_SIZE+7)>>3]; /* alignment fixup */
649 uint32_t size = _IOC_SIZE(cmd);
650 struct umpp_session *session = filp->private_data;
652 #ifndef HAVE_UNLOCKED_IOCTL
653 (void)inode; /* unused arg */
657 * extract the type and number bitfields, and don't decode
658 * wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok()
660 if (_IOC_TYPE(cmd) != UMP_IOC_MAGIC)
665 if (_IOC_NR(cmd) > UMP_IOC_MAXNR)
672 case UMP_FUNC_ALLOCATE:
673 if (size != sizeof(ump_k_allocate))
677 if (copy_from_user(&msg, (void __user *)arg, size))
681 ret = do_ump_dd_allocate(session, (ump_k_allocate *)&msg);
686 if (copy_to_user((void *)arg, &msg, size))
691 case UMP_FUNC_SIZEQUERY:
692 if (size != sizeof(ump_k_sizequery))
696 if (copy_from_user(&msg, (void __user *)arg, size))
700 ret = do_ump_dd_sizequery(session,(ump_k_sizequery*) &msg);
705 if (copy_to_user((void *)arg, &msg, size))
711 if (size != sizeof(ump_k_msync))
715 if (copy_from_user(&msg, (void __user *)arg, size))
719 ret = do_ump_dd_msync_now(session,(ump_k_msync*) &msg);
724 if (copy_to_user((void *)arg, &msg, size))
729 case UMP_FUNC_IMPORT:
730 if (size != sizeof(ump_k_import))
734 if (copy_from_user(&msg, (void __user*)arg, size))
738 ret = do_ump_dd_import(session, (ump_k_import*) &msg);
743 if (copy_to_user((void *)arg, &msg, size))
748 /* used only by v1 API */
749 case UMP_FUNC_ALLOCATION_FLAGS_GET:
750 if (size != sizeof(ump_k_allocation_flags))
754 if (copy_from_user(&msg, (void __user *)arg, size))
758 ret = do_ump_dd_allocation_flags_get(session,(ump_k_allocation_flags*) &msg);
763 if (copy_to_user((void *)arg, &msg, size))
768 case UMP_FUNC_RETAIN:
769 if (size != sizeof(ump_k_retain))
773 if (copy_from_user(&msg, (void __user *)arg, size))
777 ret = do_ump_dd_retain(session,(ump_k_retain*) &msg);
783 case UMP_FUNC_RELEASE:
784 if (size != sizeof(ump_k_release))
788 if (copy_from_user(&msg, (void __user *)arg, size))
792 ret = do_ump_dd_release(session,(ump_k_release*) &msg);
807 /* Export UMP kernel space API functions */
808 EXPORT_SYMBOL(ump_dd_allocate_64);
809 EXPORT_SYMBOL(ump_dd_allocation_flags_get);
810 EXPORT_SYMBOL(ump_dd_secure_id_get);
811 EXPORT_SYMBOL(ump_dd_from_secure_id);
812 EXPORT_SYMBOL(ump_dd_phys_blocks_get_64);
813 EXPORT_SYMBOL(ump_dd_size_get_64);
814 EXPORT_SYMBOL(ump_dd_retain);
815 EXPORT_SYMBOL(ump_dd_release);
816 EXPORT_SYMBOL(ump_dd_create_from_phys_blocks_64);
818 EXPORT_SYMBOL(ump_dd_kds_resource_get);
822 EXPORT_SYMBOL(ump_import_module_register);
823 EXPORT_SYMBOL(ump_import_module_unregister);
828 EXPORT_SYMBOL(ump_dd_handle_create_from_secure_id);
829 EXPORT_SYMBOL(ump_dd_phys_block_count_get);
830 EXPORT_SYMBOL(ump_dd_phys_block_get);
831 EXPORT_SYMBOL(ump_dd_phys_blocks_get);
832 EXPORT_SYMBOL(ump_dd_size_get);
833 EXPORT_SYMBOL(ump_dd_reference_add);
834 EXPORT_SYMBOL(ump_dd_reference_release);
835 EXPORT_SYMBOL(ump_dd_handle_create_from_phys_blocks);
838 /* Setup init and exit functions for this module */
839 module_init(umpp_linux_initialize_module);
840 module_exit(umpp_linux_cleanup_module);
842 /* And some module informatio */
843 MODULE_LICENSE(UMP_KERNEL_LINUX_LICENSE);
844 MODULE_AUTHOR("ARM Ltd.");
845 MODULE_VERSION(UMP_SVN_REV_STRING);