Commit | Line | Data |
---|---|---|
b1fa6d8a LA |
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 | ||
02b23803 LA |
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 | static int validate_ioctl_arg(unsigned int cmd, union ion_ioctl_arg *arg) | |
34 | { | |
35 | int ret = 0; | |
36 | ||
37 | switch (cmd) { | |
38 | case ION_IOC_HEAP_QUERY: | |
39 | ret = arg->query.reserved0 != 0; | |
40 | ret |= arg->query.reserved1 != 0; | |
41 | ret |= arg->query.reserved2 != 0; | |
42 | break; | |
43 | default: | |
44 | break; | |
45 | } | |
46 | ||
47 | return ret ? -EINVAL : 0; | |
48 | } | |
49 | ||
b1fa6d8a LA |
50 | /* fix up the cases where the ioctl direction bits are incorrect */ |
51 | static unsigned int ion_ioctl_dir(unsigned int cmd) | |
52 | { | |
53 | switch (cmd) { | |
54 | case ION_IOC_SYNC: | |
55 | case ION_IOC_FREE: | |
56 | case ION_IOC_CUSTOM: | |
57 | return _IOC_WRITE; | |
58 | default: | |
59 | return _IOC_DIR(cmd); | |
60 | } | |
61 | } | |
62 | ||
63 | long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) | |
64 | { | |
65 | struct ion_client *client = filp->private_data; | |
66 | struct ion_device *dev = client->dev; | |
67 | struct ion_handle *cleanup_handle = NULL; | |
68 | int ret = 0; | |
69 | unsigned int dir; | |
02b23803 | 70 | union ion_ioctl_arg data; |
b1fa6d8a LA |
71 | |
72 | dir = ion_ioctl_dir(cmd); | |
73 | ||
74 | if (_IOC_SIZE(cmd) > sizeof(data)) | |
75 | return -EINVAL; | |
76 | ||
02b23803 LA |
77 | /* |
78 | * The copy_from_user is unconditional here for both read and write | |
79 | * to do the validate. If there is no write for the ioctl, the | |
80 | * buffer is cleared | |
81 | */ | |
82 | if (copy_from_user(&data, (void __user *)arg, _IOC_SIZE(cmd))) | |
83 | return -EFAULT; | |
84 | ||
85 | ret = validate_ioctl_arg(cmd, &data); | |
86 | if (WARN_ON_ONCE(ret)) | |
87 | return ret; | |
88 | ||
89 | if (!(dir & _IOC_WRITE)) | |
90 | memset(&data, 0, sizeof(data)); | |
b1fa6d8a LA |
91 | |
92 | switch (cmd) { | |
93 | case ION_IOC_ALLOC: | |
94 | { | |
95 | struct ion_handle *handle; | |
96 | ||
97 | handle = ion_alloc(client, data.allocation.len, | |
98 | data.allocation.align, | |
99 | data.allocation.heap_id_mask, | |
100 | data.allocation.flags); | |
101 | if (IS_ERR(handle)) | |
102 | return PTR_ERR(handle); | |
103 | ||
104 | data.allocation.handle = handle->id; | |
105 | ||
106 | cleanup_handle = handle; | |
107 | break; | |
108 | } | |
109 | case ION_IOC_FREE: | |
110 | { | |
111 | struct ion_handle *handle; | |
112 | ||
113 | mutex_lock(&client->lock); | |
114 | handle = ion_handle_get_by_id_nolock(client, data.handle.handle); | |
115 | if (IS_ERR(handle)) { | |
116 | mutex_unlock(&client->lock); | |
117 | return PTR_ERR(handle); | |
118 | } | |
119 | ion_free_nolock(client, handle); | |
120 | ion_handle_put_nolock(handle); | |
121 | mutex_unlock(&client->lock); | |
122 | break; | |
123 | } | |
124 | case ION_IOC_SHARE: | |
125 | case ION_IOC_MAP: | |
126 | { | |
127 | struct ion_handle *handle; | |
128 | ||
129 | handle = ion_handle_get_by_id(client, data.handle.handle); | |
130 | if (IS_ERR(handle)) | |
131 | return PTR_ERR(handle); | |
132 | data.fd.fd = ion_share_dma_buf_fd(client, handle); | |
133 | ion_handle_put(handle); | |
134 | if (data.fd.fd < 0) | |
135 | ret = data.fd.fd; | |
136 | break; | |
137 | } | |
138 | case ION_IOC_IMPORT: | |
139 | { | |
140 | struct ion_handle *handle; | |
141 | ||
142 | handle = ion_import_dma_buf_fd(client, data.fd.fd); | |
143 | if (IS_ERR(handle)) | |
144 | ret = PTR_ERR(handle); | |
145 | else | |
146 | data.handle.handle = handle->id; | |
147 | break; | |
148 | } | |
149 | case ION_IOC_SYNC: | |
150 | { | |
151 | ret = ion_sync_for_device(client, data.fd.fd); | |
152 | break; | |
153 | } | |
154 | case ION_IOC_CUSTOM: | |
155 | { | |
156 | if (!dev->custom_ioctl) | |
157 | return -ENOTTY; | |
158 | ret = dev->custom_ioctl(client, data.custom.cmd, | |
159 | data.custom.arg); | |
160 | break; | |
161 | } | |
02b23803 LA |
162 | case ION_IOC_HEAP_QUERY: |
163 | ret = ion_query_heaps(client, &data.query); | |
164 | break; | |
b1fa6d8a LA |
165 | default: |
166 | return -ENOTTY; | |
167 | } | |
168 | ||
169 | if (dir & _IOC_READ) { | |
170 | if (copy_to_user((void __user *)arg, &data, _IOC_SIZE(cmd))) { | |
171 | if (cleanup_handle) | |
172 | ion_free(client, cleanup_handle); | |
173 | return -EFAULT; | |
174 | } | |
175 | } | |
176 | return ret; | |
177 | } |