GNU Linux-libre 4.9.337-gnu1
[releases.git] / drivers / staging / android / ion / ion-ioctl.c
1 /*
2  *
3  * Copyright (C) 2011 Google, Inc.
4  *
5  * This software is licensed under the terms of the GNU General Public
6  * License version 2, as published by the Free Software Foundation, and
7  * may be copied, distributed, and modified under those terms.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  */
15
16 #include <linux/kernel.h>
17 #include <linux/file.h>
18 #include <linux/fs.h>
19 #include <linux/uaccess.h>
20
21 #include "ion.h"
22 #include "ion_priv.h"
23 #include "compat_ion.h"
24
25 union ion_ioctl_arg {
26         struct ion_fd_data fd;
27         struct ion_allocation_data allocation;
28         struct ion_handle_data handle;
29         struct ion_custom_data custom;
30         struct ion_heap_query query;
31 };
32
33 /* Must hold the client lock */
34 static void user_ion_handle_get(struct ion_handle *handle)
35 {
36         if (handle->user_ref_count++ == 0)
37                 kref_get(&handle->ref);
38 }
39
40 /* Must hold the client lock */
41 static struct ion_handle *user_ion_handle_get_check_overflow(
42         struct ion_handle *handle)
43 {
44         if (handle->user_ref_count + 1 == 0)
45                 return ERR_PTR(-EOVERFLOW);
46         user_ion_handle_get(handle);
47         return handle;
48 }
49
50 /* passes a kref to the user ref count.
51  * We know we're holding a kref to the object before and
52  * after this call, so no need to reverify handle.
53  */
54 static struct ion_handle *pass_to_user(struct ion_handle *handle)
55 {
56         struct ion_client *client = handle->client;
57         struct ion_handle *ret;
58
59         mutex_lock(&client->lock);
60         ret = user_ion_handle_get_check_overflow(handle);
61         ion_handle_put_nolock(handle);
62         mutex_unlock(&client->lock);
63         return ret;
64 }
65
66 /* Must hold the client lock */
67 static void user_ion_handle_put_nolock(struct ion_handle *handle)
68 {
69         if (--handle->user_ref_count == 0)
70                 ion_handle_put_nolock(handle);
71 }
72
73 static void user_ion_free_nolock(struct ion_client *client,
74                                  struct ion_handle *handle)
75 {
76         bool valid_handle;
77
78         WARN_ON(client != handle->client);
79
80         valid_handle = ion_handle_validate(client, handle);
81         if (!valid_handle) {
82                 WARN(1, "%s: invalid handle passed to free.\n", __func__);
83                 return;
84         }
85         if (handle->user_ref_count == 0) {
86                 WARN(1, "%s: User does not have access!\n", __func__);
87                 return;
88         }
89         user_ion_handle_put_nolock(handle);
90 }
91
92 static int validate_ioctl_arg(unsigned int cmd, union ion_ioctl_arg *arg)
93 {
94         int ret = 0;
95
96         switch (cmd) {
97         case ION_IOC_HEAP_QUERY:
98                 ret = arg->query.reserved0 != 0;
99                 ret |= arg->query.reserved1 != 0;
100                 ret |= arg->query.reserved2 != 0;
101                 break;
102         default:
103                 break;
104         }
105
106         return ret ? -EINVAL : 0;
107 }
108
109 /* fix up the cases where the ioctl direction bits are incorrect */
110 static unsigned int ion_ioctl_dir(unsigned int cmd)
111 {
112         switch (cmd) {
113         case ION_IOC_SYNC:
114         case ION_IOC_FREE:
115         case ION_IOC_CUSTOM:
116                 return _IOC_WRITE;
117         default:
118                 return _IOC_DIR(cmd);
119         }
120 }
121
122 long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
123 {
124         struct ion_client *client = filp->private_data;
125         struct ion_device *dev = client->dev;
126         struct ion_handle *cleanup_handle = NULL;
127         int ret = 0;
128         unsigned int dir;
129         union ion_ioctl_arg data;
130
131         dir = ion_ioctl_dir(cmd);
132
133         if (_IOC_SIZE(cmd) > sizeof(data))
134                 return -EINVAL;
135
136         /*
137          * The copy_from_user is unconditional here for both read and write
138          * to do the validate. If there is no write for the ioctl, the
139          * buffer is cleared
140          */
141         if (copy_from_user(&data, (void __user *)arg, _IOC_SIZE(cmd)))
142                 return -EFAULT;
143
144         ret = validate_ioctl_arg(cmd, &data);
145         if (ret) {
146                 pr_warn_once("%s: ioctl validate failed\n", __func__);
147                 return ret;
148         }
149
150         if (!(dir & _IOC_WRITE))
151                 memset(&data, 0, sizeof(data));
152
153         switch (cmd) {
154         case ION_IOC_ALLOC:
155         {
156                 struct ion_handle *handle;
157
158                 handle = __ion_alloc(client, data.allocation.len,
159                                      data.allocation.align,
160                                      data.allocation.heap_id_mask,
161                                      data.allocation.flags, true);
162                 if (IS_ERR(handle))
163                         return PTR_ERR(handle);
164                 data.allocation.handle = handle->id;
165                 cleanup_handle = handle;
166                 pass_to_user(handle);
167                 break;
168         }
169         case ION_IOC_FREE:
170         {
171                 struct ion_handle *handle;
172
173                 mutex_lock(&client->lock);
174                 handle = ion_handle_get_by_id_nolock(client, data.handle.handle);
175                 if (IS_ERR(handle)) {
176                         mutex_unlock(&client->lock);
177                         return PTR_ERR(handle);
178                 }
179                 user_ion_free_nolock(client, handle);
180                 ion_handle_put_nolock(handle);
181                 mutex_unlock(&client->lock);
182                 break;
183         }
184         case ION_IOC_SHARE:
185         case ION_IOC_MAP:
186         {
187                 struct ion_handle *handle;
188
189                 mutex_lock(&client->lock);
190                 handle = ion_handle_get_by_id_nolock(client, data.handle.handle);
191                 if (IS_ERR(handle)) {
192                         mutex_unlock(&client->lock);
193                         return PTR_ERR(handle);
194                 }
195                 data.fd.fd = ion_share_dma_buf_fd_nolock(client, handle);
196                 ion_handle_put_nolock(handle);
197                 mutex_unlock(&client->lock);
198                 if (data.fd.fd < 0)
199                         ret = data.fd.fd;
200                 break;
201         }
202         case ION_IOC_IMPORT:
203         {
204                 struct ion_handle *handle;
205
206                 handle = ion_import_dma_buf_fd(client, data.fd.fd);
207                 if (IS_ERR(handle)) {
208                         ret = PTR_ERR(handle);
209                 } else {
210                         data.handle.handle = handle->id;
211                         handle = pass_to_user(handle);
212                         if (IS_ERR(handle)) {
213                                 ret = PTR_ERR(handle);
214                                 data.handle.handle = 0;
215                         }
216                 }
217                 break;
218         }
219         case ION_IOC_SYNC:
220         {
221                 ret = ion_sync_for_device(client, data.fd.fd);
222                 break;
223         }
224         case ION_IOC_CUSTOM:
225         {
226                 if (!dev->custom_ioctl)
227                         return -ENOTTY;
228                 ret = dev->custom_ioctl(client, data.custom.cmd,
229                                                 data.custom.arg);
230                 break;
231         }
232         case ION_IOC_HEAP_QUERY:
233                 ret = ion_query_heaps(client, &data.query);
234                 break;
235         default:
236                 return -ENOTTY;
237         }
238
239         if (dir & _IOC_READ) {
240                 if (copy_to_user((void __user *)arg, &data, _IOC_SIZE(cmd))) {
241                         if (cleanup_handle) {
242                                 mutex_lock(&client->lock);
243                                 user_ion_free_nolock(client, cleanup_handle);
244                                 ion_handle_put_nolock(cleanup_handle);
245                                 mutex_unlock(&client->lock);
246                         }
247                         return -EFAULT;
248                 }
249         }
250         if (cleanup_handle)
251                 ion_handle_put(cleanup_handle);
252         return ret;
253 }