2 * Copyright (C) 2012-2014 ARM Limited. All rights reserved.
4 * This program is free software and is provided to you under the terms of the GNU General Public License version 2
5 * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence.
7 * A copy of the licence is included with the program, and can also be obtained from Free Software
8 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
11 #include <linux/init.h>
12 #include <linux/module.h>
14 #include <linux/slab.h>
15 #include <linux/cdev.h>
16 #include <linux/device.h>
17 #include <asm/uaccess.h>
18 #include "umplock_ioctl.h"
19 #include <linux/sched.h>
21 #define MAX_ITEMS 1024
24 typedef struct lock_cmd_priv {
25 uint32_t msg[128]; /*ioctl args*/
26 u32 pid; /*process id*/
29 typedef struct lock_ref {
34 typedef struct umplock_item {
38 _lock_access_usage usage;
39 _lock_ref references[MAX_PIDS];
40 struct semaphore item_lock;
43 typedef struct umplock_device_private {
44 struct mutex item_list_lock;
46 umplock_item items[MAX_ITEMS];
48 } umplock_device_private;
50 struct umplock_device {
52 struct class *umplock_class;
55 static struct umplock_device umplock_device;
56 static umplock_device_private device;
57 static dev_t umplock_dev;
58 static char umplock_dev_name[] = "umplock";
60 int umplock_debug_level = 0;
61 module_param(umplock_debug_level, int, S_IRUSR | S_IWUSR | S_IWGRP | S_IRGRP | S_IROTH); /* rw-rw-r-- */
62 MODULE_PARM_DESC(umplock_debug_level, "set umplock_debug_level to print debug messages");
64 #define PDEBUG(level, fmt, args...) do { if ((level) <= umplock_debug_level) printk(KERN_DEBUG "umplock: " fmt, ##args); } while (0)
65 #define PERROR(fmt, args...) do { printk(KERN_ERR "umplock: " fmt, ##args); } while (0)
67 int umplock_find_item(u32 secure_id)
70 for (i = 0; i < MAX_ITEMS; i++) {
71 if (device.items[i].secure_id == secure_id) {
79 static int umplock_find_item_by_pid(_lock_cmd_priv *lock_cmd, int *item_slot, int *ref_slot)
81 _lock_item_s *lock_item;
84 lock_item = (_lock_item_s *)&lock_cmd->msg;
86 i = umplock_find_item(lock_item->secure_id);
92 for (j = 0; j < MAX_PIDS; j++) {
93 if (device.items[i].references[j].pid == lock_cmd->pid) {
102 static int umplock_find_client_valid(u32 pid)
110 for (i = 0; i < MAX_PIDS; i++) {
111 if (device.pids[i] == pid) {
119 static int do_umplock_create_locked(_lock_cmd_priv *lock_cmd)
121 int i_index, ref_index;
123 _lock_item_s *lock_item = (_lock_item_s *)&lock_cmd->msg;
125 i_index = ref_index = -1;
127 ret = umplock_find_client_valid(lock_cmd->pid);
129 /*lock request from an invalid client pid, do nothing*/
133 ret = umplock_find_item_by_pid(lock_cmd, &i_index, &ref_index);
135 } else if ((i_index = umplock_find_item(lock_item->secure_id)) >= 0) {
136 for (ref_index = 0; ref_index < MAX_PIDS; ref_index++) {
137 if (device.items[i_index].references[ref_index].pid == 0) {
141 if (ref_index < MAX_PIDS) {
142 device.items[i_index].references[ref_index].pid = lock_cmd->pid;
143 device.items[i_index].references[ref_index].ref_count = 0;
145 PERROR("whoops, item ran out of available reference slots\n");
150 i_index = umplock_find_item(0);
153 device.items[i_index].secure_id = lock_item->secure_id;
154 device.items[i_index].id_ref_count = 0;
155 device.items[i_index].usage = lock_item->usage;
156 device.items[i_index].references[0].pid = lock_cmd->pid;
157 device.items[i_index].references[0].ref_count = 0;
158 sema_init(&device.items[i_index].item_lock, 1);
160 PERROR("whoops, ran out of available slots\n");
169 static int do_umplock_create(_lock_cmd_priv *lock_cmd)
174 static int do_umplock_process(_lock_cmd_priv *lock_cmd)
176 int ret, i_index, ref_index;
177 _lock_item_s *lock_item = (_lock_item_s *)&lock_cmd->msg;
179 mutex_lock(&device.item_list_lock);
181 if (0 == lock_item->secure_id) {
182 PERROR("IOCTL_UMPLOCK_PROCESS called with secure_id is 0, pid: %d\n", lock_cmd->pid);
183 mutex_unlock(&device.item_list_lock);
187 ret = do_umplock_create_locked(lock_cmd);
189 mutex_unlock(&device.item_list_lock);
193 ret = umplock_find_item_by_pid(lock_cmd, &i_index, &ref_index);
195 /*fail to find a item*/
196 PERROR("IOCTL_UMPLOCK_PROCESS called with invalid parameter, pid: %d\n", lock_cmd->pid);
197 mutex_unlock(&device.item_list_lock);
200 device.items[i_index].references[ref_index].ref_count++;
201 device.items[i_index].id_ref_count++;
202 PDEBUG(1, "try to lock, pid: %d, secure_id: 0x%x, ref_count: %d\n", lock_cmd->pid, lock_item->secure_id, device.items[i_index].references[ref_index].ref_count);
204 if (lock_cmd->pid == device.items[i_index].owner) {
205 PDEBUG(1, "already own the lock, pid: %d, secure_id: 0x%x, ref_count: %d\n", lock_cmd->pid, lock_item->secure_id, device.items[i_index].references[ref_index].ref_count);
206 mutex_unlock(&device.item_list_lock);
210 mutex_unlock(&device.item_list_lock);
211 if (down_interruptible(&device.items[i_index].item_lock)) {
212 /*wait up without hold the umplock. restore previous state and return*/
213 mutex_lock(&device.item_list_lock);
214 device.items[i_index].references[ref_index].ref_count--;
215 device.items[i_index].id_ref_count--;
216 if (0 == device.items[i_index].references[ref_index].ref_count) {
217 device.items[i_index].references[ref_index].pid = 0;
218 if (0 == device.items[i_index].id_ref_count) {
219 PDEBUG(1, "release item, pid: %d, secure_id: 0x%x\n", lock_cmd->pid, lock_item->secure_id);
220 device.items[i_index].secure_id = 0;
224 PERROR("failed lock, pid: %d, secure_id: 0x%x, ref_count: %d\n", lock_cmd->pid, lock_item->secure_id, device.items[i_index].references[ref_index].ref_count);
226 mutex_unlock(&device.item_list_lock);
230 mutex_lock(&device.item_list_lock);
231 PDEBUG(1, "got lock, pid: %d, secure_id: 0x%x, ref_count: %d\n", lock_cmd->pid, lock_item->secure_id, device.items[i_index].references[ref_index].ref_count);
232 device.items[i_index].owner = lock_cmd->pid;
233 mutex_unlock(&device.item_list_lock);
238 static int do_umplock_release(_lock_cmd_priv *lock_cmd)
240 int ret, i_index, ref_index;
241 _lock_item_s *lock_item = (_lock_item_s *)&lock_cmd->msg;
243 mutex_lock(&device.item_list_lock);
245 if (0 == lock_item->secure_id) {
246 PERROR("IOCTL_UMPLOCK_RELEASE called with secure_id is 0, pid: %d\n", lock_cmd->pid);
247 mutex_unlock(&device.item_list_lock);
251 ret = umplock_find_client_valid(lock_cmd->pid);
253 /*lock request from an invalid client pid, do nothing*/
254 mutex_unlock(&device.item_list_lock);
258 i_index = ref_index = -1;
260 ret = umplock_find_item_by_pid(lock_cmd, &i_index, &ref_index);
262 /*fail to find item*/
263 PERROR("IOCTL_UMPLOCK_RELEASE called with invalid parameter pid: %d, secid: 0x%x\n", lock_cmd->pid, lock_item->secure_id);
264 mutex_unlock(&device.item_list_lock);
268 /* if the lock is not owned by this process */
269 if (lock_cmd->pid != device.items[i_index].owner) {
270 mutex_unlock(&device.item_list_lock);
274 /* if the ref_count is 0, that means nothing to unlock, just return */
275 if (0 == device.items[i_index].references[ref_index].ref_count) {
276 mutex_unlock(&device.item_list_lock);
280 device.items[i_index].references[ref_index].ref_count--;
281 device.items[i_index].id_ref_count--;
282 PDEBUG(1, "unlock, pid: %d, secure_id: 0x%x, ref_count: %d\n", lock_cmd->pid, lock_item->secure_id, device.items[i_index].references[ref_index].ref_count);
284 if (0 == device.items[i_index].references[ref_index].ref_count) {
285 device.items[i_index].references[ref_index].pid = 0;
286 if (0 == device.items[i_index].id_ref_count) {
287 PDEBUG(1, "release item, pid: %d, secure_id: 0x%x\n", lock_cmd->pid, lock_item->secure_id);
288 device.items[i_index].secure_id = 0;
290 device.items[i_index].owner = 0;
291 up(&device.items[i_index].item_lock);
293 mutex_unlock(&device.item_list_lock);
298 static int do_umplock_zap(void)
302 PDEBUG(1, "ZAP ALL ENTRIES!\n");
304 mutex_lock(&device.item_list_lock);
306 for (i = 0; i < MAX_ITEMS; i++) {
307 device.items[i].secure_id = 0;
308 memset(&device.items[i].references, 0, sizeof(_lock_ref) * MAX_PIDS);
309 sema_init(&device.items[i].item_lock, 1);
312 for (i = 0; i < MAX_PIDS; i++) {
315 mutex_unlock(&device.item_list_lock);
320 static int do_umplock_dump(void)
324 mutex_lock(&device.item_list_lock);
325 PERROR("dump all the items begin\n");
326 for (i = 0; i < MAX_ITEMS; i++) {
327 for (j = 0; j < MAX_PIDS; j++) {
328 if (device.items[i].secure_id != 0 && device.items[i].references[j].pid != 0) {
329 PERROR("item[%d]->secure_id=0x%x, owner=%d\t reference[%d].ref_count=%d.pid=%d\n",
331 device.items[i].secure_id,
332 device.items[i].owner,
334 device.items[i].references[j].ref_count,
335 device.items[i].references[j].pid);
339 PERROR("dump all the items end\n");
340 mutex_unlock(&device.item_list_lock);
345 int do_umplock_client_add(_lock_cmd_priv *lock_cmd)
348 mutex_lock(&device.item_list_lock);
349 for (i = 0; i < MAX_PIDS; i++) {
350 if (device.pids[i] == lock_cmd->pid) {
351 mutex_unlock(&device.item_list_lock);
355 for (i = 0; i < MAX_PIDS; i++) {
356 if (device.pids[i] == 0) {
357 device.pids[i] = lock_cmd->pid;
361 mutex_unlock(&device.item_list_lock);
363 PERROR("Oops, Run out of client slots\n ");
369 int do_umplock_client_delete(_lock_cmd_priv *lock_cmd)
371 int p_index = -1, i_index = -1, ref_index = -1;
373 _lock_item_s *lock_item;
374 lock_item = (_lock_item_s *)&lock_cmd->msg;
376 mutex_lock(&device.item_list_lock);
377 p_index = umplock_find_client_valid(lock_cmd->pid);
378 /*lock item pid is not valid.*/
380 mutex_unlock(&device.item_list_lock);
384 /*walk through umplock item list and release reference attached to this client*/
385 for (i_index = 0; i_index < MAX_ITEMS; i_index++) {
386 lock_item->secure_id = device.items[i_index].secure_id;
388 /*find the item index and reference slot for the lock_item*/
389 ret = umplock_find_item_by_pid(lock_cmd, &i_index, &ref_index);
392 /*client has no reference on this umplock item, skip*/
395 while (device.items[i_index].references[ref_index].ref_count) {
396 /*release references on this client*/
398 PDEBUG(1, "delete client, pid: %d, ref_count: %d\n", lock_cmd->pid, device.items[i_index].references[ref_index].ref_count);
400 mutex_unlock(&device.item_list_lock);
401 do_umplock_release(lock_cmd);
402 mutex_lock(&device.item_list_lock);
406 /*remove the pid from umplock valid pid list*/
407 device.pids[p_index] = 0;
408 mutex_unlock(&device.item_list_lock);
413 static long umplock_driver_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
416 uint32_t size = _IOC_SIZE(cmd);
417 _lock_cmd_priv lock_cmd ;
419 if (_IOC_TYPE(cmd) != LOCK_IOCTL_GROUP) {
423 if (_IOC_NR(cmd) >= LOCK_IOCTL_MAX_CMDS) {
428 case LOCK_IOCTL_CREATE:
429 if (size != sizeof(_lock_item_s)) {
433 if (copy_from_user(&lock_cmd.msg, (void __user *)arg, size)) {
436 lock_cmd.pid = (u32)current->tgid;
437 ret = do_umplock_create(&lock_cmd);
443 case LOCK_IOCTL_PROCESS:
444 if (size != sizeof(_lock_item_s)) {
448 if (copy_from_user(&lock_cmd.msg, (void __user *)arg, size)) {
451 lock_cmd.pid = (u32)current->tgid;
452 return do_umplock_process(&lock_cmd);
454 case LOCK_IOCTL_RELEASE:
455 if (size != sizeof(_lock_item_s)) {
459 if (copy_from_user(&lock_cmd.msg, (void __user *)arg, size)) {
462 lock_cmd.pid = (u32)current->tgid;
463 ret = do_umplock_release(&lock_cmd);
473 case LOCK_IOCTL_DUMP:
481 static int umplock_driver_open(struct inode *inode, struct file *filp)
483 _lock_cmd_priv lock_cmd;
485 atomic_inc(&device.sessions);
486 PDEBUG(1, "OPEN SESSION (%i references)\n", atomic_read(&device.sessions));
488 lock_cmd.pid = (u32)current->tgid;
489 do_umplock_client_add(&lock_cmd);
494 static int umplock_driver_release(struct inode *inode, struct file *filp)
497 _lock_cmd_priv lock_cmd;
499 lock_cmd.pid = (u32)current->tgid;
500 do_umplock_client_delete(&lock_cmd);
502 mutex_lock(&device.item_list_lock);
503 atomic_dec(&device.sessions);
504 sessions = atomic_read(&device.sessions);
505 PDEBUG(1, "CLOSE SESSION (%i references)\n", sessions);
506 mutex_unlock(&device.item_list_lock);
514 static struct file_operations umplock_fops = {
515 .owner = THIS_MODULE,
516 .open = umplock_driver_open,
517 .release = umplock_driver_release,
518 .unlocked_ioctl = umplock_driver_ioctl,
521 int umplock_device_initialize(void)
525 err = alloc_chrdev_region(&umplock_dev, 0, 1, umplock_dev_name);
528 memset(&umplock_device, 0, sizeof(umplock_device));
529 cdev_init(&umplock_device.cdev, &umplock_fops);
530 umplock_device.cdev.owner = THIS_MODULE;
531 umplock_device.cdev.ops = &umplock_fops;
533 err = cdev_add(&umplock_device.cdev, umplock_dev, 1);
535 umplock_device.umplock_class = class_create(THIS_MODULE, umplock_dev_name);
536 if (IS_ERR(umplock_device.umplock_class)) {
537 err = PTR_ERR(umplock_device.umplock_class);
540 mdev = device_create(umplock_device.umplock_class, NULL, umplock_dev, NULL, umplock_dev_name);
542 return 0; /* all ok */
546 class_destroy(umplock_device.umplock_class);
548 cdev_del(&umplock_device.cdev);
551 unregister_chrdev_region(umplock_dev, 1);
553 PERROR("alloc chardev region failed\n");
559 void umplock_device_terminate(void)
561 device_destroy(umplock_device.umplock_class, umplock_dev);
562 class_destroy(umplock_device.umplock_class);
564 cdev_del(&umplock_device.cdev);
565 unregister_chrdev_region(umplock_dev, 1);
568 static int __init umplock_initialize_module(void)
570 PDEBUG(1, "Inserting UMP lock device driver. Compiled: %s, time: %s\n", __DATE__, __TIME__);
572 mutex_init(&device.item_list_lock);
573 if (umplock_device_initialize() != 0) {
574 PERROR("UMP lock device driver init failed\n");
577 memset(&device.items, 0, sizeof(umplock_item) * MAX_ITEMS);
578 memset(&device.pids, 0, sizeof(u32) * MAX_PIDS);
579 atomic_set(&device.sessions, 0);
581 PDEBUG(1, "UMP lock device driver loaded\n");
586 static void __exit umplock_cleanup_module(void)
588 PDEBUG(1, "unloading UMP lock module\n");
590 memset(&device.items, 0, sizeof(umplock_item) * MAX_ITEMS);
591 memset(&device.pids, 0, sizeof(u32) * MAX_PIDS);
592 umplock_device_terminate();
593 mutex_destroy(&device.item_list_lock);
595 PDEBUG(1, "UMP lock module unloaded\n");
598 module_init(umplock_initialize_module);
599 module_exit(umplock_cleanup_module);
602 MODULE_LICENSE("GPL");
603 MODULE_AUTHOR("ARM Ltd.");
604 MODULE_DESCRIPTION("ARM UMP locker");