xref: /OK3568_Linux_fs/kernel/drivers/gpu/arm/mali400/umplock/umplock_driver.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun  * Copyright (C) 2012-2017 ARM Limited. All rights reserved.
3*4882a593Smuzhiyun  *
4*4882a593Smuzhiyun  * This program is free software and is provided to you under the terms of the GNU General Public License version 2
5*4882a593Smuzhiyun  * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence.
6*4882a593Smuzhiyun  *
7*4882a593Smuzhiyun  * A copy of the licence is included with the program, and can also be obtained from Free Software
8*4882a593Smuzhiyun  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
9*4882a593Smuzhiyun  */
10*4882a593Smuzhiyun 
11*4882a593Smuzhiyun #include <linux/init.h>
12*4882a593Smuzhiyun #include <linux/module.h>
13*4882a593Smuzhiyun #include <linux/fs.h>
14*4882a593Smuzhiyun #include <linux/slab.h>
15*4882a593Smuzhiyun #include <linux/cdev.h>
16*4882a593Smuzhiyun #include <linux/device.h>
17*4882a593Smuzhiyun #include <linux/uaccess.h>
18*4882a593Smuzhiyun #include "umplock_ioctl.h"
19*4882a593Smuzhiyun #include <linux/sched.h>
20*4882a593Smuzhiyun 
21*4882a593Smuzhiyun #define MAX_ITEMS 1024
22*4882a593Smuzhiyun #define MAX_PIDS 128
23*4882a593Smuzhiyun 
24*4882a593Smuzhiyun typedef struct lock_cmd_priv {
25*4882a593Smuzhiyun 	uint32_t msg[128];    /*ioctl args*/
26*4882a593Smuzhiyun 	u32 pid;              /*process id*/
27*4882a593Smuzhiyun } _lock_cmd_priv;
28*4882a593Smuzhiyun 
29*4882a593Smuzhiyun typedef struct lock_ref {
30*4882a593Smuzhiyun 	int ref_count;
31*4882a593Smuzhiyun 	u32 pid;
32*4882a593Smuzhiyun 	u32 down_count;
33*4882a593Smuzhiyun } _lock_ref;
34*4882a593Smuzhiyun 
35*4882a593Smuzhiyun typedef struct umplock_item {
36*4882a593Smuzhiyun 	u32 secure_id;
37*4882a593Smuzhiyun 	u32 id_ref_count;
38*4882a593Smuzhiyun 	u32 owner;
39*4882a593Smuzhiyun 	_lock_access_usage usage;
40*4882a593Smuzhiyun 	_lock_ref references[MAX_PIDS];
41*4882a593Smuzhiyun 	struct semaphore item_lock;
42*4882a593Smuzhiyun } umplock_item;
43*4882a593Smuzhiyun 
44*4882a593Smuzhiyun typedef struct umplock_device_private {
45*4882a593Smuzhiyun 	struct mutex item_list_lock;
46*4882a593Smuzhiyun 	atomic_t sessions;
47*4882a593Smuzhiyun 	umplock_item items[MAX_ITEMS];
48*4882a593Smuzhiyun 	u32 pids[MAX_PIDS];
49*4882a593Smuzhiyun } umplock_device_private;
50*4882a593Smuzhiyun 
51*4882a593Smuzhiyun struct umplock_device {
52*4882a593Smuzhiyun 	struct cdev cdev;
53*4882a593Smuzhiyun 	struct class *umplock_class;
54*4882a593Smuzhiyun };
55*4882a593Smuzhiyun 
56*4882a593Smuzhiyun static struct umplock_device umplock_device;
57*4882a593Smuzhiyun static umplock_device_private device;
58*4882a593Smuzhiyun static dev_t umplock_dev;
59*4882a593Smuzhiyun static char umplock_dev_name[] = "umplock";
60*4882a593Smuzhiyun 
61*4882a593Smuzhiyun int umplock_debug_level = 0;
62*4882a593Smuzhiyun module_param(umplock_debug_level, int, S_IRUSR | S_IWUSR | S_IWGRP | S_IRGRP | S_IROTH); /* rw-rw-r-- */
63*4882a593Smuzhiyun MODULE_PARM_DESC(umplock_debug_level, "set umplock_debug_level to print debug messages");
64*4882a593Smuzhiyun 
65*4882a593Smuzhiyun #define PDEBUG(level, fmt, args...) do { if ((level) <= umplock_debug_level) printk(KERN_DEBUG "umplock: " fmt, ##args); } while (0)
66*4882a593Smuzhiyun #define PERROR(fmt, args...) do { printk(KERN_ERR "umplock: " fmt, ##args); } while (0)
67*4882a593Smuzhiyun 
umplock_find_item(u32 secure_id)68*4882a593Smuzhiyun int umplock_find_item(u32 secure_id)
69*4882a593Smuzhiyun {
70*4882a593Smuzhiyun 	int i;
71*4882a593Smuzhiyun 	for (i = 0; i < MAX_ITEMS; i++) {
72*4882a593Smuzhiyun 		if (device.items[i].secure_id == secure_id) {
73*4882a593Smuzhiyun 			return i;
74*4882a593Smuzhiyun 		}
75*4882a593Smuzhiyun 	}
76*4882a593Smuzhiyun 
77*4882a593Smuzhiyun 	return -1;
78*4882a593Smuzhiyun }
79*4882a593Smuzhiyun 
umplock_find_item_by_pid(_lock_cmd_priv * lock_cmd,int * item_slot,int * ref_slot)80*4882a593Smuzhiyun static int umplock_find_item_by_pid(_lock_cmd_priv *lock_cmd, int *item_slot, int *ref_slot)
81*4882a593Smuzhiyun {
82*4882a593Smuzhiyun 	_lock_item_s *lock_item;
83*4882a593Smuzhiyun 	int i, j;
84*4882a593Smuzhiyun 
85*4882a593Smuzhiyun 	lock_item = (_lock_item_s *)&lock_cmd->msg;
86*4882a593Smuzhiyun 
87*4882a593Smuzhiyun 	i = umplock_find_item(lock_item->secure_id);
88*4882a593Smuzhiyun 
89*4882a593Smuzhiyun 	if (i < 0) {
90*4882a593Smuzhiyun 		return -1;
91*4882a593Smuzhiyun 	}
92*4882a593Smuzhiyun 
93*4882a593Smuzhiyun 	for (j = 0; j < MAX_PIDS; j++) {
94*4882a593Smuzhiyun 		if (device.items[i].references[j].pid == lock_cmd->pid) {
95*4882a593Smuzhiyun 			*item_slot = i;
96*4882a593Smuzhiyun 			*ref_slot = j;
97*4882a593Smuzhiyun 			return 0;
98*4882a593Smuzhiyun 		}
99*4882a593Smuzhiyun 	}
100*4882a593Smuzhiyun 	return -1 ;
101*4882a593Smuzhiyun }
102*4882a593Smuzhiyun 
umplock_find_client_valid(u32 pid)103*4882a593Smuzhiyun static int umplock_find_client_valid(u32 pid)
104*4882a593Smuzhiyun {
105*4882a593Smuzhiyun 	int i;
106*4882a593Smuzhiyun 
107*4882a593Smuzhiyun 	if (pid == 0) {
108*4882a593Smuzhiyun 		return -1;
109*4882a593Smuzhiyun 	}
110*4882a593Smuzhiyun 
111*4882a593Smuzhiyun 	for (i = 0; i < MAX_PIDS; i++) {
112*4882a593Smuzhiyun 		if (device.pids[i] == pid) {
113*4882a593Smuzhiyun 			return i;
114*4882a593Smuzhiyun 		}
115*4882a593Smuzhiyun 	}
116*4882a593Smuzhiyun 
117*4882a593Smuzhiyun 	return -1;
118*4882a593Smuzhiyun }
119*4882a593Smuzhiyun 
do_umplock_create_locked(_lock_cmd_priv * lock_cmd)120*4882a593Smuzhiyun static int do_umplock_create_locked(_lock_cmd_priv *lock_cmd)
121*4882a593Smuzhiyun {
122*4882a593Smuzhiyun 	int i_index, ref_index;
123*4882a593Smuzhiyun 	int ret;
124*4882a593Smuzhiyun 	_lock_item_s *lock_item = (_lock_item_s *)&lock_cmd->msg;
125*4882a593Smuzhiyun 
126*4882a593Smuzhiyun 	i_index = ref_index = -1;
127*4882a593Smuzhiyun 
128*4882a593Smuzhiyun 	ret = umplock_find_client_valid(lock_cmd->pid);
129*4882a593Smuzhiyun 	if (ret < 0) {
130*4882a593Smuzhiyun 		/*lock request from an invalid client pid, do nothing*/
131*4882a593Smuzhiyun 		return -EINVAL;
132*4882a593Smuzhiyun 	}
133*4882a593Smuzhiyun 
134*4882a593Smuzhiyun 	ret = umplock_find_item_by_pid(lock_cmd, &i_index, &ref_index);
135*4882a593Smuzhiyun 	if (ret >= 0) {
136*4882a593Smuzhiyun 	} else if ((i_index = umplock_find_item(lock_item->secure_id)) >= 0) {
137*4882a593Smuzhiyun 		for (ref_index = 0; ref_index < MAX_PIDS; ref_index++) {
138*4882a593Smuzhiyun 			if (device.items[i_index].references[ref_index].pid == 0) {
139*4882a593Smuzhiyun 				break;
140*4882a593Smuzhiyun 			}
141*4882a593Smuzhiyun 		}
142*4882a593Smuzhiyun 		if (ref_index < MAX_PIDS) {
143*4882a593Smuzhiyun 			device.items[i_index].references[ref_index].pid = lock_cmd->pid;
144*4882a593Smuzhiyun 			device.items[i_index].references[ref_index].ref_count = 0;
145*4882a593Smuzhiyun 			device.items[i_index].references[ref_index].down_count = 0;
146*4882a593Smuzhiyun 		} else {
147*4882a593Smuzhiyun 			PERROR("whoops, item ran out of available reference slots\n");
148*4882a593Smuzhiyun 			return -EINVAL;
149*4882a593Smuzhiyun 
150*4882a593Smuzhiyun 		}
151*4882a593Smuzhiyun 	} else {
152*4882a593Smuzhiyun 		i_index = umplock_find_item(0);
153*4882a593Smuzhiyun 
154*4882a593Smuzhiyun 		if (i_index >= 0) {
155*4882a593Smuzhiyun 			device.items[i_index].secure_id = lock_item->secure_id;
156*4882a593Smuzhiyun 			device.items[i_index].id_ref_count = 0;
157*4882a593Smuzhiyun 			device.items[i_index].usage = lock_item->usage;
158*4882a593Smuzhiyun 			device.items[i_index].references[0].pid = lock_cmd->pid;
159*4882a593Smuzhiyun 			device.items[i_index].references[0].ref_count = 0;
160*4882a593Smuzhiyun 			device.items[i_index].references[0].down_count = 0;
161*4882a593Smuzhiyun 			sema_init(&device.items[i_index].item_lock, 1);
162*4882a593Smuzhiyun 		} else {
163*4882a593Smuzhiyun 			PERROR("whoops, ran out of available slots\n");
164*4882a593Smuzhiyun 			return -EINVAL;
165*4882a593Smuzhiyun 		}
166*4882a593Smuzhiyun 	}
167*4882a593Smuzhiyun 
168*4882a593Smuzhiyun 	return 0;
169*4882a593Smuzhiyun }
170*4882a593Smuzhiyun /** IOCTLs **/
171*4882a593Smuzhiyun 
do_umplock_create(_lock_cmd_priv * lock_cmd)172*4882a593Smuzhiyun static int do_umplock_create(_lock_cmd_priv *lock_cmd)
173*4882a593Smuzhiyun {
174*4882a593Smuzhiyun 	return 0;
175*4882a593Smuzhiyun }
176*4882a593Smuzhiyun 
do_umplock_process(_lock_cmd_priv * lock_cmd)177*4882a593Smuzhiyun static int do_umplock_process(_lock_cmd_priv *lock_cmd)
178*4882a593Smuzhiyun {
179*4882a593Smuzhiyun 	int ret, i_index, ref_index;
180*4882a593Smuzhiyun 	_lock_item_s *lock_item = (_lock_item_s *)&lock_cmd->msg;
181*4882a593Smuzhiyun 
182*4882a593Smuzhiyun 	mutex_lock(&device.item_list_lock);
183*4882a593Smuzhiyun 
184*4882a593Smuzhiyun 	if (0 == lock_item->secure_id) {
185*4882a593Smuzhiyun 		PERROR("IOCTL_UMPLOCK_PROCESS called with secure_id is 0, pid: %d\n", lock_cmd->pid);
186*4882a593Smuzhiyun 		mutex_unlock(&device.item_list_lock);
187*4882a593Smuzhiyun 		return -EINVAL;
188*4882a593Smuzhiyun 	}
189*4882a593Smuzhiyun 
190*4882a593Smuzhiyun 	ret = do_umplock_create_locked(lock_cmd);
191*4882a593Smuzhiyun 	if (ret < 0) {
192*4882a593Smuzhiyun 		mutex_unlock(&device.item_list_lock);
193*4882a593Smuzhiyun 		return -EINVAL;
194*4882a593Smuzhiyun 	}
195*4882a593Smuzhiyun 
196*4882a593Smuzhiyun 	ret = umplock_find_item_by_pid(lock_cmd, &i_index, &ref_index);
197*4882a593Smuzhiyun 	if (ret < 0) {
198*4882a593Smuzhiyun 		/*fail to find a item*/
199*4882a593Smuzhiyun 		PERROR("IOCTL_UMPLOCK_PROCESS called with invalid parameter, pid: %d\n", lock_cmd->pid);
200*4882a593Smuzhiyun 		mutex_unlock(&device.item_list_lock);
201*4882a593Smuzhiyun 		return -EINVAL;
202*4882a593Smuzhiyun 	}
203*4882a593Smuzhiyun 	device.items[i_index].references[ref_index].ref_count++;
204*4882a593Smuzhiyun 	device.items[i_index].id_ref_count++;
205*4882a593Smuzhiyun 	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);
206*4882a593Smuzhiyun 
207*4882a593Smuzhiyun 	if (lock_cmd->pid == device.items[i_index].owner) {
208*4882a593Smuzhiyun 		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);
209*4882a593Smuzhiyun 		mutex_unlock(&device.item_list_lock);
210*4882a593Smuzhiyun 		return 0;
211*4882a593Smuzhiyun 	}
212*4882a593Smuzhiyun 
213*4882a593Smuzhiyun 	device.items[i_index].references[ref_index].down_count++;
214*4882a593Smuzhiyun 	mutex_unlock(&device.item_list_lock);
215*4882a593Smuzhiyun 	if (down_interruptible(&device.items[i_index].item_lock)) {
216*4882a593Smuzhiyun 		/*wait up without hold the umplock. restore previous state and return*/
217*4882a593Smuzhiyun 		mutex_lock(&device.item_list_lock);
218*4882a593Smuzhiyun 		device.items[i_index].references[ref_index].ref_count--;
219*4882a593Smuzhiyun 		device.items[i_index].id_ref_count--;
220*4882a593Smuzhiyun 		device.items[i_index].references[ref_index].down_count--;
221*4882a593Smuzhiyun 		if (0 == device.items[i_index].references[ref_index].ref_count) {
222*4882a593Smuzhiyun 			device.items[i_index].references[ref_index].pid = 0;
223*4882a593Smuzhiyun 			if (0 == device.items[i_index].id_ref_count) {
224*4882a593Smuzhiyun 				PDEBUG(1, "release item, pid: %d, secure_id: 0x%x\n", lock_cmd->pid, lock_item->secure_id);
225*4882a593Smuzhiyun 				device.items[i_index].secure_id = 0;
226*4882a593Smuzhiyun 			}
227*4882a593Smuzhiyun 		}
228*4882a593Smuzhiyun 
229*4882a593Smuzhiyun 		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);
230*4882a593Smuzhiyun 
231*4882a593Smuzhiyun 		mutex_unlock(&device.item_list_lock);
232*4882a593Smuzhiyun 		return -ERESTARTSYS;
233*4882a593Smuzhiyun 	}
234*4882a593Smuzhiyun 
235*4882a593Smuzhiyun 	mutex_lock(&device.item_list_lock);
236*4882a593Smuzhiyun 	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);
237*4882a593Smuzhiyun 	device.items[i_index].owner = lock_cmd->pid;
238*4882a593Smuzhiyun 	mutex_unlock(&device.item_list_lock);
239*4882a593Smuzhiyun 
240*4882a593Smuzhiyun 	return 0;
241*4882a593Smuzhiyun }
242*4882a593Smuzhiyun 
do_umplock_release(_lock_cmd_priv * lock_cmd)243*4882a593Smuzhiyun static int do_umplock_release(_lock_cmd_priv *lock_cmd)
244*4882a593Smuzhiyun {
245*4882a593Smuzhiyun 	int ret, i_index, ref_index, call_up;
246*4882a593Smuzhiyun 	_lock_item_s *lock_item = (_lock_item_s *)&lock_cmd->msg;
247*4882a593Smuzhiyun 
248*4882a593Smuzhiyun 	mutex_lock(&device.item_list_lock);
249*4882a593Smuzhiyun 
250*4882a593Smuzhiyun 	if (0 == lock_item->secure_id) {
251*4882a593Smuzhiyun 		PERROR("IOCTL_UMPLOCK_RELEASE called with secure_id is 0, pid: %d\n", lock_cmd->pid);
252*4882a593Smuzhiyun 		mutex_unlock(&device.item_list_lock);
253*4882a593Smuzhiyun 		return -EINVAL;
254*4882a593Smuzhiyun 	}
255*4882a593Smuzhiyun 
256*4882a593Smuzhiyun 	ret = umplock_find_client_valid(lock_cmd->pid);
257*4882a593Smuzhiyun 	if (ret < 0) {
258*4882a593Smuzhiyun 		/*lock request from an invalid client pid, do nothing*/
259*4882a593Smuzhiyun 		mutex_unlock(&device.item_list_lock);
260*4882a593Smuzhiyun 		return -EPERM;
261*4882a593Smuzhiyun 	}
262*4882a593Smuzhiyun 
263*4882a593Smuzhiyun 	i_index = ref_index = -1;
264*4882a593Smuzhiyun 
265*4882a593Smuzhiyun 	ret = umplock_find_item_by_pid(lock_cmd, &i_index, &ref_index);
266*4882a593Smuzhiyun 	if (ret < 0) {
267*4882a593Smuzhiyun 		/*fail to find item*/
268*4882a593Smuzhiyun 		PERROR("IOCTL_UMPLOCK_RELEASE called with invalid parameter pid: %d, secid: 0x%x\n", lock_cmd->pid, lock_item->secure_id);
269*4882a593Smuzhiyun 		mutex_unlock(&device.item_list_lock);
270*4882a593Smuzhiyun 		return -EINVAL;
271*4882a593Smuzhiyun 	}
272*4882a593Smuzhiyun 
273*4882a593Smuzhiyun 	/* if the lock is not owned by this process */
274*4882a593Smuzhiyun 	if (lock_cmd->pid != device.items[i_index].owner) {
275*4882a593Smuzhiyun 		mutex_unlock(&device.item_list_lock);
276*4882a593Smuzhiyun 		return -EPERM;
277*4882a593Smuzhiyun 	}
278*4882a593Smuzhiyun 
279*4882a593Smuzhiyun 	/* if the ref_count is 0, that means nothing to unlock, just return */
280*4882a593Smuzhiyun 	if (0 == device.items[i_index].references[ref_index].ref_count) {
281*4882a593Smuzhiyun 		mutex_unlock(&device.item_list_lock);
282*4882a593Smuzhiyun 		return 0;
283*4882a593Smuzhiyun 	}
284*4882a593Smuzhiyun 
285*4882a593Smuzhiyun 	device.items[i_index].references[ref_index].ref_count--;
286*4882a593Smuzhiyun 	device.items[i_index].id_ref_count--;
287*4882a593Smuzhiyun 	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);
288*4882a593Smuzhiyun 
289*4882a593Smuzhiyun 	call_up = 0;
290*4882a593Smuzhiyun 	if (device.items[i_index].references[ref_index].down_count > 1) {
291*4882a593Smuzhiyun 		call_up = 1;
292*4882a593Smuzhiyun 		device.items[i_index].references[ref_index].down_count--;
293*4882a593Smuzhiyun 	}
294*4882a593Smuzhiyun 	if (0 == device.items[i_index].references[ref_index].ref_count) {
295*4882a593Smuzhiyun 		device.items[i_index].references[ref_index].pid = 0;
296*4882a593Smuzhiyun 		if (0 == device.items[i_index].id_ref_count) {
297*4882a593Smuzhiyun 			PDEBUG(1, "release item, pid: %d, secure_id: 0x%x\n", lock_cmd->pid, lock_item->secure_id);
298*4882a593Smuzhiyun 			device.items[i_index].secure_id = 0;
299*4882a593Smuzhiyun 		}
300*4882a593Smuzhiyun 		device.items[i_index].owner = 0;
301*4882a593Smuzhiyun 		call_up = 1;
302*4882a593Smuzhiyun 	}
303*4882a593Smuzhiyun 	if (call_up) {
304*4882a593Smuzhiyun 		PDEBUG(1, "call up, pid: %d, secure_id: 0x%x\n", lock_cmd->pid, lock_item->secure_id);
305*4882a593Smuzhiyun 		up(&device.items[i_index].item_lock);
306*4882a593Smuzhiyun 	}
307*4882a593Smuzhiyun 	mutex_unlock(&device.item_list_lock);
308*4882a593Smuzhiyun 
309*4882a593Smuzhiyun 	return 0;
310*4882a593Smuzhiyun }
311*4882a593Smuzhiyun 
do_umplock_zap(void)312*4882a593Smuzhiyun static int do_umplock_zap(void)
313*4882a593Smuzhiyun {
314*4882a593Smuzhiyun 	int i;
315*4882a593Smuzhiyun 
316*4882a593Smuzhiyun 	PDEBUG(1, "ZAP ALL ENTRIES!\n");
317*4882a593Smuzhiyun 
318*4882a593Smuzhiyun 	mutex_lock(&device.item_list_lock);
319*4882a593Smuzhiyun 
320*4882a593Smuzhiyun 	for (i = 0; i < MAX_ITEMS; i++) {
321*4882a593Smuzhiyun 		device.items[i].secure_id = 0;
322*4882a593Smuzhiyun 		memset(&device.items[i].references, 0, sizeof(_lock_ref) * MAX_PIDS);
323*4882a593Smuzhiyun 		sema_init(&device.items[i].item_lock, 1);
324*4882a593Smuzhiyun 	}
325*4882a593Smuzhiyun 
326*4882a593Smuzhiyun 	for (i = 0; i < MAX_PIDS; i++) {
327*4882a593Smuzhiyun 		device.pids[i] = 0;
328*4882a593Smuzhiyun 	}
329*4882a593Smuzhiyun 	mutex_unlock(&device.item_list_lock);
330*4882a593Smuzhiyun 
331*4882a593Smuzhiyun 	return 0;
332*4882a593Smuzhiyun }
333*4882a593Smuzhiyun 
do_umplock_dump(void)334*4882a593Smuzhiyun static int do_umplock_dump(void)
335*4882a593Smuzhiyun {
336*4882a593Smuzhiyun 	int i, j;
337*4882a593Smuzhiyun 
338*4882a593Smuzhiyun 	mutex_lock(&device.item_list_lock);
339*4882a593Smuzhiyun 	PERROR("dump all the items begin\n");
340*4882a593Smuzhiyun 	for (i = 0; i < MAX_ITEMS; i++) {
341*4882a593Smuzhiyun 		for (j = 0; j < MAX_PIDS; j++) {
342*4882a593Smuzhiyun 			if (device.items[i].secure_id != 0 && device.items[i].references[j].pid != 0) {
343*4882a593Smuzhiyun 				PERROR("item[%d]->secure_id=0x%x, owner=%d\t reference[%d].ref_count=%d.pid=%d\n",
344*4882a593Smuzhiyun 				       i,
345*4882a593Smuzhiyun 				       device.items[i].secure_id,
346*4882a593Smuzhiyun 				       device.items[i].owner,
347*4882a593Smuzhiyun 				       j,
348*4882a593Smuzhiyun 				       device.items[i].references[j].ref_count,
349*4882a593Smuzhiyun 				       device.items[i].references[j].pid);
350*4882a593Smuzhiyun 			}
351*4882a593Smuzhiyun 		}
352*4882a593Smuzhiyun 	}
353*4882a593Smuzhiyun 	PERROR("dump all the items end\n");
354*4882a593Smuzhiyun 	mutex_unlock(&device.item_list_lock);
355*4882a593Smuzhiyun 
356*4882a593Smuzhiyun 	return 0;
357*4882a593Smuzhiyun }
358*4882a593Smuzhiyun 
do_umplock_client_add(_lock_cmd_priv * lock_cmd)359*4882a593Smuzhiyun int do_umplock_client_add(_lock_cmd_priv *lock_cmd)
360*4882a593Smuzhiyun {
361*4882a593Smuzhiyun 	int i;
362*4882a593Smuzhiyun 	mutex_lock(&device.item_list_lock);
363*4882a593Smuzhiyun 	for (i = 0; i < MAX_PIDS; i++) {
364*4882a593Smuzhiyun 		if (device.pids[i] == lock_cmd->pid) {
365*4882a593Smuzhiyun 			mutex_unlock(&device.item_list_lock);
366*4882a593Smuzhiyun 			return 0;
367*4882a593Smuzhiyun 		}
368*4882a593Smuzhiyun 	}
369*4882a593Smuzhiyun 	for (i = 0; i < MAX_PIDS; i++) {
370*4882a593Smuzhiyun 		if (device.pids[i] == 0) {
371*4882a593Smuzhiyun 			device.pids[i] = lock_cmd->pid;
372*4882a593Smuzhiyun 			break;
373*4882a593Smuzhiyun 		}
374*4882a593Smuzhiyun 	}
375*4882a593Smuzhiyun 	mutex_unlock(&device.item_list_lock);
376*4882a593Smuzhiyun 	if (i == MAX_PIDS) {
377*4882a593Smuzhiyun 		PERROR("Oops, Run out of client slots\n ");
378*4882a593Smuzhiyun 		return -EINVAL;
379*4882a593Smuzhiyun 	}
380*4882a593Smuzhiyun 	return 0;
381*4882a593Smuzhiyun }
382*4882a593Smuzhiyun 
do_umplock_client_delete(_lock_cmd_priv * lock_cmd)383*4882a593Smuzhiyun int do_umplock_client_delete(_lock_cmd_priv *lock_cmd)
384*4882a593Smuzhiyun {
385*4882a593Smuzhiyun 	int p_index = -1, i_index = -1, ref_index = -1;
386*4882a593Smuzhiyun 	int ret;
387*4882a593Smuzhiyun 	_lock_item_s *lock_item;
388*4882a593Smuzhiyun 	lock_item = (_lock_item_s *)&lock_cmd->msg;
389*4882a593Smuzhiyun 
390*4882a593Smuzhiyun 	mutex_lock(&device.item_list_lock);
391*4882a593Smuzhiyun 	p_index = umplock_find_client_valid(lock_cmd->pid);
392*4882a593Smuzhiyun 	/*lock item pid is not valid.*/
393*4882a593Smuzhiyun 	if (p_index < 0) {
394*4882a593Smuzhiyun 		mutex_unlock(&device.item_list_lock);
395*4882a593Smuzhiyun 		return 0;
396*4882a593Smuzhiyun 	}
397*4882a593Smuzhiyun 
398*4882a593Smuzhiyun 	/*walk through umplock item list and release reference attached to this client*/
399*4882a593Smuzhiyun 	for (i_index = 0; i_index < MAX_ITEMS; i_index++) {
400*4882a593Smuzhiyun 		lock_item->secure_id = device.items[i_index].secure_id;
401*4882a593Smuzhiyun 
402*4882a593Smuzhiyun 		/*find the item index and reference slot for the lock_item*/
403*4882a593Smuzhiyun 		ret = umplock_find_item_by_pid(lock_cmd, &i_index, &ref_index);
404*4882a593Smuzhiyun 
405*4882a593Smuzhiyun 		if (ret < 0) {
406*4882a593Smuzhiyun 			/*client has no reference on this umplock item, skip*/
407*4882a593Smuzhiyun 			continue;
408*4882a593Smuzhiyun 		}
409*4882a593Smuzhiyun 		while (device.items[i_index].references[ref_index].ref_count) {
410*4882a593Smuzhiyun 			/*release references on this client*/
411*4882a593Smuzhiyun 
412*4882a593Smuzhiyun 			PDEBUG(1, "delete client, pid: %d, ref_count: %d\n", lock_cmd->pid, device.items[i_index].references[ref_index].ref_count);
413*4882a593Smuzhiyun 
414*4882a593Smuzhiyun 			mutex_unlock(&device.item_list_lock);
415*4882a593Smuzhiyun 			do_umplock_release(lock_cmd);
416*4882a593Smuzhiyun 			mutex_lock(&device.item_list_lock);
417*4882a593Smuzhiyun 		}
418*4882a593Smuzhiyun 	}
419*4882a593Smuzhiyun 
420*4882a593Smuzhiyun 	/*remove the pid from umplock valid pid list*/
421*4882a593Smuzhiyun 	device.pids[p_index] = 0;
422*4882a593Smuzhiyun 	mutex_unlock(&device.item_list_lock);
423*4882a593Smuzhiyun 
424*4882a593Smuzhiyun 	return 0;
425*4882a593Smuzhiyun }
426*4882a593Smuzhiyun 
umplock_driver_ioctl(struct file * f,unsigned int cmd,unsigned long arg)427*4882a593Smuzhiyun static long umplock_driver_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
428*4882a593Smuzhiyun {
429*4882a593Smuzhiyun 	int ret;
430*4882a593Smuzhiyun 	uint32_t size = _IOC_SIZE(cmd);
431*4882a593Smuzhiyun 	_lock_cmd_priv lock_cmd ;
432*4882a593Smuzhiyun 
433*4882a593Smuzhiyun 	if (_IOC_TYPE(cmd) != LOCK_IOCTL_GROUP) {
434*4882a593Smuzhiyun 		return -ENOTTY;
435*4882a593Smuzhiyun 	}
436*4882a593Smuzhiyun 
437*4882a593Smuzhiyun 	if (_IOC_NR(cmd) >= LOCK_IOCTL_MAX_CMDS) {
438*4882a593Smuzhiyun 		return -ENOTTY;
439*4882a593Smuzhiyun 	}
440*4882a593Smuzhiyun 
441*4882a593Smuzhiyun 	switch (cmd) {
442*4882a593Smuzhiyun 	case LOCK_IOCTL_CREATE:
443*4882a593Smuzhiyun 		if (size != sizeof(_lock_item_s)) {
444*4882a593Smuzhiyun 			return -ENOTTY;
445*4882a593Smuzhiyun 		}
446*4882a593Smuzhiyun 
447*4882a593Smuzhiyun 		if (copy_from_user(&lock_cmd.msg, (void __user *)arg, size)) {
448*4882a593Smuzhiyun 			return -EFAULT;
449*4882a593Smuzhiyun 		}
450*4882a593Smuzhiyun 		lock_cmd.pid = (u32)current->tgid;
451*4882a593Smuzhiyun 		ret = do_umplock_create(&lock_cmd);
452*4882a593Smuzhiyun 		if (ret) {
453*4882a593Smuzhiyun 			return ret;
454*4882a593Smuzhiyun 		}
455*4882a593Smuzhiyun 		return 0;
456*4882a593Smuzhiyun 
457*4882a593Smuzhiyun 	case LOCK_IOCTL_PROCESS:
458*4882a593Smuzhiyun 		if (size != sizeof(_lock_item_s)) {
459*4882a593Smuzhiyun 			return -ENOTTY;
460*4882a593Smuzhiyun 		}
461*4882a593Smuzhiyun 
462*4882a593Smuzhiyun 		if (copy_from_user(&lock_cmd.msg, (void __user *)arg, size)) {
463*4882a593Smuzhiyun 			return -EFAULT;
464*4882a593Smuzhiyun 		}
465*4882a593Smuzhiyun 		lock_cmd.pid = (u32)current->tgid;
466*4882a593Smuzhiyun 		return do_umplock_process(&lock_cmd);
467*4882a593Smuzhiyun 
468*4882a593Smuzhiyun 	case LOCK_IOCTL_RELEASE:
469*4882a593Smuzhiyun 		if (size != sizeof(_lock_item_s)) {
470*4882a593Smuzhiyun 			return -ENOTTY;
471*4882a593Smuzhiyun 		}
472*4882a593Smuzhiyun 
473*4882a593Smuzhiyun 		if (copy_from_user(&lock_cmd.msg, (void __user *)arg, size)) {
474*4882a593Smuzhiyun 			return -EFAULT;
475*4882a593Smuzhiyun 		}
476*4882a593Smuzhiyun 		lock_cmd.pid = (u32)current->tgid;
477*4882a593Smuzhiyun 		ret = do_umplock_release(&lock_cmd);
478*4882a593Smuzhiyun 		if (ret) {
479*4882a593Smuzhiyun 			return ret;
480*4882a593Smuzhiyun 		}
481*4882a593Smuzhiyun 		return 0;
482*4882a593Smuzhiyun 
483*4882a593Smuzhiyun 	case LOCK_IOCTL_ZAP:
484*4882a593Smuzhiyun 		do_umplock_zap();
485*4882a593Smuzhiyun 		return 0;
486*4882a593Smuzhiyun 
487*4882a593Smuzhiyun 	case LOCK_IOCTL_DUMP:
488*4882a593Smuzhiyun 		do_umplock_dump();
489*4882a593Smuzhiyun 		return 0;
490*4882a593Smuzhiyun 	}
491*4882a593Smuzhiyun 
492*4882a593Smuzhiyun 	return -ENOIOCTLCMD;
493*4882a593Smuzhiyun }
494*4882a593Smuzhiyun 
umplock_driver_open(struct inode * inode,struct file * filp)495*4882a593Smuzhiyun static int umplock_driver_open(struct inode *inode, struct file *filp)
496*4882a593Smuzhiyun {
497*4882a593Smuzhiyun 	_lock_cmd_priv lock_cmd;
498*4882a593Smuzhiyun 
499*4882a593Smuzhiyun 	atomic_inc(&device.sessions);
500*4882a593Smuzhiyun 	PDEBUG(1, "OPEN SESSION (%i references)\n", atomic_read(&device.sessions));
501*4882a593Smuzhiyun 
502*4882a593Smuzhiyun 	lock_cmd.pid = (u32)current->tgid;
503*4882a593Smuzhiyun 	do_umplock_client_add(&lock_cmd);
504*4882a593Smuzhiyun 
505*4882a593Smuzhiyun 	return 0;
506*4882a593Smuzhiyun }
507*4882a593Smuzhiyun 
umplock_driver_release(struct inode * inode,struct file * filp)508*4882a593Smuzhiyun static int umplock_driver_release(struct inode *inode, struct file *filp)
509*4882a593Smuzhiyun {
510*4882a593Smuzhiyun 	int sessions = 0;
511*4882a593Smuzhiyun 	_lock_cmd_priv lock_cmd;
512*4882a593Smuzhiyun 
513*4882a593Smuzhiyun 	lock_cmd.pid = (u32)current->tgid;
514*4882a593Smuzhiyun 	do_umplock_client_delete(&lock_cmd);
515*4882a593Smuzhiyun 
516*4882a593Smuzhiyun 	mutex_lock(&device.item_list_lock);
517*4882a593Smuzhiyun 	atomic_dec(&device.sessions);
518*4882a593Smuzhiyun 	sessions = atomic_read(&device.sessions);
519*4882a593Smuzhiyun 	PDEBUG(1, "CLOSE SESSION (%i references)\n", sessions);
520*4882a593Smuzhiyun 	mutex_unlock(&device.item_list_lock);
521*4882a593Smuzhiyun 	if (sessions == 0) {
522*4882a593Smuzhiyun 		do_umplock_zap();
523*4882a593Smuzhiyun 	}
524*4882a593Smuzhiyun 
525*4882a593Smuzhiyun 	return 0;
526*4882a593Smuzhiyun }
527*4882a593Smuzhiyun 
528*4882a593Smuzhiyun static struct file_operations umplock_fops = {
529*4882a593Smuzhiyun 	.owner   = THIS_MODULE,
530*4882a593Smuzhiyun 	.open    = umplock_driver_open,
531*4882a593Smuzhiyun 	.release = umplock_driver_release,
532*4882a593Smuzhiyun 	.unlocked_ioctl = umplock_driver_ioctl,
533*4882a593Smuzhiyun };
534*4882a593Smuzhiyun 
umplock_device_initialize(void)535*4882a593Smuzhiyun int umplock_device_initialize(void)
536*4882a593Smuzhiyun {
537*4882a593Smuzhiyun 	int err;
538*4882a593Smuzhiyun 
539*4882a593Smuzhiyun 	err = alloc_chrdev_region(&umplock_dev, 0, 1, umplock_dev_name);
540*4882a593Smuzhiyun 
541*4882a593Smuzhiyun 	if (0 == err) {
542*4882a593Smuzhiyun 		memset(&umplock_device, 0, sizeof(umplock_device));
543*4882a593Smuzhiyun 		cdev_init(&umplock_device.cdev, &umplock_fops);
544*4882a593Smuzhiyun 		umplock_device.cdev.owner = THIS_MODULE;
545*4882a593Smuzhiyun 		umplock_device.cdev.ops = &umplock_fops;
546*4882a593Smuzhiyun 
547*4882a593Smuzhiyun 		err = cdev_add(&umplock_device.cdev, umplock_dev, 1);
548*4882a593Smuzhiyun 		if (0 == err) {
549*4882a593Smuzhiyun 			umplock_device.umplock_class = class_create(THIS_MODULE, umplock_dev_name);
550*4882a593Smuzhiyun 			if (IS_ERR(umplock_device.umplock_class)) {
551*4882a593Smuzhiyun 				err = PTR_ERR(umplock_device.umplock_class);
552*4882a593Smuzhiyun 			} else {
553*4882a593Smuzhiyun 				struct device *mdev;
554*4882a593Smuzhiyun 				mdev = device_create(umplock_device.umplock_class, NULL, umplock_dev, NULL, umplock_dev_name);
555*4882a593Smuzhiyun 				if (!IS_ERR(mdev)) {
556*4882a593Smuzhiyun 					return 0; /* all ok */
557*4882a593Smuzhiyun 				}
558*4882a593Smuzhiyun 
559*4882a593Smuzhiyun 				err = PTR_ERR(mdev);
560*4882a593Smuzhiyun 				class_destroy(umplock_device.umplock_class);
561*4882a593Smuzhiyun 			}
562*4882a593Smuzhiyun 			cdev_del(&umplock_device.cdev);
563*4882a593Smuzhiyun 		}
564*4882a593Smuzhiyun 
565*4882a593Smuzhiyun 		unregister_chrdev_region(umplock_dev, 1);
566*4882a593Smuzhiyun 	} else {
567*4882a593Smuzhiyun 		PERROR("alloc chardev region failed\n");
568*4882a593Smuzhiyun 	}
569*4882a593Smuzhiyun 
570*4882a593Smuzhiyun 	return err;
571*4882a593Smuzhiyun }
572*4882a593Smuzhiyun 
umplock_device_terminate(void)573*4882a593Smuzhiyun void umplock_device_terminate(void)
574*4882a593Smuzhiyun {
575*4882a593Smuzhiyun 	device_destroy(umplock_device.umplock_class, umplock_dev);
576*4882a593Smuzhiyun 	class_destroy(umplock_device.umplock_class);
577*4882a593Smuzhiyun 
578*4882a593Smuzhiyun 	cdev_del(&umplock_device.cdev);
579*4882a593Smuzhiyun 	unregister_chrdev_region(umplock_dev, 1);
580*4882a593Smuzhiyun }
581*4882a593Smuzhiyun 
umplock_initialize_module(void)582*4882a593Smuzhiyun static int __init umplock_initialize_module(void)
583*4882a593Smuzhiyun {
584*4882a593Smuzhiyun 	PDEBUG(1, "Inserting UMP lock device driver. Compiled: %s, time: %s\n", __DATE__, __TIME__);
585*4882a593Smuzhiyun 
586*4882a593Smuzhiyun 	mutex_init(&device.item_list_lock);
587*4882a593Smuzhiyun 	if (umplock_device_initialize() != 0) {
588*4882a593Smuzhiyun 		PERROR("UMP lock device driver init failed\n");
589*4882a593Smuzhiyun 		return -ENOTTY;
590*4882a593Smuzhiyun 	}
591*4882a593Smuzhiyun 	memset(&device.items, 0, sizeof(umplock_item) * MAX_ITEMS);
592*4882a593Smuzhiyun 	memset(&device.pids, 0, sizeof(u32) * MAX_PIDS);
593*4882a593Smuzhiyun 	atomic_set(&device.sessions, 0);
594*4882a593Smuzhiyun 
595*4882a593Smuzhiyun 	PDEBUG(1, "UMP lock device driver loaded\n");
596*4882a593Smuzhiyun 
597*4882a593Smuzhiyun 	return 0;
598*4882a593Smuzhiyun }
599*4882a593Smuzhiyun 
umplock_cleanup_module(void)600*4882a593Smuzhiyun static void __exit umplock_cleanup_module(void)
601*4882a593Smuzhiyun {
602*4882a593Smuzhiyun 	PDEBUG(1, "unloading UMP lock module\n");
603*4882a593Smuzhiyun 
604*4882a593Smuzhiyun 	memset(&device.items, 0, sizeof(umplock_item) * MAX_ITEMS);
605*4882a593Smuzhiyun 	memset(&device.pids, 0, sizeof(u32) * MAX_PIDS);
606*4882a593Smuzhiyun 	umplock_device_terminate();
607*4882a593Smuzhiyun 	mutex_destroy(&device.item_list_lock);
608*4882a593Smuzhiyun 
609*4882a593Smuzhiyun 	PDEBUG(1, "UMP lock module unloaded\n");
610*4882a593Smuzhiyun }
611*4882a593Smuzhiyun 
612*4882a593Smuzhiyun module_init(umplock_initialize_module);
613*4882a593Smuzhiyun module_exit(umplock_cleanup_module);
614*4882a593Smuzhiyun 
615*4882a593Smuzhiyun 
616*4882a593Smuzhiyun MODULE_LICENSE("GPL");
617*4882a593Smuzhiyun MODULE_AUTHOR("ARM Ltd.");
618*4882a593Smuzhiyun MODULE_DESCRIPTION("ARM UMP locker");
619