1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright (C) 2011 Google, Inc.
4 */
5
6 #include <linux/kernel.h>
7 #include <linux/file.h>
8 #include <linux/fs.h>
9 #include <linux/uaccess.h>
10
11 #include "ion.h"
12
13 union ion_ioctl_arg {
14 struct ion_allocation_data allocation;
15 struct ion_heap_query query;
16 };
17
validate_ioctl_arg(unsigned int cmd,union ion_ioctl_arg * arg)18 static int validate_ioctl_arg(unsigned int cmd, union ion_ioctl_arg *arg)
19 {
20 switch (cmd) {
21 case ION_IOC_HEAP_QUERY:
22 if (arg->query.reserved0 ||
23 arg->query.reserved1 ||
24 arg->query.reserved2)
25 return -EINVAL;
26 break;
27 default:
28 break;
29 }
30
31 return 0;
32 }
33
34 /* fix up the cases where the ioctl direction bits are incorrect */
ion_ioctl_dir(unsigned int cmd)35 static unsigned int ion_ioctl_dir(unsigned int cmd)
36 {
37 switch (cmd) {
38 default:
39 return _IOC_DIR(cmd);
40 }
41 }
42
ion_ioctl(struct file * filp,unsigned int cmd,unsigned long arg)43 long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
44 {
45 int ret = 0;
46 unsigned int dir;
47 union ion_ioctl_arg data;
48
49 dir = ion_ioctl_dir(cmd);
50
51 if (_IOC_SIZE(cmd) > sizeof(data))
52 return -EINVAL;
53
54 /*
55 * The copy_from_user is unconditional here for both read and write
56 * to do the validate. If there is no write for the ioctl, the
57 * buffer is cleared
58 */
59 if (copy_from_user(&data, (void __user *)arg, _IOC_SIZE(cmd)))
60 return -EFAULT;
61
62 ret = validate_ioctl_arg(cmd, &data);
63 if (ret) {
64 pr_warn_once("%s: ioctl validate failed\n", __func__);
65 return ret;
66 }
67
68 if (!(dir & _IOC_WRITE))
69 memset(&data, 0, sizeof(data));
70
71 switch (cmd) {
72 case ION_IOC_ALLOC:
73 {
74 int fd;
75
76 fd = ion_alloc(data.allocation.len,
77 data.allocation.heap_id_mask,
78 data.allocation.flags);
79 if (fd < 0)
80 return fd;
81
82 data.allocation.fd = fd;
83
84 break;
85 }
86 case ION_IOC_HEAP_QUERY:
87 ret = ion_query_heaps(&data.query);
88 break;
89 default:
90 return -ENOTTY;
91 }
92
93 if (dir & _IOC_READ) {
94 if (copy_to_user((void __user *)arg, &data, _IOC_SIZE(cmd)))
95 return -EFAULT;
96 }
97 return ret;
98 }
99