1
2
3#include "gasket.h"
4#include "gasket_ioctl.h"
5#include "gasket_constants.h"
6#include "gasket_core.h"
7#include "gasket_interrupt.h"
8#include "gasket_page_table.h"
9#include <linux/compiler.h>
10#include <linux/device.h>
11#include <linux/fs.h>
12#include <linux/uaccess.h>
13
14#ifdef GASKET_KERNEL_TRACE_SUPPORT
15#define CREATE_TRACE_POINTS
16#include <trace/events/gasket_ioctl.h>
17#else
18#define trace_gasket_ioctl_entry(x, ...)
19#define trace_gasket_ioctl_exit(x)
20#define trace_gasket_ioctl_integer_data(x)
21#define trace_gasket_ioctl_eventfd_data(x, ...)
22#define trace_gasket_ioctl_page_table_data(x, ...)
23#define trace_gasket_ioctl_config_coherent_allocator(x, ...)
24#endif
25
26
27static int gasket_set_event_fd(struct gasket_dev *gasket_dev,
28 struct gasket_interrupt_eventfd __user *argp)
29{
30 struct gasket_interrupt_eventfd die;
31
32 if (copy_from_user(&die, argp, sizeof(struct gasket_interrupt_eventfd)))
33 return -EFAULT;
34
35 trace_gasket_ioctl_eventfd_data(die.interrupt, die.event_fd);
36
37 return gasket_interrupt_set_eventfd(
38 gasket_dev->interrupt_data, die.interrupt, die.event_fd);
39}
40
41
42static int gasket_read_page_table_size(
43 struct gasket_dev *gasket_dev,
44 struct gasket_page_table_ioctl __user *argp)
45{
46 int ret = 0;
47 struct gasket_page_table_ioctl ibuf;
48
49 if (copy_from_user(&ibuf, argp, sizeof(struct gasket_page_table_ioctl)))
50 return -EFAULT;
51
52 if (ibuf.page_table_index >= gasket_dev->num_page_tables)
53 return -EFAULT;
54
55 ibuf.size = gasket_page_table_num_entries(
56 gasket_dev->page_table[ibuf.page_table_index]);
57
58 trace_gasket_ioctl_page_table_data(
59 ibuf.page_table_index, ibuf.size, ibuf.host_address,
60 ibuf.device_address);
61
62 if (copy_to_user(argp, &ibuf, sizeof(ibuf)))
63 return -EFAULT;
64
65 return ret;
66}
67
68
69static int gasket_read_simple_page_table_size(
70 struct gasket_dev *gasket_dev,
71 struct gasket_page_table_ioctl __user *argp)
72{
73 int ret = 0;
74 struct gasket_page_table_ioctl ibuf;
75
76 if (copy_from_user(&ibuf, argp, sizeof(struct gasket_page_table_ioctl)))
77 return -EFAULT;
78
79 if (ibuf.page_table_index >= gasket_dev->num_page_tables)
80 return -EFAULT;
81
82 ibuf.size =
83 gasket_page_table_num_simple_entries(gasket_dev->page_table[ibuf.page_table_index]);
84
85 trace_gasket_ioctl_page_table_data(ibuf.page_table_index, ibuf.size,
86 ibuf.host_address,
87 ibuf.device_address);
88
89 if (copy_to_user(argp, &ibuf, sizeof(ibuf)))
90 return -EFAULT;
91
92 return ret;
93}
94
95
96static int gasket_partition_page_table(
97 struct gasket_dev *gasket_dev,
98 struct gasket_page_table_ioctl __user *argp)
99{
100 int ret;
101 struct gasket_page_table_ioctl ibuf;
102 uint max_page_table_size;
103
104 if (copy_from_user(&ibuf, argp, sizeof(struct gasket_page_table_ioctl)))
105 return -EFAULT;
106
107 trace_gasket_ioctl_page_table_data(
108 ibuf.page_table_index, ibuf.size, ibuf.host_address,
109 ibuf.device_address);
110
111 if (ibuf.page_table_index >= gasket_dev->num_page_tables)
112 return -EFAULT;
113 max_page_table_size = gasket_page_table_max_size(
114 gasket_dev->page_table[ibuf.page_table_index]);
115
116 if (ibuf.size > max_page_table_size) {
117 dev_dbg(gasket_dev->dev,
118 "Partition request 0x%llx too large, max is 0x%x\n",
119 ibuf.size, max_page_table_size);
120 return -EINVAL;
121 }
122
123 mutex_lock(&gasket_dev->mutex);
124
125 ret = gasket_page_table_partition(
126 gasket_dev->page_table[ibuf.page_table_index], ibuf.size);
127 mutex_unlock(&gasket_dev->mutex);
128
129 return ret;
130}
131
132
133static int gasket_map_buffers(struct gasket_dev *gasket_dev,
134 struct gasket_page_table_ioctl __user *argp)
135{
136 struct gasket_page_table_ioctl ibuf;
137
138 if (copy_from_user(&ibuf, argp, sizeof(struct gasket_page_table_ioctl)))
139 return -EFAULT;
140
141 trace_gasket_ioctl_page_table_data(ibuf.page_table_index, ibuf.size,
142 ibuf.host_address,
143 ibuf.device_address);
144
145 if (ibuf.page_table_index >= gasket_dev->num_page_tables)
146 return -EFAULT;
147
148 if (gasket_page_table_are_addrs_bad(gasket_dev->page_table[ibuf.page_table_index],
149 ibuf.host_address,
150 ibuf.device_address, ibuf.size))
151 return -EINVAL;
152
153 return gasket_page_table_map(gasket_dev->page_table[ibuf.page_table_index],
154 ibuf.host_address, ibuf.device_address,
155 ibuf.size / PAGE_SIZE);
156}
157
158
159static int gasket_unmap_buffers(struct gasket_dev *gasket_dev,
160 struct gasket_page_table_ioctl __user *argp)
161{
162 struct gasket_page_table_ioctl ibuf;
163
164 if (copy_from_user(&ibuf, argp, sizeof(struct gasket_page_table_ioctl)))
165 return -EFAULT;
166
167 trace_gasket_ioctl_page_table_data(ibuf.page_table_index, ibuf.size,
168 ibuf.host_address,
169 ibuf.device_address);
170
171 if (ibuf.page_table_index >= gasket_dev->num_page_tables)
172 return -EFAULT;
173
174 if (gasket_page_table_is_dev_addr_bad(gasket_dev->page_table[ibuf.page_table_index],
175 ibuf.device_address, ibuf.size))
176 return -EINVAL;
177
178 gasket_page_table_unmap(gasket_dev->page_table[ibuf.page_table_index],
179 ibuf.device_address, ibuf.size / PAGE_SIZE);
180
181 return 0;
182}
183
184
185
186
187
188static int gasket_config_coherent_allocator(
189 struct gasket_dev *gasket_dev,
190 struct gasket_coherent_alloc_config_ioctl __user *argp)
191{
192 int ret;
193 struct gasket_coherent_alloc_config_ioctl ibuf;
194
195 if (copy_from_user(&ibuf, argp,
196 sizeof(struct gasket_coherent_alloc_config_ioctl)))
197 return -EFAULT;
198
199 trace_gasket_ioctl_config_coherent_allocator(ibuf.enable, ibuf.size,
200 ibuf.dma_address);
201
202 if (ibuf.page_table_index >= gasket_dev->num_page_tables)
203 return -EFAULT;
204
205 if (ibuf.size > PAGE_SIZE * MAX_NUM_COHERENT_PAGES)
206 return -ENOMEM;
207
208 if (ibuf.enable == 0) {
209 ret = gasket_free_coherent_memory(gasket_dev, ibuf.size,
210 ibuf.dma_address,
211 ibuf.page_table_index);
212 } else {
213 ret = gasket_alloc_coherent_memory(gasket_dev, ibuf.size,
214 &ibuf.dma_address,
215 ibuf.page_table_index);
216 }
217 if (ret)
218 return ret;
219 if (copy_to_user(argp, &ibuf, sizeof(ibuf)))
220 return -EFAULT;
221
222 return 0;
223}
224
225
226static bool gasket_ioctl_check_permissions(struct file *filp, uint cmd)
227{
228 bool alive;
229 bool read, write;
230 struct gasket_dev *gasket_dev = (struct gasket_dev *)filp->private_data;
231
232 alive = (gasket_dev->status == GASKET_STATUS_ALIVE);
233 if (!alive)
234 dev_dbg(gasket_dev->dev, "%s alive %d status %d\n",
235 __func__, alive, gasket_dev->status);
236
237 read = !!(filp->f_mode & FMODE_READ);
238 write = !!(filp->f_mode & FMODE_WRITE);
239
240 switch (cmd) {
241 case GASKET_IOCTL_RESET:
242 case GASKET_IOCTL_CLEAR_INTERRUPT_COUNTS:
243 return write;
244
245 case GASKET_IOCTL_PAGE_TABLE_SIZE:
246 case GASKET_IOCTL_SIMPLE_PAGE_TABLE_SIZE:
247 case GASKET_IOCTL_NUMBER_PAGE_TABLES:
248 return read;
249
250 case GASKET_IOCTL_PARTITION_PAGE_TABLE:
251 case GASKET_IOCTL_CONFIG_COHERENT_ALLOCATOR:
252 return alive && write;
253
254 case GASKET_IOCTL_MAP_BUFFER:
255 case GASKET_IOCTL_UNMAP_BUFFER:
256 return alive && write;
257
258 case GASKET_IOCTL_CLEAR_EVENTFD:
259 case GASKET_IOCTL_SET_EVENTFD:
260 return alive && write;
261 }
262
263 return false;
264}
265
266
267
268
269
270
271
272
273
274long gasket_handle_ioctl(struct file *filp, uint cmd, void __user *argp)
275{
276 struct gasket_dev *gasket_dev;
277 unsigned long arg = (unsigned long)argp;
278 gasket_ioctl_permissions_cb_t ioctl_permissions_cb;
279 int retval;
280
281 gasket_dev = (struct gasket_dev *)filp->private_data;
282 trace_gasket_ioctl_entry(gasket_dev->dev_info.name, cmd);
283
284 ioctl_permissions_cb = gasket_get_ioctl_permissions_cb(gasket_dev);
285 if (ioctl_permissions_cb) {
286 retval = ioctl_permissions_cb(filp, cmd, argp);
287 if (retval < 0) {
288 trace_gasket_ioctl_exit(retval);
289 return retval;
290 } else if (retval == 0) {
291 trace_gasket_ioctl_exit(-EPERM);
292 return -EPERM;
293 }
294 } else if (!gasket_ioctl_check_permissions(filp, cmd)) {
295 trace_gasket_ioctl_exit(-EPERM);
296 dev_dbg(gasket_dev->dev, "ioctl cmd=%x noperm\n", cmd);
297 return -EPERM;
298 }
299
300
301
302
303
304
305 switch (cmd) {
306 case GASKET_IOCTL_RESET:
307 retval = gasket_reset(gasket_dev);
308 break;
309 case GASKET_IOCTL_SET_EVENTFD:
310 retval = gasket_set_event_fd(gasket_dev, argp);
311 break;
312 case GASKET_IOCTL_CLEAR_EVENTFD:
313 trace_gasket_ioctl_integer_data(arg);
314 retval =
315 gasket_interrupt_clear_eventfd(gasket_dev->interrupt_data,
316 (int)arg);
317 break;
318 case GASKET_IOCTL_PARTITION_PAGE_TABLE:
319 trace_gasket_ioctl_integer_data(arg);
320 retval = gasket_partition_page_table(gasket_dev, argp);
321 break;
322 case GASKET_IOCTL_NUMBER_PAGE_TABLES:
323 trace_gasket_ioctl_integer_data(gasket_dev->num_page_tables);
324 if (copy_to_user(argp, &gasket_dev->num_page_tables,
325 sizeof(uint64_t)))
326 retval = -EFAULT;
327 else
328 retval = 0;
329 break;
330 case GASKET_IOCTL_PAGE_TABLE_SIZE:
331 retval = gasket_read_page_table_size(gasket_dev, argp);
332 break;
333 case GASKET_IOCTL_SIMPLE_PAGE_TABLE_SIZE:
334 retval = gasket_read_simple_page_table_size(gasket_dev, argp);
335 break;
336 case GASKET_IOCTL_MAP_BUFFER:
337 retval = gasket_map_buffers(gasket_dev, argp);
338 break;
339 case GASKET_IOCTL_CONFIG_COHERENT_ALLOCATOR:
340 retval = gasket_config_coherent_allocator(gasket_dev, argp);
341 break;
342 case GASKET_IOCTL_UNMAP_BUFFER:
343 retval = gasket_unmap_buffers(gasket_dev, argp);
344 break;
345 case GASKET_IOCTL_CLEAR_INTERRUPT_COUNTS:
346
347 trace_gasket_ioctl_integer_data(0);
348 retval = gasket_interrupt_reset_counts(gasket_dev);
349 break;
350 default:
351
352
353
354 trace_gasket_ioctl_integer_data(arg);
355 dev_dbg(gasket_dev->dev,
356 "Unknown ioctl cmd=0x%x not caught by gasket_is_supported_ioctl\n",
357 cmd);
358 retval = -EINVAL;
359 break;
360 }
361
362 trace_gasket_ioctl_exit(retval);
363 return retval;
364}
365
366
367
368
369
370
371
372long gasket_is_supported_ioctl(uint cmd)
373{
374 switch (cmd) {
375 case GASKET_IOCTL_RESET:
376 case GASKET_IOCTL_SET_EVENTFD:
377 case GASKET_IOCTL_CLEAR_EVENTFD:
378 case GASKET_IOCTL_PARTITION_PAGE_TABLE:
379 case GASKET_IOCTL_NUMBER_PAGE_TABLES:
380 case GASKET_IOCTL_PAGE_TABLE_SIZE:
381 case GASKET_IOCTL_SIMPLE_PAGE_TABLE_SIZE:
382 case GASKET_IOCTL_MAP_BUFFER:
383 case GASKET_IOCTL_UNMAP_BUFFER:
384 case GASKET_IOCTL_CLEAR_INTERRUPT_COUNTS:
385 case GASKET_IOCTL_CONFIG_COHERENT_ALLOCATOR:
386 return 1;
387 default:
388 return 0;
389 }
390}
391