1 | /* |
2 | * \file drm_ioc32.c |
3 | * |
4 | * 32-bit ioctl compatibility routines for the DRM. |
5 | * |
6 | * \author Paul Mackerras <paulus@samba.org> |
7 | * |
8 | * Copyright (C) Paul Mackerras 2005. |
9 | * All Rights Reserved. |
10 | * |
11 | * Permission is hereby granted, free of charge, to any person obtaining a |
12 | * copy of this software and associated documentation files (the "Software"), |
13 | * to deal in the Software without restriction, including without limitation |
14 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
15 | * and/or sell copies of the Software, and to permit persons to whom the |
16 | * Software is furnished to do so, subject to the following conditions: |
17 | * |
18 | * The above copyright notice and this permission notice (including the next |
19 | * paragraph) shall be included in all copies or substantial portions of the |
20 | * Software. |
21 | * |
22 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
23 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
24 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
25 | * THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, |
26 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
27 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
28 | * IN THE SOFTWARE. |
29 | */ |
30 | #include <linux/compat.h> |
31 | #include <linux/ratelimit.h> |
32 | #include <linux/export.h> |
33 | |
34 | #include <drm/drm_file.h> |
35 | #include <drm/drm_print.h> |
36 | |
37 | #include "drm_crtc_internal.h" |
38 | #include "drm_internal.h" |
39 | #include "drm_legacy.h" |
40 | |
41 | #define DRM_IOCTL_VERSION32 DRM_IOWR(0x00, drm_version32_t) |
42 | #define DRM_IOCTL_GET_UNIQUE32 DRM_IOWR(0x01, drm_unique32_t) |
43 | #define DRM_IOCTL_GET_MAP32 DRM_IOWR(0x04, drm_map32_t) |
44 | #define DRM_IOCTL_GET_CLIENT32 DRM_IOWR(0x05, drm_client32_t) |
45 | #define DRM_IOCTL_GET_STATS32 DRM_IOR( 0x06, drm_stats32_t) |
46 | |
47 | #define DRM_IOCTL_SET_UNIQUE32 DRM_IOW( 0x10, drm_unique32_t) |
48 | #define DRM_IOCTL_ADD_MAP32 DRM_IOWR(0x15, drm_map32_t) |
49 | #define DRM_IOCTL_ADD_BUFS32 DRM_IOWR(0x16, drm_buf_desc32_t) |
50 | #define DRM_IOCTL_MARK_BUFS32 DRM_IOW( 0x17, drm_buf_desc32_t) |
51 | #define DRM_IOCTL_INFO_BUFS32 DRM_IOWR(0x18, drm_buf_info32_t) |
52 | #define DRM_IOCTL_MAP_BUFS32 DRM_IOWR(0x19, drm_buf_map32_t) |
53 | #define DRM_IOCTL_FREE_BUFS32 DRM_IOW( 0x1a, drm_buf_free32_t) |
54 | |
55 | #define DRM_IOCTL_RM_MAP32 DRM_IOW( 0x1b, drm_map32_t) |
56 | |
57 | #define DRM_IOCTL_SET_SAREA_CTX32 DRM_IOW( 0x1c, drm_ctx_priv_map32_t) |
58 | #define DRM_IOCTL_GET_SAREA_CTX32 DRM_IOWR(0x1d, drm_ctx_priv_map32_t) |
59 | |
60 | #define DRM_IOCTL_RES_CTX32 DRM_IOWR(0x26, drm_ctx_res32_t) |
61 | #define DRM_IOCTL_DMA32 DRM_IOWR(0x29, drm_dma32_t) |
62 | |
63 | #define DRM_IOCTL_AGP_ENABLE32 DRM_IOW( 0x32, drm_agp_mode32_t) |
64 | #define DRM_IOCTL_AGP_INFO32 DRM_IOR( 0x33, drm_agp_info32_t) |
65 | #define DRM_IOCTL_AGP_ALLOC32 DRM_IOWR(0x34, drm_agp_buffer32_t) |
66 | #define DRM_IOCTL_AGP_FREE32 DRM_IOW( 0x35, drm_agp_buffer32_t) |
67 | #define DRM_IOCTL_AGP_BIND32 DRM_IOW( 0x36, drm_agp_binding32_t) |
68 | #define DRM_IOCTL_AGP_UNBIND32 DRM_IOW( 0x37, drm_agp_binding32_t) |
69 | |
70 | #define DRM_IOCTL_SG_ALLOC32 DRM_IOW( 0x38, drm_scatter_gather32_t) |
71 | #define DRM_IOCTL_SG_FREE32 DRM_IOW( 0x39, drm_scatter_gather32_t) |
72 | |
73 | #define DRM_IOCTL_UPDATE_DRAW32 DRM_IOW( 0x3f, drm_update_draw32_t) |
74 | |
75 | #define DRM_IOCTL_WAIT_VBLANK32 DRM_IOWR(0x3a, drm_wait_vblank32_t) |
76 | |
77 | #define DRM_IOCTL_MODE_ADDFB232 DRM_IOWR(0xb8, drm_mode_fb_cmd232_t) |
78 | |
79 | typedef struct drm_version_32 { |
80 | int version_major; /* Major version */ |
81 | int version_minor; /* Minor version */ |
82 | int version_patchlevel; /* Patch level */ |
83 | u32 name_len; /* Length of name buffer */ |
84 | u32 name; /* Name of driver */ |
85 | u32 date_len; /* Length of date buffer */ |
86 | u32 date; /* User-space buffer to hold date */ |
87 | u32 desc_len; /* Length of desc buffer */ |
88 | u32 desc; /* User-space buffer to hold desc */ |
89 | } drm_version32_t; |
90 | |
91 | static int compat_drm_version(struct file *file, unsigned int cmd, |
92 | unsigned long arg) |
93 | { |
94 | drm_version32_t v32; |
95 | struct drm_version v; |
96 | int err; |
97 | |
98 | if (copy_from_user(to: &v32, from: (void __user *)arg, n: sizeof(v32))) |
99 | return -EFAULT; |
100 | |
101 | memset(&v, 0, sizeof(v)); |
102 | |
103 | v = (struct drm_version) { |
104 | .name_len = v32.name_len, |
105 | .name = compat_ptr(uptr: v32.name), |
106 | .date_len = v32.date_len, |
107 | .date = compat_ptr(uptr: v32.date), |
108 | .desc_len = v32.desc_len, |
109 | .desc = compat_ptr(uptr: v32.desc), |
110 | }; |
111 | err = drm_ioctl_kernel(file, drm_version, &v, |
112 | DRM_RENDER_ALLOW); |
113 | if (err) |
114 | return err; |
115 | |
116 | v32.version_major = v.version_major; |
117 | v32.version_minor = v.version_minor; |
118 | v32.version_patchlevel = v.version_patchlevel; |
119 | v32.name_len = v.name_len; |
120 | v32.date_len = v.date_len; |
121 | v32.desc_len = v.desc_len; |
122 | if (copy_to_user(to: (void __user *)arg, from: &v32, n: sizeof(v32))) |
123 | return -EFAULT; |
124 | return 0; |
125 | } |
126 | |
127 | typedef struct drm_unique32 { |
128 | u32 unique_len; /* Length of unique */ |
129 | u32 unique; /* Unique name for driver instantiation */ |
130 | } drm_unique32_t; |
131 | |
132 | static int compat_drm_getunique(struct file *file, unsigned int cmd, |
133 | unsigned long arg) |
134 | { |
135 | drm_unique32_t uq32; |
136 | struct drm_unique uq; |
137 | int err; |
138 | |
139 | if (copy_from_user(to: &uq32, from: (void __user *)arg, n: sizeof(uq32))) |
140 | return -EFAULT; |
141 | |
142 | memset(&uq, 0, sizeof(uq)); |
143 | |
144 | uq = (struct drm_unique){ |
145 | .unique_len = uq32.unique_len, |
146 | .unique = compat_ptr(uptr: uq32.unique), |
147 | }; |
148 | |
149 | err = drm_ioctl_kernel(file, drm_getunique, &uq, 0); |
150 | if (err) |
151 | return err; |
152 | |
153 | uq32.unique_len = uq.unique_len; |
154 | if (copy_to_user(to: (void __user *)arg, from: &uq32, n: sizeof(uq32))) |
155 | return -EFAULT; |
156 | return 0; |
157 | } |
158 | |
159 | static int compat_drm_setunique(struct file *file, unsigned int cmd, |
160 | unsigned long arg) |
161 | { |
162 | /* it's dead */ |
163 | return -EINVAL; |
164 | } |
165 | |
166 | #if IS_ENABLED(CONFIG_DRM_LEGACY) |
167 | typedef struct drm_map32 { |
168 | u32 offset; /* Requested physical address (0 for SAREA) */ |
169 | u32 size; /* Requested physical size (bytes) */ |
170 | enum drm_map_type type; /* Type of memory to map */ |
171 | enum drm_map_flags flags; /* Flags */ |
172 | u32 handle; /* User-space: "Handle" to pass to mmap() */ |
173 | int mtrr; /* MTRR slot used */ |
174 | } drm_map32_t; |
175 | |
176 | static int compat_drm_getmap(struct file *file, unsigned int cmd, |
177 | unsigned long arg) |
178 | { |
179 | drm_map32_t __user *argp = (void __user *)arg; |
180 | drm_map32_t m32; |
181 | struct drm_map map; |
182 | int err; |
183 | |
184 | if (copy_from_user(to: &m32, from: argp, n: sizeof(m32))) |
185 | return -EFAULT; |
186 | |
187 | map.offset = m32.offset; |
188 | err = drm_ioctl_kernel(file, drm_legacy_getmap_ioctl, &map, 0); |
189 | if (err) |
190 | return err; |
191 | |
192 | m32.offset = map.offset; |
193 | m32.size = map.size; |
194 | m32.type = map.type; |
195 | m32.flags = map.flags; |
196 | m32.handle = ptr_to_compat(uptr: (void __user *)map.handle); |
197 | m32.mtrr = map.mtrr; |
198 | if (copy_to_user(to: argp, from: &m32, n: sizeof(m32))) |
199 | return -EFAULT; |
200 | return 0; |
201 | |
202 | } |
203 | |
204 | static int compat_drm_addmap(struct file *file, unsigned int cmd, |
205 | unsigned long arg) |
206 | { |
207 | drm_map32_t __user *argp = (void __user *)arg; |
208 | drm_map32_t m32; |
209 | struct drm_map map; |
210 | int err; |
211 | |
212 | if (copy_from_user(to: &m32, from: argp, n: sizeof(m32))) |
213 | return -EFAULT; |
214 | |
215 | map.offset = m32.offset; |
216 | map.size = m32.size; |
217 | map.type = m32.type; |
218 | map.flags = m32.flags; |
219 | |
220 | err = drm_ioctl_kernel(file, drm_legacy_addmap_ioctl, &map, |
221 | DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY); |
222 | if (err) |
223 | return err; |
224 | |
225 | m32.offset = map.offset; |
226 | m32.mtrr = map.mtrr; |
227 | m32.handle = ptr_to_compat(uptr: (void __user *)map.handle); |
228 | if (map.handle != compat_ptr(uptr: m32.handle)) |
229 | pr_err_ratelimited("compat_drm_addmap truncated handle %p for type %d offset %x\n" , |
230 | map.handle, m32.type, m32.offset); |
231 | |
232 | if (copy_to_user(to: argp, from: &m32, n: sizeof(m32))) |
233 | return -EFAULT; |
234 | |
235 | return 0; |
236 | } |
237 | |
238 | static int compat_drm_rmmap(struct file *file, unsigned int cmd, |
239 | unsigned long arg) |
240 | { |
241 | drm_map32_t __user *argp = (void __user *)arg; |
242 | struct drm_map map; |
243 | u32 handle; |
244 | |
245 | if (get_user(handle, &argp->handle)) |
246 | return -EFAULT; |
247 | map.handle = compat_ptr(uptr: handle); |
248 | return drm_ioctl_kernel(file, drm_legacy_rmmap_ioctl, &map, DRM_AUTH); |
249 | } |
250 | #endif |
251 | |
252 | typedef struct drm_client32 { |
253 | int idx; /* Which client desired? */ |
254 | int auth; /* Is client authenticated? */ |
255 | u32 pid; /* Process ID */ |
256 | u32 uid; /* User ID */ |
257 | u32 magic; /* Magic */ |
258 | u32 iocs; /* Ioctl count */ |
259 | } drm_client32_t; |
260 | |
261 | static int compat_drm_getclient(struct file *file, unsigned int cmd, |
262 | unsigned long arg) |
263 | { |
264 | drm_client32_t c32; |
265 | drm_client32_t __user *argp = (void __user *)arg; |
266 | struct drm_client client; |
267 | int err; |
268 | |
269 | if (copy_from_user(to: &c32, from: argp, n: sizeof(c32))) |
270 | return -EFAULT; |
271 | |
272 | memset(&client, 0, sizeof(client)); |
273 | |
274 | client.idx = c32.idx; |
275 | |
276 | err = drm_ioctl_kernel(file, drm_getclient, &client, 0); |
277 | if (err) |
278 | return err; |
279 | |
280 | c32.idx = client.idx; |
281 | c32.auth = client.auth; |
282 | c32.pid = client.pid; |
283 | c32.uid = client.uid; |
284 | c32.magic = client.magic; |
285 | c32.iocs = client.iocs; |
286 | |
287 | if (copy_to_user(to: argp, from: &c32, n: sizeof(c32))) |
288 | return -EFAULT; |
289 | return 0; |
290 | } |
291 | |
292 | typedef struct drm_stats32 { |
293 | u32 count; |
294 | struct { |
295 | u32 value; |
296 | enum drm_stat_type type; |
297 | } data[15]; |
298 | } drm_stats32_t; |
299 | |
300 | static int compat_drm_getstats(struct file *file, unsigned int cmd, |
301 | unsigned long arg) |
302 | { |
303 | drm_stats32_t __user *argp = (void __user *)arg; |
304 | |
305 | /* getstats is defunct, just clear */ |
306 | if (clear_user(to: argp, n: sizeof(drm_stats32_t))) |
307 | return -EFAULT; |
308 | return 0; |
309 | } |
310 | |
311 | #if IS_ENABLED(CONFIG_DRM_LEGACY) |
312 | typedef struct drm_buf_desc32 { |
313 | int count; /* Number of buffers of this size */ |
314 | int size; /* Size in bytes */ |
315 | int low_mark; /* Low water mark */ |
316 | int high_mark; /* High water mark */ |
317 | int flags; |
318 | u32 agp_start; /* Start address in the AGP aperture */ |
319 | } drm_buf_desc32_t; |
320 | |
321 | static int compat_drm_addbufs(struct file *file, unsigned int cmd, |
322 | unsigned long arg) |
323 | { |
324 | drm_buf_desc32_t __user *argp = (void __user *)arg; |
325 | drm_buf_desc32_t desc32; |
326 | struct drm_buf_desc desc; |
327 | int err; |
328 | |
329 | if (copy_from_user(to: &desc32, from: argp, n: sizeof(drm_buf_desc32_t))) |
330 | return -EFAULT; |
331 | |
332 | desc = (struct drm_buf_desc){ |
333 | desc32.count, desc32.size, desc32.low_mark, desc32.high_mark, |
334 | desc32.flags, desc32.agp_start |
335 | }; |
336 | |
337 | err = drm_ioctl_kernel(file, drm_legacy_addbufs, &desc, |
338 | DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY); |
339 | if (err) |
340 | return err; |
341 | |
342 | desc32 = (drm_buf_desc32_t){ |
343 | desc.count, desc.size, desc.low_mark, desc.high_mark, |
344 | desc.flags, desc.agp_start |
345 | }; |
346 | if (copy_to_user(to: argp, from: &desc32, n: sizeof(drm_buf_desc32_t))) |
347 | return -EFAULT; |
348 | |
349 | return 0; |
350 | } |
351 | |
352 | static int compat_drm_markbufs(struct file *file, unsigned int cmd, |
353 | unsigned long arg) |
354 | { |
355 | drm_buf_desc32_t b32; |
356 | drm_buf_desc32_t __user *argp = (void __user *)arg; |
357 | struct drm_buf_desc buf; |
358 | |
359 | if (copy_from_user(to: &b32, from: argp, n: sizeof(b32))) |
360 | return -EFAULT; |
361 | |
362 | buf.size = b32.size; |
363 | buf.low_mark = b32.low_mark; |
364 | buf.high_mark = b32.high_mark; |
365 | |
366 | return drm_ioctl_kernel(file, drm_legacy_markbufs, &buf, |
367 | DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY); |
368 | } |
369 | |
370 | typedef struct drm_buf_info32 { |
371 | int count; /**< Entries in list */ |
372 | u32 list; |
373 | } drm_buf_info32_t; |
374 | |
375 | static int copy_one_buf32(void *data, int count, struct drm_buf_entry *from) |
376 | { |
377 | drm_buf_info32_t *request = data; |
378 | drm_buf_desc32_t __user *to = compat_ptr(uptr: request->list); |
379 | drm_buf_desc32_t v = {.count = from->buf_count, |
380 | .size = from->buf_size, |
381 | .low_mark = from->low_mark, |
382 | .high_mark = from->high_mark}; |
383 | |
384 | if (copy_to_user(to: to + count, from: &v, offsetof(drm_buf_desc32_t, flags))) |
385 | return -EFAULT; |
386 | return 0; |
387 | } |
388 | |
389 | static int drm_legacy_infobufs32(struct drm_device *dev, void *data, |
390 | struct drm_file *file_priv) |
391 | { |
392 | drm_buf_info32_t *request = data; |
393 | |
394 | return __drm_legacy_infobufs(dev, data, &request->count, copy_one_buf32); |
395 | } |
396 | |
397 | static int compat_drm_infobufs(struct file *file, unsigned int cmd, |
398 | unsigned long arg) |
399 | { |
400 | drm_buf_info32_t req32; |
401 | drm_buf_info32_t __user *argp = (void __user *)arg; |
402 | int err; |
403 | |
404 | if (copy_from_user(to: &req32, from: argp, n: sizeof(req32))) |
405 | return -EFAULT; |
406 | |
407 | if (req32.count < 0) |
408 | req32.count = 0; |
409 | |
410 | err = drm_ioctl_kernel(file, drm_legacy_infobufs32, &req32, DRM_AUTH); |
411 | if (err) |
412 | return err; |
413 | |
414 | if (put_user(req32.count, &argp->count)) |
415 | return -EFAULT; |
416 | |
417 | return 0; |
418 | } |
419 | |
420 | typedef struct drm_buf_pub32 { |
421 | int idx; /**< Index into the master buffer list */ |
422 | int total; /**< Buffer size */ |
423 | int used; /**< Amount of buffer in use (for DMA) */ |
424 | u32 address; /**< Address of buffer */ |
425 | } drm_buf_pub32_t; |
426 | |
427 | typedef struct drm_buf_map32 { |
428 | int count; /**< Length of the buffer list */ |
429 | u32 virtual; /**< Mmap'd area in user-virtual */ |
430 | u32 list; /**< Buffer information */ |
431 | } drm_buf_map32_t; |
432 | |
433 | static int map_one_buf32(void *data, int idx, unsigned long virtual, |
434 | struct drm_buf *buf) |
435 | { |
436 | drm_buf_map32_t *request = data; |
437 | drm_buf_pub32_t __user *to = compat_ptr(uptr: request->list) + idx; |
438 | drm_buf_pub32_t v; |
439 | |
440 | v.idx = buf->idx; |
441 | v.total = buf->total; |
442 | v.used = 0; |
443 | v.address = virtual + buf->offset; |
444 | if (copy_to_user(to, from: &v, n: sizeof(v))) |
445 | return -EFAULT; |
446 | return 0; |
447 | } |
448 | |
449 | static int drm_legacy_mapbufs32(struct drm_device *dev, void *data, |
450 | struct drm_file *file_priv) |
451 | { |
452 | drm_buf_map32_t *request = data; |
453 | void __user *v; |
454 | int err = __drm_legacy_mapbufs(dev, data, &request->count, |
455 | &v, map_one_buf32, |
456 | file_priv); |
457 | request->virtual = ptr_to_compat(uptr: v); |
458 | return err; |
459 | } |
460 | |
461 | static int compat_drm_mapbufs(struct file *file, unsigned int cmd, |
462 | unsigned long arg) |
463 | { |
464 | drm_buf_map32_t __user *argp = (void __user *)arg; |
465 | drm_buf_map32_t req32; |
466 | int err; |
467 | |
468 | if (copy_from_user(to: &req32, from: argp, n: sizeof(req32))) |
469 | return -EFAULT; |
470 | if (req32.count < 0) |
471 | return -EINVAL; |
472 | |
473 | err = drm_ioctl_kernel(file, drm_legacy_mapbufs32, &req32, DRM_AUTH); |
474 | if (err) |
475 | return err; |
476 | |
477 | if (put_user(req32.count, &argp->count) |
478 | || put_user(req32.virtual, &argp->virtual)) |
479 | return -EFAULT; |
480 | |
481 | return 0; |
482 | } |
483 | |
484 | typedef struct drm_buf_free32 { |
485 | int count; |
486 | u32 list; |
487 | } drm_buf_free32_t; |
488 | |
489 | static int compat_drm_freebufs(struct file *file, unsigned int cmd, |
490 | unsigned long arg) |
491 | { |
492 | drm_buf_free32_t req32; |
493 | struct drm_buf_free request; |
494 | drm_buf_free32_t __user *argp = (void __user *)arg; |
495 | |
496 | if (copy_from_user(to: &req32, from: argp, n: sizeof(req32))) |
497 | return -EFAULT; |
498 | |
499 | request.count = req32.count; |
500 | request.list = compat_ptr(uptr: req32.list); |
501 | return drm_ioctl_kernel(file, drm_legacy_freebufs, &request, DRM_AUTH); |
502 | } |
503 | |
504 | typedef struct drm_ctx_priv_map32 { |
505 | unsigned int ctx_id; /**< Context requesting private mapping */ |
506 | u32 handle; /**< Handle of map */ |
507 | } drm_ctx_priv_map32_t; |
508 | |
509 | static int compat_drm_setsareactx(struct file *file, unsigned int cmd, |
510 | unsigned long arg) |
511 | { |
512 | drm_ctx_priv_map32_t req32; |
513 | struct drm_ctx_priv_map request; |
514 | drm_ctx_priv_map32_t __user *argp = (void __user *)arg; |
515 | |
516 | if (copy_from_user(to: &req32, from: argp, n: sizeof(req32))) |
517 | return -EFAULT; |
518 | |
519 | request.ctx_id = req32.ctx_id; |
520 | request.handle = compat_ptr(uptr: req32.handle); |
521 | return drm_ioctl_kernel(file, drm_legacy_setsareactx, &request, |
522 | DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY); |
523 | } |
524 | |
525 | static int compat_drm_getsareactx(struct file *file, unsigned int cmd, |
526 | unsigned long arg) |
527 | { |
528 | struct drm_ctx_priv_map req; |
529 | drm_ctx_priv_map32_t req32; |
530 | drm_ctx_priv_map32_t __user *argp = (void __user *)arg; |
531 | int err; |
532 | |
533 | if (copy_from_user(to: &req32, from: argp, n: sizeof(req32))) |
534 | return -EFAULT; |
535 | |
536 | req.ctx_id = req32.ctx_id; |
537 | err = drm_ioctl_kernel(file, drm_legacy_getsareactx, &req, DRM_AUTH); |
538 | if (err) |
539 | return err; |
540 | |
541 | req32.handle = ptr_to_compat(uptr: (void __user *)req.handle); |
542 | if (copy_to_user(to: argp, from: &req32, n: sizeof(req32))) |
543 | return -EFAULT; |
544 | |
545 | return 0; |
546 | } |
547 | |
548 | typedef struct drm_ctx_res32 { |
549 | int count; |
550 | u32 contexts; |
551 | } drm_ctx_res32_t; |
552 | |
553 | static int compat_drm_resctx(struct file *file, unsigned int cmd, |
554 | unsigned long arg) |
555 | { |
556 | drm_ctx_res32_t __user *argp = (void __user *)arg; |
557 | drm_ctx_res32_t res32; |
558 | struct drm_ctx_res res; |
559 | int err; |
560 | |
561 | if (copy_from_user(to: &res32, from: argp, n: sizeof(res32))) |
562 | return -EFAULT; |
563 | |
564 | res.count = res32.count; |
565 | res.contexts = compat_ptr(uptr: res32.contexts); |
566 | err = drm_ioctl_kernel(file, drm_legacy_resctx, &res, DRM_AUTH); |
567 | if (err) |
568 | return err; |
569 | |
570 | res32.count = res.count; |
571 | if (copy_to_user(to: argp, from: &res32, n: sizeof(res32))) |
572 | return -EFAULT; |
573 | |
574 | return 0; |
575 | } |
576 | |
577 | typedef struct drm_dma32 { |
578 | int context; /**< Context handle */ |
579 | int send_count; /**< Number of buffers to send */ |
580 | u32 send_indices; /**< List of handles to buffers */ |
581 | u32 send_sizes; /**< Lengths of data to send */ |
582 | enum drm_dma_flags flags; /**< Flags */ |
583 | int request_count; /**< Number of buffers requested */ |
584 | int request_size; /**< Desired size for buffers */ |
585 | u32 request_indices; /**< Buffer information */ |
586 | u32 request_sizes; |
587 | int granted_count; /**< Number of buffers granted */ |
588 | } drm_dma32_t; |
589 | |
590 | static int compat_drm_dma(struct file *file, unsigned int cmd, |
591 | unsigned long arg) |
592 | { |
593 | drm_dma32_t d32; |
594 | drm_dma32_t __user *argp = (void __user *)arg; |
595 | struct drm_dma d; |
596 | int err; |
597 | |
598 | if (copy_from_user(to: &d32, from: argp, n: sizeof(d32))) |
599 | return -EFAULT; |
600 | |
601 | d.context = d32.context; |
602 | d.send_count = d32.send_count; |
603 | d.send_indices = compat_ptr(uptr: d32.send_indices); |
604 | d.send_sizes = compat_ptr(uptr: d32.send_sizes); |
605 | d.flags = d32.flags; |
606 | d.request_count = d32.request_count; |
607 | d.request_indices = compat_ptr(uptr: d32.request_indices); |
608 | d.request_sizes = compat_ptr(uptr: d32.request_sizes); |
609 | err = drm_ioctl_kernel(file, drm_legacy_dma_ioctl, &d, DRM_AUTH); |
610 | if (err) |
611 | return err; |
612 | |
613 | if (put_user(d.request_size, &argp->request_size) |
614 | || put_user(d.granted_count, &argp->granted_count)) |
615 | return -EFAULT; |
616 | |
617 | return 0; |
618 | } |
619 | #endif |
620 | |
621 | #if IS_ENABLED(CONFIG_DRM_LEGACY) |
622 | #if IS_ENABLED(CONFIG_AGP) |
623 | typedef struct drm_agp_mode32 { |
624 | u32 mode; /**< AGP mode */ |
625 | } drm_agp_mode32_t; |
626 | |
627 | static int compat_drm_agp_enable(struct file *file, unsigned int cmd, |
628 | unsigned long arg) |
629 | { |
630 | drm_agp_mode32_t __user *argp = (void __user *)arg; |
631 | struct drm_agp_mode mode; |
632 | |
633 | if (get_user(mode.mode, &argp->mode)) |
634 | return -EFAULT; |
635 | |
636 | return drm_ioctl_kernel(file, drm_legacy_agp_enable_ioctl, &mode, |
637 | DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY); |
638 | } |
639 | |
640 | typedef struct drm_agp_info32 { |
641 | int agp_version_major; |
642 | int agp_version_minor; |
643 | u32 mode; |
644 | u32 aperture_base; /* physical address */ |
645 | u32 aperture_size; /* bytes */ |
646 | u32 memory_allowed; /* bytes */ |
647 | u32 memory_used; |
648 | |
649 | /* PCI information */ |
650 | unsigned short id_vendor; |
651 | unsigned short id_device; |
652 | } drm_agp_info32_t; |
653 | |
654 | static int compat_drm_agp_info(struct file *file, unsigned int cmd, |
655 | unsigned long arg) |
656 | { |
657 | drm_agp_info32_t __user *argp = (void __user *)arg; |
658 | drm_agp_info32_t i32; |
659 | struct drm_agp_info info; |
660 | int err; |
661 | |
662 | err = drm_ioctl_kernel(file, drm_legacy_agp_info_ioctl, &info, DRM_AUTH); |
663 | if (err) |
664 | return err; |
665 | |
666 | i32.agp_version_major = info.agp_version_major; |
667 | i32.agp_version_minor = info.agp_version_minor; |
668 | i32.mode = info.mode; |
669 | i32.aperture_base = info.aperture_base; |
670 | i32.aperture_size = info.aperture_size; |
671 | i32.memory_allowed = info.memory_allowed; |
672 | i32.memory_used = info.memory_used; |
673 | i32.id_vendor = info.id_vendor; |
674 | i32.id_device = info.id_device; |
675 | if (copy_to_user(to: argp, from: &i32, n: sizeof(i32))) |
676 | return -EFAULT; |
677 | |
678 | return 0; |
679 | } |
680 | |
681 | typedef struct drm_agp_buffer32 { |
682 | u32 size; /**< In bytes -- will round to page boundary */ |
683 | u32 handle; /**< Used for binding / unbinding */ |
684 | u32 type; /**< Type of memory to allocate */ |
685 | u32 physical; /**< Physical used by i810 */ |
686 | } drm_agp_buffer32_t; |
687 | |
688 | static int compat_drm_agp_alloc(struct file *file, unsigned int cmd, |
689 | unsigned long arg) |
690 | { |
691 | drm_agp_buffer32_t __user *argp = (void __user *)arg; |
692 | drm_agp_buffer32_t req32; |
693 | struct drm_agp_buffer request; |
694 | int err; |
695 | |
696 | if (copy_from_user(to: &req32, from: argp, n: sizeof(req32))) |
697 | return -EFAULT; |
698 | |
699 | request.size = req32.size; |
700 | request.type = req32.type; |
701 | err = drm_ioctl_kernel(file, drm_legacy_agp_alloc_ioctl, &request, |
702 | DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY); |
703 | if (err) |
704 | return err; |
705 | |
706 | req32.handle = request.handle; |
707 | req32.physical = request.physical; |
708 | if (copy_to_user(to: argp, from: &req32, n: sizeof(req32))) { |
709 | drm_ioctl_kernel(file, drm_legacy_agp_free_ioctl, &request, |
710 | DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY); |
711 | return -EFAULT; |
712 | } |
713 | |
714 | return 0; |
715 | } |
716 | |
717 | static int compat_drm_agp_free(struct file *file, unsigned int cmd, |
718 | unsigned long arg) |
719 | { |
720 | drm_agp_buffer32_t __user *argp = (void __user *)arg; |
721 | struct drm_agp_buffer request; |
722 | |
723 | if (get_user(request.handle, &argp->handle)) |
724 | return -EFAULT; |
725 | |
726 | return drm_ioctl_kernel(file, drm_legacy_agp_free_ioctl, &request, |
727 | DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY); |
728 | } |
729 | |
730 | typedef struct drm_agp_binding32 { |
731 | u32 handle; /**< From drm_agp_buffer */ |
732 | u32 offset; /**< In bytes -- will round to page boundary */ |
733 | } drm_agp_binding32_t; |
734 | |
735 | static int compat_drm_agp_bind(struct file *file, unsigned int cmd, |
736 | unsigned long arg) |
737 | { |
738 | drm_agp_binding32_t __user *argp = (void __user *)arg; |
739 | drm_agp_binding32_t req32; |
740 | struct drm_agp_binding request; |
741 | |
742 | if (copy_from_user(to: &req32, from: argp, n: sizeof(req32))) |
743 | return -EFAULT; |
744 | |
745 | request.handle = req32.handle; |
746 | request.offset = req32.offset; |
747 | return drm_ioctl_kernel(file, drm_legacy_agp_bind_ioctl, &request, |
748 | DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY); |
749 | } |
750 | |
751 | static int compat_drm_agp_unbind(struct file *file, unsigned int cmd, |
752 | unsigned long arg) |
753 | { |
754 | drm_agp_binding32_t __user *argp = (void __user *)arg; |
755 | struct drm_agp_binding request; |
756 | |
757 | if (get_user(request.handle, &argp->handle)) |
758 | return -EFAULT; |
759 | |
760 | return drm_ioctl_kernel(file, drm_legacy_agp_unbind_ioctl, &request, |
761 | DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY); |
762 | } |
763 | #endif /* CONFIG_AGP */ |
764 | |
765 | typedef struct drm_scatter_gather32 { |
766 | u32 size; /**< In bytes -- will round to page boundary */ |
767 | u32 handle; /**< Used for mapping / unmapping */ |
768 | } drm_scatter_gather32_t; |
769 | |
770 | static int compat_drm_sg_alloc(struct file *file, unsigned int cmd, |
771 | unsigned long arg) |
772 | { |
773 | drm_scatter_gather32_t __user *argp = (void __user *)arg; |
774 | struct drm_scatter_gather request; |
775 | int err; |
776 | |
777 | if (get_user(request.size, &argp->size)) |
778 | return -EFAULT; |
779 | |
780 | err = drm_ioctl_kernel(file, drm_legacy_sg_alloc, &request, |
781 | DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY); |
782 | if (err) |
783 | return err; |
784 | |
785 | /* XXX not sure about the handle conversion here... */ |
786 | if (put_user(request.handle >> PAGE_SHIFT, &argp->handle)) |
787 | return -EFAULT; |
788 | |
789 | return 0; |
790 | } |
791 | |
792 | static int compat_drm_sg_free(struct file *file, unsigned int cmd, |
793 | unsigned long arg) |
794 | { |
795 | drm_scatter_gather32_t __user *argp = (void __user *)arg; |
796 | struct drm_scatter_gather request; |
797 | unsigned long x; |
798 | |
799 | if (get_user(x, &argp->handle)) |
800 | return -EFAULT; |
801 | request.handle = x << PAGE_SHIFT; |
802 | return drm_ioctl_kernel(file, drm_legacy_sg_free, &request, |
803 | DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY); |
804 | } |
805 | #endif |
806 | #if defined(CONFIG_X86) |
807 | typedef struct drm_update_draw32 { |
808 | drm_drawable_t handle; |
809 | unsigned int type; |
810 | unsigned int num; |
811 | /* 64-bit version has a 32-bit pad here */ |
812 | u64 data; /**< Pointer */ |
813 | } __attribute__((packed)) drm_update_draw32_t; |
814 | |
815 | static int compat_drm_update_draw(struct file *file, unsigned int cmd, |
816 | unsigned long arg) |
817 | { |
818 | /* update_draw is defunct */ |
819 | return 0; |
820 | } |
821 | #endif |
822 | |
823 | struct drm_wait_vblank_request32 { |
824 | enum drm_vblank_seq_type type; |
825 | unsigned int sequence; |
826 | u32 signal; |
827 | }; |
828 | |
829 | struct drm_wait_vblank_reply32 { |
830 | enum drm_vblank_seq_type type; |
831 | unsigned int sequence; |
832 | s32 tval_sec; |
833 | s32 tval_usec; |
834 | }; |
835 | |
836 | typedef union drm_wait_vblank32 { |
837 | struct drm_wait_vblank_request32 request; |
838 | struct drm_wait_vblank_reply32 reply; |
839 | } drm_wait_vblank32_t; |
840 | |
841 | static int compat_drm_wait_vblank(struct file *file, unsigned int cmd, |
842 | unsigned long arg) |
843 | { |
844 | drm_wait_vblank32_t __user *argp = (void __user *)arg; |
845 | drm_wait_vblank32_t req32; |
846 | union drm_wait_vblank req; |
847 | int err; |
848 | |
849 | if (copy_from_user(to: &req32, from: argp, n: sizeof(req32))) |
850 | return -EFAULT; |
851 | |
852 | memset(&req, 0, sizeof(req)); |
853 | |
854 | req.request.type = req32.request.type; |
855 | req.request.sequence = req32.request.sequence; |
856 | req.request.signal = req32.request.signal; |
857 | err = drm_ioctl_kernel(file, drm_wait_vblank_ioctl, &req, DRM_UNLOCKED); |
858 | |
859 | req32.reply.type = req.reply.type; |
860 | req32.reply.sequence = req.reply.sequence; |
861 | req32.reply.tval_sec = req.reply.tval_sec; |
862 | req32.reply.tval_usec = req.reply.tval_usec; |
863 | if (copy_to_user(to: argp, from: &req32, n: sizeof(req32))) |
864 | return -EFAULT; |
865 | |
866 | return err; |
867 | } |
868 | |
869 | #if defined(CONFIG_X86) |
870 | typedef struct drm_mode_fb_cmd232 { |
871 | u32 fb_id; |
872 | u32 width; |
873 | u32 height; |
874 | u32 pixel_format; |
875 | u32 flags; |
876 | u32 handles[4]; |
877 | u32 pitches[4]; |
878 | u32 offsets[4]; |
879 | u64 modifier[4]; |
880 | } __attribute__((packed)) drm_mode_fb_cmd232_t; |
881 | |
882 | static int compat_drm_mode_addfb2(struct file *file, unsigned int cmd, |
883 | unsigned long arg) |
884 | { |
885 | struct drm_mode_fb_cmd232 __user *argp = (void __user *)arg; |
886 | struct drm_mode_fb_cmd2 req64; |
887 | int err; |
888 | |
889 | memset(&req64, 0, sizeof(req64)); |
890 | |
891 | if (copy_from_user(to: &req64, from: argp, |
892 | offsetof(drm_mode_fb_cmd232_t, modifier))) |
893 | return -EFAULT; |
894 | |
895 | if (copy_from_user(to: &req64.modifier, from: &argp->modifier, |
896 | n: sizeof(req64.modifier))) |
897 | return -EFAULT; |
898 | |
899 | err = drm_ioctl_kernel(file, drm_mode_addfb2, &req64, 0); |
900 | if (err) |
901 | return err; |
902 | |
903 | if (put_user(req64.fb_id, &argp->fb_id)) |
904 | return -EFAULT; |
905 | |
906 | return 0; |
907 | } |
908 | #endif |
909 | |
910 | static struct { |
911 | drm_ioctl_compat_t *fn; |
912 | char *name; |
913 | } drm_compat_ioctls[] = { |
914 | #define DRM_IOCTL32_DEF(n, f) [DRM_IOCTL_NR(n##32)] = {.fn = f, .name = #n} |
915 | DRM_IOCTL32_DEF(DRM_IOCTL_VERSION, compat_drm_version), |
916 | DRM_IOCTL32_DEF(DRM_IOCTL_GET_UNIQUE, compat_drm_getunique), |
917 | #if IS_ENABLED(CONFIG_DRM_LEGACY) |
918 | DRM_IOCTL32_DEF(DRM_IOCTL_GET_MAP, compat_drm_getmap), |
919 | #endif |
920 | DRM_IOCTL32_DEF(DRM_IOCTL_GET_CLIENT, compat_drm_getclient), |
921 | DRM_IOCTL32_DEF(DRM_IOCTL_GET_STATS, compat_drm_getstats), |
922 | DRM_IOCTL32_DEF(DRM_IOCTL_SET_UNIQUE, compat_drm_setunique), |
923 | #if IS_ENABLED(CONFIG_DRM_LEGACY) |
924 | DRM_IOCTL32_DEF(DRM_IOCTL_ADD_MAP, compat_drm_addmap), |
925 | DRM_IOCTL32_DEF(DRM_IOCTL_ADD_BUFS, compat_drm_addbufs), |
926 | DRM_IOCTL32_DEF(DRM_IOCTL_MARK_BUFS, compat_drm_markbufs), |
927 | DRM_IOCTL32_DEF(DRM_IOCTL_INFO_BUFS, compat_drm_infobufs), |
928 | DRM_IOCTL32_DEF(DRM_IOCTL_MAP_BUFS, compat_drm_mapbufs), |
929 | DRM_IOCTL32_DEF(DRM_IOCTL_FREE_BUFS, compat_drm_freebufs), |
930 | DRM_IOCTL32_DEF(DRM_IOCTL_RM_MAP, compat_drm_rmmap), |
931 | DRM_IOCTL32_DEF(DRM_IOCTL_SET_SAREA_CTX, compat_drm_setsareactx), |
932 | DRM_IOCTL32_DEF(DRM_IOCTL_GET_SAREA_CTX, compat_drm_getsareactx), |
933 | DRM_IOCTL32_DEF(DRM_IOCTL_RES_CTX, compat_drm_resctx), |
934 | DRM_IOCTL32_DEF(DRM_IOCTL_DMA, compat_drm_dma), |
935 | #if IS_ENABLED(CONFIG_AGP) |
936 | DRM_IOCTL32_DEF(DRM_IOCTL_AGP_ENABLE, compat_drm_agp_enable), |
937 | DRM_IOCTL32_DEF(DRM_IOCTL_AGP_INFO, compat_drm_agp_info), |
938 | DRM_IOCTL32_DEF(DRM_IOCTL_AGP_ALLOC, compat_drm_agp_alloc), |
939 | DRM_IOCTL32_DEF(DRM_IOCTL_AGP_FREE, compat_drm_agp_free), |
940 | DRM_IOCTL32_DEF(DRM_IOCTL_AGP_BIND, compat_drm_agp_bind), |
941 | DRM_IOCTL32_DEF(DRM_IOCTL_AGP_UNBIND, compat_drm_agp_unbind), |
942 | #endif |
943 | #endif |
944 | #if IS_ENABLED(CONFIG_DRM_LEGACY) |
945 | DRM_IOCTL32_DEF(DRM_IOCTL_SG_ALLOC, compat_drm_sg_alloc), |
946 | DRM_IOCTL32_DEF(DRM_IOCTL_SG_FREE, compat_drm_sg_free), |
947 | #endif |
948 | #if defined(CONFIG_X86) |
949 | DRM_IOCTL32_DEF(DRM_IOCTL_UPDATE_DRAW, compat_drm_update_draw), |
950 | #endif |
951 | DRM_IOCTL32_DEF(DRM_IOCTL_WAIT_VBLANK, compat_drm_wait_vblank), |
952 | #if defined(CONFIG_X86) |
953 | DRM_IOCTL32_DEF(DRM_IOCTL_MODE_ADDFB2, compat_drm_mode_addfb2), |
954 | #endif |
955 | }; |
956 | |
957 | /** |
958 | * drm_compat_ioctl - 32bit IOCTL compatibility handler for DRM drivers |
959 | * @filp: file this ioctl is called on |
960 | * @cmd: ioctl cmd number |
961 | * @arg: user argument |
962 | * |
963 | * Compatibility handler for 32 bit userspace running on 64 kernels. All actual |
964 | * IOCTL handling is forwarded to drm_ioctl(), while marshalling structures as |
965 | * appropriate. Note that this only handles DRM core IOCTLs, if the driver has |
966 | * botched IOCTL itself, it must handle those by wrapping this function. |
967 | * |
968 | * Returns: |
969 | * Zero on success, negative error code on failure. |
970 | */ |
971 | long drm_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) |
972 | { |
973 | unsigned int nr = DRM_IOCTL_NR(cmd); |
974 | struct drm_file *file_priv = filp->private_data; |
975 | struct drm_device *dev = file_priv->minor->dev; |
976 | drm_ioctl_compat_t *fn; |
977 | int ret; |
978 | |
979 | /* Assume that ioctls without an explicit compat routine will just |
980 | * work. This may not always be a good assumption, but it's better |
981 | * than always failing. |
982 | */ |
983 | if (nr >= ARRAY_SIZE(drm_compat_ioctls)) |
984 | return drm_ioctl(filp, cmd, arg); |
985 | |
986 | fn = drm_compat_ioctls[nr].fn; |
987 | if (!fn) |
988 | return drm_ioctl(filp, cmd, arg); |
989 | |
990 | drm_dbg_core(dev, "comm=\"%s\", pid=%d, dev=0x%lx, auth=%d, %s\n" , |
991 | current->comm, task_pid_nr(current), |
992 | (long)old_encode_dev(file_priv->minor->kdev->devt), |
993 | file_priv->authenticated, |
994 | drm_compat_ioctls[nr].name); |
995 | ret = (*fn)(filp, cmd, arg); |
996 | if (ret) |
997 | drm_dbg_core(dev, "ret = %d\n" , ret); |
998 | return ret; |
999 | } |
1000 | EXPORT_SYMBOL(drm_compat_ioctl); |
1001 | |